From a32010bc11c7020c58529a06b20307f729cdf27c Mon Sep 17 00:00:00 2001 From: Arinde Eniola Date: Wed, 27 Apr 2016 17:13:55 +0100 Subject: [PATCH 1/3] initial setup for making issues from line comment --- app/assets/javascripts/api.js.coffee | 16 ++++++ .../create_issue_comment.js.coffee | 56 +++++++++++++++++++ app/assets/javascripts/dispatcher.js.coffee | 1 + app/assets/javascripts/notes.js.coffee | 1 + app/views/projects/_md_preview.html.haml | 5 ++ .../projects/merge_requests/_show.html.haml | 2 +- 6 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/create_issue_comment.js.coffee diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index 3f61ea1eaf43..486e3c212da1 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -7,6 +7,7 @@ labelsPath: "/api/:version/projects/:id/labels" licensePath: "/api/:version/licenses/:key" gitignorePath: "/api/:version/gitignores/:key" + issuesPath: "/api/:version/projects/:id/issues" group: (group_id, callback) -> url = Api.buildUrl(Api.groupPath) @@ -79,6 +80,21 @@ .error (message) -> callback(message.responseJSON) + newIssue: (project_id, data, callback) -> + url = Api.buildUrl(Api.issues_path) + url = url.replace(':id', project_id) + + data.private_token = gon.api_token + $.ajax( + url: url + type: "POST" + data: data + dataType: "json" + ).done (issue) -> + callback(issue) + .error (message) -> + callback(message.responseJSON) + # Return group projects list. Filtered by query groupProjects: (group_id, query, callback) -> url = Api.buildUrl(Api.groupProjectsPath) diff --git a/app/assets/javascripts/create_issue_comment.js.coffee b/app/assets/javascripts/create_issue_comment.js.coffee new file mode 100644 index 000000000000..2b6b87a9cda1 --- /dev/null +++ b/app/assets/javascripts/create_issue_comment.js.coffee @@ -0,0 +1,56 @@ +class @CreateIssueFromComment + + constructor: -> + @hideOrShowCheckbox() + + $('.diffs').on 'click', '.create-issue-from-note', (e) => + $eventTarget = $(event.target) + if $eventTarget.is('[type="checkbox"]:checked') + $form = $eventTarget.closest('.js-discussion-note-form') + @addFormatToTextArea $form + @setUpListenerForFormSubmit $form + else if not $eventTarget.is('[type="checkbox"]:checked') + @setBackToDefault($eventTarget) + + + hideOrShowCheckbox: -> + $('.merge-request').on 'createIssueFromComment:show', () -> + $(this).find('.create-issue-from-note').removeClass('hidden') + + addFormatToTextArea: (form) -> + textArea = $(form).find('textarea.markdown-area') + + textNote = "Issue Title (Required)\n---\nDescription" + $(textArea).val(textNote) + + setBackToDefault: (checkbox) -> + $form = checkbox.closest('.js-discussion-note-form') + textArea = $form.find('textarea.markdown-area') + + $(textArea).val('') + + setUpListenerForFormSubmit: (form) -> + $(form).on 'ajax:beforeSend', (e) => + e.preventDefault() + textAreaValue = $(form).find('textarea.markdown-area').val() + + # ensure the user uses the format on the textarea + if textAreaValue.indexOf('---') is -1 + @addFormatToTextArea(form) + return false + + # check if the the title is present since it is required + textValueArr = textAreaValue.split('---') + if $.trim(textValueArr[0]) + @createIssue textValueArr, $(form).closest('.tab-content').data('project-id') + else + return false + + createIssue: (textArr, projectId) -> + # Create new label with API + Api.newIssue projectId, { + title: textArr[0] + description: textArr[1] if textArr[1] + }, (issue) -> + if issue + console.log 'crap' diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index a3185f876408..c8186d11d9a8 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -53,6 +53,7 @@ class Dispatcher shortcut_handler = new ShortcutsIssuable(true) new ZenMode() when "projects:merge_requests:diffs" + new CreateIssueFromComment() new Diff() new ZenMode() when 'projects:merge_requests:index' diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index f8151963fa79..d5b58be854f6 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -535,6 +535,7 @@ class @Notes # show the form @setupDiscussionNoteForm $link, newForm + $link.trigger('createIssueFromComment:show') ### Called in response to "cancel" on a diff note form. diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 81afea2c60a2..97b328c68bef 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -7,6 +7,11 @@ %li %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 } Preview + - if @merge_request + %li.create-issue-from-note.hidden + = check_box_tag 'create-issue' + %span + Make an issue from this note %li.pull-right %button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 } Go full screen diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 7af227129ecb..96d9e40cfd49 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -64,7 +64,7 @@ Changes %span.badge= @merge_request.diff_size - .tab-content + .tab-content{data: { project_id: @project.id } } #notes.notes.tab-pane.voting_notes .content-block.content-block-small.oneline-block = render 'votes/votes_block', votable: @merge_request -- GitLab From 47e67f3cc2dc395c887f4e8693ad224876519826 Mon Sep 17 00:00:00 2001 From: Arinde Eniola Date: Sat, 30 Apr 2016 22:45:22 +0100 Subject: [PATCH 2/3] add check to append the issue url to the newly created note --- app/assets/javascripts/api.js.coffee | 5 +-- .../create_issue_comment.js.coffee | 33 ++++++++++++------- app/assets/javascripts/dispatcher.js.coffee | 1 + app/assets/javascripts/notes.js.coffee | 2 ++ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index 486e3c212da1..0d58da39e0a4 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -88,12 +88,13 @@ $.ajax( url: url type: "POST" + context: @ data: data dataType: "json" ).done (issue) -> - callback(issue) + callback.call(@, issue) .error (message) -> - callback(message.responseJSON) + callback.call(@, message.responseJSON) # Return group projects list. Filtered by query groupProjects: (group_id, query, callback) -> diff --git a/app/assets/javascripts/create_issue_comment.js.coffee b/app/assets/javascripts/create_issue_comment.js.coffee index 2b6b87a9cda1..3d476dab155f 100644 --- a/app/assets/javascripts/create_issue_comment.js.coffee +++ b/app/assets/javascripts/create_issue_comment.js.coffee @@ -3,16 +3,16 @@ class @CreateIssueFromComment constructor: -> @hideOrShowCheckbox() - $('.diffs').on 'click', '.create-issue-from-note', (e) => + $('#diffs').on 'click', '.create-issue-from-note', (e) => $eventTarget = $(event.target) if $eventTarget.is('[type="checkbox"]:checked') $form = $eventTarget.closest('.js-discussion-note-form') @addFormatToTextArea $form @setUpListenerForFormSubmit $form + @setUpListenerForNoteCreation() else if not $eventTarget.is('[type="checkbox"]:checked') @setBackToDefault($eventTarget) - hideOrShowCheckbox: -> $('.merge-request').on 'createIssueFromComment:show', () -> $(this).find('.create-issue-from-note').removeClass('hidden') @@ -40,17 +40,28 @@ class @CreateIssueFromComment return false # check if the the title is present since it is required - textValueArr = textAreaValue.split('---') - if $.trim(textValueArr[0]) - @createIssue textValueArr, $(form).closest('.tab-content').data('project-id') + @textValueArr = textAreaValue.split('---') + @projectId = $(form).closest('.tab-content').data('project-id') + if $.trim(@textValueArr[0]) + @setUpListenerForNoteCreation() else return false + setUpListenerForNoteCreation: -> + $('#diffs') + .off 'note:created' + .on 'note:created', (e, note) => + @note = note + @createIssue @textValueArr, @projectId + createIssue: (textArr, projectId) -> - # Create new label with API - Api.newIssue projectId, { + # Create new Issue with API + newIssue = Api.newIssue.bind(@) + newIssue projectId, { title: textArr[0] - description: textArr[1] if textArr[1] - }, (issue) -> - if issue - console.log 'crap' + description: textArr[1] + }, (issue) => + if issue and issue.title + $noteText = $('.js-task-list-container').find('note-text') + projectUrl = $('.shortcuts-issues').attr('href') + $noteText.append("

") diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index c8186d11d9a8..d4e27d966309 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -49,6 +49,7 @@ class Dispatcher new ZenMode() new GLForm($('.release-form')) when 'projects:merge_requests:show' + new CreateIssueFromComment() new Diff() shortcut_handler = new ShortcutsIssuable(true) new ZenMode() diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index d5b58be854f6..1fa2f6528afd 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -225,6 +225,7 @@ class @Notes # Add note to 'Changes' page discussions discussionContainer.append note_html + $('#diffs').trigger('note:created', note) # Init discussion on 'Discussion' page if it is merge request page if $('body').attr('data-page').indexOf('projects:merge_request') is 0 @@ -234,6 +235,7 @@ class @Notes else # append new note to all matching discussions discussionContainer.append note_html + $('#diffs').trigger('note:created', note) gl.utils.localTimeAgo($('.js-timeago', note_html), false) -- GitLab From 5b322f0276878470f573c84e1f5bb4d27997f522 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 4 May 2016 20:56:11 +0200 Subject: [PATCH 3/3] Notes can be used to create a new issue --- app/controllers/projects/notes_controller.rb | 6 +++- app/services/notes/create_service.rb | 19 +++++++++++ db/schema.rb | 13 ++++++++ spec/services/notes/create_service_spec.rb | 34 +++++++++++++++++++- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 40b24d550e0e..26fe79e56cbf 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -20,7 +20,11 @@ def index end def create - @note = Notes::CreateService.new(project, current_user, note_params).execute + @note = if params[:new_issue] + Notes::CreateService.new(project, current_user, note_params).new_issue + else + Notes::CreateService.new(project, current_user, note_params).execute + end respond_to do |format| format.json { render json: note_json(@note) } diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 2bb312bb252c..25fcac13540d 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -13,5 +13,24 @@ def execute note end + + # An issue can be create from a line comment. This issue has the specified format: + # + # + # ## Title here + # --- + # description should be here + # + def new_issue + lines = params[:note].lines + title = lines[0].gsub(/\A.{3}\W*/, '').rstrip + description = lines[2..-1].join('\n').strip # lines[1] == '---' and is thus discarded + + issue = Issues::CreateService.new(project, current_user, title: title, description: description).execute + return issue unless issue.valid? + + params[:note] = "#{issue.to_reference} was created from this line comment." + execute + end end end diff --git a/db/schema.rb b/db/schema.rb index b2af810f600c..30b5e9fc92e6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -100,6 +100,19 @@ add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree + create_table "award_emoji", force: :cascade do |t| + t.string "name" + t.integer "user_id" + t.integer "awardable_id" + t.string "awardable_type" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "award_emoji", ["awardable_id"], name: "index_award_emoji_on_awardable_id", using: :btree + add_index "award_emoji", ["awardable_type"], name: "index_award_emoji_on_awardable_type", using: :btree + add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree + create_table "broadcast_messages", force: :cascade do |t| t.text "message", null: false t.datetime "starts_at" diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index ff23f13e1cb3..1e59ffa05bfa 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -14,7 +14,7 @@ noteable_type: 'Issue', noteable_id: issue.id } - + @note = Notes::CreateService.new(project, user, opts).execute end @@ -23,6 +23,38 @@ end end + describe "#new_issue" do + let(:content) do + <<-NOTE + My new title + --- + This is my body + NOTE + end + let(:merge_request) { create(:merge_request) } + let(:params) { {note: content, noteable_type: "MergeRequest", noteable_id: merge_request.id} } + + before do + project.team << [user, :master] + end + + it "creates a new issue" do + expect { Notes::CreateService.new(project, user, params).new_issue }.to change { Issue.count }.by(1) + end + + it 'sets a bota a note and a reference' do + expect { Notes::CreateService.new(project, user, params).new_issue }.to change { Note.count }.by(2) + end + + it "parses the note" do + Notes::CreateService.new(project, user, params).new_issue + new_issue = Issue.last + + expect(new_issue.title).to eq 'My new title' + expect(new_issue.description).to eq 'This is my body' + end + end + describe "award emoji" do before do project.team << [user, :master] -- GitLab