Project

General

Profile

Patch #7610 » rollback-2.4.4.patch

Brian Lindahl, 2014-04-17 20:22

View differences:

redmine-2.4.4-rollback/app/controllers/journals_controller.rb 2014-04-17 10:33:21.234634640 -0600
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, :diff]
19
  before_filter :find_journal, :only => [:edit, :diff, :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, :diff]
22
  before_filter :authorize, :only => [:new, :edit, :diff, :rollback]
23 23
  accept_rss_auth :index
24 24
  menu_item :issues
25 25

  
......
94 94
    end
95 95
  end
96 96

  
97
  private
97
  def rollback
98
    (render_403; return false) unless @journal.can_rollback?(User.current)
99

  
100
    if @journal.rollback
101
      flash[:notice] = l(:notice_successful_update)
102
    else
103
      # can't seem to bring in the helper method 'error_messages_for'
104
      # and injecting it into show.rhtml doesn't seem to work, since 
105
      # the @issue loses the errors on redirect (due to issue reload)
106
      flash[:error] = "<ul>" + @journal.errors.full_messages.map {|msg| "<li>" + ERB::Util.html_escape(msg) + "</li>"}.join + "</ul>"
107
    end
108
    respond_to do |format|
109
      format.html { redirect_to :controller => 'issues', :action => 'show', :id => @journal.journalized_id }
110
      format.api  { render_validation_errors(@issue) }
111
    end
112
  end
113

  
114
private
98 115

  
99 116
  def find_journal
100 117
    @journal = Journal.visible.find(params[:id])
redmine-2.4.4-rollback/app/models/issue.rb 2014-04-17 10:33:21.234634640 -0600
672 672
    end
673 673
  end
674 674

  
675
  # rollback the changes in a journal, the journal is destroyed on success
676
  def rollback(journal)
677
    # only allow rollback of journal details for the last journal
678
    (errors.add_to_base(l(:notice_locking_conflict)); return) if !journal.last_valid_journal?(journals)
679

  
680
    # avoid the creation of journals during rollback (see 'attachment removed')
681
    @rolling_back = true
682

  
683
    # roll back each change detailed by the journal
684
    journal.details.each do |d|
685
      case d.property
686
        # issue attribute change
687
        when 'attr'; send "#{d.prop_key}=", d.old_value
688
        # rollback custom field change
689
        when 'cf'; custom_field_values.each {|v| v.value = d.old_value if v.custom_field_id == d.prop_key.to_i}
690
        # remove any added attachments (we can't recover removed attachments)
691
        when 'attachment'; attachments.each {|v| attachments.delete(v) if v.id == d.prop_key.to_i}
692
      end
693
    end
694

  
695
    # allow the creation of journals again
696
    remove_instance_variable(:@rolling_back)
697

  
698
    # destroy the journal once we save the issue changes
699
    if save(:validate => false)
700
      journal.rolled_back = true
701
      journal.save
702
    end
703
  end
704

  
675 705
  # Return true if the issue is closed, otherwise false
676 706
  def closed?
677 707
    self.status.is_closed?
redmine-2.4.4-rollback/app/models/journal.rb 2014-04-17 10:33:21.234634640 -0600
95 95
    usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
96 96
  end
97 97

  
98
  def last_valid_journal?(journals)
99
    self == journals.last
100
  end
101

  
102
  def can_rollback?(usr = nil)
103
    user ||= User.current
104
    editable_by?(user) && (details.empty? || user.allowed_to?(:rollback_issue_notes, project))
105
  end
106
  
107
  def show?
108
    !self.rolled_back || User.current.pref.hide_rolled_back_issue_notes == '0'
109
  end
110

  
111
  # rollback the changes in a journal, the journal is destroyed on success
112
  def rollback
113
    # we could have details to rollback, so let the issue take care of this more complicated task
114
    journalized.rollback(self) || 
115
    # on failure, collect the error messages from the issue on failure
116
      (journalized.errors.each_full {|msg| errors.add_to_base(msg)}; false)
117
  end
118

  
98 119
  def project
99 120
    journalized.respond_to?(:project) ? journalized.project : nil
100 121
  end
redmine-2.4.4-rollback/app/models/user_preference.rb 2014-04-17 10:35:35.682637045 -0600
57 57
  def warn_on_leaving_unsaved; self[:warn_on_leaving_unsaved] || '1'; end
58 58
  def warn_on_leaving_unsaved=(value); self[:warn_on_leaving_unsaved]=value; end
59 59

  
60
  def hide_rolled_back_issue_notes; self[:hide_rolled_back_issue_notes] || '0'; end
61
  def hide_rolled_back_issue_notes=(value); self[:hide_rolled_back_issue_notes]=value; end
62

  
60 63
  def no_self_notified; (self[:no_self_notified] == true || self[:no_self_notified] == '1'); end
