Patch #26534 » allow_project_bulk_edit_for_time_entries.patch
| app/controllers/timelog_controller.rb | ||
|---|---|---|
| 164 | 164 |
end |
| 165 | 165 | |
| 166 | 166 |
def bulk_edit |
| 167 |
@available_activities = @projects.map(&:activities).reduce(:&)
|
|
| 167 |
@target_projects = Project.allowed_to(:log_time).to_a
|
|
| 168 | 168 |
@custom_fields = TimeEntry.first.available_custom_fields.select {|field| field.format.bulk_edit_supported}
|
| 169 |
if params[:time_entry] |
|
| 170 |
@target_project = @target_projects.detect {|p| p.id.to_s == params[:time_entry][:project_id].to_s}
|
|
| 171 |
end |
|
| 172 |
if @target_project |
|
| 173 |
@available_activities = @target_project.activities |
|
| 174 |
else |
|
| 175 |
@available_activities = @projects.map(&:activities).reduce(:&) |
|
| 176 |
end |
|
| 177 |
@time_entry_params = params[:time_entry] || {}
|
|
| 178 |
@time_entry_params[:custom_field_values] ||= {}
|
|
| 169 | 179 |
end |
| 170 | 180 | |
| 171 | 181 |
def bulk_update |
| app/helpers/application_helper.rb | ||
|---|---|---|
| 1479 | 1479 |
encoding = l(:general_csv_encoding) |
| 1480 | 1480 |
end |
| 1481 | 1481 | |
| 1482 |
# Returns an array of error messages for bulk edited items (issues, time entries) |
|
| 1483 |
def bulk_edit_error_messages(items) |
|
| 1484 |
messages = {}
|
|
| 1485 |
items.each do |item| |
|
| 1486 |
item.errors.full_messages.each do |message| |
|
| 1487 |
messages[message] ||= [] |
|
| 1488 |
messages[message] << item |
|
| 1489 |
end |
|
| 1490 |
end |
|
| 1491 |
messages.map { |message, items|
|
|
| 1492 |
"#{message}: " + items.map {|i| "##{i.id}"}.join(', ')
|
|
| 1493 |
} |
|
| 1494 |
end |
|
| 1495 | ||
| 1482 | 1496 |
private |
| 1483 | 1497 | |
| 1484 | 1498 |
def wiki_helper |
| app/helpers/issues_helper.rb | ||
|---|---|---|
| 164 | 164 |
end |
| 165 | 165 |
end |
| 166 | 166 | |
| 167 |
# Returns an array of error messages for bulk edited issues |
|
| 168 |
def bulk_edit_error_messages(issues) |
|
| 169 |
messages = {}
|
|
| 170 |
issues.each do |issue| |
|
| 171 |
issue.errors.full_messages.each do |message| |
|
| 172 |
messages[message] ||= [] |
|
| 173 |
messages[message] << issue |
|
| 174 |
end |
|
| 175 |
end |
|
| 176 |
messages.map { |message, issues|
|
|
| 177 |
"#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
|
|
| 178 |
} |
|
| 179 |
end |
|
| 180 | ||
| 181 | 167 |
# Returns a link for adding a new subtask to the given issue |
| 182 | 168 |
def link_to_new_subtask(issue) |
| 183 | 169 |
attrs = {
|
| app/views/timelog/bulk_edit.html.erb | ||
|---|---|---|
| 28 | 28 |
<div class="box tabular"> |
| 29 | 29 |
<div> |
| 30 | 30 |
<p> |
| 31 |
<label><%= l(:field_project) %></label> |
|
| 32 |
<%= select_tag('time_entry[project_id]', project_tree_options_for_select(@target_projects,
|
|
| 33 |
:include_blank => l(:label_no_change_option), :selected => @target_project), |
|
| 34 |
:onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')" ) %>
|
|
| 35 |
</p> |
|
| 36 |
<p> |
|
| 31 | 37 |
<label for="time_entry_issue_id"><%= l(:field_issue) %></label> |
| 32 | 38 |
<%= text_field :time_entry, :issue_id, :size => 6 %> |
| 39 |
<label class="inline"><%= check_box_tag 'time_entry[issue_id]', 'none', (@time_entry_params[:issue_id] == 'none'), :id => nil, :data => {:disables => '#time_entry_issue_id'} %><%= l(:button_clear) %></label>
|
|
| 40 |
<span id="time_entry_issue"></span> |
|
| 33 | 41 |
</p> |
| 34 | 42 | |
| 35 | 43 |
<p> |
| 36 | 44 |
<label for="time_entry_spent_on"><%= l(:field_spent_on) %></label> |
| 37 |
<%= date_field :time_entry, :spent_on, :size => 10 %><%= calendar_for('time_entry_spent_on') %>
|
|
| 45 |
<%= date_field :time_entry, :spent_on, :size => 10, :value => @time_entry_params[:spent_on] %><%= calendar_for('time_entry_spent_on') %>
|
|
| 38 | 46 |
</p> |
| 39 | 47 | |
| 40 | 48 |
<p> |
| 41 | 49 |
<label for="time_entry_hours"><%= l(:field_hours) %></label> |
| 42 |
<%= text_field :time_entry, :hours, :size => 6 %> |
|
| 50 |
<%= text_field :time_entry, :hours, :size => 6, :value => @time_entry_params[:hours] %>
|
|
| 43 | 51 |
</p> |
| 44 | 52 | |
| 45 | 53 |
<% if @available_activities.any? %> |
| 46 | 54 |
<p> |
| 47 | 55 |
<label for="time_entry_activity_id"><%= l(:field_activity) %></label> |
| 48 |
<%= select_tag('time_entry[activity_id]', content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(@available_activities, :id, :name)) %>
|
|
| 56 |
<%= select_tag('time_entry[activity_id]', content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(@available_activities, :id, :name, @time_entry_params[:activity_id])) %>
|
|
| 49 | 57 |
</p> |
| 50 | 58 |
<% end %> |
| 51 | 59 | |
| 52 | 60 |
<p> |
| 53 | 61 |
<label for="time_entry_comments"><%= l(:field_comments) %></label> |
| 54 |
<%= text_field(:time_entry, :comments, :size => 100) %> |
|
| 62 |
<%= text_field(:time_entry, :comments, :size => 100, :value => @time_entry_params[:comments]) %>
|
|
| 55 | 63 |
</p> |
| 56 | 64 | |
| 57 | 65 |
<% @custom_fields.each do |custom_field| %> |
| 58 |
<p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field, @time_entries) %></p>
|
|
| 66 |
<p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field, @time_entries, @time_entry_params[:custom_field_values][custom_field.id.to_s]) %></p>
|
|
| 59 | 67 |
<% end %> |
| 60 | 68 | |
| 61 | 69 |
<%= call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries }) %>
|
| ... | ... | |
| 64 | 72 | |
| 65 | 73 |
<p><%= submit_tag l(:button_submit) %></p> |
| 66 | 74 |
<% end %> |
| 75 | ||
| 76 |
<%= javascript_tag do %> |
|
| 77 |
$(document).ready(function(){
|
|
| 78 |
$('#time_entry_project_id').change(function(){
|
|
| 79 |
<!-- $('#time_entry_issue_id').val(''); -->
|
|
| 80 |
$('#time_entry_issue').text('');
|
|
| 81 |
}); |
|
| 82 |
}); |
|
| 83 | ||
| 84 |
<% if @project || @target_project %> |
|
| 85 |
observeAutocompleteField('time_entry_issue_id',
|
|
| 86 |
function(request, callback) {
|
|
| 87 |
var url = '<%= j auto_complete_issues_path %>'; |
|
| 88 |
var data = {
|
|
| 89 |
term: request.term |
|
| 90 |
}; |
|
| 91 |
var project_id; |
|
| 92 |
<% if @project %> |
|
| 93 |
project_id = '<%= @project.id %>'; |
|
| 94 |
<% end %> |
|
| 95 | ||
| 96 |
current_project_id = $('#time_entry_project_id').val();
|
|
| 97 |
if(current_project_id === ''){
|
|
| 98 |
data['project_id'] = project_id; |
|
| 99 |
} else {
|
|
| 100 |
data['project_id'] = current_project_id; |
|
| 101 |
} |
|
| 102 | ||
| 103 |
$.get(url, data, null, 'json') |
|
| 104 |
.done(function(data){
|
|
| 105 |
callback(data); |
|
| 106 |
}) |
|
| 107 |
.fail(function(jqXHR, status, error){
|
|
| 108 |
callback([]); |
|
| 109 |
}); |
|
| 110 |
}, |
|
| 111 |
{
|
|
| 112 |
select: function(event, ui, data) {
|
|
| 113 |
$('#time_entry_issue').text(ui.item.label);
|
|
| 114 |
} |
|
| 115 |
} |
|
| 116 |
); |
|
| 117 |
<% end %> |
|
| 118 |
<% end %> |
|
| app/views/timelog/bulk_edit.js.erb | ||
|---|---|---|
| 1 |
$('#content').html('<%= escape_javascript(render :template => 'timelog/bulk_edit', :formats => [:html]) %>');
|
|
| config/routes.rb | ||
|---|---|---|
| 158 | 158 |
end |
| 159 | 159 |
end |
| 160 | 160 |
end |
| 161 |
|
|
| 161 | ||
| 162 | 162 |
match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get |
| 163 | 163 |
resources :wiki, :except => [:index, :create], :as => 'wiki_page' do |
| 164 | 164 |
member do |
| ... | ... | |
| 234 | 234 |
match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete |
| 235 | 235 |
# Used to update the new time entry form |
| 236 | 236 |
post '/time_entries/new', :to => 'timelog#new' |
| 237 |
# Used to update the bulk edit time entry form |
|
| 238 |
post '/time_entries/bulk_edit', :to => 'timelog#bulk_edit' |
|
| 237 | 239 | |
| 238 | 240 |
get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity |
| 239 | 241 |
get 'activity', :to => 'activities#index' |
| test/functional/timelog_controller_test.rb | ||
|---|---|---|
| 545 | 545 |
end |
| 546 | 546 | |
| 547 | 547 |
assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do |
| 548 |
assert_select 'select[name=?]', 'time_entry[project_id]' |
|
| 549 | ||
| 550 |
# Clear issue checkbox |
|
| 551 |
assert_select 'input[name=?][value=?]', 'time_entry[issue_id]', 'none' |
|
| 552 | ||
| 548 | 553 |
# System wide custom field |
| 549 | 554 |
assert_select 'select[name=?]', 'time_entry[custom_field_values][10]' |
| 550 | 555 | |
| ... | ... | |
| 563 | 568 |
assert_response :success |
| 564 | 569 |
end |
| 565 | 570 | |
| 571 |
def test_get_bulk_edit_on_different_projects_should_propose_only_common_activites |
|
| 572 |
project = Project.find(3) |
|
| 573 |
TimeEntryActivity.create!(:name => 'QA', :project => project, :parent => TimeEntryActivity.find_by_name('QA'), :active => false)
|
|
| 574 |
@request.session[:user_id] = 1 |
|
| 575 | ||
| 576 |
get :bulk_edit, :params => {:ids => [1, 2, 4]}
|
|
| 577 |
assert_response :success |
|
| 578 |
assert_select 'select[id=?]', 'time_entry_activity_id' do |
|
| 579 |
assert_select 'option', 3 |
|
| 580 |
assert_select 'option[value=?]', '11', 0, :text => 'QA' |
|
| 581 |
end |
|
| 582 |
end |
|
| 583 | ||
| 584 |
def test_get_bulk_edit_on_same_project_should_propose_project_activities |
|
| 585 |
project = Project.find(1) |
|
| 586 |
override_activity = TimeEntryActivity.create!({:name => "QA override", :parent => TimeEntryActivity.find_by_name("QA"), :project => project})
|
|
| 587 | ||
| 588 |
@request.session[:user_id] = 1 |
|
| 589 | ||
| 590 |
get :bulk_edit, :params => {:ids => [1, 2]}
|
|
| 591 |
assert_response :success |
|
| 592 | ||
| 593 |
assert_select 'select[id=?]', 'time_entry_activity_id' do |
|
| 594 |
assert_select 'option', 4 |
|
| 595 |
assert_select 'option[value=?]', override_activity.id.to_s, :text => 'QA override' |
|
| 596 |
end |
|
| 597 |
end |
|
| 598 | ||
| 566 | 599 |
def test_bulk_edit_with_edit_own_time_entries_permission |
| 567 | 600 |
@request.session[:user_id] = 2 |
| 568 | 601 |
Role.find_by_name('Manager').remove_permission! :edit_time_entries
|
| test/integration/routing/timelog_test.rb | ||
|---|---|---|
| 55 | 55 | |
| 56 | 56 |
def test_timelogs_bulk_edit |
| 57 | 57 |
should_route 'GET /time_entries/bulk_edit' => 'timelog#bulk_edit' |
| 58 |
should_route 'POST /time_entries/bulk_edit' => 'timelog#bulk_edit' |
|
| 58 | 59 |
should_route 'POST /time_entries/bulk_update' => 'timelog#bulk_update' |
| 59 | 60 |
should_route 'DELETE /time_entries/destroy' => 'timelog#destroy' |
| 60 | 61 |
end |