Merge remote-tracking branch 'origin/feature/rails3' into feature/magic_comparison

Conflicts:
	doc/CHANGELOG.md
pull/513/head
Hagen Schink 11 years ago
commit 0c1490d1e4
  1. 13
      app/controllers/messages_controller.rb
  2. 5
      app/controllers/work_packages_controller.rb
  3. 5
      app/models/message.rb
  4. 26
      app/models/work_package.rb
  5. 55
      db/migrate/20130625124242_work_package_custom_field_data_migration.rb
  6. 31
      db/migrate/20131017064039_repair_work_packages_initial_attachable_journal.rb
  7. 54
      db/migrate/20131018134525_repair_messages_initial_attachable_journal.rb
  8. 148
      db/migrate/migration_utils/attachable_utils.rb
  9. 3
      doc/CHANGELOG.md
  10. 41
      lib/open_project/journal/attachment_helper.rb
  11. 14
      lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb
  12. 169
      spec/controllers/messages_controller_spec.rb
  13. 121
      spec/controllers/work_packages_controller_spec.rb
  14. 32
      spec/factories/journal/message_journal_factory.rb
  15. 6
      spec/factories/journal_factory.rb
  16. 4
      spec/models/work_package_spec.rb

@ -73,11 +73,14 @@ class MessagesController < ApplicationController
m.author = User.current
m.board = @board
end
@message.safe_attributes = params[:message]
@message.attach_files(params[:attachments])
if @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
redirect_to topic_path(@message)
else
render :action => 'new'
@ -111,10 +114,12 @@ class MessagesController < ApplicationController
# Edit a message
def update
(render_403; return false) unless @message.editable_by?(User.current)
@message.safe_attributes = params[:message]
@message.attach_files(params[:attachments])
if @message.save
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
@message.reload
redirect_to topic_path(@message.root, :r => (@message.parent_id && @message.id))

@ -128,12 +128,11 @@ class WorkPackagesController < ApplicationController
WorkPackageObserver.instance.send_notification = send_notifications?
work_package.attach_files(params[:attachments])
if work_package.save
flash[:notice] = I18n.t(:notice_successful_create)
Attachment.attach_files(work_package, params[:attachments])
render_attachment_warning_if_needed(work_package)
call_hook(:controller_work_package_new_after_save, { :params => params, :work_package => work_package })
redirect_to(work_package_path(work_package))

@ -29,10 +29,13 @@
class Message < ActiveRecord::Base
include Redmine::SafeAttributes
include OpenProject::Journal::AttachmentHelper
belongs_to :board
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
acts_as_attachable
acts_as_attachable after_add: :attachments_changed,
after_remove: :attachments_changed
belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
acts_as_journalized :event_title => Proc.new {|o| "#{o.journal.journable.board.name}: #{o.journal.journable.subject}"},

@ -36,6 +36,8 @@ class WorkPackage < ActiveRecord::Base
include WorkPackage::SchedulingRules
include WorkPackage::StatusTransitions
include OpenProject::Journal::AttachmentHelper
# >>> issues.rb >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include Redmine::SafeAttributes
@ -141,6 +143,8 @@ class WorkPackage < ActiveRecord::Base
acts_as_attachable :after_add => :attachments_changed,
:after_remove => :attachments_changed
after_validation :set_attachments_error_details, if: lambda {|work_package| work_package.errors.messages.has_key? :attachments}
# Mapping attributes, that are passed in as id's onto their respective associations
# (eg. type=4711 onto type=Type.find(4711))
include AssociationsMapper
@ -281,13 +285,6 @@ class WorkPackage < ActiveRecord::Base
!due_date.nil? && (due_date < Date.today) && !status.is_closed?
end
# ACTS AS ATTACHABLE
# Callback on attachment deletion
def attachments_changed(obj)
add_journal
save!
end
# ACTS AS JOURNALIZED
def activity_type
"work_packages"
@ -407,13 +404,9 @@ class WorkPackage < ActiveRecord::Base
update_by(user, attributes)
if save
# as attach_files always saves an attachment right away
# it is not possible to stage attaching and check for
# valid. If this would be possible, we could check
# for this along with update_attributes
attachments = Attachment.attach_files(self, raw_attachments)
end
attach_files(raw_attachments)
save
end
def update_by(user, attributes)
@ -1001,4 +994,9 @@ class WorkPackage < ActiveRecord::Base
end
# <<< issues.rb <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
def set_attachments_error_details
if invalid_attachment = self.attachments.detect{|a| !a.valid?}
errors.messages[:attachments].first << " - #{invalid_attachment.errors.full_messages.first}"
end
end
end

