Add label descriptions (#3662)
* Add label descriptions * Add default descriptions to label template
This commit is contained in:
		
							parent
							
								
									ad33730dca
								
							
						
					
					
						commit
						c0d41b1b77
					
				
					 14 changed files with 109 additions and 34 deletions
				
			
		|  | @ -19,22 +19,24 @@ import ( | ||||||
| var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})") | var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})") | ||||||
| 
 | 
 | ||||||
| // GetLabelTemplateFile loads the label template file by given name,
 | // GetLabelTemplateFile loads the label template file by given name,
 | ||||||
| // then parses and returns a list of name-color pairs.
 | // then parses and returns a list of name-color pairs and optionally description.
 | ||||||
| func GetLabelTemplateFile(name string) ([][2]string, error) { | func GetLabelTemplateFile(name string) ([][3]string, error) { | ||||||
| 	data, err := getRepoInitFile("label", name) | 	data, err := getRepoInitFile("label", name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("getRepoInitFile: %v", err) | 		return nil, fmt.Errorf("getRepoInitFile: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	lines := strings.Split(string(data), "\n") | 	lines := strings.Split(string(data), "\n") | ||||||
| 	list := make([][2]string, 0, len(lines)) | 	list := make([][3]string, 0, len(lines)) | ||||||
| 	for i := 0; i < len(lines); i++ { | 	for i := 0; i < len(lines); i++ { | ||||||
| 		line := strings.TrimSpace(lines[i]) | 		line := strings.TrimSpace(lines[i]) | ||||||
| 		if len(line) == 0 { | 		if len(line) == 0 { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		fields := strings.SplitN(line, " ", 2) | 		parts := strings.SplitN(line, ";", 2) | ||||||
|  | 
 | ||||||
|  | 		fields := strings.SplitN(parts[0], " ", 2) | ||||||
| 		if len(fields) != 2 { | 		if len(fields) != 2 { | ||||||
| 			return nil, fmt.Errorf("line is malformed: %s", line) | 			return nil, fmt.Errorf("line is malformed: %s", line) | ||||||
| 		} | 		} | ||||||
|  | @ -43,8 +45,14 @@ func GetLabelTemplateFile(name string) ([][2]string, error) { | ||||||
| 			return nil, fmt.Errorf("bad HTML color code in line: %s", line) | 			return nil, fmt.Errorf("bad HTML color code in line: %s", line) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		var description string | ||||||
|  | 
 | ||||||
|  | 		if len(parts) > 1 { | ||||||
|  | 			description = strings.TrimSpace(parts[1]) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		fields[1] = strings.TrimSpace(fields[1]) | 		fields[1] = strings.TrimSpace(fields[1]) | ||||||
| 		list = append(list, [2]string{fields[1], fields[0]}) | 		list = append(list, [3]string{fields[1], fields[0], description}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return list, nil | 	return list, nil | ||||||
|  | @ -55,6 +63,7 @@ type Label struct { | ||||||
| 	ID              int64 `xorm:"pk autoincr"` | 	ID              int64 `xorm:"pk autoincr"` | ||||||
| 	RepoID          int64 `xorm:"INDEX"` | 	RepoID          int64 `xorm:"INDEX"` | ||||||
| 	Name            string | 	Name            string | ||||||
|  | 	Description     string | ||||||
| 	Color           string `xorm:"VARCHAR(7)"` | 	Color           string `xorm:"VARCHAR(7)"` | ||||||
| 	NumIssues       int | 	NumIssues       int | ||||||
| 	NumClosedIssues int | 	NumClosedIssues int | ||||||
|  |  | ||||||
|  | @ -168,6 +168,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("remove is_owner, num_teams columns from org_user", removeIsOwnerColumnFromOrgUser), | 	NewMigration("remove is_owner, num_teams columns from org_user", removeIsOwnerColumnFromOrgUser), | ||||||
| 	// v57 -> v58
 | 	// v57 -> v58
 | ||||||
| 	NewMigration("add closed_unix column for issues", addIssueClosedTime), | 	NewMigration("add closed_unix column for issues", addIssueClosedTime), | ||||||
|  | 	// v58 -> v59
 | ||||||
|  | 	NewMigration("add label descriptions", addLabelsDescriptions), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Migrate database to current version
 | // Migrate database to current version
 | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								models/migrations/v58.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								models/migrations/v58.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | // Copyright 2018 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package migrations | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-xorm/xorm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func addLabelsDescriptions(x *xorm.Engine) error { | ||||||
|  | 	type Label struct { | ||||||
|  | 		Description string | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := x.Sync2(new(Label)); err != nil { | ||||||
|  | 		return fmt.Errorf("Sync2: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -311,7 +311,8 @@ func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors | ||||||
| // CreateLabelForm form for creating label
 | // CreateLabelForm form for creating label
 | ||||||
| type CreateLabelForm struct { | type CreateLabelForm struct { | ||||||
| 	ID          int64 | 	ID          int64 | ||||||
| 	Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_name"` | 	Title       string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"` | ||||||
|  | 	Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"` | ||||||
| 	Color       string `binding:"Required;Size(7)" locale:"repo.issues.label_color"` | 	Color       string `binding:"Required;Size(7)" locale:"repo.issues.label_color"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| #ee0701 bug | #ee0701 bug ; Something is not working | ||||||
| #cccccc duplicate | #cccccc duplicate ; This issue or pull request already exists | ||||||
| #84b6eb enhancement | #84b6eb enhancement ; New feature | ||||||
| #128a0c help wanted | #128a0c help wanted ; Need some help | ||||||
| #e6e6e6 invalid | #e6e6e6 invalid ; Something is wrong | ||||||
| #cc317c question | #cc317c question ; More information is needed | ||||||
| #ffffff wontfix | #ffffff wontfix ; This won't be fixed | ||||||
|  |  | ||||||
|  | @ -627,6 +627,7 @@ issues.no_ref = No Branch/Tag Specified | ||||||
| issues.create = Create Issue | 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.new_label_desc_placeholder = Description… | ||||||
| issues.create_label = Create Label | issues.create_label = Create Label | ||||||
| issues.label_templates.title = Load a predefined set of labels | issues.label_templates.title = Load a predefined set of labels | ||||||
| issues.label_templates.info = There are not any labels yet. You can click on the "New Label" button above to create one or use a predefined set below. | issues.label_templates.info = There are not any labels yet. You can click on the "New Label" button above to create one or use a predefined set below. | ||||||
|  | @ -697,6 +698,7 @@ issues.edit = Edit | ||||||
| issues.cancel = Cancel | issues.cancel = Cancel | ||||||
| issues.save = Save | issues.save = Save | ||||||
| issues.label_title = Label name | issues.label_title = Label name | ||||||
|  | issues.label_description = Label description | ||||||
| issues.label_color = Label color | issues.label_color = Label color | ||||||
| issues.label_count = %d labels | issues.label_count = %d labels | ||||||
| issues.label_open_issues = %d open issues | issues.label_open_issues = %d open issues | ||||||
|  |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -491,6 +491,7 @@ function initRepository() { | ||||||
|         $('.edit-label-button').click(function () { |         $('.edit-label-button').click(function () { | ||||||
|             $('#label-modal-id').val($(this).data('id')); |             $('#label-modal-id').val($(this).data('id')); | ||||||
|             $('.edit-label .new-label-input').val($(this).data('title')); |             $('.edit-label .new-label-input').val($(this).data('title')); | ||||||
|  |             $('.edit-label .new-label-desc-input').val($(this).data('description')); | ||||||
|             $('.edit-label .color-picker').val($(this).data('color')); |             $('.edit-label .color-picker').val($(this).data('color')); | ||||||
|             $('.minicolors-swatch-color').css("background-color", $(this).data('color')); |             $('.minicolors-swatch-color').css("background-color", $(this).data('color')); | ||||||
|             $('.edit-label.modal').modal({ |             $('.edit-label.modal').modal({ | ||||||
|  |  | ||||||
|  | @ -134,6 +134,17 @@ | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     .select-label { | ||||||
|  |         .item { | ||||||
|  |             max-width: 250px; | ||||||
|  |             overflow: hidden; | ||||||
|  |             text-overflow: ellipsis; | ||||||
|  |         } | ||||||
|  |         .desc { | ||||||
|  |             padding-left: 16px; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     .ui.tabs { |     .ui.tabs { | ||||||
|         &.container { |         &.container { | ||||||
|             margin-top: 14px; |             margin-top: 14px; | ||||||
|  |  | ||||||
|  | @ -44,6 +44,7 @@ func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { | ||||||
| 		labels[i] = &models.Label{ | 		labels[i] = &models.Label{ | ||||||
| 			RepoID:      ctx.Repo.Repository.ID, | 			RepoID:      ctx.Repo.Repository.ID, | ||||||
| 			Name:        list[i][0], | 			Name:        list[i][0], | ||||||
|  | 			Description: list[i][2], | ||||||
| 			Color:       list[i][1], | 			Color:       list[i][1], | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -83,6 +84,7 @@ func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { | ||||||
| 	l := &models.Label{ | 	l := &models.Label{ | ||||||
| 		RepoID:      ctx.Repo.Repository.ID, | 		RepoID:      ctx.Repo.Repository.ID, | ||||||
| 		Name:        form.Title, | 		Name:        form.Title, | ||||||
|  | 		Description: form.Description, | ||||||
| 		Color:       form.Color, | 		Color:       form.Color, | ||||||
| 	} | 	} | ||||||
| 	if err := models.NewLabel(l); err != nil { | 	if err := models.NewLabel(l); err != nil { | ||||||
|  | @ -106,6 +108,7 @@ func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	l.Name = form.Title | 	l.Name = form.Title | ||||||
|  | 	l.Description = form.Description | ||||||
| 	l.Color = form.Color | 	l.Color = form.Color | ||||||
| 	if err := models.UpdateLabel(l); err != nil { | 	if err := models.UpdateLabel(l); err != nil { | ||||||
| 		ctx.ServerError("UpdateLabel", err) | 		ctx.ServerError("UpdateLabel", err) | ||||||
|  |  | ||||||
|  | @ -14,11 +14,16 @@ | ||||||
| 			<form class="ui form" action="{{$.RepoLink}}/labels/new" method="post"> | 			<form class="ui form" action="{{$.RepoLink}}/labels/new" method="post"> | ||||||
| 				{{.CsrfTokenHtml}} | 				{{.CsrfTokenHtml}} | ||||||
| 				<div class="ui grid"> | 				<div class="ui grid"> | ||||||
| 					<div class="five wide column"> | 					<div class="three wide column"> | ||||||
| 						<div class="ui small input"> | 						<div class="ui small input"> | ||||||
| 							<input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required> | 							<input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
|  | 					<div class="five wide column"> | ||||||
|  | 						<div class="ui small fluid input"> | ||||||
|  | 							<input class="new-label-desc-input" name="description" placeholder="{{.i18n.Tr "repo.issues.new_label_desc_placeholder"}}"> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
| 					<div class="color picker column"> | 					<div class="color picker column"> | ||||||
| 						<input class="color-picker" name="color" value="#70c24a" required> | 						<input class="color-picker" name="color" value="#70c24a" required> | ||||||
| 					</div> | 					</div> | ||||||
|  | @ -85,14 +90,27 @@ | ||||||
| 				</div> | 				</div> | ||||||
| 			{{end}} | 			{{end}} | ||||||
| 
 | 
 | ||||||
|  | 			<div class="ui divider"></div> | ||||||
|  | 
 | ||||||
| 			{{range .Labels}} | 			{{range .Labels}} | ||||||
| 				<li class="item"> | 				<li class="item"> | ||||||
|  | 					<div class="ui grid"> | ||||||
|  | 						<div class="three wide column"> | ||||||
| 							<div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div> | 							<div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div> | ||||||
|  | 						</div> | ||||||
|  | 						<div class="seven wide column"> | ||||||
|  | 							{{.Description}} | ||||||
|  | 						</div> | ||||||
|  | 						<div class="three wide column"> | ||||||
|  | 							<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> | ||||||
|  | 						</div> | ||||||
|  | 						<div class="three wide column"> | ||||||
| 						{{if $.IsRepositoryWriter}} | 						{{if $.IsRepositoryWriter}} | ||||||
| 							<a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a> | 							<a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a> | ||||||
| 						<a class="ui right edit-label-button" href="#" data-id={{.ID}} data-title={{.Name}} data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a> | 							<a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" data-description="{{.Description}}" data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a> | ||||||
| 						{{end}} | 						{{end}} | ||||||
| 					<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> | 						</div> | ||||||
|  | 					</div> | ||||||
| 				</li> | 				</li> | ||||||
| 			{{end}} | 			{{end}} | ||||||
| 		</div> | 		</div> | ||||||
|  | @ -129,11 +147,16 @@ | ||||||
| 				{{.CsrfTokenHtml}} | 				{{.CsrfTokenHtml}} | ||||||
| 				<input id="label-modal-id" name="id" type="hidden"> | 				<input id="label-modal-id" name="id" type="hidden"> | ||||||
| 				<div class="ui grid"> | 				<div class="ui grid"> | ||||||
| 					<div class="five wide column"> | 					<div class="three wide column"> | ||||||
| 						<div class="ui small input"> | 						<div class="ui small input"> | ||||||
| 							<input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required> | 							<input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
|  | 					<div class="five wide column"> | ||||||
|  | 						<div class="ui small fluid input"> | ||||||
|  | 							<input class="new-label-desc-input" name="description" placeholder="{{.i18n.Tr "repo.issues.new_label_desc_placeholder"}}"> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
| 					<div class="color picker column"> | 					<div class="color picker column"> | ||||||
| 						<input class="color-picker" name="color" value="#70c24a" required> | 						<input class="color-picker" name="color" value="#70c24a" required> | ||||||
| 					</div> | 					</div> | ||||||
|  |  | ||||||
|  | @ -191,7 +191,7 @@ | ||||||
| 						<a class="ui label" href="{{$.RepoLink}}/src/branch/{{.Ref}}">{{.Ref}}</a> | 						<a class="ui label" href="{{$.RepoLink}}/src/branch/{{.Ref}}">{{.Ref}}</a> | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 					{{range .Labels}} | 					{{range .Labels}} | ||||||
| 						<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name}}</a> | 						<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description}}">{{.Name}}</a> | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 
 | 
 | ||||||
| 					{{if .NumComments}} | 					{{if .NumComments}} | ||||||
|  |  | ||||||
|  | @ -10,7 +10,8 @@ | ||||||
| 			<div class="filter menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/labels"> | 			<div class="filter menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/labels"> | ||||||
| 				<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_labels"}}</div> | 				<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_labels"}}</div> | ||||||
| 				{{range .Labels}} | 				{{range .Labels}} | ||||||
| 					<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon {{if .IsChecked}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a> | 					<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon {{if .IsChecked}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}} | ||||||
|  | 					{{if .Description }}<br><small class="desc">{{.Description}}</small>{{end}}</a> | ||||||
| 				{{end}} | 				{{end}} | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
|  | @ -18,7 +19,7 @@ | ||||||
| 			<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span> | 			<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span> | ||||||
| 			{{range .Labels}} | 			{{range .Labels}} | ||||||
| 				<div class="item"> | 				<div class="item"> | ||||||
| 					<a class="ui label {{if not .IsChecked}}hide{{end}}" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name}}</a> | 					<a class="ui label {{if not .IsChecked}}hide{{end}}" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description}}">{{.Name}}</a> | ||||||
| 				</div> | 				</div> | ||||||
| 
 | 
 | ||||||
| 			{{end}} | 			{{end}} | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ | ||||||
| 								especially on mobile views. */}} | 								especially on mobile views. */}} | ||||||
| 								<span style="line-height: 2.5"> | 								<span style="line-height: 2.5"> | ||||||
| 									{{range .}} | 									{{range .}} | ||||||
| 										<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name}}</a> | 										<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description}}">{{.Name}}</a> | ||||||
| 									{{end}} | 									{{end}} | ||||||
| 								</span> | 								</span> | ||||||
| 							{{end}} | 							{{end}} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue