diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index 8e7bcab40..4f37f0b09 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -46,7 +46,7 @@ class JournalsController < ApplicationController end def diff - @issue = @journal.issue + @issue = @journal.journalized if params[:detail_id].present? @detail = @journal.details.find_by_id(params[:detail_id]) else @@ -101,7 +101,7 @@ class JournalsController < ApplicationController def find_journal @journal = Journal.visible.find(params[:id]) - @project = @journal.journalized.project + @project = @journal.project rescue ActiveRecord::RecordNotFound render_404 end diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb index 4092b7b0f..206df1d0c 100644 --- a/app/helpers/journals_helper.rb +++ b/app/helpers/journals_helper.rb @@ -30,7 +30,7 @@ module JournalsHelper def render_journal_actions(issue, journal, options={}) links = [] dropbown_links = [] - indice = journal.indice || journal.issue.visible_journals_with_index.find{|j| j.id == journal.id}&.indice + indice = journal.indice || journal.journalized.visible_journals_with_index.find{|j| j.id == journal.id}&.indice dropbown_links << copy_object_url_link(issue_url(issue, anchor: "note-#{indice}", only_path: false)) if journal.attachments.size > 1 diff --git a/app/models/issue.rb b/app/models/issue.rb index fa2d7707d..416776e4a 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -38,7 +38,6 @@ class Issue < ApplicationRecord belongs_to :priority, :class_name => 'IssuePriority' belongs_to :category, :class_name => 'IssueCategory' - has_many :journals, :as => :journalized, :dependent => :destroy, :inverse_of => :journalized has_many :time_entries, :dependent => :destroy has_and_belongs_to_many :changesets, lambda {order("#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC")} @@ -48,6 +47,7 @@ class Issue < ApplicationRecord acts_as_attachable :after_add => :attachment_added, :after_remove => :attachment_removed acts_as_customizable acts_as_watchable + acts_as_journalized :activity_type => 'issues' acts_as_searchable :columns => ['subject', "#{table_name}.description"], :preload => [:project, :status, :tracker], :scope => lambda {|options| options[:open_issues] ? self.open : self.all} @@ -1304,9 +1304,8 @@ class Issue < ApplicationRecord def self.load_visible_last_updated_by(issues, user=User.current) if issues.any? issue_ids = issues.map(&:id) - journal_ids = Journal.joins(issue: :project). - where(:journalized_type => 'Issue', :journalized_id => issue_ids). - where(Journal.visible_notes_condition(user, :skip_pre_condition => true)). + journals = journal_visibility_scope(Journal.where(journalized_type: 'Issue', journalized_id: issue_ids)) + journal_ids = journal_notes_visibility_scope(journals, user, skip_pre_condition: true). group(:journalized_id). maximum(:id). values @@ -1323,9 +1322,8 @@ class Issue < ApplicationRecord def self.load_visible_last_notes(issues, user=User.current) if issues.any? issue_ids = issues.map(&:id) - journal_ids = Journal.joins(issue: :project). - where(:journalized_type => 'Issue', :journalized_id => issue_ids). - where(Journal.visible_notes_condition(user, :skip_pre_condition => true)). + journals = journal_visibility_scope(Journal.where(journalized_type: 'Issue', journalized_id: issue_ids)) + journal_ids = journal_notes_visibility_scope(journals, user, skip_pre_condition: true). where.not(notes: ''). group(:journalized_id). maximum(:id). @@ -1721,6 +1719,107 @@ class Issue < ApplicationRecord end end + def self.journal_activity_preload + [{ journalized: :project }, { journalized: :tracker }, :user] + end + + def self.journal_activity_scope(scope) + scope.joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id"). + where("#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> ''").distinct + end + + def journal_event_title(journal) + status = (s = journal_new_status(journal)) ? " (#{s})" : nil + "#{tracker} ##{id}#{status}: #{subject}" + end + + def journal_event_type(journal) + status = journal_new_status(journal) + if status + status.is_closed? ? 'issue-closed' : 'issue-edit' + else + 'issue-note' + end + end + + def journal_event_url(journal) + {controller: 'issues', action: 'show', id: id, anchor: "change-#{journal.id}"} + end + + def self.journal_visibility_scope(scope) + scope.joins("INNER JOIN #{Issue.table_name} ON #{Issue.table_name}.id = #{Journal.table_name}.journalized_id"). + joins("INNER JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Issue.table_name}.project_id") + end + + def self.journal_notes_visibility_scope(scope, user, options = {}) + scope.where(Journal.visible_notes_condition(user, options)) + end + + def journal_editable_by?(journal, usr) + usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (journal.user == usr && usr.allowed_to?(:edit_own_issue_notes, project))) + end + + def journal_relation_visible?(detail, user) + Issue.find_by(id: detail.value || detail.old_value)&.visible?(user) + end + + def journal_notify(journal) + return unless journal.notify? + + if Setting.notified_events.include?('issue_updated') || + (Setting.notified_events.include?('issue_note_added') && journal.notes.present?) || + (Setting.notified_events.include?('issue_status_updated') && journal_new_status(journal).present?) || + (Setting.notified_events.include?('issue_assigned_to_updated') && journal.detail_for_attribute('assigned_to_id').present?) || + (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?) || + (Setting.notified_events.include?('issue_fixed_version_updated') && journal.detail_for_attribute('fixed_version_id').present?) || + (Setting.notified_events.include?('issue_attachment_added') && + journal.details.any? {|d| d.property == 'attachment' && d.value }) + + Mailer.deliver_issue_edit(journal) + end + end + + def journal_process_watchers(journal) + usr = journal.user + if usr.is_a?(User) && + usr.pref.auto_watch_on?('issue_contributed_to') && + journal.valid_watcher?(usr) + set_watcher(usr, true) + end + + assignee = assigned_to + if assignee.is_a?(User) && + assignee.pref.auto_watch_on?('issue_assigned_to_me') && + journal.valid_watcher?(assignee) + set_watcher(assignee, true) + end + end + + def self.journal_visible_scope(user = User.current, options = {}) + joins(:project).where(visible_condition(user, options)) + end + + def journal_user_can_view_private_notes?(user) + user.allowed_to?(:view_private_notes, project) + end + + def journal_private_notes_allowed?(user) + user.allowed_to?(:set_notes_private, project) + end + + def journal_valid_watcher?(user) + user.active? && + user.allowed_to?(:add_issue_watchers, project) && + valid_watcher?(user) && + !watched_by?(user) + end + + # Returns the new status if the journal contains a status change, otherwise nil + def journal_new_status(journal) + s = journal.new_value_for('status_id') + s ? IssueStatus.find_by_id(s.to_i) : nil + end + private def user_tracker_permission?(user, permission) diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index 3431750f6..1a5b47f4b 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -477,13 +477,13 @@ class IssueQuery < Query # Returns the journals # Valid options are :order, :offset, :limit def journals(options={}) - Journal.visible. - joins(:issue => [:project, :status]). + Issue.journal_visibility_scope( + Journal.visible.where(journalized_type: 'Issue')). where(statement). order(options[:order]). limit(options[:limit]). offset(options[:offset]). - preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}). + preload(:details, :user, {:journalized => [:project, :author, :tracker, :status]}). to_a rescue ::ActiveRecord::StatementInvalid => e raise StatementInvalid.new(e.message) diff --git a/app/models/journal.rb b/app/models/journal.rb index 1874feb0c..55ab62069 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -21,44 +21,43 @@ class Journal < ApplicationRecord include Redmine::SafeAttributes include Redmine::Reaction::Reactable - 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 - + belongs_to :journalized, :polymorphic => true, :inverse_of => :journals belongs_to :user belongs_to :updated_by, :class_name => 'User' + has_many :details, :class_name => "JournalDetail", :dependent => :delete_all, :inverse_of => :journal attr_accessor :indice + cattr_accessor :journalized_types + + def self.register_journalized_type(klass) + self.journalized_types ||= [] + self.journalized_types << klass unless journalized_types.include?(klass) + end + + def self.activity_scope_for(klass) + scope = where(journalized_type: klass.name) + scope = scope.preload(*klass.journal_activity_preload) + klass.journal_activity_scope(scope) + end + + def self.register_activity_provider(klass) + return if klass.journal_activity_type.blank? + + acts_as_activity_provider( + type: klass.journal_activity_type, + author_key: :user_id, + scope: proc { activity_scope_for(klass) } + ) + end + acts_as_event( - :title => - Proc.new do |o| - status = ((s = o.new_status) ? " (#{s})" : nil) - "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" - end, - :description => :notes, - :author => :user, - :group => :issue, - :type => - Proc.new do |o| - (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' - end, - :url => - Proc.new do |o| - {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"} - end - ) - acts_as_activity_provider( - :type => 'issues', - :author_key => :user_id, - :scope => - proc do - preload({:issue => :project}, {:issue => :tracker}, :user). - joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id"). - where("#{Journal.table_name}.journalized_type = 'Issue' AND" + - " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").distinct - end + title: proc {|o| o.journalized.journal_event_title(o) }, + description: :notes, + author: :user, + group: :journalized, + type: proc {|o| o.journalized.journal_event_type(o) }, + url: proc {|o| o.journalized.journal_event_url(o) } ) acts_as_mentionable :attributes => ['notes'] before_create :split_private_notes @@ -68,10 +67,16 @@ class Journal < ApplicationRecord scope :visible, (lambda do |*args| user = args.shift || User.current options = args.shift || {} - - joins(:issue => :project). - where(Issue.visible_condition(user, options)). - where(Journal.visible_notes_condition(user, :skip_pre_condition => true)) + visible_scope = none + + journalized_types.each do |klass| + relation = where(journalized_type: klass.name) + relation = klass.journal_visibility_scope(relation) + relation = relation.where(journalized_id: klass.journal_visible_scope(user, options).select(:id)) + relation = klass.journal_notes_visibility_scope(relation, user, options) + visible_scope = visible_scope.or(relation) + end + visible_scope end) safe_attributes( @@ -79,7 +84,7 @@ class Journal < ApplicationRecord :if => lambda {|journal, user| journal.new_record? || journal.editable_by?(user)}) safe_attributes( 'private_notes', - :if => lambda {|journal, user| user.allowed_to?(:set_notes_private, journal.project)}) + :if => lambda {|journal, user| journal.journalized.journal_private_notes_allowed?(user)}) safe_attributes 'updated_by' # Returns a SQL condition to filter out journals with notes that are not visible to user @@ -109,22 +114,13 @@ class Journal < ApplicationRecord notes.blank? && details.empty? end - def journalized - if journalized_type == 'Issue' && association(:issue).loaded? - # Avoid extra query by using preloaded association - issue - else - super - end - end - # Returns journal details that are visible to user def visible_details(user=User.current) details.select do |detail| if detail.property == 'cf' detail.custom_field && detail.custom_field.visible_by?(project, user) elsif detail.property == 'relation' - Issue.find_by_id(detail.value || detail.old_value).try(:visible?, user) + journalized.journal_relation_visible?(detail, user) else true end @@ -137,10 +133,8 @@ class Journal < ApplicationRecord details.detect {|detail| detail.prop_key == attribute} end - # Returns the new status if the journal contains a status change, otherwise nil def new_status - s = new_value_for('status_id') - s ? IssueStatus.find_by_id(s.to_i) : nil + journalized.journal_new_status(self) end def new_value_for(prop) @@ -148,11 +142,11 @@ class Journal < ApplicationRecord 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))) + journalized.journal_editable_by?(self, usr) end def project - journalized.respond_to?(:project) ? journalized.project : nil + journalized.journal_project end def attachments @@ -163,7 +157,7 @@ class Journal < ApplicationRecord end def visible?(*) - journalized.visible?(*) + journalized.journal_visible?(*) end def attachments_visible? @@ -188,9 +182,9 @@ class Journal < ApplicationRecord end def notified_users - notified = journalized.notified_users + notified = journalized.journal_notified_users if private_notes? - notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)} + notified = select_journal_visible_user(notified) end notified end @@ -200,7 +194,7 @@ class Journal < ApplicationRecord end def notified_watchers - notified = journalized.notified_watchers + notified = journalized.journal_notified_watchers select_journal_visible_user(notified) end @@ -264,6 +258,10 @@ class Journal < ApplicationRecord ) end + def valid_watcher?(user) + journalized.journal_valid_watcher?(user) + end + private # Generates journal details for attribute and custom field changes @@ -354,45 +352,16 @@ class Journal < ApplicationRecord end def add_watcher - if user.is_a?(User) && - user.pref.auto_watch_on?('issue_contributed_to') && - valid_watcher?(user) - journalized.set_watcher(user, true) - end - - assignee = journalized.assigned_to - if assignee.is_a?(User) && - assignee.pref.auto_watch_on?('issue_assigned_to_me') && - valid_watcher?(assignee) - journalized.set_watcher(assignee, true) - end - end - - def valid_watcher?(user) - user.active? && - user.allowed_to?(:add_issue_watchers, journalized.project) && - journalized.valid_watcher?(user) && - !journalized.watched_by?(user) + journalized.journal_process_watchers(self) end def send_notification - if notify? && - ( - Setting.notified_events.include?('issue_updated') || - (Setting.notified_events.include?('issue_note_added') && notes.present?) || - (Setting.notified_events.include?('issue_status_updated') && new_status.present?) || - (Setting.notified_events.include?('issue_assigned_to_updated') && detail_for_attribute('assigned_to_id').present?) || - (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?) || - (Setting.notified_events.include?('issue_fixed_version_updated') && detail_for_attribute('fixed_version_id').present?) || - (Setting.notified_events.include?('issue_attachment_added') && details.any? {|d| d.property == 'attachment' && d.value }) - ) - Mailer.deliver_issue_edit(self) - end + journalized.journal_notify(self) end def select_journal_visible_user(notified) if private_notes? - notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)} + notified = notified.select {|user| journalized.journal_user_can_view_private_notes?(user)} end notified end diff --git a/app/views/journals/_notes_form.html.erb b/app/views/journals/_notes_form.html.erb index 4213d6613..1e44f0d84 100644 --- a/app/views/journals/_notes_form.html.erb +++ b/app/views/journals/_notes_form.html.erb @@ -18,4 +18,4 @@

