mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Load a set of predefined labels (#3459)
* 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 variable
This commit is contained in:
		| @@ -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). | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								conf/label/Default
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								conf/label/Default
									
									
									
									
									
										Normal file
									
								
							| @@ -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", "label"} | ||||||
| 	types := []string{"gitignore", "license", "readme"} | 	typeFiles := make([][]string, 4) | ||||||
| 	typeFiles := make([][]string, 3) |  | ||||||
| 	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) | ||||||
|  | } | ||||||
|  |  | ||||||
| // __________       .__ | // __________       .__ | ||||||
| // \______   \ ____ |  |   ____ _____    ______ ____ | // \______   \ ____ |  |   ____ _____    ______ ____ | ||||||
| //  |       _// __ \|  | _/ __ \\__  \  /  ___// __ \ | //  |       _// __ \|  | _/ __ \\__  \  /  ___// __ \ | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -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}} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user