##// END OF EJS Templates
Ability to delete multiple attachments while updating an issue (#13072)....
Jean-Philippe Lang -
r15268:19492226f7b2
parent child
Show More
@@ -59,6 +59,7 class Issue < ActiveRecord::Base
59
59
60 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
60 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
61
61
62 attr_accessor :deleted_attachment_ids
62 attr_reader :current_journal
63 attr_reader :current_journal
63 delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true
64 delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true
64
65
@@ -109,7 +110,7 class Issue < ActiveRecord::Base
109 :force_updated_on_change, :update_closed_on, :set_assigned_to_was
110 :force_updated_on_change, :update_closed_on, :set_assigned_to_was
110 after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?}
111 after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?}
111 after_save :reschedule_following_issues, :update_nested_set_attributes,
112 after_save :reschedule_following_issues, :update_nested_set_attributes,
112 :update_parent_attributes, :create_journal
113 :update_parent_attributes, :delete_selected_attachments, :create_journal
113 # Should be after_create but would be called before previous after_save callbacks
114 # Should be after_create but would be called before previous after_save callbacks
114 after_save :after_create_from_copy
115 after_save :after_create_from_copy
115 after_destroy :update_parent_attributes
116 after_destroy :update_parent_attributes
@@ -403,6 +404,10 class Issue < ActiveRecord::Base
403 write_attribute(:description, arg)
404 write_attribute(:description, arg)
404 end
405 end
405
406
407 def deleted_attachment_ids
408 Array(@deleted_attachment_ids).map(&:to_i)
409 end
410
406 # Overrides assign_attributes so that project and tracker get assigned first
411 # Overrides assign_attributes so that project and tracker get assigned first
407 def assign_attributes_with_project_and_tracker_first(new_attributes, *args)
412 def assign_attributes_with_project_and_tracker_first(new_attributes, *args)
408 return if new_attributes.nil?
413 return if new_attributes.nil?
@@ -465,6 +470,9 class Issue < ActiveRecord::Base
465 :if => lambda {|issue, user| (issue.new_record? || issue.attributes_editable?(user)) &&
470 :if => lambda {|issue, user| (issue.new_record? || issue.attributes_editable?(user)) &&
466 user.allowed_to?(:manage_subtasks, issue.project)}
471 user.allowed_to?(:manage_subtasks, issue.project)}
467
472
473 safe_attributes 'deleted_attachment_ids',
474 :if => lambda {|issue, user| issue.attachments_deletable?(user)}
475
468 def safe_attribute_names(user=nil)
476 def safe_attribute_names(user=nil)
469 names = super
477 names = super
470 names -= disabled_core_fields
478 names -= disabled_core_fields
@@ -1586,6 +1594,13 class Issue < ActiveRecord::Base
1586 end
1594 end
1587 end
1595 end
1588
1596
1597 def delete_selected_attachments
1598 if deleted_attachment_ids.present?
1599 objects = attachments.where(:id => deleted_attachment_ids.map(&:to_i))
1600 attachments.delete(objects)
1601 end
1602 end
1603
1589 # Callback on file attachment
1604 # Callback on file attachment
1590 def attachment_added(attachment)
1605 def attachment_added(attachment)
1591 if current_journal && !attachment.new_record?
1606 if current_journal && !attachment.new_record?
@@ -40,7 +40,27
40 </fieldset>
40 </fieldset>
41
41
42 <fieldset><legend><%= l(:label_attachment_plural) %></legend>
42 <fieldset><legend><%= l(:label_attachment_plural) %></legend>
43 <p><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
43 <% if @issue.attachments.any? && @issue.safe_attribute?('deleted_attachment_ids') %>
44 <div class="contextual"><%= link_to l(:label_edit_attachments), '#', :onclick => "$('#existing-attachments').toggle(); return false;" %></div>
45 <div id="existing-attachments" style="<%= @issue.deleted_attachment_ids.blank? ? 'display:none;' : '' %>">
46 <% @issue.attachments.each do |attachment| %>
47 <span class="existing-attachment">
48 <%= text_field_tag '', attachment.filename, :class => "filename", :disabled => true %>
49 <label>
50 <%= check_box_tag 'issue[deleted_attachment_ids][]',
51 attachment.id,
52 @issue.deleted_attachment_ids.include?(attachment.id),
53 :id => nil, :class => "deleted_attachment" %> <%= l(:button_delete) %>
54 </label>
55 </span>
56 <% end %>
57 <hr />
58 </div>
59 <% end %>
60
61 <div id="new-attachments" style="display:inline-block;">
62 <%= render :partial => 'attachments/form', :locals => {:container => @issue} %>
63 </div>
44 </fieldset>
64 </fieldset>
45 <% end %>
65 <% end %>
46 </div>
66 </div>
@@ -189,3 +189,8 function setupFileDrop() {
189 }
189 }
190
190
191 $(document).ready(setupFileDrop);
191 $(document).ready(setupFileDrop);
192 $(document).ready(function(){
193 $("input.deleted_attachment").change(function(){
194 $(this).parents('.existing-attachment').toggleClass('deleted', $(this).is(":checked"));
195 }).change();
196 });
@@ -363,7 +363,7 div.square {
363 overflow: hidden;
363 overflow: hidden;
364 width: .6em; height: .6em;
364 width: .6em; height: .6em;
365 }
365 }
366 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
366 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin:5px 0px; padding-left: 10px; font-size:0.9em;}
367 .contextual input, .contextual select {font-size:0.9em;}
367 .contextual input, .contextual select {font-size:0.9em;}
368 .message .contextual { margin-top: 0; }
368 .message .contextual { margin-top: 0; }
369
369
@@ -673,14 +673,16 span.required {color: #bb0000;}
673 .check_box_group.bool_cf {border:0; background:inherit;}
673 .check_box_group.bool_cf {border:0; background:inherit;}
674 .check_box_group.bool_cf label {display: inline;}
674 .check_box_group.bool_cf label {display: inline;}
675
675
676 #attachments_fields input.description {margin-left:4px; width:340px;}
676 #attachments_fields input.description, #existing-attachments input.description {margin-left:4px; width:340px;}
677 #attachments_fields span {display:block; white-space:nowrap;}
677 #attachments_fields>span, #existing-attachments>span {display:block; white-space:nowrap;}
678 #attachments_fields input.filename {border:0; height:1.8em; width:250px; color:#555; background-color:inherit; background:url(../images/attachment.png) no-repeat 1px 50%; padding-left:18px;}
678 #attachments_fields input.filename, #existing-attachments .filename {border:0; width:250px; color:#555; background-color:inherit; background:url(../images/attachment.png) no-repeat 1px 50%; padding-left:18px;}
679 #attachments_fields input.filename {height:1.8em;}
679 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
680 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
680 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
681 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
681 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
682 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
682 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
683 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
683 a.remove-upload:hover {text-decoration:none !important;}
684 a.remove-upload:hover {text-decoration:none !important;}
685 .existing-attachment.deleted .filename {text-decoration:line-through; color:#999 !important;}
684
686
685 div.fileover { background-color: lavender; }
687 div.fileover { background-color: lavender; }
686
688
@@ -1137,8 +1139,6 a.close-icon:hover {background-image:url('../images/close_hl.png');}
1137 background-position: 0% 50%;
1139 background-position: 0% 50%;
1138 background-repeat: no-repeat;
1140 background-repeat: no-repeat;
1139 padding-left: 16px;
1141 padding-left: 16px;
1140 }
1141 a.icon-only {
1142 display: inline-block;
1142 display: inline-block;
1143 width: 0;
1143 width: 0;
1144 height: 16px;
1144 height: 16px;
@@ -1148,7 +1148,7 a.icon-only {
1148 font-size: 8px;
1148 font-size: 8px;
1149 vertical-align: text-bottom;
1149 vertical-align: text-bottom;
1150 }
1150 }
1151 a.icon-only::after {
1151 .icon-only::after {
1152 content: "&nbsp;";
1152 content: "&nbsp;";
1153 }
1153 }
1154
1154
@@ -3761,6 +3761,44 class IssuesControllerTest < ActionController::TestCase
3761 end
3761 end
3762 end
3762 end
3763
3763
3764 def test_put_update_with_attachment_deletion_should_create_a_single_journal
3765 set_tmp_attachments_directory
3766 @request.session[:user_id] = 2
3767
3768 journal = new_record(Journal) do
3769 assert_difference 'Attachment.count', -2 do
3770 put :update,
3771 :id => 3,
3772 :issue => {
3773 :notes => 'Removing attachments',
3774 :deleted_attachment_ids => ['1', '5']
3775 }
3776 end
3777 end
3778 assert_equal 'Removing attachments', journal.notes
3779 assert_equal 2, journal.details.count
3780 end
3781
3782 def test_put_update_with_attachment_deletion_and_failure_should_preserve_selected_attachments
3783 set_tmp_attachments_directory
3784 @request.session[:user_id] = 2
3785
3786 assert_no_difference 'Journal.count' do
3787 assert_no_difference 'Attachment.count' do
3788 put :update,
3789 :id => 3,
3790 :issue => {
3791 :subject => '',
3792 :notes => 'Removing attachments',
3793 :deleted_attachment_ids => ['1', '5']
3794 }
3795 end
3796 end
3797 assert_select 'input[name=?][value="1"][checked=checked]', 'issue[deleted_attachment_ids][]'
3798 assert_select 'input[name=?][value="5"][checked=checked]', 'issue[deleted_attachment_ids][]'
3799 assert_select 'input[name=?][value="6"]:not([checked])', 'issue[deleted_attachment_ids][]'
3800 end
3801
3764 def test_put_update_with_no_change
3802 def test_put_update_with_no_change
3765 issue = Issue.find(1)
3803 issue = Issue.find(1)
3766 issue.journals.clear
3804 issue.journals.clear
General Comments 0
You need to be logged in to leave comments. Login now