diff --git a/app/models/journal.rb b/app/models/journal.rb index a0e1ae87..d25cc94c 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -1,68 +1,15 @@ -# redMine - project management software -# Copyright (C) 2006 Jean-Philippe Lang -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - class Journal < ActiveRecord::Base - belongs_to :journalized, :polymorphic => true - # added as a quick fix to allow eager loading of the polymorphic association - # since always associated to an issue, for now - belongs_to :issue, :foreign_key => :journalized_id + self.abstract_class = true belongs_to :user - has_many :details, :class_name => "JournalDetail", :dependent => :delete_all + serialize :details + attr_accessor :indice - acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" }, - :description => :notes, - :author => :user, - :type => Proc.new {|o| (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' }, - :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}} + before_save :check_for_empty_journal - acts_as_activity_provider :type => 'issues', - :permission => :view_issues, - :author_key => :user_id, - :find_options => {:include => [{:issue => :project}, :details, :user], - :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" + - " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"} - - def save(*args) + def check_for_empty_journal # Do not save an empty journal - (details.empty? && notes.blank?) ? false : super - end - - # Returns the new status if the journal contains a status change, otherwise nil - def new_status - c = details.detect {|detail| detail.prop_key == 'status_id'} - (c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil - end - - def new_value_for(prop) - c = details.detect {|detail| detail.prop_key == prop} - c ? c.value : nil - end - - def editable_by?(usr) - usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project))) - end - - def project - journalized.respond_to?(:project) ? journalized.project : nil - end - - def attachments - journalized.respond_to?(:attachments) ? journalized.attachments : nil + !(details.empty? && notes.blank?) end end diff --git a/vendor/plugins/acts_as_journalized/init.rb b/vendor/plugins/acts_as_journalized/init.rb new file mode 100644 index 00000000..ade67d28 --- /dev/null +++ b/vendor/plugins/acts_as_journalized/init.rb @@ -0,0 +1,2 @@ +require File.dirname(__FILE__) + '/lib/acts_as_journalized' +ActiveRecord::Base.send(:include, Redmine::Acts::Journalized) diff --git a/vendor/plugins/acts_as_journalized/lib/acts_as_journalized.rb b/vendor/plugins/acts_as_journalized/lib/acts_as_journalized.rb new file mode 100644 index 00000000..4bcdf1ba --- /dev/null +++ b/vendor/plugins/acts_as_journalized/lib/acts_as_journalized.rb @@ -0,0 +1,139 @@ +module Redmine + module Acts + module Journalized + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def acts_as_journalized(options = {}) + return if self.included_modules.include?(Redmine::Acts::Journalized::InstanceMethods) + + self.include Redmine::Acts::Journalized::InstanceMethods + + plural_name = self.name.underscore.pluralize + journal_name = "#{self.name}Journal" + + extra_module = options.delete(:extra_module) + + event_hash = { + :description => :notes, + :author => Proc.new {|o| User.find_by_id(o.journal.user_id)}, + :url => Proc.new do |o| + { + :controller => self.name.underscore.pluralize, + :action => 'show', + :id => o.id, + :anchor => "change-#{o.id}" + } + end + } + + activity_hash = { + :type => plural_name, + :permission => "view_#{plural_name}".to_sym, + :author_key => :user_id, + } + + options.each_pair do |k, v| + case + when key = k.to_s.slice(/event_(.+)/, 1) + event_hash[key.to_sym] = v + when key = k.to_s.slice(/activity_(.+)/, 1) + activity_hash[key.to_sym] = v + end + end + + # create the new model class + journal = Class.new(Journal) + journal.belongs_to self.name.underscore + journal.acts_as_event event_hash + journal.acts_as_activity_provider activity_hash + journal.send(:include, extra_module) + Object.const_set("#{self.name}Journal", journal) + + unless Redmine::Activity.providers[plural_name].include? self.name + Redmine::Activity.register plural_name.to_sym + end + end + end + + module InstanceMethods + def self.included(base) + base.extend ClassMethods + + base.class_eval do + after_save :create_journal + has_many :journals, :class_name => "#{self.name}Journal", :dependent => :destroy + end + end + + def journal_class + "#{self.class.name}Journal".constantize + end + + def init_journal(user, notes = "") + @notes ||= "" + @current_journal ||= journal_class.new(:journalized => self, :user => user, :notes => notes) + @object_before_change = self.clone + @object_before_change.status = self.status + if self.respond_to? :custom_values + @custom_values_before_change = {} + self.custom_values.each {|c| @custom_values_before_change[c.custom_field_id] = c.value } + end + # Make sure updated_on is updated when adding a note. + updated_on_will_change! + @current_journal + end + + # Saves the changes in a Journal + # Called after_save + def create_journal + if @current_journal + details = {:attr => {}} + if self.respond_to? :custom_values + details[:cf] = {} + end + + # attributes changes + self.class.journalized_columns.each do |c| + unless send(c) == @object_before_change.send(c) + details[:attr][c] = { + :old => @object_before_change.send(c), + :new => send(c) + } + end + end + + if self.respond_to? :custom_values + # custom fields changes + custom_values.each do |c| + unless ( @custom_values_before_change[c.custom_field_id]==c.value || + (@custom_values_before_change[c.custom_field_id].blank? && c.value.blank?)) + details[:cf][c.custom_field_id] = { + :old => @custom_values_before_change[c.custom_field_id], + :new => c.value + } + end + end + end + @current_journal.details = details + @current_journal.save + end + end + + module ClassMethods + def journalized_columns=(columns = []) + @journalized_columns = columns + end + + def journalized_columns + @journalized_columns ||= begin + (self.column_names - %w(id description lock_version created_on updated_on)) + end + end + end + end + end + end +end