From 719429f32ef57d7ef64af989ac82df73caa4e0e2 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Wed, 5 Jan 2022 15:08:31 +0800 Subject: [PATCH 01/23] add repository alias --- models/action.go | 18 ++++++++++++ models/repo.go | 16 +++++++++++ models/repo_generate.go | 1 + modules/auth/repo_form.go | 1 + modules/repository/create.go | 1 + modules/repository/generate.go | 1 + modules/structs/repo.go | 51 +++++++++++++++++----------------- routers/repo/repo.go | 2 ++ templates/repo/header.tmpl | 8 +++--- templates/user/dashboard/feeds.tmpl | 6 ++-- templates/user/dashboard/repolist.tmpl | 2 +- 11 files changed, 74 insertions(+), 33 deletions(-) diff --git a/models/action.go b/models/action.go index ab7d576e8..9bf67b8b9 100755 --- a/models/action.go +++ b/models/action.go @@ -164,12 +164,24 @@ func (a *Action) GetRepoName() string { return a.Repo.Name } +// GetRepoName returns the name of the action repository. +func (a *Action) GetRepoDisplayName() string { + a.loadRepo() + return a.Repo.DisplayName() +} + // ShortRepoName returns the name of the action repository // trimmed to max 33 chars. func (a *Action) ShortRepoName() string { return base.EllipsisString(a.GetRepoName(), 33) } +// ShortRepoName returns the name of the action repository +// trimmed to max 33 chars. +func (a *Action) ShortRepoDisplayName() string { + return base.EllipsisString(a.GetRepoDisplayName(), 33) +} + // GetRepoPath returns the virtual path to the action repository. func (a *Action) GetRepoPath() string { return path.Join(a.GetRepoUserName(), a.GetRepoName()) @@ -181,6 +193,12 @@ func (a *Action) ShortRepoPath() string { return path.Join(a.ShortRepoUserName(), a.ShortRepoName()) } +// ShortRepoPath returns the virtual path to the action repository +// trimmed to max 20 + 1 + 33 chars. +func (a *Action) ShortRepoFullDisplayName() string { + return path.Join(a.ShortRepoUserName(), a.ShortRepoDisplayName()) +} + // GetRepoLink returns relative link to action repository. func (a *Action) GetRepoLink() string { if len(setting.AppSubURL) > 0 { diff --git a/models/repo.go b/models/repo.go index 8070d7442..7661f4dba 100755 --- a/models/repo.go +++ b/models/repo.go @@ -222,6 +222,7 @@ type Repository struct { Hot int64 `xorm:"-"` Active int64 `xorm:"-"` + Alias string } // SanitizedOriginalURL returns a sanitized OriginalURL @@ -232,6 +233,14 @@ func (repo *Repository) SanitizedOriginalURL() string { return util.SanitizeURLCredentials(repo.OriginalURL, false) } +// GetAlias returns a sanitized OriginalURL +func (repo *Repository) DisplayName() string { + if repo.Alias == "" { + return repo.Name + } + return repo.Alias +} + // ColorFormat returns a colored string to represent this repo func (repo *Repository) ColorFormat(s fmt.State) { var ownerName interface{} @@ -285,6 +294,11 @@ func (repo *Repository) FullName() string { return repo.OwnerName + "/" + repo.Name } +// FullDisplayName returns the repository full display name +func (repo *Repository) FullDisplayName() string { + return repo.OwnerName + "/" + repo.DisplayName() +} + // HTMLURL returns the repository HTML URL func (repo *Repository) HTMLURL() string { return setting.AppURL + repo.FullName() @@ -385,6 +399,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) Owner: repo.Owner.APIFormat(), Name: repo.Name, FullName: repo.FullName(), + FullDisplayName: repo.FullDisplayName(), Description: repo.Description, Private: repo.IsPrivate, Template: repo.IsTemplate, @@ -995,6 +1010,7 @@ func CheckCreateRepository(doer, u *User, name string) error { // CreateRepoOptions contains the create repository options type CreateRepoOptions struct { Name string + Alias string Description string OriginalURL string GitServiceType api.GitServiceType diff --git a/models/repo_generate.go b/models/repo_generate.go index 480683cd4..08bb1463d 100644 --- a/models/repo_generate.go +++ b/models/repo_generate.go @@ -19,6 +19,7 @@ import ( // GenerateRepoOptions contains the template units to generate type GenerateRepoOptions struct { Name string + Alias string Description string Private bool GitContent bool diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 8061c6469..5c5d1ba5a 100755 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -29,6 +29,7 @@ import ( type CreateRepoForm struct { UID int64 `binding:"Required"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` + Alias string `binding:"Required;MaxSize(100)"` Private bool Description string `binding:"MaxSize(1024)"` DefaultBranch string `binding:"GitRefName;MaxSize(100)"` diff --git a/modules/repository/create.go b/modules/repository/create.go index d740c58b1..f5dfa63d6 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -28,6 +28,7 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m Owner: u, OwnerName: u.Name, Name: opts.Name, + Alias: opts.Alias, LowerName: strings.ToLower(opts.Name), Description: opts.Description, OriginalURL: opts.OriginalURL, diff --git a/modules/repository/generate.go b/modules/repository/generate.go index 6d80488de..86c9a5c28 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -236,6 +236,7 @@ func GenerateRepository(ctx models.DBContext, doer, owner *models.User, template Owner: owner, OwnerName: owner.Name, Name: opts.Name, + Alias: opts.Alias, LowerName: strings.ToLower(opts.Name), Description: opts.Description, IsPrivate: opts.Private, diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 70de9b746..2928b6123 100755 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -46,31 +46,32 @@ type ExternalWiki struct { // Repository represents a repository type Repository struct { - ID int64 `json:"id"` - Owner *User `json:"owner"` - Name string `json:"name"` - FullName string `json:"full_name"` - Description string `json:"description"` - Empty bool `json:"empty"` - Private bool `json:"private"` - Fork bool `json:"fork"` - Template bool `json:"template"` - Parent *Repository `json:"parent"` - Mirror bool `json:"mirror"` - Size int `json:"size"` - HTMLURL string `json:"html_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - OriginalURL string `json:"original_url"` - Website string `json:"website"` - Stars int `json:"stars_count"` - Forks int `json:"forks_count"` - Watchers int `json:"watchers_count"` - OpenIssues int `json:"open_issues_count"` - OpenPulls int `json:"open_pr_counter"` - Releases int `json:"release_counter"` - DefaultBranch string `json:"default_branch"` - Archived bool `json:"archived"` + ID int64 `json:"id"` + Owner *User `json:"owner"` + Name string `json:"name"` + FullName string `json:"full_name"` + FullDisplayName string `json:"full_display_name"` + Description string `json:"description"` + Empty bool `json:"empty"` + Private bool `json:"private"` + Fork bool `json:"fork"` + Template bool `json:"template"` + Parent *Repository `json:"parent"` + Mirror bool `json:"mirror"` + Size int `json:"size"` + HTMLURL string `json:"html_url"` + SSHURL string `json:"ssh_url"` + CloneURL string `json:"clone_url"` + OriginalURL string `json:"original_url"` + Website string `json:"website"` + Stars int `json:"stars_count"` + Forks int `json:"forks_count"` + Watchers int `json:"watchers_count"` + OpenIssues int `json:"open_issues_count"` + OpenPulls int `json:"open_pr_counter"` + Releases int `json:"release_counter"` + DefaultBranch string `json:"default_branch"` + Archived bool `json:"archived"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time diff --git a/routers/repo/repo.go b/routers/repo/repo.go index a182e9087..067a75c44 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -201,6 +201,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { if form.RepoTemplate > 0 { opts := models.GenerateRepoOptions{ Name: form.RepoName, + Alias: form.Alias, Description: form.Description, Private: form.Private, GitContent: form.GitContent, @@ -235,6 +236,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { } else { repo, err = repo_service.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{ Name: form.RepoName, + Alias: form.Alias, Description: form.Description, Gitignores: form.Gitignores, IssueLabels: form.IssueLabels, diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 138a323e1..2c54215d0 100755 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -24,7 +24,7 @@ {{end}} {{.Owner.Name}}
/
- {{.Name}} + {{.DisplayName}} {{if .RelAvatarLink}} {{if .IsTemplate}} {{if .IsPrivate}} @@ -114,9 +114,9 @@ {{end}} - + {{end}} - + {{if .Permission.CanRead $.UnitTypeIssues}} @@ -152,7 +152,7 @@ {{if .Permission.CanRead $.UnitTypeCloudBrain}} - + {{.i18n.Tr "repo.cloudbrain"}} diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index a1b4218dc..3eefca78c 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -13,12 +13,12 @@ {{.ShortActUserName}} {{end}} {{if eq .GetOpType 1}} - {{$.i18n.Tr "action.create_repo" .GetRepoLink .ShortRepoPath | Str2html}} + {{$.i18n.Tr "action.create_repo" .GetRepoLink .ShortRepoFullDisplayName | Str2html}} {{else if eq .GetOpType 2}} - {{$.i18n.Tr "action.rename_repo" .GetContent .GetRepoLink .ShortRepoPath | Str2html}} + {{$.i18n.Tr "action.rename_repo" .GetContent .GetRepoLink .ShortRepoFullDisplayName | Str2html}} {{else if eq .GetOpType 5}} {{ $branchLink := .GetBranch | EscapePound | Escape}} - {{$.i18n.Tr "action.commit_repo" .GetRepoLink $branchLink (Escape .GetBranch) .ShortRepoPath | Str2html}} + {{$.i18n.Tr "action.commit_repo" .GetRepoLink $branchLink (Escape .GetBranch) .ShortRepoFullDisplayName | Str2html}} {{else if eq .GetOpType 6}} {{ $index := index .GetIssueInfos 0}} {{$.i18n.Tr "action.create_issue" .GetRepoLink $index .ShortRepoPath | Str2html}} diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index dc1507403..5df52d77a 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -104,7 +104,7 @@
  • - ${repo.full_name} + ${repo.full_display_name} ${repo.stars_count} {{svg "octicon-star" 16}} From 6050394af74216dc3ed8526962b232c3565177c0 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Wed, 5 Jan 2022 17:19:23 +0800 Subject: [PATCH 02/23] add check_name --- models/repo.go | 6 ++++++ routers/repo/repo.go | 24 ++++++++++++++++++++++++ routers/routes/routes.go | 1 + 3 files changed, 31 insertions(+) diff --git a/models/repo.go b/models/repo.go index 7661f4dba..802a16465 100755 --- a/models/repo.go +++ b/models/repo.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "html/template" + "math/rand" "xorm.io/xorm" "code.gitea.io/gitea/modules/blockchain" @@ -2535,3 +2536,8 @@ func UpdateRepositoryCommitNum(repo *Repository) error { return nil } + +func GenerateDefaultRepoName(ownerName string) string { + now := time.Now().Format("20060102150405") + return ownerName + now + fmt.Sprint(rand.Intn(10)) +} diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 067a75c44..ac132c21a 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path" + "regexp" "strings" "code.gitea.io/gitea/models" @@ -554,3 +555,26 @@ func Status(ctx *context.Context) { "err": task.Errors, }) } + +var AlphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]") + +// CheckName returns repository's default name(by given alias) +func CheckName(ctx *context.Context) { + var r = make(map[string]string, 1) + q := ctx.Query("q") + owner := ctx.Query("owner") + if q == "" || owner == "" { + r["name"] = "" + ctx.JSON(200, r) + return + } + if !AlphaDashDotPattern.MatchString(q) { + r["name"] = q + ctx.JSON(200, r) + return + } + n := models.GenerateDefaultRepoName(owner) + r["name"] = n + ctx.JSON(200, r) + return +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 95931c723..72dc7a6e1 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -718,6 +718,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("/:repoid").Get(repo.Fork). Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) + m.Get("/check_name", repo.CheckName) }, reqSignIn) // ***** Release Attachment Download without Signin From ce0b64a292734d07ef8e17454ac8132f7b790803 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Wed, 5 Jan 2022 17:40:31 +0800 Subject: [PATCH 03/23] update repo display name --- templates/explore/repo_list.tmpl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index f27990701..141dde055 100755 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -5,7 +5,7 @@ border-radius: 0.8rem; margin-bottom: 1.0rem; padding: 1.0rem !important; - } + } .ui.repository.list>.item .header { font-size: 1.4rem !important; font-weight: 200; @@ -24,7 +24,7 @@ content: ""; height: 1px; background-color: #E1E3E6; - bottom: 2.8rem; + bottom: 2.8rem; } .repository .ui.mini.menu{ font-size: .6rem; @@ -43,13 +43,13 @@ + 热门{{.i18n.Tr "explore.repos"}} + 活跃{{.i18n.Tr "explore.repos"}} {{end}} @@ -93,7 +93,7 @@
    - {{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}} + {{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.DisplayName}} {{if .IsArchived}}{{end}} {{if .IsPrivate}} @@ -114,7 +114,7 @@ - + {{.Hot}} {{else if eq $.SortType "active"}} @@ -130,7 +130,7 @@ {{svg "octicon-git-branch" 16}} {{.NumForks}} - {{end}} + {{end}} {{svg "octicon-star" 16}} {{.NumStars}} From 58a6e421bf460a83cba3a5b2b75a9358161a6ba0 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Thu, 6 Jan 2022 15:31:41 +0800 Subject: [PATCH 04/23] update alias pattern check --- modules/auth/repo_form.go | 3 ++- modules/validation/binding.go | 24 ++++++++++++++++++++++++ options/locale/locale_zh-CN.ini | 1 + routers/repo/repo.go | 8 +++++--- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 5c5d1ba5a..6fefc135d 100755 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -29,7 +29,7 @@ import ( type CreateRepoForm struct { UID int64 `binding:"Required"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` - Alias string `binding:"Required;MaxSize(100)"` + Alias string `binding:"Required;MaxSize(100);AlphaDashDotChinese"` Private bool Description string `binding:"MaxSize(1024)"` DefaultBranch string `binding:"GitRefName;MaxSize(100)"` @@ -110,6 +110,7 @@ func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) { // RepoSettingForm form for changing repository settings type RepoSettingForm struct { RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` + Alias string `binding:"Required;AlphaDashDotChinese;MaxSize(100)"` Description string `binding:"MaxSize(255)"` Website string `binding:"ValidUrl;MaxSize(255)"` Interval string diff --git a/modules/validation/binding.go b/modules/validation/binding.go index 1c67878ea..b608cdea2 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -19,6 +19,8 @@ const ( // ErrGlobPattern is returned when glob pattern is invalid ErrGlobPattern = "GlobPattern" + + ErrAlphaDashDotChinese = "AlphaDashDotChineseError" ) var ( @@ -26,6 +28,8 @@ var ( // They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere. // They cannot have question-mark ?, asterisk *, or open bracket [ anywhere GitRefNamePatternInvalid = regexp.MustCompile(`[\000-\037\177 \\~^:?*[]+`) + + AlphaDashDotChinese = regexp.MustCompile("^[\u4e00-\u9fa5\\.\\-_A-Za-z0-9]+$") ) // CheckGitRefAdditionalRulesValid check name is valid on additional rules @@ -53,6 +57,7 @@ func AddBindingRules() { addGitRefNameBindingRule() addValidURLBindingRule() addGlobPatternRule() + addAlphaDashDotChineseRule() } func addGitRefNameBindingRule() { @@ -117,6 +122,21 @@ func addGlobPatternRule() { }) } +func addAlphaDashDotChineseRule() { + binding.AddRule(&binding.Rule{ + IsMatch: func(rule string) bool { + return strings.HasPrefix(rule, "AlphaDashDotChinese") + }, + IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + if !ValidAlphaDashDotChinese(fmt.Sprintf("%v", val)) { + errs.Add([]string{name}, ErrAlphaDashDotChinese, "ErrAlphaDashDotChinese") + return false, errs + } + return true, errs + }, + }) +} + func portOnly(hostport string) string { colon := strings.IndexByte(hostport, ':') if colon == -1 { @@ -139,3 +159,7 @@ func validPort(p string) bool { } return true } + +func ValidAlphaDashDotChinese(value string) bool { + return AlphaDashDotChinese.MatchString(value) +} diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 0389471c2..053884c2b 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -344,6 +344,7 @@ require_error=不能为空。 alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。 alpha_dash_dot_error=应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 git_ref_name_error=` 必须是格式良好的 git 引用名称。` +alpha_dash_dot_chinese_error=应该只包含字母数字中文, 破折号 ('-'), 下划线 ('_') 和点 ('. ') size_error=长度必须为 %s。 min_size_error=长度最小为 %s 个字符。 max_size_error=长度最大为 %s 个字符。 diff --git a/routers/repo/repo.go b/routers/repo/repo.go index ac132c21a..e20bc7fb3 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -6,6 +6,7 @@ package repo import ( + "code.gitea.io/gitea/modules/validation" "fmt" "net/url" "os" @@ -556,19 +557,20 @@ func Status(ctx *context.Context) { }) } -var AlphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]") +var repoNamePattern = regexp.MustCompile("^[0-9a-zA-Z\\.\\-_]{1,100}$") +var repoAliasPattern = regexp.MustCompile("^[\u4e00-\u9fa5\\.\\-_A-Za-z0-9]{1,100}$") // CheckName returns repository's default name(by given alias) func CheckName(ctx *context.Context) { var r = make(map[string]string, 1) q := ctx.Query("q") owner := ctx.Query("owner") - if q == "" || owner == "" { + if q == "" || owner == "" || len(q) > 100 || !validation.ValidAlphaDashDotChinese(q) { r["name"] = "" ctx.JSON(200, r) return } - if !AlphaDashDotPattern.MatchString(q) { + if repoNamePattern.MatchString(q) { r["name"] = q ctx.JSON(200, r) return From 8b4ffbaf18cd4017e9c8f2ff3c00d59ba1489232 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Thu, 6 Jan 2022 16:21:12 +0800 Subject: [PATCH 05/23] fix issue --- templates/repo/create.tmpl | 47 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 641dd3de6..e5aa2a630 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -9,7 +9,7 @@
    {{template "base/alert" .}} -
    + +
    + + +
    + +
    + +
    + + + +
    + +
    /
    +
    + + + +
    +
    + + +
    From 01ed84bbb999076b583f5f1472885d3c685a5bac Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Fri, 7 Jan 2022 09:39:38 +0800 Subject: [PATCH 06/23] check alias --- models/repo.go | 58 +++++++++++++++++++++++++++++------------ modules/auth/repo_form.go | 1 + modules/structs/repo.go | 1 + modules/task/task.go | 1 + options/locale/locale_en-US.ini | 5 ++-- routers/repo/repo.go | 3 ++- 6 files changed, 49 insertions(+), 20 deletions(-) diff --git a/models/repo.go b/models/repo.go index 802a16465..3311869c0 100755 --- a/models/repo.go +++ b/models/repo.go @@ -936,17 +936,24 @@ func (repo *Repository) DescriptionHTML() template.HTML { return template.HTML(markup.Sanitize(string(desc))) } -func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { - has, err := e.Get(&Repository{ - OwnerID: u.ID, - LowerName: strings.ToLower(repoName), - }) - return has && com.IsDir(RepoPath(u.Name, repoName)), err +func isRepositoryExist(e Engine, u *User, repoName string, alias string) (bool, error) { + var cond = builder.NewCond() + cond = cond.And(builder.Eq{"owner_id": u.ID}) + if alias != "" { + subCon := builder.NewCond() + subCon = subCon.Or(builder.Eq{"alias": alias}, builder.Eq{"lower_name": repoName}) + cond = cond.And(subCon) + } else { + cond = cond.And(builder.Eq{"lower_name": repoName}) + } + count, err := e.Where(cond).Count(&Repository{}) + //todo 确定从 && 改成 || 是否有问题 + return count > 0 || com.IsDir(RepoPath(u.Name, repoName)), err } // IsRepositoryExist returns true if the repository with given name under user has already existed. -func IsRepositoryExist(u *User, repoName string) (bool, error) { - return isRepositoryExist(x, u, repoName) +func IsRepositoryExist(u *User, repoName string, alias string) (bool, error) { + return isRepositoryExist(x, u, repoName, alias) } // CloneLink represents different types of clone URLs of repository. @@ -990,20 +997,24 @@ func (repo *Repository) CloneLink() (cl *CloneLink) { } // CheckCreateRepository check if could created a repository -func CheckCreateRepository(doer, u *User, name string) error { +func CheckCreateRepository(doer, u *User, repoName, alias string) error { if !doer.CanCreateRepo() { return ErrReachLimitOfRepo{u.MaxRepoCreation} } - if err := IsUsableRepoName(name); err != nil { + if err := IsUsableRepoName(repoName); err != nil { + return err + } + + if err := IsUsableRepoAlias(alias); err != nil { return err } - has, err := isRepositoryExist(x, u, name) + has, err := isRepositoryExist(x, u, repoName, alias) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{u.Name, name} + return ErrRepoAlreadyExist{u.Name, repoName} } return nil } @@ -1052,8 +1063,10 @@ func GetRepoInitFile(tp, name string) ([]byte, error) { } var ( - reservedRepoNames = []string{".", ".."} - reservedRepoPatterns = []string{"*.git", "*.wiki"} + reservedRepoNames = []string{".", ".."} + reservedRepoPatterns = []string{"*.git", "*.wiki"} + reservedRepoAliasNames = []string{} + reservedRepoAliasPatterns = []string{} ) // IsUsableRepoName returns true when repository is usable @@ -1061,13 +1074,21 @@ func IsUsableRepoName(name string) error { return isUsableName(reservedRepoNames, reservedRepoPatterns, name) } +// IsUsableRepoAlias returns true when repository alias is usable +func IsUsableRepoAlias(name string) error { + return isUsableName(reservedRepoAliasNames, reservedRepoAliasPatterns, name) +} + // CreateRepository creates a repository for the user/organization. func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error) { if err = IsUsableRepoName(repo.Name); err != nil { return err } - has, err := isRepositoryExist(ctx.e, u, repo.Name) + if err := IsUsableRepoAlias(repo.Alias); err != nil { + return err + } + has, err := isRepositoryExist(ctx.e, u, repo.Name, repo.Alias) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { @@ -1249,7 +1270,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newOwner, repo.Name) + has, err := IsRepositoryExist(newOwner, repo.Name, repo.Alias) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { @@ -1382,7 +1403,7 @@ func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err return err } - has, err := IsRepositoryExist(repo.Owner, newRepoName) + has, err := IsRepositoryExist(repo.Owner, newRepoName, "") if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { @@ -2538,6 +2559,9 @@ func UpdateRepositoryCommitNum(repo *Repository) error { } func GenerateDefaultRepoName(ownerName string) string { + if len(ownerName) > 5 { + ownerName = ownerName[:5] + } now := time.Now().Format("20060102150405") return ownerName + now + fmt.Sprint(rand.Intn(10)) } diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 6fefc135d..32890a5c1 100755 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -63,6 +63,7 @@ type MigrateRepoForm struct { UID int64 `json:"uid" binding:"Required"` // required: true RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` + Alias string `json:"alias" binding:"Required;AlphaDashDotChinese;MaxSize(100)"` Mirror bool `json:"mirror"` Private bool `json:"private"` Description string `json:"description" binding:"MaxSize(255)"` diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 2928b6123..b824813bb 100755 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -218,6 +218,7 @@ type MigrateRepoOption struct { UID int `json:"uid" binding:"Required"` // required: true RepoName string `json:"repo_name" binding:"Required"` + Alias string `json:"alias" binding:"Required"` Mirror bool `json:"mirror"` Private bool `json:"private"` Description string `json:"description"` diff --git a/modules/task/task.go b/modules/task/task.go index 72f111ecc..722e39bec 100644 --- a/modules/task/task.go +++ b/modules/task/task.go @@ -84,6 +84,7 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models. repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{ Name: opts.RepoName, + Alias: opts.Alias, Description: opts.Description, OriginalURL: opts.OriginalURL, GitServiceType: opts.GitServiceType, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 35f0a98bf..98b2a92f0 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -340,6 +340,7 @@ require_error = ` cannot be empty.` alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` git_ref_name_error = ` must be a well-formed Git reference name.` +alpha_dash_dot_chinese_error= ` should contain only alphanumeric, chinese, dash ('-') and underscore ('_') characters.` size_error = ` must be size %s.` min_size_error = ` must contain at least %s characters.` max_size_error = ` must contain at most %s characters.` @@ -856,7 +857,7 @@ modelarts.train_job.description=Description modelarts.train_job.parameter_setting=Parameter setting modelarts.train_job.parameter_setting_info=Parameter Info modelarts.train_job.fast_parameter_setting=fast_parameter_setting -modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config +modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config modelarts.train_job.fast_parameter_setting_config_link=fast_parameter_setting_config_link modelarts.train_job.frames=frames modelarts.train_job.algorithm_origin=Algorithm Origin @@ -1136,7 +1137,7 @@ issues.filter_label_exclude = `Use alt + click/enter t issues.filter_label_no_select = All labels issues.filter_milestone = Milestone issues.filter_milestone_no_select = All milestones -issues.filter_milestone_no_add = Not add milestones +issues.filter_milestone_no_add = Not add milestones issues.filter_assignee = Assignee issues.filter_assginee_no_select = All assignees issues.filter_type = Type diff --git a/routers/repo/repo.go b/routers/repo/repo.go index e20bc7fb3..a509cb52e 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -362,6 +362,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { GitServiceType: gitServiceType, CloneAddr: remoteAddr, RepoName: form.RepoName, + Alias: form.Alias, Description: form.Description, Private: form.Private || setting.Repository.ForcePrivate, Mirror: form.Mirror, @@ -384,7 +385,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { opts.Releases = false } - err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName) + err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, opts.Alias) if err != nil { handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) return From ec49bebe3a50345736d708377d72bd621e7885b9 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Fri, 7 Jan 2022 09:59:50 +0800 Subject: [PATCH 07/23] fix issue --- templates/repo/create.tmpl | 52 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index e5aa2a630..1526a2150 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -41,7 +41,7 @@
    -->
    - +
    @@ -78,12 +78,16 @@
    - +
    +
    + + - +
    +
    @@ -228,3 +232,45 @@
    {{template "base/footer" .}} + \ No newline at end of file From 2baefa48f619d1693f04467aaaccbd3b5288d2e1 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Fri, 7 Jan 2022 11:15:14 +0800 Subject: [PATCH 08/23] fix issue --- templates/repo/create.tmpl | 30 ------------------------------ templates/repo/settings/options.tmpl | 3 ++- templates/user/settings/repos.tmpl | 2 +- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 1526a2150..2d89bf661 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -9,36 +9,6 @@
    {{template "base/alert" .}} -
    diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index b9195a266..8eb57fc2a 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -13,7 +13,8 @@
    - + +
    diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl index d28f067fd..d859954be 100644 --- a/templates/user/settings/repos.tmpl +++ b/templates/user/settings/repos.tmpl @@ -21,7 +21,7 @@ {{else}} {{svg "octicon-repo" 16}} {{end}} - {{$.Owner.Name}}/{{.Name}} + {{$.Owner.Name}}/{{.DisplayName}} {{SizeFmt .Size}} {{if .IsFork}} {{$.i18n.Tr "repo.forked_from"}} From 9d80718b769b59a651542d0291c4e17f544f8bf7 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Fri, 7 Jan 2022 11:27:43 +0800 Subject: [PATCH 09/23] fix issue --- templates/repo/settings/options.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 8eb57fc2a..a9b637420 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -13,8 +13,8 @@
    - - + +
    From 413f17b899fa6d3e5a4add2c67bc5bb1ff26f202 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Fri, 7 Jan 2022 11:33:51 +0800 Subject: [PATCH 10/23] add alias in repo setting --- routers/repo/setting.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index f7da8f4a8..055627fc1 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -98,6 +98,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { repo.Description = form.Description repo.Website = form.Website repo.IsTemplate = form.Template + repo.Alias = form.Alias // Visibility of forked repository is forced sync with base repository. if repo.IsFork { From 1f716e2fa7a802919859f3ae62553926c3356021 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Fri, 7 Jan 2022 14:40:50 +0800 Subject: [PATCH 11/23] update error msg for alpha_dash_dot_chinese_error --- modules/auth/auth.go | 2 ++ options/locale/locale_en-US.ini | 3 ++- options/locale/locale_zh-CN.ini | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 352e50ca0..6a156491d 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -186,6 +186,8 @@ func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaro data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field)) case validation.ErrGlobPattern: data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) + case validation.ErrAlphaDashDotChinese: + data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_chinese_error") default: data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 98b2a92f0..94e265e7b 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -315,7 +315,8 @@ modify = Update [form] UserName = Username -RepoName = Repository name +Alias = Repository name +RepoName = Repository path Email = Email address Password = Password Retype = Re-Type Password diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 053884c2b..a2f4a42a5 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -319,7 +319,8 @@ modify=更新 [form] UserName=用户名 -RepoName=项目名称 +RepoName=项目路径 +Alias=项目名称 Email=邮箱地址 Password=密码 Retype=重新输入密码 @@ -344,7 +345,7 @@ require_error=不能为空。 alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。 alpha_dash_dot_error=应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 git_ref_name_error=` 必须是格式良好的 git 引用名称。` -alpha_dash_dot_chinese_error=应该只包含字母数字中文, 破折号 ('-'), 下划线 ('_') 和点 ('. ') +alpha_dash_dot_chinese_error=应该只包含字母数字中文, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 size_error=长度必须为 %s。 min_size_error=长度最小为 %s 个字符。 max_size_error=长度最大为 %s 个字符。 From d2e5afc5b19104e787b85647f0a427dcdab40a0f Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Fri, 7 Jan 2022 14:49:56 +0800 Subject: [PATCH 12/23] add alias to repo search field --- models/repo_list.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models/repo_list.go b/models/repo_list.go index c4d8ee823..d2762d4d3 100755 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -200,8 +200,8 @@ const ( SearchOrderByForks SearchOrderBy = "num_forks ASC" SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC" - SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC" - SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC" + SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC" + SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC" ) // SearchRepositoryCondition creates a query condition according search repository options @@ -321,6 +321,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { var likes = builder.NewCond() for _, v := range strings.Split(opts.Keyword, ",") { likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)}) + likes = likes.Or(builder.Like{"alias", strings.ToLower(v)}) if opts.IncludeDescription { likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)}) } From bb0d6e611dbacc44225bab95ef00080233518df0 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Mon, 10 Jan 2022 11:33:34 +0800 Subject: [PATCH 13/23] add alias when fork --- modules/repository/fork.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/repository/fork.go b/modules/repository/fork.go index 2ed2a0eb7..7549c2661 100644 --- a/modules/repository/fork.go +++ b/modules/repository/fork.go @@ -33,6 +33,7 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, Owner: owner, OwnerName: owner.Name, Name: name, + Alias: oldRepo.Alias, LowerName: strings.ToLower(name), Description: desc, DefaultBranch: oldRepo.DefaultBranch, From 8a8fb49ad6d8c5d6d77edc822c271f8ff2a9274d Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Tue, 11 Jan 2022 09:48:07 +0800 Subject: [PATCH 14/23] fix issue --- options/locale/locale_en-US.ini | 3 ++ options/locale/locale_zh-CN.ini | 3 ++ templates/repo/create.tmpl | 51 ++++------------------------ templates/repo/settings/options.tmpl | 55 +++++++++++++++++++++++++++++- web_src/js/index.js | 66 +++++++++++++++++++++++++++++++++++- 5 files changed, 132 insertions(+), 46 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 98b2a92f0..2f57863e3 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -917,6 +917,9 @@ template.avatar = Avatar template.issue_labels = Issue Labels template.one_item = Must select at least one template item template.invalid = Must select a template repository +template.repo_adress=Adress +template.repo_path=path +template.repo_name=Name archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests. archive.issue.nocomment = This repo is archived. You cannot comment on issues. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 053884c2b..e34092ac9 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -927,6 +927,9 @@ template.avatar=头像 template.issue_labels=任务标签 template.one_item=必须至少选择一个模板项 template.invalid=必须选择一个模板项目 +template.repo_adress=项目地址 +template.repo_path=项目地址 +template.repo_name=项目名称 archive.title=此项目已存档。您可以查看文件和克隆,但不能推送或创建任务/合并请求。 archive.issue.nocomment=此项目已存档,您不能在此任务添加评论。 diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 2d89bf661..3e55a24fa 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -10,13 +10,13 @@
    {{template "base/alert" .}}
    - +
    - +
    -
    - - - +
    @@ -203,44 +203,7 @@
    {{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index a9b637420..ecea30e52 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -11,11 +11,64 @@
    {{.CsrfTokenHtml}} -
    + +
    + + +
    + +
    + +
    +
    + + + +
    + +
    /
    +
    + + + +
    +
    + +
    + + + +
    {{SizeFmt .Repository.Size}} diff --git a/web_src/js/index.js b/web_src/js/index.js index 7d59cc0eb..68e7e8d7a 100755 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -4136,4 +4136,68 @@ $('.question.circle.icon').hover(function(){ //云脑详情页面跳转回上一个页面 $(".section.backTodeBug").attr("href",localStorage.getItem('all')) //新建调试取消跳转 -$(".ui.button.cancel").attr("href",localStorage.getItem('all')) \ No newline at end of file +$(".ui.button.cancel").attr("href",localStorage.getItem('all')) + + +function initcreateRepo(){ + let timeout; + let keydown_flag = false + $('#repo_name').keyup(function(e){ + keydown_flag = $('#repo_name').val() ? true : false + if(keydown_flag){ + $('#repoAdress').css("display","block") + $('#repoAdress span').text($('#repo_name').val()+'.git') + + } + }) + + // const createRepoAdress = () => { + // //在此处写调用的方法,可以实现仅最后一次操作生效 + // const aliasValue = $('#alias').val() + // const ownerValue = $('#uid').val() + // if(keydown_flag){ + // $('#repo_name').attr("placeholder","") + // } + // else if(aliasValue){ + // $('#repo_name').attr("placeholder","正在获取路径...") + // $.get(`${window.config.AppSubUrl}/repo/check_name?q=${aliasValue}&owner=${ownerValue }`,(data)=>{ + // const repo_name = data.name + // $('#repo_name').val(repo_name) + // $('#repoAdress').css("display","block") + // $('#repoAdress span').text("{{AppSubUrl}}/{{.ContextUser.ShortName 20}}/"+$('#repo_name').val()+'.git') + // }) + // }else{ + // $('#repo_name').val('') + // $('#repo_name').attr("placeholder","") + // } + // } + + $("#alias").on('input',function(){ + clearTimeout(timeout) + timeout = setTimeout(() => { + //在此处写调用的方法,可以实现仅最后一次操作生效 + const aliasValue = $('#alias').val() + const ownerValue = $('#uid').val() + if(keydown_flag){ + $('#repo_name').attr("placeholder","") + } + else if(aliasValue){ + $('#repo_name').attr("placeholder","正在获取路径...") + $.get(`${window.config.AppSubUrl}/repo/check_name?q=${aliasValue}&owner=${ownerValue }`,(data)=>{ + const repo_name = data.name + $('#repo_name').val(repo_name) + $('#repoAdress').css("display","block") + $('#repoAdress span').text("{{AppSubUrl}}/{{.ContextUser.ShortName 20}}/"+$('#repo_name').val()+'.git') + }) + }else{ + $('#repo_name').val('') + $('#repo_name').attr("placeholder","") + } + }, 500) + }) + +} + +initcreateRepo() + + From 80f949c14d13c00b990a3bf52de8279e5afa75d8 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 11 Jan 2022 14:28:02 +0800 Subject: [PATCH 15/23] update --- models/repo.go | 1 - models/repo_list.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/models/repo.go b/models/repo.go index 3311869c0..3e32c831c 100755 --- a/models/repo.go +++ b/models/repo.go @@ -947,7 +947,6 @@ func isRepositoryExist(e Engine, u *User, repoName string, alias string) (bool, cond = cond.And(builder.Eq{"lower_name": repoName}) } count, err := e.Where(cond).Count(&Repository{}) - //todo 确定从 && 改成 || 是否有问题 return count > 0 || com.IsDir(RepoPath(u.Name, repoName)), err } diff --git a/models/repo_list.go b/models/repo_list.go index d2762d4d3..e41b0f49d 100755 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -321,7 +321,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { var likes = builder.NewCond() for _, v := range strings.Split(opts.Keyword, ",") { likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)}) - likes = likes.Or(builder.Like{"alias", strings.ToLower(v)}) + likes = likes.Or(builder.Like{"alias", v}) if opts.IncludeDescription { likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)}) } From f7d72d36b5785c9742d6a93f51a66b7e6e89dea4 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Tue, 11 Jan 2022 15:50:15 +0800 Subject: [PATCH 16/23] fix issue --- options/locale/locale_en-US.ini | 5 +- options/locale/locale_zh-CN.ini | 4 ++ templates/repo/create.tmpl | 93 +----------------------------------- templates/repo/migrate.tmpl | 30 +----------- templates/repo/pulls/fork.tmpl | 9 ++-- templates/repo/repo_name.tmpl | 50 +++++++++++++++++++ templates/repo/settings/options.tmpl | 53 +++++++++++++++++++- web_src/js/index.js | 60 ++++++++++++++++++++++- web_src/less/_form.less | 2 +- 9 files changed, 176 insertions(+), 130 deletions(-) create mode 100644 templates/repo/repo_name.tmpl diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 94e265e7b..db148498c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -316,7 +316,8 @@ modify = Update [form] UserName = Username Alias = Repository name -RepoName = Repository path +RepoPath = Repository path +RepoAdress = Repository Adress Email = Email address Password = Password Retype = Re-Type Password @@ -340,6 +341,8 @@ SSPIDefaultLanguage = Default Language require_error = ` cannot be empty.` alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` +reponame_dash_dot_error=` Please enter Chinese, alphanumeric, dash ('-') ,underscore ('_') and dot ('.')characters, up to 100 characters. ` +repoadd_dash_dot_error=` Path only allows input alphanumeric, dash ('-') ,underscore ('_') and dot ('.')characters, up to 100 characters. ` git_ref_name_error = ` must be a well-formed Git reference name.` alpha_dash_dot_chinese_error= ` should contain only alphanumeric, chinese, dash ('-') and underscore ('_') characters.` size_error = ` must be size %s.` diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index a2f4a42a5..c5c499eb0 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -321,6 +321,8 @@ modify=更新 UserName=用户名 RepoName=项目路径 Alias=项目名称 +RepoPath=项目路径 +RepoAdress=项目地址 Email=邮箱地址 Password=密码 Retype=重新输入密码 @@ -344,6 +346,8 @@ SSPIDefaultLanguage=默认语言 require_error=不能为空。 alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。 alpha_dash_dot_error=应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 +reponame_dash_dot_error=请输入中文、字母、数字和-_.、最多100个字符。 +repoadd_dash_dot_error=路径只允许字母、数字和-_.,最多100个字符。 git_ref_name_error=` 必须是格式良好的 git 引用名称。` alpha_dash_dot_chinese_error=应该只包含字母数字中文, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 size_error=长度必须为 %s。 diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 2d89bf661..53aec2aca 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -9,54 +9,7 @@
    {{template "base/alert" .}} -
    - - - -
    - -
    - -
    - - - -
    - -
    /
    -
    - - - -
    -
    - -
    - - - -
    + {{template "repo/repo_name" .}}
    @@ -201,46 +154,4 @@
    -{{template "base/footer" .}} - \ No newline at end of file +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/migrate.tmpl b/templates/repo/migrate.tmpl index daf8c549e..61d2f4b21 100644 --- a/templates/repo/migrate.tmpl +++ b/templates/repo/migrate.tmpl @@ -37,35 +37,7 @@
    - -
    - - -
    - -
    - - -
    + {{template "repo/repo_name" .}}
    diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl index 30f9939fa..bbf4be0b1 100644 --- a/templates/repo/pulls/fork.tmpl +++ b/templates/repo/pulls/fork.tmpl @@ -9,7 +9,7 @@
    {{template "base/alert" .}} -
    +
    {{.ForkFrom}}
    -
    - - -
    + {{template "repo/repo_name" .}}
    diff --git a/templates/repo/repo_name.tmpl b/templates/repo/repo_name.tmpl new file mode 100644 index 000000000..2a9c00097 --- /dev/null +++ b/templates/repo/repo_name.tmpl @@ -0,0 +1,50 @@ +
    + + + {{.i18n.Tr "form.reponame_dash_dot_error"}} +
    + +
    + +
    + + + +
    + +
    /
    +
    + + + +
    + + +
    + {{.i18n.Tr "form.repoadd_dash_dot_error"}} + \ No newline at end of file diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index a9b637420..24283890c 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -11,10 +11,61 @@ {{.CsrfTokenHtml}} -
    + +
    + + + {{.i18n.Tr "form.reponame_dash_dot_error"}} +
    + +
    + +
    +
    + + + +
    + +
    /
    +
    + + + {{.i18n.Tr "form.repoadd_dash_dot_error"}} +
    +
    + +
    + +
    diff --git a/web_src/js/index.js b/web_src/js/index.js index 7d59cc0eb..d1bec586a 100755 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -4136,4 +4136,62 @@ $('.question.circle.icon').hover(function(){ //云脑详情页面跳转回上一个页面 $(".section.backTodeBug").attr("href",localStorage.getItem('all')) //新建调试取消跳转 -$(".ui.button.cancel").attr("href",localStorage.getItem('all')) \ No newline at end of file +$(".ui.button.cancel").attr("href",localStorage.getItem('all')) + +function initcreateRepo(){ + let timeout; + let keydown_flag = false + const urlAdd = location.href.split('/')[0] + '//' + location.href.split('/')[2] + let owner = $('input[name=uid]').val() + console.log(owner) + console.log(location.href.split('/'),urlAdd) + $(document).ready(function(){ + $('#ownerDropdown').dropdown({ + onChange:function(value){ + owner = value + $('#repoAdress').css("display","block") + $('#repoAdress span').text(urlAdd+'/'+owner+'/'+$('#repo_name').val()+'.git') + } + }); + }) + $('#repo_name').keyup(function(){ + keydown_flag = $('#repo_name').val() ? true : false + if(keydown_flag){ + $('#repoAdress').css("display","block") + $('#repoAdress span').text(urlAdd+'/'+owner+'/'+$('#repo_name').val()+'.git') + console.log("key_up",owner) + } + else{ + $('#repoAdress').css("display","none") + $('#repo_name').attr("placeholder","") + } + }) + + $('#alias').bind('input propertychange', function (event) { + clearTimeout(timeout) + timeout = setTimeout(() => { + //在此处写调用的方法,可以实现仅最后一次操作生效 + const aliasValue = $('#alias').val() + const ownerValue = $('#uid').val() + if(keydown_flag){ + $('#repo_name').attr("placeholder","") + } + else if(aliasValue){ + $('#repo_name').attr("placeholder","正在获取路径...") + $.get(`${window.config.AppSubUrl}/repo/check_name?q=${aliasValue}&owner=${ownerValue }`,(data)=>{ + const repo_name = data.name + $('#repo_name').val(repo_name) + $('#repoAdress').css("display","block") + $('#repoAdress span').text(urlAdd+'/'+owner+'/'+$('#repo_name').val()+'.git') + console.log("oninput",owner) + }) + }else{ + $('#repo_name').val('') + $('#repo_name').attr("placeholder","") + $('#repoAdress').css("display","none") + } + }, 500) + }); +} + +initcreateRepo() \ No newline at end of file diff --git a/web_src/less/_form.less b/web_src/less/_form.less index de48f0ba5..e41c428c8 100644 --- a/web_src/less/_form.less +++ b/web_src/less/_form.less @@ -2,7 +2,7 @@ .help { color: #999999; padding-top: .6em; - padding-bottom: .6em; + display: inline-block; } } From 25b04fcd13a6f020c24bdac6135c505b4336248e Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Tue, 11 Jan 2022 17:42:16 +0800 Subject: [PATCH 17/23] fix issue --- templates/repo/create.tmpl | 4 ++-- templates/repo/migrate.tmpl | 4 ++-- templates/repo/pulls/fork.tmpl | 28 +----------------------- templates/repo/repo_name.tmpl | 8 +++---- templates/repo/settings/options.tmpl | 2 +- web_src/js/index.js | 42 +++++++++++++++++++++++++++--------- 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 53aec2aca..12bd0551c 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -2,7 +2,7 @@
    - + {{.CsrfTokenHtml}}

    {{.i18n.Tr "new_repo"}} @@ -144,7 +144,7 @@
    - {{.i18n.Tr "cancel"}} diff --git a/templates/repo/migrate.tmpl b/templates/repo/migrate.tmpl index 61d2f4b21..da4b31896 100644 --- a/templates/repo/migrate.tmpl +++ b/templates/repo/migrate.tmpl @@ -2,7 +2,7 @@
    - + {{.CsrfTokenHtml}}

    {{.i18n.Tr "new_migrate"}} @@ -99,7 +99,7 @@
    - {{.i18n.Tr "cancel"}} diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl index bbf4be0b1..ccffe7170 100644 --- a/templates/repo/pulls/fork.tmpl +++ b/templates/repo/pulls/fork.tmpl @@ -2,39 +2,13 @@
    - + {{.CsrfTokenHtml}}

    {{.i18n.Tr "new_fork"}}

    {{template "base/alert" .}} - -
    {{.ForkFrom}} diff --git a/templates/repo/repo_name.tmpl b/templates/repo/repo_name.tmpl index 2a9c00097..7c43cf814 100644 --- a/templates/repo/repo_name.tmpl +++ b/templates/repo/repo_name.tmpl @@ -9,19 +9,19 @@

    - + {{.CsrfTokenHtml}} -
    /
    @@ -67,54 +49,6 @@
    - -
    - -
    -
    - - - -
    - -
    /
    -
    - - - -
    -
    - -
    - - - -
    {{SizeFmt .Repository.Size}} From c802b47c27be15f0b93452e9eaa2fa4c1e01e9dd Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Fri, 14 Jan 2022 16:56:57 +0800 Subject: [PATCH 22/23] =?UTF-8?q?=E5=8D=83=E6=A0=A1=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E4=BB=A3=E7=A0=81=E5=90=88=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/repo.go | 75 ++++++++++----- models/repo_list.go | 21 +++++ modules/auth/repo_form.go | 12 +++ modules/repository/create.go | 10 +- options/locale/locale_en-US.ini | 8 ++ options/locale/locale_zh-CN.ini | 8 ++ routers/home.go | 65 ++----------- routers/org/home.go | 41 +++++++-- routers/org/members.go | 10 +- routers/org/teams.go | 11 ++- routers/repo/course.go | 189 ++++++++++++++++++++++++++++++++++++++ routers/routes/routes.go | 7 ++ services/repository/repository.go | 83 ++++++++++++++++- 13 files changed, 447 insertions(+), 93 deletions(-) create mode 100644 routers/repo/course.go diff --git a/models/repo.go b/models/repo.go index 8ba48f7a3..93e2cb140 100755 --- a/models/repo.go +++ b/models/repo.go @@ -140,6 +140,7 @@ func NewRepoContext() { // RepositoryStatus defines the status of repository type RepositoryStatus int type RepoBlockChainStatus int +type RepoType int // all kinds of RepositoryStatus const ( @@ -153,6 +154,11 @@ const ( RepoBlockChainFailed ) +const ( + RepoNormal RepoType = iota + RepoCourse +) + // Repository represents a git repository. type Repository struct { ID int64 `xorm:"pk autoincr"` @@ -166,7 +172,8 @@ type Repository struct { OriginalServiceType api.GitServiceType `xorm:"index"` OriginalURL string `xorm:"VARCHAR(2048)"` DefaultBranch string - + CreatorID int64 `xorm:"INDEX NOT NULL DEFAULT 0"` + Creator *User `xorm:"-"` NumWatches int NumStars int NumForks int @@ -175,11 +182,12 @@ type Repository struct { NumOpenIssues int `xorm:"-"` NumPulls int NumClosedPulls int - NumOpenPulls int `xorm:"-"` - NumMilestones int `xorm:"NOT NULL DEFAULT 0"` - NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` - NumOpenMilestones int `xorm:"-"` - NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` + NumOpenPulls int `xorm:"-"` + NumMilestones int `xorm:"NOT NULL DEFAULT 0"` + NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` + NumOpenMilestones int `xorm:"-"` + NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` + RepoType RepoType `xorm:"NOT NULL DEFAULT 0"` IsPrivate bool `xorm:"INDEX"` IsEmpty bool `xorm:"INDEX"` @@ -1035,6 +1043,8 @@ type CreateRepoOptions struct { IsMirror bool AutoInit bool Status RepositoryStatus + IsCourse bool + Topics []string } // GetRepoInitFile returns repository init files @@ -1080,7 +1090,7 @@ func IsUsableRepoAlias(name string) error { } // CreateRepository creates a repository for the user/organization. -func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error) { +func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...CreateRepoOptions) (err error) { if err = IsUsableRepoName(repo.Name); err != nil { return err } @@ -1094,7 +1104,10 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error } else if has { return ErrRepoAlreadyExist{u.Name, repo.Name} } - + isCourse := isCourse(opts) + if isCourse { + repo.CreatorID = doer.ID + } if _, err = ctx.e.Insert(repo); err != nil { return err } @@ -1128,17 +1141,23 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error Config: &PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true}, }) } else if tp == UnitTypeDatasets { - units = append(units, RepoUnit{ - RepoID: repo.ID, - Type: tp, - Config: &DatasetConfig{EnableDataset: true}, - }) + if !isCourse { + units = append(units, RepoUnit{ + RepoID: repo.ID, + Type: tp, + Config: &DatasetConfig{EnableDataset: true}, + }) + } + } else if tp == UnitTypeCloudBrain { - units = append(units, RepoUnit{ - RepoID: repo.ID, - Type: tp, - Config: &CloudBrainConfig{EnableCloudBrain: true}, - }) + if !isCourse { + units = append(units, RepoUnit{ + RepoID: repo.ID, + Type: tp, + Config: &CloudBrainConfig{EnableCloudBrain: true}, + }) + } + } else if tp == UnitTypeBlockChain { units = append(units, RepoUnit{ RepoID: repo.ID, @@ -1146,11 +1165,13 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error Config: &BlockChainConfig{EnableBlockChain: true}, }) } else if tp == UnitTypeModelManage { - units = append(units, RepoUnit{ - RepoID: repo.ID, - Type: tp, - Config: &ModelManageConfig{EnableModelManage: true}, - }) + if !isCourse { + units = append(units, RepoUnit{ + RepoID: repo.ID, + Type: tp, + Config: &ModelManageConfig{EnableModelManage: true}, + }) + } } else { units = append(units, RepoUnit{ RepoID: repo.ID, @@ -1220,6 +1241,14 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error return nil } +func isCourse(opts []CreateRepoOptions) bool { + var isCourse = false + if len(opts) > 0 { + isCourse = opts[0].IsCourse + } + return isCourse +} + func countRepositories(userID int64, private bool) int64 { sess := x.Where("id > 0") diff --git a/models/repo_list.go b/models/repo_list.go index e41b0f49d..6fb9380de 100755 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -48,9 +48,12 @@ func (repos RepositoryList) loadAttributes(e Engine) error { set := make(map[int64]struct{}) repoIDs := make([]int64, len(repos)) + setCreator := make(map[int64]struct{}) for i := range repos { set[repos[i].OwnerID] = struct{}{} repoIDs[i] = repos[i].ID + setCreator[repos[i].CreatorID] = struct{}{} + } // Load owners. @@ -61,8 +64,18 @@ func (repos RepositoryList) loadAttributes(e Engine) error { Find(&users); err != nil { return fmt.Errorf("find users: %v", err) } + //Load creator + creators := make(map[int64]*User, len(set)) + if err := e. + Where("id > 0"). + In("id", keysInt64(setCreator)). + Find(&creators); err != nil { + return fmt.Errorf("find create repo users: %v", err) + } + for i := range repos { repos[i].Owner = users[repos[i].OwnerID] + repos[i].Creator = creators[repos[i].CreatorID] } // Load primary language. @@ -174,6 +187,10 @@ type SearchRepoOptions struct { // True -> include just has milestones // False -> include just has no milestone HasMilestones util.OptionalBool + // None -> include all repos + // True -> include just courses + // False -> include just no courses + Course util.OptionalBool } //SearchOrderBy is used to sort the result @@ -351,6 +368,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue}) } + if opts.Course == util.OptionalBoolTrue { + cond = cond.And(builder.Eq{"repo_type": RepoCourse}) + } + if opts.Actor != nil && opts.Actor.IsRestricted { cond = cond.And(accessibleRepositoryCondition(opts.Actor)) } diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 32890a5c1..c113aa890 100755 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -728,3 +728,15 @@ type DeadlineForm struct { func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { return validate(errs, ctx.Data, f, ctx.Locale) } + +type CreateCourseForm struct { + RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` + Alias string `binding:"Required;MaxSize(100);AlphaDashDotChinese"` + Topics string + Description string `binding:"MaxSize(1024)"` +} + +// Validate validates the fields +func (f *CreateCourseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/modules/repository/create.go b/modules/repository/create.go index f5dfa63d6..2a24cb708 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -22,6 +22,10 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m Limit: u.MaxRepoCreation, } } + var RepoType = models.RepoNormal + if opts.IsCourse { + RepoType = models.RepoCourse + } repo := &models.Repository{ OwnerID: u.ID, @@ -38,10 +42,14 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, Status: opts.Status, IsEmpty: !opts.AutoInit, + RepoType: RepoType, } err = models.WithTx(func(ctx models.DBContext) error { - if err = models.CreateRepository(ctx, doer, u, repo); err != nil { + if err = models.CreateRepository(ctx, doer, u, repo, opts); err != nil { + return err + } + if err = models.SaveTopics(repo.ID, opts.Topics...); err != nil { return err } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0b3bb1792..1756b9533 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -50,6 +50,7 @@ repository = Repository organization = Organization mirror = Mirror new_repo = New Repository +new_course=Publish Course new_migrate = New Migration new_dataset = New Dataset edit_dataset = Edit Dataset @@ -390,6 +391,7 @@ lang_select_error = Select a language from the list. username_been_taken = The username is already taken. repo_name_been_taken = The repository name or path is already used. +course_name_been_taken=The course path is already used. visit_rate_limit = Remote visit addressed rate limitation. 2fa_auth_required = Remote visit required two factors authentication. org_name_been_taken = The organization name is already taken. @@ -802,6 +804,8 @@ readme = README readme_helper = Select a README file template. auto_init = Initialize Repository (Adds .gitignore, License and README) create_repo = Create Repository +create_course = Publish Course +failed_to_create_course=Fail to publish course, please try again later. default_branch = Default Branch mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references @@ -968,8 +972,12 @@ archive.issue.nocomment = This repo is archived. You cannot comment on issues. archive.pull.nocomment = This repo is archived. You cannot comment on pull requests. form.reach_limit_of_creation = You have already reached your limit of %d repositories. +form.reach_limit_of_course_creation=You have already reached your limit of %d courses or repositories. form.name_reserved = The repository name '%s' is reserved. +form.course_name_reserved=The course name '%s' is reserved. form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name. +form.course_name_pattern_not_allowed=The pattern '%s' is not allowed in a course name. +add_course_org_fail=Fail to add organization, please try again later. need_auth = Clone Authorization migrate_type = Migration Type diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index d5eac032b..d034b1722 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -50,6 +50,7 @@ repository=项目 organization=组织 mirror=镜像 new_repo=创建项目 +new_course=发布课程 new_dataset=创建数据集 new_migrate=迁移外部项目 edit_dataset = Edit Dataset @@ -395,6 +396,7 @@ lang_select_error=从列表中选出语言 username_been_taken=用户名已被使用。 repo_name_been_taken=项目名称或项目路径已被使用。 +course_name_been_taken=课程名称或路径已被使用。 visit_rate_limit=远程访问达到速度限制。 2fa_auth_required=远程访问需要双重验证。 org_name_been_taken=组织名称已被使用。 @@ -808,6 +810,8 @@ readme=自述 readme_helper=选择自述文件模板。 auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) create_repo=创建项目 +create_course=发布课程 +failed_to_create_course=发布课程失败,请稍后再试。 default_branch=默认分支 mirror_prune=修剪 mirror_prune_desc=删除过时的远程跟踪引用 @@ -980,8 +984,12 @@ archive.issue.nocomment=此项目已存档,您不能在此任务添加评论 archive.pull.nocomment=此项目已存档,您不能在此合并请求添加评论。 form.reach_limit_of_creation=你已经达到了您的 %d 项目的限制。 +form.reach_limit_of_course_creation=你已经达到了您的 %d 课程的限制。 form.name_reserved=项目名称 '%s' 是被保留的。 +form.course_name_reserved=课程名称 '%s' 是被保留的。 form.name_pattern_not_allowed=项目名称中不允许使用模式 "%s"。 +form.course_name_pattern_not_allowed=课程名称中不允许使用模式 "%s"。 +add_course_org_fail=加入组织失败,请稍后重试。 need_auth=需要授权验证 migrate_type=迁移类型 diff --git a/routers/home.go b/routers/home.go index 24de1a10c..397e1990d 100755 --- a/routers/home.go +++ b/routers/home.go @@ -7,11 +7,11 @@ package routers import ( "bytes" - "fmt" - "io/ioutil" "net/http" "strings" + "code.gitea.io/gitea/services/repository" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -133,6 +133,7 @@ type RepoSearchOptions struct { Restricted bool PageSize int TplName base.TplName + Course util.OptionalBool } var ( @@ -211,6 +212,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { AllLimited: true, TopicName: topic, IncludeDescription: setting.UI.SearchRepoDescription, + Course: opts.Course, }) if err != nil { ctx.ServerError("SearchRepository", err) @@ -559,7 +561,7 @@ func NotFound(ctx *context.Context) { func RecommendOrgFromPromote(ctx *context.Context) { url := setting.RecommentRepoAddr + "organizations" - result, err := recommendFromPromote(url) + result, err := repository.RecommendFromPromote(url) if err != nil { ctx.ServerError("500", err) return @@ -586,62 +588,11 @@ func RecommendOrgFromPromote(ctx *context.Context) { ctx.JSON(200, resultOrg) } -func recommendFromPromote(url string) ([]string, error) { - resp, err := http.Get(url) - if err != nil || resp.StatusCode != 200 { - log.Info("Get organizations url error=" + err.Error()) - return nil, err - } - bytes, err := ioutil.ReadAll(resp.Body) - resp.Body.Close() - if err != nil { - log.Info("Get organizations url error=" + err.Error()) - return nil, err - } - - allLineStr := string(bytes) - lines := strings.Split(allLineStr, "\n") - result := make([]string, len(lines)) - for i, line := range lines { - log.Info("i=" + fmt.Sprint(i) + " line=" + line) - result[i] = strings.Trim(line, " ") - } - return result, nil -} - func RecommendRepoFromPromote(ctx *context.Context) { - url := setting.RecommentRepoAddr + "projects" - result, err := recommendFromPromote(url) + result, err := repository.GetRecommendRepoFromPromote("projects") if err != nil { ctx.ServerError("500", err) - return - } - resultRepo := make([]map[string]interface{}, 0) - //resultRepo := make([]*models.Repository, 0) - for _, repoName := range result { - tmpIndex := strings.Index(repoName, "/") - if tmpIndex == -1 { - log.Info("error repo name format.") - } else { - ownerName := strings.Trim(repoName[0:tmpIndex], " ") - repoName := strings.Trim(repoName[tmpIndex+1:], " ") - repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) - if err == nil { - repoMap := make(map[string]interface{}) - repoMap["ID"] = fmt.Sprint(repo.ID) - repoMap["Name"] = repo.Name - repoMap["OwnerName"] = repo.OwnerName - repoMap["NumStars"] = repo.NumStars - repoMap["NumForks"] = repo.NumForks - repoMap["Description"] = repo.Description - repoMap["NumWatchs"] = repo.NumWatches - repoMap["Topics"] = repo.Topics - repoMap["Avatar"] = repo.RelAvatarLink() - resultRepo = append(resultRepo, repoMap) - } else { - log.Info("query repo error," + err.Error()) - } - } + } else { + ctx.JSON(200, result) } - ctx.JSON(200, resultRepo) } diff --git a/routers/org/home.go b/routers/org/home.go index df600d96d..4c350d352 100755 --- a/routers/org/home.go +++ b/routers/org/home.go @@ -7,6 +7,10 @@ package org import ( "strings" + "code.gitea.io/gitea/services/repository" + + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -14,7 +18,8 @@ import ( ) const ( - tplOrgHome base.TplName = "org/home" + tplOrgHome base.TplName = "org/home" + tplOrgCourseHome base.TplName = "org/home_courses" ) // Home show organization home page @@ -59,10 +64,16 @@ func Home(ctx *context.Context) { case "fewestforks": orderBy = models.SearchOrderByForks default: - ctx.Data["SortType"] = "recentupdate" - orderBy = models.SearchOrderByRecentUpdated - } + if setting.Course.OrgName == org.Name { + ctx.Data["SortType"] = "newest" + orderBy = models.SearchOrderByNewest + } else { + ctx.Data["SortType"] = "recentupdate" + orderBy = models.SearchOrderByRecentUpdated + } + } + orderBy = orderBy + ",id" keyword := strings.Trim(ctx.Query("q"), " ") ctx.Data["Keyword"] = keyword @@ -76,9 +87,21 @@ func Home(ctx *context.Context) { count int64 err error ) + pageSize := setting.UI.User.RepoPagingNum + var CourseOptional util.OptionalBool = util.OptionalBoolNone + if setting.Course.OrgName == org.Name { + pageSize = 15 + CourseOptional = util.OptionalBoolTrue + recommendCourses, _ := repository.GetRecommendRepoFromPromote("courses") + ctx.Data["RecommendCourses"] = recommendCourses + recommendCourseKeyWords, _ := repository.GetRecommendCourseKeyWords() + ctx.Data["CoursesKeywords"] = recommendCourseKeyWords + + } + repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ ListOptions: models.ListOptions{ - PageSize: setting.UI.User.RepoPagingNum, + PageSize: pageSize, Page: page, }, Keyword: keyword, @@ -87,6 +110,7 @@ func Home(ctx *context.Context) { Private: ctx.IsSigned, Actor: ctx.User, IncludeDescription: setting.UI.SearchRepoDescription, + Course: CourseOptional, }) if err != nil { ctx.ServerError("SearchRepository", err) @@ -138,5 +162,10 @@ func Home(ctx *context.Context) { } ctx.Data["tags"] = tags - ctx.HTML(200, tplOrgHome) + if setting.Course.OrgName == org.Name { + ctx.HTML(200, tplOrgCourseHome) + } else { + ctx.HTML(200, tplOrgHome) + } + } diff --git a/routers/org/members.go b/routers/org/members.go index 9f13d1be3..39df692d2 100755 --- a/routers/org/members.go +++ b/routers/org/members.go @@ -17,7 +17,8 @@ import ( const ( // tplMembers template for organization members page - tplMembers base.TplName = "org/member/members" + tplMembers base.TplName = "org/member/members" + tplCourseMembers base.TplName = "org/member/course_members" ) // Members render organization users page @@ -64,8 +65,11 @@ func Members(ctx *context.Context) { ctx.Data["MembersIsPublicMember"] = membersIsPublic ctx.Data["MembersIsUserOrgOwner"] = members.IsUserOrgOwner(org.ID) ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus() - - ctx.HTML(200, tplMembers) + if setting.Course.OrgName == org.Name { + ctx.HTML(200, tplCourseMembers) + } else { + ctx.HTML(200, tplMembers) + } } // MembersAction response for operation to a member of organization diff --git a/routers/org/teams.go b/routers/org/teams.go index 03fbf068d..8aa3e3947 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -10,6 +10,8 @@ import ( "path" "strings" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" @@ -22,7 +24,8 @@ import ( const ( // tplTeams template path for teams list page - tplTeams base.TplName = "org/team/teams" + tplTeams base.TplName = "org/team/teams" + tplCourseTeams base.TplName = "org/team/courseTeams" // tplTeamNew template path for create new team page tplTeamNew base.TplName = "org/team/new" // tplTeamMembers template path for showing team members page @@ -44,8 +47,12 @@ func Teams(ctx *context.Context) { } } ctx.Data["Teams"] = org.Teams + if setting.Course.OrgName == org.Name { + ctx.HTML(200, tplCourseTeams) + } else { + ctx.HTML(200, tplTeams) + } - ctx.HTML(200, tplTeams) } // TeamsAction response for join, leave, remove, add operations to team diff --git a/routers/repo/course.go b/routers/repo/course.go new file mode 100644 index 000000000..b8f39817b --- /dev/null +++ b/routers/repo/course.go @@ -0,0 +1,189 @@ +package repo + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + repo_service "code.gitea.io/gitea/services/repository" +) + +const ( + tplCreateCourse base.TplName = "repo/createCourse" +) + +func CreateCourse(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("new_course") + + ctx.Data["Owner"] = setting.Course.OrgName + + ctx.HTML(200, tplCreateCourse) + +} + +func CreateCoursePost(ctx *context.Context, form auth.CreateCourseForm) { + ctx.Data["Title"] = ctx.Tr("new_course") + + if ctx.Written() { + return + } + + var topics = make([]string, 0) + var topicsStr = strings.TrimSpace(form.Topics) + if len(topicsStr) > 0 { + topics = strings.Split(topicsStr, ",") + } + + validTopics, invalidTopics := models.SanitizeAndValidateTopics(topics) + + if len(validTopics) > 25 { + ctx.RenderWithErr(ctx.Tr("repo.topic.count_prompt"), tplCreateCourse, form) + return + } + + if len(invalidTopics) > 0 { + ctx.RenderWithErr(ctx.Tr("repo.topic.format_prompt"), tplCreateCourse, form) + return + } + + var repo *models.Repository + var err error + + if setting.Course.OrgName == "" || setting.Course.TeamName == "" { + log.Error("then organization name or team name of course is empty.") + ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) + return + } + + org, team, err := getOrgAndTeam() + + if err != nil { + log.Error("Failed to get team from db.", err) + ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) + return + } + isInTeam, err := models.IsUserInTeams(ctx.User.ID, []int64{team.ID}) + if err != nil { + log.Error("Failed to get user in team from db.") + ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) + return + } + + if !isInTeam { + err = models.AddTeamMember(team, ctx.User.ID) + if err != nil { + log.Error("Failed to add user to team.") + ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) + return + } + } + + if ctx.HasError() { + ctx.HTML(200, tplCreateCourse) + return + } + + repo, err = repo_service.CreateRepository(ctx.User, org, models.CreateRepoOptions{ + Name: form.RepoName, + Alias: form.Alias, + Description: form.Description, + Gitignores: "", + IssueLabels: "", + License: "", + Readme: "Default", + IsPrivate: false, + DefaultBranch: "master", + AutoInit: true, + IsCourse: true, + Topics: validTopics, + }) + if err == nil { + log.Trace("Repository created [%d]: %s/%s", repo.ID, org.Name, repo.Name) + ctx.Redirect(setting.AppSubURL + "/" + org.Name + "/" + repo.Name) + return + } + + handleCreateCourseError(ctx, org, err, "CreateCoursePost", tplCreateCourse, &form) +} + +func AddCourseOrg(ctx *context.Context) { + + _, team, err := getOrgAndTeam() + + if err != nil { + log.Error("Failed to get team from db.", err) + ctx.JSON(http.StatusOK, map[string]interface{}{ + "code": 1, + "message": ctx.Tr("repo.addCourseOrgFail"), + }) + return + } + isInTeam, err := models.IsUserInTeams(ctx.User.ID, []int64{team.ID}) + if err != nil { + log.Error("Failed to get user in team from db.", err) + ctx.JSON(http.StatusOK, map[string]interface{}{ + "code": 1, + "message": ctx.Tr("repo.add_course_org_fail"), + }) + return + } + + if !isInTeam { + err = models.AddTeamMember(team, ctx.User.ID) + if err != nil { + log.Error("Failed to add user to team.", err) + ctx.JSON(http.StatusOK, map[string]interface{}{ + "code": 1, + "message": ctx.Tr("repo.add_course_org_fail"), + }) + return + } + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "code": 0, + "message": "", + }) + +} + +func getOrgAndTeam() (*models.User, *models.Team, error) { + org, err := models.GetUserByName(setting.Course.OrgName) + + if err != nil { + log.Error("Failed to get organization from db.", err) + return nil, nil, err + } + + team, err := models.GetTeam(org.ID, setting.Course.TeamName) + + if err != nil { + log.Error("Failed to get team from db.", err) + + return nil, nil, err + } + return org, team, nil +} + +func handleCreateCourseError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) { + switch { + case models.IsErrReachLimitOfRepo(err): + ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_course_creation", owner.MaxCreationLimit()), tpl, form) + case models.IsErrRepoAlreadyExist(err): + ctx.Data["Err_RepoName"] = true + ctx.RenderWithErr(ctx.Tr("form.course_name_been_taken"), tpl, form) + case models.IsErrNameReserved(err): + ctx.Data["Err_RepoName"] = true + ctx.RenderWithErr(ctx.Tr("repo.form.course_name_reserved", err.(models.ErrNameReserved).Name), tpl, form) + case models.IsErrNamePatternNotAllowed(err): + ctx.Data["Err_RepoName"] = true + ctx.RenderWithErr(ctx.Tr("repo.form.course_name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) + default: + ctx.ServerError(name, err) + } +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 0d6aa5531..5f2237dd8 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -708,6 +708,13 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqSignIn) // ***** END: Organization ***** + m.Group("/course", func() { + m.Get("/create", repo.CreateCourse) + m.Post("/create", bindIgnErr(auth.CreateCourseForm{}), repo.CreateCoursePost) + m.Get("/addOrg", repo.AddCourseOrg) + + }, reqSignIn) + // ***** START: Repository ***** m.Group("/repo", func() { m.Get("/create", repo.Create) diff --git a/services/repository/repository.go b/services/repository/repository.go index dffa1ba11..86ee9370e 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -5,12 +5,17 @@ package repository import ( + "fmt" + "io/ioutil" + "net/http" + "strings" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" pull_service "code.gitea.io/gitea/services/pull" - "fmt" ) // CreateRepository creates a repository for the user/organization. @@ -86,3 +91,79 @@ func PushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repo return repo, nil } + +func GetRecommendCourseKeyWords() ([]string, error) { + + url := setting.RecommentRepoAddr + "course_keywords" + result, err := RecommendFromPromote(url) + + if err != nil { + return []string{}, err + } + return result, err + +} + +func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, error) { + resultRepo := make([]map[string]interface{}, 0) + url := setting.RecommentRepoAddr + filename + result, err := RecommendFromPromote(url) + + if err != nil { + + return resultRepo, err + } + + //resultRepo := make([]*models.Repository, 0) + for _, repoName := range result { + tmpIndex := strings.Index(repoName, "/") + if tmpIndex == -1 { + log.Info("error repo name format.") + } else { + ownerName := strings.Trim(repoName[0:tmpIndex], " ") + repoName := strings.Trim(repoName[tmpIndex+1:], " ") + repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) + if err == nil { + repoMap := make(map[string]interface{}) + repoMap["ID"] = fmt.Sprint(repo.ID) + repoMap["Name"] = repo.Name + repoMap["Alias"] = repo.Alias + repoMap["Creator"] = repo.Creator + repoMap["OwnerName"] = repo.OwnerName + repoMap["NumStars"] = repo.NumStars + repoMap["NumForks"] = repo.NumForks + repoMap["Description"] = repo.Description + repoMap["NumWatchs"] = repo.NumWatches + repoMap["Topics"] = repo.Topics + repoMap["Avatar"] = repo.RelAvatarLink() + resultRepo = append(resultRepo, repoMap) + } else { + log.Info("query repo error," + err.Error()) + } + } + } + return resultRepo, nil +} + +func RecommendFromPromote(url string) ([]string, error) { + resp, err := http.Get(url) + if err != nil || resp.StatusCode != 200 { + log.Info("Get organizations url error=" + err.Error()) + return nil, err + } + bytes, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + log.Info("Get organizations url error=" + err.Error()) + return nil, err + } + + allLineStr := string(bytes) + lines := strings.Split(allLineStr, "\n") + result := make([]string, len(lines)) + for i, line := range lines { + log.Info("i=" + fmt.Sprint(i) + " line=" + line) + result[i] = strings.Trim(line, " ") + } + return result, nil +} From 110fb5cbc0b5b65608613ba7c2f40882d74a6cb0 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Fri, 14 Jan 2022 17:16:44 +0800 Subject: [PATCH 23/23] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/setting/setting.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index e7ab0b7d2..3d2bd91c5 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -568,6 +568,11 @@ var ( }{} Warn_Notify_Mails []string + + Course = struct { + OrgName string + TeamName string + }{} ) // DateLang transforms standard language locale name to corresponding value in datetime plugin. @@ -1331,6 +1336,10 @@ func NewContext() { sec = Cfg.Section("warn_mail") Warn_Notify_Mails = strings.Split(sec.Key("mails").MustString(""), ",") + + sec = Cfg.Section("course") + Course.OrgName = sec.Key("org_name").MustString("") + Course.TeamName = sec.Key("team_name").MustString("") } func SetRadarMapConfig() {