* Add optional label sets on repo creation * Fix CRLF * Instead of hardcoding default, make it the helper * Move label set init out of repo init Add a new error for the router Combine router label init with repo creation label init Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add issue labels to Swagger for repo creation Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update models/issue_label.go Co-Authored-By: Lauris BH <lauris@nix.lv> * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>tags/v1.11.0-dev
| @@ -1058,6 +1058,22 @@ func (err ErrIssueNotExist) Error() string { | |||||
| return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) | return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) | ||||
| } | } | ||||
| // ErrIssueLabelTemplateLoad represents a "ErrIssueLabelTemplateLoad" kind of error. | |||||
| type ErrIssueLabelTemplateLoad struct { | |||||
| TemplateFile string | |||||
| OriginalError error | |||||
| } | |||||
| // IsErrIssueLabelTemplateLoad checks if an error is a ErrIssueLabelTemplateLoad. | |||||
| func IsErrIssueLabelTemplateLoad(err error) bool { | |||||
| _, ok := err.(ErrIssueLabelTemplateLoad) | |||||
| return ok | |||||
| } | |||||
| func (err ErrIssueLabelTemplateLoad) Error() string { | |||||
| return fmt.Sprintf("Failed to load label template file '%s': %v", err.TemplateFile, err.OriginalError) | |||||
| } | |||||
| // __________ .__ .__ __________ __ | // __________ .__ .__ __________ __ | ||||
| // \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_ | // \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_ | ||||
| // | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\ | // | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\ | ||||
| @@ -127,6 +127,34 @@ func (label *Label) ForegroundColor() template.CSS { | |||||
| return template.CSS("#000") | return template.CSS("#000") | ||||
| } | } | ||||
| func initalizeLabels(e Engine, repoID int64, labelTemplate string) error { | |||||
| list, err := GetLabelTemplateFile(labelTemplate) | |||||
| if err != nil { | |||||
| return ErrIssueLabelTemplateLoad{labelTemplate, err} | |||||
| } | |||||
| labels := make([]*Label, len(list)) | |||||
| for i := 0; i < len(list); i++ { | |||||
| labels[i] = &Label{ | |||||
| RepoID: repoID, | |||||
| Name: list[i][0], | |||||
| Description: list[i][2], | |||||
| Color: list[i][1], | |||||
| } | |||||
| } | |||||
| for _, label := range labels { | |||||
| if err = newLabel(e, label); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // InitalizeLabels adds a label set to a repository using a template | |||||
| func InitalizeLabels(repoID int64, labelTemplate string) error { | |||||
| return initalizeLabels(x, repoID, labelTemplate) | |||||
| } | |||||
| func newLabel(e Engine, label *Label) error { | func newLabel(e Engine, label *Label) error { | ||||
| _, err := e.Insert(label) | _, err := e.Insert(label) | ||||
| return err | return err | ||||
| @@ -1098,6 +1098,7 @@ type CreateRepoOptions struct { | |||||
| Description string | Description string | ||||
| OriginalURL string | OriginalURL string | ||||
| Gitignores string | Gitignores string | ||||
| IssueLabels string | |||||
| License string | License string | ||||
| Readme string | Readme string | ||||
| IsPrivate bool | IsPrivate bool | ||||
| @@ -1394,6 +1395,13 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err | |||||
| return nil, fmt.Errorf("initRepository: %v", err) | return nil, fmt.Errorf("initRepository: %v", err) | ||||
| } | } | ||||
| // Initialize Issue Labels if selected | |||||
| if len(opts.IssueLabels) > 0 { | |||||
| if err = initalizeLabels(sess, repo.ID, opts.IssueLabels); err != nil { | |||||
| return nil, fmt.Errorf("initalizeLabels: %v", err) | |||||
| } | |||||
| } | |||||
| _, stderr, err := process.GetManager().ExecDir(-1, | _, stderr, err := process.GetManager().ExecDir(-1, | ||||
| repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), | repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), | ||||
| git.GitExecutable, "update-server-info") | git.GitExecutable, "update-server-info") | ||||
| @@ -33,6 +33,7 @@ type CreateRepoForm struct { | |||||
| Description string `binding:"MaxSize(255)"` | Description string `binding:"MaxSize(255)"` | ||||
| AutoInit bool | AutoInit bool | ||||
| Gitignores string | Gitignores string | ||||
| IssueLabels string | |||||
| License string | License string | ||||
| Readme string | Readme string | ||||
| } | } | ||||
| @@ -67,6 +67,8 @@ type CreateRepoOption struct { | |||||
| Description string `json:"description" binding:"MaxSize(255)"` | Description string `json:"description" binding:"MaxSize(255)"` | ||||
| // Whether the repository is private | // Whether the repository is private | ||||
| Private bool `json:"private"` | Private bool `json:"private"` | ||||
| // Issue Label set to use | |||||
| IssueLabels string `json:"issue_labels"` | |||||
| // Whether the repository should be auto-intialized? | // Whether the repository should be auto-intialized? | ||||
| AutoInit bool `json:"auto_init"` | AutoInit bool `json:"auto_init"` | ||||
| // Gitignores to use | // Gitignores to use | ||||
| @@ -578,6 +578,8 @@ fork_visibility_helper = The visibility of a forked repository cannot be changed | |||||
| repo_desc = Description | repo_desc = Description | ||||
| repo_lang = Language | repo_lang = Language | ||||
| repo_gitignore_helper = Select .gitignore templates. | repo_gitignore_helper = Select .gitignore templates. | ||||
| issue_labels = Issue Labels | |||||
| issue_labels_helper = Select an issue label set. | |||||
| license = License | license = License | ||||
| license_helper = Select a license file. | license_helper = Select a license file. | ||||
| readme = README | readme = README | ||||
| @@ -206,6 +206,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR | |||||
| repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{ | repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{ | ||||
| Name: opt.Name, | Name: opt.Name, | ||||
| Description: opt.Description, | Description: opt.Description, | ||||
| IssueLabels: opt.IssueLabels, | |||||
| Gitignores: opt.Gitignores, | Gitignores: opt.Gitignores, | ||||
| License: opt.License, | License: opt.License, | ||||
| Readme: opt.Readme, | Readme: opt.Readme, | ||||
| @@ -33,24 +33,15 @@ func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ||||
| return | return | ||||
| } | } | ||||
| list, err := models.GetLabelTemplateFile(form.TemplateName) | |||||
| if err != nil { | |||||
| ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, err)) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | |||||
| return | |||||
| } | |||||
| labels := make([]*models.Label, len(list)) | |||||
| for i := 0; i < len(list); i++ { | |||||
| labels[i] = &models.Label{ | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| Name: list[i][0], | |||||
| Description: list[i][2], | |||||
| Color: list[i][1], | |||||
| if err := models.InitalizeLabels(ctx.Repo.Repository.ID, form.TemplateName); err != nil { | |||||
| if models.IsErrIssueLabelTemplateLoad(err) { | |||||
| originalErr := err.(models.ErrIssueLabelTemplateLoad).OriginalError | |||||
| ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr)) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | |||||
| return | |||||
| } | } | ||||
| } | |||||
| if err := models.NewLabels(labels...); err != nil { | |||||
| ctx.ServerError("NewLabels", err) | |||||
| ctx.ServerError("InitalizeLabels", err) | |||||
| return | return | ||||
| } | } | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ||||
| @@ -115,6 +115,7 @@ func Create(ctx *context.Context) { | |||||
| // Give default value for template to render. | // Give default value for template to render. | ||||
| ctx.Data["Gitignores"] = models.Gitignores | ctx.Data["Gitignores"] = models.Gitignores | ||||
| ctx.Data["LabelTemplates"] = models.LabelTemplates | |||||
| ctx.Data["Licenses"] = models.Licenses | ctx.Data["Licenses"] = models.Licenses | ||||
| ctx.Data["Readmes"] = models.Readmes | ctx.Data["Readmes"] = models.Readmes | ||||
| ctx.Data["readme"] = "Default" | ctx.Data["readme"] = "Default" | ||||
| @@ -155,6 +156,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("new_repo") | ctx.Data["Title"] = ctx.Tr("new_repo") | ||||
| ctx.Data["Gitignores"] = models.Gitignores | ctx.Data["Gitignores"] = models.Gitignores | ||||
| ctx.Data["LabelTemplates"] = models.LabelTemplates | |||||
| ctx.Data["Licenses"] = models.Licenses | ctx.Data["Licenses"] = models.Licenses | ||||
| ctx.Data["Readmes"] = models.Readmes | ctx.Data["Readmes"] = models.Readmes | ||||
| @@ -173,6 +175,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | |||||
| Name: form.RepoName, | Name: form.RepoName, | ||||
| Description: form.Description, | Description: form.Description, | ||||
| Gitignores: form.Gitignores, | Gitignores: form.Gitignores, | ||||
| IssueLabels: form.IssueLabels, | |||||
| License: form.License, | License: form.License, | ||||
| Readme: form.Readme, | Readme: form.Readme, | ||||
| IsPrivate: form.Private || setting.Repository.ForcePrivate, | IsPrivate: form.Private || setting.Repository.ForcePrivate, | ||||
| @@ -56,6 +56,20 @@ | |||||
| <textarea id="description" name="description">{{.description}}</textarea> | <textarea id="description" name="description">{{.description}}</textarea> | ||||
| </div> | </div> | ||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.issue_labels"}}</label> | |||||
| <div class="ui search normal selection dropdown"> | |||||
| <input type="hidden" name="issue_labels" value="{{.issueLabels}}"> | |||||
| <div class="default text">{{.i18n.Tr "repo.issue_labels_helper"}}</div> | |||||
| <div class="menu"> | |||||
| <div class="item" data-value="">{{.i18n.Tr "repo.issue_labels_helper"}}</div> | |||||
| {{range .LabelTemplates}} | |||||
| <div class="item" data-value="{{.}}">{{.}}</div> | |||||
| {{end}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| @@ -7843,6 +7843,11 @@ | |||||
| "type": "string", | "type": "string", | ||||
| "x-go-name": "Gitignores" | "x-go-name": "Gitignores" | ||||
| }, | }, | ||||
| "issue_labels": { | |||||
| "description": "Issue Label set to use", | |||||
| "type": "string", | |||||
| "x-go-name": "IssueLabels" | |||||
| }, | |||||
| "license": { | "license": { | ||||
| "description": "License to use", | "description": "License to use", | ||||
| "type": "string", | "type": "string", | ||||