<%= submit_tag l(:button_save) %> <%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %>

<% end %> -<%= wikitoolbar_for "journal_#{@journal.id}_notes", preview_issue_path(:project_id => @project, :issue_id => @journal.issue) %> +<%= wikitoolbar_for "journal_#{@journal.id}_notes", preview_issue_path(:project_id => @project, :issue_id => @journal.journalized) %> diff --git a/app/views/journals/index.builder b/app/views/journals/index.builder index a7ddd71c4..a6d9fbb90 100644 --- a/app/views/journals/index.builder +++ b/app/views/journals/index.builder @@ -10,11 +10,11 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do xml.updated((@journals.first ? @journals.first.event_datetime : Time.now).xmlschema) xml.author {xml.name "#{Setting.app_title}"} @journals.each do |change| - issue = change.issue + journalized = change.journalized xml.entry do - xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}" - xml.link "rel" => "alternate", "href" => issue_url(issue) - xml.id issue_url(issue, :journal_id => change) + xml.title journalized.journal_event_title(change) + xml.link "rel" => "alternate", "href" => issue_url(journalized) + xml.id issue_url(journalized, :journal_id => change) xml.updated change.created_on.xmlschema xml.author do xml.name change.user.name diff --git a/app/views/journals/update.js.erb b/app/views/journals/update.js.erb index 2ab11cac9..230333821 100644 --- a/app/views/journals/update.js.erb +++ b/app/views/journals/update.js.erb @@ -2,9 +2,9 @@ $("#change-<%= @journal.id %>").remove(); <% else %> $("#change-<%= @journal.id %>").attr('class', '<%= @journal.css_classes %>'); - $("#change-<%= @journal.id %> .journal-actions").html('<%= escape_javascript(render_journal_actions(@journal.issue, @journal, :reply_links => authorize_for('issues', 'edit'))) %>'); + $("#change-<%= @journal.id %> .journal-actions").html('<%= escape_javascript(render_journal_actions(@journal.journalized, @journal, :reply_links => authorize_for('issues', 'edit'))) %>'); $("#journal-<%= @journal.id %>-private_notes").replaceWith('<%= escape_javascript(render_private_notes_indicator(@journal)) %>'); - $("#journal-<%= @journal.id %>-notes").replaceWith('<%= escape_javascript(render_notes(@journal.issue, @journal, :reply_links => authorize_for('issues', 'edit'))) %>'); + $("#journal-<%= @journal.id %>-notes").replaceWith('<%= escape_javascript(render_notes(@journal.journalized, @journal, :reply_links => authorize_for('issues', 'edit'))) %>'); $("#journal-<%= @journal.id %>-notes").show(); $("#journal-<%= @journal.id %>-form").remove(); var journal_header = $("#change-<%= @journal.id %>>div.note>h4.journal-header>.journal-info"); diff --git a/lib/plugins/acts_as_journalized/init.rb b/lib/plugins/acts_as_journalized/init.rb new file mode 100644 index 000000000..bf0eb1891 --- /dev/null +++ b/lib/plugins/acts_as_journalized/init.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# 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. + +require_relative 'lib/acts_as_journalized' +Rails.application.reloader.to_prepare do + ApplicationRecord.send(:include, Redmine::Acts::Journalized) +end diff --git a/lib/plugins/acts_as_journalized/lib/acts_as_journalized.rb b/lib/plugins/acts_as_journalized/lib/acts_as_journalized.rb new file mode 100644 index 000000000..da11863c5 --- /dev/null +++ b/lib/plugins/acts_as_journalized/lib/acts_as_journalized.rb @@ -0,0 +1,138 @@ +# frozen_string_literal: true + +# 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. + +module Redmine + module Acts + module Journalized + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def acts_as_journalized(options = {}) + class_attribute :journal_activity_type + + has_many :journals, as: :journalized, dependent: :destroy, inverse_of: :journalized + + Journal.register_journalized_type(self) + self.journal_activity_type = options[:activity_type] + Journal.register_activity_provider(self) if journal_activity_type.present? + + include InstanceMethods + end + + # Visibility + # Returns journalized records visible for the given user. + # Default implementation simply returns all records. + def journal_visible_scope(user = User.current, options = {}) + all + end + + def journal_notes_visibility_scope(scope, user, options = {}) + scope + end + + def journal_visibility_scope(scope) + scope + end + + # scope contains journals of the current journalized class only + def journal_activity_scope(scope) + scope + end + + def journal_activity_preload + [] + end + end + + module InstanceMethods + # Event API + def journal_event_title(journal) + "#{self.class.model_name.human} ##{id}" + end + + def journal_event_type(journal) + 'journal-update' + end + + def journal_event_url(journal) + {} + end + + # Notification API + def journal_notify(journal) + # no-op + end + + # Watcher API + def journal_process_watchers(journal) + # no-op + end + + # Journal detail tracking API + def journalized_attribute_names + [] + end + + # Recipient API + def journal_notified_users + respond_to?(:notified_users) ? notified_users : [] + end + + def journal_notified_watchers + respond_to?(:notified_watchers) ? notified_watchers : [] + end + + # Visibility API + def journal_visible?(user = User.current) + respond_to?(:visible?) ? visible?(user) : true + end + + def journal_project + respond_to?(:project) ? project : nil + end + + def journal_editable_by?(journal, user) + false + end + + def journal_relation_visible?(detail, user) + true + end + + def journal_user_can_view_private_notes?(user) + true + end + + def journal_private_notes_allowed?(user) + false + end + + def journal_valid_watcher?(user) + false + end + + def journal_new_status(journal) + nil + end + end + end + end +end diff --git a/test/unit/changeset_test.rb b/test/unit/changeset_test.rb index 7a814fe34..766a951a8 100644 --- a/test/unit/changeset_test.rb +++ b/test/unit/changeset_test.rb @@ -313,7 +313,7 @@ class ChangesetTest < ActiveSupport::TestCase end assert issue.reload.closed? journal = Journal.order(id: :desc).first - assert_equal issue, journal.issue + assert_equal issue, journal.journalized assert_include "Applied in changeset ecookbook:r12345.", journal.notes end end diff --git a/test/unit/journal_test.rb b/test/unit/journal_test.rb index 4f130990a..7c0583a33 100644 --- a/test/unit/journal_test.rb +++ b/test/unit/journal_test.rb @@ -26,7 +26,7 @@ class JournalTest < ActiveSupport::TestCase end def test_journalized_is_an_issue - issue = @journal.issue + issue = @journal.journalized assert_kind_of Issue, issue assert_equal 1, issue.id end @@ -181,7 +181,7 @@ class JournalTest < ActiveSupport::TestCase # Anonymous user should see issues of public projects only journals = Journal.visible(User.anonymous).to_a assert journals.any? - assert_nil journals.detect {|journal| !journal.issue.project.is_public?} + assert_nil journals.detect {|journal| !journal.project.is_public?} # Anonymous user should not see issues without permission Role.anonymous.remove_permission!(:view_issues) journals = Journal.visible(User.anonymous).to_a @@ -194,7 +194,7 @@ class JournalTest < ActiveSupport::TestCase # Non member user should see issues of public projects only journals = Journal.visible(user).to_a assert journals.any? - assert_nil journals.detect {|journal| !journal.issue.project.is_public?} + assert_nil journals.detect {|journal| !journal.project.is_public?} # Non member user should not see issues without permission Role.non_member.remove_permission!(:view_issues) user.reload @@ -205,7 +205,7 @@ class JournalTest < ActiveSupport::TestCase user.reload journals = Journal.visible(user).to_a assert journals.any? - assert_nil journals.detect {|journal| journal.issue.project_id != 1} + assert_nil journals.detect {|journal| journal.journalized.project_id != 1} end def test_visible_scope_for_admin @@ -215,7 +215,7 @@ class JournalTest < ActiveSupport::TestCase journals = Journal.visible(user).to_a assert journals.any? # Admin should see issues on private projects that admin does not belong to - assert journals.detect {|journal| !journal.issue.project.is_public?} + assert journals.detect {|journal| !journal.project.is_public?} end def test_preload_journals_details_custom_fields_should_set_custom_field_instance_variable @@ -268,7 +268,7 @@ class JournalTest < ActiveSupport::TestCase visible_issue = Issue.generate! hidden_issue = Issue.generate!(:is_private => true) - journal = Journal.new + journal = Journal.new(journalized: issue) journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'relates', :value => visible_issue.id) journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'relates', :value => hidden_issue.id) diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb index 24ec07f65..5f22d1d18 100644 --- a/test/unit/mail_handler_test.rb +++ b/test/unit/mail_handler_test.rb @@ -944,7 +944,7 @@ class MailHandlerTest < ActiveSupport::TestCase assert_equal Issue.find(2), journal.journalized assert_match /This is reply/, journal.notes assert_equal false, journal.private_notes - assert_equal 'Feature request', journal.issue.tracker.name + assert_equal 'Feature request', journal.journalized.tracker.name end def test_update_issue_should_accept_issue_id_after_space_inside_brackets @@ -977,11 +977,11 @@ class MailHandlerTest < ActiveSupport::TestCase 'start_date', 'due_date', 'float field']) assert journal.is_a?(Journal) - issue = Issue.find(journal.issue.id) + issue = Issue.find(journal.journalized.id) assert_equal User.find_by_login('jsmith'), journal.user assert_equal Issue.find(2), journal.journalized assert_match /This is reply/, journal.notes - assert_equal 'Feature request', journal.issue.tracker.name + assert_equal 'Feature request', journal.journalized.tracker.name assert_equal IssueStatus.find_by_name("Resolved"), issue.status assert_equal '2010-01-01', issue.start_date.to_s assert_equal '2010-12-31', issue.due_date.to_s @@ -1042,8 +1042,8 @@ class MailHandlerTest < ActiveSupport::TestCase ) assert journal.is_a?(Journal) assert_match /This is reply/, journal.notes - assert_equal 'Feature request', journal.issue.tracker.name - assert_equal 'Normal', journal.issue.priority.name + assert_equal 'Feature request', journal.journalized.tracker.name + assert_equal 'Normal', journal.journalized.priority.name end def test_update_issue_should_add_cc_as_watchers diff --git a/test/unit/mailer_test.rb b/test/unit/mailer_test.rb index ab880ef45..272404626 100644 --- a/test/unit/mailer_test.rb +++ b/test/unit/mailer_test.rb @@ -362,7 +362,7 @@ class MailerTest < ActiveSupport::TestCase def test_issue_edit_message_id journal = Journal.find(3) - journal.issue = Issue.find(2) + journal.journalized = Issue.find(2) Mailer.deliver_issue_edit(journal) mail = last_email