Patch #7610 » rollback-1.1.0.patch
redmine-1.1.0-rollback/app/controllers/journals_controller.rb 2011-02-11 17:15:13.829487200 -0700 | ||
---|---|---|
16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | 17 |
|
18 | 18 |
class JournalsController < ApplicationController |
19 |
before_filter :find_journal, :only => [:edit] |
|
19 |
before_filter :find_journal, :only => [:edit, :rollback]
|
|
20 | 20 |
before_filter :find_issue, :only => [:new] |
21 | 21 |
before_filter :find_optional_project, :only => [:index] |
22 |
before_filter :authorize, :only => [:new, :edit] |
|
22 |
before_filter :authorize, :only => [:new, :edit, :rollback]
|
|
23 | 23 |
accept_key_auth :index |
24 | 24 |
|
25 | 25 |
helper :issues |
... | ... | |
67 | 67 |
end |
68 | 68 |
|
69 | 69 |
def edit |
70 |
(render_403; return false) unless @journal.editable_by?(User.current) |
|
71 |
|
|
70 | 72 |
if request.post? |
71 | 73 |
@journal.update_attributes(:notes => params[:notes]) if params[:notes] |
72 | 74 |
@journal.destroy if @journal.details.empty? && @journal.notes.blank? |
... | ... | |
77 | 79 |
end |
78 | 80 |
end |
79 | 81 |
end |
82 |
|
|
83 |
def rollback |
|
84 |
(render_403; return false) unless @journal.can_rollback?(User.current) |
|
85 |
|
|
86 |
if @journal.rollback |
|
87 |
flash[:notice] = l(:notice_successful_update) |
|
88 |
else |
|
89 |
# can't seem to bring in the helper method 'error_messages_for' |
|
90 |
# and injecting it into show.rhtml doesn't seem to work, since |
|
91 |
# the @issue loses the errors on redirect (due to issue reload) |
|
92 |
flash[:error] = "<ul>" + @journal.errors.full_messages.map {|msg| "<li>" + ERB::Util.html_escape(msg) + "</li>"}.join + "</ul>" |
|
93 |
end |
|
94 |
respond_to do |format| |
|
95 |
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @journal.journalized_id } |
|
96 |
format.api { render_validation_errors(@issue) } |
|
97 |
end |
|
98 |
end |
|
80 | 99 |
|
81 | 100 |
private |
82 | 101 |
def find_journal |
83 | 102 |
@journal = Journal.find(params[:id]) |
84 |
(render_403; return false) unless @journal.editable_by?(User.current) |
|
85 | 103 |
@project = @journal.journalized.project |
86 | 104 |
rescue ActiveRecord::RecordNotFound |
87 | 105 |
render_404 |
redmine-1.1.0-rollback/app/models/issue.rb 2011-02-11 17:17:51.065874800 -0700 | ||
---|---|---|
182 | 182 |
end |
183 | 183 |
issue |
184 | 184 |
end |
185 |
|
|
186 |
# parent_id is never set directly, see :after_save |
|
187 |
def parent_id=(id) |
|
188 |
@parent_issue = Issue.find_by_id(id) |
|
189 |
end |
|
185 | 190 |
|
186 | 191 |
def status_id=(sid) |
187 | 192 |
self.status = nil |
... | ... | |
358 | 363 |
@current_journal |
359 | 364 |
end |
360 | 365 |
|
366 |
# rollback the changes in a journal, the journal is destroyed on success |
|
367 |
def rollback(journal) |
|
368 |
# only allow rollback of journal details for the last journal |
|
369 |
(errors.add_to_base(l(:notice_locking_conflict)); return) if journal != journals.last |
|
370 |
|
|
371 |
# avoid the creation of journals during rollback (see 'attachment removed') |
|
372 |
@rolling_back = true |
|
373 |
|
|
374 |
# roll back each change detailed by the journal |
|
375 |
journal.details.each do |d| |
|
376 |
case d.property |
|
377 |
# issue attribute change |
|
378 |
when 'attr'; send("#{d.prop_key}=", d.old_value) |
|
379 |
# rollback custom field change |
|
380 |
when 'cf'; custom_field_values.each {|v| v.value = d.old_value if v.custom_field_id == d.prop_key.to_i} |
|
381 |
# remove any added attachments (we can't recover removed attachments) |
|
382 |
when 'attachment'; attachments.each {|v| attachments.delete(v) if v.id == d.prop_key.to_i} |
|
383 |
end |
|
384 |
end |
|
385 |
|
|
386 |
# allow the creation of journals again |
|
387 |
remove_instance_variable(:@rolling_back) |
|
388 |
|
|
389 |
# destroy the journal once we save the issue changes |
|
390 |
journal.destroy if save(false) |
|
391 |
end |
|
392 |
|
|
361 | 393 |
# Return true if the issue is closed, otherwise false |
362 | 394 |
def closed? |
363 | 395 |
self.status.is_closed? |
... | ... | |
785 | 817 |
|
786 | 818 |
# Callback on attachment deletion |
787 | 819 |
def attachment_removed(obj) |
820 |
return if @rolling_back |
|
788 | 821 |
journal = init_journal(User.current) |
789 | 822 |
journal.details << JournalDetail.new(:property => 'attachment', |
790 | 823 |
:prop_key => obj.id, |
redmine-1.1.0-rollback/app/models/journal.rb 2011-02-11 17:10:38.544713600 -0700 | ||
---|---|---|
58 | 58 |
usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project))) |
59 | 59 |
end |
60 | 60 |
|
61 |
def can_rollback?(usr = nil) |
|
62 |
user = usr || User.current |
|
63 |
editable_by?(user) && (details.empty? || user.allowed_to?(:rollback_issue_notes, project)) |
|
64 |
end |
|
65 |
|
|
66 |
# rollback the changes in a journal, the journal is destroyed on success |
|
67 |
def rollback |
|
68 |
# we could have details to rollback, so let the issue |
|
69 |
# take care of this more complicated task |
|
70 |
journalized.rollback(self) || |
|
71 |
# on failure, collect the error messages from the issue on failure |
|
72 |
(journalized.errors.each_full {|msg| errors.add_to_base(msg)}; false) |
|
73 |
end |
|
74 |
|
|
61 | 75 |
def project |
62 | 76 |
journalized.respond_to?(:project) ? journalized.project : nil |
63 | 77 |
end |
redmine-1.1.0-rollback/app/views/issues/_history.rhtml 2011-02-11 17:10:38.560338800 -0700 | ||
---|---|---|
2 | 2 |
<% for journal in journals %> |
3 | 3 |
<div id="change-<%= journal.id %>" class="<%= journal.css_classes %>"> |
4 | 4 |
<h4><div class="journal-link"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div> |
5 |
<% if journal == journals.last && journal.can_rollback? %> |
|
6 |
<div class="journal-rollback"><%= link_to(image_tag('cancel.png'), {:controller => 'journals', :action => 'rollback', :id => journal}, :confirm => l(:text_are_you_sure), :method => :post, :title => l(:label_rollback)) %> </div> |
|
7 |
<% end %> |
|
5 | 8 |
<%= avatar(journal.user, :size => "24") %> |
6 | 9 |
<%= content_tag('a', '', :name => "note-#{journal.indice}")%> |
7 | 10 |
<%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4> |
redmine-1.1.0-rollback/config/locales/en.yml 2011-02-11 17:10:38.560338800 -0700 | ||
---|---|---|
378 | 378 |
permission_add_issue_notes: Add notes |
379 | 379 |
permission_edit_issue_notes: Edit notes |
380 | 380 |
permission_edit_own_issue_notes: Edit own notes |
381 |
permission_rollback_issue_notes: Rollback issue notes |
|
381 | 382 |
permission_move_issues: Move issues |
382 | 383 |
permission_delete_issues: Delete issues |
383 | 384 |
permission_manage_public_queries: Manage public queries |
... | ... | |
793 | 794 |
label_project_copy_notifications: Send email notifications during the project copy |
794 | 795 |
label_principal_search: "Search for user or group:" |
795 | 796 |
label_user_search: "Search for user:" |
797 |
label_rollback: Rollback |
|
796 | 798 |
|
797 | 799 |
button_login: Login |
798 | 800 |
button_submit: Submit |
redmine-1.1.0-rollback/lib/redmine.rb 2011-02-11 17:10:38.575964000 -0700 | ||
---|---|---|
72 | 72 |
map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new]} |
73 | 73 |
map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin |
74 | 74 |
map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin |
75 |
map.permission :rollback_issue_notes, {:journals => [:rollback]} |
|
75 | 76 |
map.permission :move_issues, {:issue_moves => [:new, :create]}, :require => :loggedin |
76 | 77 |
map.permission :delete_issues, {:issues => :destroy}, :require => :member |
77 | 78 |
# Queries |
redmine-1.1.0-rollback/public/stylesheets/application.css 2011-02-11 15:59:42.537112400 -0700 | ||
---|---|---|
938 | 938 |
float: right; |
939 | 939 |
} |
940 | 940 |
|
941 |
.journal-rollback { |
|
942 |
float: right; |
|
943 |
} |
|
944 |
|
|
941 | 945 |
h2 img { vertical-align:middle; } |
942 | 946 |
|
943 | 947 |
.hascontextmenu { cursor: context-menu; } |