mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-16 20:01:35 +01:00
Merge remote-tracking branch 'upstream/new-ui-with-sync-image' into job-service
This commit is contained in:
commit
0b25569536
@ -1,15 +1,35 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICWDCCAcGgAwIBAgIJAN1nLuloDeHNMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTYwMTI3MDQyMDM1WhcNNDMwNjE0MDQyMDM1WjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||||
gQClak/4HO7EeLU0w/BhtVENPLOqU0AP2QjVUdg1qhNiDWVrbWx9KYHqz5Kn0n2+
|
||||
fxdZo3o7ZY5/2+hhgkKh1z6Kge9XGgune6z4fx2J/X2Se8WsGeQUTiND8ngSnsCA
|
||||
NtYFwW50SbUZPtyf5XjAfKRofZem51OxbxzN3217L/ubKwIDAQABo1AwTjAdBgNV
|
||||
HQ4EFgQU5EG2VrB3I6G/TudUpz+kBgQXSvYwHwYDVR0jBBgwFoAU5EG2VrB3I6G/
|
||||
TudUpz+kBgQXSvYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAx+2eo
|
||||
oOm0YNy9KQ81+7GQkKVWoPQXjAGGgZuZj8WCFepYqUSJ4q5qbuVCY8WbGcHVk2Rx
|
||||
Jg1XDCmMjBgYP6S0ikezBRqSmNA3G6oFiydTKBfPs6RNalsB0C78Xk5l5+PIyd2R
|
||||
jFKOKoMpkjwfeJv2j64WNGoBgqj7XRBoJ11a4g==
|
||||
MIIGBzCCA++gAwIBAgIJAKB8CNqCxhr7MA0GCSqGSIb3DQEBCwUAMIGZMQswCQYD
|
||||
VQQGEwJDTjEOMAwGA1UECAwFU3RhdGUxCzAJBgNVBAcMAkNOMRUwEwYDVQQKDAxv
|
||||
cmdhbml6YXRpb24xHDAaBgNVBAsME29yZ2FuaXphdGlvbmFsIHVuaXQxFDASBgNV
|
||||
BAMMC2V4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNleGFtcGxlQGV4YW1wbGUu
|
||||
Y29tMB4XDTE2MDUxNjAyNDY1NVoXDTI2MDUxNDAyNDY1NVowgZkxCzAJBgNVBAYT
|
||||
AkNOMQ4wDAYDVQQIDAVTdGF0ZTELMAkGA1UEBwwCQ04xFTATBgNVBAoMDG9yZ2Fu
|
||||
aXphdGlvbjEcMBoGA1UECwwTb3JnYW5pemF0aW9uYWwgdW5pdDEUMBIGA1UEAwwL
|
||||
ZXhhbXBsZS5jb20xIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVAZXhhbXBsZS5jb20w
|
||||
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2ky/K/XneJKbCbpOsWlQ7
|
||||
OwgYEQNsa044RkwSbTwPwgLafUZ3r9c5nkXE8APqAikTQQBwyiNjk7QeXgIOjJXd
|
||||
7+IpwGoU6Bi2miA21qfvJPknyDAqw9tT/ycGQrvkY6rnqd++ri30ZUByUgO0du6+
|
||||
aWHo7af5/G1HQz0tu6i1tIF1dhSHNeqJKwxyUG8vIiT/PfbtU/mXSdQ07M+4ojBC
|
||||
O7FgoOS+rWgbL3yhWUTrCXSV2HZlhksYBhtWGoFVRPVSf89iqL02h9rZEjmfVY6R
|
||||
QlCnzu9v49Q8WFU528f+gDNXr9v13PKEDmloMzTqWPaCyD2FBbEKBsWHXHf1zqlI
|
||||
jyGZV7rHZ3i0C1LI6bdDDP7M7aVs8O+RjxK+HmfFRg5us2t6g7zAevwwLpMZRAud
|
||||
S39F91Up7l9g8WXpViok/8vcsOdePvvWcWro8qJhuEHAnDdMzj2Cko1L85/vRM/a
|
||||
budWXK7Ix0TlPWPfHJc2SLFeqqcm5Iypf/cGabQ6f0oRt6bCfspFgX9upznT5FwZ
|
||||
R0o1w6Q3q+4xVl6LgZvEAudWppyz79RACJA/jbXZQ7uJkXAxoI0nev9vgY6XJqUj
|
||||
XIQDih2hmi/uTnNU7Me7w7pCYKPdHlNU652kaJSH6W6ZFGk2rEOCOeAuWO9pZTq2
|
||||
3IhuOcDAKOcmimlkzaWRGQIDAQABo1AwTjAdBgNVHQ4EFgQUPJF++WMsv1OJvf7F
|
||||
oCew37JTnfQwHwYDVR0jBBgwFoAUPJF++WMsv1OJvf7FoCew37JTnfQwDAYDVR0T
|
||||
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAb5LvqukMxWd5Zajbh3orfYsXmhWn
|
||||
UWiwG176+bd3b5xMlG9iLd4vQ11lTZoIhFOfprRQzbizQ8BzR2JBQckpLcy+5hyA
|
||||
D3M9vLL37OwA0wT6kxFnd6LtlFaH5gG++huw2ts2PDXFz0jqw+0YE/R8ov2+YdaZ
|
||||
aPSEMunmAuEY1TbYWzz4u6PxycxhQzDQ34ZmJZ34Elvw1NYMfPMGTKp34PsxIcgT
|
||||
ao5jqb9RMU6JAumfXrOvXRjjl573vX2hgMZzEU6OF2/+uyg95chn6nO1GUQrT2+F
|
||||
/1xIqfHfFCm8+jujSDgqfBtGI+2C7No+Dq8LEyEINZe6wSQ81+ryt5jy5SZmAsnj
|
||||
V4OsSIwlpR5fLUwrFStVoUWHEKl1DflkYki/cAC1TL0Om+ldJ219kcOnaXDNaq66
|
||||
3I75BvRY7/88MYLl4Fgt7sn05Mn3uNPrCrci8d0R1tlXIcwMdCowIHeZdWHX43f7
|
||||
NsVk/7VSOxJ343csgaQc+3WxEFK0tBxGO6GP+Xj0XmdVGLhalVBsEhPjnmx+Yyrn
|
||||
oMsTA1Yrs88C8ItQn7zuO/30eKNGTnby0gptHiS6sa/c3O083Mpi8y33GPVZDvBl
|
||||
l9PfSZT8LG7SvpjsdgdNZlyFvTY4vsB+Vd5Howh7gXYPVXdCs4k7HMyo7zvzliZS
|
||||
ekCw9NGLoNqQqnA=
|
||||
-----END CERTIFICATE-----
|
||||
|
@ -1,15 +1,51 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQClak/4HO7EeLU0w/BhtVENPLOqU0AP2QjVUdg1qhNiDWVrbWx9
|
||||
KYHqz5Kn0n2+fxdZo3o7ZY5/2+hhgkKh1z6Kge9XGgune6z4fx2J/X2Se8WsGeQU
|
||||
TiND8ngSnsCANtYFwW50SbUZPtyf5XjAfKRofZem51OxbxzN3217L/ubKwIDAQAB
|
||||
AoGBAITMMuNYJwAogCGaZHOs4yMjZoIJT9bpQMQxbsi2f9UqOA/ky0I4foqKloyQ
|
||||
2k6DLbXTHqBsydgwLgGKWAAiE5xIR2bPMUNSLgjbA2eLly3aOR/0FJ5n09k2EmGg
|
||||
Am7tLP+6yneXWKVi3HI3NzXriVjWK94WHGGC1b9F+n5CY/2RAkEA1d62OJUNve2k
|
||||
IY6/b6T0BdssFo3VFcm22vnayEL/wcYrnRfF9Pb5wM4HUUqwVelKTouivXg60GNK
|
||||
ZKYAx5CtHwJBAMYAEf5u0CQ/8URcwBuMkm0LzK4AM2x1nGs7gIxAEFhu1Z4xPjVe
|
||||
MtIxuHhDhlLvD760uccmo5yE72QJ1ZrYBHUCQQCAxLZMPRpoB4QyHEOREe1G9V6H
|
||||
OeBZXPk2wQcEWqqo3gt2a1DqHCXl+2aWgHTJVUxDHHngwFoRDCdHkFeZ0LcbAkAj
|
||||
T8/luI2WaXD16DS6tQ9IM1qFjbOeHDuRRENgv+wqWVnvpIibq/kUU5m6mRBTqh78
|
||||
u+6F/fYf6/VluftGalAhAkAukdMtt+sksq2e7Qw2dRr5GXtXjt+Otjj0NaJENmWk
|
||||
a7SgAs34EOWtbd0XGYpZFrg134MzQGbweFeEUTj++e8p
|
||||
MIIJKAIBAAKCAgEAtpMvyv153iSmwm6TrFpUOzsIGBEDbGtOOEZMEm08D8IC2n1G
|
||||
d6/XOZ5FxPAD6gIpE0EAcMojY5O0Hl4CDoyV3e/iKcBqFOgYtpogNtan7yT5J8gw
|
||||
KsPbU/8nBkK75GOq56nfvq4t9GVAclIDtHbuvmlh6O2n+fxtR0M9LbuotbSBdXYU
|
||||
hzXqiSsMclBvLyIk/z327VP5l0nUNOzPuKIwQjuxYKDkvq1oGy98oVlE6wl0ldh2
|
||||
ZYZLGAYbVhqBVUT1Un/PYqi9Nofa2RI5n1WOkUJQp87vb+PUPFhVOdvH/oAzV6/b
|
||||
9dzyhA5paDM06lj2gsg9hQWxCgbFh1x39c6pSI8hmVe6x2d4tAtSyOm3Qwz+zO2l
|
||||
bPDvkY8Svh5nxUYObrNreoO8wHr8MC6TGUQLnUt/RfdVKe5fYPFl6VYqJP/L3LDn
|
||||
Xj771nFq6PKiYbhBwJw3TM49gpKNS/Of70TP2m7nVlyuyMdE5T1j3xyXNkixXqqn
|
||||
JuSMqX/3Bmm0On9KEbemwn7KRYF/bqc50+RcGUdKNcOkN6vuMVZei4GbxALnVqac
|
||||
s+/UQAiQP4212UO7iZFwMaCNJ3r/b4GOlyalI1yEA4odoZov7k5zVOzHu8O6QmCj
|
||||
3R5TVOudpGiUh+lumRRpNqxDgjngLljvaWU6ttyIbjnAwCjnJoppZM2lkRkCAwEA
|
||||
AQKCAgAvsvCPlf2a3fR7Y6xNISRUfS22K+u7DaXX6fXB8qv4afWY45Xfex89vG35
|
||||
78L2Bi55C0h0LztjrpkmPeVHq88TtrJduhl88M5UFpxH93jUb9JwZErBQX4xyb2G
|
||||
UzUHjEqAT89W3+a9rR5TP74cDd59/MZJtp1mIF7keVqochi3sDsKVxkx4hIuWALe
|
||||
csk5hTApRyUWCBRzRCSe1yfF0wnMpA/JcP+SGXfTcmqbNNlelo/Q/kaga59+3UmT
|
||||
C0Wy41s8fIvP+MnGT2QLxkkrqYyfwrWTweqoTtuKEIHjpdnwUcoYJKfQ6jKp8aH0
|
||||
STyP5UIyFOKNuFjyh6ZfoPbuT1nGW+YKlUnK4hQ9N/GE0oMoecTaHTbqM+psQvbj
|
||||
6+CG/1ukA5ZTQyogNyuOApArFBQ+RRmVudPKA3JYygIhwctuB2oItsVEOEZMELCn
|
||||
g2aVFAVXGfGRDXvpa8oxs3Pc6RJEp/3tON6+w7cMCx0lwN/Jk2Ie6RgTzUycT3k6
|
||||
MoTQJRoO6/ZHcx3hTut/CfnrWiltyAUZOsefLuLg+Pwf9GHhOycLRI6gHfgSwdIV
|
||||
S77UbbELWdscVr1EoPIasUm1uYWBBcFRTturRW+GHJ8TZX+mcWSBcWwBhp15LjEl
|
||||
tJf+9U6lWMOSB2LvT+vFmR0M9q56fo7UeKFIR7mo7/GpiVu5AQKCAQEA6Qs7G9mw
|
||||
N/JZOSeQO6xIQakC+sKApPyXO58fa7WQzri+l2UrLNp0DEQfZCujqDgwys6OOzR/
|
||||
xg8ZKQWVoad08Ind3ZwoJgnLn6QLENOcE6PpWxA/JjnVGP4JrXCYR98cP0sf9jEI
|
||||
xkR1qT50GbeqU3RDFliI4kGRvbZ8cekzuWppfQcjstSBPdvuxqAcUVmTnTw83nvD
|
||||
FmBbhlLiEgI3iKtJ97UB7480ivnWnOuusduk7FO4jF3hkrOa+YRidinTCi8JBo0Y
|
||||
jx4Ci3Y5x6nvwkXhKzXapd7YmPNisUc5xA7/a+W71cyC0IKUwRc/8pYWLL3R3CpR
|
||||
YiV8gf6gwzOckQKCAQEAyI9CSNoAQH4zpS8B9PF8zILqEEuun8m1f5JB3hQnfWzm
|
||||
7uz/zg6I0TkcCE0AJVSKPHQm1V9+TRbF9+DiOWHEYYzPmK8h63SIufaWxZPqai4E
|
||||
PUj6eQWykBUVJ96n6/AW0JHRZ+WrJ5RXBqCLuY7NP6wDhORrCJjBwaGMohNpbKPS
|
||||
H3QewsoxCh+CEXKdKyy+/yU/f4E89PlHapkW1/bDJ5u7puSD+KvmiDDIXSBncdOO
|
||||
uFT8n+XH5IwgjdXFSDim15rQ8jD2l2xLcwKboTpx5GeRl8oB1VGm0fUbBn1dvGPG
|
||||
4WfHGyrp9VNZtP160WoHr+vRVPqvHNkoeAlCfEwQCQKCAQBN1dtzLN0HgqE8TrOE
|
||||
ysEDdTCykj4nXNoiJr522hi4gsndhQPLolb6NdKKQW0S5Vmekyi8K4e1nhtYMS5N
|
||||
5MFRCasZtmtOcR0af87WWucZRDjPmniNCunaxBZ1YFLsRl+H4E6Xir8UgY8O7PYY
|
||||
FNkFsKIrl3x4nU/RHl8oKKyG9Dyxbq4Er6dPAuMYYiezIAkGjjUCVjHNindnQM2T
|
||||
GDx2IEe/PSydV6ZD+LguhyU88FCAQmI0N7L8rZJIXmgIcWW0VAterceTHYHaFK2t
|
||||
u1uB9pcDOKSDnA+Z3kiLT2/CxQOYhQ2clgbnH4YRi/Nm0awsW2X5dATklAKm5GXL
|
||||
bLSRAoIBAQClaNnPQdTBXBR2IN3pSZ2XAkXPKMwdxvtk+phOc6raHA4eceLL7FrU
|
||||
y9gd1HvRTfcwws8gXcDKDYU62gNaNhMELWEt2QsNqS/2x7Qzwbms1sTyUpUZaSSL
|
||||
BohLOKyfv4ThgdIGcXoGi6Z2tcRnRqpq4BCK8uR/05TBgN5+8amaS0ZKYLfaCW4G
|
||||
nlPk1fVgHWhtAChtnYZLuKg494fKmB7+NMfAbmmVlxjrq+gkPkxyqXvk9Vrg+V8y
|
||||
VIuozu0Fkouv+GRpyw4ldtCHS1hV0eEK8ow2dwmqCMygDxm58X10mYn2b2PcOTl5
|
||||
9sNerUw1GNC8O66K+rGgBk4FKgXmg8kZAoIBABBcuisK250fXAfjAWXGqIMs2+Di
|
||||
vqAdT041SNZEOJSGNFsLJbhd/3TtCLf29PN/YXtnvBmC37rqryTsqjSbx/YT2Jbr
|
||||
Bk3jOr9JVbmcoSubXl8d/uzf7IGs91qaCgBwPZHgeH+kK13FCLexz+U9zYMZ78fF
|
||||
/yO82CpoekT+rcl1jzYn43b6gIklHABQU1uCD6MMyMhJ9Op2WmbDk3X+py359jMc
|
||||
+Cr2zfzdHAIVff2dOV3OL+ZHEWbwtnn3htKUdOmjoTJrciFx0xNZJS5Q7QYHMONj
|
||||
yPqbajyhopiN01aBQpCSGF1F1uRpWeIjTrAZPbrwLl9YSYXz0AT05QeFEFk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
@ -46,6 +46,8 @@ services:
|
||||
volumes:
|
||||
- ./config/ui/app.conf:/etc/ui/app.conf
|
||||
- ./config/ui/private_key.pem:/etc/ui/private_key.pem
|
||||
- ../static:/go/bin/static
|
||||
- ../views:/go/bin/views
|
||||
depends_on:
|
||||
- log
|
||||
logging:
|
||||
|
@ -2,8 +2,8 @@ appname = registry
|
||||
runmode = dev
|
||||
|
||||
[lang]
|
||||
types = en-US|zh-CN|de-DE|ru-RU|ja-JP
|
||||
names = en-US|zh-CN|de-DE|ru-RU|ja-JP
|
||||
types = en-US|zh-CN
|
||||
names = English|中文
|
||||
|
||||
[dev]
|
||||
httpport = 80
|
||||
|
11
controllers/ng/accountsetting.go
Normal file
11
controllers/ng/accountsetting.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// AccountSettingController handles request to /ng/account_setting
|
||||
type AccountSettingController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the account settings page
|
||||
func (asc *AccountSettingController) Get() {
|
||||
asc.Forward("Account Settings", "account-settings.htm")
|
||||
}
|
11
controllers/ng/adminoption.go
Normal file
11
controllers/ng/adminoption.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// AdminOptionController handles requests to /ng/admin_option
|
||||
type AdminOptionController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the admin options page
|
||||
func (aoc *AdminOptionController) Get() {
|
||||
aoc.Forward("Admin Options", "admin-options.htm")
|
||||
}
|
162
controllers/ng/base.go
Normal file
162
controllers/ng/base.go
Normal file
@ -0,0 +1,162 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/beego/i18n"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers.
|
||||
type BaseController struct {
|
||||
beego.Controller
|
||||
i18n.Locale
|
||||
SelfRegistration bool
|
||||
IsAdmin bool
|
||||
AuthMode string
|
||||
}
|
||||
|
||||
type langType struct {
|
||||
Lang string
|
||||
Name string
|
||||
}
|
||||
|
||||
const (
|
||||
viewPath = "sections"
|
||||
prefixNg = "ng"
|
||||
defaultLang = "en-US"
|
||||
)
|
||||
|
||||
var supportLanguages map[string]langType
|
||||
|
||||
// Prepare extracts the language information from request and populate data for rendering templates.
|
||||
func (b *BaseController) Prepare() {
|
||||
|
||||
var lang string
|
||||
al := b.Ctx.Request.Header.Get("Accept-Language")
|
||||
|
||||
if len(al) > 4 {
|
||||
al = al[:5] // Only compare first 5 letters.
|
||||
if i18n.IsExist(al) {
|
||||
lang = al
|
||||
}
|
||||
}
|
||||
|
||||
if _, exist := supportLanguages[lang]; exist == false { //Check if support the request language.
|
||||
lang = defaultLang //Set default language if not supported.
|
||||
}
|
||||
|
||||
sessionLang := b.GetSession("lang")
|
||||
if sessionLang != nil {
|
||||
b.SetSession("Lang", lang)
|
||||
lang = sessionLang.(string)
|
||||
}
|
||||
|
||||
curLang := langType{
|
||||
Lang: lang,
|
||||
}
|
||||
|
||||
restLangs := make([]*langType, 0, len(langTypes)-1)
|
||||
for _, v := range langTypes {
|
||||
if lang != v.Lang {
|
||||
restLangs = append(restLangs, v)
|
||||
} else {
|
||||
curLang.Name = v.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Set language properties.
|
||||
b.Lang = lang
|
||||
b.Data["Lang"] = curLang.Lang
|
||||
b.Data["CurLang"] = curLang.Name
|
||||
b.Data["RestLangs"] = restLangs
|
||||
b.Data["SupportLanguages"] = supportLanguages
|
||||
|
||||
authMode := strings.ToLower(os.Getenv("AUTH_MODE"))
|
||||
if authMode == "" {
|
||||
authMode = "db_auth"
|
||||
}
|
||||
b.AuthMode = authMode
|
||||
b.Data["AuthMode"] = b.AuthMode
|
||||
|
||||
}
|
||||
|
||||
// Forward to setup layout and template for content for a page.
|
||||
func (b *BaseController) Forward(title, templateName string) {
|
||||
b.Layout = filepath.Join(prefixNg, "layout.htm")
|
||||
b.TplName = filepath.Join(prefixNg, templateName)
|
||||
b.Data["Title"] = title
|
||||
b.LayoutSections = make(map[string]string)
|
||||
b.LayoutSections["HeaderInclude"] = filepath.Join(prefixNg, viewPath, "header-include.htm")
|
||||
b.LayoutSections["FooterInclude"] = filepath.Join(prefixNg, viewPath, "footer-include.htm")
|
||||
b.LayoutSections["HeaderContent"] = filepath.Join(prefixNg, viewPath, "header-content.htm")
|
||||
b.LayoutSections["FooterContent"] = filepath.Join(prefixNg, viewPath, "footer-content.htm")
|
||||
|
||||
}
|
||||
|
||||
var langTypes []*langType
|
||||
|
||||
// CommonController handles request from UI that doesn't expect a page, such as /SwitchLanguage /logout ...
|
||||
type CommonController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Render returns nil.
|
||||
func (cc *CommonController) Render() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogOut Habor UI
|
||||
func (cc *CommonController) LogOut() {
|
||||
cc.DestroySession()
|
||||
}
|
||||
|
||||
// SwitchLanguage User can swith to prefered language
|
||||
func (cc *CommonController) SwitchLanguage() {
|
||||
lang := cc.GetString("lang")
|
||||
if _, exist := supportLanguages[lang]; exist {
|
||||
cc.SetSession("lang", lang)
|
||||
cc.Data["Lang"] = lang
|
||||
}
|
||||
cc.Redirect(cc.Ctx.Request.Header.Get("Referer"), http.StatusFound)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
//conf/app.conf -> os.Getenv("config_path")
|
||||
configPath := os.Getenv("CONFIG_PATH")
|
||||
if len(configPath) != 0 {
|
||||
log.Infof("Config path: %s", configPath)
|
||||
beego.AppConfigPath = configPath
|
||||
if err := beego.ParseConfig(); err != nil {
|
||||
log.Warningf("Failed to parse config file: %s, error: %v", configPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
beego.AddFuncMap("i18n", i18n.Tr)
|
||||
|
||||
langs := strings.Split(beego.AppConfig.String("lang::types"), "|")
|
||||
names := strings.Split(beego.AppConfig.String("lang::names"), "|")
|
||||
|
||||
supportLanguages = make(map[string]langType)
|
||||
|
||||
langTypes = make([]*langType, 0, len(langs))
|
||||
for i, v := range langs {
|
||||
t := langType{
|
||||
Lang: v,
|
||||
Name: names[i],
|
||||
}
|
||||
langTypes = append(langTypes, &t)
|
||||
supportLanguages[v] = t
|
||||
}
|
||||
|
||||
for _, lang := range langs {
|
||||
if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil {
|
||||
log.Errorf("Fail to set message file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
11
controllers/ng/dashboard.go
Normal file
11
controllers/ng/dashboard.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// DashboardController handles requests to /ng/dashboard
|
||||
type DashboardController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the dashboard page
|
||||
func (dc *DashboardController) Get() {
|
||||
dc.Forward("Dashboard", "dashboard.htm")
|
||||
}
|
11
controllers/ng/index.go
Normal file
11
controllers/ng/index.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// IndexController handles request to /ng
|
||||
type IndexController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the index page
|
||||
func (ic *IndexController) Get() {
|
||||
ic.Forward("Index", "index.htm")
|
||||
}
|
34
controllers/ng/navigationdetail.go
Normal file
34
controllers/ng/navigationdetail.go
Normal file
@ -0,0 +1,34 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
type NavigationDetailController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (ndc *NavigationDetailController) Get() {
|
||||
sessionUserID := ndc.GetSession("userId")
|
||||
var isAdmin int
|
||||
if sessionUserID != nil {
|
||||
userID := sessionUserID.(int)
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
ndc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
log.Warningf("User was deleted already, user id: %d, canceling request.", userID)
|
||||
ndc.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
isAdmin = u.HasAdminRole
|
||||
}
|
||||
ndc.Data["IsAdmin"] = isAdmin
|
||||
ndc.TplName = "ng/navigation-detail.htm"
|
||||
ndc.Render()
|
||||
}
|
39
controllers/ng/navigationheader.go
Normal file
39
controllers/ng/navigationheader.go
Normal file
@ -0,0 +1,39 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// NavigationHeaderController handles requests to /ng/navigation_header
|
||||
type NavigationHeaderController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders user's navigation header
|
||||
func (nhc *NavigationHeaderController) Get() {
|
||||
sessionUserID := nhc.GetSession("userId")
|
||||
var hasLoggedIn bool
|
||||
var isAdmin int
|
||||
if sessionUserID != nil {
|
||||
hasLoggedIn = true
|
||||
userID := sessionUserID.(int)
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
nhc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
log.Warningf("User was deleted already, user id: %d, canceling request.", userID)
|
||||
nhc.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
isAdmin = u.HasAdminRole
|
||||
}
|
||||
nhc.Data["HasLoggedIn"] = hasLoggedIn
|
||||
nhc.Data["IsAdmin"] = isAdmin
|
||||
nhc.TplName = "ng/navigation-header.htm"
|
||||
nhc.Render()
|
||||
}
|
39
controllers/ng/optionalmenu.go
Normal file
39
controllers/ng/optionalmenu.go
Normal file
@ -0,0 +1,39 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// OptionalMenuController handles request to /ng/optional_menu
|
||||
type OptionalMenuController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders optional menu, Admin user has "Add User" menu
|
||||
func (omc *OptionalMenuController) Get() {
|
||||
sessionUserID := omc.GetSession("userId")
|
||||
|
||||
var hasLoggedIn bool
|
||||
if sessionUserID != nil {
|
||||
hasLoggedIn = true
|
||||
userID := sessionUserID.(int)
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
omc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
log.Warningf("User was deleted already, user id: %d, canceling request.", userID)
|
||||
omc.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
omc.Data["Username"] = u.Username
|
||||
}
|
||||
omc.Data["HasLoggedIn"] = hasLoggedIn
|
||||
omc.TplName = "ng/optional-menu.htm"
|
||||
omc.Render()
|
||||
|
||||
}
|
169
controllers/ng/password.go
Normal file
169
controllers/ng/password.go
Normal file
@ -0,0 +1,169 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
type messageDetail struct {
|
||||
Hint string
|
||||
URL string
|
||||
UUID string
|
||||
}
|
||||
|
||||
// SendEmail verifies the Email address and contact SMTP server to send reset password Email.
|
||||
func (cc *CommonController) SendEmail() {
|
||||
|
||||
email := cc.GetString("email")
|
||||
|
||||
pass, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, email)
|
||||
|
||||
if !pass {
|
||||
cc.CustomAbort(http.StatusBadRequest, "email_content_illegal")
|
||||
} else {
|
||||
|
||||
queryUser := models.User{Email: email}
|
||||
exist, err := dao.UserExists(queryUser, "email")
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserExists: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
cc.CustomAbort(http.StatusNotFound, "email_does_not_exist")
|
||||
}
|
||||
|
||||
messageTemplate, err := template.ParseFiles("views/ng/reset-password-mail.tpl")
|
||||
if err != nil {
|
||||
log.Errorf("Parse email template file failed: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
message := new(bytes.Buffer)
|
||||
|
||||
harborURL := os.Getenv("HARBOR_URL")
|
||||
if harborURL == "" {
|
||||
harborURL = "localhost"
|
||||
}
|
||||
uuid, err := dao.GenerateRandomString()
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GenerateRandomString: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
err = messageTemplate.Execute(message, messageDetail{
|
||||
Hint: cc.Tr("reset_email_hint"),
|
||||
URL: harborURL,
|
||||
UUID: uuid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Message template error: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
|
||||
}
|
||||
|
||||
config, err := beego.AppConfig.GetSection("mail")
|
||||
if err != nil {
|
||||
log.Errorf("Can not load app.conf: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
|
||||
}
|
||||
|
||||
mail := utils.Mail{
|
||||
From: config["from"],
|
||||
To: []string{email},
|
||||
Subject: cc.Tr("reset_email_subject"),
|
||||
Message: message.String()}
|
||||
|
||||
err = mail.SendMail()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Send email failed: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "send_email_failed")
|
||||
}
|
||||
|
||||
user := models.User{ResetUUID: uuid, Email: email}
|
||||
dao.UpdateUserResetUUID(user)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ForgotPasswordController handles requests to /ng/forgot_password
|
||||
type ForgotPasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders forgot password page
|
||||
func (fpc *ForgotPasswordController) Get() {
|
||||
fpc.Forward("Forgot Password", "forgot-password.htm")
|
||||
}
|
||||
|
||||
// ResetPasswordController handles request to /resetPassword
|
||||
type ResetPasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get checks if reset_uuid in the reset link is valid and render the result page for user to reset password.
|
||||
func (rpc *ResetPasswordController) Get() {
|
||||
|
||||
resetUUID := rpc.GetString("reset_uuid")
|
||||
if resetUUID == "" {
|
||||
log.Error("Reset uuid is blank.")
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
queryUser := models.User{ResetUUID: resetUUID}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser: %v", err)
|
||||
rpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
rpc.Data["ResetUuid"] = user.ResetUUID
|
||||
rpc.Forward("Reset Password", "reset-password.htm")
|
||||
} else {
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// ResetPassword handles request from the reset page and reset password
|
||||
func (cc *CommonController) ResetPassword() {
|
||||
|
||||
resetUUID := cc.GetString("reset_uuid")
|
||||
if resetUUID == "" {
|
||||
cc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.")
|
||||
}
|
||||
|
||||
queryUser := models.User{ResetUUID: resetUUID}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if user == nil {
|
||||
log.Error("User does not exist")
|
||||
cc.CustomAbort(http.StatusBadRequest, "User does not exist")
|
||||
}
|
||||
|
||||
password := cc.GetString("password")
|
||||
|
||||
if password != "" {
|
||||
user.Password = password
|
||||
err = dao.ResetUserPassword(*user)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in ResetUserPassword: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
} else {
|
||||
cc.CustomAbort(http.StatusBadRequest, "password_is_required")
|
||||
}
|
||||
}
|
11
controllers/ng/project.go
Normal file
11
controllers/ng/project.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// ProjectController handles requests to /ng/projec
|
||||
type ProjectController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders project page
|
||||
func (pc *ProjectController) Get() {
|
||||
pc.Forward("My Projects", "project.htm")
|
||||
}
|
14
controllers/ng/repository.go
Normal file
14
controllers/ng/repository.go
Normal file
@ -0,0 +1,14 @@
|
||||
package ng
|
||||
|
||||
import "os"
|
||||
|
||||
// RepositoryController handles request to /ng/repository
|
||||
type RepositoryController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders repository page
|
||||
func (rc *RepositoryController) Get() {
|
||||
rc.Data["HarborRegUrl"] = os.Getenv("HARBOR_REG_URL")
|
||||
rc.Forward("Repository", "repository.htm")
|
||||
}
|
11
controllers/ng/search.go
Normal file
11
controllers/ng/search.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// SearchController handles request to ng/search
|
||||
type SearchController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get rendlers search bar
|
||||
func (sc *SearchController) Get() {
|
||||
sc.Forward("Search", "search.htm")
|
||||
}
|
39
controllers/ng/signin.go
Normal file
39
controllers/ng/signin.go
Normal file
@ -0,0 +1,39 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// SignInController handles requests to /ng/sign_in
|
||||
type SignInController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
//Get renders sign_in page
|
||||
func (sic *SignInController) Get() {
|
||||
sessionUserID := sic.GetSession("userId")
|
||||
var hasLoggedIn bool
|
||||
var username string
|
||||
if sessionUserID != nil {
|
||||
hasLoggedIn = true
|
||||
userID := sessionUserID.(int)
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
sic.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
log.Warningf("User was deleted already, user id: %d, canceling request.", userID)
|
||||
sic.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
username = u.Username
|
||||
}
|
||||
sic.Data["Username"] = username
|
||||
sic.Data["HasLoggedIn"] = hasLoggedIn
|
||||
sic.TplName = "ng/sign-in.htm"
|
||||
sic.Render()
|
||||
}
|
11
controllers/ng/signup.go
Normal file
11
controllers/ng/signup.go
Normal file
@ -0,0 +1,11 @@
|
||||
package ng
|
||||
|
||||
// SignUpController handles requests to /ng/sign_up
|
||||
type SignUpController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders sign up page
|
||||
func (suc *SignUpController) Get() {
|
||||
suc.Forward("Sign Up", "sign-up.htm")
|
||||
}
|
59
static/ng/Gruntfile.js
Normal file
59
static/ng/Gruntfile.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*global module:false*/
|
||||
module.exports = function(grunt) {
|
||||
|
||||
'use strict';
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
// Task configuration.
|
||||
jshint: {
|
||||
options: {
|
||||
browser: true,
|
||||
curly: true,
|
||||
freeze: true,
|
||||
bitwise: true,
|
||||
eqeqeq: true,
|
||||
strict: true,
|
||||
immed: true,
|
||||
latedef: false,
|
||||
newcap: false,
|
||||
smarttabs: true,
|
||||
noarg: true,
|
||||
devel: true,
|
||||
sub: true,
|
||||
undef: true,
|
||||
unused: false,
|
||||
boss: true,
|
||||
eqnull: true,
|
||||
globals: {
|
||||
jQuery: true,
|
||||
angular: true,
|
||||
$: true,
|
||||
}
|
||||
},
|
||||
gruntfile: {
|
||||
src: 'Gruntfile.js'
|
||||
},
|
||||
scripts: {
|
||||
src: ['resources/**/**/*.js']
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
gruntfile: {
|
||||
files: '<%= jshint.gruntfile.src %>',
|
||||
tasks: ['jshint:gruntfile']
|
||||
},
|
||||
scripts: {
|
||||
files: '<%= jshint.scripts.src %>',
|
||||
tasks: ['jshint:scripts']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// These plugins provide necessary tasks.
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('default', ['jshint']);
|
||||
|
||||
};
|
10
static/ng/package.json
Normal file
10
static/ng/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-contrib-watch": "~0.6.1"
|
||||
}
|
||||
}
|
BIN
static/ng/resources/.DS_Store
vendored
Normal file
BIN
static/ng/resources/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
static/ng/resources/css/.DS_Store
vendored
Normal file
BIN
static/ng/resources/css/.DS_Store
vendored
Normal file
Binary file not shown.
0
static/ng/resources/css/account-settings.css
Normal file
0
static/ng/resources/css/account-settings.css
Normal file
17
static/ng/resources/css/admin-options.css
Normal file
17
static/ng/resources/css/admin-options.css
Normal file
@ -0,0 +1,17 @@
|
||||
.switch-pane-admin-options {
|
||||
display: inline;
|
||||
width: 245px;
|
||||
float: right;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.switch-pane-admin-options a, .switch-pane-admin-options span {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.switch-pane-admin-options li .active {
|
||||
border-bottom: 2px solid rgb(0, 84, 190);
|
||||
font-weight: bold;
|
||||
}
|
14
static/ng/resources/css/dashboard.css
Normal file
14
static/ng/resources/css/dashboard.css
Normal file
@ -0,0 +1,14 @@
|
||||
.up-section .up-table-pane {
|
||||
overflow-y: scroll;
|
||||
height: 180px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.up-section .dl-horizontal dt{
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.up-section .dl-horizontal dt {
|
||||
text-align: left;
|
||||
}
|
||||
|
3
static/ng/resources/css/destination.css
Normal file
3
static/ng/resources/css/destination.css
Normal file
@ -0,0 +1,3 @@
|
||||
.create-destination {
|
||||
height: 235px;
|
||||
}
|
26
static/ng/resources/css/footer.css
Normal file
26
static/ng/resources/css/footer.css
Normal file
@ -0,0 +1,26 @@
|
||||
.footer-absolute {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer-static {
|
||||
position: static;
|
||||
|
||||
}
|
||||
|
||||
.footer{
|
||||
width: 100%;
|
||||
clear: both;
|
||||
background-color: #A8A8A8;
|
||||
height: 44px;
|
||||
}
|
||||
.footer p {
|
||||
padding-top: 8px;
|
||||
color: #FFFFFF;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 385px;
|
||||
}
|
115
static/ng/resources/css/header.css
Normal file
115
static/ng/resources/css/header.css
Normal file
@ -0,0 +1,115 @@
|
||||
.navbar-custom {
|
||||
background-image: none;
|
||||
background-color: #057ac9;
|
||||
height: 66px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
nav .container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
nav .container-custom {
|
||||
min-width: 1024px;
|
||||
}
|
||||
|
||||
.navbar-custom .navbar-nav > li > a,
|
||||
.navbar-custom .navbar-nav > li > a:hover,
|
||||
.navbar-custom .navbar-nav > li > a:focus,
|
||||
.navbar-custom .navbar-nav > li > a:active {
|
||||
color: white; /*Change active text color here*/
|
||||
}
|
||||
|
||||
.navbar-brand > img {
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
.navbar-form {
|
||||
margin-top: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
background: url("/static/ng/resources/img/magnitude-glass.jpg") no-repeat 97% 6px;
|
||||
background-size: 1.5em;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-custom li {
|
||||
float: left;
|
||||
padding: 10px 0 0 0;
|
||||
margin-right: 12px;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-custom li a {
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-custom .active {
|
||||
border-bottom: 3px solid #EFEFEF;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.dropdown .btn-link:hover,
|
||||
.dropdown .btn-link:visited,
|
||||
.dropdown .btn-link:link {
|
||||
display:inline-block;
|
||||
text-decoration: none;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.dropdown-submenu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-submenu>.dropdown-menu {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
margin-top: -6px;
|
||||
margin-left: -1px;
|
||||
-webkit-border-radius: 0 6px 6px 6px;
|
||||
-moz-border-radius: 0 6px 6px;
|
||||
border-radius: 0 6px 6px 6px;
|
||||
}
|
||||
|
||||
.dropdown-submenu:hover>.dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-submenu>a:after {
|
||||
display: block;
|
||||
content: " ";
|
||||
float: right;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-left-color: #ccc;
|
||||
margin-top: 5px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
.dropdown-submenu:hover>a:after {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
|
||||
.dropdown-submenu.pull-left {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.dropdown-submenu.pull-left>.dropdown-menu {
|
||||
left: -100%;
|
||||
margin-left: 10px;
|
||||
-webkit-border-radius: 6px 0 6px 6px;
|
||||
-moz-border-radius: 6px 0 6px 6px;
|
||||
border-radius: 6px 0 6px 6px;
|
||||
}
|
113
static/ng/resources/css/index.css
Normal file
113
static/ng/resources/css/index.css
Normal file
@ -0,0 +1,113 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.has-logged-in {
|
||||
position: relative;
|
||||
top: 50px;
|
||||
}
|
||||
|
||||
.has-logged-in h4 {
|
||||
float: left;
|
||||
width: 100%;
|
||||
line-height: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.has-logged-in .last-logged-in-time {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.has-logged-in .control-button {
|
||||
height: 2em;
|
||||
width: 100%;
|
||||
padding-right: 10%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.container-fluid-custom {
|
||||
background-color: #EFEFEF;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.up-section {
|
||||
position: relative;
|
||||
padding: 15px 15px 15px;
|
||||
margin: 20px -10px 0 0;
|
||||
background-color: #FFFFFF;
|
||||
height: 277px;
|
||||
}
|
||||
|
||||
.up-section h4 label {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
padding: 2px;
|
||||
box-shadow: none;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.down-section {
|
||||
position: relative;
|
||||
padding: 15px 15px 15px;
|
||||
margin: 20px -10px 0 0;
|
||||
background-color: #FFFFFF;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.down-section ul {
|
||||
padding: 0;
|
||||
margin-left: 30px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.long-line {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.down-table-pane {
|
||||
overflow-y: auto;
|
||||
height: 260px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding-bottom: 10px;
|
||||
margin: 10px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.underlined {
|
||||
border-bottom: 2px solid #EFEFEF;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.display-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title-color {
|
||||
color: #057ac9;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
margin: 0 20px 0 20px;
|
||||
}
|
64
static/ng/resources/css/project.css
Normal file
64
static/ng/resources/css/project.css
Normal file
@ -0,0 +1,64 @@
|
||||
.container-custom {
|
||||
position: relative;
|
||||
height: 680px;
|
||||
}
|
||||
|
||||
.extend-height {
|
||||
height: 100%;
|
||||
min-height: 1px;
|
||||
max-height: 680px;
|
||||
min-width: 1024px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
background-color: #FFFFFF;
|
||||
height: 660px;
|
||||
width: 100%;
|
||||
min-height: 1px;
|
||||
max-height: 660px;
|
||||
}
|
||||
|
||||
.search-pane {
|
||||
margin: 0 10px 0 10px;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
|
||||
.table>tbody>tr>td, .table>tbody>tr>th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.gutter {
|
||||
margin: 0 1em 0 1em;
|
||||
}
|
||||
|
||||
.project-pane {
|
||||
margin: 0 10px 0 10px;
|
||||
}
|
||||
|
||||
.pane {
|
||||
height: auto;
|
||||
min-height: 1px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sub-pane {
|
||||
margin: 15px;
|
||||
height: 380px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.well-custom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
79
static/ng/resources/css/replication.css
Normal file
79
static/ng/resources/css/replication.css
Normal file
@ -0,0 +1,79 @@
|
||||
.create-policy {
|
||||
height: 535px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-group-custom {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.h4-custom {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.h4-custom-down {
|
||||
display: inline-block;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.hr-line {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-control-custom {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.pane-split {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#upon-pane {
|
||||
margin-top: 10px;
|
||||
height: 270px;
|
||||
min-height: 100px;
|
||||
max-height: 270px;
|
||||
}
|
||||
|
||||
#upon-pane table>tbody>tr {
|
||||
cursor: all-scroll;
|
||||
}
|
||||
#down-pane {
|
||||
height: 80px;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.sub-pane-split {
|
||||
margin: 15px;
|
||||
height: auto;
|
||||
min-height: 50px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.well-split {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.split-handle {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
cursor: ns-resize;
|
||||
color: #C0C0C0;
|
||||
}
|
||||
|
||||
.color-success {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.color-danger {
|
||||
color: #d9534f
|
||||
}
|
||||
|
||||
.color-warning {
|
||||
color: #f0ad4e;
|
||||
}
|
129
static/ng/resources/css/repository.css
Normal file
129
static/ng/resources/css/repository.css
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
.panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pane-container {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pane-container .list-group-item {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.item-line {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.switch-pane {
|
||||
padding: 10px 15px 0 15px;
|
||||
margin: 0 10px 10px 10px;
|
||||
height: 3em;
|
||||
background-color: rgb(231,244,254);
|
||||
}
|
||||
|
||||
.switch-pane-projects {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.switch-pane-tabs {
|
||||
width: 265px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.switch-pane-tabs a,.switch-pane-tabs span {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.switch-pane-tabs li .active {
|
||||
border-bottom: 2px solid rgb(0, 84, 190);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.switch-pane-drop-down {
|
||||
display: block;
|
||||
position: absolute;
|
||||
margin: -10px 0 0 10px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.search-projects {
|
||||
padding: 15px 10px 10px;
|
||||
}
|
||||
|
||||
.project-list {
|
||||
height: 440px;
|
||||
}
|
||||
|
||||
.project-selected {
|
||||
margin-left: -1.2em;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
text-align: left;
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
.panel-group {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.repository-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.repository-table th, .repository-table td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.each-tab-pane {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.datetime-picker-title {
|
||||
float: left;
|
||||
line-height: 2.5em;
|
||||
margin: 0 0.5em 0 0;
|
||||
}
|
||||
|
||||
.input-group .form-control {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.well {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.popover{
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.popover-header {
|
||||
padding:8px 14px;
|
||||
background-color:#f7f7f7;
|
||||
border-bottom:1px solid #ebebeb;
|
||||
-webkit-border-radius:5px 5px 0 0;
|
||||
-moz-border-radius:5px 5px 0 0;
|
||||
border-radius:5px 5px 0 0;
|
||||
}
|
||||
|
||||
.popover-title {
|
||||
height: 2.5em;
|
||||
padding: 8px 14px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
9
static/ng/resources/css/search.css
Normal file
9
static/ng/resources/css/search.css
Normal file
@ -0,0 +1,9 @@
|
||||
.search-result {
|
||||
min-height: 200px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.search-result li {
|
||||
margin-bottom: 15px;
|
||||
}
|
48
static/ng/resources/css/sign-up.css
Normal file
48
static/ng/resources/css/sign-up.css
Normal file
@ -0,0 +1,48 @@
|
||||
.main-title {
|
||||
margin-top: 20px;
|
||||
margin-left: 180px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
width: 60%;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.form-horizontal .control-label {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.form-horizontal .form-group {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
||||
.small-size-fonts {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.asterisk {
|
||||
display: inline-block;
|
||||
margin-left: -25px;
|
||||
padding-top: 8px;
|
||||
color: red;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.css-form input.ng-invalid.ng-touched {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
width: 100%;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
BIN
static/ng/resources/img/.DS_Store
vendored
Normal file
BIN
static/ng/resources/img/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
static/ng/resources/img/Harbor_Logo_rec.png
Normal file
BIN
static/ng/resources/img/Harbor_Logo_rec.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
static/ng/resources/img/Step1.png
Normal file
BIN
static/ng/resources/img/Step1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
BIN
static/ng/resources/img/Step2.png
Normal file
BIN
static/ng/resources/img/Step2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
BIN
static/ng/resources/img/Step3.png
Normal file
BIN
static/ng/resources/img/Step3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
BIN
static/ng/resources/img/magnitude-glass.jpg
Normal file
BIN
static/ng/resources/img/magnitude-glass.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
BIN
static/ng/resources/img/magnitude-glass.png
Normal file
BIN
static/ng/resources/img/magnitude-glass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,21 @@
|
||||
<div id="retrievePane" class="switch-pane-drop-down" ng-show="vm.isOpen">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="form-inline search-projects">
|
||||
<div class="input-group">
|
||||
<input type="text" id="retrieveFilter" class="form-control search-icon" placeholder="" ng-model="vm.filterInput" size="30">
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="page-header">//vm.projectType | tr//: <span class="badge">//vm.resultCount//</span></h5>
|
||||
<div class="project-list pane-container">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" ng-repeat="item in vm.projects | name: vm.filterInput: 'Name'" ng-click="vm.selectItem(item)">
|
||||
<span ng-show="item.ProjectId == vm.selectedId" class="glyphicon glyphicon-ok project-selected"></span> <a href="#/repositories?project_id=//item.project_id//">//item.name//</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,152 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.details')
|
||||
.directive('retrieveProjects', retrieveProjects);
|
||||
|
||||
RetrieveProjectsController.$inject = ['$scope', 'nameFilter', '$filter', 'ListProjectService', '$location', 'getParameterByName', 'CurrentProjectMemberService'];
|
||||
|
||||
function RetrieveProjectsController($scope, nameFilter, $filter, ListProjectService, $location, getParameterByName, CurrentProjectMemberService) {
|
||||
var vm = this;
|
||||
|
||||
vm.projectName = '';
|
||||
vm.isOpen = false;
|
||||
|
||||
if(getParameterByName('is_public', $location.absUrl())) {
|
||||
vm.isPublic = getParameterByName('is_public', $location.absUrl()) === 'true' ? 1 : 0;
|
||||
vm.publicity = (vm.isPublic === 1) ? true : false;
|
||||
}
|
||||
|
||||
vm.retrieve = retrieve;
|
||||
vm.filterInput = "";
|
||||
vm.selectItem = selectItem;
|
||||
vm.checkProjectMember = checkProjectMember;
|
||||
|
||||
$scope.$watch('vm.selectedProject', function(current, origin) {
|
||||
if(current) {
|
||||
vm.selectedId = current.project_id;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('vm.publicity', function(current, origin) {
|
||||
vm.publicity = current ? true : false;
|
||||
vm.isPublic = vm.publicity ? 1 : 0;
|
||||
vm.projectType = (vm.isPublic === 1) ? 'public_projects' : 'my_projects';
|
||||
vm.retrieve();
|
||||
});
|
||||
|
||||
function retrieve() {
|
||||
ListProjectService(vm.projectName, vm.isPublic)
|
||||
.success(getProjectSuccess)
|
||||
.error(getProjectFailed);
|
||||
}
|
||||
|
||||
function getProjectSuccess(data, status) {
|
||||
vm.projects = data;
|
||||
|
||||
if(!angular.isDefined(vm.projects)) {
|
||||
vm.isPublic = 1;
|
||||
vm.publicity = 1;
|
||||
vm.projectType = 'public_projects';
|
||||
console.log('vm.projects is undefined, load public projects.');
|
||||
}
|
||||
|
||||
vm.selectedProject = vm.projects[0];
|
||||
|
||||
if(getParameterByName('project_id', $location.absUrl())){
|
||||
angular.forEach(vm.projects, function(value, index) {
|
||||
if(value['project_id'] === Number(getParameterByName('project_id', $location.absUrl()))) {
|
||||
vm.selectedProject = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$location.search('project_id', vm.selectedProject.project_id);
|
||||
vm.checkProjectMember(vm.selectedProject.project_id);
|
||||
|
||||
vm.resultCount = vm.projects.length;
|
||||
|
||||
$scope.$watch('vm.filterInput', function(current, origin) {
|
||||
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'Name').length;
|
||||
});
|
||||
}
|
||||
|
||||
function getProjectFailed(response) {
|
||||
console.log('Failed to list projects:' + response);
|
||||
}
|
||||
|
||||
function selectItem(item) {
|
||||
vm.selectedProject = item;
|
||||
$location.search('project_id', vm.selectedProject.project_id);
|
||||
}
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function(e) {
|
||||
var projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.checkProjectMember(projectId);
|
||||
vm.isOpen = false;
|
||||
});
|
||||
|
||||
function checkProjectMember(projectId) {
|
||||
CurrentProjectMemberService(projectId)
|
||||
.success(getCurrentProjectMemberSuccess)
|
||||
.error(getCurrentProjectMemberFailed);
|
||||
}
|
||||
|
||||
function getCurrentProjectMemberSuccess(data, status) {
|
||||
console.log('Successful get current project member:' + status);
|
||||
vm.isProjectMember = true;
|
||||
}
|
||||
|
||||
function getCurrentProjectMemberFailed(data, status) {
|
||||
console.log('Use has no member for current project:' + status);
|
||||
vm.isProjectMember = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function retrieveProjects() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/details/retrieve-projects.directive.html',
|
||||
scope: {
|
||||
'isOpen': '=',
|
||||
'selectedProject': '=',
|
||||
'publicity': '=',
|
||||
'isProjectMember': '='
|
||||
},
|
||||
link: link,
|
||||
controller: RetrieveProjectsController,
|
||||
bindToController: true,
|
||||
controllerAs: 'vm'
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
$(document).on('click', clickHandler);
|
||||
|
||||
function clickHandler(e) {
|
||||
$('[data-toggle="popover"]').each(function () {
|
||||
if (!$(this).is(e.target) &&
|
||||
$(this).has(e.target).length === 0 &&
|
||||
$('.popover').has(e.target).length === 0) {
|
||||
$(this).parent().popover('hide');
|
||||
}
|
||||
});
|
||||
var targetId = $(e.target).attr('id');
|
||||
if(targetId === 'switchPane' ||
|
||||
targetId === 'retrievePane' ||
|
||||
targetId === 'retrieveFilter') {
|
||||
return;
|
||||
}else{
|
||||
ctrl.isOpen = false;
|
||||
scope.$apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,5 @@
|
||||
<div class="switch-pane-projects" ng-switch="vm.isOpen">
|
||||
<a id="switchPane" href="javascript:void(0);" ng-click="vm.switchPane()" >//vm.projectName//</a>
|
||||
<span ng-switch-default class="glyphicon glyphicon-triangle-right" style="font-size: 12px;"></span>
|
||||
<span ng-switch-when="true" class="glyphicon glyphicon-triangle-bottom" style="font-size: 12px;"></span>
|
||||
</div>
|
@ -0,0 +1,50 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.details')
|
||||
.directive('switchPaneProjects', switchPaneProjects);
|
||||
|
||||
SwitchPaneProjectsController.$inject = ['$scope'];
|
||||
|
||||
function SwitchPaneProjectsController($scope) {
|
||||
var vm = this;
|
||||
|
||||
$scope.$watch('vm.selectedProject', function(current, origin) {
|
||||
if(current){
|
||||
vm.projectName = current.name;
|
||||
vm.selectedProject = current;
|
||||
}
|
||||
});
|
||||
|
||||
vm.switchPane = switchPane;
|
||||
|
||||
function switchPane() {
|
||||
if(vm.isOpen) {
|
||||
vm.isOpen = false;
|
||||
}else{
|
||||
vm.isOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function switchPaneProjects() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/details/switch-pane-projects.directive.html',
|
||||
scope: {
|
||||
'isOpen': '=',
|
||||
'selectedProject': '='
|
||||
},
|
||||
controller: SwitchPaneProjectsController,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,53 @@
|
||||
<div class="well panel-group">
|
||||
<div class="row">
|
||||
<div class="col-xs-10 col-md-10">
|
||||
<form class="form">
|
||||
<div class="form-group">
|
||||
<label for="">// 'operation' | tr //:</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" ng-model="vm.opAll" ng-checked="vm.opCreate && vm.opPull && vm.opPush && vm.opDelete && vm.opOthers" ng-click="vm.checkOperation({checked: 'all'})"> // 'all' | tr //
|
||||
<input type="checkbox" ng-checked="vm.opCreate" ng-model="vm.opCreate" ng-click="vm.checkOperation({checked: 'create'})"> Create
|
||||
<input type="checkbox" ng-checked="vm.opPull" ng-model="vm.opPull" ng-click="vm.checkOperation({checked: 'pull'})"> Pull
|
||||
<input type="checkbox" ng-checked="vm.opPush" ng-model="vm.opPush" ng-click="vm.checkOperation({checked: 'push'})"> Push
|
||||
<input type="checkbox" ng-checked="vm.opDelete" ng-model="vm.opDelete" ng-click="vm.checkOperation({checked: 'delete'})"> Delete
|
||||
<input type="checkbox" ng-checked="vm.opOthers" ng-model="vm.opOthers" ng-click="vm.checkOperation({checked: 'others'})"> // 'others' | tr //
|
||||
<input type="text" ng-model="vm.others" size="10">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="">// 'duration' | tr //:</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group inline-block col-md-5">
|
||||
<span class="datetime-picker-title">// 'from' | tr //:</span>
|
||||
<!--date-picker picked-date="vm.fromDate"></date-picker-->
|
||||
<div class="input-group datetimepicker">
|
||||
<input id="fromDatePicker" class="form-control" type="text" readonly="readonly" ng-model="vm.fromDate" ng-change="vm.pickUp({key:'fromDate', value: vm.fromDate})">
|
||||
<span class="input-group-addon">
|
||||
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group inline-block col-md-5">
|
||||
<span class="datetime-picker-title">// 'to' | tr //:</span>
|
||||
<div class="input-group datetimepicker">
|
||||
<input id="toDatePicker" class="form-control" type="text" readonly="readonly" ng-model="vm.toDate" ng-change="vm.pickUp({key:'toDate', value: vm.toDate})">
|
||||
<span class="input-group-addon">
|
||||
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2">
|
||||
<form>
|
||||
<div class="form-group" style="margin-top: 40%;">
|
||||
<button type="button" class="btn btn-primary" ng-click="vm.search({op: vm.op})">// 'search' | tr //</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,107 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.log')
|
||||
.directive('advancedSearch', advancedSearch);
|
||||
|
||||
AdvancedSearchController.$inject = ['$scope', 'ListLogService'];
|
||||
|
||||
function AdvancedSearchController($scope, ListLogService) {
|
||||
var vm = this;
|
||||
|
||||
vm.checkOperation = checkOperation;
|
||||
|
||||
vm.opAll = true;
|
||||
vm.opCreate = true;
|
||||
vm.opPull = true;
|
||||
vm.opPush = true;
|
||||
vm.opDelete = true;
|
||||
vm.opOthers = true;
|
||||
vm.others = "";
|
||||
|
||||
vm.op = [];
|
||||
vm.op.push('all');
|
||||
function checkOperation(e) {
|
||||
if(e.checked === 'all') {
|
||||
vm.opCreate = vm.opAll;
|
||||
vm.opPull = vm.opAll;
|
||||
vm.opPush = vm.opAll;
|
||||
vm.opDelete = vm.opAll;
|
||||
vm.opOthers = vm.opAll;
|
||||
}else {
|
||||
vm.opAll = false;
|
||||
}
|
||||
|
||||
vm.op = [];
|
||||
|
||||
if(vm.opCreate) {
|
||||
vm.op.push('create');
|
||||
}
|
||||
if(vm.opPull) {
|
||||
vm.op.push('pull');
|
||||
}
|
||||
if(vm.opPush) {
|
||||
vm.op.push('push');
|
||||
}
|
||||
if(vm.opDelete) {
|
||||
vm.op.push('delete');
|
||||
}
|
||||
if(vm.opOthers && vm.others !== "") {
|
||||
vm.op.push(vm.others);
|
||||
}
|
||||
}
|
||||
|
||||
vm.pickUp = pickUp;
|
||||
|
||||
function pickUp(e) {
|
||||
switch(e.key){
|
||||
case 'fromDate':
|
||||
vm.fromDate = e.value;
|
||||
break;
|
||||
case 'toDate':
|
||||
vm.toDate = e.value;
|
||||
break;
|
||||
}
|
||||
$scope.$apply();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function advancedSearch() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/log/advanced-search.directive.html',
|
||||
'scope': {
|
||||
'isOpen': '=',
|
||||
'op': '=',
|
||||
'others': '=',
|
||||
'fromDate': '=',
|
||||
'toDate': '=',
|
||||
'search': '&'
|
||||
},
|
||||
'link': link,
|
||||
'controller': AdvancedSearchController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
element.find('.datetimepicker').datetimepicker({
|
||||
locale: 'en-US',
|
||||
ignoreReadonly: true,
|
||||
format: 'L',
|
||||
showClear: true
|
||||
});
|
||||
element.find('#fromDatePicker').on('blur', function(){
|
||||
ctrl.pickUp({'key': 'fromDate', 'value': $(this).val()});
|
||||
});
|
||||
element.find('#toDatePicker').on('blur', function(){
|
||||
ctrl.pickUp({'key': 'toDate', 'value': $(this).val()});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,30 @@
|
||||
<div class="tab-pane" id="logs">
|
||||
<div class="col-xs-12 col-md-12 each-tab-pane">
|
||||
<div class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.username" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.search({op: vm.op, username: vm.username})"><span class="glyphicon glyphicon-search"></span></button>
|
||||
</span>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-link" type="button" ng-click="vm.showAdvancedSearch()">// 'advanced_search' | tr //</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane">
|
||||
<advanced-search ng-show="vm.isOpen" is-open="vm.isOpen" op="vm.op" others="vm.others" search='vm.search({op: vm.op, username: vm.username})' from-date="vm.fromDate" to-date="vm.toDate"></advanced-search>
|
||||
<div class="sub-pane">
|
||||
<table class="table table-pane">
|
||||
<thead>
|
||||
<th>// 'username' | tr //</th><th>// 'repository_name' | tr //</th><th>// 'operation' | tr //</th><th>// 'timestamp' | tr //</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="log in vm.logs">
|
||||
<td>//log.username//</td><td>//log.repo_name//</td><td>//log.operation//</td><td>//log.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
115
static/ng/resources/js/components/log/list-log.directive.js
Normal file
115
static/ng/resources/js/components/log/list-log.directive.js
Normal file
@ -0,0 +1,115 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.log')
|
||||
.directive('listLog', listLog);
|
||||
|
||||
ListLogController.$inject = ['$scope','ListLogService', 'getParameterByName', '$location'];
|
||||
|
||||
function ListLogController($scope, ListLogService, getParameterByName, $location) {
|
||||
var vm = this;
|
||||
vm.isOpen = false;
|
||||
|
||||
vm.beginTimestamp = 0;
|
||||
vm.endTimestamp = 0;
|
||||
vm.keywords = "";
|
||||
vm.username = "";
|
||||
|
||||
vm.op = [];
|
||||
|
||||
vm.search = search;
|
||||
vm.showAdvancedSearch = showAdvancedSearch;
|
||||
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.queryParams = {
|
||||
'beginTimestamp' : vm.beginTimestamp,
|
||||
'endTimestamp' : vm.endTimestamp,
|
||||
'keywords' : vm.keywords,
|
||||
'projectId': vm.projectId,
|
||||
'username' : vm.username
|
||||
};
|
||||
retrieve(vm.queryParams);
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function() {
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.queryParams = {
|
||||
'beginTimestamp' : vm.beginTimestamp,
|
||||
'endTimestamp' : vm.endTimestamp,
|
||||
'keywords' : vm.keywords,
|
||||
'projectId': vm.projectId,
|
||||
'username' : vm.username
|
||||
};
|
||||
retrieve(vm.queryParams);
|
||||
});
|
||||
|
||||
function search(e) {
|
||||
if(e.op[0] === 'all') {
|
||||
vm.queryParams.keywords = '';
|
||||
}else {
|
||||
vm.queryParams.keywords = e.op.join('/') ;
|
||||
}
|
||||
vm.queryParams.username = e.username;
|
||||
|
||||
vm.queryParams.beginTimestamp = toUTCSeconds(vm.fromDate, 0, 0, 0);
|
||||
vm.queryParams.endTimestamp = toUTCSeconds(vm.toDate, 23, 59, 59);
|
||||
|
||||
retrieve(vm.queryParams);
|
||||
}
|
||||
|
||||
function showAdvancedSearch() {
|
||||
if(vm.isOpen){
|
||||
vm.isOpen = false;
|
||||
}else{
|
||||
vm.isOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
function retrieve(queryParams) {
|
||||
ListLogService(queryParams)
|
||||
.then(listLogComplete)
|
||||
.catch(listLogFailed);
|
||||
}
|
||||
|
||||
function listLogComplete(response) {
|
||||
vm.logs = response.data;
|
||||
}
|
||||
function listLogFailed(e){
|
||||
console.log('listLogFailed:' + e);
|
||||
}
|
||||
|
||||
function toUTCSeconds(date, hour, min, sec) {
|
||||
if(date === "") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var t = new Date(date);
|
||||
t.setHours(hour);
|
||||
t.setMinutes(min);
|
||||
t.setSeconds(sec);
|
||||
var utcTime = new Date(t.getUTCFullYear(),
|
||||
t.getUTCMonth(),
|
||||
t.getUTCDate(),
|
||||
t.getUTCHours(),
|
||||
t.getUTCMinutes(),
|
||||
t.getUTCSeconds());
|
||||
return utcTime.getTime() / 1000;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function listLog() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/log/list-log.directive.html',
|
||||
scope: true,
|
||||
controller: ListLogController,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
9
static/ng/resources/js/components/log/log.config.js
Normal file
9
static/ng/resources/js/components/log/log.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.log');
|
||||
|
||||
|
||||
})();
|
10
static/ng/resources/js/components/log/log.module.js
Normal file
10
static/ng/resources/js/components/log/log.module.js
Normal file
@ -0,0 +1,10 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.log', [
|
||||
'harbor.services.log'
|
||||
]);
|
||||
|
||||
})();
|
@ -0,0 +1,16 @@
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">//vm.title//</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="btnOk">// 'ok' | tr //</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
@ -0,0 +1,58 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.modal.dialog')
|
||||
.directive('modalDialog', modalDialog);
|
||||
|
||||
ModalDialogController.$inject = ['$scope'];
|
||||
|
||||
function ModalDialogController($scope) {
|
||||
var vm = this;
|
||||
}
|
||||
|
||||
function modalDialog() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/modal-dialog/modal-dialog.directive.html',
|
||||
'link': link,
|
||||
'scope': {
|
||||
'contentType': '@',
|
||||
'title': '@',
|
||||
'message': '@',
|
||||
'action': '&'
|
||||
},
|
||||
'controller': ModalDialogController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
console.log('Received contentType in modal:' + ctrl.contentType);
|
||||
|
||||
scope.$watch('vm.message', function(current) {
|
||||
if(current) {
|
||||
switch(ctrl.contentType) {
|
||||
case 'text/html':
|
||||
element.find('.modal-body').html(current); break;
|
||||
case 'text/plain':
|
||||
element.find('.modal-body').text(current); break;
|
||||
default:
|
||||
element.find('.modal-body').text(current); break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
element.find('#btnOk').on('click', clickHandler);
|
||||
|
||||
function clickHandler(e) {
|
||||
ctrl.action();
|
||||
element.find('#myModal').modal('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,8 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.modal.dialog', []);
|
||||
|
||||
})();
|
@ -0,0 +1,54 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.optional.menu')
|
||||
.directive('optionalMenu', optionalMenu);
|
||||
|
||||
OptionalMenuController.$inject = ['$window', 'I18nService', 'LogOutService', 'currentUser', '$timeout'];
|
||||
|
||||
function OptionalMenuController($window, I18nService, LogOutService, currentUser, $timeout) {
|
||||
var vm = this;
|
||||
|
||||
vm.currentLanguage = I18nService().getCurrentLanguage();
|
||||
vm.languageName = I18nService().getLanguageName(vm.currentLanguage);
|
||||
|
||||
console.log('current language:' + I18nService().getCurrentLanguage());
|
||||
|
||||
vm.user = currentUser.get();
|
||||
vm.setLanguage = setLanguage;
|
||||
vm.logOut = logOut;
|
||||
|
||||
function setLanguage(language) {
|
||||
I18nService().setCurrentLanguage(language);
|
||||
$window.location.href = '/ng/language?lang=' + language;
|
||||
}
|
||||
function logOut() {
|
||||
LogOutService()
|
||||
.success(logOutSuccess)
|
||||
.error(logOutFailed);
|
||||
}
|
||||
function logOutSuccess(data, status) {
|
||||
currentUser.unset();
|
||||
I18nService().unset();
|
||||
$window.location.href= '/ng';
|
||||
}
|
||||
function logOutFailed(data, status) {
|
||||
console.log('Failed to log out:' + data);
|
||||
}
|
||||
}
|
||||
|
||||
function optionalMenu() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/ng/optional_menu',
|
||||
'scope': true,
|
||||
'controller': OptionalMenuController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,11 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.optional.menu', [
|
||||
'harbor.services.user',
|
||||
'harbor.services.i18n'
|
||||
]);
|
||||
|
||||
})();
|
@ -0,0 +1,33 @@
|
||||
<div class="well panel-group">
|
||||
<div class="row">
|
||||
<div class="col-xs-10 col-md-10">
|
||||
<form name="form" novalidate ng-submit="form.$valid">
|
||||
<div class="form-group col-md-6">
|
||||
<input type="text" class="form-control" id="addUsername" placeholder="// 'username' | tr //" ng-model="pm.username" name="uUsername" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" required>
|
||||
<div class="error-message">
|
||||
<div ng-messages="form.$dirty && form.uUsername.$error">
|
||||
<span ng-message="required">// 'username_is_required' | tr //</span>
|
||||
</div>
|
||||
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-inline clearfix">
|
||||
<div class="form-group">
|
||||
<label for="roleIdList">// 'role' | tr //:</label>
|
||||
<span ng-repeat="role in vm.roles">
|
||||
<input type="radio" name="role" ng-model="vm.optRole" value="//role.id//"> //role.name//
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-primary" id="btnSave" ng-disabled="form.$invalid" ng-click="vm.save(pm)">// 'save' | tr //</button>
|
||||
<button type="button" class="btn btn-default" id="btnCancel" ng-click="vm.cancel(form)">// 'cancel' | tr //</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,82 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project.member')
|
||||
.directive('addProjectMember', addProjectMember);
|
||||
|
||||
AddProjectMemberController.$inject = ['$scope', 'roles', 'AddProjectMemberService'];
|
||||
|
||||
function AddProjectMemberController($scope, roles, AddProjectMemberService) {
|
||||
var vm = this;
|
||||
vm.username = '';
|
||||
vm.roles = roles();
|
||||
vm.optRole = 1;
|
||||
|
||||
vm.reset = reset;
|
||||
vm.save = save;
|
||||
vm.cancel = cancel;
|
||||
|
||||
vm.hasError = false;
|
||||
vm.errorMessage = '';
|
||||
|
||||
function reset() {
|
||||
vm.hasError = false;
|
||||
vm.errorMessage = '';
|
||||
}
|
||||
|
||||
function save(pm) {
|
||||
if(pm && angular.isDefined(pm.username)) {
|
||||
AddProjectMemberService(vm.projectId, vm.optRole, pm.username)
|
||||
.success(addProjectMemberComplete)
|
||||
.error(addProjectMemberFailed);
|
||||
}
|
||||
}
|
||||
|
||||
function cancel(form) {
|
||||
if(form) {
|
||||
form.$setPristine();
|
||||
}
|
||||
vm.isOpen = false;
|
||||
vm.username = '';
|
||||
vm.optRole = 1;
|
||||
}
|
||||
|
||||
function addProjectMemberComplete(data, status, header) {
|
||||
console.log('addProjectMemberComplete: status:' + status + ', data:' + data);
|
||||
vm.reload();
|
||||
}
|
||||
|
||||
function addProjectMemberFailed(data, status, headers) {
|
||||
if(status === 409) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'username_already_exist';
|
||||
}
|
||||
if(status === 404) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'username_does_not_exist';
|
||||
}
|
||||
console.log('addProjectMemberFailed: status:' + status + ', data:' + data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function addProjectMember() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project-member/add-project-member.directive.html',
|
||||
'scope': {
|
||||
'projectId': '@',
|
||||
'isOpen': '=',
|
||||
'reload': '&'
|
||||
},
|
||||
'controller': AddProjectMemberController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,11 @@
|
||||
<td style="padding: 15px;">//vm.username//</td>
|
||||
<td><switch-role roles="vm.roles" edit-mode="vm.editMode" user-id="vm.userId" role-name="vm.roleName"></switch-role></td>
|
||||
<td>
|
||||
<a ng-show="vm.userId != vm.currentUserId" href="javascript:void(0);" ng-click="vm.updateProjectMember({projectId: vm.projectId, userId: vm.userId, roleId: vm.roleId})">
|
||||
<span ng-if="!vm.editMode" class="glyphicon glyphicon-pencil" title="Edit"></span><span ng-if="vm.editMode" class="glyphicon glyphicon-ok" title="Confirm">
|
||||
</a>
|
||||
<a ng-show="vm.userId != vm.currentUserId" href="javascript:void(0);" ng-click="vm.cancelUpdate()" title="Cancel">
|
||||
<span ng-if="vm.editMode" class="glyphicon glyphicon-repeat"></span>
|
||||
</a>
|
||||
<a ng-show="vm.userId != vm.currentUserId && !vm.editMode" href="javascript:void(0);" ng-click="vm.deleteProjectMember({projectId: vm.projectId, userId: vm.userId})" title="Delete"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</td>
|
@ -0,0 +1,83 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project.member')
|
||||
.directive('editProjectMember', editProjectMember);
|
||||
|
||||
EditProjectMemberController.$inject = ['$scope', 'roles', 'getRole','EditProjectMemberService', 'DeleteProjectMemberService'];
|
||||
|
||||
function EditProjectMemberController($scope, roles, getRole, EditProjectMemberService, DeleteProjectMemberService) {
|
||||
var vm = this;
|
||||
|
||||
vm.roles = roles();
|
||||
vm.editMode = false;
|
||||
vm.lastRoleName = vm.roleName;
|
||||
|
||||
$scope.$watch('vm.roleName', function(current, origin) {
|
||||
if(current) {
|
||||
vm.currentRole = getRole({'key': 'roleName', 'value': current});
|
||||
vm.roleId = vm.currentRole.id;
|
||||
}
|
||||
});
|
||||
|
||||
vm.updateProjectMember = updateProjectMember;
|
||||
vm.deleteProjectMember = deleteProjectMember;
|
||||
vm.cancelUpdate = cancelUpdate;
|
||||
|
||||
function updateProjectMember(e) {
|
||||
if(vm.editMode) {
|
||||
console.log('update project member, roleId:' + e.roleId);
|
||||
EditProjectMemberService(e.projectId, e.userId, e.roleId)
|
||||
.success(editProjectMemberComplete)
|
||||
.error(editProjectMemberFailed);
|
||||
}else {
|
||||
vm.editMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteProjectMember(e) {
|
||||
DeleteProjectMemberService(e.projectId, e.userId)
|
||||
.success(editProjectMemberComplete)
|
||||
.error(editProjectMemberFailed);
|
||||
}
|
||||
|
||||
function editProjectMemberComplete(data, status, headers) {
|
||||
console.log('edit project member complete: ' + status);
|
||||
vm.lastRoleName = vm.roleName;
|
||||
vm.editMode = false;
|
||||
vm.reload();
|
||||
}
|
||||
|
||||
function editProjectMemberFailed(e) {
|
||||
console.log('Failed to edit project member:' + e);
|
||||
}
|
||||
|
||||
function cancelUpdate() {
|
||||
vm.editMode = false;
|
||||
vm.roleName = vm.lastRoleName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function editProjectMember() {
|
||||
var directive = {
|
||||
'restrict': 'A',
|
||||
'templateUrl': '/static/ng/resources/js/components/project-member/edit-project-member.directive.html',
|
||||
'scope': {
|
||||
'username': '=',
|
||||
'userId': '=',
|
||||
'currentUserId': '=',
|
||||
'roleName': '=',
|
||||
'projectId': '=',
|
||||
'reload': '&'
|
||||
},
|
||||
'controller': EditProjectMemberController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,27 @@
|
||||
<div class="tab-pane" id="users">
|
||||
<div class="col-xs-12 col-md-12 each-tab-pane">
|
||||
<div class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.username" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.search({projectId: vm.projectId, username: vm.username})"><span class="glyphicon glyphicon-search"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
<button ng-if="!vm.isOpen" class="btn btn-success" type="button" ng-click="vm.addProjectMember()"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button>
|
||||
<button ng-if="vm.isOpen" class="btn btn-default" disabled="disabled" type="button"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button>
|
||||
</div>
|
||||
<div class="pane">
|
||||
<add-project-member ng-show="vm.isOpen" is-open="vm.isOpen" project-id="//vm.projectId//" reload='vm.search({projectId: vm.projectId, username: vm.username})'></add-project-member>
|
||||
<div class="sub-pane">
|
||||
<table class="table table-pane" >
|
||||
<thead>
|
||||
<th width="30%">// 'username' | tr //</th><th width="40%">// 'role' | tr //</th><th width="30%">// 'operation' | tr //</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.user_id" current-user-id="vm.user.user_id" role-name="pr.role_name" reload='vm.search({projectId: vm.projectId, username: vm.username})'></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,72 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project.member')
|
||||
.directive('listProjectMember', listProjectMember);
|
||||
|
||||
ListProjectMemberController.$inject = ['$scope', 'ListProjectMemberService', 'getParameterByName', '$location', 'currentUser'];
|
||||
|
||||
function ListProjectMemberController($scope, ListProjectMemberService, getParameterByName, $location, currentUser) {
|
||||
var vm = this;
|
||||
|
||||
vm.isOpen = false;
|
||||
vm.search = search;
|
||||
vm.addProjectMember = addProjectMember;
|
||||
vm.retrieve = retrieve;
|
||||
vm.username = '';
|
||||
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.retrieve();
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function() {
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.retrieve();
|
||||
});
|
||||
|
||||
function search(e) {
|
||||
vm.projectId = e.projectId;
|
||||
vm.username = e.username;
|
||||
retrieve();
|
||||
}
|
||||
|
||||
function addProjectMember() {
|
||||
if(vm.isOpen) {
|
||||
vm.isOpen = false;
|
||||
}else{
|
||||
vm.isOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
function retrieve() {
|
||||
ListProjectMemberService(vm.projectId, {'username': vm.username})
|
||||
.then(getProjectMemberComplete)
|
||||
.catch(getProjectMemberFailed);
|
||||
}
|
||||
|
||||
function getProjectMemberComplete(response) {
|
||||
vm.user = currentUser.get();
|
||||
vm.projectMembers = response.data;
|
||||
}
|
||||
|
||||
function getProjectMemberFailed(response) {
|
||||
console.log('Failed get project members:' + response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function listProjectMember() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/project-member/list-project-member.directive.html',
|
||||
scope: true,
|
||||
controller: ListProjectMemberController,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,35 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project.member')
|
||||
.constant('roles', roles)
|
||||
.factory('getRole', getRole);
|
||||
|
||||
function roles() {
|
||||
return [
|
||||
{'id': '0', 'name': 'N/A', 'roleName': 'n/a'},
|
||||
{'id': '1', 'name': 'Project Admin', 'roleName': 'projectAdmin'},
|
||||
{'id': '2', 'name': 'Developer', 'roleName': 'developer'},
|
||||
{'id': '3', 'name': 'Guest', 'roleName': 'guest'}
|
||||
];
|
||||
}
|
||||
|
||||
getRole.$inject = ['roles'];
|
||||
|
||||
function getRole(roles) {
|
||||
var r = roles();
|
||||
return get;
|
||||
function get(query) {
|
||||
|
||||
for(var i = 0; i < r.length; i++) {
|
||||
var role = r[i];
|
||||
if(query.key === 'roleName' && role.roleName === query.value
|
||||
|| query.key === 'roleId' && role.id === String(query.value)) {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,11 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project.member', [
|
||||
'harbor.services.project.member',
|
||||
'harbor.services.user'
|
||||
]);
|
||||
|
||||
})();
|
@ -0,0 +1,5 @@
|
||||
<ng-switch on="vm.editMode">
|
||||
<span ng-switch-default>//vm.currentRole.name//</span>
|
||||
<select ng-switch-when="true" ng-model="vm.currentRole" ng-options="role as role.name for role in vm.roles track by role.roleName" ng-click="vm.selectRole(vm.currentRole)">
|
||||
</select>
|
||||
</ng-switch>
|
@ -0,0 +1,46 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project.member')
|
||||
.directive('switchRole', switchRole);
|
||||
|
||||
SwitchRoleController.$inject = ['getRole', '$scope'];
|
||||
|
||||
function SwitchRoleController(getRole, $scope) {
|
||||
var vm = this;
|
||||
|
||||
$scope.$watch('vm.roleName', function(current,origin) {
|
||||
if(current) {
|
||||
vm.currentRole = getRole({'key': 'roleName', 'value': current});
|
||||
}
|
||||
});
|
||||
|
||||
vm.selectRole = selectRole;
|
||||
|
||||
function selectRole(role) {
|
||||
vm.currentRole = getRole({'key': 'roleName', 'value': role.roleName});
|
||||
vm.roleName = role.roleName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function switchRole() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project-member/switch-role.directive.html',
|
||||
'scope': {
|
||||
'roles': '=',
|
||||
'editMode': '=',
|
||||
'userId': '=',
|
||||
'roleName': '='
|
||||
},
|
||||
'controller' : SwitchRoleController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,28 @@
|
||||
<div class="well panel-group">
|
||||
<div class="row">
|
||||
<div class="col-xs-10 col-md-10">
|
||||
<form name="form" ng-submit="form.$valid">
|
||||
<div class="form-group col-md-6">
|
||||
<input type="text" class="form-control" placeholder="// 'project_name' | tr //" ng-model="p.projectName" name="uProjectName" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" required>
|
||||
<div class="error-message">
|
||||
<div ng-messages="form.$dirty && form.uProjectName.$error" ng-if="form.uProjectName.$touched">
|
||||
<span ng-message="required">// 'project_name_is_required' | tr //</span>
|
||||
</div>
|
||||
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" ng-model="vm.isPublic"> // 'public' | tr //
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-primary" ng-disabled="form.$invalid" ng-click="vm.addProject(p)">// 'save' | tr //</button>
|
||||
<button type="button" class="btn btn-default" ng-click="vm.cancel(form)">// 'cancel' | tr //</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,83 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project')
|
||||
.directive('addProject', addProject);
|
||||
|
||||
AddProjectController.$inject = ['AddProjectService', '$scope'];
|
||||
|
||||
function AddProjectController(AddProjectService, $scope) {
|
||||
var vm = this;
|
||||
vm.projectName = "";
|
||||
vm.isPublic = false;
|
||||
|
||||
vm.reset = reset;
|
||||
vm.addProject = addProject;
|
||||
vm.cancel = cancel;
|
||||
|
||||
vm.hasError = false;
|
||||
vm.errorMessage = '';
|
||||
|
||||
function reset() {
|
||||
vm.hasError = false;
|
||||
vm.errorMessage = '';
|
||||
}
|
||||
|
||||
function addProject(p) {
|
||||
if(p && angular.isDefined(p.projectName)) {
|
||||
AddProjectService(p.projectName, vm.isPublic)
|
||||
.success(addProjectSuccess)
|
||||
.error(addProjectFailed);
|
||||
}
|
||||
}
|
||||
|
||||
function addProjectSuccess(data, status) {
|
||||
vm.projectName = "";
|
||||
vm.isPublic = false;
|
||||
$scope.$emit('addedSuccess', true);
|
||||
}
|
||||
|
||||
function addProjectFailed(data, status) {
|
||||
if(status === 409) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'project_already_exist';
|
||||
}
|
||||
if(status === 500) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'project_name_is_invalid';
|
||||
}
|
||||
console.log('Failed to add project:' + status);
|
||||
}
|
||||
|
||||
function cancel(form){
|
||||
if(form) {
|
||||
form.$setPristine();
|
||||
}
|
||||
vm.isOpen = false;
|
||||
vm.projectName = '';
|
||||
vm.isPublic = false;
|
||||
}
|
||||
}
|
||||
|
||||
function addProject() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project/add-project.directive.html',
|
||||
'controller': AddProjectController,
|
||||
'scope' : {
|
||||
'isOpen': '='
|
||||
},
|
||||
'link': link,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,9 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project', [
|
||||
'harbor.services.project',
|
||||
'harbor.services.user'
|
||||
]);
|
||||
})();
|
@ -0,0 +1,2 @@
|
||||
<button ng-if="vm.isPublic" ng-disabled="!vm.owned" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
|
||||
<button ng-if="!vm.isPublic" ng-disabled="!vm.owned" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button>
|
@ -0,0 +1,64 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.project')
|
||||
.directive('publicityButton', publicityButton);
|
||||
|
||||
PublicityButtonController.$inject = ['ToggleProjectPublicityService'];
|
||||
|
||||
function PublicityButtonController(ToggleProjectPublicityService) {
|
||||
var vm = this;
|
||||
vm.toggle = toggle;
|
||||
|
||||
if(vm.isPublic === 1) {
|
||||
vm.isPublic = true;
|
||||
}else{
|
||||
vm.isPublic = false;
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
|
||||
if(vm.isPublic) {
|
||||
vm.isPublic = false;
|
||||
}else{
|
||||
vm.isPublic = true;
|
||||
}
|
||||
|
||||
ToggleProjectPublicityService(vm.projectId, vm.isPublic)
|
||||
.success(toggleProjectPublicitySuccess)
|
||||
.error(toggleProjectPublicityFailed);
|
||||
}
|
||||
|
||||
function toggleProjectPublicitySuccess(data, status) {
|
||||
console.log('Successful toggle project publicity.');
|
||||
}
|
||||
|
||||
function toggleProjectPublicityFailed(e) {
|
||||
console.log('Failed toggle project publicity:' + e);
|
||||
}
|
||||
}
|
||||
|
||||
function publicityButton() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project/publicity-button.directive.html',
|
||||
'scope': {
|
||||
'isPublic': '=',
|
||||
'owned': '=',
|
||||
'projectId': '='
|
||||
},
|
||||
'link': link,
|
||||
'controller': PublicityButtonController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attr, ctrl) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,91 @@
|
||||
<div class="modal fade" id="createPolicyModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<form name="form" class="form-horizontal css-form" novalidate>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><span class="glyphicon glyphicon-plus"></span> //vm.modalTitle//</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="create-policy">
|
||||
<div class="col-md-12">
|
||||
<h4>General</h4>
|
||||
<hr class="hr-line"/>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="name" class="col-md-3 control-label">Name:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control form-control-custom" id="name" ng-model="replication.policy.name" name="uName" required>
|
||||
<div ng-messages="form.$submitted && form.uName.$error">
|
||||
<span ng-message="required">Name is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="description" class="col-md-3 control-label">Description:</label>
|
||||
<div class="col-md-9">
|
||||
<textarea class="form-control form-control-custom" id="description" ng-model="replication.policy.description" name="uDescription" required></textarea>
|
||||
<div ng-messages="form.$submitted && form.uDescription.$error">
|
||||
<span ng-message="required">Description is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="enable" class="col-md-3 control-label">Enable:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="checkbox" class="form-control" style="margin-top: 10px; height: auto;" ng-model="replication.policy.enabled">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<h4 class="h4-custom">Destination Setting</h4>
|
||||
<hr class="hr-line"/>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="destinationName" class="col-md-3 control-label">Name:</label>
|
||||
<div class="col-md-7">
|
||||
<select class="form-control form-control-custom" id="destinationName" ng-model="replication.destination.selection" ng-options="d as d.name for d in vm.destinations track by d.id" ng-click="vm.selectDestination(replication.destination.selection)"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="endpoint" class="col-md-3 control-label">Endpoint:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control form-control-custom" id="endpoint" ng-model="replication.destination.endpoint" name="uEndpoint" ng-value="vm.endpoint" required>
|
||||
<div ng-messages="form.$submitted && form.uEndpoint.$error">
|
||||
<span ng-message="required">Endpoint is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="username" class="col-md-3 control-label">Username:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" id="username" ng-model="replication.destination.username" name="uUsername" ng-value="vm.username" required>
|
||||
<div ng-messages="form.$submitted && form.uUsername.$error">
|
||||
<span ng-message="required">Username is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="password" class="col-md-3 control-label">Password:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" class="form-control" id="password" ng-model="replication.destination.password" name="uPassword" ng-value="vm.password" required>
|
||||
<div ng-messages="form.$submitted && form.uPassword.$error">
|
||||
<span ng-message="required">Password is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<div class="col-md-3"></div>
|
||||
<div class="col-md-9">
|
||||
<button type="button" class="btn btn-default" ng-click="vm.pingDestination()">Test Connection</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" id="btnOk" ng-click="form.$valid && vm.save(replication)">// 'ok' | tr //</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
@ -0,0 +1,213 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.replication')
|
||||
.directive('createPolicy', createPolicy);
|
||||
|
||||
CreatePolicyController.$inject = ['$scope', 'ListReplicationPolicyService', 'ListDestinationService', 'UpdateDestinationService', 'PingDestinationService', 'CreateReplicationPolicyService', 'UpdateReplicationPolicyService', '$location', 'getParameterByName'];
|
||||
|
||||
function CreatePolicyController($scope, ListReplicationPolicyService, ListDestinationService, UpdateDestinationService, PingDestinationService, CreateReplicationPolicyService, UpdateReplicationPolicyService, $location, getParameterByName) {
|
||||
var vm = this;
|
||||
|
||||
//Since can not set value for textarea by using vm
|
||||
//use $scope for instead.
|
||||
$scope.replication = {};
|
||||
$scope.replication.policy = {};
|
||||
$scope.replication.destination = {};
|
||||
|
||||
var vm0 = $scope.replication.policy;
|
||||
var vm1 = $scope.replication.destination;
|
||||
|
||||
vm.selectDestination = selectDestination;
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
|
||||
vm.addNew = addNew;
|
||||
vm.edit = edit;
|
||||
vm.prepareDestination = prepareDestination;
|
||||
vm.createPolicy = createPolicy;
|
||||
vm.updatePolicy = updatePolicy;
|
||||
vm.pingDestination = pingDestination;
|
||||
|
||||
$scope.$watch('vm.destinations', function(current) {
|
||||
if(current) {
|
||||
console.log('destination:' + angular.toJson(current));
|
||||
vm1.selection = current[0];
|
||||
vm1.endpoint = vm1.selection.endpoint;
|
||||
vm1.username = vm1.selection.username;
|
||||
vm1.password = vm1.selection.password;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('vm.action+","+vm.policyId', function(current) {
|
||||
if(current) {
|
||||
console.log('Current action for replication policy:' + current);
|
||||
var parts = current.split(',');
|
||||
vm.action = parts[0];
|
||||
vm.policyId = Number(parts[1]);
|
||||
switch(parts[0]) {
|
||||
case 'ADD_NEW':
|
||||
vm.addNew(); break;
|
||||
case 'EDIT':
|
||||
vm.edit(vm.policyId); break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function selectDestination(item) {
|
||||
vm1.selection = item;
|
||||
vm1.endpoint = item.endpoint;
|
||||
vm1.username = item.username;
|
||||
vm1.password = item.password;
|
||||
}
|
||||
|
||||
function prepareDestination() {
|
||||
ListDestinationService('')
|
||||
.success(listDestinationSuccess)
|
||||
.error(listDestinationFailed);
|
||||
}
|
||||
|
||||
function addNew() {
|
||||
vm0.name = '';
|
||||
vm0.description = '';
|
||||
vm0.enabled = true;
|
||||
}
|
||||
|
||||
function edit(policyId) {
|
||||
console.log('Edit policy ID:' + policyId);
|
||||
ListReplicationPolicyService(policyId)
|
||||
.success(listReplicationPolicySuccess)
|
||||
.error(listReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function createPolicy(policy) {
|
||||
CreateReplicationPolicyService(policy)
|
||||
.success(createReplicationPolicySuccess)
|
||||
.error(createReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function updatePolicy(policy) {
|
||||
console.log('Update policy ID:' + vm.policyId);
|
||||
UpdateReplicationPolicyService(vm.policyId, policy)
|
||||
.success(updateReplicationPolicySuccess)
|
||||
.error(updateReplicationPolicyFailed);
|
||||
|
||||
var targetId = vm1.selection.id;
|
||||
console.log('Update target ID:' + targetId);
|
||||
var target = {
|
||||
'name': vm1.selection.name,
|
||||
'endpoint': vm1.endpoint,
|
||||
'username': vm1.username,
|
||||
'password': vm1.password
|
||||
};
|
||||
UpdateDestinationService(targetId, target)
|
||||
.success(updateDestinationSuccess)
|
||||
.error(updateDestinationFailed);
|
||||
}
|
||||
|
||||
function pingDestination() {
|
||||
var target = {
|
||||
'name': vm1.selection.name,
|
||||
'endpoint': vm1.endpoint,
|
||||
'username': vm1.username,
|
||||
'password': vm1.password
|
||||
};
|
||||
PingDestinationService(target)
|
||||
.success(pingDestinationSuccess)
|
||||
.error(pingDestinationFailed);
|
||||
}
|
||||
|
||||
function listDestinationSuccess(data, status) {
|
||||
vm.destinations = data;
|
||||
}
|
||||
function listDestinationFailed(data, status) {
|
||||
console.log('Failed list destination:' + data);
|
||||
}
|
||||
function listReplicationPolicySuccess(data, status) {
|
||||
var replicationPolicy = data;
|
||||
vm0.name = replicationPolicy.name;
|
||||
vm0.description = replicationPolicy.description;
|
||||
vm0.enabled = replicationPolicy.enabled == 1;
|
||||
vm.targetId = replicationPolicy.target_id;
|
||||
}
|
||||
function listReplicationPolicyFailed(data, status) {
|
||||
console.log('Failed list replication policy:' + data);
|
||||
}
|
||||
function createReplicationPolicySuccess(data, status) {
|
||||
console.log('Successful create replication policy.');
|
||||
vm.clearUp();
|
||||
}
|
||||
function createReplicationPolicyFailed(data, status) {
|
||||
console.log('Failed create replication policy.');
|
||||
}
|
||||
function updateReplicationPolicySuccess(data, status) {
|
||||
console.log('Successful update replication policy.');
|
||||
}
|
||||
function updateReplicationPolicyFailed(data, status) {
|
||||
console.log('Failed update replication policy.');
|
||||
}
|
||||
function updateDestinationSuccess(data, status) {
|
||||
console.log('Successful update destination.');
|
||||
}
|
||||
function updateDestinationFailed(data, status) {
|
||||
console.log('Failed update destination.');
|
||||
}
|
||||
function pingDestinationSuccess(data, status) {
|
||||
alert('Successful ping target.');
|
||||
}
|
||||
function pingDestinationFailed(data, status) {
|
||||
alert('Failed ping target:' + data);
|
||||
}
|
||||
}
|
||||
|
||||
function createPolicy() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/replication/create-policy.directive.html',
|
||||
'scope': {
|
||||
'policyId': '@',
|
||||
'modalTitle': '@',
|
||||
'reload': '&',
|
||||
'action': '='
|
||||
},
|
||||
'link': link,
|
||||
'controller': CreatePolicyController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attr, ctrl) {
|
||||
|
||||
element.find('#createPolicyModal').on('shown.bs.modal', function() {
|
||||
ctrl.prepareDestination();
|
||||
scope.form.$setPristine();
|
||||
});
|
||||
ctrl.save = save;
|
||||
|
||||
function save(form) {
|
||||
console.log(angular.toJson(form));
|
||||
var postPayload = {
|
||||
'projectId': Number(ctrl.projectId),
|
||||
'targetId': form.destination.selection.id,
|
||||
'name': form.policy.name,
|
||||
'enabled': form.policy.enabled ? 1 : 0,
|
||||
'description': form.policy.description,
|
||||
'cron_str': '',
|
||||
'start_time': ''
|
||||
};
|
||||
switch(ctrl.action) {
|
||||
case 'ADD_NEW':
|
||||
ctrl.createPolicy(postPayload); break;
|
||||
case 'EDIT':
|
||||
ctrl.updatePolicy(postPayload); break;
|
||||
}
|
||||
element.find('#createPolicyModal').modal('hide');
|
||||
ctrl.reload();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,89 @@
|
||||
<div class="tab-pane">
|
||||
<div class="col-xs-12 col-md-12 each-tab-pane">
|
||||
<div class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.replicationPolicyName" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.search()"><span class="glyphicon glyphicon-search"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
<button ng-if="!vm.isOpen" class="btn btn-success" type="button" ng-click="vm.addReplication()" data-toggle="modal" data-target="#createPolicyModal"><span class="glyphicon glyphicon-plus"></span>New Replication</button>
|
||||
<create-policy reload="vm.retrievePolicy()" action="vm.action" modal-title="//vm.modalTitle//" policy-id="//vm.policyId//"></create-policy>
|
||||
</div>
|
||||
<div class="pane-split" id="upon-pane">
|
||||
<div class="sub-pane-split">
|
||||
<table class="table table-pane">
|
||||
<thead>
|
||||
<th width="10%">Name</th>
|
||||
<th width="18%">Description</th>
|
||||
<th width="18%">Destination</th>
|
||||
<th width="18%">Start Time</th>
|
||||
<th width="14%">Activation</th>
|
||||
<th width="15%">Actions</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="vm.replicationPolicies.length == 0">
|
||||
<td colspan="7" height="100%" class="empty-hint" ><h3 class="text-muted">No replication policies, add new replication policy.</h3></td>
|
||||
</tr>
|
||||
<tr policy_id="//r.id//" ng-if="vm.replicationPolicies.length > 0" ng-repeat="r in vm.replicationPolicies" value="//vm.last = $last//">
|
||||
<td>//r.name//</td>
|
||||
<td>//r.description//</td>
|
||||
<td>//r.target_name//</td>
|
||||
<td>//r.start_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
<td ng-switch on="//r.enabled//">
|
||||
<span ng-switch-when="1">Enabled</span>
|
||||
<span ng-switch-when="0">Disabled</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="display-inline-block" ng-switch on="//r.enabled//">
|
||||
<a href="javascript:void(0);" ng-click="vm.togglePolicy(r.id, 0)"><span ng-switch-when="1" class="glyphicon glyphicon-play color-success"></span></a>
|
||||
<a href="javascript:void(0);" ng-click="vm.togglePolicy(r.id, 1)"><span ng-switch-when="0" class="glyphicon glyphicon-stop color-danger"></span></a>
|
||||
</div>
|
||||
|
||||
<a href="javascript:void(0);" data-toggle="modal" data-target="#createPolicyModal" ng-click="vm.editReplication(r.id)"><span class="glyphicon glyphicon-pencil"></span></a>
|
||||
|
||||
<a href="javascript:void(0);"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4 col-md-12 well well-sm well-custom well-split"><div class="col-md-offset-10">//vm.projects ? vm.projects.length : 0// // 'items' | tr //</div></div>
|
||||
<p class="split-handle"><span class="glyphicon glyphicon-align-justify"></span></p>
|
||||
<h4 class="h4-custom-down">Replication Jobs</h4>
|
||||
<hr class="hr-line"/>
|
||||
<div class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.replicationJobnName" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.search()"><span class="glyphicon glyphicon-search"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane-split" id="down-pane">
|
||||
<div class="sub-pane-split">
|
||||
<table class="table table-pane" width="98%">
|
||||
<thead>
|
||||
<th width="20%">Name</th>
|
||||
<th width="25%">Operation</th>
|
||||
<th width="40%">Start Time</th>
|
||||
<th width="15%">Status</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="vm.replicationJobs.length == 0">
|
||||
<td colspan="4" height="100%" class="empty-hint" ><h3 class="text-muted">No replication jobs.</h3></td>
|
||||
</tr>
|
||||
<tr ng-if="vm.replicationJobs.length > 0" ng-repeat="r in vm.replicationJobs">
|
||||
<td>//r.tags != null ? r.tags.join(',') : 'N/A'//</td>
|
||||
<td>//r.operation//</td>
|
||||
<td>//r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
<td>//r.status//</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,174 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.replication')
|
||||
.directive('listReplication', listReplication);
|
||||
|
||||
ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService'];
|
||||
|
||||
function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService) {
|
||||
var vm = this;
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function() {
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.retrievePolicy();
|
||||
});
|
||||
|
||||
vm.addReplication = addReplication;
|
||||
vm.editReplication = editReplication;
|
||||
|
||||
vm.search = search;
|
||||
|
||||
vm.retrievePolicy = retrievePolicy;
|
||||
vm.retrieveJob = retrieveJob;
|
||||
vm.togglePolicy = togglePolicy;
|
||||
|
||||
vm.last = false;
|
||||
vm.retrievePolicy();
|
||||
|
||||
function search() {
|
||||
vm.retrievePolicy();
|
||||
}
|
||||
|
||||
function retrievePolicy() {
|
||||
ListReplicationPolicyService('', vm.projectId, vm.replicationPolicyName)
|
||||
.success(listReplicationPolicySuccess)
|
||||
.error(listReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function retrieveJob(policyId) {
|
||||
ListReplicationJobService(policyId, vm.replicationJobName)
|
||||
.success(listReplicationJobSuccess)
|
||||
.error(listReplicationJobFailed);
|
||||
}
|
||||
|
||||
function listReplicationPolicySuccess(data, status) {
|
||||
vm.replicationPolicies = data || [];
|
||||
}
|
||||
|
||||
function listReplicationPolicyFailed(data, status) {
|
||||
console.log('Failed list replication policy:' + data);
|
||||
}
|
||||
|
||||
function listReplicationJobSuccess(data, status) {
|
||||
vm.replicationJobs = data || [];
|
||||
}
|
||||
|
||||
function listReplicationJobFailed(data, status) {
|
||||
console.log('Failed list replication job:' + data);
|
||||
}
|
||||
|
||||
function addReplication() {
|
||||
vm.modalTitle = 'Create New Policy';
|
||||
vm.action = 'ADD_NEW';
|
||||
}
|
||||
|
||||
function editReplication(policyId) {
|
||||
vm.policyId = policyId;
|
||||
vm.modalTitle = 'Edit Policy';
|
||||
vm.action = 'EDIT';
|
||||
|
||||
console.log('Selected policy ID:' + vm.policyId);
|
||||
}
|
||||
|
||||
function togglePolicy(policyId, enabled) {
|
||||
ToggleReplicationPolicyService(policyId, enabled)
|
||||
.success(toggleReplicationPolicySuccess)
|
||||
.error(toggleReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function toggleReplicationPolicySuccess(data, status) {
|
||||
console.log('Successful toggle replication policy.');
|
||||
vm.retrievePolicy();
|
||||
}
|
||||
|
||||
function toggleReplicationPolicyFailed(data, status) {
|
||||
console.log('Failed toggle replication policy.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function listReplication($timeout) {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/replication/list-replication.directive.html',
|
||||
'scope': true,
|
||||
'link': link,
|
||||
'controller': ListReplicationController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
var uponPaneHeight = element.find('#upon-pane').height();
|
||||
var handleHeight = element.find('.split-handle').height() + element.find('.split-handle').offset().top + element.find('.well').height() - 24;
|
||||
|
||||
var maxDownPaneHeight = 245;
|
||||
|
||||
element.find('.split-handle').on('mousedown', mousedownHandler);
|
||||
|
||||
function mousedownHandler(e) {
|
||||
e.preventDefault();
|
||||
$(document).on('mousemove', mousemoveHandler);
|
||||
$(document).on('mouseup', mouseupHandler);
|
||||
}
|
||||
|
||||
function mousemoveHandler(e) {
|
||||
if(element.find('#down-pane').height() <= maxDownPaneHeight) {
|
||||
element.find('#upon-pane').css({'height' : (uponPaneHeight - (handleHeight - e.pageY)) + 'px'});
|
||||
element.find('#down-pane').css({'height' : (uponPaneHeight + (handleHeight - e.pageY - 196)) + 'px'});
|
||||
}else{
|
||||
element.find('#down-pane').css({'height' : (maxDownPaneHeight) + 'px'});
|
||||
$(document).off('mousemove');
|
||||
}
|
||||
}
|
||||
function mouseupHandler(e) {
|
||||
$(document).off('mousedown');
|
||||
$(document).off('mousemove');
|
||||
}
|
||||
|
||||
ctrl.lastPolicyId = -1;
|
||||
|
||||
scope.$watch('vm.replicationPolicies', function(current) {
|
||||
$timeout(function(){
|
||||
if(current) {
|
||||
if(current.length > 0) {
|
||||
element.find('#upon-pane table>tbody>tr').on('click', trClickHandler);
|
||||
if(ctrl.lastPolicyId === -1) {
|
||||
element.find('#upon-pane table>tbody>tr:eq(0)').trigger('click');
|
||||
}else{
|
||||
element.find('#upon-pane table>tbody>tr').filter('[policy_id="' + ctrl.lastPolicyId + '"]').trigger('click');
|
||||
}
|
||||
}else{
|
||||
element
|
||||
.find('#upon-pane table>tbody>tr')
|
||||
.css({'background-color': '#FFFFFF'})
|
||||
.css({'color': '#000'});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function trClickHandler(e) {
|
||||
element
|
||||
.find('#upon-pane table>tbody>tr')
|
||||
.css({'background-color': '#FFFFFF'})
|
||||
.css({'color': '#000'});
|
||||
element
|
||||
.find('#upon-pane table>tbody>tr a')
|
||||
.css({'color': '#337ab7'});
|
||||
$(this)
|
||||
.css({'background-color': '#057ac9'})
|
||||
.css({'color': '#fff'});
|
||||
$('a', this)
|
||||
.css({'color': '#fff'});
|
||||
ctrl.retrieveJob($(this).attr('policy_id'));
|
||||
ctrl.lastPolicyId = $(this).attr('policy_id');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,11 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.replication', [
|
||||
'harbor.services.replication.policy',
|
||||
'harbor.services.replication.job'
|
||||
]);
|
||||
|
||||
})();
|
@ -0,0 +1,15 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository')
|
||||
.controller('DeleteRepositoryController', DeleteRepositoryController);
|
||||
|
||||
DeleteRepositoryController.$inject = ['DeleteRepositoryService'];
|
||||
|
||||
function DeleteRepositoryController(DeleteRepositoryService) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,29 @@
|
||||
<div class="tab-pane active" id="repositories" >
|
||||
<div class="col-xs-12 col-md-12 each-tab-pane">
|
||||
<div class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.filterInput" value="" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.retrieve()"><span class="glyphicon glyphicon-search"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="vm.repositories.length === 0" class="empty-hint">
|
||||
<h3 style="margin-top: 200px;" class="text-muted">// 'no_repositories' | tr //</h3>
|
||||
</div>
|
||||
<div ng-if="vm.repositories.length > 0" class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
<modal-dialog action="vm.deleteImage()" content-type="text/html" title="//vm.modalTitle//" message="//vm.modalMessage//"></modal-dialog>
|
||||
<div class="panel panel-default" ng-repeat="repo in vm.repositories">
|
||||
<div class="panel-heading" role="tab" id="heading//$index + 1//">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-parent="" href="?project_id=//vm.projectId//#collapse//$index + 1//" aria-expanded="true" aria-controls="collapse//$index+1//">
|
||||
<span class="glyphicon glyphicon-book"></span> //repo// <span class="badge">//vm.tagCount[repo]//</span>
|
||||
</a>
|
||||
<a class="pull-right" style="margin-right: 78px;" href="javascript:void(0)" data-toggle="modal" data-target="#myModal" ng-click="vm.deleteByRepo(repo)"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</h4>
|
||||
</div>
|
||||
<list-tag associate-id="$index + 1" repo-name="repo" tag-count="vm.tagCount"></list-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,118 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository')
|
||||
.directive('listRepository', listRepository);
|
||||
|
||||
ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName'];
|
||||
|
||||
function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName) {
|
||||
var vm = this;
|
||||
|
||||
vm.filterInput = '';
|
||||
|
||||
var hashValue = $location.hash();
|
||||
if(hashValue) {
|
||||
var slashIndex = hashValue.indexOf('/');
|
||||
if(slashIndex >=0) {
|
||||
vm.filterInput = hashValue.substring(slashIndex + 1);
|
||||
}else{
|
||||
vm.filterInput = hashValue;
|
||||
}
|
||||
}
|
||||
|
||||
vm.retrieve = retrieve;
|
||||
vm.tagCount = {};
|
||||
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.retrieve();
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function() {
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.retrieve();
|
||||
});
|
||||
|
||||
|
||||
$scope.$watch('vm.repositories', function(current) {
|
||||
if(current) {
|
||||
vm.repositories = current || [];
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('repoName', function(e, val) {
|
||||
vm.repoName = val;
|
||||
});
|
||||
|
||||
$scope.$on('tag', function(e, val){
|
||||
vm.tag = val;
|
||||
});
|
||||
|
||||
$scope.$on('tagCount', function(e, val) {
|
||||
vm.tagCount = val;
|
||||
});
|
||||
|
||||
$scope.$on('modalTitle', function(e, val) {
|
||||
vm.modalTitle = val;
|
||||
});
|
||||
|
||||
$scope.$on('modalMessage', function(e, val) {
|
||||
vm.modalMessage = val;
|
||||
});
|
||||
|
||||
vm.deleteByRepo = deleteByRepo;
|
||||
vm.deleteImage = deleteImage;
|
||||
|
||||
function retrieve(){
|
||||
ListRepositoryService(vm.projectId, vm.filterInput)
|
||||
.success(getRepositoryComplete)
|
||||
.error(getRepositoryFailed);
|
||||
}
|
||||
|
||||
function getRepositoryComplete(data, status) {
|
||||
vm.repositories = data || [];
|
||||
}
|
||||
|
||||
function getRepositoryFailed(response) {
|
||||
console.log('Failed list repositories:' + response);
|
||||
}
|
||||
|
||||
|
||||
function deleteByRepo(repoName) {
|
||||
vm.repoName = repoName;
|
||||
vm.tag = '';
|
||||
vm.modalTitle = $filter('tr')('alert_delete_repo_title', [repoName]);
|
||||
vm.modalMessage = $filter('tr')('alert_delete_repo', [repoName]);
|
||||
}
|
||||
|
||||
function deleteImage() {
|
||||
console.log('repoName:' + vm.repoName + ', tag:' + vm.tag);
|
||||
DeleteRepositoryService(vm.repoName, vm.tag)
|
||||
.success(deleteRepositorySuccess)
|
||||
.error(deleteRepositoryFailed);
|
||||
}
|
||||
|
||||
function deleteRepositorySuccess(data, status) {
|
||||
vm.retrieve();
|
||||
}
|
||||
|
||||
function deleteRepositoryFailed(data, status) {
|
||||
console.log('Failed delete repository:' + data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function listRepository() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/repository/list-repository.directive.html',
|
||||
controller: ListRepositoryController,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,22 @@
|
||||
<div id="collapse//vm.associateId//" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading//vm.associateId//">
|
||||
<div class="panel-body">
|
||||
<table class="repository-table">
|
||||
<thead>
|
||||
<th width="20%"><span class="glyphicon glyphicon-tags"></span> // 'tag' | tr //</th>
|
||||
<th width="15%" style="text-align: center;">// 'image_details' | tr //</th>
|
||||
<th width="40%">// 'pull_command' | tr //</th>
|
||||
<th width="15%" style="text-align: center;">// 'operation' | tr //</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="tag in vm.tags">
|
||||
<td>//tag//</td>
|
||||
<td style="text-align: center;"><popup-details repo-name="//vm.repoName//" tag="//tag//" index="//$index//"></popup-details></td>
|
||||
<td>
|
||||
<pull-command repo-name="//vm.repoName//" tag="//tag//"></pull-command>
|
||||
</td>
|
||||
<td style="text-align: center;"><a href="javascript:void(0);" data-toggle="modal" data-target="#myModal" ng-click="vm.deleteByTag({repoName: vm.repoName, tag: tag})"><span class="glyphicon glyphicon-trash"></span></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,78 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository')
|
||||
.directive('listTag', listTag);
|
||||
|
||||
ListTagController.$inject = ['$scope', 'ListTagService', '$filter', 'trFilter'];
|
||||
|
||||
function ListTagController($scope, ListTagService, $filter, trFilter) {
|
||||
var vm = this;
|
||||
|
||||
vm.tags = [];
|
||||
|
||||
$scope.$watch('vm.repoName', function(current, origin) {
|
||||
if(current) {
|
||||
console.log('vm.repoName in tags:' + current);
|
||||
ListTagService(current)
|
||||
.then(getTagComplete)
|
||||
.catch(getTagFailed);
|
||||
}
|
||||
});
|
||||
|
||||
vm.deleteByTag = deleteByTag;
|
||||
|
||||
function getTagComplete(response) {
|
||||
vm.tags = response.data;
|
||||
vm.tagCount[vm.repoName] = vm.tags.length;
|
||||
$scope.$emit('tagCount', vm.tagCount);
|
||||
}
|
||||
|
||||
function getTagFailed(response) {
|
||||
console.log('Failed get tag:' + response);
|
||||
}
|
||||
|
||||
function deleteByTag(e) {
|
||||
$scope.$emit('tag', e.tag);
|
||||
$scope.$emit('repoName', e.repoName);
|
||||
$scope.$emit('modalTitle', $filter('tr')('alert_delete_tag_title', [e.tag]));
|
||||
|
||||
var message;
|
||||
if(vm.tags.length === 1) {
|
||||
message = $filter('tr')('alert_delete_last_tag', [e.tag]);
|
||||
}else {
|
||||
message = $filter('tr')('alert_delete_tag', [e.tag]);
|
||||
}
|
||||
|
||||
$scope.$emit('modalMessage', message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function listTag() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/repository/list-tag.directive.html',
|
||||
'scope': {
|
||||
'tagCount': '=',
|
||||
'associateId': '=',
|
||||
'repoName': '='
|
||||
},
|
||||
'replace': true,
|
||||
'link': link,
|
||||
'controller': ListTagController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,4 @@
|
||||
<a href="javascript:void(0)" >
|
||||
<span class="glyphicon glyphicon-info-sign" role="button" data-trigger="click" data-toggle="popover" data-placement="right">
|
||||
</span>
|
||||
</a>
|
@ -0,0 +1,101 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository')
|
||||
.directive('popupDetails', popupDetails);
|
||||
|
||||
PopupDetailsController.$inject = ['ListManifestService', '$filter', 'dateLFilter'];
|
||||
|
||||
function PopupDetailsController(ListManifestService, $filter, dateLFilter) {
|
||||
var vm = this;
|
||||
|
||||
vm.retrieve = retrieve;
|
||||
|
||||
function retrieve() {
|
||||
ListManifestService(vm.repoName, vm.tag)
|
||||
.success(getManifestSuccess)
|
||||
.error(getManifestFailed);
|
||||
}
|
||||
|
||||
function getManifestSuccess(data, status) {
|
||||
console.log('Successful get manifest:' + data);
|
||||
vm.manifest = data;
|
||||
vm.manifest['Created'] = $filter('dateL')(vm.manifest['Created'], 'YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
function getManifestFailed(data, status) {
|
||||
console.log('Failed get manifest:' + data);
|
||||
}
|
||||
}
|
||||
|
||||
function popupDetails() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/repository/popup-details.directive.html',
|
||||
'scope': {
|
||||
'repoName': '@',
|
||||
'tag': '@',
|
||||
'index': '@'
|
||||
},
|
||||
'replace': true,
|
||||
'link': link,
|
||||
'controller': PopupDetailsController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
ctrl.retrieve();
|
||||
scope.$watch('vm.manifest', function(current) {
|
||||
if(current) {
|
||||
|
||||
element
|
||||
.popover({
|
||||
'template': '<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-title"></div><div class="popover-content"></div></div>',
|
||||
'title': '<div class="pull-right clearfix"><a href="javascript:void(0);"><span class="glyphicon glyphicon-remove-circle"></span></a></div>',
|
||||
'content': generateContent,
|
||||
'html': true
|
||||
})
|
||||
.on('shown.bs.popover', function(e){
|
||||
var self = jQuery(this);
|
||||
$('[type="text"]:input', self.parent())
|
||||
.select()
|
||||
.end()
|
||||
.on('click', function() {
|
||||
$(this).select();
|
||||
});
|
||||
self.parent().find('.glyphicon.glyphicon-remove-circle').on('click', function() {
|
||||
element.trigger('click');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
function generateContent() {
|
||||
var content = '<form class="form-horizontal">' +
|
||||
'<div class="form-group">' +
|
||||
'<label class="col-sm-3 control-label">Id</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static long-line"><input type="text" id="txtImageId" value="' + ctrl.manifest['Id'] + '" readonly size="40"></p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">Parent</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static long-line">' + ctrl.manifest['Parent'] + '</p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">Created</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static">' + ctrl.manifest['Created'] + '</p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">Duration Days</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Duration Days'] === '' ? 'N/A' : ctrl.manifest['Duration Days']) + ' days</p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">Author</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Author'] === '' ? 'N/A' : ctrl.manifest['Author']) + '</p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">Architecture</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Architecture'] === '' ? 'N/A' : ctrl.manifest['Architecture']) + '</p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">Docker Version</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Docker Version'] === '' ? 'N/A' : ctrl.manifest['Docker Version']) + '</p></div></div>' +
|
||||
'<div class="form-group"><label class="col-sm-3 control-label">OS</label>' +
|
||||
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['OS'] === '' ? 'N/A' : ctrl.manifest['OS']) + '</p></div></div>' +
|
||||
'</form>';
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,10 @@
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" value="docker pull //vm.harborRegUrl////vm.repoName//://vm.tag//" readonly="readonly" size="60">
|
||||
<div class="input-group-addon">
|
||||
<a href="javascript:void(0);"><span class="glyphicon glyphicon-duplicate"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,41 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository')
|
||||
.directive('pullCommand', pullCommand);
|
||||
|
||||
function PullCommandController() {
|
||||
|
||||
}
|
||||
|
||||
function pullCommand() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/repository/pull-command.directive.html',
|
||||
'scope': {
|
||||
'repoName': '@',
|
||||
'tag': '@'
|
||||
},
|
||||
'link': link,
|
||||
'controller': PullCommandController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
ctrl.harborRegUrl = $('#HarborRegUrl').val() + '/';
|
||||
|
||||
element.find('a').on('click', clickHandler);
|
||||
function clickHandler(e) {
|
||||
element.find('input[type="text"]').select();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,7 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository', [
|
||||
'harbor.services.repository']);
|
||||
})();
|
@ -0,0 +1,5 @@
|
||||
<form class="navbar-form pull-right" role="search">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control search-icon" ng-model="vm.searchInput" placeholder="// 'projects_or_repositories' | tr //" size="35">
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,54 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.search')
|
||||
.directive('searchInput', searchInput);
|
||||
|
||||
SearchInputController.$inject = ['$scope', '$location', '$window'];
|
||||
|
||||
function SearchInputController($scope, $location, $window) {
|
||||
var vm = this;
|
||||
|
||||
vm.searchFor = searchFor;
|
||||
|
||||
function searchFor(searchContent) {
|
||||
$location
|
||||
.path('/ng/search')
|
||||
.search({'q': searchContent});
|
||||
$window.location.href = $location.url();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function searchInput() {
|
||||
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/search/search-input.directive.html',
|
||||
'scope': {
|
||||
'searchInput': '=',
|
||||
},
|
||||
'link': link,
|
||||
'controller': SearchInputController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
element
|
||||
.find('input[type="text"]')
|
||||
.on('keydown', keydownHandler);
|
||||
|
||||
function keydownHandler(e) {
|
||||
if(e.keyCode === 13) {
|
||||
ctrl.searchFor($(this).val());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,12 @@
|
||||
<div class="down-table-pane">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>// 'project_repo_name' | tr //</th><th>// 'creation_time' | tr //</th><th>// 'author' | tr //</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="s in vm.searchResult">
|
||||
<td>//s.repository_name//</td><td>N/A</td><td>N/A</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
52
static/ng/resources/js/components/search/search.directive.js
Normal file
52
static/ng/resources/js/components/search/search.directive.js
Normal file
@ -0,0 +1,52 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.search')
|
||||
.directive('search', search);
|
||||
|
||||
SearchController.$inject = ['SearchService', '$scope'];
|
||||
|
||||
function SearchController(SearchService, $scope) {
|
||||
var vm = this;
|
||||
vm.keywords = "";
|
||||
vm.search = searchByFilter;
|
||||
vm.filterBy = 'repository';
|
||||
|
||||
searchByFilter();
|
||||
|
||||
|
||||
function searchByFilter() {
|
||||
SearchService(vm.keywords)
|
||||
.success(searchSuccess)
|
||||
.error(searchFailed);
|
||||
}
|
||||
|
||||
function searchSuccess(data, status) {
|
||||
console.log('filterBy:' + vm.filterBy + ", data:" + data);
|
||||
vm.searchResult = data[vm.filterBy];
|
||||
}
|
||||
|
||||
function searchFailed(data, status) {
|
||||
console.log('Failed search:' + data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function search() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/search/search.directive.html',
|
||||
'scope': {
|
||||
'filterBy': '='
|
||||
},
|
||||
'controller': SearchController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,7 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.search', [
|
||||
'harbor.services.search']);
|
||||
})();
|
@ -0,0 +1,92 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.sign.in')
|
||||
.directive('signIn', signIn);
|
||||
|
||||
SignInController.$inject = ['SignInService', 'LogOutService', 'currentUser', 'I18nService', '$window', '$scope'];
|
||||
function SignInController(SignInService, LogOutService, currentUser, I18nService, $window, $scope) {
|
||||
var vm = this;
|
||||
|
||||
vm.hasError = false;
|
||||
vm.errorMessage = '';
|
||||
|
||||
vm.reset = reset;
|
||||
vm.doSignIn = doSignIn;
|
||||
vm.doSignUp = doSignUp;
|
||||
vm.doForgotPassword = doForgotPassword;
|
||||
|
||||
vm.doContinue = doContinue;
|
||||
vm.doLogOut = doLogOut;
|
||||
|
||||
function reset() {
|
||||
vm.hasError = false;
|
||||
vm.errorMessage = '';
|
||||
}
|
||||
|
||||
function doSignIn(user) {
|
||||
if(user && angular.isDefined(user.principal) && angular.isDefined(user.password)) {
|
||||
SignInService(user.principal, user.password)
|
||||
.success(signedInSuccess)
|
||||
.error(signedInFailed);
|
||||
}
|
||||
}
|
||||
|
||||
function signedInSuccess(data, status) {
|
||||
$window.location.href = "/ng/dashboard";
|
||||
}
|
||||
|
||||
function signedInFailed(data, status) {
|
||||
if(status === 401) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'username_or_password_is_incorrect';
|
||||
}
|
||||
console.log('Failed sign in:' + data + ', status:' + status);
|
||||
}
|
||||
|
||||
function doSignUp() {
|
||||
$window.location.href = '/ng/sign_up';
|
||||
}
|
||||
|
||||
function doForgotPassword() {
|
||||
$window.location.href = '/ng/forgot_password';
|
||||
}
|
||||
|
||||
function doContinue() {
|
||||
$window.location.href = '/ng/dashboard';
|
||||
}
|
||||
|
||||
function doLogOut() {
|
||||
LogOutService()
|
||||
.success(logOutSuccess)
|
||||
.error(logOutFailed);
|
||||
}
|
||||
|
||||
function logOutSuccess(data, status) {
|
||||
currentUser.unset();
|
||||
I18nService().unset();
|
||||
$window.location.href= '/ng';
|
||||
}
|
||||
|
||||
function logOutFailed(data, status) {
|
||||
console.log('Failed to log out:' + data);
|
||||
}
|
||||
}
|
||||
|
||||
function signIn() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/ng/sign_in',
|
||||
'scope': true,
|
||||
'controller': SignInController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
})();
|
10
static/ng/resources/js/components/sign-in/sign-in.module.js
Normal file
10
static/ng/resources/js/components/sign-in/sign-in.module.js
Normal file
@ -0,0 +1,10 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.sign.in', [
|
||||
'harbor.services.user'
|
||||
]);
|
||||
|
||||
})();
|
@ -0,0 +1,4 @@
|
||||
<h4 class="page-header title-color underlined">// 'summary' | tr //</h4>
|
||||
<dl class="page-content dl-horizontal" ng-repeat="(key, value) in vm.statProjects">
|
||||
<dt>// key | tr //:</dt><dd>//value//</dd>
|
||||
</dl>
|
@ -0,0 +1,40 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.summary')
|
||||
.directive('projectSummary', projectSummary);
|
||||
|
||||
ProjectSummaryController.$inject = ['StatProjectService'];
|
||||
|
||||
function ProjectSummaryController(StatProjectService) {
|
||||
var vm = this;
|
||||
|
||||
StatProjectService()
|
||||
.success(statProjectSuccess)
|
||||
.error(statProjectFailed);
|
||||
|
||||
function statProjectSuccess(data) {
|
||||
vm.statProjects = data;
|
||||
}
|
||||
|
||||
function statProjectFailed(status) {
|
||||
console.log('Failed stat project:' + status);
|
||||
}
|
||||
}
|
||||
|
||||
function projectSummary() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/summary/summary.directive.html',
|
||||
'controller': ProjectSummaryController,
|
||||
'scope' : true,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
10
static/ng/resources/js/components/summary/summary.module.js
Normal file
10
static/ng/resources/js/components/summary/summary.module.js
Normal file
@ -0,0 +1,10 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.summary', [
|
||||
'harbor.services.project'
|
||||
]);
|
||||
|
||||
})();
|
@ -0,0 +1,62 @@
|
||||
<form name="form" class="form-horizontal" ng-submit="form.$valid && vm.changeSettings(system)" >
|
||||
<div class="col-md-12">
|
||||
<h5>System Settings</h5>
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="col-md-12 col-md-off-set-1 main-content">
|
||||
<div class="form-group">
|
||||
<label for="hostName" class="col-sm-3 control-label">Host Name:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="hostName" ng-model="system.hostName" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.hostName" name="uHostName" required>
|
||||
<div ng-messages="form.$dirty && form.uHostName.$error">
|
||||
<span ng-message="required">Host name is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="urlProtocol" class="col-sm-3 control-label">URL Protocol:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="urlProtocol" ng-model="system.urlProtocol" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.urlProtocol" name="uUrlProtocol" required>
|
||||
<div ng-messages="form.$dirty && form.uUrlProtocol.$error">
|
||||
<span ng-message="required">Url protocol is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="emailServer" class="col-sm-3 control-label">Email server:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="emailServer" ng-model="system.emailServer" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.emailServer" name="uEmailServer" required>
|
||||
<div ng-messages="form.$dirty && form.uEmailServer.$error">
|
||||
<span ng-message="required">Email server is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ldapUrl" class="col-sm-3 control-label">LDAP URL:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="ldapUrl" ng-model="system.ldapUrl" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.ldapUrl" name="uLdapUrl" required>
|
||||
<div ng-messages="form.$dirty && form.uLdapUrl.$error">
|
||||
<span ng-message="required">LDAP URL is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<h5>Registration</h5>
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="col-md-12 col-md-off-set-1 main-content">
|
||||
<div class="form-group">
|
||||
<label for="registration" class="col-sm-3 control-label">Registration:</label>
|
||||
<div class="col-sm-7">
|
||||
<select class="form-control" ng-model="vm.currentRegistration" ng-options="r as r.name for r in vm.registrationOptions track by r.value" ng-click="vm.selectRegistration()"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-7 col-md-10">
|
||||
<input type="submit" class="btn btn-primary" ng-disabled="form.$invalid" value="Save">
|
||||
<input type="submit" class="btn btn-default" ng-click="vm.cancel()" value="Cancel">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,54 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.system.management')
|
||||
.directive('configuration', configuration);
|
||||
|
||||
ConfigurationController.$inject = [];
|
||||
|
||||
function ConfigurationController() {
|
||||
var vm = this;
|
||||
|
||||
vm.registrationOptions = [
|
||||
{
|
||||
'name': 'on',
|
||||
'value': true
|
||||
},
|
||||
{
|
||||
'name': 'off',
|
||||
'value': false
|
||||
}
|
||||
];
|
||||
vm.currentRegistration = {
|
||||
'name': 'on',
|
||||
'value': true
|
||||
};
|
||||
|
||||
vm.changeSettings = changeSettings;
|
||||
|
||||
vm.selectRegistration = selectRegistration;
|
||||
|
||||
function selectRegistration() {
|
||||
|
||||
}
|
||||
|
||||
function changeSettings(system) {
|
||||
console.log(system);
|
||||
}
|
||||
}
|
||||
|
||||
function configuration() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/system-management/configuration.directive.html',
|
||||
'scope': true,
|
||||
'controller': ConfigurationController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,63 @@
|
||||
<div class="modal fade" id="createDestinationModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<form name="form" class="form-horizontal css-form" novalidate>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><span class="glyphicon glyphicon-plus"></span> //vm.modalTitle//</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="create-destination">
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="name" class="col-md-3 control-label">Name:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control form-control-custom" id="name" ng-model="destination.name" name="uName" required>
|
||||
<div ng-messages="form.$submitted && form.uName.$error">
|
||||
<span ng-message="required">Name is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="description" class="col-md-3 control-label">Endpoint:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control form-control-custom" id="endpoint" ng-model="destination.endpoint" name="uEndpoint" required >
|
||||
<div ng-messages="form.$submitted && form.uEndpoint.$error">
|
||||
<span ng-message="required">Endpoint is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="username" class="col-md-3 control-label">Username:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" id="username" ng-model="destination.username" name="uUsername" required>
|
||||
<div ng-messages="form.$submitted && form.uUsername.$error">
|
||||
<span ng-message="required">Username is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<label for="password" class="col-md-3 control-label">Password:</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" class="form-control" id="password" ng-model="destination.password" name="uPassword" required>
|
||||
<div ng-messages="form.$submitted && form.uPassword.$error">
|
||||
<span ng-message="required">Password is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12 form-group-custom">
|
||||
<div class="col-md-3"></div>
|
||||
<div class="col-md-9">
|
||||
<button type="submit" class="btn btn-default" ng-click="form.$valid && vm.pingDestination()">Test Connection</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" id="btnOk" ng-click="form.$valid && vm.save(destination)">// 'ok' | tr //</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</form>
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
@ -0,0 +1,160 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.system.management')
|
||||
.directive('createDestination', createDestination);
|
||||
|
||||
CreateDestinationController.$inject = ['$scope', 'ListDestinationService', 'CreateDestinationService', 'UpdateDestinationService', 'PingDestinationService'];
|
||||
|
||||
function CreateDestinationController($scope, ListDestinationService, CreateDestinationService, UpdateDestinationService, PingDestinationService) {
|
||||
var vm = this;
|
||||
|
||||
$scope.destination = {};
|
||||
|
||||
var vm0 = $scope.destination;
|
||||
vm.addNew = addNew;
|
||||
vm.edit = edit;
|
||||
vm.create = create;
|
||||
vm.update = update;
|
||||
vm.pingDestination = pingDestination;
|
||||
|
||||
$scope.$watch('vm.action + "," + vm.targetId', function(current) {
|
||||
if(current) {
|
||||
var parts = current.split(',');
|
||||
vm.action = parts[0];
|
||||
vm.targetId = parts[1];
|
||||
switch(vm.action) {
|
||||
case 'ADD_NEW':
|
||||
vm.modalTitle = 'Create destination';
|
||||
vm.addNew();
|
||||
break;
|
||||
case 'EDIT':
|
||||
vm.modalTitle = 'Edit destination';
|
||||
vm.edit(vm.targetId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function addNew() {
|
||||
vm0.name = '';
|
||||
vm0.endpoint = '';
|
||||
vm0.username = '';
|
||||
vm0.password = '';
|
||||
}
|
||||
|
||||
function edit(targetId) {
|
||||
getDestination(targetId);
|
||||
}
|
||||
|
||||
function create(destination) {
|
||||
CreateDestinationService(destination.name, destination.endpoint,
|
||||
destination.username, destination.password)
|
||||
.success(createDestinationSuccess)
|
||||
.error(createDestinationFailed);
|
||||
}
|
||||
|
||||
function createDestinationSuccess(data, status) {
|
||||
alert('Successful created destination.');
|
||||
}
|
||||
|
||||
function createDestinationFailed(data, status) {
|
||||
console.log('Failed create destination:' + data);
|
||||
}
|
||||
|
||||
function update(destination) {
|
||||
UpdateDestinationService(vm.targetId, destination)
|
||||
.success(updateDestinationSuccess)
|
||||
.error(updateDestinationFailed);
|
||||
}
|
||||
|
||||
function updateDestinationSuccess(data, status) {
|
||||
console.log('Successful update destination.');
|
||||
}
|
||||
|
||||
function updateDestinationFailed(data, status) {
|
||||
console.log('Failed update destination.');
|
||||
}
|
||||
|
||||
function getDestination(targetId) {
|
||||
ListDestinationService(targetId)
|
||||
.success(getDestinationSuccess)
|
||||
.error(getDestinationFailed);
|
||||
}
|
||||
|
||||
function getDestinationSuccess(data, status) {
|
||||
var destination = data;
|
||||
vm0.name = destination.name;
|
||||
vm0.endpoint = destination.endpoint;
|
||||
vm0.username = destination.username;
|
||||
vm0.password = destination.password;
|
||||
}
|
||||
|
||||
function getDestinationFailed(data, status) {
|
||||
console.log('Failed get destination.');
|
||||
}
|
||||
|
||||
function pingDestination() {
|
||||
var target = {
|
||||
'name': vm0.name,
|
||||
'endpoint': vm0.endpoint,
|
||||
'username': vm0.username,
|
||||
'password': vm0.password
|
||||
};
|
||||
console.log('Ping target:' + angular.toJson(target));
|
||||
PingDestinationService(target)
|
||||
.success(pingDestinationSuccess)
|
||||
.error(pingDestinationFailed);
|
||||
}
|
||||
function pingDestinationSuccess(data, status) {
|
||||
alert('Successful ping target.');
|
||||
}
|
||||
function pingDestinationFailed(data, status) {
|
||||
alert('Failed ping target:' + data);
|
||||
}
|
||||
}
|
||||
|
||||
function createDestination() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/system-management/create-destination.directive.html',
|
||||
'scope': {
|
||||
'action': '@',
|
||||
'targetId': '@',
|
||||
'reload': '&'
|
||||
},
|
||||
'link': link,
|
||||
'controller': CreateDestinationController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
element.find('#createDestinationModal').on('hidden.bs.modal', function() {
|
||||
scope.form.$setPristine();
|
||||
});
|
||||
|
||||
ctrl.save = save;
|
||||
|
||||
function save(destination) {
|
||||
if(destination) {
|
||||
console.log('destination:' + angular.toJson(destination));
|
||||
switch(ctrl.action) {
|
||||
case 'ADD_NEW':
|
||||
ctrl.create(destination);
|
||||
break;
|
||||
case 'EDIT':
|
||||
ctrl.update(destination);
|
||||
}
|
||||
element.find('#createDestinationModal').modal('hide');
|
||||
ctrl.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user