Feature: Bulk Issues update
This commit is contained in:
parent
d63706d72c
commit
00b280c3f9
10 changed files with 144 additions and 32 deletions
|
@ -52,14 +52,6 @@ $(document).ready(function(){
|
|||
}
|
||||
});
|
||||
|
||||
$("#issues-table .issue").live('click', function(e){
|
||||
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
|
||||
location.href = $(this).attr("url");
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Focus search field by pressing 's' key
|
||||
*/
|
||||
|
|
|
@ -67,6 +67,10 @@ function initIssuesSearch() {
|
|||
*/
|
||||
function issuesPage(){
|
||||
initIssuesSearch();
|
||||
$("#update_status").chosen();
|
||||
$("#update_assignee_id").chosen();
|
||||
$("#update_milestone_id").chosen();
|
||||
|
||||
$("#label_name").chosen();
|
||||
$("#assignee_id").chosen();
|
||||
$("#milestone_id").chosen();
|
||||
|
@ -94,4 +98,29 @@ function issuesPage(){
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
$(".check_all_issues").click(function () {
|
||||
$('.selected_issue').attr('checked', this.checked);
|
||||
issuesCheckChanged();
|
||||
});
|
||||
|
||||
$('.selected_issue').bind('change', issuesCheckChanged);
|
||||
}
|
||||
|
||||
function issuesCheckChanged() {
|
||||
var checked_issues = $('.selected_issue:checked');
|
||||
|
||||
if(checked_issues.length > 0) {
|
||||
var ids = []
|
||||
$.each(checked_issues, function(index, value) {
|
||||
ids.push($(value).attr("data-id"));
|
||||
})
|
||||
$('#update_issues_ids').val(ids);
|
||||
$('.issues_filters').hide();
|
||||
$('.issues_bulk_update').show();
|
||||
} else {
|
||||
$('#update_issues_ids').val([]);
|
||||
$('.issues_bulk_update').hide();
|
||||
$('.issues_filters').show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,13 @@
|
|||
.issue {
|
||||
padding:7px 10px;
|
||||
|
||||
.issue_check {
|
||||
float:left;
|
||||
padding: 8px 0;
|
||||
padding-right: 8px;
|
||||
min-width: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
padding-top:0;
|
||||
padding-bottom:2px;
|
||||
|
@ -41,3 +48,28 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
input.check_all_issues {
|
||||
float:left;
|
||||
padding: 8px 0;
|
||||
margin: 14px 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#issues-table-holder {
|
||||
.issues_bulk_update {
|
||||
padding: 0 5px;
|
||||
margin: 0;
|
||||
form {
|
||||
margin:0;
|
||||
padding-bottom:5px;
|
||||
}
|
||||
.update_selected_issues {
|
||||
position:relative;
|
||||
top:-2px;
|
||||
margin-left:3px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,17 @@ class BaseContext
|
|||
def initialize(project, user, params)
|
||||
@project, @current_user, @params = project, user, params.dup
|
||||
end
|
||||
|
||||
def abilities
|
||||
@abilities ||= begin
|
||||
abilities = Six.new
|
||||
abilities << Ability
|
||||
abilities
|
||||
end
|
||||
end
|
||||
|
||||
def can?(object, action, subject)
|
||||
abilities.allowed?(object, action, subject)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
24
app/contexts/issues_bulk_update_context.rb
Normal file
24
app/contexts/issues_bulk_update_context.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
class IssuesBulkUpdateContext < BaseContext
|
||||
def execute
|
||||
update_data = params[:update]
|
||||
|
||||
issues_ids = update_data[:issues_ids].split(",")
|
||||
milestone_id = update_data[:milestone_id]
|
||||
assignee_id = update_data[:assignee_id]
|
||||
status = update_data[:status]
|
||||
|
||||
opts = {}
|
||||
opts[:milestone_id] = milestone_id if milestone_id.present?
|
||||
opts[:assignee_id] = assignee_id if assignee_id.present?
|
||||
opts[:closed] = (status == "closed") if status.present?
|
||||
|
||||
issues = Issue.where(:id => issues_ids).all
|
||||
issues = issues.select { |issue| can?(current_user, :modify_issue, issue) }
|
||||
issues.each { |issue| issue.update_attributes(opts) }
|
||||
{
|
||||
:count => issues.count,
|
||||
:success => !issues.count.zero?
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -113,6 +113,11 @@ class IssuesController < ApplicationController
|
|||
render :partial => 'issues'
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
result = IssuesBulkUpdateContext.new(project, current_user, params).execute
|
||||
redirect_to :back, :notice => "#{result[:count]} issues updated"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def issue
|
||||
|
|
|
@ -12,3 +12,4 @@
|
|||
- else
|
||||
%li
|
||||
%h4.nothing_here_message Nothing to show here
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
%li.wll{ :id => dom_id(issue), :class => issue_css_classes(issue), :url => project_issue_path(issue.project, issue) }
|
||||
.list_legend
|
||||
.icon
|
||||
.issue_check
|
||||
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, :class => "selected_issue", :disabled => !can?(current_user, :modify_issue, issue)
|
||||
.right
|
||||
- issue.labels.each do |label|
|
||||
%span.label.label-issue.grouped
|
||||
|
@ -34,4 +34,4 @@
|
|||
|
||||
|
||||
- if issue.upvotes > 0
|
||||
%span.badge.badge-success= "+#{issue.upvotes}"
|
||||
%span.badge.badge-success= "+#{issue.upvotes}"
|
||||
|
|
|
@ -14,35 +14,51 @@
|
|||
= search_field_tag :issue_search, nil, { :placeholder => 'Search', :class => 'issue_search span3 right neib' }
|
||||
|
||||
.clearfix
|
||||
|
||||
%div#issues-table-holder.ui-box
|
||||
.title
|
||||
.left
|
||||
%ul.nav.nav-pills.left
|
||||
%li{:class => ("active" if (params[:f] == issues_filter[:open] || !params[:f]))}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:open], :milestone_id => params[:milestone_id]) do
|
||||
Open
|
||||
%li{:class => ("active" if params[:f] == issues_filter[:closed])}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:closed], :milestone_id => params[:milestone_id]) do
|
||||
Closed
|
||||
%li{:class => ("active" if params[:f] == issues_filter[:to_me])}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:to_me], :milestone_id => params[:milestone_id]) do
|
||||
To Me
|
||||
%li{:class => ("active" if params[:f] == issues_filter[:all])}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:all], :milestone_id => params[:milestone_id]) do
|
||||
All
|
||||
= check_box_tag "check_all_issues", nil, false, :class => "check_all_issues left"
|
||||
|
||||
.right
|
||||
= form_tag project_issues_path(@project), :method => :get, :class => :right do
|
||||
= select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), :prompt => "Labels")
|
||||
= select_tag(:assignee_id, options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), :prompt => "Assignee")
|
||||
= select_tag(:milestone_id, options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), :prompt => "Milestone")
|
||||
|
||||
.issues_bulk_update.hide
|
||||
= form_tag bulk_update_project_issues_path(@project), :method => :post do
|
||||
%span Update selected issues with
|
||||
|
||||
= select_tag('update[status]', options_for_select(['open', 'closed']), :prompt => "Status")
|
||||
= select_tag('update[assignee_id]', options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), :prompt => "Assignee")
|
||||
= select_tag('update[milestone_id]', options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), :prompt => "Milestone")
|
||||
= hidden_field_tag 'update[issues_ids]', []
|
||||
= hidden_field_tag :f, params[:f]
|
||||
.clearfix
|
||||
= button_tag "Save", :class => "btn update_selected_issues"
|
||||
.issues_filters
|
||||
.left
|
||||
%ul.nav.nav-pills.left
|
||||
%li{:class => ("active" if (params[:f] == issues_filter[:open] || !params[:f]))}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:open], :milestone_id => params[:milestone_id]) do
|
||||
Open
|
||||
%li{:class => ("active" if params[:f] == issues_filter[:closed])}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:closed], :milestone_id => params[:milestone_id]) do
|
||||
Closed
|
||||
%li{:class => ("active" if params[:f] == issues_filter[:to_me])}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:to_me], :milestone_id => params[:milestone_id]) do
|
||||
To Me
|
||||
%li{:class => ("active" if params[:f] == issues_filter[:all])}
|
||||
= link_to project_issues_path(@project, :f => issues_filter[:all], :milestone_id => params[:milestone_id]) do
|
||||
All
|
||||
|
||||
.right
|
||||
= form_tag project_issues_path(@project), :method => :get, :class => :right do
|
||||
= select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), :prompt => "Labels")
|
||||
= select_tag(:assignee_id, options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), :prompt => "Assignee")
|
||||
= select_tag(:milestone_id, options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), :prompt => "Milestone")
|
||||
= hidden_field_tag :f, params[:f]
|
||||
.clearfix
|
||||
|
||||
%ul#issues-table.unstyled.issues_table
|
||||
= render "issues"
|
||||
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
issuesPage();
|
||||
})
|
||||
})
|
||||
|
|
|
@ -194,6 +194,7 @@ Gitlab::Application.routes.draw do
|
|||
resources :issues do
|
||||
collection do
|
||||
post :sort
|
||||
post :bulk_update
|
||||
get :search
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue