diff --git a/CHANGELOG b/CHANGELOG index f6d39b28c3b..d8ef5866a1a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -120,6 +120,7 @@ v 8.10.0 (unreleased) - Limit the number of retries on error to 3 for exporting projects - Allow empty repositories on project import/export - Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska) + - Allow bulk (un)subscription from issues in issue index v 8.9.6 - Fix importing of events under notes for GitLab projects. !5154 diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee index 6b0e69dbae7..3d09ea08e3b 100644 --- a/app/assets/javascripts/issues-bulk-assignment.js.coffee +++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee @@ -85,12 +85,13 @@ class @IssuableBulkActions getFormDataAsObject: -> formData = update: - state_event : @form.find('input[name="update[state_event]"]').val() - assignee_id : @form.find('input[name="update[assignee_id]"]').val() - milestone_id : @form.find('input[name="update[milestone_id]"]').val() - issues_ids : @form.find('input[name="update[issues_ids]"]').val() - add_label_ids : [] - remove_label_ids : [] + state_event : @form.find('input[name="update[state_event]"]').val() + assignee_id : @form.find('input[name="update[assignee_id]"]').val() + milestone_id : @form.find('input[name="update[milestone_id]"]').val() + issues_ids : @form.find('input[name="update[issues_ids]"]').val() + subscription_event : @form.find('input[name="update[subscription_event]"]').val() + add_label_ids : [] + remove_label_ids : [] if @willUpdateLabels @getLabelsToApply().map (id) -> diff --git a/app/assets/javascripts/subscription_select.js.coffee b/app/assets/javascripts/subscription_select.js.coffee new file mode 100644 index 00000000000..e5eb7a50d80 --- /dev/null +++ b/app/assets/javascripts/subscription_select.js.coffee @@ -0,0 +1,18 @@ +class @SubscriptionSelect + constructor: -> + $('.js-subscription-event').each (i, el) -> + fieldName = $(el).data("field-name") + + $(el).glDropdown( + selectable: true + fieldName: fieldName + toggleLabel: (selected, el, instance) => + label = 'Subscription' + $item = instance.dropdown.find('.is-active') + label = $item.text() if $item.length + label + clicked: (item, $el, e)-> + e.preventDefault() + id: (obj, el) -> + $(el).data("id") + ) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b6e80762e3c..fa663c9bda4 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -226,6 +226,7 @@ class Projects::IssuesController < Projects::ApplicationController :assignee_id, :milestone_id, :state_event, + :subscription_event, label_ids: [], add_label_ids: [], remove_label_ids: [] diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index e3dc569152c..2d96efe1042 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -101,6 +101,7 @@ class IssuableBaseService < BaseService def update(issuable) change_state(issuable) + change_subscription(issuable) filter_params old_labels = issuable.labels.to_a @@ -124,6 +125,15 @@ class IssuableBaseService < BaseService end end + def change_subscription(issuable) + case params.delete(:subscription_event) + when 'subscribe' + issuable.subscribe(current_user) + when 'unsubscribe' + issuable.unsubscribe(current_user) + end + end + def has_changes?(issuable, old_labels: []) valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch] diff --git a/app/services/issues/bulk_update_service.rb b/app/services/issues/bulk_update_service.rb index cd08c3a0cb9..7e19a73f71a 100644 --- a/app/services/issues/bulk_update_service.rb +++ b/app/services/issues/bulk_update_service.rb @@ -4,7 +4,7 @@ module Issues issues_ids = params.delete(:issues_ids).split(",") issue_params = params - %i(state_event milestone_id assignee_id add_label_ids remove_label_ids).each do |key| + %i(state_event milestone_id assignee_id add_label_ids remove_label_ids subscription_event).each do |key| issue_params.delete(key) unless issue_params[key].present? end diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 094d6636c66..0b7fa8c7d06 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -44,9 +44,15 @@ placeholder: "Search authors", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: @project.id, field_name: "update[assignee_id]" } }) .filter-item.inline = dropdown_tag("Milestone", options: { title: "Assign milestone", toggle_class: 'js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update', filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true } }) - .filter-item.inline.labels-filter = render "shared/issuable/label_dropdown", classes: ['js-filter-bulk-update', 'js-multiselect'], show_create: false, show_footer: false, extra_options: false, filter_submit: false, show_footer: false, data_options: { persist_when_hide: "true", field_name: "update[label_ids][]", show_no: false, show_any: false, use_id: true } + .filter-item.inline + = dropdown_tag("Subscription", options: { toggle_class: "js-subscription-event", title: "Change subscription", dropdown_class: "dropdown-menu-selectable", data: { field_name: "update[subscription_event]" } } ) do + %ul + %li + %a{href: "#", data: {id: "subscribe"}} Subscribe + %li + %a{href: "#", data: {id: "unsubscribe"}} Unsubscribe = hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag :state_event, params[:state_event] @@ -63,6 +69,7 @@ new LabelsSelect(); new MilestoneSelect(); new IssueStatusSelect(); + new SubscriptionSelect(); $('form.filter-form').on('submit', function (event) { event.preventDefault(); Turbolinks.visit(this.action + '&' + $(this).serialize()); diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb index 4a689e64dc5..ba3a4dfc048 100644 --- a/spec/services/issues/bulk_update_service_spec.rb +++ b/spec/services/issues/bulk_update_service_spec.rb @@ -262,4 +262,42 @@ describe Issues::BulkUpdateService, services: true do end end end + + describe :subscribe_issues do + let(:issues) { create_list(:issue, 5, project: project) } + let(:params) do + { + subscription_event: 'subscribe', + issues_ids: issues.map(&:id).join(',') + } + end + + it 'subscribes the given user' do + issues.each do |issue| + expect(issue.subscribed?(user)).to be_truthy + end + end + end + + describe :unsubscribe_issues do + let(:issues) { create_list(:closed_issue, 5, project: project) } + let(:params) do + { + subscription_event: 'unsubscribe', + issues_ids: issues.map(&:id).join(',') + } + end + + before do + issues.each do |issue| + issue.subscriptions.create(user: user, subscribed: true) + end + end + + it 'unsubscribes the given user' do + issues.each do |issue| + expect(issue.subscribed?(user)).to be_falsey + end + end + end end