merge
@ -1,16 +1,16 @@
|
||||
/*
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
|
||||
package api
|
||||
@ -55,17 +55,25 @@ func (ra *RepJobAPI) Prepare() {
|
||||
|
||||
}
|
||||
|
||||
// Get gets all the jobs according to the policy
|
||||
func (ra *RepJobAPI) Get() {
|
||||
policyID, err := ra.GetInt64("policy_id")
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get policy id, error: %v", err)
|
||||
ra.RenderError(http.StatusBadRequest, "Invalid policy id")
|
||||
return
|
||||
// List filters jobs according to the policy and repository
|
||||
func (ra *RepJobAPI) List() {
|
||||
var policyID int64
|
||||
var repository string
|
||||
var err error
|
||||
|
||||
policyIDStr := ra.GetString("policy_id")
|
||||
if len(policyIDStr) != 0 {
|
||||
policyID, err = strconv.ParseInt(policyIDStr, 10, 64)
|
||||
if err != nil || policyID <= 0 {
|
||||
ra.CustomAbort(http.StatusBadRequest, fmt.Sprintf("invalid policy ID: %s", policyIDStr))
|
||||
}
|
||||
}
|
||||
jobs, err := dao.GetRepJobByPolicy(policyID)
|
||||
|
||||
repository = ra.GetString("repository")
|
||||
|
||||
jobs, err := dao.FilterRepJobs(repository, policyID)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to query job from db, error: %v", err)
|
||||
log.Errorf("failed to filter jobs according policy ID %d and repository %s: %v", policyID, repository, err)
|
||||
ra.RenderError(http.StatusInternalServerError, "Failed to query job")
|
||||
return
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ func (pa *RepPolicyAPI) Post() {
|
||||
pa.Redirect(http.StatusCreated, strconv.FormatInt(pid, 10))
|
||||
}
|
||||
|
||||
// Put modifies name and description of policy
|
||||
// Put modifies name, description, target and enablement of policy
|
||||
func (pa *RepPolicyAPI) Put() {
|
||||
id := pa.GetIDFromURL()
|
||||
originalPolicy, err := dao.GetRepPolicy(id)
|
||||
@ -157,7 +157,6 @@ func (pa *RepPolicyAPI) Put() {
|
||||
policy := &models.RepPolicy{}
|
||||
pa.DecodeJSONReq(policy)
|
||||
policy.ProjectID = originalPolicy.ProjectID
|
||||
policy.TargetID = originalPolicy.TargetID
|
||||
pa.Validate(policy)
|
||||
|
||||
if policy.Name != originalPolicy.Name {
|
||||
@ -172,19 +171,77 @@ func (pa *RepPolicyAPI) Put() {
|
||||
}
|
||||
}
|
||||
|
||||
if policy.TargetID != originalPolicy.TargetID {
|
||||
target, err := dao.GetRepTarget(policy.TargetID)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get target %d: %v", policy.TargetID, err)
|
||||
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID))
|
||||
}
|
||||
}
|
||||
|
||||
policy.ID = id
|
||||
|
||||
isTargetChanged := !(policy.TargetID == originalPolicy.TargetID)
|
||||
isEnablementChanged := !(policy.Enabled == policy.Enabled)
|
||||
|
||||
var shouldStop, shouldTrigger bool
|
||||
|
||||
// if target and enablement are not changed, do nothing
|
||||
if !isTargetChanged && !isEnablementChanged {
|
||||
shouldStop = false
|
||||
shouldTrigger = false
|
||||
} else if !isTargetChanged && isEnablementChanged {
|
||||
// target is not changed, but enablement is changed
|
||||
if policy.Enabled == 0 {
|
||||
shouldStop = true
|
||||
shouldTrigger = false
|
||||
} else {
|
||||
shouldStop = false
|
||||
shouldTrigger = true
|
||||
}
|
||||
} else if isTargetChanged && !isEnablementChanged {
|
||||
// target is changed, but enablement is not changed
|
||||
if policy.Enabled == 0 {
|
||||
// enablement is 0, do nothing
|
||||
shouldStop = false
|
||||
shouldTrigger = false
|
||||
} else {
|
||||
// enablement is 1, so stop original target's jobs
|
||||
// and trigger new target's jobs
|
||||
shouldStop = true
|
||||
shouldTrigger = true
|
||||
}
|
||||
} else {
|
||||
// both target and enablement are changed
|
||||
|
||||
// enablement: 1 -> 0
|
||||
if policy.Enabled == 0 {
|
||||
shouldStop = true
|
||||
shouldTrigger = false
|
||||
} else {
|
||||
shouldStop = false
|
||||
shouldTrigger = true
|
||||
}
|
||||
}
|
||||
|
||||
if shouldStop {
|
||||
if err := postReplicationAction(id, "stop"); err != nil {
|
||||
log.Errorf("failed to stop replication of %d: %v", id, err)
|
||||
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
log.Infof("replication of %d has been stopped", id)
|
||||
}
|
||||
|
||||
if err = dao.UpdateRepPolicy(policy); err != nil {
|
||||
log.Errorf("failed to update policy %d: %v", id, err)
|
||||
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if policy.Enabled == originalPolicy.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
//enablement has been modified
|
||||
if policy.Enabled == 1 {
|
||||
if shouldTrigger {
|
||||
go func() {
|
||||
if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil {
|
||||
log.Errorf("failed to trigger replication of %d: %v", id, err)
|
||||
@ -192,14 +249,6 @@ func (pa *RepPolicyAPI) Put() {
|
||||
log.Infof("replication of %d triggered", id)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
if err := postReplicationAction(id, "stop"); err != nil {
|
||||
log.Errorf("failed to stop replication of %d: %v", id, err)
|
||||
} else {
|
||||
log.Infof("try to stop replication of %d", id)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,6 +258,16 @@ func (t *TargetAPI) Delete() {
|
||||
t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
}
|
||||
|
||||
policies, err := dao.GetRepPolicyByTarget(id)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get policies according target %d: %v", id, err)
|
||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if len(policies) > 0 {
|
||||
t.CustomAbort(http.StatusBadRequest, "the target is used by policies, can not be deleted")
|
||||
}
|
||||
|
||||
if err = dao.DeleteRepTarget(id); err != nil {
|
||||
log.Errorf("failed to delete target %d: %v", id, err)
|
||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
|
@ -292,7 +292,7 @@ func (ua *UserAPI) ChangePassword() {
|
||||
}
|
||||
}
|
||||
|
||||
// ToggleUserAdminRole handles PUT api/users/{}/toggleadmin
|
||||
// ToggleUserAdminRole handles PUT api/users/{}/sysadmin
|
||||
func (ua *UserAPI) ToggleUserAdminRole() {
|
||||
if !ua.IsAdmin {
|
||||
log.Warningf("current user, id: %d does not have admin role, can not update other user's role", ua.currentUserID)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
// AccountSettingController handles request to /ng/account_setting
|
||||
// AccountSettingController handles request to /account_setting
|
||||
type AccountSettingController struct {
|
||||
BaseController
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
// AdminOptionController handles requests to /ng/admin_option
|
||||
// AdminOptionController handles requests to /admin_option
|
||||
type AdminOptionController struct {
|
||||
BaseController
|
||||
}
|
@ -1,47 +1,24 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/beego/i18n"
|
||||
"github.com/vmware/harbor/auth"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// CommonController handles request from UI that doesn't expect a page, such as /login /logout ...
|
||||
type CommonController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Render returns nil.
|
||||
func (c *CommonController) Render() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
IsLdapAdminUser bool
|
||||
IsAdmin bool
|
||||
AuthMode string
|
||||
}
|
||||
@ -52,6 +29,8 @@ type langType struct {
|
||||
}
|
||||
|
||||
const (
|
||||
viewPath = "sections"
|
||||
prefixNg = ""
|
||||
defaultLang = "en-US"
|
||||
)
|
||||
|
||||
@ -98,6 +77,7 @@ func (b *BaseController) Prepare() {
|
||||
b.Data["Lang"] = curLang.Lang
|
||||
b.Data["CurLang"] = curLang.Name
|
||||
b.Data["RestLangs"] = restLangs
|
||||
b.Data["SupportLanguages"] = supportLanguages
|
||||
|
||||
authMode := strings.ToLower(os.Getenv("AUTH_MODE"))
|
||||
if authMode == "" {
|
||||
@ -106,50 +86,92 @@ func (b *BaseController) Prepare() {
|
||||
b.AuthMode = authMode
|
||||
b.Data["AuthMode"] = b.AuthMode
|
||||
|
||||
selfRegistration := strings.ToLower(os.Getenv("SELF_REGISTRATION"))
|
||||
|
||||
if selfRegistration == "on" {
|
||||
b.SelfRegistration = true
|
||||
}
|
||||
|
||||
sessionUserID := b.GetSession("userId")
|
||||
if sessionUserID != nil {
|
||||
b.Data["Username"] = b.GetSession("username")
|
||||
b.Data["UserId"] = sessionUserID.(int)
|
||||
|
||||
if (sessionUserID == 1 && b.AuthMode == "ldap_auth") {
|
||||
b.IsLdapAdminUser = true
|
||||
}
|
||||
|
||||
var err error
|
||||
b.IsAdmin, err = dao.IsAdminRole(sessionUserID.(int))
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in IsAdminRole:%v", err)
|
||||
b.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
}
|
||||
|
||||
b.Data["IsAdmin"] = b.IsAdmin
|
||||
b.Data["SelfRegistration"] = b.SelfRegistration
|
||||
b.Data["IsLdapAdminUser"] = b.IsLdapAdminUser
|
||||
|
||||
}
|
||||
|
||||
// ForwardTo setup layout and template for content for a page.
|
||||
func (b *BaseController) ForwardTo(pageTitle string, pageName string) {
|
||||
b.Layout = "segment/base-layout.tpl"
|
||||
b.TplName = "segment/base-layout.tpl"
|
||||
b.Data["PageTitle"] = b.Tr(pageTitle)
|
||||
// Forward to setup layout and template for content for a page.
|
||||
func (b *BaseController) Forward(title, templateName string) {
|
||||
b.Layout = filepath.Join(prefixNg, "layout.htm")
|
||||
b.TplName = filepath.Join(prefixNg, templateName)
|
||||
b.Data["Title"] = title
|
||||
b.LayoutSections = make(map[string]string)
|
||||
b.LayoutSections["HeaderInc"] = "segment/header-include.tpl"
|
||||
b.LayoutSections["HeaderContent"] = "segment/header-content.tpl"
|
||||
b.LayoutSections["BodyContent"] = pageName + ".tpl"
|
||||
b.LayoutSections["ModalDialog"] = "segment/modal-dialog.tpl"
|
||||
b.LayoutSections["FootContent"] = "segment/foot-content.tpl"
|
||||
b.LayoutSections["HeaderInclude"] = filepath.Join(prefixNg, viewPath, "header-include.htm")
|
||||
b.LayoutSections["FooterInclude"] = filepath.Join(prefixNg, viewPath, "footer-include.htm")
|
||||
b.LayoutSections["HeaderContent"] = filepath.Join(prefixNg, viewPath, "header-content.htm")
|
||||
b.LayoutSections["FooterContent"] = filepath.Join(prefixNg, viewPath, "footer-content.htm")
|
||||
|
||||
}
|
||||
|
||||
var langTypes []*langType
|
||||
|
||||
// CommonController handles request from UI that doesn't expect a page, such as /SwitchLanguage /logout ...
|
||||
type CommonController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Render returns nil.
|
||||
func (cc *CommonController) Render() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Login handles login request from UI.
|
||||
func (cc *CommonController) Login() {
|
||||
principal := cc.GetString("principal")
|
||||
password := cc.GetString("password")
|
||||
|
||||
user, err := auth.Login(models.AuthModel{
|
||||
Principal: principal,
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserLogin: %v", err)
|
||||
cc.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
cc.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
|
||||
cc.SetSession("userId", user.UserID)
|
||||
cc.SetSession("username", user.Username)
|
||||
}
|
||||
|
||||
// LogOut Habor UI
|
||||
func (cc *CommonController) LogOut() {
|
||||
cc.DestroySession()
|
||||
}
|
||||
|
||||
// SwitchLanguage User can swith to prefered language
|
||||
func (cc *CommonController) SwitchLanguage() {
|
||||
lang := cc.GetString("lang")
|
||||
if _, exist := supportLanguages[lang]; exist {
|
||||
cc.SetSession("lang", lang)
|
||||
cc.Data["Lang"] = lang
|
||||
}
|
||||
cc.Redirect(cc.Ctx.Request.Header.Get("Referer"), http.StatusFound)
|
||||
}
|
||||
|
||||
// UserExists checks if user exists when user input value in sign in form.
|
||||
func (cc *CommonController) UserExists() {
|
||||
target := cc.GetString("target")
|
||||
value := cc.GetString("value")
|
||||
|
||||
user := models.User{}
|
||||
switch target {
|
||||
case "username":
|
||||
user.Username = value
|
||||
case "email":
|
||||
user.Email = value
|
||||
}
|
||||
|
||||
exist, err := dao.UserExists(user, target)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserExists: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
cc.Data["json"] = exist
|
||||
cc.ServeJSON()
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
//conf/app.conf -> os.Getenv("config_path")
|
||||
@ -179,9 +201,4 @@ func init() {
|
||||
supportLanguages[v] = t
|
||||
}
|
||||
|
||||
for _, lang := range langs {
|
||||
if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil {
|
||||
log.Errorf("Fail to set message file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
// DashboardController handles requests to /ng/dashboard
|
||||
// DashboardController handles requests to /dashboard
|
||||
type DashboardController struct {
|
||||
BaseController
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
// IndexController handles request to /ng
|
||||
// IndexController handles request to /
|
||||
type IndexController struct {
|
||||
BaseController
|
||||
}
|
@ -1,102 +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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// ItemDetailController handles requet to /registry/detail, which shows the detail of a project.
|
||||
type ItemDetailController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get will check if user has permission to view a certain project, if not user will be redirected to signin or his homepage.
|
||||
// If the check is passed it renders the project detail page.
|
||||
func (idc *ItemDetailController) Get() {
|
||||
|
||||
projectID, _ := idc.GetInt64("project_id")
|
||||
|
||||
if projectID <= 0 {
|
||||
log.Errorf("Invalid project id: %d", projectID)
|
||||
idc.Redirect("/signIn", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
project, err := dao.GetProjectByID(projectID)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetProjectById: %v", err)
|
||||
idc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
idc.Redirect("/signIn", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
sessionUserID := idc.GetSession("userId")
|
||||
|
||||
if project.Public != 1 && sessionUserID == nil {
|
||||
idc.Redirect("/signIn?uri="+url.QueryEscape(idc.Ctx.Input.URI()), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if sessionUserID != nil {
|
||||
|
||||
userID := sessionUserID.(int)
|
||||
|
||||
idc.Data["Username"] = idc.GetSession("username")
|
||||
idc.Data["UserId"] = userID
|
||||
|
||||
roleList, err := dao.GetUserProjectRoles(userID, projectID)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUserProjectRoles: %v", err)
|
||||
idc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
isAdmin, err := dao.IsAdminRole(userID)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in IsAdminRole: %v", err)
|
||||
idc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if !isAdmin && (project.Public == 0 && len(roleList) == 0) {
|
||||
idc.Redirect("/registry/project", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if len(roleList) > 0 {
|
||||
idc.Data["RoleId"] = roleList[0].RoleID
|
||||
}
|
||||
}
|
||||
|
||||
idc.Data["ProjectId"] = project.ProjectID
|
||||
idc.Data["ProjectName"] = project.Name
|
||||
idc.Data["OwnerName"] = project.OwnerName
|
||||
idc.Data["OwnerId"] = project.OwnerID
|
||||
|
||||
idc.Data["HarborRegUrl"] = os.Getenv("HARBOR_REG_URL")
|
||||
idc.Data["RepoName"] = idc.GetString("repo_name")
|
||||
|
||||
idc.ForwardTo("page_title_item_details", "item-detail")
|
||||
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/auth"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// IndexController handles request to /
|
||||
type IndexController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the index page.
|
||||
func (c *IndexController) Get() {
|
||||
c.Data["Username"] = c.GetSession("username")
|
||||
c.ForwardTo("page_title_index", "index")
|
||||
}
|
||||
|
||||
// SignInController handles request to /signIn
|
||||
type SignInController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders Sign In page.
|
||||
func (sic *SignInController) Get() {
|
||||
sic.ForwardTo("page_title_sign_in", "sign-in")
|
||||
}
|
||||
|
||||
// Login handles login request from UI.
|
||||
func (c *CommonController) Login() {
|
||||
principal := c.GetString("principal")
|
||||
password := c.GetString("password")
|
||||
|
||||
user, err := auth.Login(models.AuthModel{
|
||||
Principal: principal,
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserLogin: %v", err)
|
||||
c.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
|
||||
c.SetSession("userId", user.UserID)
|
||||
c.SetSession("username", user.Username)
|
||||
}
|
||||
|
||||
// SwitchLanguage handles UI request to switch between different languages and re-render template based on language.
|
||||
func (c *CommonController) SwitchLanguage() {
|
||||
lang := c.GetString("lang")
|
||||
if lang == "en-US" || lang == "zh-CN" || lang == "de-DE" || lang == "ru-RU" || lang == "ja-JP" {
|
||||
c.SetSession("lang", lang)
|
||||
c.Data["Lang"] = lang
|
||||
}
|
||||
c.Redirect(c.Ctx.Request.Header.Get("Referer"), http.StatusFound)
|
||||
}
|
||||
|
||||
// Logout handles UI request to logout.
|
||||
func (c *CommonController) Logout() {
|
||||
c.DestroySession()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@ -8,10 +8,12 @@ import (
|
||||
"github.com/vmware/harbor/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
|
||||
@ -29,6 +31,6 @@ func (ndc *NavigationDetailController) Get() {
|
||||
isAdmin = u.HasAdminRole
|
||||
}
|
||||
ndc.Data["IsAdmin"] = isAdmin
|
||||
ndc.TplName = "ng/navigation-detail.htm"
|
||||
ndc.TplName = "navigation-detail.htm"
|
||||
ndc.Render()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// NavigationHeaderController handles requests to /ng/navigation_header
|
||||
// NavigationHeaderController handles requests to /navigation_header
|
||||
type NavigationHeaderController struct {
|
||||
BaseController
|
||||
}
|
||||
@ -34,6 +34,6 @@ func (nhc *NavigationHeaderController) Get() {
|
||||
}
|
||||
nhc.Data["HasLoggedIn"] = hasLoggedIn
|
||||
nhc.Data["IsAdmin"] = isAdmin
|
||||
nhc.TplName = "ng/navigation-header.htm"
|
||||
nhc.TplName = "navigation-header.htm"
|
||||
nhc.Render()
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/beego/i18n"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers.
|
||||
type BaseController struct {
|
||||
beego.Controller
|
||||
i18n.Locale
|
||||
SelfRegistration bool
|
||||
IsAdmin bool
|
||||
AuthMode string
|
||||
}
|
||||
|
||||
type langType struct {
|
||||
Lang string
|
||||
Name string
|
||||
}
|
||||
|
||||
const (
|
||||
viewPath = "sections"
|
||||
prefixNg = "ng"
|
||||
defaultLang = "en-US"
|
||||
)
|
||||
|
||||
var supportLanguages map[string]langType
|
||||
|
||||
// Prepare extracts the language information from request and populate data for rendering templates.
|
||||
func (b *BaseController) Prepare() {
|
||||
|
||||
var lang string
|
||||
al := b.Ctx.Request.Header.Get("Accept-Language")
|
||||
|
||||
if len(al) > 4 {
|
||||
al = al[:5] // Only compare first 5 letters.
|
||||
if i18n.IsExist(al) {
|
||||
lang = al
|
||||
}
|
||||
}
|
||||
|
||||
if _, exist := supportLanguages[lang]; exist == false { //Check if support the request language.
|
||||
lang = defaultLang //Set default language if not supported.
|
||||
}
|
||||
|
||||
sessionLang := b.GetSession("lang")
|
||||
if sessionLang != nil {
|
||||
b.SetSession("Lang", lang)
|
||||
lang = sessionLang.(string)
|
||||
}
|
||||
|
||||
curLang := langType{
|
||||
Lang: lang,
|
||||
}
|
||||
|
||||
restLangs := make([]*langType, 0, len(langTypes)-1)
|
||||
for _, v := range langTypes {
|
||||
if lang != v.Lang {
|
||||
restLangs = append(restLangs, v)
|
||||
} else {
|
||||
curLang.Name = v.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Set language properties.
|
||||
b.Lang = lang
|
||||
b.Data["Lang"] = curLang.Lang
|
||||
b.Data["CurLang"] = curLang.Name
|
||||
b.Data["RestLangs"] = restLangs
|
||||
b.Data["SupportLanguages"] = supportLanguages
|
||||
|
||||
authMode := strings.ToLower(os.Getenv("AUTH_MODE"))
|
||||
if authMode == "" {
|
||||
authMode = "db_auth"
|
||||
}
|
||||
b.AuthMode = authMode
|
||||
b.Data["AuthMode"] = b.AuthMode
|
||||
|
||||
}
|
||||
|
||||
// Forward to setup layout and template for content for a page.
|
||||
func (b *BaseController) Forward(title, templateName string) {
|
||||
b.Layout = filepath.Join(prefixNg, "layout.htm")
|
||||
b.TplName = filepath.Join(prefixNg, templateName)
|
||||
b.Data["Title"] = title
|
||||
b.LayoutSections = make(map[string]string)
|
||||
b.LayoutSections["HeaderInclude"] = filepath.Join(prefixNg, viewPath, "header-include.htm")
|
||||
b.LayoutSections["FooterInclude"] = filepath.Join(prefixNg, viewPath, "footer-include.htm")
|
||||
b.LayoutSections["HeaderContent"] = filepath.Join(prefixNg, viewPath, "header-content.htm")
|
||||
b.LayoutSections["FooterContent"] = filepath.Join(prefixNg, viewPath, "footer-content.htm")
|
||||
|
||||
}
|
||||
|
||||
var langTypes []*langType
|
||||
|
||||
// CommonController handles request from UI that doesn't expect a page, such as /SwitchLanguage /logout ...
|
||||
type CommonController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Render returns nil.
|
||||
func (cc *CommonController) Render() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogOut Habor UI
|
||||
func (cc *CommonController) LogOut() {
|
||||
cc.DestroySession()
|
||||
}
|
||||
|
||||
// SwitchLanguage User can swith to prefered language
|
||||
func (cc *CommonController) SwitchLanguage() {
|
||||
lang := cc.GetString("lang")
|
||||
if _, exist := supportLanguages[lang]; exist {
|
||||
cc.SetSession("lang", lang)
|
||||
cc.Data["Lang"] = lang
|
||||
}
|
||||
cc.Redirect(cc.Ctx.Request.Header.Get("Referer"), http.StatusFound)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
//conf/app.conf -> os.Getenv("config_path")
|
||||
configPath := os.Getenv("CONFIG_PATH")
|
||||
if len(configPath) != 0 {
|
||||
log.Infof("Config path: %s", configPath)
|
||||
beego.AppConfigPath = configPath
|
||||
if err := beego.ParseConfig(); err != nil {
|
||||
log.Warningf("Failed to parse config file: %s, error: %v", configPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
beego.AddFuncMap("i18n", i18n.Tr)
|
||||
|
||||
langs := strings.Split(beego.AppConfig.String("lang::types"), "|")
|
||||
names := strings.Split(beego.AppConfig.String("lang::names"), "|")
|
||||
|
||||
supportLanguages = make(map[string]langType)
|
||||
|
||||
langTypes = make([]*langType, 0, len(langs))
|
||||
for i, v := range langs {
|
||||
t := langType{
|
||||
Lang: v,
|
||||
Name: names[i],
|
||||
}
|
||||
langTypes = append(langTypes, &t)
|
||||
supportLanguages[v] = t
|
||||
}
|
||||
|
||||
for _, lang := range langs {
|
||||
if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil {
|
||||
log.Errorf("Fail to set message file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
package ng
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
type messageDetail struct {
|
||||
Hint string
|
||||
URL string
|
||||
UUID string
|
||||
}
|
||||
|
||||
// SendEmail verifies the Email address and contact SMTP server to send reset password Email.
|
||||
func (cc *CommonController) SendEmail() {
|
||||
|
||||
email := cc.GetString("email")
|
||||
|
||||
pass, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, email)
|
||||
|
||||
if !pass {
|
||||
cc.CustomAbort(http.StatusBadRequest, "email_content_illegal")
|
||||
} else {
|
||||
|
||||
queryUser := models.User{Email: email}
|
||||
exist, err := dao.UserExists(queryUser, "email")
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserExists: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
cc.CustomAbort(http.StatusNotFound, "email_does_not_exist")
|
||||
}
|
||||
|
||||
messageTemplate, err := template.ParseFiles("views/ng/reset-password-mail.tpl")
|
||||
if err != nil {
|
||||
log.Errorf("Parse email template file failed: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
message := new(bytes.Buffer)
|
||||
|
||||
harborURL := os.Getenv("HARBOR_URL")
|
||||
if harborURL == "" {
|
||||
harborURL = "localhost"
|
||||
}
|
||||
uuid, err := dao.GenerateRandomString()
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GenerateRandomString: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
err = messageTemplate.Execute(message, messageDetail{
|
||||
Hint: cc.Tr("reset_email_hint"),
|
||||
URL: harborURL,
|
||||
UUID: uuid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Message template error: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
|
||||
}
|
||||
|
||||
config, err := beego.AppConfig.GetSection("mail")
|
||||
if err != nil {
|
||||
log.Errorf("Can not load app.conf: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
|
||||
}
|
||||
|
||||
mail := utils.Mail{
|
||||
From: config["from"],
|
||||
To: []string{email},
|
||||
Subject: cc.Tr("reset_email_subject"),
|
||||
Message: message.String()}
|
||||
|
||||
err = mail.SendMail()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Send email failed: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "send_email_failed")
|
||||
}
|
||||
|
||||
user := models.User{ResetUUID: uuid, Email: email}
|
||||
dao.UpdateUserResetUUID(user)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ForgotPasswordController handles requests to /ng/forgot_password
|
||||
type ForgotPasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders forgot password page
|
||||
func (fpc *ForgotPasswordController) Get() {
|
||||
fpc.Forward("Forgot Password", "forgot-password.htm")
|
||||
}
|
||||
|
||||
// ResetPasswordController handles request to /resetPassword
|
||||
type ResetPasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get checks if reset_uuid in the reset link is valid and render the result page for user to reset password.
|
||||
func (rpc *ResetPasswordController) Get() {
|
||||
|
||||
resetUUID := rpc.GetString("reset_uuid")
|
||||
if resetUUID == "" {
|
||||
log.Error("Reset uuid is blank.")
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
queryUser := models.User{ResetUUID: resetUUID}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser: %v", err)
|
||||
rpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
rpc.Data["ResetUuid"] = user.ResetUUID
|
||||
rpc.Forward("Reset Password", "reset-password.htm")
|
||||
} else {
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// ResetPassword handles request from the reset page and reset password
|
||||
func (cc *CommonController) ResetPassword() {
|
||||
|
||||
resetUUID := cc.GetString("reset_uuid")
|
||||
if resetUUID == "" {
|
||||
cc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.")
|
||||
}
|
||||
|
||||
queryUser := models.User{ResetUUID: resetUUID}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if user == nil {
|
||||
log.Error("User does not exist")
|
||||
cc.CustomAbort(http.StatusBadRequest, "User does not exist")
|
||||
}
|
||||
|
||||
password := cc.GetString("password")
|
||||
|
||||
if password != "" {
|
||||
user.Password = password
|
||||
err = dao.ResetUserPassword(*user)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in ResetUserPassword: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
} else {
|
||||
cc.CustomAbort(http.StatusBadRequest, "password_is_required")
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package ng
|
||||
|
||||
// ProjectController handles requests to /ng/projec
|
||||
type ProjectController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders project page
|
||||
func (pc *ProjectController) Get() {
|
||||
pc.Forward("My Projects", "project.htm")
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package ng
|
||||
|
||||
// SearchController handles request to ng/search
|
||||
type SearchController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get rendlers search bar
|
||||
func (sc *SearchController) Get() {
|
||||
sc.Forward("Search", "search.htm")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// OptionalMenuController handles request to /ng/optional_menu
|
||||
// OptionalMenuController handles request to /optional_menu
|
||||
type OptionalMenuController struct {
|
||||
BaseController
|
||||
}
|
||||
@ -33,7 +33,7 @@ func (omc *OptionalMenuController) Get() {
|
||||
omc.Data["Username"] = u.Username
|
||||
}
|
||||
omc.Data["HasLoggedIn"] = hasLoggedIn
|
||||
omc.TplName = "ng/optional-menu.htm"
|
||||
omc.TplName = "optional-menu.htm"
|
||||
omc.Render()
|
||||
|
||||
}
|
@ -1,18 +1,3 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
@ -22,40 +7,13 @@ import (
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// ChangePasswordController handles request to /changePassword
|
||||
type ChangePasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the page for user to change password.
|
||||
func (cpc *ChangePasswordController) Get() {
|
||||
sessionUserID := cpc.GetSession("userId")
|
||||
if sessionUserID == nil {
|
||||
cpc.Redirect("/signIn", http.StatusFound)
|
||||
return
|
||||
}
|
||||
cpc.Data["Username"] = cpc.GetSession("username")
|
||||
cpc.ForwardTo("page_title_change_password", "change-password")
|
||||
}
|
||||
|
||||
// ForgotPasswordController handles request to /forgotPassword
|
||||
type ForgotPasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get Renders the page for user to input Email to reset password.
|
||||
func (fpc *ForgotPasswordController) Get() {
|
||||
fpc.ForwardTo("page_title_forgot_password", "forgot-password")
|
||||
}
|
||||
|
||||
type messageDetail struct {
|
||||
Hint string
|
||||
URL string
|
||||
@ -137,6 +95,16 @@ func (cc *CommonController) SendEmail() {
|
||||
|
||||
}
|
||||
|
||||
// ForgotPasswordController handles requests to /forgot_password
|
||||
type ForgotPasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders forgot password page
|
||||
func (fpc *ForgotPasswordController) Get() {
|
||||
fpc.Forward("Forgot Password", "forgot-password.htm")
|
||||
}
|
||||
|
||||
// ResetPasswordController handles request to /resetPassword
|
||||
type ResetPasswordController struct {
|
||||
BaseController
|
||||
@ -161,7 +129,7 @@ func (rpc *ResetPasswordController) Get() {
|
||||
|
||||
if user != nil {
|
||||
rpc.Data["ResetUuid"] = user.ResetUUID
|
||||
rpc.ForwardTo("page_title_reset_password", "reset-password")
|
||||
rpc.Forward("Reset Password", "reset-password.htm")
|
||||
} else {
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
}
|
||||
|
@ -1,27 +1,11 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
// ProjectController handles request to /registry/project
|
||||
// ProjectController handles requests to /project
|
||||
type ProjectController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders project page.
|
||||
func (p *ProjectController) Get() {
|
||||
p.Data["Username"] = p.GetSession("username")
|
||||
p.ForwardTo("page_title_project", "project")
|
||||
// Get renders project page
|
||||
func (pc *ProjectController) Get() {
|
||||
pc.Forward("My Projects", "project.htm")
|
||||
}
|
||||
|
@ -1,87 +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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// RegisterController handles request to /register
|
||||
type RegisterController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the Sign In page, it only works if the auth mode is set to db_auth
|
||||
func (rc *RegisterController) Get() {
|
||||
|
||||
if !rc.SelfRegistration {
|
||||
log.Warning("Registration is disabled when self-registration is off.")
|
||||
rc.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
|
||||
if rc.AuthMode == "db_auth" {
|
||||
rc.ForwardTo("page_title_registration", "register")
|
||||
} else {
|
||||
rc.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// AddUserController handles request for adding user with an admin role user
|
||||
type AddUserController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the Sign In page, it only works if the auth mode is set to db_auth
|
||||
func (ac *AddUserController) Get() {
|
||||
|
||||
if !ac.IsAdmin {
|
||||
log.Warning("Add user can only be used by admin role user.")
|
||||
ac.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
|
||||
if ac.AuthMode == "db_auth" {
|
||||
ac.ForwardTo("page_title_add_user", "register")
|
||||
} else {
|
||||
ac.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// UserExists checks if user exists when user input value in sign in form.
|
||||
func (cc *CommonController) UserExists() {
|
||||
target := cc.GetString("target")
|
||||
value := cc.GetString("value")
|
||||
|
||||
user := models.User{}
|
||||
switch target {
|
||||
case "username":
|
||||
user.Username = value
|
||||
case "email":
|
||||
user.Email = value
|
||||
}
|
||||
|
||||
exist, err := dao.UserExists(user, target)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserExists: %v", err)
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
cc.Data["json"] = exist
|
||||
cc.ServeJSON()
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
import "os"
|
||||
|
||||
// RepositoryController handles request to /ng/repository
|
||||
// RepositoryController handles request to /repository
|
||||
type RepositoryController struct {
|
||||
BaseController
|
||||
}
|
@ -1,18 +1,3 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
// SearchController handles request to /search
|
||||
@ -20,9 +5,7 @@ type SearchController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders page for displaying search result.
|
||||
// Get rendlers search bar
|
||||
func (sc *SearchController) Get() {
|
||||
sc.Data["Username"] = sc.GetSession("username")
|
||||
sc.Data["QueryParam"] = sc.GetString("q")
|
||||
sc.ForwardTo("page_title_search", "search")
|
||||
sc.Forward("Search", "search.htm")
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// SignInController handles requests to /ng/sign_in
|
||||
// SignInController handles requests to /sign_in
|
||||
type SignInController struct {
|
||||
BaseController
|
||||
}
|
||||
@ -34,6 +34,6 @@ func (sic *SignInController) Get() {
|
||||
}
|
||||
sic.Data["Username"] = username
|
||||
sic.Data["HasLoggedIn"] = hasLoggedIn
|
||||
sic.TplName = "ng/sign-in.htm"
|
||||
sic.TplName = "sign-in.htm"
|
||||
sic.Render()
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package ng
|
||||
package controllers
|
||||
|
||||
// SignUpController handles requests to /ng/sign_up
|
||||
// SignUpController handles requests to /sign_up
|
||||
type SignUpController struct {
|
||||
BaseController
|
||||
}
|
@ -911,6 +911,21 @@ func TestAddRepPolicy(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestGetRepPolicyByTarget(t *testing.T) {
|
||||
policies, err := GetRepPolicyByTarget(targetID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get policy according target %d: %v", targetID, err)
|
||||
}
|
||||
|
||||
if len(policies) == 0 {
|
||||
t.Fatal("unexpected length of policies 0, expected is >0")
|
||||
}
|
||||
|
||||
if policies[0].ID != policyID {
|
||||
t.Fatalf("unexpected policy: %d, expected: %d", policies[0].ID, policyID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRepPolicyByName(t *testing.T) {
|
||||
policy, err := GetRepPolicy(policyID)
|
||||
if err != nil {
|
||||
@ -1102,9 +1117,27 @@ func TestDeleteRepJob(t *testing.T) {
|
||||
j, err := GetRepJob(jobID)
|
||||
if err != nil {
|
||||
t.Errorf("Error occured in GetRepJob:%v", err)
|
||||
return
|
||||
}
|
||||
if j != nil {
|
||||
t.Errorf("Able to find rep job after deletion, id: %d", jobID)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepJobs(t *testing.T) {
|
||||
jobs, err := FilterRepJobs("", policyID)
|
||||
if err != nil {
|
||||
log.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID)
|
||||
return
|
||||
}
|
||||
if len(jobs) != 1 {
|
||||
log.Errorf("Unexpected length of jobs, expected: 1, in fact: %d", len(jobs))
|
||||
return
|
||||
}
|
||||
if jobs[0].ID != jobID {
|
||||
log.Errorf("Unexpected job ID in the result, expected: %d, in fact: %d", jobID, jobs[0].ID)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,10 +192,24 @@ func GetRepPolicyByProject(projectID int64) ([]*models.RepPolicy, error) {
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// GetRepPolicyByTarget ...
|
||||
func GetRepPolicyByTarget(targetID int64) ([]*models.RepPolicy, error) {
|
||||
o := GetOrmer()
|
||||
sql := `select * from replication_policy where target_id = ?`
|
||||
|
||||
var policies []*models.RepPolicy
|
||||
|
||||
if _, err := o.Raw(sql, targetID).QueryRows(&policies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// UpdateRepPolicy ...
|
||||
func UpdateRepPolicy(policy *models.RepPolicy) error {
|
||||
o := GetOrmer()
|
||||
_, err := o.Update(policy, "Name", "Enabled", "Description", "CronStr")
|
||||
_, err := o.Update(policy, "TargetID", "Name", "Enabled", "Description", "CronStr")
|
||||
return err
|
||||
}
|
||||
|
||||
@ -259,6 +273,38 @@ func GetRepJobByPolicy(policyID int64) ([]*models.RepJob, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FilterRepJobs filters jobs by repo and policy ID
|
||||
func FilterRepJobs(repo string, policyID int64) ([]*models.RepJob, error) {
|
||||
o := GetOrmer()
|
||||
|
||||
var args []interface{}
|
||||
|
||||
sql := `select * from replication_job `
|
||||
|
||||
if len(repo) != 0 && policyID != 0 {
|
||||
sql += `where repository like ? and policy_id = ? `
|
||||
args = append(args, "%"+repo+"%")
|
||||
args = append(args, policyID)
|
||||
} else if len(repo) != 0 {
|
||||
sql += `where repository like ? `
|
||||
args = append(args, "%"+repo+"%")
|
||||
} else if policyID != 0 {
|
||||
sql += `where policy_id = ? `
|
||||
args = append(args, policyID)
|
||||
}
|
||||
|
||||
sql += `order by creation_time`
|
||||
|
||||
var jobs []*models.RepJob
|
||||
if _, err := o.Raw(sql, args).QueryRows(&jobs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genTagListForJob(jobs...)
|
||||
|
||||
return jobs, nil
|
||||
}
|
||||
|
||||
// GetRepJobToStop get jobs that are possibly being handled by workers of a certain policy.
|
||||
func GetRepJobToStop(policyID int64) ([]*models.RepJob, error) {
|
||||
var res []*models.RepJob
|
||||
|
@ -18,6 +18,7 @@ package replication
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -43,6 +44,11 @@ const (
|
||||
StatePushManifest = "push_manifest"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrConflict represents http 409 error
|
||||
ErrConflict = errors.New("conflict")
|
||||
)
|
||||
|
||||
// BaseHandler holds informations shared by other state handlers
|
||||
type BaseHandler struct {
|
||||
project string // project_name
|
||||
@ -136,15 +142,24 @@ type Checker struct {
|
||||
// Enter check existence of project, if it does not exist, create it,
|
||||
// if it exists, check whether the user has write privilege to it.
|
||||
func (c *Checker) Enter() (string, error) {
|
||||
enter:
|
||||
exist, canWrite, err := c.projectExist()
|
||||
if err != nil {
|
||||
c.logger.Errorf("an error occurred while checking existence of project %s on %s with user %s : %v", c.project, c.dstURL, c.dstUsr, err)
|
||||
return "", err
|
||||
}
|
||||
if !exist {
|
||||
if err := c.createProject(); err != nil {
|
||||
c.logger.Errorf("an error occurred while creating project %s on %s with user %s : %v", c.project, c.dstURL, c.dstUsr, err)
|
||||
return "", err
|
||||
err := c.createProject()
|
||||
if err != nil {
|
||||
// other job may be also doing the same thing when the current job
|
||||
// is creating project, so when the response code is 409, re-check
|
||||
// the existence of project
|
||||
if err == ErrConflict {
|
||||
goto enter
|
||||
} else {
|
||||
c.logger.Errorf("an error occurred while creating project %s on %s with user %s : %v", c.project, c.dstURL, c.dstUsr, err)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
c.logger.Infof("project %s is created on %s with user %s", c.project, c.dstURL, c.dstUsr)
|
||||
return StatePullManifest, nil
|
||||
@ -246,6 +261,10 @@ func (c *Checker) createProject() error {
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
if resp.StatusCode == http.StatusConflict {
|
||||
return ErrConflict
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
message, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
@ -1,88 +0,0 @@
|
||||
page_title_index = Harbor
|
||||
page_title_sign_in = Anmelden - Harbor
|
||||
page_title_project = Projekt - Harbor
|
||||
page_title_item_details = Details - Harbor
|
||||
page_title_registration = Registrieren - Harbor
|
||||
page_title_add_user = Benutzer anlegen - Harbor
|
||||
page_title_forgot_password = Passwort vergessen - Harbor
|
||||
title_forgot_password = Passwort vergessen
|
||||
page_title_reset_password = Passwort zurücksetzen - Harbor
|
||||
title_reset_password = Passwort zurücksetzen
|
||||
page_title_change_password = Passwort ändern - Harbor
|
||||
title_change_password = Passwort ändern
|
||||
page_title_search = Suche - Harbor
|
||||
sign_in = Anmelden
|
||||
sign_up = Registrieren
|
||||
add_user = Benutzer anlegen
|
||||
log_out = Abmelden
|
||||
search_placeholder = Projekte oder Repositories
|
||||
change_password = Passwort ändern
|
||||
username_email = Benutzername/E-Mail
|
||||
password = Passwort
|
||||
forgot_password = Passwort vergessen
|
||||
welcome = Willkommen
|
||||
my_projects = Meine Projekte
|
||||
public_projects = Öffentliche Projekte
|
||||
admin_options = Admin Optionen
|
||||
project_name = Projektname
|
||||
creation_time = Erstellungsdatum
|
||||
publicity = Öffentlich
|
||||
add_project = Projekt hinzufügen
|
||||
check_for_publicity = öffentliches Projekt
|
||||
button_save = Speichern
|
||||
button_cancel = Abbrechen
|
||||
button_submit = Absenden
|
||||
username = Benutzername
|
||||
email = E-Mail
|
||||
system_admin = System Admininistrator
|
||||
dlg_button_ok = OK
|
||||
dlg_button_cancel = Abbrechen
|
||||
registration = Registrieren
|
||||
username_description = Dies wird Ihr Benutzername sein.
|
||||
email_description = Die E-Mail Adresse wird für das Zurücksetzen des Passworts genutzt.
|
||||
full_name = Sollständiger Name
|
||||
full_name_description = Vor- und Nachname.
|
||||
password_description = Mindestens sieben Zeichen bestehend aus einem Kleinbuchstaben, einem Großbuchstaben und einer Zahl
|
||||
confirm_password = Passwort bestätigen
|
||||
note_to_the_admin = Kommentar
|
||||
old_password = Altes Passwort
|
||||
new_password = Neues Passwort
|
||||
forgot_password_description = Bitte gebe die E-Mail Adresse ein, die du zur Registrierung verwendet hast. Ein Link zur Wiederherstellung wird dir per E-Mail an diese Adresse geschickt.
|
||||
|
||||
projects = Projekte
|
||||
repositories = Repositories
|
||||
search = Suche
|
||||
home = Home
|
||||
project = Projekt
|
||||
owner = Besitzer
|
||||
repo = Repositories
|
||||
user = Benutzer
|
||||
logs = Logs
|
||||
repo_name = Repository
|
||||
add_members = Benutzer hinzufügen
|
||||
operation = Aktion
|
||||
advance = erweiterte Suche
|
||||
all = Alle
|
||||
others = Andere
|
||||
start_date = Start Datum
|
||||
end_date = End Datum
|
||||
timestamp = Zeitstempel
|
||||
role = Rolle
|
||||
reset_email_hint = Bitte klicke auf diesen Link um dein Passwort zurückzusetzen
|
||||
reset_email_subject = Passwort zurücksetzen
|
||||
language = Deutsch
|
||||
language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
language_de-DE = Deutsch
|
||||
language_ru-RU = Русский
|
||||
language_ja-JP = 日本語
|
||||
copyright = Copyright
|
||||
all_rights_reserved = Alle Rechte vorbehalten.
|
||||
index_desc = Project Harbor ist ein zuverlässiger Enterprise-Class Registry Server. Unternehmen können ihren eigenen Registry Server aufsetzen um die Produktivität und Sicherheit zu erhöhen. Project Harbor kann für Entwicklungs- wie auch Produktiv-Umgebungen genutzt werden.
|
||||
index_desc_0 = Vorteile:
|
||||
index_desc_1 = 1. Sicherheit: Halten Sie ihr geistiges Eigentum innerhalb der Organisation.
|
||||
index_desc_2 = 2. Effizienz: Ein privater Registry Server innerhalb des Netzwerks ihrer Organisation kann den Traffic zu öffentlichen Services im Internet signifikant reduzieren.
|
||||
index_desc_3 = 3. Zugriffskontrolle: RBAC (Role Based Access Control) wird zur Verfügung gestellt. Benutzerverwaltung kann mit bestehenden Identitätsservices wie AD/LDAP integriert werden.
|
||||
index_desc_4 = 4. Audit: Jeglicher Zugriff auf die Registry wird protokolliert und kann für ein Audit verwendet werden.
|
||||
index_desc_5 = 5. GUI: Benutzerfreundliche Verwaltung über eine einzige Management-Konsole
|
||||
index_title = Ein Enterprise-Class Registry Server
|
@ -1,89 +0,0 @@
|
||||
page_title_index = Harbor
|
||||
page_title_sign_in = Sign In - Harbor
|
||||
page_title_project = Project - Harbor
|
||||
page_title_item_details = Details - Harbor
|
||||
page_title_registration = Sign Up - Harbor
|
||||
page_title_add_user = Add User - Harbor
|
||||
page_title_forgot_password = Forgot Password - Harbor
|
||||
title_forgot_password = Forgot Password
|
||||
page_title_reset_password = Reset Password - Harbor
|
||||
title_reset_password = Reset Password
|
||||
page_title_change_password = Change Password - Harbor
|
||||
title_change_password = Change Password
|
||||
page_title_search = Search - Harbor
|
||||
sign_in = Sign In
|
||||
sign_up = Sign Up
|
||||
add_user = Add User
|
||||
log_out = Log Out
|
||||
search_placeholder = projects or repositories
|
||||
change_password = Change Password
|
||||
username_email = Username/Email
|
||||
password = Password
|
||||
forgot_password = Forgot Password
|
||||
welcome = Welcome
|
||||
my_projects = My Projects
|
||||
public_projects = Public Projects
|
||||
admin_options = Admin Options
|
||||
project_name = Project Name
|
||||
creation_time = Creation Time
|
||||
publicity = Publicity
|
||||
add_project = Add Project
|
||||
check_for_publicity = Public project
|
||||
button_save = Save
|
||||
button_cancel = Cancel
|
||||
button_submit = Submit
|
||||
username = Username
|
||||
email = Email
|
||||
system_admin = System Admin
|
||||
dlg_button_ok = OK
|
||||
dlg_button_cancel = Cancel
|
||||
registration = Sign Up
|
||||
username_description = This will be your username.
|
||||
email_description = The Email address will be used for resetting password.
|
||||
full_name = Full Name
|
||||
full_name_description = First name & Last name.
|
||||
password_description = At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.
|
||||
confirm_password = Confirm Password
|
||||
note_to_the_admin = Comments
|
||||
old_password = Old Password
|
||||
new_password = New Password
|
||||
forgot_password_description = Please input the Email used when you signed up, a reset password Email will be sent to you.
|
||||
|
||||
projects = Projects
|
||||
repositories = Repositories
|
||||
search = Search
|
||||
home = Home
|
||||
project = Project
|
||||
owner = Owner
|
||||
repo = Repositories
|
||||
user = Users
|
||||
logs = Logs
|
||||
repo_name = Repository Name
|
||||
repo_tag = Tag
|
||||
add_members = Add Members
|
||||
operation = Operation
|
||||
advance = Advanced Search
|
||||
all = All
|
||||
others = Others
|
||||
start_date = Start Date
|
||||
end_date = End Date
|
||||
timestamp = Timestamp
|
||||
role = Role
|
||||
reset_email_hint = Please click this link to reset your password
|
||||
reset_email_subject = Reset your password
|
||||
language = English
|
||||
language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
language_de-DE = Deutsch
|
||||
language_ru-RU = Русский
|
||||
language_ja-JP = 日本語
|
||||
copyright = Copyright
|
||||
all_rights_reserved = All rights reserved.
|
||||
index_desc = Project Harbor is to build an enterprise-class, reliable registry server. Enterprises can set up a private registry server in their own environment to improve productivity as well as security. Project Harbor can be used in both development and production environment.
|
||||
index_desc_0 = Key benefits:
|
||||
index_desc_1 = 1. Security: Keep their intellectual properties within their organizations.
|
||||
index_desc_2 = 2. Efficiency: A private registry server is set up within the organization's network and can reduce significantly the internet traffic to the public service.
|
||||
index_desc_3 = 3. Access Control: RBAC (Role Based Access Control) is provided. User management can be integrated with existing enterprise identity services like AD/LDAP.
|
||||
index_desc_4 = 4. Audit: All access to the registry are logged and can be used for audit purpose.
|
||||
index_desc_5 = 5. GUI: User friendly single-pane-of-glass management console.
|
||||
index_title = An enterprise-class registry server
|
@ -1,89 +0,0 @@
|
||||
page_title_index = Harbor
|
||||
page_title_sign_in = ログイン - Harbor
|
||||
page_title_project = プロジェクト - Harbor
|
||||
page_title_item_details = 詳しい - Harbor
|
||||
page_title_registration = 登録 - Harbor
|
||||
page_title_add_user = ユーザを追加 - Harbor
|
||||
page_title_forgot_password = パスワードを忘れました - Harbor
|
||||
title_forgot_password = パスワードを忘れました
|
||||
page_title_reset_password = パスワードをリセット - Harbor
|
||||
title_reset_password = パスワードをリセット
|
||||
page_title_change_password = パスワードを変更 - Harbor
|
||||
title_change_password = パスワードを変更
|
||||
page_title_search = サーチ - Harbor
|
||||
sign_in = ログイン
|
||||
sign_up = 登録
|
||||
add_user = ユーザを追加
|
||||
log_out = ログアウト
|
||||
search_placeholder = プロジェクト名またはイメージ名
|
||||
change_password = パスワードを変更
|
||||
username_email = ユーザ名/メールアドレス
|
||||
password = パスワード
|
||||
forgot_password = パスワードを忘れました
|
||||
welcome = ようこそ
|
||||
my_projects = マイプロジェクト
|
||||
public_projects = パブリックプロジェクト
|
||||
admin_options = 管理者
|
||||
project_name = プロジェクト名
|
||||
creation_time = 作成日時
|
||||
publicity = パブリック
|
||||
add_project = プロジェクトを追加
|
||||
check_for_publicity = パブリックプロジェクト
|
||||
button_save = 保存する
|
||||
button_cancel = 取り消しする
|
||||
button_submit = 送信する
|
||||
username = ユーザ名
|
||||
email = メールアドレス
|
||||
system_admin = システム管理者
|
||||
dlg_button_ok = OK
|
||||
dlg_button_cancel = 取り消し
|
||||
registration = 登録
|
||||
username_description = ログイン際に使うユーザ名を入力してください。
|
||||
email_description = メールアドレスはパスワードをリセットする際に使われます。
|
||||
full_name = フルネーム
|
||||
full_name_description = フルネームを入力してください。
|
||||
password_description = パスワード7英数字以上で、少なくとも 1小文字、 1大文字と 1数字でなければなりません。
|
||||
confirm_password = パスワードを確認する
|
||||
note_to_the_admin = メモ
|
||||
old_password = 現在のパスワード
|
||||
new_password = 新しいパスワード
|
||||
forgot_password_description = ぱプロジェクトをリセットするメールはこのアドレスに送信します。
|
||||
|
||||
projects = プロジェクト
|
||||
repositories = リポジトリ
|
||||
search = サーチ
|
||||
home = ホーム
|
||||
project = プロジェクト
|
||||
owner = オーナー
|
||||
repo = リポジトリ
|
||||
user = ユーザ
|
||||
logs = ログ
|
||||
repo_name = リポジトリ名
|
||||
repo_tag = リポジトリタグ
|
||||
add_members = メンバーを追加
|
||||
operation = 操作
|
||||
advance = さらに絞りこみで検索
|
||||
all = 全部
|
||||
others = その他
|
||||
start_date = 開始日
|
||||
end_date = 終了日
|
||||
timestamp = タイムスタンプ
|
||||
role = 役割
|
||||
reset_email_hint = このリンクをクリックしてパスワードリセットの処理を続けてください
|
||||
reset_email_subject = パスワードをリセットします
|
||||
language = 日本語
|
||||
language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
language_de-DE = Deutsch
|
||||
language_ru-RU = Русский
|
||||
language_ja-JP = 日本語
|
||||
copyright = コピーライト
|
||||
all_rights_reserved = 無断複写・転載を禁じます
|
||||
index_desc = Harborは、信頼性の高いエンタープライズクラスのRegistryサーバです。タープライズユーザはHarborを利用し、プライベートのRegistryサビースを構築し、生産性および安全性を向上させる事ができます。開発環境はもちろん、生産環境にも使用する事ができます。
|
||||
index_desc_0 = 主な利点:
|
||||
index_desc_1 = 1. セキュリティ: 知的財産権を組織内で確保する。
|
||||
index_desc_2 = 2. 効率: プライベートなので、パブリックRegistryサビースにネットワーク通信が減らす。
|
||||
index_desc_3 = 3. アクセス制御: ロールベースアクセス制御機能を実装し、更に既存のユーザ管理システム(AD/LDAP)と統合することも可能。
|
||||
index_desc_4 = 4. 監査: すべてRegistryサビースへの操作が記録され、検査にに利用できる。
|
||||
index_desc_5 = 5. 管理UI: 使いやすい管理UIが搭載する。
|
||||
index_title = エンタープライズ Registry サビース
|
@ -1,457 +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.
|
||||
*/
|
||||
var global_messages = {
|
||||
"username_is_required" : {
|
||||
"en-US": "Username is required.",
|
||||
"zh-CN": "用户名为必填项。",
|
||||
"ja-JP": "ユーザ名は必須項目です。",
|
||||
"de-DE": "Benutzername erforderlich.",
|
||||
"ru-RU": "Требуется ввести имя пользователя."
|
||||
},
|
||||
"username_has_been_taken" : {
|
||||
"en-US": "Username has been taken.",
|
||||
"zh-CN": "用户名已被占用。",
|
||||
"ja-JP": "ユーザ名はすでに登録されました。",
|
||||
"de-DE": "Benutzername bereits vergeben.",
|
||||
"ru-RU": "Имя пользователя уже используется."
|
||||
},
|
||||
"username_is_too_long" : {
|
||||
"en-US": "Username is too long. (maximum 20 characters)",
|
||||
"zh-CN": "用户名长度超出限制。(最长为20个字符)",
|
||||
"ja-JP": "ユーザ名が長すぎです。(20文字まで)",
|
||||
"de-DE": "Benutzername ist zu lang. (maximal 20 Zeichen)",
|
||||
"ru-RU": "Имя пользователя слишком длинное. (максимум 20 символов)"
|
||||
},
|
||||
"username_contains_illegal_chars": {
|
||||
"en-US": "Username contains illegal character(s).",
|
||||
"zh-CN": "用户名包含不合法的字符。",
|
||||
"ja-JP": "ユーザ名に使えない文字が入っています。",
|
||||
"de-DE": "Benutzername enthält ungültige Zeichen.",
|
||||
"ru-RU": "Имя пользователя содержит недопустимые символы."
|
||||
},
|
||||
"email_is_required" : {
|
||||
"en-US": "Email is required.",
|
||||
"zh-CN": "邮箱为必填项。",
|
||||
"ja-JP": "メールアドレスが必須です。",
|
||||
"de-DE": "E-Mail Adresse erforderlich.",
|
||||
"ru-RU": "Требуется ввести E-mail адрес."
|
||||
},
|
||||
"email_contains_illegal_chars" : {
|
||||
"en-US": "Email contains illegal character(s).",
|
||||
"zh-CN": "邮箱包含不合法的字符。",
|
||||
"ja-JP": "メールアドレスに使えない文字が入っています。",
|
||||
"de-DE": "E-Mail Adresse enthält ungültige Zeichen.",
|
||||
"ru-RU": "E-mail адрес содержит недопеустимые символы."
|
||||
},
|
||||
"email_has_been_taken" : {
|
||||
"en-US": "Email has been taken.",
|
||||
"zh-CN": "邮箱已被占用。",
|
||||
"ja-JP": "メールアドレスがすでに使われました。",
|
||||
"de-DE": "E-Mail Adresse wird bereits verwendet.",
|
||||
"ru-RU": "Такой E-mail адрес уже используется."
|
||||
},
|
||||
"email_content_illegal" : {
|
||||
"en-US": "Email format is illegal.",
|
||||
"zh-CN": "邮箱格式不合法。",
|
||||
"ja-JP": "メールアドレスフォーマットエラー。",
|
||||
"de-DE": "Format der E-Mail Adresse ist ungültig.",
|
||||
"ru-RU": "Недопустимый формат E-mail адреса."
|
||||
},
|
||||
"email_does_not_exist" : {
|
||||
"en-US": "Email does not exist.",
|
||||
"zh-CN": "邮箱不存在。",
|
||||
"ja-JP": "メールアドレスが存在しません。",
|
||||
"de-DE": "E-Mail Adresse existiert nicht.",
|
||||
"ru-RU": "E-mail адрес не существует."
|
||||
},
|
||||
"realname_is_required" : {
|
||||
"en-US": "Full name is required.",
|
||||
"zh-CN": "全名为必填项。",
|
||||
"ja-JP": "フルネームが必須です。",
|
||||
"de-DE": "Vollständiger Name erforderlich.",
|
||||
"ru-RU": "Требуется ввести полное имя."
|
||||
},
|
||||
"realname_is_too_long" : {
|
||||
"en-US": "Full name is too long. (maximum 20 characters)",
|
||||
"zh-CN": "全名长度超出限制。(最长为20个字符)",
|
||||
"ja-JP": "フルネームは長すぎです。(20文字まで)",
|
||||
"de-DE": "Vollständiger Name zu lang. (maximal 20 Zeichen)",
|
||||
"ru-RU": "Полное имя слишком длинное. (максимум 20 символов)"
|
||||
},
|
||||
"realname_contains_illegal_chars" : {
|
||||
"en-US": "Full name contains illegal character(s).",
|
||||
"zh-CN": "全名包含不合法的字符。",
|
||||
"ja-JP": "フルネームに使えない文字が入っています。",
|
||||
"de-DE": "Vollständiger Name enthält ungültige Zeichen.",
|
||||
"ru-RU": "Полное имя содержит недопустимые символы."
|
||||
},
|
||||
"password_is_required" : {
|
||||
"en-US": "Password is required.",
|
||||
"zh-CN": "密码为必填项。",
|
||||
"ja-JP": "パスワードは必須です。",
|
||||
"de-DE": "Passwort erforderlich.",
|
||||
"ru-RU": "Требуется ввести пароль."
|
||||
},
|
||||
"password_is_invalid" : {
|
||||
"en-US": "Password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.",
|
||||
"zh-CN": "密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。",
|
||||
"ja-JP": "無効なパスワードです。7英数字以上で、 少なくとも1小文字、1大文字と1数字となります。",
|
||||
"de-DE": "Passwort ungültig. Mindestens sieben Zeichen bestehend aus einem Kleinbuchstaben, einem Großbuchstaben und einer Zahl",
|
||||
"ru-RU": "Такой пароль недопустим. Парольл должен содержать Минимум 7 символов, в которых будет присутствовать по меньшей мере 1 буква нижнего регистра, 1 буква верхнего регистра и 1 цифра"
|
||||
},
|
||||
"password_is_too_long" : {
|
||||
"en-US": "Password is too long. (maximum 20 characters)",
|
||||
"zh-CN": "密码长度超出限制。(最长为20个字符)",
|
||||
"ja-JP": "パスワードは長すぎです。(20文字まで)",
|
||||
"de-DE": "Passwort zu lang. (maximal 20 Zeichen)",
|
||||
"ru-RU": "Пароль слишком длинный (максимум 20 символов)"
|
||||
},
|
||||
"password_does_not_match" : {
|
||||
"en-US": "Passwords do not match.",
|
||||
"zh-CN": "两次密码输入不一致。",
|
||||
"ja-JP": "確認のパスワードが正しくありません。",
|
||||
"de-DE": "Passwörter stimmen nicht überein.",
|
||||
"ru-RU": "Пароли не совпадают."
|
||||
},
|
||||
"comment_is_too_long" : {
|
||||
"en-US": "Comment is too long. (maximum 20 characters)",
|
||||
"zh-CN": "备注长度超出限制。(最长为20个字符)",
|
||||
"ja-JP": "コメントは長すぎです。(20文字まで)",
|
||||
"de-DE": "Kommentar zu lang. (maximal 20 Zeichen)",
|
||||
"ru-RU": "Комментарий слишком длинный. (максимум 20 символов)"
|
||||
},
|
||||
"comment_contains_illegal_chars" : {
|
||||
"en-US": "Comment contains illegal character(s).",
|
||||
"zh-CN": "备注包含不合法的字符。",
|
||||
"ja-JP": "コメントに使えない文字が入っています。",
|
||||
"de-DE": "Kommentar enthält ungültige Zeichen.",
|
||||
"ru-RU": "Комментарий содержит недопустимые символы."
|
||||
},
|
||||
"project_name_is_required" : {
|
||||
"en-US": "Project name is required.",
|
||||
"zh-CN": "项目名称为必填项。",
|
||||
"ja-JP": "プロジェクト名は必須です。",
|
||||
"de-DE": "Projektname erforderlich.",
|
||||
"ru-RU": "Необходимо ввести название Проекта."
|
||||
},
|
||||
"project_name_is_too_short" : {
|
||||
"en-US": "Project name is too short. (minimum 4 characters)",
|
||||
"zh-CN": "项目名称至少要求 4个字符。",
|
||||
"ja-JP": "プロジェクト名は4文字以上です。",
|
||||
"de-DE": "Projektname zu kurz. (mindestens 4 Zeichen)",
|
||||
"ru-RU": "Название проекта слишком короткое. (миниму 4 символа)"
|
||||
},
|
||||
"project_name_is_too_long" : {
|
||||
"en-US": "Project name is too long. (maximum 30 characters)",
|
||||
"zh-CN": "项目名称长度超出限制。(最长为30个字符)",
|
||||
"ja-JP": "プロジェクト名は長すぎです。(30文字まで)",
|
||||
"de-DE": "Projektname zu lang. (maximal 30 Zeichen)",
|
||||
"ru-RU": "Название проекта слишком длинное (максимум 30 символов)"
|
||||
},
|
||||
"project_name_contains_illegal_chars" : {
|
||||
"en-US": "Project name contains illegal character(s).",
|
||||
"zh-CN": "项目名称包含不合法的字符。",
|
||||
"ja-JP": "プロジェクト名に使えない文字が入っています。",
|
||||
"de-DE": "Projektname enthält ungültige Zeichen.",
|
||||
"ru-RU": "Название проекта содержит недопустимые символы."
|
||||
},
|
||||
"project_exists" : {
|
||||
"en-US": "Project exists.",
|
||||
"zh-CN": "项目已存在。",
|
||||
"ja-JP": "プロジェクトはすでに存在しました。",
|
||||
"de-DE": "Projekt existiert bereits.",
|
||||
"ru-RU": "Такой проект уже существует."
|
||||
},
|
||||
"delete_user" : {
|
||||
"en-US": "Delete User",
|
||||
"zh-CN": "删除用户",
|
||||
"ja-JP": "ユーザを削除",
|
||||
"de-DE": "Benutzer löschen",
|
||||
"ru-RU": "Удалить пользователя"
|
||||
},
|
||||
"are_you_sure_to_delete_user" : {
|
||||
"en-US": "Are you sure to delete ",
|
||||
"zh-CN": "确认要删除用户 ",
|
||||
"ja-JP": "ユーザを削除でよろしでしょうか ",
|
||||
"de-DE": "Sind Sie sich sicher, dass Sie folgenden Benutzer löschen möchten: ",
|
||||
"ru-RU": "Вы уверены что хотите удалить пользователя? "
|
||||
},
|
||||
"input_your_username_and_password" : {
|
||||
"en-US": "Please input your username and password.",
|
||||
"zh-CN": "请输入用户名和密码。",
|
||||
"ja-JP": "ユーザ名とパスワードを入力してください。",
|
||||
"de-DE": "Bitte geben Sie ihr Benutzername und Passwort ein.",
|
||||
"ru-RU": "Введите имя пользователя и пароль."
|
||||
},
|
||||
"check_your_username_or_password" : {
|
||||
"en-US": "Please check your username or password.",
|
||||
"zh-CN": "请输入正确的用户名或密码。",
|
||||
"ja-JP": "正しいユーザ名とパスワードを入力してください。",
|
||||
"de-DE": "Bitte überprüfen Sie ihren Benutzernamen und Passwort.",
|
||||
"ru-RU": "Проверьте свои имя пользователя и пароль."
|
||||
},
|
||||
"title_login_failed" : {
|
||||
"en-US": "Login Failed",
|
||||
"zh-CN": "登录失败",
|
||||
"ja-JP": "ログインに失敗しました。",
|
||||
"de-DE": "Anmeldung fehlgeschlagen",
|
||||
"ru-RU": "Ошибка входа"
|
||||
},
|
||||
"title_change_password" : {
|
||||
"en-US": "Change Password",
|
||||
"zh-CN": "修改密码",
|
||||
"ja-JP": "パスワードを変更します。",
|
||||
"de-DE": "Passwort ändern",
|
||||
"ru-RU": "Сменить пароль"
|
||||
},
|
||||
"change_password_successfully" : {
|
||||
"en-US": "Password changed successfully.",
|
||||
"zh-CN": "密码已修改。",
|
||||
"ja-JP": "パスワードを変更しました。",
|
||||
"de-DE": "Passwort erfolgreich geändert.",
|
||||
"ru-RU": "Пароль успешно изменен."
|
||||
},
|
||||
"title_forgot_password" : {
|
||||
"en-US": "Forgot Password",
|
||||
"zh-CN": "忘记密码",
|
||||
"ja-JP": "パスワードをリセットします。",
|
||||
"de-DE": "Passwort vergessen",
|
||||
"ru-RU": "Забыли пароль?"
|
||||
},
|
||||
"email_has_been_sent" : {
|
||||
"en-US": "Email for resetting password has been sent.",
|
||||
"zh-CN": "重置密码邮件已发送。",
|
||||
"ja-JP": "パスワードをリセットするメールを送信しました。",
|
||||
"de-DE": "Eine E-Mail mit einem Wiederherstellungslink wurde an Sie gesendet.",
|
||||
"ru-RU": "На ваш E-mail было выслано письмо с инструкциями по сбросу пароля."
|
||||
},
|
||||
"send_email_failed" : {
|
||||
"en-US": "Failed to send Email for resetting password.",
|
||||
"zh-CN": "重置密码邮件发送失败。",
|
||||
"ja-JP": "パスワードをリセットするメールを送信する際エラーが出ました",
|
||||
"de-DE": "Fehler beim Senden der Wiederherstellungs-E-Mail.",
|
||||
"ru-RU": "Ошибка отправки сообщения."
|
||||
},
|
||||
"please_login_first" : {
|
||||
"en-US": "Please login first.",
|
||||
"zh-CN": "请先登录。",
|
||||
"ja-JP": "この先にログインが必要です。",
|
||||
"de-DE": "Bitte melden Sie sich zuerst an.",
|
||||
"ru-RU": "Сначала выполните вход в систему."
|
||||
},
|
||||
"old_password_is_not_correct" : {
|
||||
"en-US": "Old password is not correct.",
|
||||
"zh-CN": "原密码输入不正确。",
|
||||
"ja-JP": "現在のパスワードが正しく入力されていません。",
|
||||
"de-DE": "Altes Passwort ist nicht korrekt.",
|
||||
"ru-RU": "Старый пароль введен неверно."
|
||||
},
|
||||
"please_input_new_password" : {
|
||||
"en-US": "Please input new password.",
|
||||
"zh-CN": "请输入新密码。",
|
||||
"ja-JP": "あたらしいパスワードを入力してください",
|
||||
"de-DE": "Bitte geben Sie ihr neues Passwort ein.",
|
||||
"ru-RU": "Пожалуйста, введите новый пароль."
|
||||
},
|
||||
"invalid_reset_url": {
|
||||
"en-US": "Invalid URL for resetting password.",
|
||||
"zh-CN": "无效密码重置链接。",
|
||||
"ja-JP": "無効なパスワードをリセットするリンク。",
|
||||
"de-DE": "Ungültige URL zum Passwort wiederherstellen.",
|
||||
"ru-RU": "Неверный URL для сброса пароля."
|
||||
},
|
||||
"reset_password_successfully" : {
|
||||
"en-US": "Reset password successfully.",
|
||||
"zh-CN": "密码重置成功。",
|
||||
"ja-JP": "パスワードをリセットしました。",
|
||||
"de-DE": "Passwort erfolgreich wiederhergestellt.",
|
||||
"ru-RU": "Пароль успешно сброшен."
|
||||
},
|
||||
"internal_error": {
|
||||
"en-US": "Internal error.",
|
||||
"zh-CN": "内部错误,请联系系统管理员。",
|
||||
"ja-JP": "エラーが出ました、管理者に連絡してください。",
|
||||
"de-DE": "Interner Fehler.",
|
||||
"ru-RU": "Внутренняя ошибка."
|
||||
},
|
||||
"title_reset_password" : {
|
||||
"en-US": "Reset Password",
|
||||
"zh-CN": "重置密码",
|
||||
"ja-JP": "パスワードをリセットする",
|
||||
"de-DE": "Passwort zurücksetzen",
|
||||
"ru-RU": "Сбросить пароль"
|
||||
},
|
||||
"title_sign_up" : {
|
||||
"en-US": "Sign Up",
|
||||
"zh-CN": "注册",
|
||||
"ja-JP": "登録",
|
||||
"de-DE": "Registrieren",
|
||||
"ru-RU": "Регистрация"
|
||||
},
|
||||
"title_add_user": {
|
||||
"en-US": "Add User",
|
||||
"zh-CN": "新增用户",
|
||||
"ja-JP": "ユーザを追加",
|
||||
"de-DE": "Benutzer hinzufügen",
|
||||
"ru-RU": "Добавить пользователя"
|
||||
},
|
||||
"registered_successfully": {
|
||||
"en-US": "Signed up successfully.",
|
||||
"zh-CN": "注册成功。",
|
||||
"ja-JP": "登録しました。",
|
||||
"de-DE": "Erfolgreich registriert.",
|
||||
"ru-RU": "Регистрация прошла успешно."
|
||||
},
|
||||
"registered_failed" : {
|
||||
"en-US": "Failed to sign up.",
|
||||
"zh-CN": "注册失败。",
|
||||
"ja-JP": "登録でませんでした。",
|
||||
"de-DE": "Registrierung fehlgeschlagen.",
|
||||
"ru-RU": "Ошибка регистрации."
|
||||
},
|
||||
"added_user_successfully": {
|
||||
"en-US": "Added user successfully.",
|
||||
"zh-CN": "新增用户成功。",
|
||||
"ja-JP": "ユーザを追加しました。",
|
||||
"de-DE": "Benutzer erfolgreich erstellt.",
|
||||
"ru-RU": "Пользователь успешно добавлен."
|
||||
},
|
||||
"added_user_failed": {
|
||||
"en-US": "Adding user failed.",
|
||||
"zh-CN": "新增用户失败。",
|
||||
"ja-JP": "ユーザを追加できませんでした。",
|
||||
"de-DE": "Benutzer erstellen fehlgeschlagen.",
|
||||
"ru-RU": "Ошибка добавления пользователя."
|
||||
},
|
||||
"projects": {
|
||||
"en-US": "Projects",
|
||||
"zh-CN": "项目",
|
||||
"ja-JP": "プロジェクト",
|
||||
"de-DE": "Projekte",
|
||||
"ru-RU": "Проекты"
|
||||
},
|
||||
"repositories" : {
|
||||
"en-US": "Repositories",
|
||||
"zh-CN": "镜像仓库",
|
||||
"ja-JP": "リポジトリ",
|
||||
"de-DE": "Repositories",
|
||||
"ru-RU": "Репозитории"
|
||||
},
|
||||
"no_repo_exists" : {
|
||||
"en-US": "No repositories found, please use 'docker push' to upload images.",
|
||||
"zh-CN": "未发现镜像,请用‘docker push’命令上传镜像。",
|
||||
"ja-JP": "イメージが見つかりませんでした。’docker push’を利用しイメージをアップロードしてください。",
|
||||
"de-DE": "Keine Repositories gefunden, bitte benutzen Sie 'docker push' um ein Image hochzuladen.",
|
||||
"ru-RU": "Репозитории не найдены, используйте команду 'docker push' для добавления образов."
|
||||
},
|
||||
"tag" : {
|
||||
"en-US": "Tag",
|
||||
"zh-CN": "标签",
|
||||
"ja-JP": "タグ",
|
||||
"de-DE": "Tag",
|
||||
"ru-RU": "Метка"
|
||||
},
|
||||
"pull_command": {
|
||||
"en-US": "Pull Command",
|
||||
"zh-CN": "Pull 命令",
|
||||
"ja-JP": "Pull コマンド",
|
||||
"de-DE": "Pull Befehl",
|
||||
"ru-RU": "Команда для скачивания образа"
|
||||
},
|
||||
"image_details" : {
|
||||
"en-US": "Image Details",
|
||||
"zh-CN": "镜像详细信息",
|
||||
"ja-JP": "イメージ詳細",
|
||||
"de-DE": "Image Details",
|
||||
"ru-RU": "Информация об образе"
|
||||
},
|
||||
"add_members" : {
|
||||
"en-US": "Add Member",
|
||||
"zh-CN": "添加成员",
|
||||
"ja-JP": "メンバーを追加する",
|
||||
"de-DE": "Mitglied hinzufügen",
|
||||
"ru-RU": "Добавить Участника"
|
||||
},
|
||||
"edit_members" : {
|
||||
"en-US": "Edit Members",
|
||||
"zh-CN": "编辑成员",
|
||||
"ja-JP": "メンバーを編集する",
|
||||
"de-DE": "Mitglieder bearbeiten",
|
||||
"ru-RU": "Редактировать Участников"
|
||||
},
|
||||
"add_member_failed" : {
|
||||
"en-US": "Adding Member Failed",
|
||||
"zh-CN": "添加成员失败",
|
||||
"ja-JP": "メンバーを追加できません出した",
|
||||
"de-DE": "Mitglied hinzufügen fehlgeschlagen",
|
||||
"ru-RU": "Ошибка при добавлении нового участника"
|
||||
},
|
||||
"please_input_username" : {
|
||||
"en-US": "Please input a username.",
|
||||
"zh-CN": "请输入用户名。",
|
||||
"ja-JP": "ユーザ名を入力してください。",
|
||||
"de-DE": "Bitte geben Sie einen Benutzernamen ein.",
|
||||
"ru-RU": "Пожалуйста, введите имя пользователя."
|
||||
},
|
||||
"please_assign_a_role_to_user" : {
|
||||
"en-US": "Please assign a role to the user.",
|
||||
"zh-CN": "请为用户分配角色。",
|
||||
"ja-JP": "ユーザーに役割を割り当てるしてください。",
|
||||
"de-DE": "Bitte weisen Sie dem Benutzer eine Rolle zu.",
|
||||
"ru-RU": "Пожалуйста, назначьте роль пользователю."
|
||||
},
|
||||
"user_id_exists" : {
|
||||
"en-US": "User is already a member.",
|
||||
"zh-CN": "用户已经是成员。",
|
||||
"ja-JP": "すでにメンバーに登録しました。",
|
||||
"de-DE": "Benutzer ist bereits Mitglied.",
|
||||
"ru-RU": "Пользователь уже является участником."
|
||||
},
|
||||
"user_id_does_not_exist" : {
|
||||
"en-US": "User does not exist.",
|
||||
"zh-CN": "不存在此用户。",
|
||||
"ja-JP": "ユーザが見つかりませんでした。",
|
||||
"de-DE": "Benutzer existiert nicht.",
|
||||
"ru-RU": "Пользователя с таким именем не существует."
|
||||
},
|
||||
"insufficient_privileges" : {
|
||||
"en-US": "Insufficient privileges.",
|
||||
"zh-CN": "权限不足。",
|
||||
"ja-JP": "権限エラー。",
|
||||
"de-DE": "Unzureichende Berechtigungen.",
|
||||
"ru-RU": "Недостаточно прав."
|
||||
},
|
||||
"operation_failed" : {
|
||||
"en-US": "Operation Failed",
|
||||
"zh-CN": "操作失败",
|
||||
"ja-JP": "操作に失敗しました。",
|
||||
"de-DE": "Befehl fehlgeschlagen",
|
||||
"ru-RU": "Ошибка при выполнении данной операции"
|
||||
},
|
||||
"button_on" : {
|
||||
"en-US": "On",
|
||||
"zh-CN": "打开",
|
||||
"ja-JP": "オン",
|
||||
"de-DE": "An",
|
||||
"ru-RU": "Вкл."
|
||||
},
|
||||
"button_off" : {
|
||||
"en-US": "Off",
|
||||
"zh-CN": "关闭",
|
||||
"ja-JP": "オフ",
|
||||
"de-DE": "Aus",
|
||||
"ru-RU": "Откл."
|
||||
}
|
||||
};
|
@ -1,89 +0,0 @@
|
||||
page_title_index = Harbor
|
||||
page_title_sign_in = Войти - Harbor
|
||||
page_title_project = Проект - Harbor
|
||||
page_title_item_details = Подробнее - Harbor
|
||||
page_title_registration = Регистрация - Harbor
|
||||
page_title_add_user = Добавить пользователя - Harbor
|
||||
page_title_forgot_password = Забыли пароль - Harbor
|
||||
title_forgot_password = Забыли пароль
|
||||
page_title_reset_password = Сбросить пароль - Harbor
|
||||
title_reset_password = Сбросить пароль
|
||||
page_title_change_password = Поменять пароль - Harbor
|
||||
title_change_password = Поменять пароль
|
||||
page_title_search = Поиск - Harbor
|
||||
sign_in = Войти
|
||||
sign_up = Регистрация
|
||||
add_user = Добавить пользователя
|
||||
log_out = Выйти
|
||||
search_placeholder = проекты или репозитории
|
||||
change_password = Сменить Пароль
|
||||
username_email = Логин/Email
|
||||
password = Пароль
|
||||
forgot_password = Забыли пароль
|
||||
welcome = Добро пожаловать
|
||||
my_projects = Мои Проекты
|
||||
public_projects = Общедоступные Проекты
|
||||
admin_options = Административные Настройки
|
||||
project_name = Название Проекта
|
||||
creation_time = Время Создания
|
||||
publicity = Публичность
|
||||
add_project = Добавить Проект
|
||||
check_for_publicity = Публичный проекта
|
||||
button_save = Сохранить
|
||||
button_cancel = Отмена
|
||||
button_submit = Применить
|
||||
username = Имя пользователя
|
||||
email = Email
|
||||
system_admin = Системный администратор
|
||||
dlg_button_ok = OK
|
||||
dlg_button_cancel = Отмена
|
||||
registration = Регистрация
|
||||
username_description = Ваше имя пользователя.
|
||||
email_description = Email адрес, который будет использоваться для сброса пароля.
|
||||
full_name = Полное Имя
|
||||
full_name_description = Имя и Фамилия.
|
||||
password_description = Минимум 7 символов, в которых будет присутствовать по меньшей мере 1 буква нижнего регистра, 1 буква верхнего регистра и 1 цифра.
|
||||
confirm_password = Подтвердить Пароль
|
||||
note_to_the_admin = Комментарии
|
||||
old_password = Старый Пароль
|
||||
new_password = Новый Пароль
|
||||
forgot_password_description = Введите Email, который вы использовали для регистрации, вам будет выслано письмо для сброса пароля.
|
||||
|
||||
projects = Проекты
|
||||
repositories = Репозитории
|
||||
search = Поиск
|
||||
home = Домой
|
||||
project = Проект
|
||||
owner = Владелец
|
||||
repo = Репозитории
|
||||
user = Пользователи
|
||||
logs = Логи
|
||||
repo_name = Имя Репозитория
|
||||
repo_tag = Метка
|
||||
add_members = Добавить Участников
|
||||
operation = Операция
|
||||
advance = Расширенный Поиск
|
||||
all = Все
|
||||
others = Другие
|
||||
start_date = Дата Начала
|
||||
end_date = Дата Окончания
|
||||
timestamp = Временная метка
|
||||
role = Роль
|
||||
reset_email_hint = Нажмите на ссылку ниже для сброса вашего пароля
|
||||
reset_email_subject = Сброс вашего пароля
|
||||
language = Русский
|
||||
language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
language_de-DE = Deutsch
|
||||
language_ru-RU = Русский
|
||||
language_ja-JP = 日本語
|
||||
copyright = Copyright
|
||||
all_rights_reserved = Все права защищены.
|
||||
index_desc = Проект Harbor представляет собой надежный сервер управления docker-образами корпоративного класса. Компании могут использовать данный сервер в своей инфарструктуе для повышения производительности и безопасности . Проект Harbor может использоваться как в среде разработки так и в продуктивной среде.
|
||||
index_desc_0 = Основные преимущества данного решения:
|
||||
index_desc_1 = 1. Безопасность: Хранение интеллектуальной собственности внутри организации.
|
||||
index_desc_2 = 2. Эффективность: сервер хранения docker образов устанавливается в рамках внутренней сети организации, и может значительно сократить расход Интернет траффика
|
||||
index_desc_3 = 3. Управление доступом: реализована модель RBAC (Ролевая модель управление доступом). Управление пользователями может быть интегрировано с существующими корпоративными сервисами идентификациями такими как AD/LDAP.
|
||||
index_desc_4 = 4. Аудит: Любой доступ к хранилищу логируется и может быть использован для последующего анализа.
|
||||
index_desc_5 = 5. GUI-интерфейс: удобная, единая консоль управления.
|
||||
index_title = Сервер управления docker-образами корпоративного класса
|
@ -1,89 +0,0 @@
|
||||
page_title_index = Harbor
|
||||
page_title_sign_in = 登录 - Harbor
|
||||
page_title_project = 项目 - Harbor
|
||||
page_title_item_details = 详细信息 - Harbor
|
||||
page_title_registration = 注册 - Harbor
|
||||
page_title_add_user = 新增用户 - Harbor
|
||||
page_title_forgot_password = 忘记密码 - Harbor
|
||||
title_forgot_password = 忘记密码
|
||||
page_title_reset_password = 重置密码 - Harbor
|
||||
title_reset_password = 重置密码
|
||||
page_title_change_password = 修改密码 - Harbor
|
||||
title_change_password = 修改密码
|
||||
page_title_search = 搜索 - Harbor
|
||||
sign_in = 登录
|
||||
sign_up = 注册
|
||||
add_user = 新增用户
|
||||
log_out = 注销
|
||||
search_placeholder = 项目或镜像名称
|
||||
change_password = 修改密码
|
||||
username_email = 用户名/邮箱
|
||||
password = 密码
|
||||
forgot_password = 忘记密码
|
||||
welcome = 欢迎
|
||||
my_projects = 我的项目
|
||||
public_projects = 公开项目
|
||||
admin_options = 管理员选项
|
||||
project_name = 项目名称
|
||||
creation_time = 创建时间
|
||||
publicity = 公开
|
||||
add_project = 新建项目
|
||||
check_for_publicity = 公开项目
|
||||
button_save = 保存
|
||||
button_cancel = 取消
|
||||
button_submit = 提交
|
||||
username = 用户名
|
||||
email = 邮箱
|
||||
system_admin = 系统管理员
|
||||
dlg_button_ok = 确定
|
||||
dlg_button_cancel = 取消
|
||||
registration = 注册
|
||||
username_description = 在此填入登录时的用户名。
|
||||
email_description = 此邮箱将用于重置密码。
|
||||
full_name = 全名
|
||||
full_name_description = 请输入全名。
|
||||
password_description = 至少输入 7个字符且包含 1个小写字母, 1个大写字母和 1个数字。
|
||||
confirm_password = 确认密码
|
||||
note_to_the_admin = 备注
|
||||
old_password = 原密码
|
||||
new_password = 新密码
|
||||
forgot_password_description = 重置邮件将发送到此邮箱。
|
||||
|
||||
projects = 项目
|
||||
repositories = 镜像仓库
|
||||
search = 搜索
|
||||
home = 首页
|
||||
project = 项目
|
||||
owner = 所有者
|
||||
repo = 镜像仓库
|
||||
user = 用户
|
||||
logs = 日志
|
||||
repo_name = 镜像名称
|
||||
repo_tag = 镜像标签
|
||||
add_members = 添加成员
|
||||
operation = 操作
|
||||
advance = 高级检索
|
||||
all = 全部
|
||||
others = 其他
|
||||
start_date = 开始日期
|
||||
end_date = 结束日期
|
||||
timestamp = 时间戳
|
||||
role = 角色
|
||||
reset_email_hint = 请点击下面的链接进行重置密码操作
|
||||
reset_email_subject = 重置您的密码
|
||||
language = 中文
|
||||
language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
language_de-DE = Deutsch
|
||||
language_ru-RU = Русский
|
||||
language_ja-JP = 日本語
|
||||
copyright = 版权所有
|
||||
all_rights_reserved = 保留所有权利。
|
||||
index_desc = Harbor是可靠的企业级Registry服务器。企业用户可使用Harbor搭建私有容器Registry服务,提高生产效率和安全度,既可应用于生产环境,也可以在开发环境中使用。
|
||||
index_desc_0 = 主要优点:
|
||||
index_desc_1 = 1. 安全: 确保知识产权在自己组织内部的管控之下。
|
||||
index_desc_2 = 2. 效率: 搭建组织内部的私有容器Registry服务,可显著降低访问公共Registry服务的网络需求。
|
||||
index_desc_3 = 3. 访问控制: 提供基于角色的访问控制,可集成企业目前拥有的用户管理系统(如:AD/LDAP)。
|
||||
index_desc_4 = 4. 审计: 所有访问Registry服务的操作均被记录,便于日后审计。
|
||||
index_desc_5 = 5. 管理界面: 具有友好易用图形管理界面。
|
||||
index_title = 企业级 Registry 服务
|
@ -1,15 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.repository')
|
||||
.controller('DeleteRepositoryController', DeleteRepositoryController);
|
||||
|
||||
DeleteRepositoryController.$inject = ['DeleteRepositoryService'];
|
||||
|
||||
function DeleteRepositoryController(DeleteRepositoryService) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -1,38 +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 {
|
||||
width: 100%;
|
||||
/* Set the fixed height of the footer here */
|
||||
height: 60px;
|
||||
}
|
||||
.div-height {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.blue {
|
||||
color: #428bca;
|
||||
}
|
||||
sub {
|
||||
font-size: 6pt;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
height: 430px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
table > tbody > tr > td {
|
||||
vertical-align: bottom;
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
clear: both;
|
||||
background-color: #A8A8A8;
|
||||
height: 44px;
|
||||
z-index: 10;
|
||||
}
|
||||
.footer p {
|
||||
padding-top: 8px;
|
@ -1,12 +1,11 @@
|
||||
.container-custom {
|
||||
position: relative;
|
||||
height: 680px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.extend-height {
|
||||
height: 100%;
|
||||
min-height: 1px;
|
||||
max-height: 680px;
|
||||
min-width: 1024px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@ -16,10 +15,10 @@
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
background-color: #FFFFFF;
|
||||
height: 660px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 1px;
|
||||
max-height: 660px;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.search-pane {
|
@ -1,27 +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 {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
max-width: 380px;
|
||||
margin: 10% auto;
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@ -1,98 +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.
|
||||
*/
|
||||
jQuery(function(){
|
||||
|
||||
new AjaxUtil({
|
||||
url: "/api/users/current",
|
||||
type: "get",
|
||||
success: function(data, status, xhr){},
|
||||
errors: {
|
||||
403: ""
|
||||
},
|
||||
error: function(jqXhr){
|
||||
if(jqXhr && jqXhr.status == 401){
|
||||
document.location = "/signIn";
|
||||
}
|
||||
}
|
||||
}).exec();
|
||||
|
||||
$("#divErrMsg").css({"display": "none"});
|
||||
|
||||
$("#OldPassword,#Password,#ConfirmedPassword").on("blur", validateCallback);
|
||||
validateOptions.Items = ["#OldPassword", "#Password", "#ConfirmedPassword"];
|
||||
|
||||
function bindEnterKey(){
|
||||
$(document).on("keydown", function(e){
|
||||
if(e.keyCode == 13){
|
||||
e.preventDefault();
|
||||
if($("#txtCommonSearch").is(":focus")){
|
||||
document.location = "/search?q=" + $("#txtCommonSearch").val();
|
||||
}else{
|
||||
$("#btnSubmit").trigger("click");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function unbindEnterKey(){
|
||||
$(document).off("keydown");
|
||||
}
|
||||
bindEnterKey();
|
||||
|
||||
var spinner = new Spinner({scale:1}).spin();
|
||||
|
||||
$("#btnSubmit").on("click", function(){
|
||||
validateOptions.Validate(function(){
|
||||
var oldPassword = $("#OldPassword").val();
|
||||
var password = $("#Password").val();
|
||||
new AjaxUtil({
|
||||
url: "/api/users/current/password",
|
||||
type: "put",
|
||||
data: {"old_password": oldPassword, "new_password" : password},
|
||||
beforeSend: function(e){
|
||||
unbindEnterKey();
|
||||
$("h1").append(spinner.el);
|
||||
$("#btnSubmit").prop("disabled", true);
|
||||
},
|
||||
complete: function(xhr, status){
|
||||
spinner.stop();
|
||||
$("#btnSubmit").prop("disabled", false);
|
||||
if(xhr && xhr.status == 200){
|
||||
$("#dlgModal")
|
||||
.dialogModal({
|
||||
"title": i18n.getMessage("title_change_password"),
|
||||
"content": i18n.getMessage("change_password_successfully"),
|
||||
"callback": function(){
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(jqXhr, status, error){
|
||||
if(jqXhr && jqXhr.responseText.length){
|
||||
$("#dlgModal")
|
||||
.dialogModal({
|
||||
"title": i18n.getMessage("title_change_password"),
|
||||
"content": i18n.getMessage(jqXhr.responseText),
|
||||
"callback": function(){
|
||||
bindEnterKey();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).exec();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,169 +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.
|
||||
*/
|
||||
|
||||
var AjaxUtil = function(params){
|
||||
|
||||
this.url = params.url;
|
||||
this.data = params.data;
|
||||
this.dataRaw = params.dataRaw;
|
||||
this.type = params.type;
|
||||
this.errors = params.errors || {};
|
||||
|
||||
this.success = params.success;
|
||||
this.complete = params.complete;
|
||||
this.error = params.error;
|
||||
|
||||
};
|
||||
|
||||
AjaxUtil.prototype.exec = function(){
|
||||
|
||||
var self = this;
|
||||
|
||||
return $.ajax({
|
||||
url: self.url,
|
||||
contentType: (self.dataRaw ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/json; charset=utf-8"),
|
||||
data: JSON.stringify(self.data) || self.dataRaw,
|
||||
type: self.type,
|
||||
dataType: "json",
|
||||
success: function(data, status, xhr){
|
||||
if(self.success != null){
|
||||
self.success(data, status, xhr);
|
||||
}
|
||||
},
|
||||
complete: function(jqXhr, status) {
|
||||
if(self.complete != null){
|
||||
self.complete(jqXhr, status);
|
||||
}
|
||||
},
|
||||
error: function(jqXhr){
|
||||
if(self.error != null){
|
||||
self.error(jqXhr);
|
||||
}else{
|
||||
var errorMessage = self.errors[jqXhr.status] || jqXhr.responseText;
|
||||
if(jqXhr.status == 401){
|
||||
var lastUri = location.pathname + location.search;
|
||||
if(lastUri != ""){
|
||||
document.location = "/signIn?uri=" + encodeURIComponent(lastUri);
|
||||
}else{
|
||||
document.location = "/signIn";
|
||||
}
|
||||
}else if($.trim(errorMessage).length > 0){
|
||||
$("#dlgModal").dialogModal({"title": i18n.getMessage("operation_failed"), "content": errorMessage});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var SUPPORT_LANGUAGES = {
|
||||
"en-US": "English",
|
||||
"zh-CN": "Chinese",
|
||||
"de-DE": "German",
|
||||
"ru-RU": "Russian",
|
||||
"ja-JP": "Japanese"
|
||||
};
|
||||
|
||||
var DEFAULT_LANGUAGE = "en-US";
|
||||
|
||||
var I18n = function(messages) {
|
||||
this.messages = messages;
|
||||
};
|
||||
|
||||
I18n.prototype.isSupportLanguage = function(lang){
|
||||
return (lang in SUPPORT_LANGUAGES);
|
||||
}
|
||||
|
||||
I18n.prototype.getLocale = function(){
|
||||
var lang = $("#currentLanguage").val();
|
||||
if(this.isSupportLanguage(lang)){
|
||||
return lang;
|
||||
}else{
|
||||
return DEFAULT_LANGUAGE;
|
||||
}
|
||||
};
|
||||
|
||||
I18n.prototype.getMessage = function(key){
|
||||
return this.messages[key][this.getLocale()];
|
||||
};
|
||||
|
||||
var i18n = new I18n(global_messages);
|
||||
|
||||
moment().locale(i18n.getLocale());
|
||||
|
||||
jQuery(function(){
|
||||
|
||||
$("#aLogout").on("click", function(){
|
||||
new AjaxUtil({
|
||||
url:'/logout',
|
||||
dataRaw:{"timestamp" : new Date().getTime()},
|
||||
type: "get",
|
||||
complete: function(jqXhr){
|
||||
if(jqXhr && jqXhr.status == 200){
|
||||
document.location = "/";
|
||||
}
|
||||
}
|
||||
}).exec();
|
||||
});
|
||||
|
||||
$.fn.dialogModal = function(options){
|
||||
var settings = $.extend({
|
||||
title: '',
|
||||
content: '',
|
||||
text: false,
|
||||
callback: null,
|
||||
enableCancel: false,
|
||||
}, options || {});
|
||||
|
||||
if(settings.enableCancel){
|
||||
$("#dlgCancel").show();
|
||||
$("#dlgCancel").on("click", function(){
|
||||
$(self).modal('close');
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
$("#dlgLabel", self).text(settings.title);
|
||||
|
||||
if(options.text){
|
||||
$("#dlgBody", self).html(settings.content);
|
||||
}else if(typeof settings.content == "object"){
|
||||
$(".modal-dialog", self).addClass("modal-lg");
|
||||
var lines = ['<form class="form-horizontal">'];
|
||||
for(var item in settings.content){
|
||||
lines.push('<div class="form-group">'+
|
||||
'<label class="col-sm-2 control-label">'+ item +'</label>' +
|
||||
'<div class="col-sm-10"><p class="form-control-static">' + settings.content[item] + '</p></div>' +
|
||||
'</div>');
|
||||
}
|
||||
lines.push('</form>');
|
||||
$("#dlgBody", self).html(lines.join(""));
|
||||
}else{
|
||||
$(".modal-dialog", self).removeClass("modal-lg");
|
||||
$("#dlgBody", self).text(settings.content);
|
||||
}
|
||||
|
||||
if(settings.callback != null){
|
||||
var hasEntered = false;
|
||||
$("#dlgConfirm").on("click", function(e){
|
||||
if(!hasEntered) {
|
||||
hasEntered = true;
|
||||
settings.callback();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
$(self).modal('show');
|
||||
}
|
||||
});
|
@ -10,8 +10,8 @@
|
||||
<h5 class="page-header">//vm.projectType | tr//: <span class="badge">//vm.resultCount//</span></h5>
|
||||
<div class="project-list pane-container">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" ng-repeat="item in vm.projects | name: vm.filterInput: 'Name'" ng-click="vm.selectItem(item)">
|
||||
<span ng-show="item.ProjectId == vm.selectedId" class="glyphicon glyphicon-ok project-selected"></span> <a href="#/repositories?project_id=//item.project_id//">//item.name//</a>
|
||||
<li 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>
|
@ -69,7 +69,7 @@
|
||||
vm.resultCount = vm.projects.length;
|
||||
|
||||
$scope.$watch('vm.filterInput', function(current, origin) {
|
||||
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'Name').length;
|
||||
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'name').length;
|
||||
});
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@
|
||||
function retrieveProjects() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/details/retrieve-projects.directive.html',
|
||||
templateUrl: '/static/resources/js/components/details/retrieve-projects.directive.html',
|
||||
scope: {
|
||||
'isOpen': '=',
|
||||
'selectedProject': '=',
|
@ -33,7 +33,7 @@
|
||||
function switchPaneProjects() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/details/switch-pane-projects.directive.html',
|
||||
templateUrl: '/static/resources/js/components/details/switch-pane-projects.directive.html',
|
||||
scope: {
|
||||
'isOpen': '=',
|
||||
'selectedProject': '='
|
@ -72,7 +72,7 @@
|
||||
function advancedSearch() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/log/advanced-search.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/log/advanced-search.directive.html',
|
||||
'scope': {
|
||||
'isOpen': '=',
|
||||
'op': '=',
|
@ -102,7 +102,7 @@
|
||||
function listLog() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/log/list-log.directive.html',
|
||||
templateUrl: '/static/resources/js/components/log/list-log.directive.html',
|
||||
scope: true,
|
||||
controller: ListLogController,
|
||||
controllerAs: 'vm',
|
@ -15,7 +15,7 @@
|
||||
function modalDialog() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/modal-dialog/modal-dialog.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/modal-dialog/modal-dialog.directive.html',
|
||||
'link': link,
|
||||
'scope': {
|
||||
'contentType': '@',
|
@ -22,7 +22,7 @@
|
||||
|
||||
function setLanguage(language) {
|
||||
I18nService().setCurrentLanguage(language);
|
||||
$window.location.href = '/ng/language?lang=' + language;
|
||||
$window.location.href = '/language?lang=' + language;
|
||||
}
|
||||
function logOut() {
|
||||
LogOutService()
|
||||
@ -32,7 +32,7 @@
|
||||
function logOutSuccess(data, status) {
|
||||
currentUser.unset();
|
||||
I18nService().unset();
|
||||
$window.location.href= '/ng';
|
||||
$window.location.href= '/';
|
||||
}
|
||||
function logOutFailed(data, status) {
|
||||
console.log('Failed to log out:' + data);
|
||||
@ -42,7 +42,7 @@
|
||||
function optionalMenu() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/ng/optional_menu',
|
||||
'templateUrl': '/optional_menu',
|
||||
'scope': true,
|
||||
'controller': OptionalMenuController,
|
||||
'controllerAs': 'vm',
|
@ -65,7 +65,7 @@
|
||||
function addProjectMember() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project-member/add-project-member.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/project-member/add-project-member.directive.html',
|
||||
'scope': {
|
||||
'projectId': '@',
|
||||
'isOpen': '=',
|
@ -64,7 +64,7 @@
|
||||
function editProjectMember() {
|
||||
var directive = {
|
||||
'restrict': 'A',
|
||||
'templateUrl': '/static/ng/resources/js/components/project-member/edit-project-member.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/project-member/edit-project-member.directive.html',
|
||||
'scope': {
|
||||
'username': '=',
|
||||
'userId': '=',
|
@ -59,7 +59,7 @@
|
||||
function listProjectMember() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
templateUrl: '/static/ng/resources/js/components/project-member/list-project-member.directive.html',
|
||||
templateUrl: '/static/resources/js/components/project-member/list-project-member.directive.html',
|
||||
scope: true,
|
||||
controller: ListProjectMemberController,
|
||||
controllerAs: 'vm',
|
@ -9,7 +9,6 @@
|
||||
|
||||
function roles() {
|
||||
return [
|
||||
{'id': '0', 'name': 'N/A', 'roleName': 'n/a'},
|
||||
{'id': '1', 'name': 'Project Admin', 'roleName': 'projectAdmin'},
|
||||
{'id': '2', 'name': 'Developer', 'roleName': 'developer'},
|
||||
{'id': '3', 'name': 'Guest', 'roleName': 'guest'}
|
@ -29,7 +29,7 @@
|
||||
function switchRole() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project-member/switch-role.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/project-member/switch-role.directive.html',
|
||||
'scope': {
|
||||
'roles': '=',
|
||||
'editMode': '=',
|
@ -40,13 +40,12 @@
|
||||
}
|
||||
|
||||
function addProjectFailed(data, status) {
|
||||
if(status === 409) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'project_already_exist';
|
||||
vm.hasError = true;
|
||||
if(status == 400) {
|
||||
vm.errorMessage = 'project_name_is_invalid';
|
||||
}
|
||||
if(status === 500) {
|
||||
vm.hasError = true;
|
||||
vm.errorMessage = 'project_name_is_invalid';
|
||||
if(status === 409) {
|
||||
vm.errorMessage = 'project_already_exist';
|
||||
}
|
||||
console.log('Failed to add project:' + status);
|
||||
}
|
||||
@ -64,7 +63,7 @@
|
||||
function addProject() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project/add-project.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/project/add-project.directive.html',
|
||||
'controller': AddProjectController,
|
||||
'scope' : {
|
||||
'isOpen': '='
|
@ -43,7 +43,7 @@
|
||||
function publicityButton() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/project/publicity-button.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/project/publicity-button.directive.html',
|
||||
'scope': {
|
||||
'isPublic': '=',
|
||||
'owned': '=',
|
@ -19,27 +19,30 @@
|
||||
|
||||
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.createPolicy = createPolicy;
|
||||
vm.updatePolicy = updatePolicy;
|
||||
vm.create = create;
|
||||
vm.update = update;
|
||||
vm.pingDestination = pingDestination;
|
||||
|
||||
$scope.$watch('vm.destinations', function(current) {
|
||||
if(current) {
|
||||
console.log('destination:' + angular.toJson(current));
|
||||
vm1.selection = current[0];
|
||||
vm1.endpoint = vm1.selection.endpoint;
|
||||
vm1.username = vm1.selection.username;
|
||||
vm1.password = vm1.selection.password;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$scope.$watch('vm.action+","+vm.policyId', function(current) {
|
||||
if(current) {
|
||||
console.log('Current action for replication policy:' + current);
|
||||
@ -48,9 +51,11 @@
|
||||
vm.policyId = Number(parts[1]);
|
||||
switch(parts[0]) {
|
||||
case 'ADD_NEW':
|
||||
vm.addNew(); break;
|
||||
vm.addNew();
|
||||
break;
|
||||
case 'EDIT':
|
||||
vm.edit(vm.policyId); break;
|
||||
vm.edit(vm.policyId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -68,7 +73,7 @@
|
||||
.error(listDestinationFailed);
|
||||
}
|
||||
|
||||
function addNew() {
|
||||
function addNew() {
|
||||
vm0.name = '';
|
||||
vm0.description = '';
|
||||
vm0.enabled = true;
|
||||
@ -81,13 +86,13 @@
|
||||
.error(listReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function createPolicy(policy) {
|
||||
function create(policy) {
|
||||
CreateReplicationPolicyService(policy)
|
||||
.success(createReplicationPolicySuccess)
|
||||
.error(createReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function updatePolicy(policy) {
|
||||
function update(policy) {
|
||||
console.log('Update policy ID:' + vm.policyId);
|
||||
UpdateReplicationPolicyService(vm.policyId, policy)
|
||||
.success(updateReplicationPolicySuccess)
|
||||
@ -119,7 +124,7 @@
|
||||
}
|
||||
|
||||
function listDestinationSuccess(data, status) {
|
||||
vm.destinations = data;
|
||||
vm.destinations = data || [];
|
||||
}
|
||||
function listDestinationFailed(data, status) {
|
||||
console.log('Failed list destination:' + data);
|
||||
@ -136,13 +141,17 @@
|
||||
}
|
||||
function createReplicationPolicySuccess(data, status) {
|
||||
console.log('Successful create replication policy.');
|
||||
vm.clearUp();
|
||||
vm.reload();
|
||||
}
|
||||
function createReplicationPolicyFailed(data, status) {
|
||||
if(status === 409) {
|
||||
alert('Policy name already exists.');
|
||||
}
|
||||
console.log('Failed create replication policy.');
|
||||
}
|
||||
function updateReplicationPolicySuccess(data, status) {
|
||||
console.log('Successful update replication policy.');
|
||||
vm.reload();
|
||||
}
|
||||
function updateReplicationPolicyFailed(data, status) {
|
||||
console.log('Failed update replication policy.');
|
||||
@ -164,7 +173,7 @@
|
||||
function createPolicy() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/replication/create-policy.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/replication/create-policy.directive.html',
|
||||
'scope': {
|
||||
'policyId': '@',
|
||||
'modalTitle': '@',
|
||||
@ -178,16 +187,17 @@
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attr, ctrl) {
|
||||
|
||||
element.find('#createPolicyModal').on('shown.bs.modal', function() {
|
||||
function link(scope, element, attr, ctrl) {
|
||||
|
||||
element.find('#createPolicyModal').on('show.bs.modal', function() {
|
||||
ctrl.prepareDestination();
|
||||
scope.form.$setPristine();
|
||||
});
|
||||
scope.form.$setUntouched();
|
||||
});
|
||||
|
||||
ctrl.save = save;
|
||||
|
||||
function save(form) {
|
||||
console.log(angular.toJson(form));
|
||||
var postPayload = {
|
||||
'projectId': Number(ctrl.projectId),
|
||||
'targetId': form.destination.selection.id,
|
||||
@ -199,14 +209,14 @@
|
||||
};
|
||||
switch(ctrl.action) {
|
||||
case 'ADD_NEW':
|
||||
ctrl.createPolicy(postPayload); break;
|
||||
ctrl.create(postPayload);
|
||||
break;
|
||||
case 'EDIT':
|
||||
ctrl.updatePolicy(postPayload); break;
|
||||
ctrl.update(postPayload);
|
||||
break;
|
||||
}
|
||||
element.find('#createPolicyModal').modal('hide');
|
||||
ctrl.reload();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.replicationPolicyName" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.search()"><span class="glyphicon glyphicon-search"></span></button>
|
||||
<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>New Replication</button>
|
||||
@ -49,15 +49,15 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4 col-md-12 well well-sm well-custom well-split"><div class="col-md-offset-10">//vm.projects ? vm.projects.length : 0// // 'items' | tr //</div></div>
|
||||
<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</h4>
|
||||
<hr class="hr-line"/>
|
||||
<div class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.replicationJobnName" size="30">
|
||||
<input type="text" class="form-control" placeholder="" ng-model="vm.replicationJobName" size="30">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.search()"><span class="glyphicon glyphicon-search"></span></button>
|
||||
<button class="btn btn-primary" type="button" ng-click="vm.searchReplicationJob()"><span class="glyphicon glyphicon-search"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,18 +67,20 @@
|
||||
<thead>
|
||||
<th width="20%">Name</th>
|
||||
<th width="25%">Operation</th>
|
||||
<th width="40%">Start Time</th>
|
||||
<th width="25%">Start Time</th>
|
||||
<th width="15%">Status</th>
|
||||
<th width="15%">Logs</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="vm.replicationJobs.length == 0">
|
||||
<td colspan="4" height="100%" class="empty-hint" ><h3 class="text-muted">No replication jobs.</h3></td>
|
||||
</tr>
|
||||
<tr ng-if="vm.replicationJobs.length > 0" ng-repeat="r in vm.replicationJobs">
|
||||
<td>//r.tags != null ? r.tags.join(',') : 'N/A'//</td>
|
||||
<td>//r.repository//</td>
|
||||
<td>//r.operation//</td>
|
||||
<td>//r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
<td>//r.status//</td>
|
||||
<td><a href="javascript:void(0);" ng-click="vm.downloadLog(r.id)"><span style="margin-left: 10px;" class="glyphicon glyphicon-file"></span></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
@ -6,9 +6,9 @@
|
||||
.module('harbor.replication')
|
||||
.directive('listReplication', listReplication);
|
||||
|
||||
ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService'];
|
||||
ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService', '$window'];
|
||||
|
||||
function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService) {
|
||||
function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService, $window) {
|
||||
var vm = this;
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function() {
|
||||
@ -19,19 +19,30 @@
|
||||
vm.addReplication = addReplication;
|
||||
vm.editReplication = editReplication;
|
||||
|
||||
vm.search = search;
|
||||
vm.searchReplicationPolicy = searchReplicationPolicy;
|
||||
vm.searchReplicationJob = searchReplicationJob;
|
||||
|
||||
vm.retrievePolicy = retrievePolicy;
|
||||
vm.retrieveJob = retrieveJob;
|
||||
vm.togglePolicy = togglePolicy;
|
||||
|
||||
vm.downloadLog = downloadLog;
|
||||
|
||||
vm.last = false;
|
||||
|
||||
vm.projectId = getParameterByName('project_id', $location.absUrl());
|
||||
vm.retrievePolicy();
|
||||
|
||||
function search() {
|
||||
function searchReplicationPolicy() {
|
||||
vm.retrievePolicy();
|
||||
}
|
||||
|
||||
function searchReplicationJob() {
|
||||
if(vm.lastPolicyId !== -1) {
|
||||
vm.retrieveJob(vm.lastPolicyId);
|
||||
}
|
||||
}
|
||||
|
||||
function retrievePolicy() {
|
||||
ListReplicationPolicyService('', vm.projectId, vm.replicationPolicyName)
|
||||
.success(listReplicationPolicySuccess)
|
||||
@ -45,6 +56,7 @@
|
||||
}
|
||||
|
||||
function listReplicationPolicySuccess(data, status) {
|
||||
vm.replicationJobs = [];
|
||||
vm.replicationPolicies = data || [];
|
||||
}
|
||||
|
||||
@ -88,12 +100,15 @@
|
||||
console.log('Failed toggle replication policy.');
|
||||
}
|
||||
|
||||
function downloadLog(policyId) {
|
||||
$window.open('/api/jobs/replication/' + policyId + '/log', '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
function listReplication($timeout) {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/replication/list-replication.directive.html',
|
||||
'templateUrl': '/static/resources/js/components/replication/list-replication.directive.html',
|
||||
'scope': true,
|
||||
'link': link,
|
||||
'controller': ListReplicationController,
|
||||
@ -151,7 +166,7 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function trClickHandler(e) {
|
||||
element
|
||||
.find('#upon-pane table>tbody>tr')
|