* Can use a predefined set of labels * Change UI * Fix HTML file indentation * Avoid reading file from other directory (security issue) * Apply a better fix * Remove not used variable * Merge upstream/develop * Do modifications * Raname * remove binding + rename variabletags/v1.21.12.1
| @@ -477,6 +477,7 @@ func runWeb(ctx *cli.Context) error { | |||||
| m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) | m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) | ||||
| m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) | m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) | ||||
| m.Post("/delete", repo.DeleteLabel) | m.Post("/delete", repo.DeleteLabel) | ||||
| m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels) | |||||
| }, repo.MustEnableIssues, reqRepoWriter, context.RepoRef()) | }, repo.MustEnableIssues, reqRepoWriter, context.RepoRef()) | ||||
| m.Group("/milestones", func() { | m.Group("/milestones", func() { | ||||
| m.Combo("/new").Get(repo.NewMilestone). | m.Combo("/new").Get(repo.NewMilestone). | ||||
| @@ -0,0 +1,7 @@ | |||||
| #ee0701 bug | |||||
| #cccccc duplicate | |||||
| #84b6eb enhancement | |||||
| #128a0c help wanted | |||||
| #e6e6e6 invalid | |||||
| #cc317c question | |||||
| #ffffff wontfix | |||||
| @@ -489,6 +489,10 @@ issues.create = Create Issue | |||||
| issues.new_label = New Label | issues.new_label = New Label | ||||
| issues.new_label_placeholder = Label name... | issues.new_label_placeholder = Label name... | ||||
| issues.create_label = Create Label | issues.create_label = Create Label | ||||
| issues.label_templates.title=Load a set of labels | |||||
| issues.label_templates.info=There aren’t any labels. You can click on the "New Label" button above to create one or use a predefined set below. | |||||
| issues.label_templates.helper=Select a label set | |||||
| issues.label_templates.use=Use this label set | |||||
| issues.open_tab = %d Open | issues.open_tab = %d Open | ||||
| issues.close_tab = %d Closed | issues.close_tab = %d Closed | ||||
| issues.filter_label = Label | issues.filter_label = Label | ||||
| @@ -54,7 +54,7 @@ var ( | |||||
| ) | ) | ||||
| var ( | var ( | ||||
| Gitignores, Licenses, Readmes []string | |||||
| Gitignores, Licenses, Readmes, LabelTemplates []string | |||||
| // Maximum items per page in forks, watchers and stars of a repo | // Maximum items per page in forks, watchers and stars of a repo | ||||
| ItemsPerPage = 40 | ItemsPerPage = 40 | ||||
| @@ -62,9 +62,8 @@ var ( | |||||
| func LoadRepoConfig() { | func LoadRepoConfig() { | ||||
| // Load .gitignore and license files and readme templates. | // Load .gitignore and license files and readme templates. | ||||
| // TODO: should we allow custom files overwrite default ones? | |||||
| types := []string{"gitignore", "license", "readme"} | |||||
| typeFiles := make([][]string, 3) | |||||
| types := []string{"gitignore", "license", "readme", "label"} | |||||
| typeFiles := make([][]string, 4) | |||||
| for i, t := range types { | for i, t := range types { | ||||
| files, err := bindata.AssetDir("conf/" + t) | files, err := bindata.AssetDir("conf/" + t) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -89,9 +88,11 @@ func LoadRepoConfig() { | |||||
| Gitignores = typeFiles[0] | Gitignores = typeFiles[0] | ||||
| Licenses = typeFiles[1] | Licenses = typeFiles[1] | ||||
| Readmes = typeFiles[2] | Readmes = typeFiles[2] | ||||
| LabelTemplates = typeFiles[3] | |||||
| sort.Strings(Gitignores) | sort.Strings(Gitignores) | ||||
| sort.Strings(Licenses) | sort.Strings(Licenses) | ||||
| sort.Strings(Readmes) | sort.Strings(Readmes) | ||||
| sort.Strings(LabelTemplates) | |||||
| // Filter out invalid names and promote preferred licenses. | // Filter out invalid names and promote preferred licenses. | ||||
| sortedLicenses := make([]string, 0, len(Licenses)) | sortedLicenses := make([]string, 0, len(Licenses)) | ||||
| @@ -220,6 +220,16 @@ func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) bi | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | return validate(errs, ctx.Data, f, ctx.Locale) | ||||
| } | } | ||||
| // Label templates | |||||
| type InitializeLabelsForm struct { | |||||
| TemplateName string `binding:"Required"` | |||||
| } | |||||
| func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| // __________ .__ | // __________ .__ | ||||
| // \______ \ ____ | | ____ _____ ______ ____ | // \______ \ ____ | | ____ _____ ______ ____ | ||||
| // | _// __ \| | _/ __ \\__ \ / ___// __ \ | // | _// __ \| | _/ __ \\__ \ / ___// __ \ | ||||
| @@ -11,6 +11,8 @@ import ( | |||||
| "io/ioutil" | "io/ioutil" | ||||
| "net/http" | "net/http" | ||||
| "net/url" | "net/url" | ||||
| "path" | |||||
| "regexp" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| @@ -20,6 +22,7 @@ import ( | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/auth" | "github.com/gogits/gogs/modules/auth" | ||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/bindata" | |||||
| "github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
| "github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
| "github.com/gogits/gogs/modules/markdown" | "github.com/gogits/gogs/modules/markdown" | ||||
| @@ -938,9 +941,58 @@ func Labels(ctx *context.Context) { | |||||
| ctx.Data["PageIsIssueList"] = true | ctx.Data["PageIsIssueList"] = true | ||||
| ctx.Data["PageIsLabels"] = true | ctx.Data["PageIsLabels"] = true | ||||
| ctx.Data["RequireMinicolors"] = true | ctx.Data["RequireMinicolors"] = true | ||||
| ctx.Data["LabelTemplates"] = models.LabelTemplates | |||||
| ctx.HTML(200, LABELS) | ctx.HTML(200, LABELS) | ||||
| } | } | ||||
| func getLabelTemplateFile(name string) ([]byte, error) { | |||||
| relPath := path.Join("conf/label", strings.TrimLeft(name, "./")) | |||||
| // Use custom file when available. | |||||
| customPath := path.Join(setting.CustomPath, relPath) | |||||
| if com.IsFile(customPath) { | |||||
| return ioutil.ReadFile(customPath) | |||||
| } | |||||
| return bindata.Asset(relPath) | |||||
| } | |||||
| func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { | |||||
| if ctx.HasError() { | |||||
| ctx.Flash.Error(ctx.Data["ErrorMsg"].(string)) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | |||||
| return | |||||
| } | |||||
| data, err := getLabelTemplateFile(form.TemplateName) | |||||
| if err != nil { | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | |||||
| return | |||||
| } | |||||
| r, _ := regexp.Compile("#([a-fA-F0-9]{6})") | |||||
| for i, line := range strings.Split(string(data), "\n") { | |||||
| if len(line) > 0 { | |||||
| line_x := strings.SplitN(strings.Trim(line, " \t"), " ", 2) | |||||
| if len(line_x) == 2 && len(line_x[1]) > 0 { | |||||
| if r.MatchString(line_x[0]) { | |||||
| l := &models.Label{ | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| Name: line_x[1], | |||||
| Color: line_x[0], | |||||
| } | |||||
| if err := models.NewLabel(l); err != nil { | |||||
| ctx.Handle(500, "InitializeLabelsFromTemplate", err) | |||||
| return | |||||
| } | |||||
| } else { | |||||
| log.Warn("Line %d on the label template file '%s': Bad HTML color code", i+1, form.TemplateName) | |||||
| } | |||||
| } else { | |||||
| log.Warn("Line %d on the label template file '%s': Line is malformed", i+1, form.TemplateName) | |||||
| } | |||||
| } | |||||
| } | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | |||||
| } | |||||
| func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { | func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { | ||||
| ctx.Data["Title"] = ctx.Tr("repo.labels") | ctx.Data["Title"] = ctx.Tr("repo.labels") | ||||
| ctx.Data["PageIsLabels"] = true | ctx.Data["PageIsLabels"] = true | ||||
| @@ -33,8 +33,43 @@ | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| <div class="ui black label">{{.i18n.Tr "repo.issues.label_count" .NumLabels}}</div> | <div class="ui black label">{{.i18n.Tr "repo.issues.label_count" .NumLabels}}</div> | ||||
| {{if $.IsRepositoryWriter}} | |||||
| {{if eq .NumLabels 0}} | |||||
| <div class="ui centered grid"> | |||||
| <div class="twelve wide column eight wide computer column"> | |||||
| <div class="ui attached left aligned segment" style="margin-top:30px"> | |||||
| <h4 class="ui header"> | |||||
| {{.i18n.Tr "repo.issues.label_templates.title"}} | |||||
| <a target="_blank" | |||||
| href="https://github.com/gogits/go-gogs-client/wiki/Repositories#litte-notes-on-label-template"> | |||||
| <span class="octicon octicon-question"></span> | |||||
| </a> | |||||
| </h4> | |||||
| <p>{{.i18n.Tr "repo.issues.label_templates.info"}}</p> | |||||
| <br/> | |||||
| <form class="ui form center" action="{{.Link}}/initialize" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <div class="field"> | |||||
| <div class="ui selection dropdown"> | |||||
| <input type="hidden" name="template_name" id="templatename" value="Default"> | |||||
| <div class="default text">{{.i18n.Tr "repo.issues.label_templates.helper"}}</div> | |||||
| <div class="menu"> | |||||
| {{range .LabelTemplates}} | |||||
| <div class="item" data-value="{{.}}">{{.}}</div> | |||||
| {{end}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <button type="submit" class="ui blue button">{{.i18n.Tr "repo.issues.label_templates.use"}}</button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} | |||||
| {{end}} | |||||
| <div class="label list"> | <div class="label list"> | ||||
| {{range .Labels}} | {{range .Labels}} | ||||