61 64
  def no_self_notified=(value); self[:no_self_notified]=value; end
62 65
end
redmine-2.4.4-rollback/app/views/issues/_history.html.erb 2014-04-17 12:04:34.686732511 -0600
1 1
<% reply_links = authorize_for('issues', 'edit') -%>
2 2
<% for journal in journals %>
3
  <% if journal.show? %>
4

  
5
  <% if journal.rolled_back? %>
6
  <strike>
7
  <% end %>
8

  
3 9
  <div id="change-<%= journal.id %>" class="<%= journal.css_classes %>">
4 10
    <div id="note-<%= journal.indice %>">
5 11
    <h4><%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %>
12
    <% if journal.last_valid_journal?(journals) && journal.can_rollback? %>
13
       <div class="journal-link"><%= link_to(image_tag('cancel.png'), {:controller => 'journals', :action => 'rollback', :id => journal}, :confirm => l(:text_are_you_sure), :method => :post, :title => l(:button_rollback)) %>&nbsp;</div>
14
    <% end %>
6 15
    <%= avatar(journal.user, :size => "24") %>
7 16
    <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4>
8 17

  
......
17 26
    </div>
18 27
  </div>
19 28
  <%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %>
29

  
30
  <% if journal.rolled_back? %>
31
  </strike>
32
  <% end %>
33

  
34
<% end %>
20 35
<% end %>
21 36

  
22 37
<% heads_for_wiki_formatter if User.current.allowed_to?(:edit_issue_notes, issue.project) || User.current.allowed_to?(:edit_own_issue_notes, issue.project) %>
redmine-2.4.4-rollback/app/views/users/_preferences.html.erb 2014-04-17 10:33:21.234634640 -0600
3 3
<p><%= pref_fields.time_zone_select :time_zone, nil, :include_blank => true %></p>
4 4
<p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
5 5
<p><%= pref_fields.check_box :warn_on_leaving_unsaved %></p>
6
<p><%= pref_fields.check_box :hide_rolled_back_issue_notes %></p>
6 7
<% end %>
redmine-2.4.4-rollback/config/locales/en.yml 2014-04-17 11:02:25.618665831 -0600
337 337
  field_inherit_members: Inherit members
338 338
  field_generate_password: Generate password
339 339
  field_must_change_passwd: Must change password at next logon
340
  field_hide_rolled_back_issue_notes: "Hide rolled-back issue notes"
340 341

  
341 342
  setting_app_title: Application title
342 343
  setting_app_subtitle: Application subtitle
......
472 473
  permission_export_wiki_pages: Export wiki pages
473 474
  permission_manage_subtasks: Manage subtasks
474 475
  permission_manage_related_issues: Manage related issues
476
  permission_rollback_issue_notes: Rollback issue notes
475 477

  
476 478
  project_module_issue_tracking: Issue tracking
477 479
  project_module_time_tracking: Time tracking
......
956 958
  button_delete_my_account: Delete my account
957 959
  button_close: Close
958 960
  button_reopen: Reopen
961
  button_rollback: Rollback
959 962

  
960 963
  status_active: active
961 964
  status_registered: registered
redmine-2.4.4-rollback/config/routes.rb 2014-04-17 12:07:01.410735133 -0600
51 51

  
52 52
  match '/journals/diff/:id', :to => 'journals#diff', :id => /\d+/, :via => :get
53 53
  match '/journals/edit/:id', :to => 'journals#edit', :id => /\d+/, :via => [:get, :post]
54
  match '/journals/rollback/:id', :to => 'journals#rollback', :id => /\d+/, :via => :post
54 55

  
55 56
  get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
56 57
  get '/issues/gantt', :to => 'gantts#show'
redmine-2.4.4-rollback/db/migrate/20140417000000_add_journals_rolled_back.rb 2014-04-17 10:33:21.238634641 -0600
1
class AddJournalsRolledBack < ActiveRecord::Migration
2
  def up
3
    add_column :journals, :rolled_back, :boolean, :default => 0
4
  end
5

  
6
  def down
7
    remove_column :journals, :rolled_back
8
  end
9
end
redmine-2.4.4-rollback/lib/redmine.rb 2014-04-17 11:07:10.274670924 -0600
118 118
    map.permission :add_issue_notes, {:issues => [:edit, :update, :update_form], :journals => [:new], :attachments => :upload}
119 119
    map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
120 120
    map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
121
    map.permission :rollback_issue_notes, {:journals => [:rollback]}
121 122
    map.permission :view_private_notes, {}, :read => true, :require => :member
122 123
    map.permission :set_notes_private, {}, :require => :member
123 124
    map.permission :move_issues, {:issues => [:bulk_edit, :bulk_update]}, :require => :loggedin
(11-11/15)