Remove old UI.

This commit is contained in:
kunw 2017-03-15 17:27:10 +08:00
parent 959af275d8
commit a80008d0f9
294 changed files with 106840 additions and 16382 deletions

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,17 +42,6 @@ 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")
@ -84,67 +73,6 @@ func TestMain(t *testing.T) {
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>'")
r, _ = http.NewRequest("POST", "/login", nil)
w = httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
@ -171,37 +99,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

@ -0,0 +1,370 @@
{
"SIGN_IN": {
"REMEMBER": "Remember me",
"INVALID_MSG": "Invalid user name or password",
"FORGOT_PWD": "Forgot password",
"HEADER_LINK": "Sign In"
},
"SIGN_UP": {
"TITLE": "Sign Up"
},
"BUTTON": {
"CANCEL": "CANCEL",
"OK": "OK",
"DELETE": "DELETE",
"LOG_IN": "LOG IN",
"SIGN_UP_LINK": "Sign up for an account",
"SIGN_UP": "SIGN UP",
"CONFIRM": "CONFIRM",
"SEND": "SEND",
"SAVE": "SAVE",
"TEST_MAIL": "TEST MAIL SERVER",
"CLOSE": "CLOSE",
"TEST_LDAP": "TEST LDAP SERVER"
},
"TOOLTIP": {
"EMAIL": "Email should be a valid email address like name@example.com",
"USER_NAME": "Can not contain \"~#$% and max length should be less than 20",
"FULL_NAME": "Max length should be less than 20",
"COMMENT": "Length of comment should be less than 20",
"CURRENT_PWD": "Current password is Required",
"PASSWORD": "Password should be at least 8 characters with 1 uppercase, 1 lower case and 1 number",
"CONFIRM_PWD": "Password input here should be same with above password",
"SIGN_IN_USERNAME": "Username is required",
"SIGN_IN_PWD": "Password is required",
"SIGN_UP_MAIL": "Email is only used for resetting your password",
"SIGN_UP_REAL_NAME": "First name and last name",
"ITEM_REQUIRED": "Field is required",
"NUMBER_REQUIRED": "Filed is required and should be numbers",
"PORT_REQUIRED": "Filed is required and should be valid port number"
},
"PLACEHOLDER": {
"CURRENT_PWD": "Enter current password",
"NEW_PWD": "Enter new password",
"CONFIRM_PWD": "Confirm new password",
"USER_NAME": "Enter username",
"MAIL": "Enter email address",
"FULL_NAME": "Enter full name",
"SIGN_IN_NAME": "Username",
"SIGN_IN_PWD": "Password"
},
"PROFILE": {
"TITLE": "User Profile",
"USER_NAME": "Username",
"EMAIL": "Email",
"FULL_NAME": "Full name",
"COMMENT": "Comments",
"PASSWORD": "Password",
"SAVE_SUCCESS": "Save user profile successfully"
},
"CHANGE_PWD": {
"TITLE": "Change Password",
"CURRENT_PWD": "Current Password",
"NEW_PWD": "New Password",
"CONFIRM_PWD": "Confirm Password",
"SAVE_SUCCESS": "Change user password successfully"
},
"ACCOUNT_SETTINGS": {
"PROFILE": "User Profile",
"CHANGE_PWD": "Change Password",
"ABOUT": "About",
"LOGOUT": "Log Out"
},
"GLOBAL_SEARCH": {
"PLACEHOLDER": "Search Harbor..."
},
"SIDE_NAV": {
"DASHBOARD": "Dashboard",
"PROJECTS": "Projects",
"SYSTEM_MGMT": {
"NAME": "Administration",
"USER": "Users",
"REPLICATION": "Replication",
"CONFIG": "Configuration"
},
"LOGS": "Logs"
},
"USER": {
"ADD_ACTION": "USER",
"ENABLE_ADMIN_ACTION": "Enable administrator",
"DISABLE_ADMIN_ACTION": "Disable administrator",
"DEL_ACTION": "Delete",
"FILTER_PLACEHOLDER": "Filter users",
"COLUMN_NAME": "Name",
"COLUMN_ADMIN": "Administrator",
"COLUMN_EMAIL": "Email",
"COLUMN_REG_NAME": "Registration time",
"IS_ADMIN": "Yes",
"IS_NOT_ADMIN": "No",
"ADD_USER_TITLE": "Add User",
"SAVE_SUCCESS": "Add new user successfully",
"DELETION_TITLE": "Confirm user deletion",
"DELETION_SUMMARY": "Do you want to delete user {{param}}?",
"DELETE_SUCCESS": "Delete user successfully"
},
"PROJECT": {
"PROJECTS": "Projects",
"NAME": "Project Name",
"PUBLIC_OR_PRIVATE": "Public/Private",
"REPO_COUNT": "Repositories Count",
"CREATION_TIME": "Creation Time",
"DESCRIPTION": "Description",
"PUBLIC": "Public",
"PRIVATE": "Private",
"MAKE": "Make",
"NEW_POLICY": "New Policy",
"DELETE": "Delete",
"MY_PROJECTS": "My Projects",
"PUBLIC_PROJECTS": "Public Projects",
"NEW_PROJECT": "New Project",
"NAME_IS_REQUIRED": "Project name is required.",
"NAME_MINIMUM_LENGTH": "Project name is too short, it should be greater than 2 characters.",
"NAME_ALREADY_EXISTS": "Project name already exists.",
"NAME_IS_ILLEGAL": "Project name is illegal.",
"UNKNOWN_ERROR": "Unknown error occurred while creating project.",
"ITEMS": "item(s)",
"DELETION_TITLE": "Confirm project deletion",
"DELETION_SUMMARY": "Do you want to delete project {{param}}?",
"FILTER_PLACEHOLDER": "Filter Projects"
},
"PROJECT_DETAIL": {
"REPOSITORIES": "Repositories",
"REPLICATION": "Replication",
"USERS": "Users",
"LOGS": "Logs",
"PROJECTS": "Projects"
},
"MEMBER": {
"NEW_MEMBER": "New Member",
"NAME": "Name",
"ROLE": "Role",
"SYS_ADMIN": "System Admin",
"PROJECT_ADMIN": "Project Admin",
"DEVELOPER": "Developer",
"GUEST": "Guest",
"DELETE": "Delete",
"ITEMS": "item(s)",
"ACTIONS": "Actions",
"USERNAME_DOES_NOT_EXISTS": "Username does not exist.",
"USERNAME_ALREADY_EXISTS": "Username already exists.",
"UNKNOWN_ERROR": "Unknown error occurred while adding member.",
"FILTER_PLACEHOLDER": "Filter Members",
"DELETION_TITLE": "Confirm project member deletion",
"DELETION_SUMMARY": "Do you want to delete project member {{param}}?"
},
"AUDIT_LOG": {
"USERNAME": "Username",
"REPOSITORY_NAME": "Repository Name",
"TAGS": "Tags",
"OPERATION": "Operation",
"TIMESTAMP": "Timestamp",
"ALL_OPERATIONS": "All Operations",
"PULL": "Pull",
"PUSH": "Push",
"CREATE": "Create",
"DELETE": "Delete",
"OTHERS": "Others",
"ADVANCED": "Advanced",
"SIMPLE": "Simple",
"ITEMS": "item(s)",
"FILTER_PLACEHOLDER": "Filter Logs"
},
"REPLICATION": {
"REPLICATION_RULE": "Rules",
"NEW_REPLICATION_RULE": "New Replication Rule",
"ENDPOINTS": "Endpoints",
"FILTER_POLICIES_PLACEHOLDER": "Filter Policies",
"FILTER_JOBS_PLACEHOLDER": "Filter Jobs",
"DELETION_TITLE": "Confirm Policy Deletion",
"DELETION_SUMMARY": "Do you want to delete policy {{param}}?",
"FILTER_TARGETS_PLACEHOLDER": "Filter Targets",
"DELETION_TITLE_TARGET": "Confirm Target Deletion",
"DELETION_SUMMARY_TARGET": "Do you want to delete target {{param}}?",
"ADD_POLICY": "Add Policy",
"EDIT_POLICY": "Edit Policy",
"DELETE_POLICY": "Delete Policy",
"TEST_CONNECTION": "Test Connection",
"TESTING_CONNECTION": "Testing Connection...",
"TEST_CONNECTION_SUCCESS": "Connection tested successfully.",
"TEST_CONNECTION_FAILURE": "Failed to ping target.",
"NAME": "Name",
"PROJECT": "Project",
"NAME_IS_REQUIRED": "Name is required.",
"DESCRIPTION": "Description",
"ENABLE": "Enable",
"DISABLE": "Disable",
"DESTINATION_NAME": "Destination Name",
"DESTINATION_NAME_IS_REQUIRED": "Destination name is required.",
"NEW_DESTINATION": "New Destination",
"DESTINATION_URL": "Endpoint URL",
"DESTINATION_URL_IS_REQUIRED": "Endpoint URL is required.",
"DESTINATION_USERNAME": "Username",
"DESTINATION_PASSWORD": "Password",
"ALL_STATUS": "All Status",
"ENABLED": "Enabled",
"DISABLED": "Disabled",
"LAST_START_TIME": "Last Start Time",
"ACTIVATION": "Activation",
"REPLICATION_JOBS": "Replication Jobs",
"ALL": "All",
"PENDING": "Pending",
"RUNNING": "Running",
"ERROR": "Error",
"RETRYING": "Retrying",
"STOPPED": "Stopped",
"FINISHED": "Finished",
"CANCELED": "Canceled",
"SIMPLE": "Simple",
"ADVANCED": "Advanced",
"STATUS": "Status",
"OPERATION": "Operation",
"CREATION_TIME": "Start Time",
"END_TIME": "End Time",
"LOGS": "Logs",
"ITEMS": "item(s)"
},
"DESTINATION": {
"NEW_ENDPOINT": "New Endpoint",
"NAME": "Destination Name",
"NAME_IS_REQUIRED": "Destination name is required.",
"URL": "Endpoint URL",
"URL_IS_REQUIRED": "Endpoint URL is required.",
"USERNAME": "Username",
"PASSWORD": "Password",
"TEST_CONNECTION": "Test Connection",
"TITLE_EDIT": "Edit Endpoint",
"TITLE_ADD": "Create Endpoint",
"DELETE": "Delete Endpoint",
"TESTING_CONNECTION": "Testing Connection...",
"TEST_CONNECTION_SUCCESS": "Connection tested successfully.",
"TEST_CONNECTION_FAILURE": "Failed to ping target.",
"CONFLICT_NAME": "Name or endpoint URL already exists.",
"INVALID_NAME": "Invalid destination name.",
"FAILED_TO_GET_TARGET": "Failed to get endpoint.",
"CREATION_TIME": "Creation Time",
"ITEMS": "item(s)"
},
"REPOSITORY": {
"COPY_ID": "Copy ID",
"COPY_PARENT_ID": "Copy Parent ID",
"DELETE": "Delete",
"NAME": "Name",
"TAGS_COUNT": "Tags",
"PULL_COUNT": "Pulls",
"PULL_COMMAND": "Pull Command",
"MY_REPOSITORY": "My Repository",
"PUBLIC_REPOSITORY": "Public Repository",
"DELETION_TITLE_REPO": "Confirm Repository Deletion",
"DELETION_SUMMARY_REPO": "Do you want to delete repository {{param}}?",
"DELETION_TITLE_TAG": "Confirm Tag Deletion",
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
"DELETION_TITLE_TAG_DENIED": "Signed Tag can't be deleted",
"DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from Notary before they can be deleted.",
"FILTER_FOR_REPOSITORIES": "Filter for repositories",
"TAG": "Tag",
"VERIFIED": "Verified",
"AUTHOR": "Author",
"CREATED": "Creation Time",
"DOCKER_VERSION": "Docker Version",
"ARCHITECTURE": "Architecture",
"OS": "OS",
"SHOW_DETAILS": "Show Details",
"REPOSITORIES": "Repositories",
"ITEMS": "item(s)"
},
"ALERT": {
"FORM_CHANGE_CONFIRMATION": "Form value changed, confirm to cancel?"
},
"RESET_PWD": {
"TITLE": "Reset Password",
"CAPTION": "Enter your email to reset your password",
"EMAIL": "Email",
"SUCCESS": "Mail of resetting password is successfully send to your mail box",
"CAPTION2": "Enter your new password",
"RESET_OK": "Password is successfully reset, click OK button to login with new password"
},
"RECENT_LOG": {
"SUB_TITLE": "Show recent",
"SUB_TITLE_SUFIX": "logs"
},
"CONFIG": {
"TITLE": "Configuration",
"AUTH": "Authentication",
"REPLICATION": "Replication",
"EMAIL": "Email",
"SYSTEM": "System Settings",
"CONFIRM_TITLE": "Confirm to cancel",
"CONFIRM_SUMMARY": "Some changes happend, confirm to cancel?",
"SAVE_SUCCESS": "Configurations are successfully saved",
"MAIL_SERVER": "Email Server",
"MAIL_SERVER_PORT": "Email Server Port",
"MAIL_USERNAME": "Email Username",
"MAIL_PASSWORD": "Email Password",
"MAIL_FROM": "Email From",
"MAIL_SSL": "Email SSL",
"SSL_TOOLTIP": "Enable SSL for email server connection",
"VERIFY_REMOTE_CERT": "Verify Remote Certificate",
"TOKEN_EXPIRATION": "Token Expiration (Minutes)",
"AUTH_MODE": "Authentication",
"PRO_CREATION_RESTRICTION": "Project Creation Restriction",
"SELF_REGISTRATION": "Self Registration",
"AUTH_MODE_DB": "Database",
"AUTH_MODE_LDAP": "LDAP",
"SCOPE_BASE": "Base",
"SCOPE_ONE_LEVEL": "OneLevel",
"SCOPE_SUBTREE": "Subtree",
"PRO_CREATION_EVERYONE": "Everyone",
"PRO_CREATION_ADMIN": "Admin Only",
"TOOLTIP": {
"SELF_REGISTRATION": "Enable sign up",
"VERIFY_REMOTE_CERT": "Determine whether the image replication should verify the certificate of a remote Habor registry. Uncheck this box when the remote registry uses a self -signed or untrusted certificate.",
"AUTH_MODE": "By default the auth mode is db_auth, i.e. the credentials are stored in a local database.Set it to ldap_auth if you want to verify a user's credentials against an LDAP server.",
"LDAP_SEARCH_DN": "A user's DN who has the permission to search the LDAP/AD server.If your LDAP/AD server does not support anonymous search, you should configure this DN and ldap_search_pwd.",
"LDAP_BASE_DN": "The base DN from which to look up a user in LDAP/AD",
"LDAP_UID": "The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD",
"LDAP_SCOPE": "The scope to search for users",
"TOKEN_EXPIRATION": "The expiration time (in minute) of token created by token service, default is 30 minutes",
"PRO_CREATION_RESTRICTION": "The flag to control what users have permission to create projects,Be default everyone can create a project, set to 'adminonly' such that only admin can create project."
},
"LDAP": {
"URL": "LDAP URL",
"SEARCH_DN": "LDAP Search DN",
"SEARCH_PWD": "LDAP Search Password",
"BASE_DN": "LDAP Base DN",
"FILTER": "LDAP Filter",
"UID": "LDAP UID",
"SCOPE": "lDAP Scope"
},
"TEST_MAIL_SUCCESS": "Connection to mail server is verified",
"TEST_LDAP_SUCCESS": "Connection to ldap server is verified"
},
"PAGE_NOT_FOUND": {
"MAIN_TITLE": "Page not found",
"SUB_TITLE": "Redirect to harbor home in",
"UNIT": "seconds..."
},
"ABOUT": {
"VERSION": "Version",
"BUILD": "Build",
"COPYRIGHT": "Copyright 1998-2016 VMware. Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at",
"TRADEMARK": "Vmware is a registered trademark or trademark of VMware. Inc. in the United States and other jurisdictions. All other marks and names mentioned herein may be trademark of their respective companies.",
"END_USER_LICENSE": "End User License Agreement",
"OPEN_SOURCE_LICENSE": "Open Source/Third Party License"
},
"START_PAGE": {
"GETTING_START": "Project Harbor is an enterprise-class registry server that stores and distributes Docker images. Harbor extends the open source Docker Distribution by adding the functionalities usually required by an enterprise, such as security, identity and management. As an enterprise private registry, Harbor offers better performance and security.",
"GETTING_START_TITLE": "Getting Start"
},
"TOP_REPO": "Popular Repositories",
"STATISTICS": {
"TITLE": "STATISTICS",
"PRO_ITEM": "PROJECTS",
"REPO_ITEM": "REPOSITORIES",
"INDEX_MY": "MY",
"INDEX_PUB": "PUBLIC",
"INDEX_TOTAL": "TOTAL"
},
"UNKNOWN_ERROR": "There are some unknown errors occurred, please try later",
"UNAUTHORIZED_ERROR": "Session is invalid or expired, you need to sign in to continue the operation",
"FORBIDDEN_ERROR": "You are not allowed to trigger the operation"
}

View File

@ -0,0 +1,370 @@
{
"SIGN_IN": {
"REMEMBER": "记住我",
"INVALID_MSG": "用户名或者密码不正确",
"FORGOT_PWD": "忘记密码",
"HEADER_LINK": "登录"
},
"SIGN_UP": {
"TITLE": "注册"
},
"BUTTON": {
"CANCEL": "取消",
"OK": "确定",
"DELETE": "删除",
"LOG_IN": "登录",
"SIGN_UP_LINK": "注册账号",
"SIGN_UP": "注册",
"CONFIRM": "确定",
"SEND": "发送",
"SAVE": "保存",
"TEST_MAIL": "测试邮件服务器",
"CLOSE": "关闭",
"TEST_LDAP": "测试LDAP服务器"
},
"TOOLTIP": {
"EMAIL": "请使用正确的邮箱地址比如name@example.com",
"USER_NAME": "不能包含\"~#$%特殊字符且长度不能超过20",
"FULL_NAME": "长度不能超过20",
"COMMENT": "长度不能超过20",
"CURRENT_PWD": "当前密码必需",
"PASSWORD": "密码长度至少为8且需包含至少一个大写字符一个小写字符和一个数字",
"CONFIRM_PWD": "当前密码须与上述输入密码一致",
"SIGN_IN_USERNAME": "用户名必需",
"SIGN_IN_PWD": "密码必需",
"SIGN_UP_MAIL": "邮件地址仅用来重置您的密码",
"SIGN_UP_REAL_NAME": "全名",
"ITEM_REQUIRED": "此项必需",
"NUMBER_REQUIRED": "此项必需且为数字",
"PORT_REQUIRED": "此项必需且为合理端口号"
},
"PLACEHOLDER": {
"CURRENT_PWD": "输入当前密码",
"NEW_PWD": "输入新密码",
"CONFIRM_PWD": "确认新密码",
"USER_NAME": "输入用户名称",
"MAIL": "输入邮箱地址",
"FULL_NAME": "输入全名",
"SIGN_IN_NAME": "用户名",
"SIGN_IN_PWD": "密码"
},
"PROFILE": {
"TITLE": "用户设置",
"USER_NAME": "用户名",
"EMAIL": "邮箱",
"FULL_NAME": "全名",
"COMMENT": "注释",
"PASSWORD": "密码",
"SAVE_SUCCESS": "成功保存用户配置"
},
"CHANGE_PWD": {
"TITLE": "修改密码",
"CURRENT_PWD": "当前密码",
"NEW_PWD": "新密码",
"CONFIRM_PWD": "确认密码",
"SAVE_SUCCESS": "更改用户密码成功"
},
"ACCOUNT_SETTINGS": {
"PROFILE": "用户设置",
"CHANGE_PWD": "修改密码",
"ABOUT": "关于",
"LOGOUT": "退出"
},
"GLOBAL_SEARCH": {
"PLACEHOLDER": "搜索 Harbor..."
},
"SIDE_NAV": {
"DASHBOARD": "仪表板",
"PROJECTS": "项目",
"SYSTEM_MGMT": {
"NAME": "系统管理",
"USER": "用户管理",
"REPLICATION": "复制管理",
"CONFIG": "配置管理"
},
"LOGS": "日志"
},
"USER": {
"ADD_ACTION": "用户",
"ENABLE_ADMIN_ACTION": "设置为管理员",
"DISABLE_ADMIN_ACTION": "取消管理员",
"DEL_ACTION": "删除",
"FILTER_PLACEHOLDER": "过滤用户",
"COLUMN_NAME": "用户名",
"COLUMN_ADMIN": "管理员",
"COLUMN_EMAIL": "邮件",
"COLUMN_REG_NAME": "注册时间",
"IS_ADMIN": "是",
"IS_NOT_ADMIN": "否",
"ADD_USER_TITLE": "添加用户",
"SAVE_SUCCESS": "添加用户成功",
"DELETION_TITLE": "删除用户确认",
"DELETION_SUMMARY": "你确认删除用户 {{param}}?",
"DELETE_SUCCESS": "删除用户成功"
},
"PROJECT": {
"PROJECTS": "项目",
"NAME": "项目名称",
"PUBLIC_OR_PRIVATE": "公开/私有",
"REPO_COUNT": "镜像仓库数",
"CREATION_TIME": "创建时间",
"DESCRIPTION": "描述",
"PUBLIC": "公开",
"PRIVATE": "私有",
"MAKE": "设为",
"NEW_POLICY": "新建策略",
"DELETE": "删除",
"MY_PROJECTS": "我的项目",
"PUBLIC_PROJECTS": "公开项目",
"NEW_PROJECT": "新建项目",
"NAME_IS_REQUIRED": "项目名称为必填项",
"NAME_MINIMUM_LENGTH": "项目名称长度过短至少多于2个字符。",
"NAME_ALREADY_EXISTS": "项目名称已存在。",
"NAME_IS_ILLEGAL": "项目名称非法。",
"UNKNOWN_ERROR": "创建项目时发生未知错误。",
"ITEMS": "条记录",
"DELETION_TITLE": "删除项目确认",
"DELETION_SUMMARY": "你确认删除项目 {{param}}",
"FILTER_PLACEHOLDER": "过滤项目"
},
"PROJECT_DETAIL": {
"REPOSITORIES": "镜像仓库",
"REPLICATION": "复制",
"USERS": "用户",
"LOGS": "日志",
"PROJECTS": "项目"
},
"MEMBER": {
"NEW_MEMBER": "新建成员",
"NAME": "姓名",
"ROLE": "角色",
"SYS_ADMIN": "系统管理员",
"PROJECT_ADMIN": "项目管理员",
"DEVELOPER": "开发人员",
"GUEST": "访客",
"DELETE": "删除",
"ITEMS": "条记录",
"ACTIONS": "操作",
"USERNAME_DOES_NOT_EXISTS": "用户名不存在",
"USERNAME_ALREADY_EXISTS": "用户名已存在",
"UNKNOWN_ERROR": "添加成员时发生未知错误。",
"FILTER_PLACEHOLDER": "过滤成员",
"DELETION_TITLE": "删除项目成员确认",
"DELETION_SUMMARY": "你确认删除项目成员 {{param}}?"
},
"AUDIT_LOG": {
"USERNAME": "用户名",
"REPOSITORY_NAME": "镜像名称",
"TAGS": "标签",
"OPERATION": "操作",
"TIMESTAMP": "时间戳",
"ALL_OPERATIONS": "所有操作",
"PULL": "Pull",
"PUSH": "Push",
"CREATE": "Create",
"DELETE": "Delete",
"OTHERS": "其他",
"ADVANCED": "高级检索",
"SIMPLE": "简单检索",
"ITEMS": "条记录",
"FILTER_PLACEHOLDER": "过滤日志"
},
"REPLICATION": {
"REPLICATION_RULE": "复制策略",
"NEW_REPLICATION_RULE": "新建策略",
"ENDPOINTS": "目标",
"FILTER_POLICIES_PLACEHOLDER": "过滤策略",
"FILTER_JOBS_PLACEHOLDER": "过滤任务",
"DELETION_TITLE": "删除策略确认",
"DELETION_SUMMARY": "确认删除策略 {{param}}?",
"FILTER_TARGETS_PLACEHOLDER": "过滤目标",
"DELETION_TITLE_TARGET": "删除目标确认",
"DELETION_SUMMARY_TARGET": "确认删除目标 {{param}}?",
"ADD_POLICY": "新建策略",
"EDIT_POLICY": "修改策略",
"DELETE_POLICY": "删除策略",
"TEST_CONNECTION": "测试连接",
"TESTING_CONNECTION": "正在测试连接...",
"TEST_CONNECTION_SUCCESS": "测试连接成功。",
"TEST_CONNECTION_FAILURE": "测试连接失败。",
"NAME": "名称",
"PROJECT": "项目",
"NAME_IS_REQUIRED": "名称为必填项",
"DESCRIPTION": "描述",
"ENABLE": "启用",
"DISABLE": "停用",
"DESTINATION_NAME": "目标名",
"DESTINATION_NAME_IS_REQUIRED": "目标名称为必填项。",
"NEW_DESTINATION": "创建目标",
"DESTINATION_URL": "目标URL",
"DESTINATION_URL_IS_REQUIRED": "目标URL为必填项。",
"DESTINATION_USERNAME": "用户名",
"DESTINATION_PASSWORD": "密码",
"ALL_STATUS": "所有状态",
"ENABLED": "启用",
"DISABLED": "停用",
"LAST_START_TIME": "上次起始时间",
"ACTIVATION": "活动状态",
"REPLICATION_JOBS": "复制任务",
"ALL": "全部",
"PENDING": "挂起",
"RUNNING": "运行中",
"ERROR": "错误",
"RETRYING": "重试中",
"STOPPED": "已停止",
"FINISHED": "已完成",
"CANCELED": "已取消",
"SIMPLE": "简单检索",
"ADVANCED": "高级检索",
"STATUS": "状态",
"OPERATION": "操作",
"CREATION_TIME": "创建时间",
"END_TIME": "结束时间",
"LOGS": "日志",
"ITEMS": "条记录"
},
"DESTINATION": {
"NEW_ENDPOINT": "新建目标",
"NAME": "目标名",
"NAME_IS_REQUIRED": "目标名为必填项。",
"URL": "目标URL",
"URL_IS_REQUIRED": "目标URL为必填项。",
"USERNAME": "用户名",
"PASSWORD": "密码",
"TEST_CONNECTION": "测试连接",
"TITLE_EDIT": "编辑目标",
"TITLE_ADD": "新建目标",
"DELETE": "删除目标",
"TESTING_CONNECTION": "正在测试连接...",
"TEST_CONNECTION_SUCCESS": "测试连接成功。",
"TEST_CONNECTION_FAILURE": "测试连接失败。",
"CONFLICT_NAME": "目标名或目标URL已存在。",
"INVALID_NAME": "无效的目标名称。",
"FAILED_TO_GET_TARGET": "获取目标失败。",
"CREATION_TIME": "创建时间",
"ITEMS": "条记录"
},
"REPOSITORY": {
"COPY_ID": "复制ID",
"COPY_PARENT_ID": "复制父级ID",
"DELETE": "删除",
"NAME": "名称",
"TAGS_COUNT": "标签数",
"PULL_COUNT": "下载数",
"PULL_COMMAND": "Pull命令",
"MY_REPOSITORY": "我的镜像",
"PUBLIC_REPOSITORY": "公共镜像",
"DELETION_TITLE_REPO": "删除镜像仓库确认",
"DELETION_SUMMARY_REPO": "确认删除镜像仓库 {{param}}?",
"DELETION_TITLE_TAG": "删除镜像标签确认",
"DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?",
"DELETION_TITLE_TAG_DENIED": "已签名的镜像不能被删除",
"DELETION_SUMMARY_TAG_DENIED": "要删除此镜像标签必须首先从Notary中删除。",
"FILTER_FOR_REPOSITORIES": "过滤镜像仓库",
"TAG": "标签",
"VERIFIED": "已验证",
"AUTHOR": "作者",
"CREATED": "创建时间",
"DOCKER_VERSION": "Docker版本",
"ARCHITECTURE": "架构",
"OS": "操作系统",
"SHOW_DETAILS": "显示详细",
"REPOSITORIES": "镜像仓库",
"ITEMS": "条记录"
},
"ALERT": {
"FORM_CHANGE_CONFIRMATION": "表单内容改变,确认取消?"
},
"RESET_PWD": {
"TITLE": "重置密码",
"CAPTION": "输入用来重置密码的邮箱",
"EMAIL": "邮箱",
"SUCCESS": "重置密码邮件已成功发送",
"CAPTION2": "请输入您的新密码",
"RESET_OK": "密码重置成功,点击确定按钮前往登录页登录"
},
"RECENT_LOG": {
"SUB_TITLE": "显示最近",
"SUB_TITLE_SUFIX": "条日志记录"
},
"CONFIG": {
"TITLE": "配置",
"AUTH": "认证",
"REPLICATION": "复制",
"EMAIL": "邮箱",
"SYSTEM": "系统设置",
"CONFIRM_TITLE": "确认取消",
"CONFIRM_SUMMARY": "配置项有改动, 确定取消?",
"SAVE_SUCCESS": "变更的配置项成功保存",
"MAIL_SERVER": "邮件服务器",
"MAIL_SERVER_PORT": "邮件服务器端口",
"MAIL_USERNAME": "用户名",
"MAIL_PASSWORD": "密码",
"MAIL_FROM": "邮件来源",
"MAIL_SSL": "邮件 SSL",
"SSL_TOOLTIP": "应用SSL到邮件服务器连接",
"VERIFY_REMOTE_CERT": "验证远程证书",
"TOKEN_EXPIRATION": "令牌过期时间(分钟)",
"AUTH_MODE": "认证模式",
"PRO_CREATION_RESTRICTION": "项目创建限制",
"SELF_REGISTRATION": "自注册",
"AUTH_MODE_DB": "数据库",
"AUTH_MODE_LDAP": "LDAP",
"SCOPE_BASE": "基础",
"SCOPE_ONE_LEVEL": "单级",
"SCOPE_SUBTREE": "子树",
"PRO_CREATION_EVERYONE": "所有人",
"PRO_CREATION_ADMIN": "仅管理员",
"TOOLTIP": {
"SELF_REGISTRATION": "激活注册功能",
"VERIFY_REMOTE_CERT": "确定镜像复制是否要验证远程Harbor镜像库的证书。如果远程镜像库使用的是自签或者非信任证书不要勾选此选项。",
"AUTH_MODE": "默认认证模式为本地认证比如用户凭证存储在本地数据库。如果使用LDAP服务来认证用户则设置为LDAP服务。",
"LDAP_SEARCH_DN": "有权搜索LDAP服务器的用户的DN。如果LDAP服务器不支持匿名搜索则需要配置此DN之和搜索密码。",
"LDAP_BASE_DN": "用来在LDAP和AD中搜寻用户的基础DN。",
"LDAP_UID": "在搜索中用来匹配用户的属性可以是uid,cn,email,sAMAccountName或者其它LDAP/AD服务器支持的属性。",
"LDAP_SCOPE": "搜索用户的范围",
"TOKEN_EXPIRATION": "由令牌服务创建的令牌的过期时间分钟默认为30分钟。",
"PRO_CREATION_RESTRICTION": "用来控制那些用户有权创建项目的标志位,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。"
},
"LDAP": {
"URL": "LDAP地址",
"SEARCH_DN": "LDAP搜索专有名称DN)",
"SEARCH_PWD": "LDAP搜索密码",
"BASE_DN": "LDAP基础专有名称DN)",
"FILTER": "LDAP过滤器",
"UID": "LDAP用户标识UID)",
"SCOPE": "lDAP范围"
},
"TEST_MAIL_SUCCESS": "邮件服务器的连通正常",
"TEST_LDAP_SUCCESS": "LDAP服务器的连通正常"
},
"PAGE_NOT_FOUND": {
"MAIN_TITLE": "页面不存在",
"SUB_TITLE": "重定向到harbor主页面在",
"UNIT": "秒后..."
},
"ABOUT": {
"VERSION": "版本",
"BUILD": "构建",
"COPYRIGHT": "Copyright 1998-2016 VMware. Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at",
"TRADEMARK": "Vmware is a registered trademark or trademark of VMware. Inc. in the United States and other jurisdictions. All other marks and names mentioned herein may be trademark of their respective companies.",
"END_USER_LICENSE": "终端用户许可协议",
"OPEN_SOURCE_LICENSE": "开源/第三方许可协议"
},
"START_PAGE": {
"GETTING_START": "Project Harbor is an enterprise-class registry server that stores and distributes Docker images. Harbor extends the open source Docker Distribution by adding the functionalities usually required by an enterprise, such as security, identity and management. As an enterprise private registry, Harbor offers better performance and security.",
"GETTING_START_TITLE": "从这开始"
},
"TOP_REPO": "受欢迎镜像库",
"STATISTICS": {
"TITLE": "统计",
"PRO_ITEM": "项目",
"REPO_ITEM": "镜像库",
"INDEX_MY": "私有的",
"INDEX_PUB": "公开的",
"INDEX_TOTAL": "总计"
},
"UNKNOWN_ERROR": "发生未知错误,请稍后再试",
"UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续",
"FORBIDDEN_ERROR": "当前操作被禁止,请确认你有合法的权限"
}

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'
]);
})();

