parent
28519714e0
commit
bc6805a6a6
@ -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 |
||||
attr_accessor :indice |
||||
serialize :details |
||||
|
||||
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}"}} |
||||
attr_accessor :indice |
||||
|
||||
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 <> '')"} |
||||
before_save :check_for_empty_journal |
||||
|
||||
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 |
||||
|
@ -0,0 +1,2 @@ |
||||
require File.dirname(__FILE__) + '/lib/acts_as_journalized' |
||||
ActiveRecord::Base.send(:include, Redmine::Acts::Journalized) |
@ -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 |
Loading…
Reference in new issue