@ -0,0 +1,55 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class WorkPackageCustomFieldDataMigration < ActiveRecord::Migration
def self.up
ActiveRecord::Base.connection.execute <<-SQL
UPDATE #{custom_fields_table}
SET type = #{quote_value('WorkPackageCustomfield')}
WHERE type = #{quote_value('IssueCustomField')}
SQL
end
def self.down
ActiveRecord::Base.connection.execute <<-SQL
UPDATE #{custom_fields_table}
SET type = #{quote_value('IssueCustomField')}
WHERE type = #{quote_value('WorkPackageCustomfield')}
SQL
end
private
def custom_fields_table
@settings_table ||= ActiveRecord::Base.connection.quote_table_name('custom_fields')
end
def quote_value s
ActiveRecord::Base.connection.quote(s)
end
end

@ -0,0 +1,31 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require_relative 'migration_utils/attachable_utils'
class RepairWorkPackagesInitialAttachableJournal < ActiveRecord::Migration
include Migration::Utils
LEGACY_JOURNAL_TYPE = 'IssueJournal'
JOURNAL_TYPE = 'WorkPackage'
def up
say_with_time_silently "Repair initial attachable journals" do
repair_attachable_journal_entries(JOURNAL_TYPE, LEGACY_JOURNAL_TYPE)
end
end
def down
say_with_time_silently "Remove initial attachable journals" do
remove_initial_journal_entries(JOURNAL_TYPE, LEGACY_JOURNAL_TYPE)
end
end
end

@ -0,0 +1,54 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require_relative 'migration_utils/attachable_utils'
class RepairMessagesInitialAttachableJournal < ActiveRecord::Migration
include Migration::Utils
JOURNAL_TYPE = 'Message'
def up
say_with_time_silently "Repair initial attachable journals" do
result = missing_message_attachments
repair_initial_journals(result, JOURNAL_TYPE)
end
end
def down
say_with_time_silently "Remove initial attachable journals" do
result = missing_message_attachments
remove_initial_journals(result, JOURNAL_TYPE)
end
end
private
def missing_message_attachments
result = select_all <<-SQL
SELECT a.id, a.container_id, a.filename, last_version
FROM attachments AS a
JOIN (SELECT journable_id, MAX(version) AS last_version FROM journals
WHERE journable_type = '#{JOURNAL_TYPE}'
GROUP BY journable_id) AS j ON (a.container_id = j.journable_id)
WHERE container_type = '#{JOURNAL_TYPE}'
SQL
result.each_with_object([]) do |row, a|
a << MissingAttachment.new(row['container_id'],
row['id'],
row['filename'],
row['last_version'])
end
end
end