View File

@ -1,44 +0,0 @@
<div class="tab-pane" id="repositories" 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.filterInput" value="" size="30">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" ng-click="vm.searchRepo()"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</div>
<div class="pane">
<div class="sub-pane">
<div ng-if="vm.repositories.length == 0" class="empty-hint">
<h4 style="margin-top: 120px;" class="text-muted">// 'no_repositories' | tr //</h4>
</div>
<div ng-if="vm.repositories.length > 0" class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default" ng-repeat="repo in vm.repositories">
<div class="panel-heading" role="tab" id="heading//$index + 1//">
<h4 class="panel-title">
<div class="row">
<div class="col-md-10" style="padding: 2px">
<a ng-show="vm.tagCount[repo] > 0" role="button" data-toggle="collapse" data-parent="" href="?project_id=//vm.projectId//#collapse//$index + 1//" aria-expanded="true" aria-controls="collapse//$index+1//">
<span class="glyphicon glyphicon-book"></span> &nbsp;//repo// &nbsp;&nbsp;<span class="badge">//vm.tagCount[repo]//</span>
</a>
<a ng-if="vm.tagCount[repo] === 0" role="button" style="text-decoration: none;" data-toggle="collapse" data-parent="" aria-expanded="true" aria-controls="collapse//$index+1//">
<span class="glyphicon glyphicon-book"></span> &nbsp;//repo// &nbsp;&nbsp;<span class="badge">//vm.tagCount[repo]//</span>
</a>
</div>
<div class="col-md-2" style="padding: 2px; padding-left: 6%;">
&nbsp;&nbsp;
<input ng-if="vm.tagCount[repo] > 0 && vm.roleId == 1" type="checkbox" ng-checked="vm.selectAll[repo]" ng-click="vm.selectAllTags(repo)">
<a ng-if="vm.tagCount[repo] > 0 && vm.roleId == 1 && vm.selectedTags[repo].length > 0" href="javascript:void(0)" ng-click="vm.deleteSelectedTagsByRepo(repo)" title="// 'delete_selected_tag' | tr //" loading-progress hide-target="true" toggle-in-progress="vm.toggleInProgress[repo + '|']"><span class="glyphicon glyphicon-trash"></span></a>
</div>
</div>
</h4>
</div>
<list-tag ng-show="vm.tagCount[repo] > 0" associate-id="$index + 1" repo-name="repo" tag-count="vm.tagCount" toggle-in-progress="vm.toggleInProgress" delete-by-tag="vm.deleteByTag()" role-id="//vm.roleId//"></list-tag>
</div>
</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,290 +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.repository')
.directive('listRepository', listRepository);
ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName', '$window'];
function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName, $window) {
$scope.subsTabPane = 30;
var vm = this;
vm.sectionHeight = {'min-height': '579px'};
vm.filterInput = '';
vm.toggleInProgress = [];
var hashValue = $location.hash();
if(hashValue) {
var slashIndex = hashValue.indexOf('/');
if(slashIndex >=0) {
vm.filterInput = hashValue.substring(slashIndex + 1);
}else{
vm.filterInput = hashValue;
}
}
vm.page = 1;
vm.pageSize = 15;
vm.retrieve = retrieve;
vm.searchRepo = searchRepo;
vm.tagCount = {};
vm.projectId = getParameterByName('project_id', $location.absUrl());
$scope.$on('retrieveData', function(e, val) {
if(val) {
vm.projectId = getParameterByName('project_id', $location.absUrl());
vm.filterInput = '';
vm.retrieve();
}
});
$scope.$watch('vm.repositories', function(current) {
if(current) {
vm.repositories = current || [];
}
});
$scope.$watch('vm.page', function(current) {
if(current) {
vm.page = current;
vm.retrieve();
}
});
$scope.$watch('vm.tagName', function(current) {
if(current) {
vm.selectedTags = [];
}
});
$scope.$on('repoName', function(e, val) {
vm.repoName = val;
});
$scope.$on('tag', function(e, val){
vm.tag = val;
});
$scope.$on('tagCount', function(e, val) {
vm.tagCount = val;
});
$scope.$on('tags', function(e, val) {
vm.tags = val;
});
vm.deleteByRepo = deleteByRepo;
vm.deleteByTag = deleteByTag;
vm.deleteSelectedTagsByRepo = deleteSelectedTagsByRepo;
vm.deleteImage = deleteImage;
vm.deleteSelectedTags = deleteSelectedTags;
vm.selectAll = [];
vm.selectAllTags = selectAllTags;
vm.selectedTags = [];
function retrieve(){
console.log('retrieve repositories, project_id:' + vm.projectId);
ListRepositoryService(vm.projectId, vm.filterInput, vm.page, vm.pageSize)
.then(getRepositoryComplete, getRepositoryFailed);
}
function getRepositoryComplete(response) {
vm.repositories = response.data || [];
vm.totalCount = response.headers('X-Total-Count');
vm.selectAll[vm.repoName] = false;
vm.selectedTags = [];
}
function getRepositoryFailed(response) {
var errorMessage = '';
if(response.status === 404) {
errorMessage = $filter('tr')('project_does_not_exist');
}else{
errorMessage = $filter('tr')('failed_to_get_project');
}
$scope.$emit('modalTitle', $filter('tr')('error'));
$scope.$emit('modalMessage', errorMessage);
var emitInfo = {
'confirmOnly': true,
'contentType': 'text/html',
'action' : function() {
$window.location.href = '/dashboard';
}
};
$scope.$emit('raiseInfo', emitInfo);
console.log('Failed to list repositories:' + response.data);
}
function searchRepo() {
$scope.$broadcast('refreshTags', true);
vm.retrieve();
}
function deleteByRepo(repoName) {
vm.repoName = repoName;
vm.tag = '';
$scope.$emit('modalTitle', $filter('tr')('alert_delete_repo_title', [repoName]));
$scope.$emit('modalMessage', $filter('tr')('alert_delete_repo', [repoName]));
var emitInfo = {
'confirmOnly': false,
'contentType': 'text/html',
'action' : vm.deleteImage
};
$scope.$emit('raiseInfo', emitInfo);
}
function deleteSelectedTagsByRepo(repo) {
vm.repoName = repo;
$scope.$broadcast('gatherSelectedTags' + vm.repoName, true);
var emitInfo = {
'confirmOnly': false,
'contentType': 'text/html',
'action' : vm.deleteSelectedTags
};
$scope.$emit('modalTitle', $filter('tr')('alert_delete_tag_title'));
$scope.$emit('modalMessage', $filter('tr')('alert_delete_selected_tag'));
$scope.$emit('raiseInfo', emitInfo);
}
function selectAllTags(repo) {
vm.selectAll[repo] = !vm.selectAll[repo];
console.log('send to tags selectAll:' + vm.selectAll[repo]);
$scope.$broadcast('selectAll' + repo, {'status': vm.selectAll[repo], 'repoName': repo});
}
$scope.$on('selectedAll', function(e, val) {
console.log('received from tags selectedAll:' + angular.toJson(val));
vm.selectAll[val.repoName] = val.status;
});
function deleteByTag() {
$scope.$emit('modalTitle', $filter('tr')('alert_delete_tag_title', [vm.tag]));
var message;
console.log('vm.tagCount:' + angular.toJson(vm.tagCount[vm.repoName]));
$scope.$emit('modalMessage', $filter('tr')('alert_delete_tag', [vm.tag]));
var emitInfo = {
'confirmOnly': false,
'contentType': 'text/html',
'action' : vm.deleteImage
};
$scope.$emit('raiseInfo', emitInfo);
}
function deleteImage() {
console.log('Delete image, repoName:' + vm.repoName + ', tag:' + vm.tag);
vm.toggleInProgress[vm.repoName + '|' + vm.tag] = true;
DeleteRepositoryService(vm.repoName, vm.tag)
.success(deleteRepositorySuccess)
.error(deleteRepositoryFailed);
}
$scope.$on('selectedTags', function(e, val) {
if(val) {
vm.selectedTags[val.repoName] = val.tags;
}
});
function deleteSelectedTags() {
console.log('Delete selected tags:' + angular.toJson(vm.selectedTags[vm.repoName]) + ' under repo:' + vm.repoName);
vm.toggleInProgress[vm.repoName + '|'] = true;
for(var i in vm.selectedTags[vm.repoName] || []) {
var tag = vm.selectedTags[vm.repoName][i];
if(tag !== '') {
vm.toggleInProgress[vm.repoName + '|' + tag] = true;
DeleteRepositoryService(vm.repoName, tag)
.success(deleteRepositorySuccess)
.error(deleteRepositoryFailed);
}
}
}
function deleteRepositorySuccess(data, status) {
vm.toggleInProgress[vm.repoName + '|'] = false;
vm.toggleInProgress[vm.repoName + '|' + vm.tag] = false;
vm.retrieve();
$scope.$broadcast('refreshTags', true);
}
function deleteRepositoryFailed(data, status) {
vm.toggleInProgress[vm.repoName + '|'] = false;
vm.toggleInProgress[vm.repoName + '|' + vm.tag] = false;
$scope.$emit('modalTitle', $filter('tr')('error'));
var message;
if(status === 401) {
message = $filter('tr')('failed_to_delete_repo_insuffient_permissions');
}else{
message = $filter('tr')('failed_to_delete_repo');
}
$scope.$emit('modalMessage', message);
$scope.$emit('raiseError', true);
console.log('Failed to delete repository:' + angular.toJson(data));
}
}
listRepository.$inject = ['$timeout'];
function listRepository($timeout) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/repository/list-repository.directive.html',
'scope': {
'sectionHeight': '=',
'roleId': '@'
},
'link': link,
'controller': ListRepositoryController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attr, ctrl) {
element.find('#txtSearchInput').on('keydown', function(e) {
if($(this).is(':focus') && e.keyCode === 13) {
ctrl.retrieve();
} else {
$timeout(function() {
if(ctrl.filterInput.length === 0) {
ctrl.retrieve();
}
});
}
});
}
}
})();

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