diff --git a/Deploy/templates/ui/app.conf b/Deploy/templates/ui/app.conf index 090bcdc47..6d8f15728 100644 --- a/Deploy/templates/ui/app.conf +++ b/Deploy/templates/ui/app.conf @@ -2,8 +2,8 @@ appname = registry runmode = dev [lang] -types = en-US|zh-CN|de-DE|ru-RU -names = en-US|zh-CN|de-DE|ru-RU +types = en-US|zh-CN +names = English|中文 [dev] httpport = 80 diff --git a/api/project.go b/api/project.go index b0c404c2b..90ccbdb8c 100644 --- a/api/project.go +++ b/api/project.go @@ -113,23 +113,50 @@ func (p *ProjectAPI) Head() { // Get ... func (p *ProjectAPI) Get() { - queryProject := models.Project{UserID: p.userID} + var projectList []models.Project projectName := p.GetString("project_name") if len(projectName) > 0 { - queryProject.Name = "%" + projectName + "%" + projectName = "%" + projectName + "%" + } + var public int + var err error + isPublic := p.GetString("is_public") + if len(isPublic) > 0 { + public, err = strconv.Atoi(isPublic) + if err != nil { + log.Errorf("Error parsing public property: %d, error: %v", isPublic, err) + p.CustomAbort(http.StatusBadRequest, "invalid project Id") + } + } + isAdmin := false + if public == 1 { + projectList, err = dao.GetPublicProjects(projectName) + } else { + isAdmin, err = dao.IsAdminRole(p.userID) + if err != nil { + log.Errorf("Error occured in check admin, error: %v", err) + p.CustomAbort(http.StatusInternalServerError, "Internal error.") + } + if isAdmin { + projectList, err = dao.GetAllProjects(projectName) + } else { + projectList, err = dao.GetUserRelevantProjects(p.userID, projectName) + } } - public, _ := p.GetInt("is_public") - queryProject.Public = public - - projectList, err := dao.QueryProject(queryProject) if err != nil { - log.Errorf("Error occurred in QueryProject, error: %v", err) + log.Errorf("Error occured in get projects info, error: %v", err) p.CustomAbort(http.StatusInternalServerError, "Internal error.") } for i := 0; i < len(projectList); i++ { - if isProjectAdmin(p.userID, projectList[i].ProjectID) { - projectList[i].Togglable = true + if public != 1 { + if isAdmin { + projectList[i].Role = models.PROJECTADMIN + } + if projectList[i].Role == models.PROJECTADMIN { + projectList[i].Togglable = true + } } + projectList[i].RepoCount = getRepoCountByProject(projectList[i].Name) } p.Data["json"] = projectList p.ServeJSON() diff --git a/api/search.go b/api/search.go index d47bb5890..3acdc7ca8 100644 --- a/api/search.go +++ b/api/search.go @@ -55,13 +55,13 @@ func (s *SearchAPI) Get() { var projects []models.Project if isSysAdmin { - projects, err = dao.GetAllProjects() + projects, err = dao.GetAllProjects("") if err != nil { log.Errorf("failed to get all projects: %v", err) s.CustomAbort(http.StatusInternalServerError, "internal error") } } else { - projects, err = dao.GetUserRelevantProjects(userID) + projects, err = dao.SearchProjects(userID) if err != nil { log.Errorf("failed to get user %d 's relevant projects: %v", userID, err) s.CustomAbort(http.StatusInternalServerError, "internal error") diff --git a/api/statistic.go b/api/statistic.go new file mode 100644 index 000000000..67a7cc388 --- /dev/null +++ b/api/statistic.go @@ -0,0 +1,117 @@ +/* + 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 + +import ( + "net/http" + "strings" + + "github.com/vmware/harbor/dao" + "github.com/vmware/harbor/models" + svc_utils "github.com/vmware/harbor/service/utils" + "github.com/vmware/harbor/utils/log" +) + +// StatisticAPI handles request to /api/statistics/ +type StatisticAPI struct { + BaseAPI + userID int +} + +//Prepare validates the URL and the user +func (s *StatisticAPI) Prepare() { + s.userID = s.ValidateUser() +} + +// Get total projects and repos of the user +func (s *StatisticAPI) Get() { + isAdmin, err := dao.IsAdminRole(s.userID) + if err != nil { + log.Errorf("Error occured in check admin, error: %v", err) + s.CustomAbort(http.StatusInternalServerError, "Internal error.") + } + var projectList []models.Project + if isAdmin { + projectList, err = dao.GetAllProjects("") + } else { + projectList, err = dao.GetUserRelevantProjects(s.userID, "") + } + if err != nil { + log.Errorf("Error occured in QueryProject, error: %v", err) + s.CustomAbort(http.StatusInternalServerError, "Internal error.") + } + proMap := map[string]int{} + proMap["my_project_count"] = 0 + proMap["my_repo_count"] = 0 + proMap["public_project_count"] = 0 + proMap["public_repo_count"] = 0 + var publicProjects []models.Project + publicProjects, err = dao.GetPublicProjects("") + if err != nil { + log.Errorf("Error occured in QueryPublicProject, error: %v", err) + s.CustomAbort(http.StatusInternalServerError, "Internal error.") + } + proMap["public_project_count"] = len(publicProjects) + for i := 0; i < len(publicProjects); i++ { + proMap["public_repo_count"] += getRepoCountByProject(publicProjects[i].Name) + } + if isAdmin { + proMap["total_project_count"] = len(projectList) + proMap["total_repo_count"] = getTotalRepoCount() + } + for i := 0; i < len(projectList); i++ { + if isAdmin { + projectList[i].Role = models.PROJECTADMIN + } + if projectList[i].Role == models.PROJECTADMIN || projectList[i].Role == models.DEVELOPER || + projectList[i].Role == models.GUEST { + proMap["my_project_count"]++ + proMap["my_repo_count"] += getRepoCountByProject(projectList[i].Name) + } + } + s.Data["json"] = proMap + s.ServeJSON() +} + +//getReposByProject returns repo numbers of specified project +func getRepoCountByProject(projectName string) int { + repoList, err := svc_utils.GetRepoFromCache() + if err != nil { + log.Errorf("Failed to get repo from cache, error: %v", err) + return 0 + } + var resp int + if len(projectName) > 0 { + for _, r := range repoList { + if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName { + resp++ + } + } + return resp + } + return 0 +} + +//getTotalRepoCount returns total repo count +func getTotalRepoCount() int { + repoList, err := svc_utils.GetRepoFromCache() + if err != nil { + log.Errorf("Failed to get repo from cache, error: %v", err) + return 0 + } + return len(repoList) + +} diff --git a/controllers/ng/base.go b/controllers/ng/base.go index c0eb02c54..3b0b94fc6 100644 --- a/controllers/ng/base.go +++ b/controllers/ng/base.go @@ -8,7 +8,6 @@ import ( "github.com/astaxie/beego" "github.com/beego/i18n" - "github.com/vmware/harbor/dao" "github.com/vmware/harbor/utils/log" ) @@ -74,6 +73,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 == "" { @@ -82,28 +82,6 @@ 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) - - 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 - } func (bc *BaseController) Forward(title, templateName string) { @@ -120,6 +98,27 @@ func (bc *BaseController) Forward(title, templateName string) { var langTypes []*langType +type CommonController struct { + BaseController +} + +func (cc *CommonController) Render() error { + return nil +} + +func (cc *CommonController) LogOut() { + cc.DestroySession() +} + +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") diff --git a/controllers/ng/navigationheader.go b/controllers/ng/navigationheader.go new file mode 100644 index 000000000..69276bbb9 --- /dev/null +++ b/controllers/ng/navigationheader.go @@ -0,0 +1,37 @@ +package ng + +import ( + "net/http" + + "github.com/vmware/harbor/dao" + "github.com/vmware/harbor/models" + "github.com/vmware/harbor/utils/log" +) + +type NavigationHeaderController struct { + BaseController +} + +func (nhc *NavigationHeaderController) Get() { + sessionUserID := nhc.GetSession("userId") + var hasLoggedIn bool + var isAdmin int + if sessionUserID != nil { + hasLoggedIn = true + userID := sessionUserID.(int) + u, err := dao.GetUser(models.User{UserID: userID}) + if err != nil { + log.Errorf("Error occurred in GetUser, error: %v", err) + nhc.CustomAbort(http.StatusInternalServerError, "Internal error.") + } + if u == nil { + log.Warningf("User was deleted already, user id: %d, canceling request.", userID) + nhc.CustomAbort(http.StatusUnauthorized, "") + } + isAdmin = u.HasAdminRole + } + nhc.Data["HasLoggedIn"] = hasLoggedIn + nhc.Data["IsAdmin"] = isAdmin + nhc.TplName = "ng/navigation-header.htm" + nhc.Render() +} diff --git a/controllers/ng/optionalmenu.go b/controllers/ng/optionalmenu.go new file mode 100644 index 000000000..6945681dc --- /dev/null +++ b/controllers/ng/optionalmenu.go @@ -0,0 +1,37 @@ +package ng + +import ( + "net/http" + + "github.com/vmware/harbor/dao" + "github.com/vmware/harbor/models" + "github.com/vmware/harbor/utils/log" +) + +type OptionalMenuController struct { + BaseController +} + +func (omc *OptionalMenuController) Get() { + sessionUserID := omc.GetSession("userId") + + var hasLoggedIn bool + if sessionUserID != nil { + hasLoggedIn = true + userID := sessionUserID.(int) + u, err := dao.GetUser(models.User{UserID: userID}) + if err != nil { + log.Errorf("Error occurred in GetUser, error: %v", err) + omc.CustomAbort(http.StatusInternalServerError, "Internal error.") + } + if u == nil { + log.Warningf("User was deleted already, user id: %d, canceling request.", userID) + omc.CustomAbort(http.StatusUnauthorized, "") + } + omc.Data["Username"] = u.Username + } + omc.Data["HasLoggedIn"] = hasLoggedIn + omc.TplName = "ng/optional-menu.htm" + omc.Render() + +} diff --git a/controllers/ng/password.go b/controllers/ng/password.go index 9b16bba93..f6f169c4a 100644 --- a/controllers/ng/password.go +++ b/controllers/ng/password.go @@ -14,18 +14,6 @@ import ( "github.com/vmware/harbor/utils/log" ) -type CommonController struct { - BaseController -} - -func (cc *CommonController) Render() error { - return nil -} - -func (cc *CommonController) LogOut() { - cc.DestroySession() -} - type messageDetail struct { Hint string URL string diff --git a/controllers/ng/search.go b/controllers/ng/search.go new file mode 100644 index 000000000..341c864a0 --- /dev/null +++ b/controllers/ng/search.go @@ -0,0 +1,9 @@ +package ng + +type SearchController struct { + BaseController +} + +func (sc *SearchController) Get() { + sc.Forward("Search", "search.htm") +} diff --git a/dao/dao_test.go b/dao/dao_test.go index 0ef18faec..8e300740d 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -353,7 +353,7 @@ func TestChangeUserPasswordWithIncorrectOldPassword(t *testing.T) { } func TestQueryRelevantProjectsWhenNoProjectAdded(t *testing.T) { - projects, err := GetUserRelevantProjects(currentUser.UserID) + projects, err := SearchProjects(currentUser.UserID) if err != nil { t.Errorf("Error occurred in QueryRelevantProjects: %v", err) } @@ -572,39 +572,6 @@ func TestIsProjectPublic(t *testing.T) { } } -func TestQueryProject(t *testing.T) { - query1 := models.Project{ - UserID: 1, - } - projects, err := QueryProject(query1) - if err != nil { - t.Errorf("Error in Query Project: %v, query: %+v", err, query1) - } - if len(projects) != 2 { - t.Errorf("Expecting get 2 projects, but actual: %d, the list: %+v", len(projects), projects) - } - query2 := models.Project{ - Public: 1, - } - projects, err = QueryProject(query2) - if err != nil { - t.Errorf("Error in Query Project: %v, query: %+v", err, query2) - } - if len(projects) != 1 { - t.Errorf("Expecting get 1 project, but actual: %d, the list: %+v", len(projects), projects) - } - query3 := models.Project{ - UserID: 9, - } - projects, err = QueryProject(query3) - if err != nil { - t.Errorf("Error in Query Project: %v, query: %+v", err, query3) - } - if len(projects) != 0 { - t.Errorf("Expecting get 0 project, but actual: %d, the list: %+v", len(projects), projects) - } -} - func TestGetUserProjectRoles(t *testing.T) { r, err := GetUserProjectRoles(currentUser.UserID, currentProject.ProjectID) if err != nil { @@ -632,20 +599,20 @@ func TestProjectPermission(t *testing.T) { } func TestGetUserRelevantProjects(t *testing.T) { - projects, err := GetUserRelevantProjects(currentUser.UserID) + projects, err := GetUserRelevantProjects(currentUser.UserID, "") if err != nil { t.Errorf("Error occurred in GetUserRelevantProjects: %v", err) } - if len(projects) != 2 { - t.Errorf("Expected length of relevant projects is 2, but actual: %d, the projects: %+v", len(projects), projects) + if len(projects) != 1 { + t.Errorf("Expected length of relevant projects is 1, but actual: %d, the projects: %+v", len(projects), projects) } - if projects[1].Name != projectName { + if projects[0].Name != projectName { t.Errorf("Expected project name in the list: %s, actual: %s", projectName, projects[1].Name) } } func TestGetAllProjects(t *testing.T) { - projects, err := GetAllProjects() + projects, err := GetAllProjects("") if err != nil { t.Errorf("Error occurred in GetAllProjects: %v", err) } @@ -657,6 +624,19 @@ func TestGetAllProjects(t *testing.T) { } } +func TestGetPublicProjects(t *testing.T) { + projects, err := GetPublicProjects("") + if err != nil { + t.Errorf("Error occurred in getProjects: %v", err) + } + if len(projects) != 1 { + t.Errorf("Expected length of projects is 1, but actual: %d, the projects: %+v", len(projects), projects) + } + if projects[0].Name != "library" { + t.Errorf("Expected project name in the list: %s, actual: %s", "library", projects[0].Name) + } +} + func TestAddProjectMember(t *testing.T) { err := AddProjectMember(currentProject.ProjectID, 1, models.DEVELOPER) if err != nil { diff --git a/dao/project.go b/dao/project.go index 6e01b070d..730b1f331 100644 --- a/dao/project.go +++ b/dao/project.go @@ -79,42 +79,6 @@ func IsProjectPublic(projectName string) bool { return project.Public == 1 } -// QueryProject querys the projects based on publicity and user, disregarding the names etc. -func QueryProject(query models.Project) ([]models.Project, error) { - o := orm.NewOrm() - - sql := `select distinct - p.project_id, p.owner_id, p.name,p.creation_time, p.update_time, p.public - from project p - left join project_member pm on p.project_id = pm.project_id - where p.deleted = 0 ` - - queryParam := make([]interface{}, 1) - - if query.Public == 1 { - sql += ` and p.public = ?` - queryParam = append(queryParam, query.Public) - } else if isAdmin, _ := IsAdminRole(query.UserID); isAdmin == false { - sql += ` and (pm.user_id = ?) ` - queryParam = append(queryParam, query.UserID) - } - - if query.Name != "" { - sql += " and p.name like ? " - queryParam = append(queryParam, query.Name) - } - - sql += " order by p.name " - - var r []models.Project - _, err := o.Raw(sql, queryParam).QueryRows(&r) - - if err != nil { - return nil, err - } - return r, nil -} - //ProjectExists returns whether the project exists according to its name of ID. func ProjectExists(nameOrID interface{}) (bool, error) { o := orm.NewOrm() @@ -208,11 +172,11 @@ func ToggleProjectPublicity(projectID int64, publicity int) error { return err } -// GetUserRelevantProjects returns a project list, +// SearchProjects returns a project list, // which satisfies the following conditions: // 1. the project is not deleted // 2. the prject is public or the user is a member of the project -func GetUserRelevantProjects(userID int) ([]models.Project, error) { +func SearchProjects(userID int) ([]models.Project, error) { o := orm.NewOrm() sql := `select distinct p.project_id, p.name, p.public from project p @@ -228,14 +192,66 @@ func GetUserRelevantProjects(userID int) ([]models.Project, error) { return projects, nil } -// GetAllProjects returns all projects which are not deleted -func GetAllProjects() ([]models.Project, error) { +// GetUserRelevantProjects returns the projects of the user which are not deleted and name like projectName +func GetUserRelevantProjects(userID int, projectName string) ([]models.Project, error) { o := orm.NewOrm() - sql := `select project_id, name, public + sql := `select distinct + p.project_id, p.owner_id, p.name,p.creation_time, p.update_time, p.public, pm.role role + from project p + left join project_member pm on p.project_id = pm.project_id + where p.deleted = 0 and pm.user_id= ?` + + queryParam := make([]interface{}, 1) + queryParam = append(queryParam, userID) + if projectName != "" { + sql += " and p.name like ? " + queryParam = append(queryParam, projectName) + } + sql += " order by p.name " + var r []models.Project + _, err := o.Raw(sql, queryParam).QueryRows(&r) + if err != nil { + return nil, err + } + return r, nil +} + +//GetPublicProjects returns all public projects whose name like projectName +func GetPublicProjects(projectName string) ([]models.Project, error) { + publicProjects, err := getProjects(1, projectName) + if err != nil { + return nil, err + } + return publicProjects, nil +} + +// GetAllProjects returns all projects which are not deleted and name like projectName +func GetAllProjects(projectName string) ([]models.Project, error) { + allProjects, err := getProjects(0, projectName) + if err != nil { + return nil, err + } + return allProjects, nil +} + +func getProjects(public int, projectName string) ([]models.Project, error) { + o := orm.NewOrm() + sql := `select project_id, owner_id, creation_time, update_time, name, public from project where deleted = 0` + queryParam := make([]interface{}, 1) + if public == 1 { + sql += " and public = ? " + queryParam = append(queryParam, public) + } + if len(projectName) > 0 { + sql += " and name like ? " + queryParam = append(queryParam, projectName) + } + sql += " order by name " var projects []models.Project - if _, err := o.Raw(sql).QueryRows(&projects); err != nil { + log.Debugf("sql xxx", sql) + if _, err := o.Raw(sql, queryParam).QueryRows(&projects); err != nil { return nil, err } return projects, nil diff --git a/models/project.go b/models/project.go index e240c609f..2ebd5fc32 100644 --- a/models/project.go +++ b/models/project.go @@ -34,4 +34,6 @@ type Project struct { Togglable bool UpdateTime time.Time `orm:"update_time" json:"update_time"` + Role int `json:"role_id"` + RepoCount int `json:"repo_count"` } diff --git a/static/ng/resources/css/dashboard.css b/static/ng/resources/css/dashboard.css index 215e5dc27..aac91bac8 100644 --- a/static/ng/resources/css/dashboard.css +++ b/static/ng/resources/css/dashboard.css @@ -12,7 +12,3 @@ text-align: left; } -.single { - margin-right: 0; -} - diff --git a/static/ng/resources/css/header.css b/static/ng/resources/css/header.css index c669545cf..4d8f86da1 100644 --- a/static/ng/resources/css/header.css +++ b/static/ng/resources/css/header.css @@ -21,11 +21,7 @@ nav .container-custom { } .navbar-brand > img { - height: 60px; - width: 100px; margin-top: -30px; - filter: brightness(0) invert(1); - -webkit-filter: brightness(0) invert(1); } .navbar-form { @@ -40,21 +36,21 @@ nav .container-custom { } .nav-custom li { - float: left; - padding: 10px 0 0 0; - margin-right: 12px; - list-style: none; - display: inline-block; + float: left; + padding: 10px 0 0 0; + margin-right: 12px; + list-style: none; + display: inline-block; } .nav-custom li a { - font-size: 14px; - color: #FFFFFF; - text-decoration: none; + font-size: 14px; + color: #FFFFFF; + text-decoration: none; } .nav-custom .active { - border-bottom: 3px solid #EFEFEF; + border-bottom: 3px solid #EFEFEF; } .dropdown { diff --git a/static/ng/resources/css/search.css b/static/ng/resources/css/search.css new file mode 100644 index 000000000..d2b2b9f27 --- /dev/null +++ b/static/ng/resources/css/search.css @@ -0,0 +1,9 @@ +.search-result { + min-height: 200px; + max-height: 200px; + overflow-y: auto; +} + +.search-result li { + margin-bottom: 15px; +} \ No newline at end of file diff --git a/static/ng/resources/img/Harbor_Logo_rec.png b/static/ng/resources/img/Harbor_Logo_rec.png old mode 100755 new mode 100644 index cccdbedf1..3a603109e Binary files a/static/ng/resources/img/Harbor_Logo_rec.png and b/static/ng/resources/img/Harbor_Logo_rec.png differ diff --git a/static/ng/resources/js/components/details/retrieve-projects.directive.html b/static/ng/resources/js/components/details/retrieve-projects.directive.html index 573d78b4e..8e1e90491 100644 --- a/static/ng/resources/js/components/details/retrieve-projects.directive.html +++ b/static/ng/resources/js/components/details/retrieve-projects.directive.html @@ -1,10 +1,10 @@ -
+
- +
diff --git a/static/ng/resources/js/components/details/retrieve-projects.directive.js b/static/ng/resources/js/components/details/retrieve-projects.directive.js index 3a0c6ba2a..8d41385a6 100644 --- a/static/ng/resources/js/components/details/retrieve-projects.directive.js +++ b/static/ng/resources/js/components/details/retrieve-projects.directive.js @@ -12,6 +12,7 @@ var vm = this; vm.projectName = ''; + vm.isOpen = false; if($route.current.params.is_public) { vm.isPublic = $route.current.params.is_public === 'true' ? 1 : 0; @@ -19,8 +20,6 @@ } vm.retrieve = retrieve; - vm.retrieve(); - vm.checkProjectMember = checkProjectMember; $scope.$watch('vm.selectedProject', function(current, origin) { @@ -33,7 +32,7 @@ vm.selectItem = selectItem; $scope.$watch('vm.publicity', function(current, origin) { - vm.publicity = current ? 1 : 0; + vm.publicity = current ? true : false; vm.isPublic = vm.publicity ? 1 : 0; vm.projectType = (vm.isPublic === 1) ? 'public_projects' : 'my_projects'; vm.retrieve(); @@ -64,9 +63,8 @@ } }); } - - $location.search('project_id', vm.selectedProject.ProjectId); vm.checkProjectMember(vm.selectedProject.ProjectId); + $location.search('project_id', vm.selectedProject.ProjectId); vm.resultCount = vm.projects.length; $scope.$watch('vm.filterInput', function(current, origin) { @@ -114,6 +112,7 @@ 'publicity': '=', 'isProjectMember': '=' }, + link: link, replace: true, controller: RetrieveProjectsController, bindToController: true, @@ -122,6 +121,22 @@ return directive; + function link(scope, element, attrs, ctrl) { + $(document).on('click', clickHandler); + + function clickHandler(e) { + var targetId = $(e.target).attr('id'); + if(targetId === 'switchPane' || + targetId === 'retrievePane' || + targetId === 'retrieveFilter') { + return; + }else{ + ctrl.isOpen = false; + scope.$apply(); + } + } + } + } })(); \ No newline at end of file diff --git a/static/ng/resources/js/components/details/switch-pane-projects.directive.html b/static/ng/resources/js/components/details/switch-pane-projects.directive.html index 5158a1088..996b666de 100644 --- a/static/ng/resources/js/components/details/switch-pane-projects.directive.html +++ b/static/ng/resources/js/components/details/switch-pane-projects.directive.html @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/static/ng/resources/js/components/details/switch-pane-projects.directive.js b/static/ng/resources/js/components/details/switch-pane-projects.directive.js index da6725a6f..1a10dd70e 100644 --- a/static/ng/resources/js/components/details/switch-pane-projects.directive.js +++ b/static/ng/resources/js/components/details/switch-pane-projects.directive.js @@ -10,11 +10,7 @@ function SwitchPaneProjectsController($scope) { var vm = this; - - $scope.$on('isOpen', function(e, val){ - vm.isOpen = val; - }); - + $scope.$watch('vm.selectedProject', function(current, origin) { if(current){ vm.projectName = current.Name; @@ -40,21 +36,16 @@ templateUrl: '/static/ng/resources/js/components/details/switch-pane-projects.directive.html', replace: true, scope: { - 'selectedProject': '=', - 'isOpen': '=' + 'isOpen': '=', + 'selectedProject': '=' }, - link: link, controller: SwitchPaneProjectsController, controllerAs: 'vm', bindToController: true } return directive; - - function link(scope, element, attrs, ctrl) { - - } - + } })(); \ No newline at end of file diff --git a/static/ng/resources/js/components/optional-menu/optional-menu.directive.js b/static/ng/resources/js/components/optional-menu/optional-menu.directive.js index a490b5e8d..8d30ee06c 100644 --- a/static/ng/resources/js/components/optional-menu/optional-menu.directive.js +++ b/static/ng/resources/js/components/optional-menu/optional-menu.directive.js @@ -6,28 +6,32 @@ .module('harbor.optional.menu') .directive('optionalMenu', optionalMenu); - OptionalMenuController.$inject = ['$scope', '$window', '$cookies', 'I18nService', 'LogOutService']; + OptionalMenuController.$inject = ['$window', 'I18nService', 'LogOutService', 'currentUser', '$timeout']; - function OptionalMenuController($scope, $window, $cookies, I18nService, LogOutService) { + function OptionalMenuController($window, I18nService, LogOutService, currentUser, $timeout) { var vm = this; + vm.currentLanguage = I18nService().getCurrentLanguage(); - vm.setLanguage = setLanguage; vm.languageName = I18nService().getLanguageName(vm.currentLanguage); + console.log('current language:' + I18nService().getCurrentLanguage()); + vm.user = currentUser.get(); + vm.setLanguage = setLanguage; vm.logOut = logOut; - function setLanguage(name) { - I18nService().setCurrentLanguage(name); - $window.location.reload(); + function setLanguage(language) { + I18nService().setCurrentLanguage(language); + $window.location.href = '/ng/language?lang=' + language; } - function logOut() { LogOutService() .success(logOutSuccess) .error(logOutFailed); } function logOutSuccess(data, status) { + currentUser.unset(); + I18nService().unset(); $window.location.href= '/ng'; } function logOutFailed(data, status) { @@ -38,24 +42,13 @@ function optionalMenu() { var directive = { 'restrict': 'E', - 'templateUrl': '/static/ng/resources/js/components/optional-menu/optional-menu.directive.html', - 'link': link, + 'templateUrl': '/ng/optional_menu', 'scope': true, 'controller': OptionalMenuController, 'controllerAs': 'vm', 'bindToController': true }; return directive; - function link(scope, element, attrs, ctrl) { - ctrl.isLoggedIn = false; - scope.$on('currentUser', function(e, val) { - if(val != null) { - ctrl.isLoggedIn = true; - ctrl.username = val.username; - } - scope.$apply(); - }); - } } })(); \ No newline at end of file diff --git a/static/ng/resources/js/components/project-member/edit-project-member.directive.js b/static/ng/resources/js/components/project-member/edit-project-member.directive.js index 752a9db6e..fc1a1626d 100644 --- a/static/ng/resources/js/components/project-member/edit-project-member.directive.js +++ b/static/ng/resources/js/components/project-member/edit-project-member.directive.js @@ -74,16 +74,11 @@ 'projectId': '=', 'reload': '&' }, - 'link': link, 'controller': EditProjectMemberController, 'controllerAs': 'vm', 'bindToController': true }; return directive; - - function link(scope, element, attrs, ctrl) { - - } } })(); \ No newline at end of file diff --git a/static/ng/resources/js/components/project-member/list-project-member.directive.html b/static/ng/resources/js/components/project-member/list-project-member.directive.html index 857dbe71d..e126cdc90 100644 --- a/static/ng/resources/js/components/project-member/list-project-member.directive.html +++ b/static/ng/resources/js/components/project-member/list-project-member.directive.html @@ -18,7 +18,7 @@ // 'username' | tr //// 'role' | tr //// 'operation' | tr // - +
diff --git a/static/ng/resources/js/components/project-member/list-project-member.directive.js b/static/ng/resources/js/components/project-member/list-project-member.directive.js index 42bb95485..c07c666c1 100644 --- a/static/ng/resources/js/components/project-member/list-project-member.directive.js +++ b/static/ng/resources/js/components/project-member/list-project-member.directive.js @@ -10,24 +10,19 @@ function ListProjectMemberController($scope, ListProjectMemberService, $routeParams, currentUser) { var vm = this; - - - vm.isOpen = false; - + + vm.isOpen = false; vm.search = search; vm.addProjectMember = addProjectMember; vm.retrieve = retrieve; - vm.projectId = $routeParams.project_id; vm.username = ""; - vm.currentUser = {}; - + vm.retrieve(); function search(e) { vm.projectId = e.projectId; vm.username = e.username; - console.log('project_id:' + e.projectId); retrieve(); } @@ -46,7 +41,7 @@ } function getProjectMemberComplete(response) { - vm.currentUser = currentUser.get(); + vm.user = currentUser.get(); vm.projectMembers = response.data; } @@ -62,17 +57,11 @@ templateUrl: '/static/ng/resources/js/components/project-member/list-project-member.directive.html', replace: true, scope: true, - link: link, controller: ListProjectMemberController, controllerAs: 'vm', bindToController: true - } - + } return directive; - - function link(scope, element, attrs, ctrl) { - - } } })(); \ No newline at end of file diff --git a/static/ng/resources/js/components/repository/list-repository.directive.js b/static/ng/resources/js/components/repository/list-repository.directive.js index a2d4eb3f6..e3362117c 100644 --- a/static/ng/resources/js/components/repository/list-repository.directive.js +++ b/static/ng/resources/js/components/repository/list-repository.directive.js @@ -5,12 +5,23 @@ .module('harbor.repository') .directive('listRepository', listRepository); - ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$routeParams', '$filter', 'trFilter']; + ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$routeParams', '$filter', 'trFilter', '$location']; - function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $routeParams, $filter, trFilter) { + function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $routeParams, $filter, trFilter, $location) { var vm = this; + + vm.filterInput = ''; + + var hashValue = $location.hash(); + if(hashValue) { + var slashIndex = hashValue.indexOf('/'); + if(slashIndex >=0) { + vm.filterInput = hashValue.substring(slashIndex + 1); + }else{ + vm.filterInput = hashValue; + } + } - vm.filterInput = ""; vm.retrieve = retrieve; vm.projectId = $routeParams.project_id; vm.tagCount = {}; @@ -84,18 +95,13 @@ restrict: 'E', templateUrl: '/static/ng/resources/js/components/repository/list-repository.directive.html', replace: true, - link: 'link', controller: ListRepositoryController, controllerAs: 'vm', bindToController: true } return directive; - - function link(scope, element, attrs, ctrl) { - - } - + } })(); \ No newline at end of file diff --git a/static/ng/resources/js/components/search/search-input.directive.html b/static/ng/resources/js/components/search/search-input.directive.html new file mode 100644 index 000000000..c60629955 --- /dev/null +++ b/static/ng/resources/js/components/search/search-input.directive.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/static/ng/resources/js/components/search/search-input.directive.js b/static/ng/resources/js/components/search/search-input.directive.js new file mode 100644 index 000000000..fe9eaaac2 --- /dev/null +++ b/static/ng/resources/js/components/search/search-input.directive.js @@ -0,0 +1,54 @@ +(function() { + + 'use strict'; + + angular + .module('harbor.search') + .directive('searchInput', searchInput); + + SearchInputController.$inject = ['$scope', '$location', '$window']; + + function SearchInputController($scope, $location, $window) { + var vm = this; + + vm.searchFor = searchFor; + + function searchFor(searchContent) { + $location + .path('/ng/search') + .search({'q': searchContent}); + $window.location.href = $location.url(); + } + + } + + function searchInput() { + + var directive = { + 'restrict': 'E', + 'templateUrl': '/static/ng/resources/js/components/search/search-input.directive.html', + 'scope': { + 'searchInput': '=', + }, + 'link': link, + 'controller': SearchInputController, + 'controllerAs': 'vm', + 'bindToController': true + }; + return directive; + + function link(scope, element, attrs, ctrl) { + element + .find('input[type="text"]') + .on('keydown', keydownHandler); + + function keydownHandler(e) { + if(e.keyCode === 13) { + ctrl.searchFor($(this).val()); + } + } + + } + } + +})(); \ No newline at end of file diff --git a/static/ng/resources/js/components/sign-in/sign-in.directive.html b/static/ng/resources/js/components/sign-in/sign-in.directive.html index f7e49a24b..a293a5f84 100644 --- a/static/ng/resources/js/components/sign-in/sign-in.directive.html +++ b/static/ng/resources/js/components/sign-in/sign-in.directive.html @@ -1,9 +1,9 @@
- +
-
+
// 'username_is_required' | tr //
@@ -11,9 +11,9 @@
- +
-
+
// 'password_is_required' | tr //
// vm.errorMessage | tr // @@ -23,7 +23,7 @@
- +
diff --git a/static/ng/resources/js/harbor.config.js b/static/ng/resources/js/harbor.config.js index b7f8990de..d256c5cf2 100644 --- a/static/ng/resources/js/harbor.config.js +++ b/static/ng/resources/js/harbor.config.js @@ -9,9 +9,22 @@ .config(function($httpProvider) { $httpProvider.defaults.headers.common = {'Accept': 'application/json, text/javascript, */*; q=0.01'}; }) + .factory('getParameterByName', getParameterByName) .filter('dateL', localizeDate) .filter('tr', tr); - + + function getParameterByName() { + return get; + function get(name, url) { + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); + } + } + function localizeDate() { return filter; diff --git a/static/ng/resources/js/harbor.data.js b/static/ng/resources/js/harbor.data.js index e06481ea6..30f977df7 100644 --- a/static/ng/resources/js/harbor.data.js +++ b/static/ng/resources/js/harbor.data.js @@ -1,36 +1,40 @@ (function() { + 'use strict'; + angular - .module('harbor.app') + .module('harbor.app') .factory('currentUser', currentUser) - .factory('projectMember', projectMember); - - function currentUser() { - var currentUser; + .factory('currentProjectMember', currentProjectMember); + + currentUser.$inject = ['$cookies', '$timeout']; + + function currentUser($cookies, $timeout) { return { set: function(user) { - currentUser = user; - console.log('set currentUser:' + currentUser); + $cookies.putObject('user', user, {'path': '/'}); }, get: function() { - console.log('get currentUser:' + currentUser); - return currentUser; + return $cookies.getObject('user'); + }, + unset: function() { + $cookies.remove('user', {'path': '/'}); } } } - function projectMember() { - var projectMember; + currentProjectMember.$inject = ['$cookies']; + + function currentProjectMember($cookies) { return { set: function(member) { - projectMember = member; - console.log('set projectMember:'); - console.log(projectMember); + $cookies.putObject('member', member, {'path': '/'}); }, get: function() { - console.log('get projectMember:'); - console.log(projectMember); - return projectMember; + return $cookies.getObject('member'); + }, + unset: function() { + $cookies.remove('member', {'path': '/'}); } } } diff --git a/static/ng/resources/js/harbor.module.js b/static/ng/resources/js/harbor.module.js index f0f190da6..0a18b1dd2 100644 --- a/static/ng/resources/js/harbor.module.js +++ b/static/ng/resources/js/harbor.module.js @@ -21,6 +21,7 @@ 'harbor.layout.system.management', 'harbor.layout.log', 'harbor.layout.admin.option', + 'harbor.layout.search', 'harbor.services.i18n', 'harbor.services.project', 'harbor.services.user', diff --git a/static/ng/resources/js/layout/details/details.controller.js b/static/ng/resources/js/layout/details/details.controller.js index c414a4ad0..096e009f0 100644 --- a/static/ng/resources/js/layout/details/details.controller.js +++ b/static/ng/resources/js/layout/details/details.controller.js @@ -5,23 +5,17 @@ angular .module('harbor.details') .controller('DetailsController', DetailsController); - - DetailsController.$inject = ['$scope', '$location', '$routeParams']; - - function DetailsController($scope, $location, $routeParams) { + + function DetailsController() { var vm = this; - vm.isOpen = false; + vm.publicity = false; - vm.isProjectMember = true; - vm.closeRetrievePane = closeRetrievePane; + vm.isProjectMember = false; + vm.togglePublicity = togglePublicity; - function closeRetrievePane() { - $scope.$broadcast('isOpen', false); - } function togglePublicity(e) { vm.publicity = e.publicity; - console.log('current project publicity:' + vm.publicity); } } diff --git a/static/ng/resources/js/layout/header/header.controller.js b/static/ng/resources/js/layout/header/header.controller.js index fa28cddc6..754a9d3d0 100644 --- a/static/ng/resources/js/layout/header/header.controller.js +++ b/static/ng/resources/js/layout/header/header.controller.js @@ -6,10 +6,14 @@ .module('harbor.layout.header') .controller('HeaderController', HeaderController); - HeaderController.$inject = ['$scope', 'I18nService', '$cookies', '$window']; + HeaderController.$inject = ['$scope', '$window', 'getParameterByName']; - function HeaderController($scope, I18nService, $cookies, $window) { - + function HeaderController($scope, $window, getParameterByName) { + var vm = this; + if($window.location.search) { + vm.searchInput = getParameterByName('q', $window.location.search); + console.log('vm.searchInput at header:' + vm.searchInput); + } } })(); \ No newline at end of file diff --git a/static/ng/resources/js/layout/navigation/navigation-details.directive.js b/static/ng/resources/js/layout/navigation/navigation-details.directive.js index 0372f516f..8c34b40a6 100644 --- a/static/ng/resources/js/layout/navigation/navigation-details.directive.js +++ b/static/ng/resources/js/layout/navigation/navigation-details.directive.js @@ -18,14 +18,6 @@ }); vm.url = $location.url(); - vm.clickTab = clickTab; - - function clickTab() { - console.log("triggered clickTab of Controller."); - vm.isOpen = false; - $scope.$apply(); - } - } function navigationDetails() { @@ -34,7 +26,6 @@ templateUrl: '/static/ng/resources/js/layout/navigation/navigation-details.directive.html', link: link, scope: { - 'isOpen': '=', 'selectedProject': '=' }, replace: true, @@ -66,7 +57,6 @@ function click(event) { element.find('a').removeClass('active'); $(event.target).addClass('active'); - ctrl.clickTab(); } } diff --git a/static/ng/resources/js/layout/navigation/navigation-header.directive.js b/static/ng/resources/js/layout/navigation/navigation-header.directive.js index ccdef15f5..974f19d22 100644 --- a/static/ng/resources/js/layout/navigation/navigation-header.directive.js +++ b/static/ng/resources/js/layout/navigation/navigation-header.directive.js @@ -6,24 +6,17 @@ .module('harbor.layout.navigation') .directive('navigationHeader', navigationHeader); - NavigationHeaderController.$inject = ['$window', '$scope']; + NavigationHeaderController.$inject = ['$window', '$scope', 'currentUser', '$timeout']; - function NavigationHeaderController($window, $scope) { + function NavigationHeaderController($window, $scope, currentUser, $timeout) { var vm = this; - vm.url = $window.location.pathname; - vm.isAdmin = false; - $scope.$on('currentUser', function(e, val) { - if(val.HasAdminRole === 1) { - vm.isAdmin = true; - } - $scope.$apply(); - }); + vm.url = $window.location.pathname; } function navigationHeader() { var directive = { restrict: 'E', - templateUrl: '/static/ng/resources/js/layout/navigation/navigation-header.directive.html', + templateUrl: '/ng/navigation_header', link: link, scope: true, controller: NavigationHeaderController, @@ -34,12 +27,11 @@ return directive; function link(scope, element, attrs, ctrl) { - element.find('a').removeClass('active'); var visited = ctrl.url; if (visited != "/ng") { element.find('a[href*="' + visited + '"]').addClass('active'); } - element.on('click', click); + element.find('a').on('click', click); function click(event) { element.find('a').removeClass('active'); $(event.target).not('span').addClass('active'); diff --git a/static/ng/resources/js/layout/project-member/project-member.controller.js b/static/ng/resources/js/layout/project-member/project-member.controller.js index 9d55143ae..ec977697d 100644 --- a/static/ng/resources/js/layout/project-member/project-member.controller.js +++ b/static/ng/resources/js/layout/project-member/project-member.controller.js @@ -5,9 +5,7 @@ angular .module('harbor.layout.project.member') .controller('ProjectMemberController', ProjectMemberController); - - ProjectMemberController.$inject = ['$scope']; - + function ProjectMemberController($scope) { } diff --git a/static/ng/resources/js/layout/project/project.controller.js b/static/ng/resources/js/layout/project/project.controller.js index d6f8334ba..3ed2d3df4 100644 --- a/static/ng/resources/js/layout/project/project.controller.js +++ b/static/ng/resources/js/layout/project/project.controller.js @@ -6,9 +6,9 @@ .module('harbor.layout.project') .controller('ProjectController', ProjectController); - ProjectController.$inject = ['$scope', 'ListProjectService']; + ProjectController.$inject = ['$scope', 'ListProjectService', '$timeout', 'currentUser']; - function ProjectController($scope, ListProjectService) { + function ProjectController($scope, ListProjectService, $timeout, currentUser) { var vm = this; vm.isOpen = false; @@ -21,8 +21,8 @@ vm.showAddButton = showAddButton; vm.togglePublicity = togglePublicity; - $scope.$on('currentUser', function(e, val) { - vm.currentUser = val; + $timeout(function() { + vm.user = currentUser.get(); }); vm.retrieve(); diff --git a/static/ng/resources/js/layout/reset-password/reset-password.controller.js b/static/ng/resources/js/layout/reset-password/reset-password.controller.js index 23ada471d..6b8c91e2b 100644 --- a/static/ng/resources/js/layout/reset-password/reset-password.controller.js +++ b/static/ng/resources/js/layout/reset-password/reset-password.controller.js @@ -6,18 +6,9 @@ .module('harbor.layout.reset.password') .controller('ResetPasswordController', ResetPasswordController); - ResetPasswordController.$inject = ['$location', 'ResetPasswordService', '$window']; + ResetPasswordController.$inject = ['$location', 'ResetPasswordService', '$window', 'getParameterByName']; - function getParameterByName(name, url) { - name = name.replace(/[\[\]]/g, "\\$&"); - var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return null; - if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } - - function ResetPasswordController($location, ResetPasswordService, $window) { + function ResetPasswordController($location, ResetPasswordService, $window, getParameterByName) { var vm = this; vm.resetUuid = getParameterByName('reset_uuid', $location.absUrl()); diff --git a/static/ng/resources/js/layout/search/search.controller.js b/static/ng/resources/js/layout/search/search.controller.js new file mode 100644 index 000000000..d0c5d4e41 --- /dev/null +++ b/static/ng/resources/js/layout/search/search.controller.js @@ -0,0 +1,31 @@ +(function() { + + 'use strict'; + + angular + .module('harbor.layout.search') + .controller('SearchController', SearchController); + + SearchController.$inject = ['$window', 'SearchService']; + + function SearchController($window, SearchService) { + var vm = this; + if($window.location.search) { + vm.q = $window.location.search.split('=')[1]; + console.log('vm.q:' + vm.q); + SearchService(vm.q) + .success(searchSuccess) + .error(searchFailed); + } + + function searchSuccess(data, status) { + vm.repository = data['repository']; + vm.project = data['project']; + } + + function searchFailed(data, status) { + console.log('Failed search:' + data); + } + } + +})(); \ No newline at end of file diff --git a/static/ng/resources/js/layout/search/search.module.js b/static/ng/resources/js/layout/search/search.module.js new file mode 100644 index 000000000..e40971e69 --- /dev/null +++ b/static/ng/resources/js/layout/search/search.module.js @@ -0,0 +1,9 @@ +(function() { + + 'use strict'; + + angular + .module('harbor.layout.search', []); + + +})(); \ No newline at end of file diff --git a/static/ng/resources/js/services/i18n/locale_messages.js b/static/ng/resources/js/services/i18n/locale_messages.js deleted file mode 100644 index 1ddfc87b2..000000000 --- a/static/ng/resources/js/services/i18n/locale_messages.js +++ /dev/null @@ -1,468 +0,0 @@ -var global_messages = { - 'sign_in': { - 'en-US': 'Sign In', - 'zh-CN': '登录' - }, - 'sign_up': { - 'en-US': 'Sign Up', - 'zh-CN': '注册' - }, - 'forgot_password': { - 'en-US': 'Forgot Password', - 'zh-CN': '忘记密码' - }, - 'login_now': { - 'en-US': 'Login Now', - 'zh-CN': '立刻登录' - }, - 'its_easy_to_get_started': { - 'en-US': 'It\'s easy to get started ...', - 'zh-CN': '这很容易上手...' - }, - 'anybody_can_read_public_projects': { - 'en-US': 'Anybody can read public projects', - 'zh-CN': '任何人都可以访问公开的项目' - }, - 'create_projects_and_connect_repositories': { - 'en-US': 'Create projects and connect repositories', - 'zh-CN': '创建项目并关联镜像仓库' - }, - 'user_management_and_role_assignment': { - 'en-US': 'User management and role assignment', - 'zh-CN': '用户管理和角色分派' - }, - 'why_use_harbor': { - 'en-US': 'Why use Harbor?', - 'zh-CN': '为什么要使用Harbor?' - }, - 'index_desc': { - 'en-US': '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.', - 'zh-CN': 'Harbor是可靠的企业级Registry服务器。企业用户可使用Harbor搭建私有容器Registry服务,提高生产效率和安全度,既可应用于生产环境,也可以在开发环境中使用。' - }, - 'index_desc_1': { - 'en-US': 'Security: Keep their intellectual properties within their organizations.', - 'zh-CN': '安全: 确保知识产权在自己组织内部的管控之下。' - }, - 'index_desc_2': { - 'en-US': 'Efficiency: A private registry server is set up within the organization\'s network and can reduce significantly the internet traffic to the public service. ', - 'zh-CN': '效率: 搭建组织内部的私有容器Registry服务,可显著降低访问公共Registry服务的网络需求。' - }, - 'index_desc_3': { - 'en-US': 'Access Control: RBAC (Role Based Access Control) is provided. User management can be integrated with existing enterprise identity services like AD/LDAP. ', - 'zh-CN': '访问控制: 提供基于角色的访问控制,可集成企业目前拥有的用户管理系统(如:AD/LDAP)。 ' - }, - 'index_desc_4': { - 'en-US': 'Audit: All access to the registry are logged and can be used for audit purpose.', - 'zh-CN': '审计: 所有访问Registry服务的操作均被记录,便于日后审计。' - }, - 'index_desc_5': { - 'en-US': 'GUI: User friendly single-pane-of-glass management console.', - 'zh-CN': '管理界面: 具有友好易用图形管理界面。' - }, - 'learn_more': { - 'en-US': 'Learn more...', - 'zh-CN': '更多...' - }, - 'repositories': { - 'en-US': 'Repositories', - 'zh-CN': '镜像仓库' - }, - 'project_repo_name': { - 'en-US': 'Project/Repository Name', - 'zh-CN': '项目/镜像仓库名称' - }, - 'creation_time': { - 'en-US': 'Creation Time', - 'zh-CN': '创建时间' - }, - 'author': { - 'en-US': 'Author', - 'zh-CN': '作者' - }, - 'username': { - 'en-US': 'Username', - 'zh-CN': '用户名' - }, - 'username_is_required' : { - 'en-US': 'Username is required.', - 'zh-CN': '用户名为必填项。' - }, - 'username_has_been_taken' : { - 'en-US': 'Username has been taken.', - 'zh-CN': '用户名已被占用。' - }, - 'username_is_too_long' : { - 'en-US': 'Username is too long. (maximum 20 characters)', - 'zh-CN': '用户名长度超出限制。(最长为20个字符)' - }, - 'username_contains_illegal_chars': { - 'en-US': 'Username contains illegal character(s).', - 'zh-CN': '用户名包含不合法的字符。' - }, - 'email': { - 'en-US': 'Email', - 'zh-CN': '邮箱' - }, - 'email_desc': { - 'en-US': 'The Email address will be used for resetting password.', - 'zh-CN': '此邮箱将用于重置密码。' - }, - 'email_is_required' : { - 'en-US': 'Email is required.', - 'zh-CN': '邮箱为必填项。' - }, - 'email_has_been_taken' : { - 'en-US': 'Email has been taken.', - 'zh-CN': '邮箱已被占用。' - }, - 'email_content_illegal' : { - 'en-US': 'Email format is illegal.', - 'zh-CN': '邮箱格式不合法。' - }, - 'email_does_not_exist' : { - 'en-US': 'Email does not exist.', - 'zh-CN': '邮箱不存在。' - }, - 'email_is_too_long': { - 'en-US': 'Email is to long. (maximum 50 characters)', - 'zh-CN': '邮箱名称长度超出限制。(最长为50个字符)' - }, - 'full_name': { - 'en-US': 'Full Name', - 'zh-CN': '全名' - }, - 'full_name_desc': { - 'en-US': 'First name & Last name', - 'zh-CN': '请输入全名。' - }, - 'full_name_is_required' : { - 'en-US': 'Full name is required.', - 'zh-CN': '全名为必填项。' - }, - 'full_name_is_too_long' : { - 'en-US': 'Full name is too long. (maximum 20 characters)', - 'zh-CN': '全名长度超出限制。(最长为20个字符)' - }, - 'full_name_contains_illegal_chars' : { - 'en-US': 'Full name contains illegal character(s).', - 'zh-CN': '全名包含不合法的字符。' - }, - 'password': { - 'en-US': 'Password', - 'zh-CN': '密码' - }, - 'password_desc': { - 'en-US': 'At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.', - 'zh-CN': '至少输入 7个字符且包含 1个小写字母, 1个大写字母和 1个数字。' - }, - 'password_is_required' : { - 'en-US': 'Password is required.', - 'zh-CN': '密码为必填项。' - }, - '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个数字。' - }, - 'confirm_password': { - 'en-US': 'Confirm Password', - 'zh-CN': '确认密码' - }, - 'password_does_not_match' : { - 'en-US': 'Passwords do not match.', - 'zh-CN': '两次密码输入不一致。' - }, - 'comments': { - 'en-US': 'Comments', - 'zh-CN': '备注' - }, - 'comment_is_too_long' : { - 'en-US': 'Comment is too long. (maximum 20 characters)', - 'zh-CN': '备注长度超出限制。(最长为20个字符)' - }, - 'forgot_password_description': { - 'en-US': 'Please input the Email used when you signed up, a reset password Email will be sent to you.', - 'zh-CN': '重置邮件将发送到此邮箱。' - }, - 'email_does_not_exist': { - 'en-US': 'Email does not exist', - 'zh-CN': '邮箱不存在。' - }, - 'reset_password': { - 'en-US': 'Reset Password', - 'zh-CN': '重置密码' - }, - 'summary': { - 'en-US': 'Summary', - 'zh-CN': '摘要' - }, - 'projects': { - 'en-US': 'Projects', - 'zh-CN': '项目' - }, - 'public_projects': { - 'en-US': 'Public Projects', - 'zh-CN': '公开项目' - }, - 'public': { - 'en-US': 'Public', - 'zh-CN': '公开' - }, - 'total_projects': { - 'en-US': 'Total Projects', - 'zh-CN': '全部项目' - }, - 'public_repositories': { - 'en-US': 'Public Repositories', - 'zh-CN': '公开镜像仓库' - }, - 'total_repositories': { - 'en-US': 'Total Repositories', - 'zh-CN': '全部镜像仓库' - }, - 'top_10_repositories': { - 'en-US': 'Top 10 Repositories', - 'zh-CN': 'Top 10 镜像仓库' - }, - 'repository_name': { - 'en-US': 'Repository Name', - 'zh-CN': '镜像仓库名' - }, - 'size': { - 'en-US': 'Size', - 'zh-CN': '规格' - }, - 'creator': { - 'en-US': 'Creator', - 'zh-CN': '创建者' - }, - 'logs': { - 'en-US': 'Logs', - 'zh-CN': '日志' - }, - 'task_name': { - 'en-US': 'Task Name', - 'zh-CN': '任务名称' - }, - 'details': { - 'en-US': 'Details', - 'zh-CN': '详细信息' - }, - 'user': { - 'en-US': 'User', - 'zh-CN': '用户' - }, - 'users': { - 'en-US': 'Users', - 'zh-CN': '用户' - }, - 'my_projects': { - 'en-US': 'My Projects', - 'zh-CN': '我的项目' - }, - 'project_name': { - 'en-US': 'Project Name', - 'zh-CN': '项目名称' - }, - 'role': { - 'en-US': 'Role', - 'zh-CN': '角色' - }, - 'publicity': { - 'en-US': 'Publicity', - 'zh-CN': '公开' - }, - 'button_on': { - 'en-US': 'On', - 'zh-CN': '打开' - }, - 'button_off': { - 'en-US': 'Off', - 'zh-CN': '关闭' - }, - 'new_project': { - 'en-US': 'New Project', - 'zh-CN': '新增项目' - }, - 'save': { - 'en-US': 'Save', - 'zh-CN': '保存' - }, - 'cancel': { - 'en-US': 'Cancel', - 'zh-CN': '取消' - }, - 'items': { - 'en-US': 'items', - 'zh-CN': '条记录' - }, - 'add_member': { - 'en-US': 'Add Member', - 'zh-CN': '新增成员' - }, - 'operation': { - 'en-US': 'Operation', - 'zh-CN': '操作' - }, - 'advanced_search': { - 'en-US': 'Advanced Search', - 'zh-CN': '高级搜索' - }, - 'all': { - 'en-US': 'All', - 'zh-CN': '全部' - }, - 'others': { - 'en-US': 'Others', - 'zh-CN': '其他' - }, - 'search': { - 'en-US': 'Search', - 'zh-CN': '搜索' - }, - 'duration': { - 'en-US': 'Duration', - 'zh-CN': '持续时间' - }, - 'from': { - 'en-US': 'From', - 'zh-CN': '起始' - }, - 'to': { - 'en-US': 'To', - 'zh-CN': '结束' - }, - 'timestamp': { - 'en-US': 'Timestamp', - 'zh-CN': '时间戳' - }, - 'dashboard': { - 'en-US': 'Dashboard', - 'zh-CN': '消息中心' - }, - 'admin_options': { - 'en-US': 'Admin Options', - 'zh-CN': '管理员选项' - }, - 'account_setting': { - 'en-US': 'Account Setting', - 'zh-CN': '个人设置' - }, - 'log_out': { - 'en-US': 'Log Out', - 'zh-CN': '注销' - }, - 'registration_time': { - 'en-US': 'Registration Time', - 'zh-CN': '注册时间' - }, - 'system_management': { - 'en-US': 'System Management', - 'zh-CN': '系统管理' - }, - 'change_password': { - 'en-US': 'Change Password', - 'zh-CN': '修改密码' - }, - 'old_password': { - 'en-US': 'Old Password', - 'zh-CN': '原密码' - }, - 'old_password_is_required': { - 'en-US': 'Old password is required.', - 'zh-CN': '原密码为必填项。' - }, - 'old_password_is_incorrect': { - 'en-US': 'Old password is incorrect.', - 'zh-CN': '原密码不正确。' - }, - 'new_password_is_required': { - 'en-US': 'New password is required.', - 'zh-CN': '新密码为必填项。' - }, - 'new_password_is_invalid': { - 'en-US': 'New password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.', - 'zh-CN': '新密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。' - }, - 'new_password': { - 'en-US': 'New Password', - 'zh-CN': '新密码' - }, - 'username_already_exist': { - 'en-US': 'Username already exist.', - 'zh-CN': '用户名已存在。' - }, - 'username_does_not_exist': { - 'en-US': 'Username does not exist.', - 'zh-CN': '用户名不存在。' - }, - 'username_or_password_is_incorrect': { - 'en-US': 'Username or password is incorrect', - 'zh-CN': '用户名或密码不正确。' - }, - 'username_email': { - 'en-US': 'Username/Email', - 'zh-CN': '用户名/邮箱' - }, - 'project_name_is_required': { - 'en-US': 'Project name is required', - 'zh-CN': '项目名称为必填项。' - }, - 'project_already_exist': { - 'en-US': 'Project already exist', - 'zh-CN': '项目已存在。' - }, - 'project_name_is_invalid': { - 'en-US': 'Project name is invalid', - 'zh-CN': '项目名称无效。' - }, - 'projects_or_repositories': { - 'en-US': 'Projects or repositories', - 'zh-CN': '项目和镜像资源' - }, - 'tag': { - 'en-US': 'Tag', - 'zh-CN': '标签' - }, - 'image_details': { - 'en-US': 'Image Details', - 'zh-CN': '镜像明细' - }, - 'pull_command': { - 'en-US': 'Pull Command', - 'zh-CN': 'Pull 命令' - }, - 'alert_delete_repo_title': { - 'en-US': 'Delete repository - $0', - 'zh-CN': '删除镜像仓库 - $0' - }, - 'alert_delete_repo': { - 'en-US': 'After deleting the associated tags with the repository will be deleted together.
' + - 'And the corresponding image will be removed from the system.
' + - '
Delete this "$0" repository now?', - 'zh-CN': '删除镜像仓库也会删除其所有相关联的镜像标签,
并且其对应镜像资源文件也会被从系统删除。
' + - '
是否删除镜像仓库 "$0" ?' - }, - 'alert_delete_last_tag': { - 'en-US': 'After deleting the associated repository with the tag will be deleted together,
' + - 'because a repository contains at least one tag. And the corresponding image will be removed from the system.
' + - '
Delete this "$0" tag now?', - 'zh-CN': '当删除只包含一个镜像标签的镜像仓库时,其对应的镜像仓库也会被从系统中删除。
' + - '
删除镜像标签 "$0" ?' - }, - 'alert_delete_tag_title': { - 'en-US': 'Delete tag - $0', - 'zh-CN': '删除镜像标签 - $0' - }, - 'alert_delete_tag': { - 'en-US': 'Delete this "$0" tag now?', - 'zh-CN': '删除镜像标签 "$0" ?' - }, - 'close': { - 'en-US': 'Close', - 'zh-CN': '关闭' - }, - 'ok': { - 'en-US': 'OK', - 'zh-CN': '确认' - } -}; \ No newline at end of file diff --git a/static/ng/resources/js/services/i18n/locale_messages_en-US.js b/static/ng/resources/js/services/i18n/locale_messages_en-US.js new file mode 100644 index 000000000..1b95db666 --- /dev/null +++ b/static/ng/resources/js/services/i18n/locale_messages_en-US.js @@ -0,0 +1,121 @@ +var locale_messages = { + 'sign_in': 'Sign In', + 'sign_up': 'Sign Up', + 'forgot_password': 'Forgot Password', + 'login_now': 'Login Now', + 'its_easy_to_get_started': 'It\'s easy to get started ...', + 'anybody_can_read_public_projects': 'Anybody can read public projects', + 'create_projects_and_connect_repositories': 'Create projects and connect repositories', + 'user_management_and_role_assignment': 'User management and role assignment', + 'why_use_harbor': 'Why use Harbor?', + '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_1': 'Security: Keep their intellectual properties within their organizations.', + 'index_desc_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': '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': 'Audit: All access to the registry are logged and can be used for audit purpose.', + 'index_desc_5': 'GUI: User friendly single-pane-of-glass management console.', + 'learn_more': 'Learn more...', + 'repositories': 'Repositories', + 'project_repo_name': 'Project/Repository Name', + 'creation_time': 'Creation Time', + 'author': 'Author', + 'username': 'Username', + 'username_is_required': 'Username is required.', + 'username_has_been_taken': 'Username has been taken.', + 'username_is_too_long': 'Username is too long. (maximum 20 characters)', + 'username_contains_illegal_chars': 'Username contains illegal character(s).', + 'email': 'Email', + 'email_desc': 'The Email address will be used for resetting password.', + 'email_is_required': 'Email is required.', + 'email_has_been_taken': 'Email has been taken.', + 'email_content_illegal': 'Email format is illegal.', + 'email_does_not_exist': 'Email does not exist.', + 'email_is_too_long': 'Email is to long. (maximum 50 characters)', + 'full_name': 'Full Name', + 'full_name_desc': 'First name & Last name', + 'full_name_is_required': 'Full name is required.', + 'full_name_is_too_long': 'Full name is too long. (maximum 20 characters)', + 'full_name_contains_illegal_chars': 'Full name contains illegal character(s).', + 'password': 'Password', + 'password_desc': 'At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.', + 'password_is_required': 'Password is required.', + 'password_is_invalid': 'Password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.', + 'confirm_password': 'Confirm Password', + 'password_does_not_match': 'Passwords do not match.', + 'comments': 'Comments', + 'comment_is_too_long': 'Comment is too long. (maximum 20 characters)', + 'forgot_password_description': 'Please input the Email used when you signed up, a reset password Email will be sent to you.', + 'email_does_not_exist': 'Email does not exist', + 'reset_password': 'Reset Password', + 'summary': 'Summary', + 'projects': 'Projects', + 'public_projects': 'Public Projects', + 'public': 'Public', + 'total_projects': 'Total Projects', + 'public_repositories': 'Public Repositories', + 'total_repositories': 'Total Repositories', + 'top_10_repositories': 'Top 10 Repositories', + 'repository_name': 'Repository Name', + 'size': 'Size', + 'creator': 'Creator', + 'logs': 'Logs', + 'task_name': 'Task Name', + 'details': 'Details', + 'user': 'User', + 'users': 'Users', + 'my_projects': 'My Projects', + 'project_name': 'Project Name', + 'role': 'Role', + 'publicity': 'Publicity', + 'button_on': 'On', + 'button_off': 'Off', + 'new_project': 'New Project', + 'save': 'Save', + 'cancel': 'Cancel', + 'items': 'items', + 'add_member': 'Add Member', + 'operation': 'Operation', + 'advanced_search': 'Advanced Search', + 'all': 'All', + 'others': 'Others', + 'search': 'Search', + 'duration': 'Duration', + 'from': 'From', + 'to': 'To', + 'timestamp': 'Timestamp', + 'dashboard': 'Dashboard', + 'admin_options': 'Admin Options', + 'account_setting': 'Account Setting', + 'log_out': 'Log Out', + 'registration_time': 'Registration Time', + 'system_management': 'System Management', + 'change_password': 'Change Password', + 'old_password': 'Old Password', + 'old_password_is_required': 'Old password is required.', + 'old_password_is_incorrect': 'Old password is incorrect.', + 'new_password_is_required': 'New password is required.', + 'new_password_is_invalid': 'New password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.', + 'new_password': 'New Password', + 'username_already_exist': 'Username already exist.', + 'username_does_not_exist': 'Username does not exist.', + 'username_or_password_is_incorrect': 'Username or password is incorrect', + 'username_email': 'Username/Email', + 'project_name_is_required': 'Project name is required', + 'project_already_exist': 'Project already exist', + 'project_name_is_invalid': 'Project name is invalid', + 'projects_or_repositories': 'Projects or repositories', + 'tag': 'Tag', + 'image_details': 'Image Details', + 'pull_command': 'Pull Command', + 'alert_delete_repo_title': 'Delete repository - $0', + 'alert_delete_repo': 'After deleting the associated tags with the repository will be deleted together.
' + + 'And the corresponding image will be removed from the system.
' + + '
Delete this "$0" repository now?', + 'alert_delete_last_tag': 'After deleting the associated repository with the tag will be deleted together,
' + + 'because a repository contains at least one tag. And the corresponding image will be removed from the system.
' + + '
Delete this "$0" tag now?', + 'alert_delete_tag_title': 'Delete tag - $0', + 'alert_delete_tag': 'Delete this "$0" tag now?', + 'close': 'Close', + 'ok': 'OK' +}; \ No newline at end of file diff --git a/static/ng/resources/js/services/i18n/locale_messages_zh-CN.js b/static/ng/resources/js/services/i18n/locale_messages_zh-CN.js new file mode 100644 index 000000000..f854d5fd4 --- /dev/null +++ b/static/ng/resources/js/services/i18n/locale_messages_zh-CN.js @@ -0,0 +1,119 @@ +var locale_messages = { + 'sign_in': '登录', + 'sign_up': '注册', + 'forgot_password': '忘记密码', + 'login_now': '立刻登录', + 'its_easy_to_get_started': '这很容易上手...', + 'anybody_can_read_public_projects': '任何人都可以访问公开的项目', + 'create_projects_and_connect_repositories': '创建项目并关联镜像仓库', + 'user_management_and_role_assignment': '用户管理和角色分派', + 'why_use_harbor': '为什么要使用Harbor?', + 'index_desc': 'Harbor是可靠的企业级Registry服务器。企业用户可使用Harbor搭建私有容器Registry服务,提高生产效率和安全度,既可应用于生产环境,也可以在开发环境中使用。', + 'index_desc_1': '安全: 确保知识产权在自己组织内部的管控之下。', + 'index_desc_2': '效率: 搭建组织内部的私有容器Registry服务,可显著降低访问公共Registry服务的网络需求。', + 'index_desc_3': '访问控制: 提供基于角色的访问控制,可集成企业目前拥有的用户管理系统(如:AD/LDAP)。', + 'index_desc_4': '审计: 所有访问Registry服务的操作均被记录,便于日后审计。', + 'index_desc_5': '管理界面: 具有友好易用图形管理界面。', + 'learn_more': '更多...', + 'repositories': '镜像仓库', + 'project_repo_name': '项目/镜像仓库名称', + 'creation_time': '创建时间', + 'author': '作者', + 'username': '用户名', + 'username_is_required' : '用户名为必填项。', + 'username_has_been_taken' : '用户名已被占用。', + 'username_is_too_long' : '用户名长度超出限制。(最长为20个字符)', + 'username_contains_illegal_chars': '用户名包含不合法的字符。', + 'email': '邮箱', + 'email_desc': '此邮箱将用于重置密码。', + 'email_is_required' : '邮箱为必填项。', + 'email_has_been_taken' : '邮箱已被占用。', + 'email_content_illegal' : '邮箱格式不合法。', + 'email_does_not_exist' : '邮箱不存在。', + 'email_is_too_long': '邮箱名称长度超出限制。(最长为50个字符)', + 'full_name': '全名', + 'full_name_desc': '请输入全名。', + 'full_name_is_required' : '全名为必填项。', + 'full_name_is_too_long' : '全名长度超出限制。(最长为20个字符)', + 'full_name_contains_illegal_chars' : '全名包含不合法的字符。', + 'password': '密码', + 'password_desc': '至少输入 7个字符且包含 1个小写字母, 1个大写字母和 1个数字。', + 'password_is_required' : '密码为必填项。', + 'password_is_invalid' : '密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。', + 'confirm_password': '确认密码', + 'password_does_not_match' : '两次密码输入不一致。', + 'comments': '备注', + 'comment_is_too_long' : '备注长度超出限制。(最长为20个字符)', + 'forgot_password_description': '重置邮件将发送到此邮箱。', + 'email_does_not_exist': '邮箱不存在。', + 'reset_password': '重置密码', + 'summary': '摘要', + 'projects': '项目', + 'public_projects': '公开项目', + 'public': '公开', + 'total_projects': '全部项目', + 'public_repositories': '公开镜像仓库', + 'total_repositories': '全部镜像仓库', + 'top_10_repositories': 'Top 10 镜像仓库', + 'repository_name': '镜像仓库名', + 'size': '规格', + 'creator': '创建者', + 'logs': '日志', + 'task_name': '任务名称', + 'details': '详细信息', + 'user': '用户', + 'users': '用户', + 'my_projects': '我的项目', + 'project_name': '项目名称', + 'role': '角色', + 'publicity': '公开', + 'button_on': '打开', + 'button_off': '关闭', + 'new_project': '新增项目', + 'save': '保存', + 'cancel': '取消', + 'items': '条记录', + 'add_member': '新增成员', + 'operation': '操作', + 'advanced_search': '高级搜索', + 'all': '全部', + 'others': '其他', + 'search':'搜索', + 'duration': '持续时间', + 'from': '起始', + 'to': '结束', + 'timestamp': '时间戳', + 'dashboard': '消息中心', + 'admin_options': '管理员选项', + 'account_setting': '个人设置', + 'log_out': '注销', + 'registration_time': '注册时间', + 'system_management': '系统管理', + 'change_password': '修改密码', + 'old_password': '原密码', + 'old_password_is_required': '原密码为必填项。', + 'old_password_is_incorrect': '原密码不正确。', + 'new_password_is_required': '新密码为必填项。', + 'new_password_is_invalid': '新密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。', + 'new_password': '新密码', + 'username_already_exist': '用户名已存在。', + 'username_does_not_exist': '用户名不存在。', + 'username_or_password_is_incorrect': '用户名或密码不正确。', + 'username_email': '用户名/邮箱', + 'project_name_is_required': '项目名称为必填项。', + 'project_already_exist': '项目已存在。', + 'project_name_is_invalid': '项目名称无效。', + 'projects_or_repositories': '项目和镜像资源', + 'tag': '标签', + 'image_details': '镜像明细', + 'pull_command': 'Pull 命令', + 'alert_delete_repo_title': '删除镜像仓库 - $0', + 'alert_delete_repo': '删除镜像仓库也会删除其所有相关联的镜像标签,
并且其对应镜像资源文件也会被从系统删除。
' + + '
是否删除镜像仓库 "$0" ?', + 'alert_delete_last_tag': '当删除只包含一个镜像标签的镜像仓库时,其对应的镜像仓库也会被从系统中删除。
' + + '
删除镜像标签 "$0" ?', + 'alert_delete_tag_title': '删除镜像标签 - $0', + 'alert_delete_tag': '删除镜像标签 "$0" ?', + 'close': '关闭', + 'ok': '确认' +}; \ No newline at end of file diff --git a/static/ng/resources/js/services/i18n/services.i18n.js b/static/ng/resources/js/services/i18n/services.i18n.js index d34a5c672..00b5ced18 100644 --- a/static/ng/resources/js/services/i18n/services.i18n.js +++ b/static/ng/resources/js/services/i18n/services.i18n.js @@ -9,29 +9,48 @@ I18nService.$inject = ['$cookies', '$window']; function I18nService($cookies, $window) { - var languages = $.extend(true, {}, global_messages); + var cookieOptions = {'path': '/'}; + var messages = $.extend(true, {}, eval('locale_messages')); var defaultLanguage = navigator.language || 'en-US'; - var languageNames = { + var supportLanguages = { 'en-US': 'English', 'zh-CN': '中文' - }; + }; + var isSupportLanguage = function(language) { + for (var i in supportLanguages) { + if(language == i) { + return true; + } + } + return false; + } + return tr; function tr() { return { - 'setCurrentLanguage': function(language) { - if(!language){ + 'setCurrentLanguage': function(language) { + if(!angular.isDefined(language) || !isSupportLanguage(language)) { language = defaultLanguage; } - $cookies.put('language', language, {'path': '/'}); + $cookies.put('language', language, cookieOptions); + }, + 'setDefaultLanguage': function() { + $cookies.put('language', defaultLanguage, cookieOptions); }, 'getCurrentLanguage': function() { return $cookies.get('language') || defaultLanguage; }, - 'getLanguageName': function(crrentLanguage) { - return languageNames[crrentLanguage]; + 'getLanguageName': function(language) { + if(!angular.isDefined(language) || !isSupportLanguage(language)) { + language = defaultLanguage; + } + return supportLanguages[language]; }, - 'getValue': function(key, currentLanguage) { - return languages[key][currentLanguage]; + 'unset': function(){ + $cookies.put('language', defaultLanguage, cookieOptions); + }, + 'getValue': function(key) { + return messages[key]; } } } diff --git a/static/ng/resources/js/services/search/services.search.js b/static/ng/resources/js/services/search/services.search.js index 11c9d3ca6..26c12a9c0 100644 --- a/static/ng/resources/js/services/search/services.search.js +++ b/static/ng/resources/js/services/search/services.search.js @@ -10,21 +10,15 @@ function SearchService($http, $log) { - return Search; + return search; - function Search(keywords) { - return $http({ - method: 'GET', - url: '/api/search', - - transformRequest: function(obj) { - var str = []; - for(var p in obj) - str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); - return str.join("&"); - }, - data: {'q': keywords} - }); + function search(keywords) { + return $http + .get('/api/search',{ + params: { + 'q': keywords + } + }); } } diff --git a/static/ng/resources/js/session/session.current-user.js b/static/ng/resources/js/session/session.current-user.js index 90b109afc..e50406cb9 100644 --- a/static/ng/resources/js/session/session.current-user.js +++ b/static/ng/resources/js/session/session.current-user.js @@ -6,34 +6,38 @@ .module('harbor.session') .controller('CurrentUserController', CurrentUserController); - CurrentUserController.$inject = ['CurrentUserService', 'currentUser', '$scope', '$timeout', '$window']; + CurrentUserController.$inject = ['CurrentUserService', 'currentUser', '$window']; - function CurrentUserController(CurrentUserService, currentUser, $scope, $timeout, $window) { + function CurrentUserController(CurrentUserService, currentUser, $window) { var vm = this; CurrentUserService() .then(getCurrentUserComplete) .catch(getCurrentUserFailed); - + function getCurrentUserComplete(response) { - console.log('Successful logged in.'); - $timeout(function(){ - $scope.$broadcast('currentUser', response.data); - currentUser.set(response.data); - }, 50); + if(angular.isDefined(response)) { + currentUser.set(response.data); + } } function getCurrentUserFailed(e){ var url = location.pathname; - var exclusions = ['/ng', '/ng/forgot_password', '/ng/sign_up', '/ng/reset_password']; + var exclusions = [ + '/ng', + '/ng/forgot_password', + '/ng/sign_up', + '/ng/reset_password', + '/ng/search' + ]; for(var i = 0; i < exclusions.length; i++) { if(exclusions[i]===url) { return; } } $window.location.href = '/ng'; - } + } } })(); \ No newline at end of file diff --git a/ui/ngrouter.go b/ui/ngrouter.go index d03c1a100..3e6a0730e 100644 --- a/ui/ngrouter.go +++ b/ui/ngrouter.go @@ -16,8 +16,13 @@ func initNgRouters() { beego.Router("/ng/admin_option", &ng.AdminOptionController{}) beego.Router("/ng/forgot_password", &ng.ForgotPasswordController{}) beego.Router("/ng/reset_password", &ng.ResetPasswordController{}) + beego.Router("/ng/search", &ng.SearchController{}) + beego.Router("/ng/log_out", &ng.CommonController{}, "get:LogOut") beego.Router("/ng/reset", &ng.CommonController{}, "post:ResetPassword") beego.Router("/ng/sendEmail", &ng.CommonController{}, "get:SendEmail") + beego.Router("/ng/language", &ng.CommonController{}, "get:SwitchLanguage") + beego.Router("/ng/optional_menu", &ng.OptionalMenuController{}) + beego.Router("/ng/navigation_header", &ng.NavigationHeaderController{}) } diff --git a/ui/router.go b/ui/router.go index 401745410..1218424c2 100644 --- a/ui/router.go +++ b/ui/router.go @@ -54,6 +54,7 @@ func initRouters() { beego.Router("/api/search", &api.SearchAPI{}) beego.Router("/api/projects/:pid/members/?:mid", &api.ProjectMemberAPI{}) beego.Router("/api/projects/?:id", &api.ProjectAPI{}) + beego.Router("/api/statistics", &api.StatisticAPI{}) beego.Router("/api/projects/:id/logs/filter", &api.ProjectAPI{}, "post:FilterAccessLog") beego.Router("/api/users", &api.UserAPI{}) beego.Router("/api/users/?:id", &api.UserAPI{}) diff --git a/views/ng/dashboard.htm b/views/ng/dashboard.htm index 53ab0d406..9b1a8727e 100644 --- a/views/ng/dashboard.htm +++ b/views/ng/dashboard.htm @@ -5,7 +5,7 @@
-
+
// 'projects' | tr //:
//vm.statProjects['projects']//
// 'public_projects' | tr //:
//vm.statProjects['public_projects']//
// 'total_projects' | tr //:
//vm.statProjects['total_projects']//
@@ -38,7 +38,7 @@
-
+
diff --git a/views/ng/forgot-password.htm b/views/ng/forgot-password.htm index c1bafd819..e8d02cc7e 100644 --- a/views/ng/forgot-password.htm +++ b/views/ng/forgot-password.htm @@ -5,7 +5,7 @@

// 'forgot_password' | tr //

- +
diff --git a/static/ng/resources/js/layout/navigation/navigation-header.directive.html b/views/ng/navigation-header.htm similarity index 50% rename from static/ng/resources/js/layout/navigation/navigation-header.directive.html rename to views/ng/navigation-header.htm index 936471a6c..1046715a0 100644 --- a/static/ng/resources/js/layout/navigation/navigation-header.directive.html +++ b/views/ng/navigation-header.htm @@ -1,5 +1,9 @@ +{{ if eq .HasLoggedIn true }} \ No newline at end of file + {{ if eq .IsAdmin 1 }} +
  • // 'admin_options' | tr //
  • + {{ end }} + +{{ end }} \ No newline at end of file diff --git a/static/ng/resources/js/components/optional-menu/optional-menu.directive.html b/views/ng/optional-menu.htm similarity index 55% rename from static/ng/resources/js/components/optional-menu/optional-menu.directive.html rename to views/ng/optional-menu.htm index 287b607c2..f7512665e 100644 --- a/static/ng/resources/js/components/optional-menu/optional-menu.directive.html +++ b/views/ng/optional-menu.htm @@ -1,27 +1,31 @@ - - - +{{ else }} + +{{ end }} \ No newline at end of file diff --git a/views/ng/project.htm b/views/ng/project.htm index 9612f9096..1b2639680 100644 --- a/views/ng/project.htm +++ b/views/ng/project.htm @@ -35,7 +35,7 @@
    - +
    N/A N/A //p.CreationTime | dateL : 'YYYY-MM-DD HH:mm:ss'//
    diff --git a/views/ng/repository.htm b/views/ng/repository.htm index 0531c4e42..2ed17f5f3 100644 --- a/views/ng/repository.htm +++ b/views/ng/repository.htm @@ -10,12 +10,12 @@ // 'public_projects' | tr // // 'public_projects' | tr //
    - + - +
    - +
    diff --git a/views/ng/search.htm b/views/ng/search.htm new file mode 100644 index 000000000..cf2a4fae3 --- /dev/null +++ b/views/ng/search.htm @@ -0,0 +1,24 @@ +
    +
    +
    +
    +
    +
    + +
    + +
    + + +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/views/ng/sections/header-content.htm b/views/ng/sections/header-content.htm index bb2099642..e0392623f 100644 --- a/views/ng/sections/header-content.htm +++ b/views/ng/sections/header-content.htm @@ -10,17 +10,13 @@
    diff --git a/views/ng/sections/header-include.htm b/views/ng/sections/header-include.htm index 22f06a38d..0d3d5b738 100644 --- a/views/ng/sections/header-include.htm +++ b/views/ng/sections/header-include.htm @@ -13,15 +13,15 @@ - + - + - + @@ -31,6 +31,7 @@ + @@ -93,8 +94,17 @@ + + + - + +{{ if eq .Lang "zh-CN" }} + +{{ else if eq .Lang "en-US"}} + +{{ end }} + @@ -156,6 +166,7 @@ + diff --git a/views/ng/sign-up.htm b/views/ng/sign-up.htm index d2955baf5..312629dea 100644 --- a/views/ng/sign-up.htm +++ b/views/ng/sign-up.htm @@ -5,7 +5,7 @@

    // 'sign_up' | tr //

    -
    +