| @@ -5,7 +5,7 @@ Gogs - Go Git Service [ |  | ||||
| ##### Current version: 0.7.29 Beta | |||||
| ##### Current version: 0.7.30 Beta | |||||
| <table> | <table> | ||||
| <tr> | <tr> | ||||
| @@ -421,7 +421,7 @@ func runWeb(ctx *cli.Context) { | |||||
| m.Get("/action/:action", repo.Action) | m.Get("/action/:action", repo.Action) | ||||
| m.Group("/issues", func() { | m.Group("/issues", func() { | ||||
| m.Combo("/new").Get(middleware.RepoRef(), repo.NewIssue). | |||||
| m.Combo("/new", repo.MustEnableIssues).Get(middleware.RepoRef(), repo.NewIssue). | |||||
| Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) | Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) | ||||
| m.Combo("/:index/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) | m.Combo("/:index/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) | ||||
| @@ -459,7 +459,7 @@ func runWeb(ctx *cli.Context) { | |||||
| m.Post("/delete", repo.DeleteRelease) | m.Post("/delete", repo.DeleteRelease) | ||||
| }, reqRepoAdmin, middleware.RepoRef()) | }, reqRepoAdmin, middleware.RepoRef()) | ||||
| m.Combo("/compare/*").Get(repo.CompareAndPullRequest). | |||||
| m.Combo("/compare/*", repo.MustEnablePulls).Get(repo.CompareAndPullRequest). | |||||
| Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) | Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) | ||||
| }, reqSignIn, middleware.RepoAssignment()) | }, reqSignIn, middleware.RepoAssignment()) | ||||
| @@ -484,7 +484,7 @@ func runWeb(ctx *cli.Context) { | |||||
| m.Combo("/:page/_edit").Get(repo.EditWiki). | m.Combo("/:page/_edit").Get(repo.EditWiki). | ||||
| Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) | Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) | ||||
| }, reqSignIn, reqRepoPusher) | }, reqSignIn, reqRepoPusher) | ||||
| }, middleware.RepoRef()) | |||||
| }, repo.MustEnableWiki, middleware.RepoRef()) | |||||
| m.Get("/archive/*", repo.Download) | m.Get("/archive/*", repo.Download) | ||||
| @@ -492,7 +492,7 @@ func runWeb(ctx *cli.Context) { | |||||
| m.Get("/commits", repo.ViewPullCommits) | m.Get("/commits", repo.ViewPullCommits) | ||||
| m.Get("/files", repo.ViewPullFiles) | m.Get("/files", repo.ViewPullFiles) | ||||
| m.Post("/merge", reqRepoAdmin, repo.MergePullRequest) | m.Post("/merge", reqRepoAdmin, repo.MergePullRequest) | ||||
| }) | |||||
| }, repo.MustEnablePulls) | |||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Get("/src/*", repo.Home) | m.Get("/src/*", repo.Home) | ||||
| @@ -558,10 +558,17 @@ settings.collaboration = Collaboration | |||||
| settings.hooks = Webhooks | settings.hooks = Webhooks | ||||
| settings.githooks = Git Hooks | settings.githooks = Git Hooks | ||||
| settings.basic_settings = Basic Settings | settings.basic_settings = Basic Settings | ||||
| settings.danger_zone = Danger Zone | |||||
| settings.site = Official Site | settings.site = Official Site | ||||
| settings.update_settings = Update Settings | settings.update_settings = Update Settings | ||||
| settings.change_reponame_prompt = This change will affect how links relate to the repository. | settings.change_reponame_prompt = This change will affect how links relate to the repository. | ||||
| settings.advanced_settings = Advanced Settings | |||||
| settings.wiki_desc = Enable wiki to allow people write documents | |||||
| settings.issues_desc = Enable builtin lightweight issue tracker | |||||
| settings.use_external_issue_tracker = Use external issue tracker | |||||
| settings.tracker_url_format = External Issue Tracker URL Format | |||||
| settings.tracker_url_format_desc = You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. | |||||
| settings.pulls_desc = Enable pull requests to accept public contributions | |||||
| settings.danger_zone = Danger Zone | |||||
| settings.transfer = Transfer Ownership | settings.transfer = Transfer Ownership | ||||
| settings.transfer_desc = Transfer this repository to another user or to an organization in which you have admin rights. | settings.transfer_desc = Transfer this repository to another user or to an organization in which you have admin rights. | ||||
| settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name. | settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name. | ||||
| @@ -33,7 +33,6 @@ Directory `/var/gogs` keeps Git repositories and Gogs data: | |||||
| |-- conf | |-- conf | ||||
| |-- data | |-- data | ||||
| |-- log | |-- log | ||||
| |-- templates | |||||
| ### Volume with data container | ### Volume with data container | ||||
| @@ -17,7 +17,7 @@ import ( | |||||
| "github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
| ) | ) | ||||
| const APP_VER = "0.7.29.1204 Beta" | |||||
| const APP_VER = "0.7.30.1204 Beta" | |||||
| func init() { | func init() { | ||||
| runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
| @@ -161,6 +161,14 @@ type Repository struct { | |||||
| IsMirror bool | IsMirror bool | ||||
| *Mirror `xorm:"-"` | *Mirror `xorm:"-"` | ||||
| // Advanced settings | |||||
| EnableWiki bool `xorm:"NOT NULL DEFAULT true"` | |||||
| EnableIssues bool `xorm:"NOT NULL DEFAULT true"` | |||||
| EnableExternalTracker bool | |||||
| ExternalTrackerFormat string | |||||
| ExternalMetas map[string]string `xorm:"-"` | |||||
| EnablePulls bool `xorm:"NOT NULL DEFAULT true"` | |||||
| IsFork bool `xorm:"NOT NULL DEFAULT false"` | IsFork bool `xorm:"NOT NULL DEFAULT false"` | ||||
| ForkID int64 | ForkID int64 | ||||
| BaseRepo *Repository `xorm:"-"` | BaseRepo *Repository `xorm:"-"` | ||||
| @@ -214,6 +222,20 @@ func (repo *Repository) MustOwner() *User { | |||||
| return repo.mustOwner(x) | return repo.mustOwner(x) | ||||
| } | } | ||||
| // ComposeMetas composes a map of metas for rendering external issue tracker URL. | |||||
| func (repo *Repository) ComposeMetas() map[string]string { | |||||
| if !repo.EnableExternalTracker { | |||||
| return nil | |||||
| } else if repo.ExternalMetas == nil { | |||||
| repo.ExternalMetas = map[string]string{ | |||||
| "format": repo.ExternalTrackerFormat, | |||||
| "user": repo.MustOwner().Name, | |||||
| "repo": repo.Name, | |||||
| } | |||||
| } | |||||
| return repo.ExternalMetas | |||||
| } | |||||
| // GetAssignees returns all users that have write access of repository. | // GetAssignees returns all users that have write access of repository. | ||||
| func (repo *Repository) GetAssignees() (_ []*User, err error) { | func (repo *Repository) GetAssignees() (_ []*User, err error) { | ||||
| if err = repo.GetOwner(); err != nil { | if err = repo.GetOwner(); err != nil { | ||||
| @@ -87,6 +87,13 @@ type RepoSettingForm struct { | |||||
| Branch string | Branch string | ||||
| Interval int | Interval int | ||||
| Private bool | Private bool | ||||
| // Advanced settings | |||||
| EnableWiki bool | |||||
| EnableIssues bool | |||||
| EnableExternalTracker bool | |||||
| TrackerURLFormat string | |||||
| EnablePulls bool | |||||
| } | } | ||||
| func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||
| @@ -137,50 +137,6 @@ var ( | |||||
| sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) | sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) | ||||
| ) | ) | ||||
| func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte { | |||||
| ms := MentionPattern.FindAll(rawBytes, -1) | |||||
| for _, m := range ms { | |||||
| m = bytes.TrimSpace(m) | |||||
| rawBytes = bytes.Replace(rawBytes, m, | |||||
| []byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, setting.AppSubUrl, m[1:], m)), -1) | |||||
| } | |||||
| ms = commitPattern.FindAll(rawBytes, -1) | |||||
| for _, m := range ms { | |||||
| m = bytes.TrimSpace(m) | |||||
| i := strings.Index(string(m), "commit/") | |||||
| j := strings.Index(string(m), "#") | |||||
| if j == -1 { | |||||
| j = len(m) | |||||
| } | |||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf( | |||||
| ` <code><a href="%s">%s</a></code>`, m, ShortSha(string(m[i+7:j])))), -1) | |||||
| } | |||||
| ms = issueFullPattern.FindAll(rawBytes, -1) | |||||
| for _, m := range ms { | |||||
| m = bytes.TrimSpace(m) | |||||
| i := strings.Index(string(m), "issues/") | |||||
| j := strings.Index(string(m), "#") | |||||
| if j == -1 { | |||||
| j = len(m) | |||||
| } | |||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf( | |||||
| ` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))), -1) | |||||
| } | |||||
| rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix) | |||||
| rawBytes = RenderSha1CurrentPattern(rawBytes, urlPrefix) | |||||
| return rawBytes | |||||
| } | |||||
| func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { | |||||
| ms := sha1CurrentPattern.FindAll(rawBytes, -1) | |||||
| for _, m := range ms { | |||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf( | |||||
| `<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, ShortSha(string(m)))), -1) | |||||
| } | |||||
| return rawBytes | |||||
| } | |||||
| func cutoutVerbosePrefix(prefix string) string { | func cutoutVerbosePrefix(prefix string) string { | ||||
| count := 0 | count := 0 | ||||
| for i := 0; i < len(prefix); i++ { | for i := 0; i < len(prefix); i++ { | ||||
| @@ -194,7 +150,7 @@ func cutoutVerbosePrefix(prefix string) string { | |||||
| return prefix | return prefix | ||||
| } | } | ||||
| func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte { | |||||
| func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { | |||||
| urlPrefix = cutoutVerbosePrefix(urlPrefix) | urlPrefix = cutoutVerbosePrefix(urlPrefix) | ||||
| ms := issueIndexPattern.FindAll(rawBytes, -1) | ms := issueIndexPattern.FindAll(rawBytes, -1) | ||||
| for _, m := range ms { | for _, m := range ms { | ||||
| @@ -204,24 +160,45 @@ func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte { | |||||
| space = " " | space = " " | ||||
| m2 = m2[1:] | m2 = m2[1:] | ||||
| } | } | ||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s/issues/%s">%s</a>`, | |||||
| space, urlPrefix, m2[1:], m2)), 1) | |||||
| if metas == nil { | |||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s/issues/%s">%s</a>`, | |||||
| space, urlPrefix, m2[1:], m2)), 1) | |||||
| } else { | |||||
| // Support for external issue tracker | |||||
| metas["index"] = string(m2[1:]) | |||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s">%s</a>`, | |||||
| space, com.Expand(metas["format"], metas), m2)), 1) | |||||
| } | |||||
| } | |||||
| return rawBytes | |||||
| } | |||||
| func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { | |||||
| ms := MentionPattern.FindAll(rawBytes, -1) | |||||
| for _, m := range ms { | |||||
| m = bytes.TrimSpace(m) | |||||
| rawBytes = bytes.Replace(rawBytes, m, | |||||
| []byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, setting.AppSubUrl, m[1:], m)), -1) | |||||
| } | |||||
| rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix, metas) | |||||
| rawBytes = RenderSha1CurrentPattern(rawBytes, urlPrefix) | |||||
| return rawBytes | |||||
| } | |||||
| func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { | |||||
| ms := sha1CurrentPattern.FindAll(rawBytes, -1) | |||||
| for _, m := range ms { | |||||
| rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf( | |||||
| `<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, ShortSha(string(m)))), -1) | |||||
| } | } | ||||
| return rawBytes | return rawBytes | ||||
| } | } | ||||
| func RenderRawMarkdown(body []byte, urlPrefix string) []byte { | func RenderRawMarkdown(body []byte, urlPrefix string) []byte { | ||||
| htmlFlags := 0 | htmlFlags := 0 | ||||
| // htmlFlags |= blackfriday.HTML_USE_XHTML | |||||
| // htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS | |||||
| // htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS | |||||
| // htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES | |||||
| // htmlFlags |= blackfriday.HTML_SKIP_HTML | |||||
| htmlFlags |= blackfriday.HTML_SKIP_STYLE | htmlFlags |= blackfriday.HTML_SKIP_STYLE | ||||
| // htmlFlags |= blackfriday.HTML_SKIP_SCRIPT | |||||
| // htmlFlags |= blackfriday.HTML_GITHUB_BLOCKCODE | |||||
| htmlFlags |= blackfriday.HTML_OMIT_CONTENTS | htmlFlags |= blackfriday.HTML_OMIT_CONTENTS | ||||
| // htmlFlags |= blackfriday.HTML_COMPLETE_PAGE | |||||
| renderer := &CustomRender{ | renderer := &CustomRender{ | ||||
| Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), | Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), | ||||
| urlPrefix: urlPrefix, | urlPrefix: urlPrefix, | ||||
| @@ -252,9 +229,36 @@ var ( | |||||
| var noEndTags = []string{"img", "input", "br", "hr"} | var noEndTags = []string{"img", "input", "br", "hr"} | ||||
| // PreProcessMarkdown renders full links of commits, issues and pulls to shorter version. | |||||
| func PreProcessMarkdown(rawHTML []byte, urlPrefix string) []byte { | |||||
| ms := commitPattern.FindAll(rawHTML, -1) | |||||
| for _, m := range ms { | |||||
| m = bytes.TrimSpace(m) | |||||
| i := strings.Index(string(m), "commit/") | |||||
| j := strings.Index(string(m), "#") | |||||
| if j == -1 { | |||||
| j = len(m) | |||||
| } | |||||
| rawHTML = bytes.Replace(rawHTML, m, []byte(fmt.Sprintf( | |||||
| ` <code><a href="%s">%s</a></code>`, m, ShortSha(string(m[i+7:j])))), -1) | |||||
| } | |||||
| ms = issueFullPattern.FindAll(rawHTML, -1) | |||||
| for _, m := range ms { | |||||
| m = bytes.TrimSpace(m) | |||||
| i := strings.Index(string(m), "issues/") | |||||
| j := strings.Index(string(m), "#") | |||||
| if j == -1 { | |||||
| j = len(m) | |||||
| } | |||||
| rawHTML = bytes.Replace(rawHTML, m, []byte(fmt.Sprintf( | |||||
| ` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))), -1) | |||||
| } | |||||
| return rawHTML | |||||
| } | |||||
| // PostProcessMarkdown treats different types of HTML differently, | // PostProcessMarkdown treats different types of HTML differently, | ||||
| // and only renders special links for plain text blocks. | // and only renders special links for plain text blocks. | ||||
| func PostProcessMarkdown(rawHtml []byte, urlPrefix string) []byte { | |||||
| func PostProcessMarkdown(rawHtml []byte, urlPrefix string, metas map[string]string) []byte { | |||||
| startTags := make([]string, 0, 5) | startTags := make([]string, 0, 5) | ||||
| var buf bytes.Buffer | var buf bytes.Buffer | ||||
| tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml)) | tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml)) | ||||
| @@ -264,7 +268,7 @@ OUTER_LOOP: | |||||
| token := tokenizer.Token() | token := tokenizer.Token() | ||||
| switch token.Type { | switch token.Type { | ||||
| case html.TextToken: | case html.TextToken: | ||||
| buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix)) | |||||
| buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix, metas)) | |||||
| case html.StartTagToken: | case html.StartTagToken: | ||||
| buf.WriteString(token.String()) | buf.WriteString(token.String()) | ||||
| @@ -322,13 +326,14 @@ OUTER_LOOP: | |||||
| return rawHtml | return rawHtml | ||||
| } | } | ||||
| func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte { | |||||
| result := RenderRawMarkdown(rawBytes, urlPrefix) | |||||
| result = PostProcessMarkdown(result, urlPrefix) | |||||
| func RenderMarkdown(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { | |||||
| result := PreProcessMarkdown(rawBytes, urlPrefix) | |||||
| result = RenderRawMarkdown(result, urlPrefix) | |||||
| result = PostProcessMarkdown(result, urlPrefix, metas) | |||||
| result = Sanitizer.SanitizeBytes(result) | result = Sanitizer.SanitizeBytes(result) | ||||
| return result | return result | ||||
| } | } | ||||
| func RenderMarkdownString(raw, urlPrefix string) string { | |||||
| return string(RenderMarkdown([]byte(raw), urlPrefix)) | |||||
| func RenderMarkdownString(raw, urlPrefix string, metas map[string]string) string { | |||||
| return string(RenderMarkdown([]byte(raw), urlPrefix, metas)) | |||||
| } | } | ||||
| @@ -125,7 +125,7 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue * | |||||
| subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index) | subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index) | ||||
| content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.", | content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.", | ||||
| base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name), | |||||
| base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas()), | |||||
| setting.AppUrl, owner.Name, repo.Name, issue.Index) | setting.AppUrl, owner.Name, repo.Name, issue.Index) | ||||
| msg := NewMessage(tos, subject, content) | msg := NewMessage(tos, subject, content) | ||||
| msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject) | msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject) | ||||
| @@ -148,7 +148,7 @@ func SendIssueMentionMail(r macaron.Render, u, owner *models.User, | |||||
| data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index) | data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index) | ||||
| data["Subject"] = subject | data["Subject"] = subject | ||||
| data["ActUserName"] = u.DisplayName() | data["ActUserName"] = u.DisplayName() | ||||
| data["Content"] = string(base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name)) | |||||
| data["Content"] = string(base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas())) | |||||
| body, err := r.HTMLString(string(NOTIFY_MENTION), data) | body, err := r.HTMLString(string(NOTIFY_MENTION), data) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -165,6 +165,7 @@ func RepoAssignment(args ...bool) macaron.Handler { | |||||
| ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner() | ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner() | ||||
| ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin() | ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin() | ||||
| ctx.Data["IsRepositoryPusher"] = ctx.Repo.IsPusher() | ctx.Data["IsRepositoryPusher"] = ctx.Repo.IsPusher() | ||||
| ctx.Data["CanPullRequest"] = ctx.Repo.IsAdmin() && repo.BaseRepo != nil && repo.BaseRepo.EnablePulls | |||||
| ctx.Data["DisableSSH"] = setting.DisableSSH | ctx.Data["DisableSSH"] = setting.DisableSSH | ||||
| ctx.Data["CloneLink"] = repo.CloneLink() | ctx.Data["CloneLink"] = repo.CloneLink() | ||||
| @@ -183,9 +183,9 @@ func ReplaceLeft(s, old, new string) string { | |||||
| } | } | ||||
| // RenderCommitMessage renders commit message with XSS-safe and special links. | // RenderCommitMessage renders commit message with XSS-safe and special links. | ||||
| func RenderCommitMessage(msg, urlPrefix string) template.HTML { | |||||
| func RenderCommitMessage(msg, urlPrefix string, metas map[string]string) template.HTML { | |||||
| cleanMsg := template.HTMLEscapeString(msg) | cleanMsg := template.HTMLEscapeString(msg) | ||||
| fullMessage := string(base.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix)) | |||||
| fullMessage := string(base.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas)) | |||||
| msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n") | msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n") | ||||
| for i := range msgLines { | for i := range msgLines { | ||||
| msgLines[i] = ReplaceLeft(msgLines[i], " ", " ") | msgLines[i] = ReplaceLeft(msgLines[i], " ", " ") | ||||
| @@ -911,6 +911,9 @@ pre.raw { | |||||
| .ui .form .fake { | .ui .form .fake { | ||||
| display: none !important; | display: none !important; | ||||
| } | } | ||||
| .ui .form .sub.field { | |||||
| margin-left: 25px; | |||||
| } | |||||
| .ui .sha.label { | .ui .sha.label { | ||||
| font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace; | font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace; | ||||
| font-size: 13px; | font-size: 13px; | ||||
| @@ -217,6 +217,10 @@ pre { | |||||
| .fake { | .fake { | ||||
| display: none !important; | display: none !important; | ||||
| } | } | ||||
| .sub.field { | |||||
| margin-left: 25px; | |||||
| } | |||||
| } | } | ||||
| .sha.label { | .sha.label { | ||||
| @@ -25,7 +25,7 @@ func Markdown(ctx *middleware.Context, form api.MarkdownOption) { | |||||
| switch form.Mode { | switch form.Mode { | ||||
| case "gfm": | case "gfm": | ||||
| ctx.Write(base.RenderMarkdown([]byte(form.Text), form.Context)) | |||||
| ctx.Write(base.RenderMarkdown([]byte(form.Text), form.Context, nil)) | |||||
| default: | default: | ||||
| ctx.Write(base.RenderRawMarkdown([]byte(form.Text), "")) | ctx.Write(base.RenderRawMarkdown([]byte(form.Text), "")) | ||||
| } | } | ||||
| @@ -41,6 +41,18 @@ var ( | |||||
| ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded") | ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded") | ||||
| ) | ) | ||||
| func MustEnableIssues(ctx *middleware.Context) { | |||||
| if !ctx.Repo.Repository.EnableIssues { | |||||
| ctx.Handle(404, "MustEnableIssues", nil) | |||||
| } | |||||
| } | |||||
| func MustEnablePulls(ctx *middleware.Context) { | |||||
| if !ctx.Repo.Repository.EnablePulls { | |||||
| ctx.Handle(404, "MustEnablePulls", nil) | |||||
| } | |||||
| } | |||||
| func RetrieveLabels(ctx *middleware.Context) { | func RetrieveLabels(ctx *middleware.Context) { | ||||
| labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID) | labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -60,7 +72,12 @@ func Issues(ctx *middleware.Context) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.pulls") | ctx.Data["Title"] = ctx.Tr("repo.pulls") | ||||
| ctx.Data["PageIsPullList"] = true | ctx.Data["PageIsPullList"] = true | ||||
| ctx.Data["HasForkedRepo"] = ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) | ctx.Data["HasForkedRepo"] = ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) | ||||
| } else { | } else { | ||||
| MustEnableIssues(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Title"] = ctx.Tr("repo.issues") | ctx.Data["Title"] = ctx.Tr("repo.issues") | ||||
| ctx.Data["PageIsIssueList"] = true | ctx.Data["PageIsIssueList"] = true | ||||
| } | } | ||||
| @@ -496,6 +513,10 @@ func ViewIssue(ctx *middleware.Context) { | |||||
| ctx.Data["PageIsPullConversation"] = true | ctx.Data["PageIsPullConversation"] = true | ||||
| ctx.Data["HasForkedRepo"] = ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) | ctx.Data["HasForkedRepo"] = ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) | ||||
| } else { | } else { | ||||
| MustEnableIssues(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["PageIsIssueList"] = true | ctx.Data["PageIsIssueList"] = true | ||||
| } | } | ||||
| @@ -503,7 +524,8 @@ func ViewIssue(ctx *middleware.Context) { | |||||
| ctx.Handle(500, "GetPoster", err) | ctx.Handle(500, "GetPoster", err) | ||||
| return | return | ||||
| } | } | ||||
| issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)) | |||||
| issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink, | |||||
| ctx.Repo.Repository.ComposeMetas())) | |||||
| repo := ctx.Repo.Repository | repo := ctx.Repo.Repository | ||||
| @@ -570,7 +592,8 @@ func ViewIssue(ctx *middleware.Context) { | |||||
| // Render comments. | // Render comments. | ||||
| for _, comment = range issue.Comments { | for _, comment = range issue.Comments { | ||||
| if comment.Type == models.COMMENT_TYPE_COMMENT { | if comment.Type == models.COMMENT_TYPE_COMMENT { | ||||
| comment.RenderedContent = string(base.RenderMarkdown([]byte(comment.Content), ctx.Repo.RepoLink)) | |||||
| comment.RenderedContent = string(base.RenderMarkdown([]byte(comment.Content), ctx.Repo.RepoLink, | |||||
| ctx.Repo.Repository.ComposeMetas())) | |||||
| // Check tag. | // Check tag. | ||||
| tag, ok = marked[comment.PosterID] | tag, ok = marked[comment.PosterID] | ||||
| @@ -656,7 +679,7 @@ func UpdateIssueContent(ctx *middleware.Context) { | |||||
| } | } | ||||
| ctx.JSON(200, map[string]interface{}{ | ctx.JSON(200, map[string]interface{}{ | ||||
| "content": string(base.RenderMarkdown([]byte(issue.Content), ctx.Query("context"))), | |||||
| "content": string(base.RenderMarkdown([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -893,7 +916,7 @@ func UpdateCommentContent(ctx *middleware.Context) { | |||||
| } | } | ||||
| ctx.JSON(200, map[string]interface{}{ | ctx.JSON(200, map[string]interface{}{ | ||||
| "content": string(base.RenderMarkdown([]byte(comment.Content), ctx.Query("context"))), | |||||
| "content": string(base.RenderMarkdown([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -991,7 +1014,7 @@ func Milestones(ctx *middleware.Context) { | |||||
| return | return | ||||
| } | } | ||||
| for _, m := range miles { | for _, m := range miles { | ||||
| m.RenderedContent = string(base.RenderMarkdown([]byte(m.Content), ctx.Repo.RepoLink)) | |||||
| m.RenderedContent = string(base.RenderMarkdown([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) | |||||
| m.CalOpenIssues() | m.CalOpenIssues() | ||||
| } | } | ||||
| ctx.Data["Milestones"] = miles | ctx.Data["Milestones"] = miles | ||||
| @@ -69,7 +69,7 @@ func Releases(ctx *middleware.Context) { | |||||
| rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits | rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits | ||||
| } | } | ||||
| rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink) | |||||
| rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) | |||||
| tags[i] = rel | tags[i] = rel | ||||
| rels[j] = nil // Mark as used. | rels[j] = nil // Mark as used. | ||||
| break | break | ||||
| @@ -129,7 +129,7 @@ func Releases(ctx *middleware.Context) { | |||||
| rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits | rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits | ||||
| } | } | ||||
| rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink) | |||||
| rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) | |||||
| tags = append(tags, rel) | tags = append(tags, rel) | ||||
| } | } | ||||
| models.SortReleases(tags) | models.SortReleases(tags) | ||||
| @@ -104,7 +104,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||||
| ctx.Handle(500, "UpdateRepository", err) | ctx.Handle(500, "UpdateRepository", err) | ||||
| return | return | ||||
| } | } | ||||
| log.Trace("Repository updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | |||||
| log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | |||||
| if isNameChanged { | if isNameChanged { | ||||
| if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { | if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { | ||||
| @@ -123,7 +123,24 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||||
| } | } | ||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | ||||
| ctx.Redirect(fmt.Sprintf("%s/%s/%s/settings", setting.AppSubUrl, ctx.Repo.Owner.Name, repo.Name)) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/settings") | |||||
| case "advanced": | |||||
| repo.EnableWiki = form.EnableWiki | |||||
| repo.EnableIssues = form.EnableIssues | |||||
| repo.EnableExternalTracker = form.EnableExternalTracker | |||||
| repo.ExternalTrackerFormat = form.TrackerURLFormat | |||||
| repo.EnablePulls = form.EnablePulls | |||||
| if err := models.UpdateRepository(repo, false); err != nil { | |||||
| ctx.Handle(500, "UpdateRepository", err) | |||||
| return | |||||
| } | |||||
| log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/settings") | |||||
| case "transfer": | case "transfer": | ||||
| if repo.Name != form.RepoName { | if repo.Name != form.RepoName { | ||||
| ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil) | ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil) | ||||
| @@ -108,7 +108,7 @@ func Home(ctx *middleware.Context) { | |||||
| readmeExist := base.IsMarkdownFile(blob.Name()) || base.IsReadmeFile(blob.Name()) | readmeExist := base.IsMarkdownFile(blob.Name()) || base.IsReadmeFile(blob.Name()) | ||||
| ctx.Data["ReadmeExist"] = readmeExist | ctx.Data["ReadmeExist"] = readmeExist | ||||
| if readmeExist { | if readmeExist { | ||||
| ctx.Data["FileContent"] = string(base.RenderMarkdown(buf, path.Dir(treeLink))) | |||||
| ctx.Data["FileContent"] = string(base.RenderMarkdown(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) | |||||
| } else { | } else { | ||||
| if err, content := template.ToUtf8WithErr(buf); err != nil { | if err, content := template.ToUtf8WithErr(buf); err != nil { | ||||
| if err != nil { | if err != nil { | ||||
| @@ -201,7 +201,7 @@ func Home(ctx *middleware.Context) { | |||||
| buf = append(buf, d...) | buf = append(buf, d...) | ||||
| switch { | switch { | ||||
| case base.IsMarkdownFile(readmeFile.Name()): | case base.IsMarkdownFile(readmeFile.Name()): | ||||
| buf = base.RenderMarkdown(buf, treeLink) | |||||
| buf = base.RenderMarkdown(buf, treeLink, ctx.Repo.Repository.ComposeMetas()) | |||||
| default: | default: | ||||
| buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1) | buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1) | ||||
| } | } | ||||
| @@ -24,6 +24,12 @@ const ( | |||||
| WIKI_PAGES base.TplName = "repo/wiki/pages" | WIKI_PAGES base.TplName = "repo/wiki/pages" | ||||
| ) | ) | ||||
| func MustEnableWiki(ctx *middleware.Context) { | |||||
| if !ctx.Repo.Repository.EnableWiki { | |||||
| ctx.Handle(404, "MustEnableWiki", nil) | |||||
| } | |||||
| } | |||||
| type PageMeta struct { | type PageMeta struct { | ||||
| Name string | Name string | ||||
| URL string | URL string | ||||
| @@ -94,7 +100,7 @@ func renderWikiPage(ctx *middleware.Context, isViewPage bool) (*git.Repository, | |||||
| return nil, "" | return nil, "" | ||||
| } | } | ||||
| if isViewPage { | if isViewPage { | ||||
| ctx.Data["content"] = string(base.RenderMarkdown(data, ctx.Repo.RepoLink)) | |||||
| ctx.Data["content"] = string(base.RenderMarkdown(data, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) | |||||
| } else { | } else { | ||||
| ctx.Data["content"] = string(data) | ctx.Data["content"] = string(data) | ||||
| } | } | ||||
| @@ -1 +1 @@ | |||||
| 0.7.29.1204 Beta | |||||
| 0.7.30.1204 Beta | |||||
| @@ -37,7 +37,7 @@ | |||||
| </td> | </td> | ||||
| <td class="message collapsing"> | <td class="message collapsing"> | ||||
| <a rel="nofollow" class="ui sha label" href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">{{ShortSha .ID.String}}</a> | <a rel="nofollow" class="ui sha label" href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">{{ShortSha .ID.String}}</a> | ||||
| {{RenderCommitMessage .Summary $.RepoLink}} | |||||
| {{RenderCommitMessage .Summary $.RepoLink $.Repository.ComposeMetas}} | |||||
| </td> | </td> | ||||
| <td class="grey text right aligned">{{TimeSince .Author.When $.Lang}}</td> | <td class="grey text right aligned">{{TimeSince .Author.When $.Lang}}</td> | ||||
| </tr> | </tr> | ||||
| @@ -51,21 +51,27 @@ | |||||
| <a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}"> | <a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}"> | ||||
| <i class="icon octicon octicon-code"></i> {{.i18n.Tr "repo.code"}} | <i class="icon octicon octicon-code"></i> {{.i18n.Tr "repo.code"}} | ||||
| </a> | </a> | ||||
| {{if .Repository.EnableIssues}} | |||||
| <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | ||||
| <i class="icon octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span> | <i class="icon octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span> | ||||
| </a> | </a> | ||||
| {{end}} | |||||
| {{if .Repository.EnablePulls}} | |||||
| <a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | <a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | ||||
| <i class="icon octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span> | <i class="icon octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span> | ||||
| </a> | </a> | ||||
| {{end}} | |||||
| <a class="{{if .PageIsCommits}}active{{end}} item" href="{{.RepoLink}}/commits/{{EscapePound .BranchName}}"> | <a class="{{if .PageIsCommits}}active{{end}} item" href="{{.RepoLink}}/commits/{{EscapePound .BranchName}}"> | ||||
| <i class="icon octicon octicon-history"></i> {{.i18n.Tr "repo.commits"}} <span class="ui {{if not .CommitsCount}}gray{{else}}blue{{end}} small label">{{.CommitsCount}}</span> | <i class="icon octicon octicon-history"></i> {{.i18n.Tr "repo.commits"}} <span class="ui {{if not .CommitsCount}}gray{{else}}blue{{end}} small label">{{.CommitsCount}}</span> | ||||
| </a> | </a> | ||||
| <a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> | <a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> | ||||
| <i class="icon octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumTags}}gray{{else}}blue{{end}} small label">{{.Repository.NumTags}}</span> | <i class="icon octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumTags}}gray{{else}}blue{{end}} small label">{{.Repository.NumTags}}</span> | ||||
| </a> | </a> | ||||
| {{if .Repository.EnableWiki}} | |||||
| <a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki"> | <a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki"> | ||||
| <i class="icon octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}} | <i class="icon octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}} | ||||
| </a> | </a> | ||||
| {{end}} | |||||
| {{if .IsRepositoryAdmin}} | {{if .IsRepositoryAdmin}} | ||||
| <div class="right menu"> | <div class="right menu"> | ||||
| <a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings"> | <a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings"> | ||||
| @@ -7,7 +7,7 @@ | |||||
| <a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | <a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | ||||
| </p> | </p> | ||||
| <div class="ui secondary menu"> | <div class="ui secondary menu"> | ||||
| {{if and .IsRepositoryAdmin .Repository.BaseRepo}} | |||||
| {{if .CanPullRequest}} | |||||
| <div class="fitted item"> | <div class="fitted item"> | ||||
| {{ $baseRepo := .Repository.BaseRepo}} | {{ $baseRepo := .Repository.BaseRepo}} | ||||
| <a href="{{AppSubUrl}}/{{$baseRepo.Owner.Name}}/{{$baseRepo.Name}}/compare/{{$.BaseDefaultBranch}}...{{$.Owner.Name}}:{{$.BranchName}}"> | <a href="{{AppSubUrl}}/{{$baseRepo.Owner.Name}}/{{$baseRepo.Name}}/compare/{{$.BaseDefaultBranch}}...{{$.Owner.Name}}:{{$.BranchName}}"> | ||||
| @@ -26,7 +26,6 @@ | |||||
| <input id="website" name="website" type="url" value="{{.Repository.Website}}"> | <input id="website" name="website" type="url" value="{{.Repository.Website}}"> | ||||
| </div> | </div> | ||||
| <div class="ui divider"></div> | |||||
| {{if not .Repository.IsBare}} | {{if not .Repository.IsBare}} | ||||
| <div class="required inline field"> | <div class="required inline field"> | ||||
| <label>{{.i18n.Tr "repo.default_branch"}}</label> | <label>{{.i18n.Tr "repo.default_branch"}}</label> | ||||
| @@ -57,13 +56,68 @@ | |||||
| <input id="interval" name="interval" type="number" value="{{.MirrorInterval}}"> | <input id="interval" name="interval" type="number" value="{{.MirrorInterval}}"> | ||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| <div class="ui divider"></div> | |||||
| <div class="field"> | <div class="field"> | ||||
| <button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | <button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | ||||
| </div> | </div> | ||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| <h4 class="ui top attached header"> | |||||
| {{.i18n.Tr "repo.settings.advanced_settings"}} | |||||
| </h4> | |||||
| <div class="ui attached segment"> | |||||
| <form class="ui form" action="{{.Link}}" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <input type="hidden" name="action" value="advanced"> | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.wiki"}}</label> | |||||
| <div class="ui checkbox"> | |||||
| <input name="enable_wiki" type="checkbox" {{if .Repository.EnableWiki}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.wiki_desc"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui divider"></div> | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.issues"}}</label> | |||||
| <div class="ui checkbox"> | |||||
| <input name="enable_issues" type="checkbox" {{if .Repository.EnableIssues}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.issues_desc"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <div class="ui checkbox"> | |||||
| <input name="enable_external_tracker" type="checkbox" {{if .Repository.EnableExternalTracker}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.use_external_issue_tracker"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <label for="tracker_url_format">{{.i18n.Tr "repo.settings.tracker_url_format"}}</label> | |||||
| <input id="tracker_url_format" name="tracker_url_format" value="{{.Repository.ExternalTrackerFormat}}" placeholder="e.g. https://github.com/{user}/{repo}/issues/{index}"> | |||||
| <p class="help">{{.i18n.Tr "repo.settings.tracker_url_format_desc" | Str2html}}</p> | |||||
| </div> | |||||
| <div class="ui divider"></div> | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.pulls"}}</label> | |||||
| <div class="ui checkbox"> | |||||
| <input name="enable_pulls" type="checkbox" {{if .Repository.EnablePulls}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.pulls_desc"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui divider"></div> | |||||
| <div class="field"> | |||||
| <button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| <h4 class="ui top attached warning header"> | <h4 class="ui top attached warning header"> | ||||
| {{.i18n.Tr "repo.settings.danger_zone"}} | {{.i18n.Tr "repo.settings.danger_zone"}} | ||||
| </h4> | </h4> | ||||