| @@ -144,8 +144,8 @@ func (i *Issue) AfterDelete() { | |||
| } | |||
| } | |||
| // CreateIssue creates new issue for repository. | |||
| func NewIssue(issue *Issue) (err error) { | |||
| // CreateIssue creates new issue with labels for repository. | |||
| func NewIssue(issue *Issue, labelIDs []int64) (err error) { | |||
| sess := x.NewSession() | |||
| defer sessionRelease(sess) | |||
| if err = sess.Begin(); err != nil { | |||
| @@ -158,6 +158,12 @@ func NewIssue(issue *Issue) (err error) { | |||
| return err | |||
| } | |||
| for _, id := range labelIDs { | |||
| if err = issue.addLabel(sess, id); err != nil { | |||
| return fmt.Errorf("addLabel: %v", err) | |||
| } | |||
| } | |||
| if err = sess.Commit(); err != nil { | |||
| return err | |||
| } | |||
| @@ -688,6 +694,10 @@ func HasIssueLabel(issueID, labelID int64) bool { | |||
| } | |||
| func newIssueLabel(e Engine, issueID, labelID int64) error { | |||
| if issueID == 0 || labelID == 0 { | |||
| return nil | |||
| } | |||
| _, err := e.Insert(&IssueLabel{ | |||
| IssueID: issueID, | |||
| LabelID: labelID, | |||
| @@ -98,8 +98,8 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) b | |||
| // \/ \/ \/ | |||
| type CreateIssueForm struct { | |||
| Title string `binding:"Required;MaxSize(255)"` | |||
| LabelIDs []int64 `form:"label_id"` | |||
| Title string `binding:"Required;MaxSize(255)"` | |||
| LabelIDs string `form:"label_ids"` | |||
| MilestoneID int64 | |||
| AssigneeID int64 | |||
| Content string | |||
| @@ -420,3 +420,21 @@ func Subtract(left interface{}, right interface{}) interface{} { | |||
| return fleft + float64(rleft) - (fright + float64(rright)) | |||
| } | |||
| } | |||
| // StringsToInt64s converts a slice of string to a slice of int64. | |||
| func StringsToInt64s(strs []string) []int64 { | |||
| ints := make([]int64, len(strs)) | |||
| for i := range strs { | |||
| ints[i] = com.StrTo(strs[i]).MustInt64() | |||
| } | |||
| return ints | |||
| } | |||
| // Int64sToMap converts a slice of int64 to a int64 map. | |||
| func Int64sToMap(ints []int64) map[int64]bool { | |||
| m := make(map[int64]bool) | |||
| for _, i := range ints { | |||
| m[i] = true | |||
| } | |||
| return m | |||
| } | |||
| @@ -134,24 +134,65 @@ $(document).ready(function () { | |||
| $('.poping.up').popup(); | |||
| // Comment form | |||
| $('.comment.form .tabular.menu .item').tab(); | |||
| $('.comment.form .tabular.menu .item[data-tab="preview"]').click(function () { | |||
| var $this = $(this); | |||
| console.log($('.comment.form .tab.segment[data-tab="write"] textarea').val()) | |||
| console.log($('.comment.form .tab.segment[data-tab="preview"]').html()) | |||
| $.post($this.data('url'), { | |||
| "_csrf": csrf, | |||
| "mode": "gfm", | |||
| "context": $this.data('context'), | |||
| "text": $('.comment.form .tab.segment[data-tab="write"] textarea').val() | |||
| }, | |||
| function (data) { | |||
| console.log(data) | |||
| $('.comment.form .tab.segment[data-tab="preview"]').html(data); | |||
| if ($('.comment.form').length > 0) { | |||
| var $form = $(this); | |||
| $form.find('.tabular.menu .item').tab(); | |||
| $form.find('.tabular.menu .item[data-tab="preview"]').click(function () { | |||
| var $this = $(this); | |||
| $.post($this.data('url'), { | |||
| "_csrf": csrf, | |||
| "mode": "gfm", | |||
| "context": $this.data('context'), | |||
| "text": $form.find('.tab.segment[data-tab="write"] textarea').val() | |||
| }, | |||
| function (data) { | |||
| $form.find('.tab.segment[data-tab="preview"]').html(data); | |||
| } | |||
| ); | |||
| }); | |||
| // Labels | |||
| var $list = $('.ui.labels.list'); | |||
| var $no_select = $list.find('.no-select'); | |||
| $('.select-label .item:not(.no-select)').click(function () { | |||
| if ($(this).hasClass('checked')) { | |||
| $(this).removeClass('checked') | |||
| $(this).find('.octicon').removeClass('octicon-check') | |||
| } else { | |||
| $(this).addClass('checked') | |||
| $(this).find('.octicon').addClass('octicon-check') | |||
| } | |||
| ) | |||
| ; | |||
| }) | |||
| var label_ids = ""; | |||
| $(this).parent().find('.item').each(function () { | |||
| if ($(this).hasClass('checked')) { | |||
| label_ids += $(this).data('id') + ","; | |||
| $($(this).data('id-selector')).removeClass('hide'); | |||
| } else { | |||
| $($(this).data('id-selector')).addClass('hide'); | |||
| } | |||
| }); | |||
| if (label_ids.length == 0) { | |||
| $no_select.removeClass('hide'); | |||
| } else { | |||
| $no_select.addClass('hide'); | |||
| } | |||
| $($(this).parent().data('id')).val(label_ids); | |||
| return false; | |||
| }); | |||
| $('.select-label .no-select.item').click(function () { | |||
| $(this).parent().find('.item').each(function () { | |||
| $(this).removeClass('checked'); | |||
| $(this).find('.octicon').removeClass('octicon-check'); | |||
| }); | |||
| $list.find('.item').each(function () { | |||
| $(this).addClass('hide'); | |||
| }); | |||
| $no_select.removeClass('hide'); | |||
| $($(this).parent().data('id')).val(''); | |||
| }); | |||
| } | |||
| // Helpers. | |||
| $('.delete-button').click(function () { | |||
| @@ -102,7 +102,7 @@ footer { | |||
| } | |||
| .hide { | |||
| display: none; | |||
| display: none!important; | |||
| } | |||
| .center { | |||
| text-align: center; | |||
| @@ -29,6 +29,22 @@ | |||
| font-weight: bold; | |||
| } | |||
| } | |||
| .metas .ui.list { | |||
| .label.color { | |||
| padding: 0 8px; | |||
| margin-right: 5px; | |||
| } | |||
| a { | |||
| padding-top: 5px; | |||
| padding-right: 10px; | |||
| .text { | |||
| color: #444; | |||
| &:hover { | |||
| color: #000; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .filter.menu { | |||
| .label.color { | |||
| margin-left: 15px; | |||
| @@ -91,6 +107,10 @@ | |||
| .comment.form { | |||
| .metas { | |||
| min-width: 220px; | |||
| .filter.menu { | |||
| max-height: 300px; | |||
| overflow-x: auto; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -180,14 +180,16 @@ func NewIssue(ctx *middleware.Context) { | |||
| ctx.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled | |||
| ctx.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes | |||
| var ( | |||
| repo = ctx.Repo.Repository | |||
| err error | |||
| ) | |||
| ctx.Data["Labels"], err = models.GetLabelsByRepoID(repo.ID) | |||
| if err != nil { | |||
| ctx.Handle(500, "GetLabelsByRepoID: %v", err) | |||
| return | |||
| if ctx.User.IsAdmin { | |||
| var ( | |||
| repo = ctx.Repo.Repository | |||
| err error | |||
| ) | |||
| ctx.Data["Labels"], err = models.GetLabelsByRepoID(repo.ID) | |||
| if err != nil { | |||
| ctx.Handle(500, "GetLabelsByRepoID: %v", err) | |||
| return | |||
| } | |||
| } | |||
| // // Get all milestones. | |||
| @@ -219,6 +221,31 @@ func NewIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
| ctx.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled | |||
| ctx.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes | |||
| var ( | |||
| repo = ctx.Repo.Repository | |||
| labelIDs []int64 | |||
| ) | |||
| if ctx.User.IsAdmin { | |||
| // Check labels. | |||
| labelIDs = base.StringsToInt64s(strings.Split(form.LabelIDs, ",")) | |||
| labelIDMark := base.Int64sToMap(labelIDs) | |||
| labels, err := models.GetLabelsByRepoID(repo.ID) | |||
| if err != nil { | |||
| ctx.Handle(500, "GetLabelsByRepoID: %v", err) | |||
| return | |||
| } | |||
| hasSelected := false | |||
| for i := range labels { | |||
| if labelIDMark[labels[i].ID] { | |||
| labels[i].IsChecked = true | |||
| hasSelected = true | |||
| } | |||
| } | |||
| ctx.Data["HasSelectedLabel"] = hasSelected | |||
| ctx.Data["label_ids"] = form.LabelIDs | |||
| ctx.Data["Labels"] = labels | |||
| } | |||
| if ctx.HasError() { | |||
| ctx.HTML(200, ISSUE_NEW) | |||
| return | |||
| @@ -226,18 +253,17 @@ func NewIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
| issue := &models.Issue{ | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| Index: int64(ctx.Repo.Repository.NumIssues) + 1, | |||
| Index: int64(repo.NumIssues) + 1, | |||
| Name: form.Title, | |||
| PosterID: ctx.User.Id, | |||
| // MilestoneID: form.MilestoneID, | |||
| // AssigneeID: form.AssigneeID, | |||
| // LabelIDs: "$" + strings.Join(form.LabelIDs, "|$") + "|", | |||
| Content: form.Content, | |||
| } | |||
| if err := models.NewIssue(issue); err != nil { | |||
| if err := models.NewIssue(issue, labelIDs); err != nil { | |||
| ctx.Handle(500, "NewIssue", err) | |||
| return | |||
| } else if err := models.NewIssueUserPairs(ctx.Repo.Repository, issue); err != nil { | |||
| } else if err := models.NewIssueUserPairs(repo, issue); err != nil { | |||
| ctx.Handle(500, "NewIssue", err) | |||
| return | |||
| } | |||
| @@ -38,22 +38,26 @@ | |||
| </div> | |||
| {{if .IsRepositoryAdmin}} | |||
| <input id="label_ids" name="label_ids" type="hidden" value="{{.label_ids}}"> | |||
| <div class="four wide column"> | |||
| <div class="ui segment metas"> | |||
| <div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item"> | |||
| <div class="ui {{if not .Labels}}disabled{{end}} jump select-label dropdown"> | |||
| <span class="text"> | |||
| <strong>{{.i18n.Tr "repo.issues.new.labels"}}</strong> | |||
| <span class="octicon octicon-gear"></span> | |||
| </span> | |||
| <div class="filter menu"> | |||
| <a class="item" href="#">{{.i18n.Tr "repo.issues.new.clear_labels"}}</a> | |||
| <div class="filter menu" data-id="#label_ids"> | |||
| <a class="no-select item" href="#">{{.i18n.Tr "repo.issues.new.clear_labels"}}</a> | |||
| {{range .Labels}} | |||
| <a class="item" href="#"><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}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| <div class="ui list"> | |||
| <span class="item {{if .SelectedLabels}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span> | |||
| <div class="ui labels list"> | |||
| <span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span> | |||
| {{range .Labels}} | |||
| <a class="{{if not .IsChecked}}hide{{end}} item" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}"><span class="label color" style="background-color: {{.Color}}"></span> <span class="text">{{.Name}}</span></a> | |||
| {{end}} | |||
| </div> | |||
| <!-- <div class="ui divider"></div> | |||
| <div class="ui {{if .Labels}}disabled{{end}} dropdown jump item"> | |||