Dev new ui (#1)

* Updates for verfied tags deletion.

* Remove old UI.

* Move i18n folder.

* Updates for latest UI codes.

* make travis with latest dev code.

* update test code

* add cat log

* cat nginx

* cat nginx

* fix template error

* remove --with-notary

* remove controller test

* fix controller test bug

* modify controller test

* debug controller test

* update controller test

* update index title to harbor, discussed with Kun.

* Update package.json

* Merge latest UI changes.

* remove git
This commit is contained in:
Yan 2017-03-20 18:51:20 +08:00 committed by GitHub
parent 800b9e34c3
commit 038d7dd90c
305 changed files with 106253 additions and 16499 deletions

View File

@ -31,6 +31,7 @@ env:
before_install:
- sudo ./tests/hostcfg.sh
- sudo ./tests/generateCerts.sh
- sudo ./make/prepare
install:
@ -92,12 +93,12 @@ script:
- goveralls -coverprofile=profile.cov -service=travis-ci
- docker-compose -f make/docker-compose.test.yml down
- sudo make/prepare
- sudo rm -rf /data/config/*
- docker-compose -f make/dev/docker-compose.yml up -d
- sudo rm -rf /data/config/*
- ls /data/cert
- sudo make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=danieljt/harbor-clarity-base:0.8.4 NOTARYFLAG=true
- docker ps
- go run tests/startuptest.go http://localhost/
- go run tests/startuptest.go https://localhost/
- go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD}
# - sudo ./tests/testprepare.sh

View File

@ -80,6 +80,12 @@ REGISTRYPROJECTNAME=vmware
DEVFLAG=true
NOTARYFLAG=false
REGISTRYVERSION=2.6.0
NGINXVERSION=1.11.5
PHOTONVERSION=1.0
NOTARYVERSION=server-0.5.0-fix
NOTARYSIGNERVERSION=signer-0.5.0
MARIADBVERSION=10.1.10
HTTPPROXY=
#clarity parameters
CLARITYIMAGE=danieljt/harbor-clarity-base[:tag]
@ -207,7 +213,11 @@ compile_jobservice:
compile_clarity:
@echo "compiling binary for clarity ui..."
@$(DOCKERCMD) run --rm -v $(UIPATH)/static/new-ui:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src -v $(UINGPATH)/src/app:$(CLARITYSEEDPATH)/src/app $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT)
@if [ "$(HTTPPROXY)" != "" ] ; then \
$(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT) -p $(HTTPPROXY); \
else \
$(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT); \
fi
@echo "Done."
compile_normal: compile_clarity compile_adminserver compile_ui compile_jobservice
@ -292,12 +302,12 @@ package_offline: compile build modify_composefile
@echo "pulling nginx and registry..."
@$(DOCKERPULL) registry:$(REGISTRYVERSION)
@$(DOCKERPULL) nginx:1.11.5
@$(DOCKERPULL) nginx:$(NGINXVERSION)
@if [ "$(NOTARYFLAG)" = "true" ] ; then \
echo "pulling notary and mariadb..."; \
$(DOCKERPULL) jiangd/notary:server-0.5.0-fix; \
$(DOCKERPULL) notary:signer-0.5.0; \
$(DOCKERPULL) mariadb:10.1.10; \
$(DOCKERPULL) jiangd/notary:$(NOTARYVERSION); \
$(DOCKERPULL) notary:$(NOTARYSIGNERVERSION); \
$(DOCKERPULL) mariadb:$(MARIADBVERSION); \
fi
@echo "saving harbor docker image"
@ -308,8 +318,8 @@ package_offline: compile build modify_composefile
$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \
$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \
$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \
nginx:1.11.5 registry:$(REGISTRYVERSION) photon:1.0 \
jiangd/notary:server-0.5.0-fix notary:signer-0.5.0 mariadb:10.1.10; \
nginx:$(NGINXVERSION) registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) \
jiangd/notary:$(NOTARYVERSION) notary:$(NOTARYSIGNERVERSION) mariadb:$(MARIADBVERSION); \
else \
$(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \
$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \
@ -317,7 +327,7 @@ package_offline: compile build modify_composefile
$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \
$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \
$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \
nginx:1.11.5 registry:$(REGISTRYVERSION) photon:1.0 ; \
nginx:$(NGINXVERSION) registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) ; \
fi
@if [ "$(NOTARYFLAG)" = "true" ] ; then \
@ -325,14 +335,14 @@ package_offline: compile build modify_composefile
$(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \
$(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \
$(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \
$(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) ; \
$(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \
$(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME) ; \
else \
$(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \
$(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \
$(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \
$(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \
$(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \
$(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME) ; \
$(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) ; \
fi
@rm -rf $(HARBORPKG)

View File

@ -1,12 +1,18 @@
FROM node:7.5.0
COPY angular-cli.json /
COPY index.html /
COPY entrypoint.sh /
RUN mkdir -p /clarity-seed
RUN npm install -g @angular/cli && \
chmod u+x entrypoint.sh
COPY src/ui_ng/package.json /clarity-seed
COPY src/ui_ng/tslint.json /clarity-seed
COPY src/ui_ng/typings.json /clarity-seed
COPY src/ui_ng/yarn.lock /clarity-seed
COPY make/dev/nodeclarity/angular-cli.json /clarity-seed
COPY make/dev/nodeclarity/entrypoint.sh /
VOLUME ["/clarity-seed", "/clarity-seed/dist"]
WORKDIR /clarity-seed
ENTRYPOINT ["/entrypoint.sh"]
RUN npm install -g @angular/cli && \
npm install && \
chmod u+x /entrypoint.sh
VOLUME ["/clarity-seed", "/clarity-seed/dist"]

View File

@ -1,11 +1,25 @@
#!/bin/bash
set -e
cd /clarity-seed
rm -rf dist/*
cp /angular-cli.json /clarity-seed
npm_proxy=
while getopts p: option
do
case "${option}"
in
p) npm_proxy=${OPTARG};;
esac
done
if [ ! -z "$npm_proxy" -a "$npm_proxy" != " " ]; then
npm config set proxy $npm_proxy
fi
npm install
ng build
cp /index.html dist/index.html
cp -r ./src/i18n/ dist/

View File

@ -14,12 +14,8 @@ ENV MYSQL_USR root \
COPY src/ui/views /go/bin/views
COPY src/ui/static /go/bin/static
COPY src/favicon.ico /go/bin/favicon.ico
COPY make/jsminify.sh /tmp/jsminify.sh
RUN chmod u+x /go/bin/harbor_ui \
&& timestamp=`date '+%s'` \
&& /tmp/jsminify.sh /go/bin/views/sections/script-include.htm /go/bin/static/resources/js/harbor.app.min.$timestamp.js /go/bin/ \
&& sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /go/bin/views/sections/script-min-include.htm
RUN chmod u+x /go/bin/harbor_ui
WORKDIR /go/bin/
ENTRYPOINT ["/go/bin/harbor_ui"]

View File

@ -1,71 +0,0 @@
#!/bin/bash
set -e
echo "This shell will minify the Javascript in Harbor project."
echo "Usage: #jsminify [src] [dest] [basedir]"
#prepare workspace
rm -rf $2 /tmp/harbor.app.temp.js
if [ -z $3 ]
then
BASEPATH=/go/bin
else
BASEPATH=$3
fi
#concat the js files from js include file
echo "Concat js files..."
cat $1 | while read LINE || [[ -n $LINE ]]
do
if [ -n "$LINE" ]
then
TEMP="$BASEPATH""$LINE"
cat `echo "$TEMP" | sed 's/<script src=\"//g' | sed 's/\"><\/script>//g'` >> /tmp/harbor.app.temp.js
printf "\n" >> /tmp/harbor.app.temp.js
fi
done
# If you want run this script on Mac OS X,
# I suggest you install gnu-sed (whth --with-default-names option).
# $ brew install gnu-sed --with-default-names
# Reference:
# http://stackoverflow.com/a/27834828/3167471
#remove space
echo "Remove space.."
sed 's/ \+/ /g' -i /tmp/harbor.app.temp.js
#remove '//' and '/*'
echo "Remove '//'and '/*' annotation..."
sed '/^\/\//'d -i /tmp/harbor.app.temp.js
sed '/\/\*/{/\*\//d;:a;N;/\*\//d;ba};s,//.*,,' -i /tmp/harbor.app.temp.js
cat > $2 << EOF
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
EOF
#remove '\n'
echo "Remove CR ..."
cat /tmp/harbor.app.temp.js | tr -d '\n' >> $2
#clear workspace
rm -rf /tmp/harbor.app.temp.js
echo "Done."
exit 0

View File

@ -1,19 +1,14 @@
FROM library/photon:1.0
RUN mkdir /harbor/
RUN tdnf install -y sed
COPY ./make/dev/ui/harbor_ui /harbor/
COPY ./src/ui/views /harbor/views
COPY ./src/ui/static /harbor/static
COPY ./src/favicon.ico /harbor/favicon.ico
COPY ./make/jsminify.sh /tmp/jsminify.sh
RUN chmod u+x /harbor/harbor_ui \
&& timestamp=`date '+%s'` \
&& /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \
&& sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /harbor/views/sections/script-min-include.htm
RUN chmod u+x /harbor/harbor_ui
WORKDIR /harbor/
ENTRYPOINT ["/harbor/harbor_ui"]

View File

@ -1,23 +0,0 @@
package controllers
// AccountSettingController handles request to /account_setting
type AccountSettingController struct {
BaseController
}
// Get renders the account settings page
func (asc *AccountSettingController) Get() {
var isAdminForLdap bool
sessionUserID, ok := asc.GetSession("userId").(int)
if !ok {
asc.Redirect("/", 302)
}
if ok && sessionUserID == 1 {
isAdminForLdap = true
}
if asc.AuthMode == "db_auth" || isAdminForLdap {
asc.Forward("page_title_account_setting", "account-settings.htm")
} else {
asc.Redirect("/dashboard", 302)
}
}

View File

@ -1,32 +0,0 @@
package controllers
import (
"net/http"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/utils/log"
)
// AddNewController handles requests to /add_new
type AddNewController struct {
BaseController
}
// Get renders the add new page
func (anc *AddNewController) Get() {
sessionUserID := anc.GetSession("userId")
anc.Data["AddNew"] = false
if sessionUserID != nil {
isAdmin, err := dao.IsAdminRole(sessionUserID.(int))
if err != nil {
log.Errorf("Error occurred in IsAdminRole: %v", err)
anc.CustomAbort(http.StatusInternalServerError, "")
}
if isAdmin && anc.AuthMode == "db_auth" {
anc.Data["AddNew"] = true
anc.Forward("page_title_add_new", "sign-up.htm")
return
}
}
anc.CustomAbort(http.StatusUnauthorized, "Status Unauthorized.")
}

View File

@ -1,27 +0,0 @@
package controllers
import (
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/utils/log"
)
// AdminOptionController handles requests to /admin_option
type AdminOptionController struct {
BaseController
}
// Get renders the admin options page
func (aoc *AdminOptionController) Get() {
sessionUserID, ok := aoc.GetSession("userId").(int)
if ok {
isAdmin, err := dao.IsAdminRole(sessionUserID)
if err != nil {
log.Errorf("Error occurred in IsAdminRole: %v", err)
}
if isAdmin {
aoc.Forward("page_title_admin_option", "admin-options.htm")
return
}
}
aoc.Redirect("/dashboard", 302)
}

View File

@ -1,180 +1,27 @@
package controllers
import (
"bytes"
"html/template"
"net/http"
"os"
"path/filepath"
"strings"
"regexp"
"github.com/astaxie/beego"
"github.com/beego/i18n"
"github.com/vmware/harbor/src/common/config"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils"
"github.com/vmware/harbor/src/common/utils/email"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/auth"
"github.com/vmware/harbor/src/ui/config"
)
// 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
UseCompressedJS bool
}
type langType struct {
Lang string
Name string
}
const (
viewPath = "sections"
prefixNg = ""
defaultLang = "en-US"
defaultRootCert = "/harbor_storage/ca_download/ca.crt"
)
var supportLanguages map[string]langType
var mappingLangNames map[string]string
// Prepare extracts the language information from request and populate data for rendering templates.
func (b *BaseController) Prepare() {
var lang string
var langHasChanged bool
var showDownloadCert bool
langRequest := b.GetString("lang")
if langRequest != "" {
lang = langRequest
langHasChanged = true
} else {
langCookie, err := b.Ctx.Request.Cookie("language")
if err != nil {
log.Errorf("Error occurred in Request.Cookie: %v", err)
}
if langCookie != nil {
lang = langCookie.Value
} else {
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
}
}
langHasChanged = true
}
}
if langHasChanged {
if _, exist := supportLanguages[lang]; !exist { //Check if support the request language.
lang = defaultLang //Set default language if not supported.
}
cookies := &http.Cookie{
Name: "language",
Value: lang,
HttpOnly: true,
Path: "/",
}
http.SetCookie(b.Ctx.ResponseWriter, cookies)
}
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
authMode, err := config.AuthMode()
if err != nil {
log.Errorf("failed to get auth mode: %v", err)
b.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if authMode == "" {
authMode = "db_auth"
}
b.AuthMode = authMode
b.Data["AuthMode"] = b.AuthMode
useCompressedJS := os.Getenv("USE_COMPRESSED_JS")
if useCompressedJS == "on" {
b.UseCompressedJS = true
}
m, err := filepath.Glob(filepath.Join("static", "resources", "js", "harbor.app.min.*.js"))
if err != nil || len(m) == 0 {
b.UseCompressedJS = false
}
b.SelfRegistration, err = config.SelfRegistration()
if err != nil {
log.Errorf("failed to get self registration: %v", err)
b.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
b.Data["SelfRegistration"] = b.SelfRegistration
sessionUserID := b.GetSession("userId")
if sessionUserID != nil {
isAdmin, err := dao.IsAdminRole(sessionUserID.(int))
if err != nil {
log.Errorf("Error occurred in IsAdminRole: %v", err)
}
if isAdmin {
if _, err := os.Stat(defaultRootCert); !os.IsNotExist(err) {
showDownloadCert = true
}
}
}
b.Data["ShowDownloadCert"] = showDownloadCert
}
// 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"] = b.Tr(title)
b.LayoutSections = make(map[string]string)
b.LayoutSections["HeaderInclude"] = filepath.Join(prefixNg, viewPath, "header-include.htm")
if b.UseCompressedJS {
b.LayoutSections["HeaderScriptInclude"] = filepath.Join(prefixNg, viewPath, "script-min-include.htm")
} else {
b.LayoutSections["HeaderScriptInclude"] = filepath.Join(prefixNg, viewPath, "script-include.htm")
}
log.Debugf("Loaded HeaderScriptInclude file: %s", b.LayoutSections["HeaderScriptInclude"])
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
beego.Controller
i18n.Locale
}
// Render returns nil.
@ -182,6 +29,12 @@ func (cc *CommonController) Render() error {
return nil
}
type messageDetail struct {
Hint string
URL string
UUID string
}
// Login handles login request from UI.
func (cc *CommonController) Login() {
principal := cc.GetString("principal")
@ -209,18 +62,6 @@ func (cc *CommonController) LogOut() {
cc.DestroySession()
}
// SwitchLanguage User can swith to prefered language
func (cc *CommonController) SwitchLanguage() {
lang := cc.GetString("lang")
hash := cc.GetString("hash")
if _, exist := supportLanguages[lang]; !exist {
lang = defaultLang
}
cc.SetSession("lang", lang)
cc.Data["Lang"] = lang
cc.Redirect(cc.Ctx.Request.Header.Get("Referer")+hash, http.StatusFound)
}
// UserExists checks if user exists when user input value in sign in form.
func (cc *CommonController) UserExists() {
target := cc.GetString("target")
@ -243,6 +84,110 @@ func (cc *CommonController) UserExists() {
cc.ServeJSON()
}
// SendEmail verifies the Email address and contact SMTP server to send reset password Email.
func (cc *CommonController) SendEmail() {
emailStr := 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,}))$`, emailStr)
if !pass {
cc.CustomAbort(http.StatusBadRequest, "email_content_illegal")
} else {
queryUser := models.User{Email: emailStr}
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/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 := config.ExtEndpoint
if harborURL == "" {
harborURL = "localhost"
}
uuid := utils.GenerateRandomString()
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 := email.Mail{
From: config["from"],
To: []string{emailStr},
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: emailStr}
dao.UpdateUserResetUUID(user)
}
}
// 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")
}
}
func init() {
//conf/app.conf -> os.Getenv("config_path")
configPath := os.Getenv("CONFIG_PATH")
@ -253,24 +198,4 @@ func init() {
}
}
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, lang := range langs {
t := langType{
Lang: lang,
Name: names[i],
}
langTypes = append(langTypes, &t)
supportLanguages[lang] = t
if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil {
log.Errorf("Fail to set message file: %s", err.Error())
}
}
}

View File

@ -1,23 +0,0 @@
package controllers
// ChangePasswordController handles request to /change_password
type ChangePasswordController struct {
BaseController
}
// Get renders the change password page
func (cpc *ChangePasswordController) Get() {
var isAdminForLdap bool
sessionUserID, ok := cpc.GetSession("userId").(int)
if !ok {
cpc.Redirect("/", 302)
}
if ok && sessionUserID == 1 {
isAdminForLdap = true
}
if cpc.AuthMode == "db_auth" || isAdminForLdap {
cpc.Forward("page_title_change_password", "change-password.htm")
} else {
cpc.Redirect("/dashboard", 302)
}
}

View File

@ -42,29 +42,12 @@ func init() {
beego.AddTemplateExt("htm")
beego.Router("/", &IndexController{})
beego.Router("/dashboard", &DashboardController{})
beego.Router("/project", &ProjectController{})
beego.Router("/repository", &RepositoryController{})
beego.Router("/sign_up", &SignUpController{})
beego.Router("/add_new", &AddNewController{})
beego.Router("/account_setting", &AccountSettingController{})
beego.Router("/change_password", &ChangePasswordController{})
beego.Router("/admin_option", &AdminOptionController{})
beego.Router("/forgot_password", &ForgotPasswordController{})
beego.Router("/reset_password", &ResetPasswordController{})
beego.Router("/search", &SearchController{})
beego.Router("/login", &CommonController{}, "post:Login")
beego.Router("/log_out", &CommonController{}, "get:LogOut")
beego.Router("/reset", &CommonController{}, "post:ResetPassword")
beego.Router("/userExists", &CommonController{}, "post:UserExists")
beego.Router("/sendEmail", &CommonController{}, "get:SendEmail")
beego.Router("/language", &CommonController{}, "get:SwitchLanguage")
beego.Router("/optional_menu", &OptionalMenuController{})
beego.Router("/navigation_header", &NavigationHeaderController{})
beego.Router("/navigation_detail", &NavigationDetailController{})
beego.Router("/sign_in", &SignInController{})
//Init user Info
//admin = &usrInfo{adminName, adminPwd}
@ -82,68 +65,7 @@ func TestMain(t *testing.T) {
w := httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_index</title>"), "http respond should have '<title>page_title_index</title>'")
r, _ = http.NewRequest("GET", "/dashboard", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/dashboard' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_dashboard</title>"), "http respond should have '<title>page_title_dashboard</title>'")
r, _ = http.NewRequest("GET", "/project", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/project' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_project</title>"), "http respond should have '<title>page_title_project</title>'")
r, _ = http.NewRequest("GET", "/repository", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/repository' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_repository</title>"), "http respond should have '<title>page_title_repository</title>'")
r, _ = http.NewRequest("GET", "/sign_up", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/sign_up' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_sign_up</title>"), "http respond should have '<title>page_title_sign_up</title>'")
r, _ = http.NewRequest("GET", "/add_new", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(401), w.Code, "'/add_new' httpStatusCode should be 401")
r, _ = http.NewRequest("GET", "/account_setting", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(302), w.Code, "'/account_setting' httpStatusCode should be 302")
r, _ = http.NewRequest("GET", "/change_password", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(302), w.Code, "'/change_password' httpStatusCode should be 302")
r, _ = http.NewRequest("GET", "/admin_option", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(302), w.Code, "'/admin_option' httpStatusCode should be 302")
r, _ = http.NewRequest("GET", "/forgot_password", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/forgot_password' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_forgot_password</title>"), "http respond should have '<title>page_title_forgot_password</title>'")
r, _ = http.NewRequest("GET", "/reset_password", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(302), w.Code, "'/reset_password' httpStatusCode should be 302")
r, _ = http.NewRequest("GET", "/search", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(200), w.Code, "'/search' httpStatusCode should be 200")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_search</title>"), "http respond should have '<title>page_title_searc</title>'")
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>Harbor</title>"), "http respond should have '<title>Harbor</title>'")
r, _ = http.NewRequest("POST", "/login", nil)
w = httptest.NewRecorder()
@ -171,37 +93,4 @@ func TestMain(t *testing.T) {
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(400), w.Code, "'/sendEmail' httpStatusCode should be 400")
r, _ = http.NewRequest("GET", "/language", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
assert.Equal(int(302), w.Code, "'/language' httpStatusCode should be 302")
r, _ = http.NewRequest("GET", "/optional_menu", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
//fmt.Printf("/optional_menu: %s\n", w.Body)
assert.Equal(int(200), w.Code, "'/optional_menu' httpStatusCode should be 200")
//assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title> </title>"), "http respond should have '<title> </title>'")
r, _ = http.NewRequest("GET", "/navigation_header", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
//fmt.Printf("/navigation_header: %s\n", w.Body)
assert.Equal(int(200), w.Code, "'/navigation_header' httpStatusCode should be 200")
//assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title> </title>"), "http respond should have '<title> </title>'")
r, _ = http.NewRequest("GET", "/navigation_detail", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
//fmt.Printf("/navigation_detail: %s\n", w.Body)
assert.Equal(int(200), w.Code, "'/navigation_detail' httpStatusCode should be 200")
//assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title> </title>"), "http respond should have '<title> </title>'")
r, _ = http.NewRequest("GET", "/sign_in", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
//fmt.Printf("/sign_in: %s\n", w.Body)
assert.Equal(int(200), w.Code, "'/sign_in' httpStatusCode should be 200")
//assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title> </title>"), "http respond should have '<title> </title>'")
}

View File

@ -1,11 +0,0 @@
package controllers
// DashboardController handles requests to /dashboard
type DashboardController struct {
BaseController
}
// Get renders the dashboard page
func (dc *DashboardController) Get() {
dc.Forward("page_title_dashboard", "dashboard.htm")
}

View File

@ -1,11 +1,14 @@
package controllers
import "github.com/astaxie/beego"
// IndexController handles request to /
type IndexController struct {
BaseController
beego.Controller
}
// Get renders the index page
func (ic *IndexController) Get() {
ic.Forward("page_title_index", "index.htm")
ic.TplExt = "html"
ic.TplName = "index.html"
}

View File

@ -1,36 +0,0 @@
package controllers
import (
"net/http"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
)
// NavigationDetailController handles requests to /navigation_detail
type NavigationDetailController struct {
BaseController
}
// Get renders user's navigation details header
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 = "navigation-detail.htm"
ndc.Render()
}

View File

@ -1,39 +0,0 @@
package controllers
import (
"net/http"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
)
// NavigationHeaderController handles requests to /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 = "navigation-header.htm"
nhc.Render()
}

View File

@ -1,64 +0,0 @@
package controllers
import (
"net/http"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
)
// OptionalMenuController handles request to /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
var allowAddNew bool
var isAdminForLdap bool
var allowSettingAccount 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
if userID == 1 {
isAdminForLdap = true
}
if omc.AuthMode == "db_auth" || isAdminForLdap {
allowSettingAccount = true
}
isAdmin, err := dao.IsAdminRole(sessionUserID.(int))
if err != nil {
log.Errorf("Error occurred in IsAdminRole: %v", err)
omc.CustomAbort(http.StatusInternalServerError, "")
}
if isAdmin && omc.AuthMode == "db_auth" {
allowAddNew = true
}
}
omc.Data["AddNew"] = allowAddNew
omc.Data["SettingAccount"] = allowSettingAccount
omc.Data["HasLoggedIn"] = hasLoggedIn
omc.TplName = "optional-menu.htm"
omc.Render()
}

View File

@ -1,169 +0,0 @@
package controllers
import (
"bytes"
"net/http"
"regexp"
"text/template"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils"
email_util "github.com/vmware/harbor/src/common/utils/email"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
)
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/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, err := config.ExtEndpoint()
if err != nil {
log.Errorf("failed to get domain name: %v", err)
cc.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if harborURL == "" {
harborURL = "localhost"
}
uuid := utils.GenerateRandomString()
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")
}
emailSettings, err := config.Email()
if err != nil {
log.Errorf("failed to get email configurations: %v", err)
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
}
mail := email_util.Mail{
From: emailSettings.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 /forgot_password
type ForgotPasswordController struct {
BaseController
}
// Get renders forgot password page
func (fpc *ForgotPasswordController) Get() {
fpc.Forward("page_title_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("page_title_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")
}
}

View File

@ -1,35 +0,0 @@
package controllers
import (
"net/http"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
)
// ProjectController handles requests to /project
type ProjectController struct {
BaseController
}
// Get renders project page
func (pc *ProjectController) Get() {
var err error
isSysAdmin := false
uid := pc.GetSession("userId")
if uid != nil {
isSysAdmin, err = dao.IsAdminRole(uid)
if err != nil {
log.Warningf("Error in checking Admin Role for user, id: %d, error: %v", uid, err)
isSysAdmin = false
}
}
onlyAdmin, err := config.OnlyAdminCreateProject()
if err != nil {
log.Errorf("failed to determine whether only admin can create projects: %v", err)
pc.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
pc.Data["CanCreate"] = !onlyAdmin || isSysAdmin
pc.Forward("page_title_project", "project.htm")
}

View File

@ -1,25 +0,0 @@
package controllers
import (
"net/http"
"strings"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
)
// RepositoryController handles request to /repository
type RepositoryController struct {
BaseController
}
// Get renders repository page
func (rc *RepositoryController) Get() {
url, err := config.ExtEndpoint()
if err != nil {
log.Errorf("failed to get domain name: %v", err)
rc.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
rc.Data["HarborRegUrl"] = strings.Split(url, "://")[1]
rc.Forward("page_title_repository", "repository.htm")
}

View File

@ -1,11 +0,0 @@
package controllers
// SearchController handles request to /search
type SearchController struct {
BaseController
}
// Get rendlers search bar
func (sc *SearchController) Get() {
sc.Forward("page_title_search", "search.htm")
}

View File

@ -1,40 +0,0 @@
package controllers
import (
"net/http"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
)
// SignInController handles requests to /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["AuthMode"] = sic.AuthMode
sic.Data["Username"] = username
sic.Data["HasLoggedIn"] = hasLoggedIn
sic.TplName = "sign-in.htm"
sic.Render()
}

View File

@ -1,19 +0,0 @@
package controllers
import (
"net/http"
)
// SignUpController handles requests to /sign_up
type SignUpController struct {
BaseController
}
// Get renders sign up page
func (suc *SignUpController) Get() {
if suc.AuthMode != "db_auth" || !suc.SelfRegistration {
suc.CustomAbort(http.StatusForbidden, "")
}
suc.Data["AddNew"] = false
suc.Forward("page_title_sign_up", "sign-up.htm")
}

View File

@ -26,39 +26,38 @@ import (
func initRouters() {
beego.SetStaticPath("static/resources", "static/resources")
beego.SetStaticPath("static/vendors", "static/vendors")
beego.SetStaticPath("/ng", "./static/new-ui")
beego.SetStaticPath("/ng/harbor", "./static/new-ui")
beego.SetStaticPath("/ng/harbor/dashboard", "./static/new-ui")
beego.SetStaticPath("/ng/harbor/projects", "./static/new-ui")
beego.SetStaticPath("/ng/harbor/users", "./static/new-ui")
beego.SetStaticPath("/static", "./static")
beego.SetStaticPath("/i18n", "./static/i18n")
//Page Controllers:
beego.Router("/", &controllers.IndexController{})
beego.Router("/dashboard", &controllers.DashboardController{})
beego.Router("/project", &controllers.ProjectController{})
beego.Router("/repository", &controllers.RepositoryController{})
beego.Router("/sign_up", &controllers.SignUpController{})
beego.Router("/add_new", &controllers.AddNewController{})
beego.Router("/account_setting", &controllers.AccountSettingController{})
beego.Router("/change_password", &controllers.ChangePasswordController{})
beego.Router("/admin_option", &controllers.AdminOptionController{})
beego.Router("/forgot_password", &controllers.ForgotPasswordController{})
beego.Router("/reset_password", &controllers.ResetPasswordController{})
beego.Router("/search", &controllers.SearchController{})
beego.Router("/sign-in", &controllers.IndexController{})
beego.Router("/sign-up", &controllers.IndexController{})
beego.Router("/password-reset", &controllers.IndexController{})
beego.Router("/harbor", &controllers.IndexController{})
beego.Router("/harbor/sign-in", &controllers.IndexController{})
beego.Router("/harbor/sign-up", &controllers.IndexController{})
beego.Router("/harbor/dashboard", &controllers.IndexController{})
beego.Router("/harbor/projects", &controllers.IndexController{})
beego.Router("/harbor/projects/:id/repository", &controllers.IndexController{})
beego.Router("/harbor/projects/:id/replication", &controllers.IndexController{})
beego.Router("/harbor/projects/:id/member", &controllers.IndexController{})
beego.Router("/harbor/projects/:id/log", &controllers.IndexController{})
beego.Router("/harbor/users", &controllers.IndexController{})
beego.Router("/harbor/logs", &controllers.IndexController{})
beego.Router("/harbor/replications", &controllers.IndexController{})
beego.Router("/harbor/replications/endpoints", &controllers.IndexController{})
beego.Router("/harbor/replications/rules", &controllers.IndexController{})
beego.Router("/harbor/tags", &controllers.IndexController{})
beego.Router("/harbor/configs", &controllers.IndexController{})
beego.Router("/login", &controllers.CommonController{}, "post:Login")
beego.Router("/log_out", &controllers.CommonController{}, "get:LogOut")
beego.Router("/reset", &controllers.CommonController{}, "post:ResetPassword")
beego.Router("/userExists", &controllers.CommonController{}, "post:UserExists")
beego.Router("/sendEmail", &controllers.CommonController{}, "get:SendEmail")
beego.Router("/language", &controllers.CommonController{}, "get:SwitchLanguage")
beego.Router("/optional_menu", &controllers.OptionalMenuController{})
beego.Router("/navigation_header", &controllers.NavigationHeaderController{})
beego.Router("/navigation_detail", &controllers.NavigationDetailController{})
beego.Router("/sign_in", &controllers.SignInController{})
//API:
beego.Router("/api/search", &api.SearchAPI{})

BIN
src/ui/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,15 +0,0 @@
reset_email_hint = Please click this link to reset your password
reset_email_subject = Reset your password of Harbor account
page_title_index = Harbor
page_title_dashboard = Dashboard - Harbor
page_title_account_setting = Account Settings - Harbor
page_title_reset_password = Reset Password - Harbor
page_title_change_password = Change Password - Harbor
page_title_forgot_password = Forgot Password - Harbor
page_title_project = Project - Harbor
page_title_repository = Project Details - Harbor
page_title_search = Search - Harbor
page_title_sign_up = Sign Up - Harbor
page_title_add_new = Add New User - Harbor
page_title_admin_option = Admin Options - Harbor

View File

@ -1,15 +0,0 @@
reset_email_hint = 请点击下面的链接进行重置密码操作
reset_email_subject = 重置您的 Harbor 密码
page_title_index = Harbor
page_title_dashboard = 控制面板 - Harbor
page_title_account_setting = 账户设置 - Harbor
page_title_reset_password = 重置密码 - Harbor
page_title_change_password = 修改密码 - Harbor
page_title_forgot_password = 忘记密码 - Harbor
page_title_project = 项目 - Harbor
page_title_repository = 项目明细 - Harbor
page_title_search = 搜索 - Harbor
page_title_sign_up = 注册 - Harbor
page_title_add_new = 增加用户 - Harbor
page_title_admin_option = 管理员选项 - Harbor

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

16
src/ui/static/index.html Normal file
View File

@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Harbor</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2">
</head>
<body>
<harbor-app>Loading...</harbor-app>
<script type="text/javascript" src="inline.bundle.js"></script><script type="text/javascript" src="scripts.bundle.js"></script><script type="text/javascript" src="styles.bundle.js"></script><script type="text/javascript" src="vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
</html>

View File

@ -0,0 +1,146 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ var parentJsonpFunction = window["webpackJsonp"];
/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [], result;
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId])
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/ while(resolves.length)
/******/ resolves.shift()();
/******/ if(executeModules) {
/******/ for(i=0; i < executeModules.length; i++) {
/******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]);
/******/ }
/******/ }
/******/ return result;
/******/ };
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // objects to store loaded and loading chunks
/******/ var installedChunks = {
/******/ 4: 0
/******/ };
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = function requireEnsure(chunkId) {
/******/ if(installedChunks[chunkId] === 0)
/******/ return Promise.resolve();
/******/
/******/ // an Promise means "currently loading".
/******/ if(installedChunks[chunkId]) {
/******/ return installedChunks[chunkId][2];
/******/ }
/******/ // start chunk loading
/******/ var head = document.getElementsByTagName('head')[0];
/******/ var script = document.createElement('script');
/******/ script.type = 'text/javascript';
/******/ script.charset = 'utf-8';
/******/ script.async = true;
/******/ script.timeout = 120000;
/******/
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.src = __webpack_require__.p + "" + chunkId + ".chunk.js";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var chunk = installedChunks[chunkId];
/******/ if(chunk !== 0) {
/******/ if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
/******/ installedChunks[chunkId] = undefined;
/******/ }
/******/ };
/******/
/******/ var promise = new Promise(function(resolve, reject) {
/******/ installedChunks[chunkId] = [resolve, reject];
/******/ });
/******/ installedChunks[chunkId][2] = promise;
/******/
/******/ head.appendChild(script);
/******/ return promise;
/******/ };
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // on error function for async loading
/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
/******/ })
/************************************************************************/
/******/ ([]);
//# sourceMappingURL=inline.bundle.map

File diff suppressed because one or more lines are too long

8877
src/ui/static/main.bundle.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,14 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

View File

@ -1,35 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.switch-pane-admin-options {
display: inline;
width: 340px;
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;
}
.inline-help-config {
padding: 6px;
}

View File

@ -1,28 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.up-section .up-table-pane {
overflow-y: auto;
height: 220px;
margin-top: -10px;
}
.up-section .dl-horizontal dt{
line-height: 25px;
}
.up-section .dl-horizontal dt {
text-align: left;
}

View File

@ -1,17 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.create-destination {
height: 275px;
}

View File

@ -1,41 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.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;
z-index: 10;
}
.footer p {
padding-top: 8px;
color: #FFFFFF;
margin-left: auto;
margin-right: auto;
width: 385px;
}

View File

@ -1,145 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.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: -25px;
}
.navbar-form {
margin-top: 0;
padding-right: 0;
}
.search-icon {
background: url("/static/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;
}
.loading-progress {
display: inline-block;
position: relative;
background-image: url('/static/resources/img/loading.gif');
background-position: center;
background-size: 107px;
width: 1em;
height: 1.2em;
margin-bottom: 2px;
vertical-align: middle;
}
a:hover, a:visited, a:link {
text-decoration: none;
}

View File

@ -1,142 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
body {
margin: 0;
padding: 0;
width: 100%;
height: 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;
width: 100%;
height: 100%;
min-height: 1px;
overflow-y: auto;
overflow-x: hidden;
}
.up-section {
position: relative;
padding: 15px 15px 15px;
margin: 20px 0 0 -15px;
background-color: #FFFFFF;
height: 277px;
}
.up-section h4 label {
margin-left: 5px;
}
.right-part {
padding-right: 0;
margin-right: -15px;
}
.thumbnail {
margin-top: 10px;
display: inline-block;
border: none;
padding: 2px;
box-shadow: none;
width: 30%;
vertical-align: top;
}
.down-section {
position: relative;
padding: 15px 15px 15px;
margin: 20px -15px 0 -15px;
background-color: #FFFFFF;
height: 350px;
}
.down-section-left {
margin-right: 0;
}
.down-section ul {
padding: 0;
margin-left: 30px;
margin-right: 20px;
}
.long-line {
overflow: hidden;
width: 100%;
}
.long-line-margin-right {
margin-right: 30px;
}
.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;
text-align: left;
overflow-y: hidden;
}

View File

@ -1,95 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.container-custom {
position: relative;
min-height: 1px;
}
.extend-height {
height: 100%;
padding-left: 0;
padding-right: 0;
}
.section {
padding: 15px;
margin-top: 20px;
background-color: #FFFFFF;
height: 100%;
min-height: 672px;
width: 100%;
}
.search-pane {
margin: 0 10px 0 10px;
height: 3em;
}
.table>tbody>tr>td, .table>tbody>tr>th {
vertical-align: middle;
}
.table-header {
margin-bottom: 0;
}
.table-head-container {
}
.table-body-container {
overflow-y: auto;
}
.gutter {
margin: 0 1em 0 1em;
}
.project-pane {
margin: 0 10px 0 10px;
}
.pane {
height: auto;
min-height: 1px;
}
.tab-pane {
min-height: 1px;
max-height: 1px;
}
.sub-pane {
min-height: 380px;
overflow-y: auto;
}
.well-custom {
width: 100%;
background-color: #f5f5f5;
background-image: none;
z-index: 10;
}
.form-custom {
margin-top: 1em;
}
.empty-hint {
text-align: center;
vertical-align: middle;
}

View File

@ -1,97 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.create-policy {
height: 565px;
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: hidden;
width: 100%;
}
#upon-pane {
margin-top: 10px;
height: 320px;
}
#upon-pane table>tbody>tr {
cursor: all-scroll;
}
#down-pane {
height: 582px;
}
.sub-pane-split {
margin: 15px;
height: auto;
min-height: 50px;
}
.well-split {
margin: 0;
position: relative;
}
.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;
}
.label-custom {
margin: 0 5px 0 10px;
}
.dialog-message {
padding: 15px;
}

View File

@ -1,159 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.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;
}
.alert-custom {
position: relative;
bottom: 42px;
z-index: 99;
width: 100%;
margin-left: auto;
margin-right: auto;
padding: 10px;
background-color: #f2dede;
background-image: none;
}
.alert-custom .close {
right: 0;
}

View File

@ -1,23 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.search-result {
min-height: 200px;
max-height: 200px;
overflow-y: auto;
}
.search-result li {
margin-bottom: 15px;
}

View File

@ -1,62 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.main-title {
margin-top: 20px;
margin-left: 180px;
}
.main-content {
width: 60%;
margin-top: 40px;
}
.form-horizontal .control-label {
text-align: right;
}
.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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

View File

@ -1,35 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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.project_id === 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>

View File

@ -1,214 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.details')
.directive('retrieveProjects', retrieveProjects);
RetrieveProjectsController.$inject = ['$scope', 'nameFilter', '$filter', 'trFilter', 'ListProjectService', '$location', 'getParameterByName', 'CurrentProjectMemberService', '$window'];
function RetrieveProjectsController($scope, nameFilter, $filter, trFilter, ListProjectService, $location, getParameterByName, CurrentProjectMemberService, $window) {
var vm = this;
vm.projectName = '';
vm.isOpen = false;
vm.isProjectMember = false;
vm.target = $location.path().substr(1) || 'repositories';
vm.roleId = 0;
vm.isPublic = Number(getParameterByName('is_public', $location.absUrl()));
var DEFAULT_PAGE = 1;
vm.page = DEFAULT_PAGE;
vm.projects = [];
vm.retrieve = retrieve;
vm.filterInput = '';
vm.selectItem = selectItem;
vm.checkProjectMember = checkProjectMember;
function retrieve() {
ListProjectService(vm.projectName, vm.isPublic, vm.page)
.then(getProjectSuccess, getProjectFailed);
}
$scope.$watch('vm.page', function(current) {
if(current) {
vm.retrieve();
}
});
$scope.$watch('vm.isPublic', function(current) {
vm.projectType = vm.isPublic === 0 ? 'my_project_count' : 'public_project_count';
});
$scope.$watch('vm.selectedProject', function(current) {
if(current) {
vm.selectedId = current.project_id;
}
});
function parseNextLink(link) {
if(link === '') {
return false;
}
var parts = link.split(",");
for(var i in parts) {
var groups = /^\<(.*)\>;\srel=\"(\w+)\"$/.exec($.trim(parts[i]));
if(groups && groups.length > 2){
var url = groups[1];
var rel = groups[2];
if(rel === 'next') {
return {
'page': getParameterByName('page', url),
'rel' : rel
};
}
}
}
return false;
}
function getProjectSuccess(response) {
var partialProjects = response.data || [];
for(var i in partialProjects) {
vm.projects.push(partialProjects[i]);
}
var nextLink = parseNextLink(response.headers("Link") || '');
if(nextLink) {
vm.page = parseInt(nextLink.page);
} else {
if(vm.projects.length == 0 && vm.isPublic === 0){
$window.location.href = '/project';
}
if(getParameterByName('project_id', $location.absUrl())){
for(var i in vm.projects) {
var project = vm.projects[i];
if(project['project_id'] == getParameterByName('project_id', $location.absUrl())) {
vm.selectedProject = project;
break;
}
}
}
if(vm.selectedProject) {
$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) {
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', $filter('tr')('failed_to_get_project'));
$scope.$emit('raiseError', true);
console.log('Failed to list projects.');
}
function selectItem(item) {
vm.selectedProject = item;
$location.search('project_id', vm.selectedProject.project_id);
$scope.$emit('projectChanged', true);
}
$scope.$on('$locationChangeSuccess', function(e) {
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.isOpen = false;
vm.checkProjectMember(vm.selectedProject.project_id);
});
function checkProjectMember(projectId) {
CurrentProjectMemberService(projectId)
.success(getCurrentProjectMemberSuccess)
.error(getCurrentProjectMemberFailed);
}
function getCurrentProjectMemberSuccess(data, status) {
console.log('Successful get current project member:' + status);
vm.isProjectMember = true;
if(data && data['roles'] && data['roles'].length > 0) {
vm.roleId = data['roles'][0]['role_id'];
}
}
function getCurrentProjectMemberFailed(data, status) {
vm.isProjectMember = false;
console.log('Current user has no member for the project:' + status + ', location.url:' + $location.url());
vm.target = 'repositories';
}
}
function retrieveProjects() {
var directive = {
restrict: 'E',
templateUrl: '/static/resources/js/components/details/retrieve-projects.directive.html',
scope: {
'target': '=',
'isOpen': '=',
'selectedProject': '=',
'isPublic': '=',
'isProjectMember': '=',
'roleId': '='
},
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"],[data-original-title]').each(function () {
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
(($(this).popover('hide').data('bs.popover')||{}).inState||{}).click = false;
}
});
var targetId = $(e.target).attr('id');
if(targetId === 'switchPane' ||
targetId === 'retrievePane' ||
targetId === 'retrieveFilter') {
return;
}else{
ctrl.isOpen = false;
scope.$apply();
}
}
}
}
})();

View File

@ -1,19 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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>

View File

@ -1,64 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(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/resources/js/components/details/switch-pane-projects.directive.html',
scope: {
'isOpen': '=',
'selectedProject': '='
},
controller: SwitchPaneProjectsController,
controllerAs: 'vm',
bindToController: true
};
return directive;
}
})();

View File

@ -1,18 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div ng-show="toggleAlert" class="alert alert-danger alert-dismissible alert-custom" role="alert">
<button type="button" class="close" ng-click="close()"><span aria-hidden="true">&times;</span></button>
<strong>// 'caution' | tr //</strong>&nbsp;&nbsp;//message//
</div>

View File

@ -1,48 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.dismissable.alerts')
.directive('dismissableAlerts', dismissableAlerts);
function dismissableAlerts() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.html',
'link': link
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.close = function() {
scope.toggleAlert = false;
};
scope.$on('raiseAlert', function(e, val) {
console.log('received raiseAlert:' + angular.toJson(val));
if(val.show) {
scope.message = val.message;
scope.toggleAlert = true;
}else{
scope.message = '';
scope.toggleAlert = false;
}
});
}
}
})();

View File

@ -1,21 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular.module('harbor.dismissable.alerts', []);
})();

View File

@ -1,60 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.layout.element.height')
.directive('elementHeight', elementHeight);
function elementHeight($window) {
var directive = {
'restrict': 'A',
'link': link
};
return directive;
function link(scope, element, attrs) {
var w = angular.element($window);
scope.getDimension = function() {
return {'h' : w.height()};
};
if(!angular.isDefined(scope.subsHeight)) {scope.subsHeight = 110;}
if(!angular.isDefined(scope.subsSection)) {scope.subsSection = 32;}
if(!angular.isDefined(scope.subsSubPane)) {scope.subsSubPane = 226;}
if(!angular.isDefined(scope.subsTblBody)) {scope.subsTblBody = 40;}
scope.$watch(scope.getDimension, function(current) {
if(current) {
var h = current.h;
element.css({'height': (h - scope.subsHeight) + 'px'});
element.find('.section').css({'height': (h - scope.subsHeight - scope.subsSection) + 'px'});
element.find('.sub-pane').css({'height': (h - scope.subsHeight - scope.subsSubPane) + 'px'});
element.find('.tab-pane').css({'height': (h - scope.subsHeight - scope.subsSubPane - scope.subsSection -100) + 'px'});
}
}, true);
w.on('pageshow, resize', function() {
scope.$apply();
});
}
}
})();

View File

@ -1,22 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.layout.element.height', []);
})();

View File

@ -1,3 +0,0 @@
<a href="javascript:void(0);" role="button" tabindex="0" data-trigger="focus" data-toggle="popover" data-placement="right">
<span class="glyphicon glyphicon-info-sign"></span>
</a>

View File

@ -1,48 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.inline.help')
.directive('inlineHelp', inlineHelp);
function InlineHelpController() {
var vm = this;
}
function inlineHelp() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/inline-help/inline-help.directive.html',
'scope': {
'helpTitle': '@',
'content': '@'
},
'replace': true,
'link': link,
'controller': InlineHelpController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attr, ctrl) {
element.popover({
'title': ctrl.helpTitle,
'content': ctrl.content,
'html': true
});
}
}
})();

View File

@ -1,22 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.inline.help', []);
})();

View File

@ -1,68 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.loading.progress')
.directive('loadingProgress', loadingProgress);
function loadingProgress() {
var directive = {
'restrict': 'EA',
'scope': {
'toggleInProgress': '=',
'hideTarget': '@'
},
'link': link
};
return directive;
function link(scope, element, attrs) {
var spinner = $('<span class="loading-progress">');
function convertToBoolean(val) {
return val === 'true' ? true : false;
}
var hideTarget = convertToBoolean(scope.hideTarget);
var pristine = element.html();
scope.$watch('toggleInProgress', function(current) {
if(scope.toggleInProgress) {
element.attr('disabled', 'disabled');
if(hideTarget) {
element.html(spinner);
}else{
spinner = spinner.css({'margin-left': '5px'});
element.append(spinner);
}
}else{
if(hideTarget) {
element.html(pristine);
}else{
element.find('.loading-progress').remove();
}
element.removeAttr('disabled');
}
});
}
}
})();

View File

@ -1,22 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.loading.progress', []);
})();

View File

@ -1,68 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="well panel-group well-custom" style="margin-left: 0; position: absolute; width: 98%;">
<div class="row">
<div class="pull-right clearfix"><a href="javascript:void(0);" ng-click="vm.close()"><span class="glyphicon glyphicon-remove-circle"></span></a></div>
<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'})">&nbsp;// 'all' | tr //&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opCreate" ng-model="vm.opCreate" ng-click="vm.checkOperation({checked: 'create'})">&nbsp;Create&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opPull" ng-model="vm.opPull" ng-click="vm.checkOperation({checked: 'pull'})">&nbsp;Pull&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opPush" ng-model="vm.opPush" ng-click="vm.checkOperation({checked: 'push'})">&nbsp;Push&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opDelete" ng-model="vm.opDelete" ng-click="vm.checkOperation({checked: 'delete'})">&nbsp;Delete&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opOthers" ng-model="vm.opOthers" ng-click="vm.checkOperation({checked: 'others'})">&nbsp;// 'others' | tr //&nbsp;&nbsp;
<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="position: relative; top: 100px;">
<button type="button" class="btn btn-primary" ng-click="vm.doSearch({op: vm.op})">// 'search' | tr //</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,178 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.log')
.directive('advancedSearch', advancedSearch);
AdvancedSearchController.$inject = ['$scope', 'ListLogService', '$filter', 'trFilter'];
function AdvancedSearchController($scope, ListLogService, $filter, trFilter) {
var vm = this;
vm.checkOperation = checkOperation;
vm.close = close;
vm.opAll = true;
vm.doSearch = doSearch;
$scope.$watch('vm.op', function(current) {
if(current && vm.op[0] === 'all') {
vm.opCreate = true;
vm.opPull = true;
vm.opPush = true;
vm.opDelete = true;
vm.opOthers = true;
}
}, true);
$scope.$watch('vm.fromDate', function(current) {
if(current) {
vm.fromDate = current;
}
});
$scope.$watch('vm.toDate', function(current) {
if(current) {
vm.toDate = current;
}
});
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 && $.trim(vm.others) !== '') {
vm.op.push($.trim(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 close() {
vm.op = [];
vm.op.push('all');
vm.fromDate = '';
vm.toDate = '';
vm.others = '';
vm.isOpen = false;
}
function doSearch (e){
if(vm.opOthers && $.trim(vm.others) !== '') {
e.op.push(vm.others);
}
if(vm.fromDate && vm.toDate && (getDateValue(vm.fromDate) > getDateValue(vm.toDate))) {
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', $filter('tr')('begin_date_is_later_than_end_date'));
$scope.$emit('raiseError', true);
return
}
vm.search(e);
}
function getDateValue(date) {
if(date) {
return new Date(date);
}
return 0;
}
}
function advancedSearch(I18nService) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/log/advanced-search.directive.html',
'scope': {
'isOpen': '=',
'op': '=',
'opOthers': '=',
'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: I18nService().getCurrentLanguage(),
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()});
});
}
}
})();

View File

@ -1,57 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="tab-pane" id="logs" element-height>
<div class="col-xs-12 col-md-12 each-tab-pane">
<div class="form-inline">
<div class="input-group">
<input type="text" id="txtSearchInput" 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>
</div>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-link" type="button" ng-click="vm.showAdvancedSearch()">// 'advanced_search' | tr //</button>
</span>
</div>
</div>
<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" op-others="vm.opOthers"></advanced-search>
<div class="pane">
<div class="sub-pane">
<div class="table-head-container">
<table class="table table-pane table-header">
<thead>
<th width="18%">// 'username' | tr //</th>
<th width="28%">// 'repository_name' | tr //</th>
<th width="15%">// 'tag' | tr //</th>
<th width="14%">// 'operation' | tr //</th>
<th width="25%">// 'timestamp' | tr //</th>
</thead>
</table>
</div>
<div class="table-body-container">
<table class="table table-pane">
<tbody>
<tr ng-repeat="log in vm.logs">
<td width="18%">//log.username//</td><td width="28%">//log.repo_name//</td><td width="15%">//log.repo_tag//</td><td width="14%">//log.operation//</td><td width="25%">//log.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
</tr>
</tbody>
</table>
</div>
</div>
<paginator ng-if="vm.totalCount > 0" total-count="//vm.totalCount//" page-size="//vm.pageSize//" page="vm.page" display-count="5"></paginator>
</div>
</div>
</div>

View File

@ -1,178 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.log')
.directive('listLog', listLog);
ListLogController.$inject = ['$scope','ListLogService', 'getParameterByName', '$location', '$filter', 'trFilter'];
function ListLogController($scope, ListLogService, getParameterByName, $location, $filter, trFilter) {
$scope.subsTabPane = 30;
var vm = this;
vm.sectionHeight = {'min-height': '579px'};
vm.isOpen = false;
vm.beginTimestamp = 0;
vm.endTimestamp = 0;
vm.keywords = '';
vm.username = $location.hash() || '';
vm.op = [];
vm.opOthers = true;
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
};
vm.page = 1;
vm.pageSize = 15;
$scope.$watch('vm.page', function(current, origin) {
if(current) {
vm.page = current;
retrieve(vm.queryParams, vm.page, vm.pageSize);
}
});
$scope.$on('retrieveData', function(e, val) {
if(val) {
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.queryParams = {
'beginTimestamp' : vm.beginTimestamp,
'endTimestamp' : vm.endTimestamp,
'keywords' : vm.keywords,
'projectId': vm.projectId,
'username' : vm.username
};
vm.username = '';
retrieve(vm.queryParams, vm.page, vm.pageSize);
}
});
function search(e) {
vm.page = 1;
if(e.op[0] === 'all') {
e.op = ['create', 'pull', 'push', 'delete'];
}
if(vm.opOthers && $.trim(vm.others) !== '') {
e.op.push(vm.others);
}
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, vm.page, vm.pageSize);
}
function showAdvancedSearch() {
if(vm.isOpen){
vm.isOpen = false;
}else{
vm.isOpen = true;
}
}
function retrieve(queryParams, page, pageSize) {
ListLogService(queryParams, page, pageSize)
.then(listLogComplete)
.catch(listLogFailed);
}
function listLogComplete(response) {
vm.logs = response.data;
vm.totalCount = response.headers('X-Total-Count');
console.log('Total Count in logs:' + vm.totalCount + ', page:' + vm.page);
vm.isOpen = false;
}
function listLogFailed(response){
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', $filter('tr')('failed_to_get_log') + response);
$scope.$emit('raiseError', true);
console.log('Failed to get log:' + response);
}
function toUTCSeconds(date, hour, min, sec) {
if(!angular.isDefined(date) || date === '') {
return 0;
}
var t = new Date(date);
t.setHours(hour);
t.setMinutes(min);
t.setSeconds(sec);
return t.getTime() / 1000;
}
}
listLog.$inject = ['$timeout'];
function listLog($timeout) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/log/list-log.directive.html',
'scope': {
'sectionHeight': '='
},
'link': link,
'controller': ListLogController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
element.find('#txtSearchInput').on('keydown', function(e) {
if($(this).is(':focus') && e.keyCode === 13) {
ctrl.search({'op': ctrl.op, 'username': ctrl.username});
} else {
$timeout(function() {
if(ctrl.username.length === 0) {
ctrl.search({'op': ctrl.op, 'username': ctrl.username});
}
});
}
});
}
}
})();

View File

@ -1,23 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.log');
})();

View File

@ -1,24 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.log', [
'harbor.services.log'
]);
})();

View File

@ -1,30 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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">&times;</span></button>
<h4 class="modal-title">//vm.modalTitle//</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" ng-show="!vm.confirmOnly" class="btn btn-default" data-dismiss="modal">// 'cancel' | tr //</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View File

@ -1,90 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(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/resources/js/components/modal-dialog/modal-dialog.directive.html',
'link': link,
'scope': {
'contentType': '@',
'modalTitle': '@',
'modalMessage': '@',
'action': '&',
'confirmOnly': '='
},
'controller': ModalDialogController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$watch('contentType', function(current) {
if(current) {
ctrl.contentType = current;
}
});
scope.$watch('confirmOnly', function(current) {
if(current) {
ctrl.confirmOnly = current;
}
});
scope.$watch('vm.modalMessage', 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;
}
}
});
scope.$on('showDialog', function(e, val) {
if(val) {
element.find('#myModal').modal('show');
}else{
element.find('#myModal').modal('hide');
}
});
element.find('#btnOk').on('click', clickHandler);
function clickHandler(e) {
ctrl.action();
}
}
}
})();

View File

@ -1,22 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.modal.dialog', []);
})();

View File

@ -1,111 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.optional.menu')
.directive('optionalMenu', optionalMenu);
OptionalMenuController.$inject = ['$scope', '$window', 'I18nService', 'LogOutService', 'currentUser', '$timeout', 'trFilter', '$filter', 'GetVolumeInfoService'];
function OptionalMenuController($scope, $window, I18nService, LogOutService, currentUser, $timeoutm, trFilter, $filter, GetVolumeInfoService) {
var vm = this;
var i18n = I18nService();
i18n.setCurrentLanguage(vm.language);
vm.languageName = i18n.getLanguageName(vm.language);
console.log('current language:' + vm.languageName);
vm.supportLanguages = i18n.getSupportLanguages();
vm.user = currentUser.get();
vm.setLanguage = setLanguage;
vm.logOut = logOut;
vm.about = about;
function setLanguage(language) {
vm.languageName = i18n.getLanguageName(vm.language);
var hash = $window.location.hash;
$window.location.href = '/language?lang=' + language + '&hash=' + encodeURIComponent(hash);
}
function logOut() {
LogOutService()
.success(logOutSuccess)
.error(logOutFailed);
}
function logOutSuccess(data, status) {
currentUser.unset();
$window.location.href= '/';
}
function logOutFailed(data, status) {
console.log('Failed to log out:' + data);
}
var raiseInfo = {
'confirmOnly': true,
'contentType': 'text/html',
'action': function() {}
};
function about() {
$scope.$emit('modalTitle', $filter('tr')('about_harbor'));
vm.modalMessage = $filter('tr')('current_version', [vm.version || 'Unknown']);
if(vm.showDownloadCert === 'true') {
appendDownloadCertLink();
}
GetVolumeInfoService("data")
.then(getVolumeInfoSuccess, getVolumeInfoFailed);
}
function getVolumeInfoSuccess(response) {
var storage = response.data;
vm.modalMessage += '<br/>' + $filter('tr')('current_storage',
[toGigaBytes(storage['storage']['free']), toGigaBytes(storage['storage']['total'])]);
$scope.$emit('modalMessage', vm.modalMessage);
$scope.$emit('raiseInfo', raiseInfo);
}
function getVolumeInfoFailed(response) {
$scope.$emit('modalMessage', vm.modalMessage);
$scope.$emit('raiseInfo', raiseInfo);
}
function toGigaBytes(val) {
return Math.round(val / (1024 * 1024 * 1024));
}
function appendDownloadCertLink() {
vm.modalMessage += '<br/>' + $filter('tr')('default_root_cert', ['/api/systeminfo/getcert', $filter('tr')('download')]);
}
}
function optionalMenu() {
var directive = {
'restrict': 'E',
'templateUrl': '/optional_menu?timestamp=' + new Date().getTime(),
'scope': {
'version': '@',
'language': '@',
'showDownloadCert': '@'
},
'controller': OptionalMenuController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
}
})();

View File

@ -1,25 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.optional.menu', [
'harbor.services.user',
'harbor.services.i18n'
]);
})();

View File

@ -1,39 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<nav aria-label="Page navigation" class="pull-left">
<ul class="pagination" style="margin: 0 0 0 10px;">
<li ng-class="vm.disabledFirst" ng-show="vm.visible">
<a href="javascript:void(0);" ng-click="vm.gotoFirst()" aria-label="Previous">
<span aria-hidden="true">&lt;&lt;</span>
</a>
</li>
<li ng-class="vm.disabledPrevious" ng-show="vm.visible">
<a href="javascript:void(0);" ng-click="vm.previous()" aria-label="Previous">
<span aria-hidden="true">&lt;</span>
</a>
</li>
<li ng-class="vm.disabledNext" ng-show="vm.visible">
<a href="javascript:void(0);" ng-click="vm.next()" aria-label="Next">
<span aria-hidden="true">&gt;</span>
</a>
</li>
<li ng-class="vm.disabledLast" ng-show="vm.visible">
<a href="javascript:void(0);" ng-click="vm.gotoLast()" aria-label="Next">
<span aria-hidden="true">&gt;&gt;</span>
</a>
</li>
</ul>
</nav>
<p class="pull-right" style="margin-right: 15%; margin-top: 5px;">// 'total' | tr // // vm.totalCount // // 'items' | tr //</p>

View File

@ -1,253 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.paginator')
.directive('paginator', paginator);
PaginatorController.$inject = [];
function PaginatorController() {
var vm = this;
}
paginator.$inject = [];
function paginator() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/paginator/paginator.directive.html',
'scope': {
'totalCount': '@',
'pageSize': '@',
'page': '=',
'displayCount': '@'
},
'link': link,
'controller': PaginatorController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$watch('vm.page', function(current) {
if(current) {
ctrl.page = current;
togglePageButton();
}
});
var tc;
scope.$watch('vm.totalCount', function(current) {
if(current) {
var totalCount = current;
tc = new TimeCounter();
console.log('Total Count:' + totalCount + ', Page Size:' + ctrl.pageSize + ', Display Count:' + ctrl.displayCount + ', Page:' + ctrl.page);
ctrl.buttonCount = Math.ceil(totalCount / ctrl.pageSize);
if(ctrl.buttonCount <= ctrl.displayCount) {
tc.setMaximum(1);
ctrl.visible = false;
}else{
tc.setMaximum(Math.ceil(ctrl.buttonCount / ctrl.displayCount));
ctrl.visible = true;
}
ctrl.gotoFirst = gotoFirst;
ctrl.gotoLast = gotoLast;
if(ctrl.buttonCount < ctrl.page) {
ctrl.page = ctrl.buttonCount;
}
ctrl.previous = previous;
ctrl.next = next;
drawButtons(tc.getTime());
togglePrevious(tc.canDecrement());
toggleNext(tc.canIncrement());
toggleFirst();
toggleLast();
togglePageButton();
}
});
var TimeCounter = function() {
this.time = 0;
this.minimum = 0;
this.maximum = 0;
};
TimeCounter.prototype.setMaximum = function(maximum) {
this.maximum = maximum;
};
TimeCounter.prototype.getMaximum = function() {
return this.maximum;
};
TimeCounter.prototype.increment = function() {
if(this.time < this.maximum) {
++this.time;
if((ctrl.page % ctrl.displayCount) != 0) {
ctrl.page = this.time * ctrl.displayCount;
}
++ctrl.page;
}
};
TimeCounter.prototype.canIncrement = function() {
if(this.time + 1 < this.maximum) {
return true;
}
return false;
};
TimeCounter.prototype.decrement = function() {
if(this.time > this.minimum) {
if(this.time === 0) {
ctrl.page = ctrl.displayCount;
}else{
ctrl.page = this.time * ctrl.displayCount;
}
--this.time;
}
};
TimeCounter.prototype.canDecrement = function() {
if(this.time > this.minimum) {
return true;
}
return false;
};
TimeCounter.prototype.getTime = function() {
return this.time;
};
TimeCounter.prototype.setTime = function(time) {
this.time = time;
};
function drawButtons(time) {
element.find('li[tag="pagination-button"]').remove();
var buttons = [];
for(var i = 1; i <= ctrl.displayCount; i++) {
var displayNumber = ctrl.displayCount * time + i;
if(displayNumber <= ctrl.buttonCount) {
buttons.push('<li tag="pagination-button"><a href="javascript:void(0)" page="' + displayNumber + '">' + displayNumber + '<span class="sr-only"></span></a></li>');
}
}
$(buttons.join(''))
.insertAfter(element.find('ul li:eq(' + (ctrl.visible ? 1 : 0) + ')')).end()
.on('click', buttonClickHandler);
}
function togglePrevious(status) {
ctrl.disabledPrevious = status ? '' : 'disabled';
toggleFirst();
toggleLast();
}
function toggleNext(status) {
ctrl.disabledNext = status ? '' : 'disabled';
toggleFirst();
toggleLast();
}
function toggleFirst() {
ctrl.disabledFirst = (ctrl.page > 1) ? '' : 'disabled';
}
function toggleLast() {
ctrl.disabledLast = (ctrl.page < ctrl.buttonCount) ? '' : 'disabled';
}
function buttonClickHandler(e) {
ctrl.page = $(e.target).attr('page');
togglePageButton();
togglePrevious(tc.canDecrement());
toggleNext(tc.canIncrement());
scope.$apply();
}
function togglePageButton() {
element.find('li[tag="pagination-button"]').removeClass('active');
element.find('li[tag="pagination-button"] a[page="' + ctrl.page + '"]').parent().addClass('active');
}
function previous() {
if(tc.canDecrement()) {
tc.decrement();
drawButtons(tc.getTime());
togglePageButton();
togglePrevious(tc.canDecrement());
toggleNext(tc.canIncrement());
}
}
function gotoFirst() {
ctrl.page = 1;
tc.setTime(0);
drawButtons(0);
toggleFirst();
toggleLast();
togglePageButton();
togglePrevious(tc.canDecrement());
toggleNext(tc.canIncrement());
}
function next() {
if(tc.canIncrement()) {
tc.increment();
drawButtons(tc.getTime());
togglePageButton();
togglePrevious(tc.canDecrement());
toggleNext(tc.canIncrement());
}
}
function gotoLast() {
ctrl.page = ctrl.buttonCount;
tc.setTime(Math.ceil(ctrl.buttonCount / ctrl.displayCount) - 1);
drawButtons(tc.getTime());
toggleFirst();
toggleLast();
togglePageButton();
togglePrevious(tc.canDecrement());
toggleNext(tc.canIncrement());
}
}
}
})();

View File

@ -1,22 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.paginator', []);
})();

View File

@ -1,44 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="well panel-group well-custom" style="margin-top: 10px; position: absolute; width: 98%;">
<div class="row">
<form name="form" class="css-form form-custom" novalidate autocomplete="off">
<div class="col-xs-10 col-md-10">
<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.$submitted && form.uUsername.$error">
<span ng-message="required">// 'username_is_required' | tr //</span>
</div>
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
</div>
</div>
<div class="form-group">
<label for="roleIdList">// 'role' | tr //:</label>
<inline-help help-title="//'inline_help_role_title' | tr//" content="//'inline_help_role' | tr//"></inline-help>&nbsp;&nbsp;
<span ng-repeat="role in vm.roles">
<input type="radio" name="role" ng-model="vm.optRole" value="//role.id//">&nbsp;//role.name | tr//&nbsp;&nbsp;
</span>
</div>
</div>
<div class="col-xs-2 col-md-2">
<div class="form-group">
<button type="submit" class="btn btn-primary" id="btnSave" 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>
</div>
</div>
</form>
</div>

View File

@ -1,124 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('addProjectMember', addProjectMember);
AddProjectMemberController.$inject = ['$scope', 'roles', 'AddProjectMemberService'];
function AddProjectMemberController($scope, roles, AddProjectMemberService) {
var vm = this;
$scope.pm = {};
var pm = $scope.pm;
vm.roles = roles();
vm.optRole = 1;
vm.save = save;
vm.cancel = cancel;
vm.reset = 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) {
form.$setPristine();
form.$setUntouched();
vm.isOpen = false;
pm.username = '';
vm.optRole = 1;
vm.hasError = false;
vm.errorMessage = '';
}
function addProjectMemberComplete(data, status, header) {
console.log('addProjectMemberComplete: status:' + status + ', data:' + data);
vm.reload();
vm.isOpen = false;
}
function addProjectMemberFailed(data, status, headers) {
if(status === 403) {
vm.hasError = true;
vm.errorMessage = 'failed_to_add_member';
}
if(status === 409 && pm.username !== '') {
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 reset() {
vm.hasError = false;
vm.errorMessage = '';
}
}
addProjectMember.$inject = ['$timeout'];
function addProjectMember($timeout) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/project-member/add-project-member.directive.html',
'scope': {
'projectId': '@',
'isOpen': '=',
'reload': '&'
},
'link': link,
'controller': AddProjectMemberController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.form.$setPristine();
scope.form.$setUntouched();
scope.$watch('vm.isOpen', function(current) {
if(current) {
$timeout(function() {
element.find('[name=uUsername]:input').focus();
});
}
});
}
}
})();

View File

@ -1,25 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<td width="30%">//vm.username//</td>
<td width="45%"><switch-role roles="vm.roles" edit-mode="vm.editMode" user-id="vm.userId" role-name="vm.roleName"></switch-role></td>
<td width="25%" ng-if="vm.currentRoleId == 1">
<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' | tr //"></span><span ng-if="vm.editMode" class="glyphicon glyphicon-ok" title="// 'confirm' | tr //">
</a>
<a ng-show="vm.userId != vm.currentUserId" href="javascript:void(0);" ng-click="vm.cancelUpdate()" title="// 'cancel' | tr //">
<span ng-if="vm.editMode" class="glyphicon glyphicon-remove"></span>
</a>
<a ng-show="vm.userId != vm.currentUserId && !vm.editMode" href="javascript:void(0);" ng-click="vm.deleteProjectMember()" title="// 'delete' | tr //"><span class="glyphicon glyphicon-trash"></span></a>
</td>

View File

@ -1,100 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('editProjectMember', editProjectMember);
EditProjectMemberController.$inject = ['$scope', 'roles', 'getRole','EditProjectMemberService', '$filter', 'trFilter'];
function EditProjectMemberController($scope, roles, getRole, EditProjectMemberService, $filter, trFilter) {
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() {
vm.delete();
}
function editProjectMemberComplete(data, status, headers) {
console.log('edit project member complete: ' + status);
vm.lastRoleName = vm.roleName;
vm.editMode = false;
vm.reload();
}
function editProjectMemberFailed(e) {
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', $filter('tr')('failed_to_change_member'));
$scope.$emit('raiseError', true);
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/resources/js/components/project-member/edit-project-member.directive.html',
'scope': {
'username': '=',
'userId': '=',
'currentUserId': '=',
'roleName': '=',
'projectId': '=',
'delete': '&',
'reload': '&',
'currentRoleId': '@'
},
'controller': EditProjectMemberController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
}
})();

View File

@ -1,47 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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" id="txtSearchInput" 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.roleId == 1 && !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.roleId == 1 && vm.isOpen" class="btn btn-default" disabled="disabled" type="button"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button>
</div>
<add-project-member ng-if="vm.isOpen" is-open="vm.isOpen" project-id="//vm.projectId//" reload='vm.search({projectId: vm.projectId, username: vm.username})'></add-project-member>
<div class="search-pane">
<div class="sub-pane">
<div class="table-head-container">
<table class="table table-pane table-header">
<thead>
<th width="30%">// 'username' | tr //</th><th width="45%">// 'role' | tr //</th><th width="25%" ng-if="vm.roleId == 1">// 'operation' | tr //</th>
</thead>
</table>
</div>
<div class="table-body-container">
<table class="table table-pane">
<tbody>
<tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.user_id" delete="vm.deleteProjectMember({projectId: vm.projectId, userId: pr.user_id})" current-user-id="vm.user.user_id" role-name="pr.role_name" reload='vm.search({projectId: vm.projectId, username: vm.username})' current-role-id="//vm.roleId//"></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,132 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('listProjectMember', listProjectMember);
ListProjectMemberController.$inject = ['$scope', 'ListProjectMemberService', 'DeleteProjectMemberService', 'getParameterByName', '$location', 'currentUser', '$filter', 'trFilter', '$window'];
function ListProjectMemberController($scope, ListProjectMemberService, DeleteProjectMemberService, getParameterByName, $location, currentUser, $filter, trFilter, $window) {
$scope.subsTabPane = 30;
var vm = this;
vm.sectionHeight = {'min-height': '579px'};
vm.isOpen = false;
vm.search = search;
vm.addProjectMember = addProjectMember;
vm.deleteProjectMember = deleteProjectMember;
vm.retrieve = retrieve;
vm.username = '';
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.retrieve();
$scope.$on('retrieveData', function(e, val) {
if(val) {
console.log('received retrieve data:' + val);
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.username = '';
vm.retrieve();
}
});
function search(e) {
vm.projectId = e.projectId;
vm.username = e.username;
retrieve();
}
function addProjectMember() {
vm.isOpen = !vm.isOpen;
}
function deleteProjectMember(e) {
DeleteProjectMemberService(e.projectId, e.userId)
.success(deleteProjectMemberSuccess)
.error(deleteProjectMemberFailed);
}
function deleteProjectMemberSuccess(data, status) {
console.log('Successful delete project member.');
vm.retrieve();
}
function deleteProjectMemberFailed(e) {
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', $filter('tr')('failed_to_delete_member'));
$scope.$emit('raiseError', true);
console.log('Failed to edit project member:' + e);
}
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 to get project members:' + response);
vm.projectMembers = [];
$location.url('repositories').search('project_id', vm.projectId);
}
}
listProjectMember.$inject = ['$timeout'];
function listProjectMember($timeout) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/project-member/list-project-member.directive.html',
'scope': {
'sectionHeight': '=',
'roleId': '@'
},
'link': link,
'controller': ListProjectMemberController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
element.find('#txtSearchInput').on('keydown', function(e) {
if($(this).is(':focus') && e.keyCode === 13) {
ctrl.retrieve();
} else {
$timeout(function() {
if(ctrl.username.length === 0) {
ctrl.retrieve();
}
});
}
});
}
}
})();

View File

@ -1,47 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project.member')
.constant('roles', roles)
.factory('getRole', getRole);
function roles() {
return [
{'id': '1', 'name': 'project_admin', 'roleName': 'projectAdmin'},
{'id': '2', 'name': 'developer', 'roleName': 'developer'},
{'id': '3', 'name': 'guest', 'roleName': 'guest'}
];
}
getRole.$inject = ['roles', '$filter', 'trFilter'];
function getRole(roles, $filter, trFilter) {
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;
}
}
}
}
})();

View File

@ -1,25 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project.member', [
'harbor.services.project.member',
'harbor.services.user'
]);
})();

View File

@ -1,19 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ng-switch on="vm.editMode">
<span ng-switch-default>//vm.currentRole.name | tr//</span>
<select class="form-control" style="width: auto; height: auto; padding: 0;" ng-switch-when="true" ng-model="vm.currentRole" ng-options="role as (role.name | tr) for role in vm.roles track by role.roleName" ng-change="vm.selectRole(vm.currentRole)">
</select>
</ng-switch>

View File

@ -1,60 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(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/resources/js/components/project-member/switch-role.directive.html',
'scope': {
'roles': '=',
'editMode': '=',
'userId': '=',
'roleName': '='
},
'controller' : SwitchRoleController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
}
})();

View File

@ -1,44 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="well well-custom" style="position: absolute; margin-left: 10px; margin-top: 5px; width: 94%;">
<form name="form" class="css-form form-custom" novalidate autocomplete="off">
<div class="row">
<div class="col-xs-10 col-md-10">
<div class="form-group col-md-7">
<input type="text" class="form-control" placeholder="// 'project_name' | tr //" ng-model="p.projectName" name="uProjectName" ng-change="vm.reset()" ng-model-options="{ debounce: 250 }" project-name required>
<div class="error-message">
<div ng-messages="form.$submitted && form.uProjectName.$error">
<span ng-message="required">// 'project_name_is_required' | tr //</span>
<span ng-message="projectName">// 'project_name_is_invalid' | tr //</span>
</div>
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
</div>
</div>
<div class="form-group" style="margin-top: 5px;">
<label>
<input type="checkbox" ng-model="vm.isPublic">&nbsp;// 'public' | tr //
</label>
<inline-help help-title="//'inline_help_publicity_title' | tr//" content="//'inline_help_publicity' | tr//"></inline-help>
</div>
</div>
<div class="col-xs-2 col-md-2">
<div class="form-group">
<button type="submit" class="btn btn-primary" ng-click="vm.addProject(p)">// 'save' | tr //</button>
<button type="button" class="btn btn-default" ng-click="vm.cancel(form)">// 'cancel' | tr //</button>
</div>
</div>
</div>
</form>
</div>

View File

@ -1,127 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project')
.directive('addProject', addProject);
AddProjectController.$inject = ['AddProjectService', '$scope'];
function AddProjectController(AddProjectService, $scope) {
var vm = this;
$scope.p = {};
var vm0 = $scope.p;
vm0.projectName = '';
vm.isPublic = 0;
vm.addProject = addProject;
vm.cancel = cancel;
vm.reset = reset;
vm.hasError = false;
vm.errorMessage = '';
$scope.$watch('vm.isOpen', function(current) {
if(current) {
$scope.form.$setPristine();
$scope.form.$setUntouched();
vm0.projectName = '';
vm.isPublic = 0;
}
});
function addProject(p) {
if(p && angular.isDefined(p.projectName)) {
vm.isPublic = vm.isPublic ? 1 : 0;
AddProjectService(p.projectName, vm.isPublic)
.success(addProjectSuccess)
.error(addProjectFailed);
}
}
function addProjectSuccess(data, status) {
$scope.$emit('addedSuccess', true);
vm.hasError = false;
vm.errorMessage = '';
vm.isOpen = false;
}
function addProjectFailed(data, status) {
vm.hasError = true;
if(status === 400 && vm0.projectName !== '' && vm0.projectName.length < 4) {
vm.errorMessage = 'project_name_is_too_short';
}
if(status === 400 && vm0.projectName.length > 30) {
vm.errorMessage = 'project_name_is_too_long';
}
if(status === 409 && vm0.projectName !== '') {
vm.errorMessage = 'project_already_exist';
}
console.log('Failed to add project:' + status);
}
function cancel(form){
if(form) {
form.$setPristine();
form.$setUntouched();
}
vm.isOpen = false;
vm0.projectName = '';
vm.isPublic = 0;
vm.hasError = false;
vm.errorMessage = '';
}
function reset() {
vm.hasError = false;
vm.errorMessage = '';
}
}
addProject.$inject = ['$timeout'];
function addProject($timeout) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/project/add-project.directive.html',
'controller': AddProjectController,
'link': link,
'scope' : {
'isOpen': '='
},
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$watch('vm.isOpen', function(current) {
if(current) {
$timeout(function() {
element.find(':input[name=uProjectName]').focus();
});
}
});
}
}
})();

View File

@ -1,23 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project', [
'harbor.services.project',
'harbor.services.user'
]);
})();

View File

@ -1,16 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<button ng-if="vm.isPublic" class="btn btn-success" ng-disabled="vm.roleId != 1" ng-click="vm.toggle()">// 'button_on' | tr //</button>
<button ng-if="!vm.isPublic" class="btn btn-danger" ng-disabled="vm.roleId != 1" ng-click="vm.toggle()">// 'button_off' | tr //</button>

View File

@ -1,82 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.project')
.directive('publicityButton', publicityButton);
PublicityButtonController.$inject = ['$scope', 'ToggleProjectPublicityService', '$filter', 'trFilter'];
function PublicityButtonController($scope, ToggleProjectPublicityService, $filter, trFilter) {
var vm = this;
vm.toggle = toggle;
function toggle() {
vm.isPublic = vm.isPublic ? 0 : 1;
ToggleProjectPublicityService(vm.projectId, vm.isPublic)
.success(toggleProjectPublicitySuccess)
.error(toggleProjectPublicityFailed);
}
function toggleProjectPublicitySuccess(data, status) {
console.log('Successful toggle project publicity.');
}
function toggleProjectPublicityFailed(e, status) {
$scope.$emit('modalTitle', $filter('tr')('error'));
var message;
if(status === 403) {
message = $filter('tr')('failed_to_toggle_publicity_insuffient_permissions');
}else{
message = $filter('tr')('failed_to_toggle_publicity');
}
$scope.$emit('modalMessage', message);
$scope.$emit('raiseError', true);
vm.isPublic = vm.isPublic ? 0 : 1;
console.log('Failed to toggle project publicity:' + e);
}
}
function publicityButton() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/project/publicity-button.directive.html',
'scope': {
'isPublic': '=',
'projectId': '=',
'roleId': '@'
},
'link': link,
'controller': PublicityButtonController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attr, ctrl) {
scope.$watch('vm.isPublic', function(current, origin) {
if(current) {
ctrl.isPublic = current;
}
});
}
}
})();

View File

@ -1,121 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="modal fade" data-backdrop="static" id="createPolicyModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<form name="form" class="form-horizontal css-form" novalidate autocomplete="off">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title"><span class="glyphicon glyphicon-plus"></span>&nbsp;&nbsp;//vm.modalTitle//</h4>
</div>
<div class="modal-body">
<div class="create-policy">
<div class="col-md-12" ng-show="vm.toggleErrorMessage">
<div class="pull-right clearfix" style="padding: 5px 10px;"><a href="javascript:void(0);" ng-click="vm.closeError()"><span class="glyphicon glyphicon-remove"></span></a></div>
<div class="bg-danger dialog-message">
<ul class="list-unstyled" style="color: red; margin: 0;" ng-repeat="msg in vm.errorMessages track by $index">
<li>// msg //</li>
</ul>
</div>
</div>
<div class="col-md-12">
<h4>// 'general_setting' | tr //</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' | tr //:</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 maxlength="20" ng-disabled="!vm.targetEditable">
<div class="error-message" ng-messages="form.$submitted && form.uName.$error">
<span ng-message="required">// 'name_is_required' | tr //</span>
<span ng-message="maxlength">// 'name_is_too_long' | tr //</span>
</div>
</div>
</div>
<div class="form-group col-md-12 form-group-custom">
<label for="description" class="col-md-3 control-label">// 'description' | tr //:</label>
<div class="col-md-9">
<textarea class="form-control form-control-custom" id="description" ng-model="replication.policy.description" name="uDescription" ng-disabled="!vm.targetEditable"></textarea>
<div class="error-message" ng-messages="form.$submitted && form.uDescription.$error">
<span ng-message="maxlength">// 'description_is_too_long' | tr //</span>
</div>
</div>
</div>
<div class="form-group col-md-12 form-group-custom">
<label for="enable" class="col-md-3 control-label">// 'enable' | tr //:</label>
<div class="col-md-9">
<input type="checkbox" class="form-control" style="margin-top: 10px; height: auto;" ng-model="replication.policy.enabled" ng-disabled="!vm.targetEditable">
</div>
</div>
<div class="col-md-12">
<div class="row">
<div class="col-md-8" style="padding-left: 0;">
<h4 class="h4-custom">// 'destination_setting' | tr //</h4>
</div>
</div>
<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' | tr //:</label>
<div class="col-md-7">
<input type="text" ng-if="vm.checkedAddTarget" class="form-control form-control-custom" style="width: 50% !important;" ng-model="replication.destination.name">
<select ng-if="!vm.checkedAddTarget" class="form-control form-control-custom" ng-model="replication.destination.selection" ng-options="d as d.name for d in vm.destinations track by d.id" ng-change="vm.selectDestination(replication.destination.selection)" style="width: 50% !important;" ng-disabled="!vm.targetEditable"></select>
<div class="display-inline-block" ng-show="vm.targetEditable">&nbsp;&nbsp;<input type="checkbox" ng-model="vm.checkedAddTarget" ng-checked="vm.checkedAddTarget">&nbsp;&nbsp;// 'add_new_destination' | tr //</div>
</div>
</div>
<div class="form-group col-md-12 form-group-custom">
<label for="endpoint" class="col-md-3 control-label">// 'endpoint' | tr //:</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" placeholder="http://ip_address" required ng-disabled="!vm.targetEditable || !vm.checkedAddTarget">
<div class="error-message" ng-messages="form.$submitted && form.uEndpoint.$error">
<span ng-message="required">// 'endpoint_is_required' | tr //</span>
</div>
</div>
</div>
<div class="form-group col-md-12 form-group-custom">
<label for="username" class="col-md-3 control-label">// 'username' | tr //:</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" ng-disabled="!vm.targetEditable || !vm.checkedAddTarget">
</div>
</div>
<div class="form-group col-md-12 form-group-custom">
<label for="password" class="col-md-3 control-label">// 'password' | tr //:</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" ng-disabled="!vm.targetEditable || !vm.checkedAddTarget">
</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-disabled="vm.notAvailable" ng-click="vm.pingDestination()" loading-progress hide-target="false" toggle-in-progress="vm.pingTIP">// 'test_connection' | tr //</button>
</div>
</div>
<div class="form-group col-md-12 form-group-custom">
<div class="col-md-3"></div>
<div class="col-md-9">
<span ng-if="vm.isError" class="error-message" >// vm.pingMessage //</span>
<span ng-if="!vm.isError">// vm.pingMessage //</span>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button ng-show="vm.targetEditable" type="submit" class="btn btn-primary" ng-click="form.$valid && vm.save(replication)" loading-progress hide-target="false" toggle-in-progress="vm.saveTIP">// '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 -->

View File

@ -1,439 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.replication')
.directive('createPolicy', createPolicy);
CreatePolicyController.$inject = ['$scope', 'ListReplicationPolicyService', 'ListDestinationService', 'CreateDestinationService', 'UpdateDestinationService', 'PingDestinationService', 'CreateReplicationPolicyService', 'UpdateReplicationPolicyService', 'ListDestinationPolicyService','$location', 'getParameterByName', '$filter', 'trFilter', '$q', '$timeout'];
function CreatePolicyController($scope, ListReplicationPolicyService, ListDestinationService, CreateDestinationService, UpdateDestinationService, PingDestinationService, CreateReplicationPolicyService, UpdateReplicationPolicyService, ListDestinationPolicyService, $location, getParameterByName, $filter, trFilter, $q, $timeout) {
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());
$scope.$on('$locationChangeSuccess', function() {
vm.projectId = getParameterByName('project_id', $location.absUrl());
});
vm.addNew = addNew;
vm.edit = edit;
vm.prepareDestination = prepareDestination;
vm.create = create;
vm.update = update;
vm.pingDestination = pingDestination;
vm.checkDestinationPolicyStatus = checkDestinationPolicyStatus;
vm.targetEditable = true;
vm.checkedAddTarget = false;
vm.notAvailable = false;
vm.pingAvailable = true;
vm.pingMessage = '';
vm.pingTIP = false;
vm.saveTIP = false;
vm.closeError = closeError;
vm.toggleErrorMessage = false;
vm.errorMessages = [];
$scope.$watch('vm.destinations', function(current) {
if(current) {
if(!angular.isArray(current) || current.length === 0) {
vm.notAvailable = true;
return;
}
if(!angular.isDefined(vm1.selection)) {
vm1.selection = current[0];
vm1.endpoint = current[0].endpoint;
vm1.username = current[0].username;
vm1.password = current[0].password;
}
}
});
$scope.$watch('vm.checkedAddTarget', function(current) {
if(current) {
vm.targetEditable = true;
vm1.name = '';
vm1.endpoint = '';
vm1.username = '';
vm1.password = '';
vm.pingMessage = '';
}
});
$scope.$watch('vm.targetId', function(current) {
if(current) {
vm1.selection.id = current;
}
});
$scope.$watch('replication.destination.endpoint', function(current) {
if(current) {
vm.notAvailable = false;
}else{
vm.notAvailable = true;
}
});
function selectDestination(item) {
vm1.selection = item;
if(angular.isDefined(item)) {
vm.targetId = item.id;
vm1.endpoint = item.endpoint;
vm1.username = item.username;
vm1.password = item.password;
}
}
function prepareDestination() {
ListDestinationService('')
.success(listDestinationSuccess)
.error(listDestinationFailed);
}
function addNew() {
vm.modalTitle = $filter('tr')('add_new_policy', []);
vm0.name = '';
vm0.description = '';
vm0.enabled = true;
}
function edit(policyId) {
console.log('Edit policy ID:' + policyId);
vm.policyId = policyId;
vm.modalTitle = $filter('tr')('edit_policy', []);
ListReplicationPolicyService(policyId)
.success(listReplicationPolicySuccess)
.error(listReplicationPolicyFailed);
}
function create(policy) {
vm.policy = policy;
saveDestination();
}
function saveDestination() {
var target = {
'name' : vm1.name,
'endpoint': vm1.endpoint,
'username': vm1.username,
'password': vm1.password
};
if(vm.checkedAddTarget){
CreateDestinationService(target.name, target.endpoint, target.username, target.password)
.success(createDestinationSuccess)
.error(createDestinationFailed);
}else{
vm.policy.targetId = vm1.selection.id || vm.destinations[0].id;
saveOrUpdatePolicy();
}
}
function saveOrUpdatePolicy() {
vm.saveTIP = true;
switch(vm.action) {
case 'ADD_NEW':
CreateReplicationPolicyService(vm.policy)
.success(createReplicationPolicySuccess)
.error(createReplicationPolicyFailed);
break;
case 'EDIT':
UpdateReplicationPolicyService(vm.policyId, vm.policy)
.success(updateReplicationPolicySuccess)
.error(updateReplicationPolicyFailed);
break;
default:
vm.saveTIP = false;
}
}
function update(policy) {
vm.policy = policy;
if(vm.targetEditable) {
vm.policy.targetId = vm1.selection.id;
saveDestination();
}
}
function pingDestination() {
var target = {
'endpoint': vm1.endpoint,
'username': vm1.username,
'password': vm1.password
};
if(vm.checkedAddTarget) {
target.name = vm1.name;
}
vm.pingMessage = $filter('tr')('pinging_target');
vm.pingTIP = true;
vm.isError = false;
PingDestinationService(target)
.success(pingDestinationSuccess)
.error(pingDestinationFailed);
}
function checkDestinationPolicyStatus() {
console.log('Checking destination policy status, target_ID:' + vm.targetId);
ListDestinationPolicyService(vm.targetId)
.success(listDestinationPolicySuccess)
.error(listDestinationPolicyFailed);
}
function closeError() {
vm.errorMessages = [];
vm.toggleErrorMessage = false;
}
function listDestinationSuccess(data, status) {
vm.destinations = data || [];
}
function listDestinationFailed(data, status) {
vm.errorMessages.push($filter('tr')('failed_to_get_destination'));
console.log('Failed to get destination:' + data);
}
function listDestinationPolicySuccess(data, status) {
if(vm.action === 'EDIT') {
console.log('Current target editable:' + vm.targetEditable + ', policy ID:' + vm.policyId);
vm.targetEditable = true;
for(var i in data) {
if(data[i].enabled === 1) {
vm.targetEditable = false;
break;
}
}
}
}
function listDestinationPolicyFailed(data, status) {
vm.errorMessages.push($filter('tr')('failed_to_get_destination_policies'));
console.log('Failed to list destination policy:' + data);
}
function listReplicationPolicySuccess(data, status) {
var replicationPolicy = data;
vm.targetId = replicationPolicy.target_id;
vm0.name = replicationPolicy.name;
vm0.description = replicationPolicy.description;
vm0.enabled = (replicationPolicy.enabled === 1);
angular.forEach(vm.destinations, function(item) {
if(item.id === vm.targetId) {
vm1.endpoint = item.endpoint;
vm1.username = item.username;
vm1.password = item.password;
}
});
vm.checkDestinationPolicyStatus();
}
function listReplicationPolicyFailed(data, status) {
vm.errorMessages.push($filter('tr')('failed_to_get_replication_policy'));
console.log('Failed to list replication policy:' + data);
}
function createReplicationPolicySuccess(data, status) {
vm.saveTIP = false;
console.log('Successful create replication policy.');
vm.reload();
vm.closeDialog();
}
function createReplicationPolicyFailed(data, status) {
vm.saveTIP = false;
if(status === 409) {
vm.errorMessages.push($filter('tr')('policy_already_exists'));
}else{
vm.errorMessages.push($filter('tr')('failed_to_create_replication_policy'));
}
console.log('Failed to create replication policy:' + data);
}
function updateReplicationPolicySuccess(data, status) {
console.log('Successful update replication policy.');
vm.reload();
vm.saveTIP = false;
vm.closeDialog();
}
function updateReplicationPolicyFailed(data, status) {
vm.saveTIP = false;
vm.errorMessages.push($filter('tr')('failed_to_update_replication_policy'));
console.log('Failed to update replication policy:' + data);
}
function createDestinationSuccess(data, status, headers) {
var content = headers('Location');
vm.policy.targetId = Number(content.substr(content.lastIndexOf('/') + 1));
console.log('Successful create destination, targetId:' + vm.policy.targetId);
saveOrUpdatePolicy();
}
function createDestinationFailed(data, status) {
vm.errorMessages.push($filter('tr')('failed_to_create_destination'));
console.log('Failed to create destination:' + data);
}
function updateDestinationSuccess(data, status) {
console.log('Successful update destination.');
vm.policy.targetId = vm1.selection.id;
saveOrUpdatePolicy();
}
function updateDestinationFailed(data, status) {
vm.errorMessages.push($filter('tr')('failed_to_update_destination'));
$scope.$broadcast('showDialog', true);
console.log('Failed to update destination:' + data);
}
function pingDestinationSuccess(data, status) {
vm.isError = false;
vm.pingMessage = $filter('tr')('successful_ping_target', []);
vm.pingTIP = false;
}
function pingDestinationFailed(data, status) {
vm.isError = true;
if(status === 404) {
data = '';
}
vm.pingMessage = $filter('tr')('failed_to_ping_target', []);
console.log("Failed to ping target:" + data);
vm.pingTIP = false;
}
}
function createPolicy($timeout) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/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('show.bs.modal', function() {
scope.$apply(function() {
scope.form.$setPristine();
scope.form.$setUntouched();
scope.$watch('vm.checkedAddTarget', function(current, origin) {
if(origin) {
var d = scope.replication.destination;
if(angular.isDefined(d) && angular.isDefined(d.selection)) {
ctrl.targetId = d.selection.id;
d.endpoint = d.selection.endpoint;
d.username = d.selection.username;
d.password = d.selection.password;
ctrl.checkDestinationPolicyStatus();
}
}
});
scope.$watch('vm.errorMessages', function(current) {
if(current && current.length > 0) {
ctrl.toggleErrorMessage = true;
}
}, true);
ctrl.checkedAddTarget = false;
ctrl.targetEditable = true;
ctrl.notAvailable = false;
ctrl.pingMessage = '';
ctrl.pingAvailable = true;
ctrl.saveTIP = false;
ctrl.pingTIP = false;
ctrl.toggleErrorMessage = false;
ctrl.errorMessages = [];
ctrl.prepareDestination();
switch(ctrl.action) {
case 'ADD_NEW':
ctrl.addNew();
break;
case 'EDIT':
ctrl.edit(ctrl.policyId);
break;
}
});
});
ctrl.save = save;
ctrl.closeDialog = closeDialog;
function save(form) {
ctrl.toggleErrorMessage = false;
ctrl.errorMessages = [];
var postPayload = {
'projectId': Number(ctrl.projectId),
'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.create(postPayload);
break;
case 'EDIT':
ctrl.update(postPayload);
break;
}
}
function closeDialog() {
element.find('#createPolicyModal').modal('hide');
}
}
}
})();

View File

@ -1,155 +0,0 @@
<!--
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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" id="txtSearchPolicyInput" 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.searchReplicationPolicy()"><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>// 'add_new_policy' | tr //</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" style="max-height: 760px;">
<div class="sub-pane-split">
<div class="table-head-container">
<table class="table table-pane table-header">
<thead>
<th width="10%">// 'name' | tr //</th>
<th width="18%">// 'description' | tr //</th>
<th width="18%">// 'destination' | tr //</th>
<th width="18%">// 'last_start_time' | tr //</th>
<th width="14%">// 'activation' | tr// </th>
<th width="10%">// 'actions' | tr //</th>
<th width="5%"></th>
</thead>
</table>
</div>
<div class="table-body-container" style="height: 250px;">
<table class="table table-pane">
<tbody>
<tr ng-if="vm.replicationPolicies.length == 0">
<td colspan="7" height="100%" class="empty-hint" ><h4 class="text-muted">// 'no_replication_policies_add_new' | tr //</h4></td>
</tr>
<tr policy_id="//r.id//" ng-if="vm.replicationPolicies.length > 0" ng-repeat="r in vm.replicationPolicies" value="//vm.last = $last//">
<td width="10%">//r.name//</td>
<td width="18%">//r.description//</td>
<td width="18%">//r.target_name//</td>
<td width="18%">//r.start_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
<td width="14%" ng-switch on="//r.enabled//">
<span ng-switch-when="1">// 'enabled' | tr //</span>
<span ng-switch-when="0">// 'disabled' | tr //</span>
</td>
<td width="10%">
<div class="display-inline-block" ng-switch on="//r.enabled//">
<a href="javascript:void(0);" ng-click="vm.confirmToTogglePolicy(r.id, 0, r.name)" title="// 'disable' | tr //"><span ng-switch-when="1" class="glyphicon glyphicon-stop color-danger"></span></a>
<a href="javascript:void(0);" ng-click="vm.confirmToTogglePolicy(r.id, 1, r.name)" title="// 'enable' | tr //"><span ng-switch-when="0" class="glyphicon glyphicon-play color-success"></span></a>
</div>
&nbsp;
<a href="javascript:void(0);" data-toggle="modal" data-target="#createPolicyModal" ng-click="vm.editReplication(r.id)" title="// 'edit_policy' | tr //"><span class="glyphicon glyphicon-pencil"></span></a>
&nbsp;
<a href="javascript:void(0);" ng-click="vm.confirmToDelete(r.id, r.name)" title="// 'delete_policy' | tr //"><span class="glyphicon glyphicon-trash"></span></a>
</td>
<td width="5%">
<a ng-if= "r.error_job_count > 0" title = "// 'found_error_in_replication_job' | tr: [r.error_job_count] //"><span class="glyphicon glyphicon-exclamation-sign color-danger" ></span></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-xs-4 col-md-12 well well-sm well-custom well-split">
<div class="col-md-offset-10">//vm.replicationPolicies ? vm.replicationPolicies.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' | tr //</h4>
<hr class="hr-line"/>
<div class="form-inline">
<div class="input-group">
<input type="text" id="txtSearchJobInput" class="form-control" placeholder="" ng-model="vm.replicationJobName" size="20">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" ng-click="vm.searchReplicationJob()" loading-progress hide-target = "true" toggle-in-progress="vm.searchJobTIP"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
<div class="input-group">
<label for="selStatus" class="control-label label-custom">// 'status' | tr //:</label>
<div class="input-group">
<select class="form-control" id="selStatus" ng-options="st.value for st in vm.jobStatus() track by st.key" ng-model="vm.currentStatus" ng-change="vm.searchReplicationJob()"></select>
</div>
</div>
<div class="input-group">
<label for="fromDatePicker" class="control-label label-custom">// 'from' | tr //:</label>
<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})" size="10">
<span class="input-group-addon">
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
</span>
</div>
</div>
<div class="input-group">
<label for="toDatePicker" class="control-label label-custom">// 'to' | tr //:</label>
<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})" size="10">
<span class="input-group-addon">
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
</span>
</div>
</div>
<div class="input-group">
<button type="button" class="btn btn-success" ng-click="vm.refreshReplicationJob()" loading-progress hide-target = "true" toggle-in-progress="vm.refreshJobTIP" title="//'refresh' | tr//"><span class="glyphicon glyphicon-refresh"></span></button>
</div>
</div>
<div class="pane-split" id="down-pane" style="min-height: 142px;">
<div class="sub-pane-split">
<div class="table-head-container">
<table class="table table-pane table-header">
<thead>
<th width="15%">// 'name' | tr //</th>
<th width="15%">// 'operation' | tr //</th>
<th width="24%">// 'creation_time' | tr //</th>
<th width="24%">// 'end_time' | tr //</th>
<th width="10%">// 'status' | tr //</th>
<th width="12%">// 'logs' | tr //</th>
</thead>
</table>
</div>
<div class="table-body-container" style="height: 510px; min-height: 80px;">
<table class="table table-pane">
<tbody>
<tr ng-if="vm.replicationJobs.length == 0">
<td colspan="4" height="100%" class="empty-hint" ><h4 class="text-muted">// 'no_replication_jobs' | tr //</h4></td>
</tr>
<tr ng-if="vm.replicationJobs.length > 0" ng-repeat="r in vm.replicationJobs">
<td width="15%">//r.repository//</td>
<td width="15%">//r.operation//</td>
<td width="24%">//r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
<td width="24%">//r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
<td width="10%">//r.status//</td>
<td width="12%">
<a style="margin-left: 20px;" ng-show="r.status != 'canceled' && r.status != 'pending'" href="javascript:void(0);" ng-click="vm.downloadLog(r.id)" title="// 'download_log' | tr //"><span class="glyphicon glyphicon-file"></span></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<paginator ng-if="vm.totalCount > 0" total-count="//vm.totalCount//" page-size="//vm.pageSize//" display-count="5" page="vm.page"></paginator>
</div>
</div>
</div>

View File

@ -1,440 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.replication')
.directive('listReplication', listReplication)
.factory('jobStatus', jobStatus);
jobStatus.inject = ['$filter', 'trFilter'];
function jobStatus($filter, trFilter) {
return function() {
return [
{'key': 'all' , 'value': $filter('tr')('all')},
{'key': 'pending', 'value': $filter('tr')('pending')},
{'key': 'running', 'value': $filter('tr')('running')},
{'key': 'error' , 'value': $filter('tr')('error')},
{'key': 'retrying', 'value': $filter('tr')('retrying')},
{'key': 'stopped', 'value': $filter('tr')('stopped')},
{'key': 'finished', 'value':$filter('tr')('finished')},
{'key': 'canceled', 'value': $filter('tr')('canceled')}
];
};
}
ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'DeleteReplicationPolicyService', 'ListReplicationJobService', '$window', '$filter', 'trFilter', 'jobStatus'];
function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, DeleteReplicationPolicyService, ListReplicationJobService, $window, $filter, trFilter, jobStatus) {
var vm = this;
vm.sectionHeight = {'min-height': '1260px'};
$scope.$on('retrieveData', function(e, val) {
if(val) {
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.retrievePolicy();
}
});
vm.addReplication = addReplication;
vm.editReplication = editReplication;
vm.deleteReplicationPolicy = deleteReplicationPolicy;
vm.confirmToDelete = confirmToDelete;
vm.searchReplicationPolicy = searchReplicationPolicy;
vm.searchReplicationJob = searchReplicationJob;
vm.refreshReplicationJob = refreshReplicationJob;
vm.retrievePolicy = retrievePolicy;
vm.retrieveJob = retrieveJob;
vm.pageSize = 20;
vm.page = 1;
$scope.$watch('vm.page', function(current) {
if(vm.lastPolicyId !== -1 && current) {
vm.page = current;
console.log('replication job: vm.page:' + current);
vm.retrieveJob(vm.lastPolicyId, vm.page, vm.pageSize);
}
});
vm.confirmToTogglePolicy = confirmToTogglePolicy;
vm.togglePolicy = togglePolicy;
vm.downloadLog = downloadLog;
vm.last = false;
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.retrievePolicy();
vm.jobStatus = jobStatus;
vm.currentStatus = vm.jobStatus()[0];
vm.pickUp = pickUp;
vm.searchJobTIP = false;
vm.refreshJobTIP = false;
function searchReplicationPolicy() {
vm.retrievePolicy();
}
function searchReplicationJob() {
if(vm.lastPolicyId !== -1) {
vm.searchJobTIP = true;
vm.retrieveJob(vm.lastPolicyId, vm.page, vm.pageSize);
}
}
function refreshReplicationJob() {
if(vm.fromDate && vm.toDate && (getDateValue(vm.fromDate) > getDateValue(vm.toDate))) {
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', $filter('tr')('begin_date_is_later_than_end_date'));
$scope.$emit('raiseError', true);
return;
}
if(vm.lastPolicyId !== -1) {
vm.refreshJobTIP = true;
vm.retrieveJob(vm.lastPolicyId, vm.page, vm.pageSize);
}
}
function retrievePolicy() {
ListReplicationPolicyService('', vm.projectId, vm.replicationPolicyName)
.success(listReplicationPolicySuccess)
.error(listReplicationPolicyFailed);
}
function retrieveJob(policyId, page, pageSize) {
var status = (vm.currentStatus.key === 'all' ? '' : vm.currentStatus.key);
ListReplicationJobService(policyId, vm.replicationJobName, status, toUTCSeconds(vm.fromDate, 0, 0, 0), toUTCSeconds(vm.toDate, 23, 59, 59), page, pageSize)
.then(listReplicationJobSuccess, listReplicationJobFailed);
}
function listReplicationPolicySuccess(data, status) {
vm.replicationJobs = [];
vm.replicationPolicies = data || [];
}
function listReplicationPolicyFailed(data, status) {
console.log('Failed to list replication policy:' + data);
}
function listReplicationJobSuccess(response) {
vm.replicationJobs = response.data || [];
vm.totalCount = response.headers('X-Total-Count');
var alertInfo = {
'show': false,
'message': ''
};
angular.forEach(vm.replicationJobs, function(item) {
for(var key in item) {
var value = item[key];
if(key === 'status' && (value === 'error' || value === 'retrying')) {
alertInfo.show = true;
alertInfo.message = $filter('tr')('alert_job_contains_error');
}
switch(key) {
case 'operation':
case 'status':
item[key] = $filter('tr')(value);
break;
default:
break;
}
}
});
$scope.$emit('raiseAlert', alertInfo);
vm.searchJobTIP = false;
vm.refreshJobTIP = false;
}
function listReplicationJobFailed(response) {
console.log('Failed to list replication job:' + response);
vm.searchJobTIP = false;
vm.refreshJobTIP = false;
}
function addReplication() {
vm.modalTitle = $filter('tr')('add_new_policy', []);
vm.action = 'ADD_NEW';
}
function editReplication(policyId) {
vm.policyId = policyId;
vm.modalTitle = $filter('tr')('edit_policy', []);
vm.action = 'EDIT';
console.log('Selected policy ID:' + vm.policyId);
}
function deleteReplicationPolicy() {
DeleteReplicationPolicyService(vm.policyId)
.success(deleteReplicationPolicySuccess)
.error(deleteReplicationPolicyFailed);
}
function deleteReplicationPolicySuccess(data, status) {
console.log('Successful delete replication policy.');
vm.retrievePolicy();
}
function deleteReplicationPolicyFailed(data, status) {
$scope.$emit('modalTitle', $filter('tr')('error'));
if(status === 412) {
$scope.$emit('modalMessage', $filter('tr')('failed_to_delete_replication_enabled'));
}else{
$scope.$emit('modalMessage', $filter('tr')('failed_to_delete_replication_policy'));
}
$scope.$emit('raiseError', true);
console.log('Failed to delete replication policy.');
}
function confirmToDelete(policyId, policyName) {
vm.policyId = policyId;
$scope.$emit('modalTitle', $filter('tr')('confirm_delete_policy_title'));
$scope.$emit('modalMessage', $filter('tr')('confirm_delete_policy', [policyName]));
var emitInfo = {
'confirmOnly': false,
'contentType': 'text/plain',
'action': vm.deleteReplicationPolicy
};
$scope.$emit('raiseInfo', emitInfo);
}
function confirmToTogglePolicy(policyId, enabled, name) {
vm.policyId = policyId;
vm.enabled = enabled;
var status = $filter('tr')(vm.enabled === 1 ? 'enable':'disable');
var title;
var message;
if(enabled === 1){
title = $filter('tr')('confirm_to_toggle_enabled_policy_title');
message = $filter('tr')('confirm_to_toggle_enabled_policy');
}else{
title = $filter('tr')('confirm_to_toggle_disabled_policy_title');
message = $filter('tr')('confirm_to_toggle_disabled_policy');
}
$scope.$emit('modalTitle', title);
$scope.$emit('modalMessage', message);
var emitInfo = {
'contentType': 'text/html',
'confirmOnly': false,
'action': vm.togglePolicy
};
$scope.$emit('raiseInfo', emitInfo);
}
function togglePolicy() {
ToggleReplicationPolicyService(vm.policyId, vm.enabled)
.success(toggleReplicationPolicySuccess)
.error(toggleReplicationPolicyFailed);
}
function toggleReplicationPolicySuccess(data, status) {
console.log('Successful toggle replication policy.');
vm.retrievePolicy();
}
function toggleReplicationPolicyFailed(data, status) {
console.log('Failed to toggle replication policy.');
}
function downloadLog(policyId) {
$window.open('/api/jobs/replication/' + policyId + '/log', '_blank');
}
function pickUp(e) {
switch(e.key){
case 'fromDate':
vm.fromDate = e.value;
break;
case 'toDate':
vm.toDate = e.value;
break;
}
$scope.$apply();
}
function toUTCSeconds(date, hour, min, sec) {
if(!angular.isDefined(date) || date === '') {
return '';
}
var t = new Date(date);
t.setHours(hour);
t.setMinutes(min);
t.setSeconds(sec);
return t.getTime() / 1000;
}
function getDateValue(date) {
if(date) {
return new Date(date);
}
return 0;
}
}
listReplication.inject = ['$timeout', 'I18nService'];
function listReplication($timeout, I18nService) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/replication/list-replication.directive.html',
'scope': {
'sectionHeight': '='
},
'link': link,
'controller': ListReplicationController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
var uponPaneHeight = element.find('#upon-pane').height();
var downPaneHeight = element.find('#down-pane').height();
var uponTableHeight = element.find('#upon-pane .table-body-container').height();
var downTableHeight = element.find('#down-pane .table-body-container').height();
var handleHeight = element.find('.split-handle').height() + element.find('.split-handle').offset().top + element.find('.well').height() - 32;
console.log('handleHeight:' + handleHeight);
var maxDownPaneHeight = 760;
element.find('.split-handle').on('mousedown', mousedownHandler);
function mousedownHandler(e) {
e.preventDefault();
$(document).on('mousemove', mousemoveHandler);
$(document).on('mouseup', mouseupHandler);
}
function mousemoveHandler(e) {
var incrementHeight = $('.container-fluid').scrollTop() + e.pageY;
if(element.find('#down-pane').height() <= maxDownPaneHeight) {
element.find('#upon-pane').css({'height' : (uponPaneHeight - (handleHeight - incrementHeight)) + 'px'});
element.find('#down-pane').css({'height' : (downPaneHeight + (handleHeight - incrementHeight)) + 'px'});
element.find('#upon-pane .table-body-container').css({'height': (uponTableHeight - (handleHeight - incrementHeight)) + 'px'});
element.find('#down-pane .table-body-container').css({'height': (downTableHeight + (handleHeight - incrementHeight)) + '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);
element.find('#upon-pane table>tbody>tr:eq(0)').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'})
.css({'cursor': 'default'});
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.page, ctrl.pageSize);
ctrl.lastPolicyId = $(this).attr('policy_id');
}
element.find('.datetimepicker').datetimepicker({
locale: I18nService().getCurrentLanguage(),
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()});
});
element.find('#txtSearchPolicyInput').on('keydown', function(e) {
if($(this).is(':focus') && e.keyCode === 13) {
ctrl.searchReplicationPolicy();
} else {
$timeout(function() {
if(ctrl.replicationPolicyName.length === 0) {
ctrl.searchReplicationPolicy();
}
});
}
});
element.find('#txtSearchJobInput').on('keydown', function(e) {
if($(this).is(':focus') && e.keyCode === 13) {
ctrl.searchReplicationJob();
} else {
$timeout(function() {
if(ctrl.replicationJobName.length === 0) {
ctrl.searchReplicationJob();
}
});
}
});
}
}
})();

View File

@ -1,25 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular
.module('harbor.replication', [
'harbor.services.replication.policy',
'harbor.services.replication.job'
]);
})();

Some files were not shown because too many files have changed in this diff Show More