@ -0,0 +1,148 @@
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require_relative 'utils'
module Migration
module Utils
MissingAttachment = Struct.new(:journaled_id,
:attachment_id,
:filename,
:last_version)
def repair_attachable_journal_entries(journal_type, legacy_journal_type)
result = invalid_attachments(legacy_journal_type)
repair_initial_journals(result, journal_type)
end
def remove_initial_journal_entries(journal_type, legacy_journal_type)
result = invalid_attachments(legacy_journal_type)
remove_initial_journals(result, journal_type)
end
def repair_initial_journals(result, journal_type)
result.each do |m|
journal_ids = affected_journal_ids(m.journaled_id, m.last_version, journal_type)
journal_ids.each do |journal_id|
insert <<-SQL
INSERT INTO attachable_journals (journal_id, attachment_id, filename)
VALUES (#{journal_id}, #{m.attachment_id}, '#{m.filename}')
SQL
end
end
end
def remove_initial_journals(result, journal_type)
result.each do |m|
journal_ids = affected_journal_ids(m.journaled_id, m.last_version, journal_type)
delete <<-SQL
DELETE FROM attachable_journals
WHERE journal_id IN (#{journal_ids.join(", ")})
SQL
end
end
private
COLUMNS = ['changed_data', 'version', 'journaled_id']
def invalid_attachments(legacy_journal_type)
result = []
update_column_values('legacy_journals',
COLUMNS,
find_work_packages_with_missing_initial_attachment(legacy_journal_type,
result),
filter(legacy_journal_type))
result.flatten
end
def filter(legacy_journal_type)
"type = '#{legacy_journal_type}' AND changed_data LIKE '%attachments%'"
end
def find_work_packages_with_missing_initial_attachment(legacy_journal_type, result)
Proc.new do |row|
missing_entries = missing_initial_attachable_journals(legacy_journal_type,
row['id'],
row['journaled_id'],
row['version'],
row['changed_data'])
result << missing_entries unless missing_entries.empty?
UpdateResult.new(row, false)
end
end
def missing_initial_attachable_journals(legacy_journal_type, journal_id, journaled_id, version, changed_data)
removed_attachments = parse_attachment_removals(changed_data)
missing_entries = missing_initial_attachment_entries(legacy_journal_type,
journaled_id,
version,
removed_attachments)
missing_entries.map { |e| MissingAttachment.new(journaled_id,
e[:id],
e[:filename],
version.to_i - 1) }
end
############################################
# Matches attachment removals of the form: #
# #
# attachments<id>: #
# - #
# - <filename> #
############################################
ATTACHMENT_REMOVAL_REGEX = /attachments_(?<id>\d+): \n-\s(?<filename>.+)\n-\s$/
def parse_attachment_removals(changed_data)
matches = changed_data.scan(ATTACHMENT_REMOVAL_REGEX)
matches.each_with_object([]) { |m, l| l << { id: m[0], filename: m[1] } }
end
def missing_initial_attachment_entries(legacy_journal_type, journaled_id, version, attachments)
attachments.select do |a|
result = select_all <<-SQL
SELECT version
FROM legacy_journals
WHERE journaled_id = #{journaled_id}
AND type = '#{legacy_journal_type}'
AND version < #{version}
AND changed_data LIKE '%attachments#{a[:id]}:%'
AND changed_data LIKE '%- #{a[:filename]}%'
ORDER BY version
SQL
result.empty?
end
end
def affected_journal_ids(journaled_id, last_version, journal_type)
result_set = select_all <<-SQL
SELECT id
FROM journals
WHERE journable_id = #{journaled_id}
AND journable_type = '#{journal_type}'
AND version <= #{last_version}
SQL
result_set.collect { |r| r['id'] }
end
end
end

@ -33,10 +33,13 @@ See doc/COPYRIGHT.rdoc for more details.
* `#1738` Forum problem when no description given.
* `#1916` Work package update screen is closed when attached file is deleted
* `#1935` Fixed bug: Default submenu for wiki pages is wrong (Configure menu item)
* `#2009` No journal entry created for attachments if the attachment is added on container creation
* `#2371` Add support for IE10 to Timelines
* `#2448` Accelerate work package updates
* `#2464` No initial attachment journal for messages
* `#2479` Remove TinyMCE spike
* `#2557` Highlight changes of any work package attribute available in the timelines table
* `#2559` Migrate existing IssueCustomFields to WorkPackageCustomFields
* Fix compatibility with old mail configuration
## 3.0.0pre22

@ -0,0 +1,41 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module OpenProject
module Journal
module AttachmentHelper
def attachments_changed(obj)
unless new_record?
add_journal
save
end
end
end
end
end

@ -66,6 +66,20 @@ module Redmine
@unsaved_attachments ||= []
end
# Bulk attaches a set of files to an object
def attach_files(attachments)
if attachments && attachments.is_a?(Hash)
attachments.each_value do |attachment|
file = attachment['file']
next unless file && file.size > 0
self.attachments.build(file: file,
container: self,
description: attachment['description'].to_s.strip,
author: User.current)
end
end
end
module ClassMethods
end
end

@ -0,0 +1,169 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe MessagesController do
let(:user) { FactoryGirl.create(:user) }
let(:project) { FactoryGirl.create(:project) }
let(:role) { FactoryGirl.create(:role) }
let!(:member) { FactoryGirl.create(:member,
project: project,
principal: user,
roles: [role]) }
let!(:board) { FactoryGirl.create(:board,
project: project) }
let(:filename) { "test1.test" }
before { User.stub(:current).and_return user }
describe :create do
context :attachments do
# see ticket #2464 on OpenProject.org
context "new attachment on new messages" do
before do
controller.should_receive(:authorize).and_return(true)
Attachment.any_instance.stub(:filename).and_return(filename)
Attachment.any_instance.stub(:copy_file_to_destination)
post 'create', board_id: board.id,
message: { subject: "Test created message",
content: "Messsage body" },
attachments: { file: { file: filename,
description: '' } }
end
describe :journal do
let(:attachment_id) { "attachments_#{Message.last.attachments.first.id}".to_sym }
subject { Message.last.journals.last.changed_data }
it { should have_key attachment_id }
it { subject[attachment_id].should eq([nil, filename]) }
end
end
end
end
describe :attachment do
let!(:message) { FactoryGirl.create(:message) }
let(:attachment_id) { "attachments_#{message.attachments.first.id}".to_sym }
let(:params) { { id: message.id,
attachments: { '1' => { file: filename,
description: '' } } } }
describe :add do
before do
Message.any_instance.stub(:editable_by?).and_return(true)
Attachment.any_instance.stub(:filename).and_return(filename)
Attachment.any_instance.stub(:copy_file_to_destination)
end
context "invalid attachment" do
let(:max_filesize) { Setting.attachment_max_size.to_i.kilobytes }
before do
Attachment.any_instance.stub(:filesize).and_return(max_filesize + 1)
post :update, params
end
describe :view do
subject { response }
it { should render_template('messages/edit', formats: ["html"]) }
end
describe :error do
subject { assigns(:message).errors.messages }
it { should have_key(:attachments) }
it { subject[:attachments] =~ /too long/ }
end
end
context :journal do
before do
put :update, params
message.reload
end
describe :key do
subject { message.journals.last.changed_data }
it { should have_key attachment_id }
end
describe :value do
subject { message.journals.last.changed_data[attachment_id].last }
it { should eq(filename) }
end
end
end
describe :remove do
let!(:attachment) { FactoryGirl.create(:attachment,
container: message,
author: user,
filename: filename) }
let!(:attachable_journal) { FactoryGirl.create(:journal_attachable_journal,
journal: message.journals.last,
attachment: attachment,
filename: filename) }
before do
message.reload
message.attachments.delete(attachment)
message.reload
end
context :journal do
let(:attachment_id) { "attachments_#{attachment.id}".to_sym }
describe :key do
subject { message.journals.last.changed_data }
it { should have_key attachment_id }
end
describe :value do
subject { message.journals.last.changed_data[attachment_id].first }
it { should eq(filename) }
end
end
end
end
end

@ -345,7 +345,7 @@ describe WorkPackagesController do
it 'should attach attachments if those are provided' do
params[:attachments] = 'attachment-blubs-data'
Attachment.should_receive(:attach_files).with(stub_work_package, params[:attachments])
stub_work_package.should_receive(:attach_files).with(params[:attachments])
controller.stub(:render_attachment_warning_if_needed)
call_action
@ -760,4 +760,123 @@ describe WorkPackagesController do
end
end
end
let(:filename) { "test1.test" }
describe :create do
let(:type) { FactoryGirl.create :type }
let(:project) { FactoryGirl.create :project,
types: [type] }
let(:status) { FactoryGirl.create :default_status }
let(:priority) { FactoryGirl.create :priority }
context :attachments do
let(:new_work_package) { FactoryGirl.build(:work_package,
project: project,
type: type,
description: "Description",
priority: priority) }
let(:params) { { project_id: project.id,
attachments: { file: { file: filename,
description: '' } } } }
before do
controller.stub(:work_package).and_return(new_work_package)
controller.should_receive(:authorize).and_return(true)
Attachment.any_instance.stub(:filename).and_return(filename)
Attachment.any_instance.stub(:copy_file_to_destination)
end
# see ticket #2009 on OpenProject.org
context "new attachment on new work package" do
before { post 'create', params }
describe :journal do
let(:attachment_id) { "attachments_#{new_work_package.attachments.first.id}".to_sym }
subject { new_work_package.journals.last.changed_data }
it { should have_key attachment_id }
it { subject[attachment_id].should eq([nil, filename]) }
end
end
context "invalid attachment" do
let(:max_filesize) { Setting.attachment_max_size.to_i.kilobytes }
before do
Attachment.any_instance.stub(:filesize).and_return(max_filesize + 1)
post :create, params
end
describe :view do
subject { response }
it { should render_template('work_packages/new', formats: ["html"]) }
end
describe :error do
subject { new_work_package.errors.messages }
it { should have_key(:attachments) }
it { subject[:attachments] =~ /too long/ }
end
end
end
end
describe :update do
let(:type) { FactoryGirl.create :type }
let(:project) { FactoryGirl.create :project,
types: [type] }
let(:status) { FactoryGirl.create :default_status }
let(:priority) { FactoryGirl.create :priority }
context :attachments do
let(:work_package) { FactoryGirl.build(:work_package,
project: project,
type: type,
description: "Description",
priority: priority) }
let(:params) { { id: work_package.id,
work_package: { attachments: { '1' => { file: filename,
description: '' } } } } }
before do
controller.stub(:work_package).and_return(work_package)
controller.should_receive(:authorize).and_return(true)
Attachment.any_instance.stub(:filename).and_return(filename)
Attachment.any_instance.stub(:copy_file_to_destination)
end
context "invalid attachment" do
let(:max_filesize) { Setting.attachment_max_size.to_i.kilobytes }
before do
Attachment.any_instance.stub(:filesize).and_return(max_filesize + 1)
post :update, params
end
describe :view do
subject { response }
it { should render_template('work_packages/edit', formats: ["html"]) }
end
describe :error do
subject { work_package.errors.messages }
it { should have_key(:attachments) }
it { subject[:attachments] =~ /too long/ }
end
end
end
end
end

@ -0,0 +1,32 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# 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.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
FactoryGirl.define do
factory :journal_message_journal, class: Journal::MessageJournal do
end
end

@ -42,5 +42,11 @@ FactoryGirl.define do
activity_type "wiki_edits"
data FactoryGirl.build(:journal_wiki_content_journal)
end
factory :message_journal, class: Journal do
journable_type "Message"
activity_type "messages"
data FactoryGirl.build(:journal_message_journal)
end
end
end

@ -1047,8 +1047,8 @@ describe WorkPackage do
raw_attachments = [double('attachment')]
attachment = FactoryGirl.build(:attachment)
Attachment.should_receive(:attach_files)
.with(instance, raw_attachments)
instance.should_receive(:attach_files)
.with(raw_attachments)
.and_return(attachment)
instance.update_by!(user, { :attachments => raw_attachments })

Loading…
Cancel
Save