mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 22:57:38 +01:00
Merge remote-tracking branch 'upstream/new-version-of-ui' into new-version-of-ui
This commit is contained in:
commit
a205487857
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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")
|
||||
|
117
api/statistic.go
Normal file
117
api/statistic.go
Normal file
@ -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)
|
||||
|
||||
}
|
@ -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")
|
||||
|
37
controllers/ng/navigationheader.go
Normal file
37
controllers/ng/navigationheader.go
Normal file
@ -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()
|
||||
}
|
37
controllers/ng/optionalmenu.go
Normal file
37
controllers/ng/optionalmenu.go
Normal file
@ -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()
|
||||
|
||||
}
|
@ -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
|
||||
|
9
controllers/ng/search.go
Normal file
9
controllers/ng/search.go
Normal file
@ -0,0 +1,9 @@
|
||||
package ng
|
||||
|
||||
type SearchController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (sc *SearchController) Get() {
|
||||
sc.Forward("Search", "search.htm")
|
||||
}
|
@ -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 {
|
||||
|
100
dao/project.go
100
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
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -12,7 +12,3 @@
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.single {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
9
static/ng/resources/css/search.css
Normal file
9
static/ng/resources/css/search.css
Normal file
@ -0,0 +1,9 @@
|
||||
.search-result {
|
||||
min-height: 200px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.search-result li {
|
||||
margin-bottom: 15px;
|
||||
}
|
BIN
static/ng/resources/img/Harbor_Logo_rec.png
Executable file → Normal file
BIN
static/ng/resources/img/Harbor_Logo_rec.png
Executable file → Normal file
Binary file not shown.
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 3.6 KiB |
@ -1,10 +1,10 @@
|
||||
<div class="switch-pane-drop-down" ng-show="vm.isOpen">
|
||||
<div id="retrievePane" class="switch-pane-drop-down" ng-show="vm.isOpen">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="form-inline search-projects">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control search-icon" placeholder="" ng-model="vm.filterInput" size="30">
|
||||
<input type="text" id="retrieveFilter" class="form-control search-icon" placeholder="" ng-model="vm.filterInput" size="30">
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="page-header">//vm.projectType | tr//: <span class="badge">//vm.resultCount//</span></h5>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -1,5 +1,5 @@
|
||||
<div class="switch-pane-projects" ng-switch="vm.isOpen">
|
||||
<a href="javascript:void(0);" ng-click="vm.switchPane()">//vm.projectName//</a>
|
||||
<a id="switchPane" href="javascript:void(0);" ng-click="vm.switchPane()" >//vm.projectName//</a>
|
||||
<span ng-switch-default class="glyphicon glyphicon-triangle-right" style="font-size: 12px;"></span>
|
||||
<span ng-switch-when="true" class="glyphicon glyphicon-triangle-bottom" style="font-size: 12px;"></span>
|
||||
</div>
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -74,16 +74,11 @@
|
||||
'projectId': '=',
|
||||
'reload': '&'
|
||||
},
|
||||
'link': link,
|
||||
'controller': EditProjectMemberController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -18,7 +18,7 @@
|
||||
<th width="30%">// 'username' | tr //</th><th width="40%">// 'role' | tr //</th><th width="30%">// 'operation' | tr //</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.UserId" current-user-id="vm.currentUser.UserId" role-name="pr.Rolename" reload='vm.search({projectId: vm.projectId, username: vm.username})'></tr>
|
||||
<tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.UserId" current-user-id="vm.user.UserId" role-name="pr.Rolename" reload='vm.search({projectId: vm.projectId, username: vm.username})'></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,5 @@
|
||||
<form class="navbar-form pull-right" role="search">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control search-icon" ng-model="vm.searchInput" placeholder="// 'projects_or_repositories' | tr //" size="35">
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,54 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.search')
|
||||
.directive('searchInput', searchInput);
|
||||
|
||||
SearchInputController.$inject = ['$scope', '$location', '$window'];
|
||||
|
||||
function SearchInputController($scope, $location, $window) {
|
||||
var vm = this;
|
||||
|
||||
vm.searchFor = searchFor;
|
||||
|
||||
function searchFor(searchContent) {
|
||||
$location
|
||||
.path('/ng/search')
|
||||
.search({'q': searchContent});
|
||||
$window.location.href = $location.url();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function searchInput() {
|
||||
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/ng/resources/js/components/search/search-input.directive.html',
|
||||
'scope': {
|
||||
'searchInput': '=',
|
||||
},
|
||||
'link': link,
|
||||
'controller': SearchInputController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
element
|
||||
.find('input[type="text"]')
|
||||
.on('keydown', keydownHandler);
|
||||
|
||||
function keydownHandler(e) {
|
||||
if(e.keyCode === 13) {
|
||||
ctrl.searchFor($(this).val());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -1,9 +1,9 @@
|
||||
<form name="form" class="form-horizontal css-form" ng-submit="form.$valid" novalidate>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<input id="username" type="text" class="form-control" placeholder="// 'username_email' | tr //" name="uPrincipal" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" ng-model="user.principal" required>
|
||||
<input id="username" type="text" class="form-control" placeholder="// 'username_email' | tr //" name="uPrincipal" ng-change="vm.reset()" ng-model="user.principal" required>
|
||||
<div class="error-message">
|
||||
<div ng-messages="form.uPrincipal.$error" ng-if="form.uPrincipal.$touched">
|
||||
<div ng-messages="form.uPrincipal.$error" ng-if="form.uPrincipal.$touched || form.$submitted">
|
||||
<span ng-message="required">// 'username_is_required' | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -11,9 +11,9 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<input type="password" class="form-control" placeholder="// 'password' | tr //" name="uPassword" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" ng-model="user.password" required>
|
||||
<input type="password" class="form-control" placeholder="// 'password' | tr //" name="uPassword" ng-change="vm.reset()" ng-model="user.password" required>
|
||||
<div class="error-message">
|
||||
<div ng-messages="form.uPassword.$error" ng-if="form.uPassword.$touched">
|
||||
<div ng-messages="form.uPassword.$error" ng-if="form.uPassword.$touched || form.$submitted">
|
||||
<span ng-message="required">// 'password_is_required' | tr //</span>
|
||||
</div>
|
||||
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
|
||||
@ -23,7 +23,7 @@
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default" ng-disabled="form.$invalid" ng-click="vm.doSignIn(user)">// 'sign_in' | tr //</button>
|
||||
<button class="btn btn-default" ng-click="vm.doSignIn(user)">// 'sign_in' | tr //</button>
|
||||
<button class="btn btn-success" ng-click="vm.doSignUp()">// 'sign_up' | tr //</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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': '/'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -5,9 +5,7 @@
|
||||
angular
|
||||
.module('harbor.layout.project.member')
|
||||
.controller('ProjectMemberController', ProjectMemberController);
|
||||
|
||||
ProjectMemberController.$inject = ['$scope'];
|
||||
|
||||
|
||||
function ProjectMemberController($scope) {
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
||||
|
31
static/ng/resources/js/layout/search/search.controller.js
Normal file
31
static/ng/resources/js/layout/search/search.controller.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
9
static/ng/resources/js/layout/search/search.module.js
Normal file
9
static/ng/resources/js/layout/search/search.module.js
Normal file
@ -0,0 +1,9 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.layout.search', []);
|
||||
|
||||
|
||||
})();
|
@ -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.<br/>' +
|
||||
'And the corresponding image will be removed from the system.<br/>' +
|
||||
'<br/>Delete this "$0" repository now?',
|
||||
'zh-CN': '删除镜像仓库也会删除其所有相关联的镜像标签,<br/>并且其对应镜像资源文件也会被从系统删除。<br/>' +
|
||||
'<br/>是否删除镜像仓库 "$0" ?'
|
||||
},
|
||||
'alert_delete_last_tag': {
|
||||
'en-US': 'After deleting the associated repository with the tag will be deleted together,<br/>' +
|
||||
'because a repository contains at least one tag. And the corresponding image will be removed from the system.<br/>' +
|
||||
'<br/>Delete this "$0" tag now?',
|
||||
'zh-CN': '当删除只包含一个镜像标签的镜像仓库时,其对应的镜像仓库也会被从系统中删除。<br/>' +
|
||||
'<br/>删除镜像标签 "$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': '确认'
|
||||
}
|
||||
};
|
121
static/ng/resources/js/services/i18n/locale_messages_en-US.js
Normal file
121
static/ng/resources/js/services/i18n/locale_messages_en-US.js
Normal file
@ -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.<br/>' +
|
||||
'And the corresponding image will be removed from the system.<br/>' +
|
||||
'<br/>Delete this "$0" repository now?',
|
||||
'alert_delete_last_tag': 'After deleting the associated repository with the tag will be deleted together,<br/>' +
|
||||
'because a repository contains at least one tag. And the corresponding image will be removed from the system.<br/>' +
|
||||
'<br/>Delete this "$0" tag now?',
|
||||
'alert_delete_tag_title': 'Delete tag - $0',
|
||||
'alert_delete_tag': 'Delete this "$0" tag now?',
|
||||
'close': 'Close',
|
||||
'ok': 'OK'
|
||||
};
|
119
static/ng/resources/js/services/i18n/locale_messages_zh-CN.js
Normal file
119
static/ng/resources/js/services/i18n/locale_messages_zh-CN.js
Normal file
@ -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': '删除镜像仓库也会删除其所有相关联的镜像标签,<br/>并且其对应镜像资源文件也会被从系统删除。<br/>' +
|
||||
'<br/>是否删除镜像仓库 "$0" ?',
|
||||
'alert_delete_last_tag': '当删除只包含一个镜像标签的镜像仓库时,其对应的镜像仓库也会被从系统中删除。<br/>' +
|
||||
'<br/>删除镜像标签 "$0" ?',
|
||||
'alert_delete_tag_title': '删除镜像标签 - $0',
|
||||
'alert_delete_tag': '删除镜像标签 "$0" ?',
|
||||
'close': '关闭',
|
||||
'ok': '确认'
|
||||
};
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -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{})
|
||||
}
|
||||
|
@ -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{})
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="row">
|
||||
<div class="up-section">
|
||||
<h4 class="page-header title-color underlined">// 'summary' | tr //</h4>
|
||||
<dl class="page-content dl-horizontal">
|
||||
<dl class="page-content dl-horizontal">
|
||||
<dt>// 'projects' | tr //:</dt><dd>//vm.statProjects['projects']//</dd>
|
||||
<dt>// 'public_projects' | tr //:</dt><dd>//vm.statProjects['public_projects']//</dd>
|
||||
<dt>// 'total_projects' | tr //:</dt><dd>//vm.statProjects['total_projects']//</dd>
|
||||
@ -38,7 +38,7 @@
|
||||
<div class="container">
|
||||
<div class="row row-custom">
|
||||
<div class="col-xs-12 col-md-12">
|
||||
<div class="down-section single">
|
||||
<div class="down-section">
|
||||
<h4 class="page-header title-color underlined">// 'logs' | tr //</h4>
|
||||
<div style="padding: 15px;">
|
||||
<table class="table">
|
||||
|
@ -5,7 +5,7 @@
|
||||
<h1 class="col-md-12 col-md-offset-2 main-title title-color">// 'forgot_password' | tr //</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-md-offset-2 main-content">
|
||||
<form name="form" class="form-horizontal css-form" ng-submit="form.$valid " >
|
||||
<form name="form" class="form-horizontal css-form" ng-submit="form.$valid" >
|
||||
<div class="form-group">
|
||||
<label for="email" class="col-sm-3 control-label">// 'email' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
|
@ -1,5 +1,9 @@
|
||||
{{ if eq .HasLoggedIn true }}
|
||||
<ul class="nav-custom">
|
||||
<li><a tag="dashboard" href="/ng/dashboard">// 'dashboard' | tr //</a></li>
|
||||
<li><a tag="project" href="/ng/project">// 'projects' | tr //</a></li>
|
||||
<li><a ng-show="vm.isAdmin" tag="admin_option" href="/ng/admin_option#/all_user">// 'admin_options' | tr //</a></li>
|
||||
</ul>
|
||||
{{ if eq .IsAdmin 1 }}
|
||||
<li><a tag="admin_option" href="/ng/admin_option#/all_user">// 'admin_options' | tr //</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
@ -1,27 +1,31 @@
|
||||
<div ng-if="!vm.isLoggedIn" class="dropdown">
|
||||
<a id="dLabel" role="button" data-toggle="dropdown" class="btn btn-link" data-target="#" href="">
|
||||
<span class="glyphicon glyphicon-globe"></span> //vm.languageName//
|
||||
</a>
|
||||
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
|
||||
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('en-US')">English</a></li>
|
||||
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('zh-CN')">中文</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.isLoggedIn" class="dropdown">
|
||||
<a id="dLabel" role="button" data-toggle="dropdown" class="btn btn-link" data-target="#" href="">
|
||||
<span class="glyphicon glyphicon-user"></span> //vm.username//
|
||||
{{if eq .HasLoggedIn true }}
|
||||
<div class="dropdown">
|
||||
<a role="button" data-toggle="dropdown" class="btn btn-link" data-target="#" href="">
|
||||
<span class="glyphicon glyphicon-user"></span> {{ .Username }}
|
||||
</a>
|
||||
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
|
||||
<li><a href="/ng/account_setting"><span class="glyphicon glyphicon-pencil"></span> // 'account_setting' | tr //</a></li>
|
||||
<li class="dropdown-submenu">
|
||||
<a tabindex="-1" href="#"><span class="glyphicon glyphicon-globe"></span> //vm.languageName//</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('en-US')">English</a></li>
|
||||
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('zh-CN')">中文</a></li>
|
||||
{{ range $key, $value := .SupportLanguages }}
|
||||
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('{{ $key }}')">{{ $value.Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="javascript:void(0)" ng-click="vm.logOut()"><span class="glyphicon glyphicon-log-out"></span> // 'log_out' | tr //</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="dropdown">
|
||||
<a role="button" data-toggle="dropdown" class="btn btn-link" data-target="#" href="">
|
||||
<span class="glyphicon glyphicon-globe"></span> //vm.languageName//
|
||||
</a>
|
||||
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
|
||||
{{ range $key, $value := .SupportLanguages }}
|
||||
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('{{ $key }}')">{{ $value.Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
{{ end }}
|
@ -35,7 +35,7 @@
|
||||
<td>N/A</td>
|
||||
<td>N/A</td>
|
||||
<td>//p.CreationTime | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
<td><publicity-button is-public="p.Public" owned="p.OwnerId == vm.currentUser.UserId" project-id="p.ProjectId"></publicity-button></td>
|
||||
<td><publicity-button is-public="p.Public" owned="p.OwnerId == vm.user.UserId" project-id="p.ProjectId"></publicity-button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -10,12 +10,12 @@
|
||||
<span ng-show="vm.publicity">// 'public_projects' | tr //</span>
|
||||
<a ng-show="!vm.publicity" href="#/repositories?project_id=//vm.selectedProject.ProjectId//" class="title-color" ng-click="vm.togglePublicity({publicity: true})">// 'public_projects' | tr //</a></h4>
|
||||
<div class="switch-pane">
|
||||
<switch-pane-projects is-open="vm.open" selected-project="vm.selectedProject"></switch-pane-projects>
|
||||
<switch-pane-projects is-open="vm.isOpen" selected-project="vm.selectedProject"></switch-pane-projects>
|
||||
<span>
|
||||
<navigation-details is-open="vm.open" selected-project="vm.selectedProject" ng-show="vm.isProjectMember"></navigation-details>
|
||||
<navigation-details selected-project="vm.selectedProject" ng-show="vm.isProjectMember"></navigation-details>
|
||||
</span>
|
||||
</div>
|
||||
<retrieve-projects is-open="vm.open" selected-project="vm.selectedProject" is-project-member="vm.isProjectMember" publicity="vm.publicity"></retrieve-projects>
|
||||
<retrieve-projects is-open="vm.isOpen" selected-project="vm.selectedProject" is-project-member="vm.isProjectMember" publicity="vm.publicity"></retrieve-projects>
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content" ng-click="vm.closeRetrievePane()">
|
||||
<input type="hidden" id="HarborRegUrl" value="{{.HarborRegUrl}}">
|
||||
|
24
views/ng/search.htm
Normal file
24
views/ng/search.htm
Normal file
@ -0,0 +1,24 @@
|
||||
<div class="container-fluid container-fluid-custom" ng-controller="SearchController as vm">
|
||||
<div class="container container-custom">
|
||||
<div class="row extend-height">
|
||||
<div class="section">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-md-offset-2 main-content">
|
||||
<h4 class="page-header title-color underlined">Projects <span class="badge">//vm.project.length//</span></h4>
|
||||
<div class="search-result">
|
||||
<ul>
|
||||
<li ng-repeat="p in vm.project"><a href="/ng/repository#/repositories?project_id=//p.id//&is_public=//p.public === 1//">//p.name//</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<h4 class="page-header title-color underlined">Repositories <span class="badge">//vm.repository.length//</span></h4>
|
||||
<div class="search-result">
|
||||
<ul>
|
||||
<li ng-repeat="r in vm.repository"><a href="/ng/repository#/repositories?project_id=//r.project_id//&is_public=//r.project_public === 1//#//r.repository_name//">//r.repository_name//</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -10,17 +10,13 @@
|
||||
</div>
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-harbor-navbar-collapse-1">
|
||||
<optional-menu></optional-menu>
|
||||
<optional-menu login-status="//vm.loginStatus//"></optional-menu>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<navigation-header></navigation-header>
|
||||
</li>
|
||||
<li>
|
||||
<form class="navbar-form pull-right" role="search">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control search-icon" placeholder="// 'projects_or_repositories' | tr //" size="35">
|
||||
</div>
|
||||
</form>
|
||||
<search-input search-input='vm.searchInput'></search-input>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
|
@ -13,15 +13,15 @@
|
||||
<script src="/static/ng/vendors/zc/v2.2.0/ZeroClipboard.js"></script>
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">-->
|
||||
<!--link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"-->
|
||||
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
|
||||
<!-- Optional theme -->
|
||||
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">-->
|
||||
<!--link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous"-->
|
||||
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
|
||||
|
||||
<!-- Latest compiled and minified JavaScript -->
|
||||
<!--<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>-->
|
||||
<!--script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script-->
|
||||
<script src="https://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/static/ng/resources/css/header.css">
|
||||
@ -31,6 +31,7 @@
|
||||
<link rel="stylesheet" href="/static/ng/resources/css/project.css">
|
||||
<link rel="stylesheet" href="/static/ng/resources/css/repository.css">
|
||||
<link rel="stylesheet" href="/static/ng/resources/css/sign-up.css">
|
||||
<link rel="stylesheet" href="/static/ng/resources/css/search.css">
|
||||
|
||||
<script src="/static/ng/vendors/angularjs/angular.js"></script>
|
||||
<script src="/static/ng/vendors/angularjs/angular-route.js"></script>
|
||||
@ -93,8 +94,17 @@
|
||||
<script src="/static/ng/resources/js/layout/admin-option/admin-option.controller.js"></script>
|
||||
<script src="/static/ng/resources/js/layout/admin-option/admin-option.config.js"></script>
|
||||
|
||||
<script src="/static/ng/resources/js/layout/search/search.module.js"></script>
|
||||
<script src="/static/ng/resources/js/layout/search/search.controller.js"></script>
|
||||
|
||||
<script src="/static/ng/resources/js/services/i18n/services.i18n.module.js"></script>
|
||||
<script src="/static/ng/resources/js/services/i18n/locale_messages.js"></script>
|
||||
|
||||
{{ if eq .Lang "zh-CN" }}
|
||||
<script src="/static/ng/resources/js/services/i18n/locale_messages_zh-CN.js"></script>
|
||||
{{ else if eq .Lang "en-US"}}
|
||||
<script src="/static/ng/resources/js/services/i18n/locale_messages_en-US.js"></script>
|
||||
{{ end }}
|
||||
|
||||
<script src="/static/ng/resources/js/services/i18n/services.i18n.js"></script>
|
||||
|
||||
<script src="/static/ng/resources/js/services/search/services.search.module.js"></script>
|
||||
@ -156,6 +166,7 @@
|
||||
|
||||
<script src="/static/ng/resources/js/components/search/search.module.js"></script>
|
||||
<script src="/static/ng/resources/js/components/search/search.directive.js"></script>
|
||||
<script src="/static/ng/resources/js/components/search/search-input.directive.js"></script>
|
||||
|
||||
<script src="/static/ng/resources/js/components/sign-in/sign-in.module.js"></script>
|
||||
<script src="/static/ng/resources/js/components/sign-in/sign-in.directive.js"></script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<h1 class="col-md-12 col-md-offset-2 main-title title-color">// 'sign_up' | tr //</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-md-offset-2 main-content">
|
||||
<form name="form" class="form-horizontal css-form" ng-submit="form.$valid" novalidate>
|
||||
<form name="form" class="form-horizontal css-form" ng-submit="form.$valid">
|
||||
<div class="form-group">
|
||||
<label for="username" class="col-sm-3 control-label">// 'username' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
|
Loading…
Reference in New Issue
Block a user