openproject-meeting 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +46 -0
  3. data/README.md +82 -0
  4. data/app/assets/images/meeting/agenda.png +0 -0
  5. data/app/assets/images/meeting/meeting.png +0 -0
  6. data/app/assets/images/meeting/minutes.png +0 -0
  7. data/app/assets/stylesheets/meeting/meeting.css.erb +39 -0
  8. data/app/controllers/meeting_agendas_controller.rb +33 -0
  9. data/app/controllers/meeting_contents_controller.rb +95 -0
  10. data/app/controllers/meeting_minutes_controller.rb +22 -0
  11. data/app/controllers/meetings_controller.rb +150 -0
  12. data/app/helpers/meeting_contents_helper.rb +68 -0
  13. data/app/helpers/meetings_helper.rb +16 -0
  14. data/app/mailers/meeting_mailer.rb +28 -0
  15. data/app/models/meeting.rb +163 -0
  16. data/app/models/meeting_agenda.rb +95 -0
  17. data/app/models/meeting_content.rb +67 -0
  18. data/app/models/meeting_minutes.rb +81 -0
  19. data/app/models/meeting_participant.rb +45 -0
  20. data/app/views/hooks/_activity_index_head.html.erb +13 -0
  21. data/app/views/meeting_contents/_form.html.erb +33 -0
  22. data/app/views/meeting_contents/_show.html.erb +40 -0
  23. data/app/views/meeting_contents/diff.html.erb +33 -0
  24. data/app/views/meeting_contents/history.html.erb +49 -0
  25. data/app/views/meeting_contents/show.html.erb +13 -0
  26. data/app/views/meeting_mailer/content_for_review.html.erb +23 -0
  27. data/app/views/meeting_mailer/content_for_review.text.erb +9 -0
  28. data/app/views/meetings/_form.html.erb +61 -0
  29. data/app/views/meetings/edit.html.erb +17 -0
  30. data/app/views/meetings/index.html.erb +51 -0
  31. data/app/views/meetings/new.html.erb +17 -0
  32. data/app/views/meetings/show.html.erb +48 -0
  33. data/app/views/shared/_meeting_header.html.erb +3 -0
  34. data/config/locales/de.yml +47 -0
  35. data/config/locales/en.yml +47 -0
  36. data/config/routes.rb +53 -0
  37. data/db/migrate/20110106210555_create_meetings.rb +29 -0
  38. data/db/migrate/20110106221214_create_meeting_contents.rb +29 -0
  39. data/db/migrate/20110106221946_create_meeting_content_versions.rb +20 -0
  40. data/db/migrate/20110108230721_create_meeting_participants.rb +30 -0
  41. data/db/migrate/20110224180804_add_lock_to_meeting_content.rb +24 -0
  42. data/db/migrate/20110819162852_create_initial_meeting_journals.rb +49 -0
  43. data/db/migrate/20111605171815_merge_meeting_content_versions_with_journals.rb +65 -0
  44. data/db/migrate/20130731151542_remove_meeting_role_id_from_meeting_participants.rb +20 -0
  45. data/lib/open_project/meeting.rb +16 -0
  46. data/lib/open_project/meeting/engine.rb +101 -0
  47. data/lib/open_project/meeting/hooks.rb +17 -0
  48. data/lib/open_project/meeting/patches/project_patch.rb +24 -0
  49. data/lib/open_project/meeting/version.rb +16 -0
  50. data/lib/openproject-meeting.rb +12 -0
  51. data/spec/controllers/meetings_controller_spec.rb +90 -0
  52. data/spec/factories/meeting_agenda_factory.rb +16 -0
  53. data/spec/factories/meeting_agenda_journal_factory.rb +18 -0
  54. data/spec/factories/meeting_factory.rb +18 -0
  55. data/spec/factories/meeting_minutes_factory.rb +16 -0
  56. data/spec/factories/meeting_minutes_journal_factory.rb +18 -0
  57. data/spec/factories/meeting_participant_factory.rb +17 -0
  58. data/spec/mailers/meeting_mailer_spec.rb +101 -0
  59. data/spec/models/meeting_agenda_journal_spec.rb +21 -0
  60. data/spec/models/meeting_agenda_spec.rb +52 -0
  61. data/spec/models/meeting_minutes_journal_spec.rb +21 -0
  62. data/spec/models/meeting_minutes_spec.rb +44 -0
  63. data/spec/models/meeting_spec.rb +168 -0
  64. data/spec/models/user_deletion_spec.rb +186 -0
  65. data/spec/spec_helper.rb +14 -0
  66. data/spec/support/plugin_spec_helper.rb +47 -0
  67. metadata +158 -0
@@ -0,0 +1,101 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ require 'rails/engine'
13
+
14
+ module OpenProject::Meeting
15
+ class Engine < ::Rails::Engine
16
+ engine_name :openproject_meeting
17
+
18
+ config.autoload_paths += Dir["#{config.root}/lib/"]
19
+
20
+ spec = Bundler.environment.specs['openproject-meeting'][0]
21
+ initializer 'meeting.register_plugin' do
22
+ Redmine::Plugin.register :openproject_meeting do
23
+
24
+ name 'OpenProject Meeting'
25
+ author ((spec.authors.kind_of? Array) ? spec.authors[0] : spec.authors)
26
+ author_url spec.homepage
27
+ description spec.description
28
+ version spec.version
29
+ url 'https://www.openproject.org/projects/plugin-meetings'
30
+
31
+ requires_openproject ">= 3.0.0pre9"
32
+
33
+ project_module :meetings do
34
+ permission :create_meetings, {:meetings => [:new, :create, :copy]}, :require => :member
35
+ permission :edit_meetings, {:meetings => [:edit, :update]}, :require => :member
36
+ permission :delete_meetings, {:meetings => [:destroy]}, :require => :member
37
+ permission :view_meetings, {:meetings => [:index, :show], :meeting_agendas => [:history, :show, :diff], :meeting_minutes => [:history, :show, :diff]}
38
+ permission :create_meeting_agendas, {:meeting_agendas => [:update, :preview]}, :require => :member
39
+ permission :close_meeting_agendas, {:meeting_agendas => [:close, :open]}, :require => :member
40
+ permission :send_meeting_agendas_notification, {:meeting_agendas => [:notify]}, :require => :member
41
+ permission :create_meeting_minutes, {:meeting_minutes => [:update, :preview]}, :require => :member
42
+ permission :send_meeting_minutes_notification, {:meeting_minutes => [:notify]}, :require => :member
43
+ end
44
+
45
+ Redmine::Search.map do |search|
46
+ search.register :meetings
47
+ end
48
+
49
+ menu :project_menu, :meetings, {:controller => '/meetings', :action => 'index'}, :caption => :project_module_meetings, :param => :project_id, :after => :wiki
50
+ menu :project_menu, :new_meeting, {:controller => '/meetings', :action => 'new'}, :param => :project_id, :caption => :label_meeting_new, :parent => :meetings
51
+
52
+ ActiveSupport::Inflector.inflections do |inflect|
53
+ inflect.uncountable "meeting_minutes"
54
+ end
55
+ end
56
+ end
57
+
58
+ initializer 'meeting.precompile_assets' do |app|
59
+ app.config.assets.precompile += ["meeting.css"]
60
+ end
61
+
62
+ initializer 'meeting.register_path_to_rspec' do |app|
63
+ app.config.plugins_to_test_paths << self.root
64
+ end
65
+
66
+ config.before_configuration do |app|
67
+ # This is required for the routes to be loaded first
68
+ # as the routes should be prepended so they take precedence over the core.
69
+ app.config.paths['config/routes'].unshift File.join(File.dirname(__FILE__), "..", "..", "..", "config", "routes.rb")
70
+ end
71
+
72
+ initializer "remove_duplicate_meeting_routes", :after => "add_routing_paths" do |app|
73
+ # removes duplicate entry from app.routes_reloader
74
+ # As we prepend the plugin's routes to the load_path up front and rails
75
+ # adds all engines' config/routes.rb later, we have double loaded the routes
76
+ # This is not harmful as such but leads to duplicate routes which decreases performance
77
+ app.routes_reloader.paths.uniq!
78
+ end
79
+
80
+ # adds our factories to factory girl's load path
81
+ initializer "meeting.register_factories", :after => "factory_girl.set_factory_paths" do |app|
82
+ FactoryGirl.definition_file_paths << File.expand_path(self.root.to_s + '/spec/factories') if defined?(FactoryGirl)
83
+ end
84
+
85
+ initializer "meeting.register_hooks" do
86
+ require 'open_project/meeting/hooks'
87
+ end
88
+
89
+ config.to_prepare do
90
+
91
+ require_dependency 'open_project/meeting/patches/project_patch'
92
+
93
+ # load classes so that all User.before_destroy filters are loaded
94
+ require_dependency 'meeting'
95
+ require_dependency 'meeting_agenda'
96
+ require_dependency 'meeting_minutes'
97
+ require_dependency 'meeting_participant'
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,17 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ module OpenProject::Meeting
13
+ class Hooks < Redmine::Hook::ViewListener
14
+ render_on :activity_index_head,
15
+ :partial => 'hooks/activity_index_head'
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ module OpenProject::Meeting
13
+ module Patches
14
+ module ProjectPatch
15
+ def self.included(receiver)
16
+ receiver.class_eval do
17
+ has_many :meetings, :include => [:author], :dependent => :destroy
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ Project.send(:include, OpenProject::Meeting::Patches::ProjectPatch)
@@ -0,0 +1,16 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ module OpenProject
13
+ module Meeting
14
+ VERSION = "3.0.0"
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ require 'open_project/meeting'
@@ -0,0 +1,90 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ require File.dirname(__FILE__) + '/../spec_helper'
13
+
14
+ describe MeetingsController do
15
+ before(:each) do
16
+ @p = mock_model(Project)
17
+ @controller.stub!(:authorize)
18
+ @controller.stub!(:check_if_login_required)
19
+ end
20
+
21
+ describe "GET" do
22
+ describe "index" do
23
+ before(:each) do
24
+ Project.stub!(:find).and_return(@p)
25
+ @ms = [mock_model(Meeting), mock_model(Meeting), mock_model(Meeting)]
26
+ @ms.stub!(:from_tomorrow).and_return(@ms)
27
+ @p.stub!(:meetings).and_return(@ms)
28
+ [:with_users_by_date, :page, :per_page].each do |meth|
29
+ @ms.should_receive(meth).and_return(@ms)
30
+ end
31
+ @grouped = double('grouped')
32
+ Meeting.should_receive(:group_by_time).with(@ms).and_return(@grouped)
33
+ end
34
+ describe "html" do
35
+ before(:each) do
36
+ get "index", :project_id => @p.id
37
+ end
38
+ it {response.should be_success}
39
+ it {assigns(:meetings_by_start_year_month_date).should eql @grouped }
40
+ end
41
+ end
42
+
43
+ describe "show" do
44
+ before(:each) do
45
+ @m = mock_model(Meeting)
46
+ Meeting.stub!(:find).and_return(@m)
47
+ @m.stub!(:project).and_return(@p)
48
+ @m.stub!(:agenda).stub!(:present?).and_return(false)
49
+ end
50
+ describe "html" do
51
+ before(:each) do
52
+ get "show", :id => @m.id
53
+ end
54
+ it {response.should be_success}
55
+ end
56
+ end
57
+
58
+ describe "new" do
59
+ before(:each) do
60
+ Project.stub!(:find).and_return(@p)
61
+ @m = mock_model(Meeting)
62
+ @m.stub!(:project=)
63
+ @m.stub!(:author=)
64
+ Meeting.stub!(:new).and_return(@m)
65
+ end
66
+ describe "html" do
67
+ before(:each) do
68
+ get "new", :project_id => @p.id
69
+ end
70
+ it {response.should be_success}
71
+ it {assigns(:meeting).should eql @m}
72
+ end
73
+ end
74
+
75
+ describe "edit" do
76
+ before(:each) do
77
+ @m = mock_model(Meeting)
78
+ Meeting.stub!(:find).and_return(@m)
79
+ @m.stub(:project).and_return(@p)
80
+ end
81
+ describe "html" do
82
+ before(:each) do
83
+ get "edit", :id => @m.id
84
+ end
85
+ it {response.should be_success}
86
+ it {assigns(:meeting).should eql @m}
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ FactoryGirl.define do
13
+ factory :meeting_agenda do |a|
14
+ meeting
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ require 'meeting_agenda'
13
+
14
+ FactoryGirl.define do
15
+ factory :meeting_agenda_journal do |m|
16
+ m.association :journaled, :factory => :meeting_agenda
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ FactoryGirl.define do
13
+ factory :meeting do |m|
14
+ author :factory => :user
15
+ project
16
+ m.sequence(:title) { |n| "Meeting #{n}" }
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ FactoryGirl.define do
13
+ factory :meeting_minutes do |m|
14
+ meeting
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ require 'meeting_minutes'
13
+
14
+ FactoryGirl.define do
15
+ factory :meeting_minutes_journal do |m|
16
+ m.association :journaled, :factory => :meeting_minutes
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ FactoryGirl.define do
13
+ factory :meeting_participant do |mp|
14
+ user
15
+ meeting
16
+ end
17
+ end
@@ -0,0 +1,101 @@
1
+ #-- copyright
2
+ # OpenProject is a project management system.
3
+ #
4
+ # Copyright (C) 2012-2013 the OpenProject Team
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # See doc/COPYRIGHT.rdoc for more details.
10
+ #++
11
+
12
+ require File.dirname(__FILE__) + '/../spec_helper'
13
+
14
+ describe MeetingMailer do
15
+ before(:each) do
16
+ @role = FactoryGirl.create(:role, :permissions => [:view_meetings])
17
+ @content = FactoryGirl.create(:meeting_agenda)
18
+ @author = @content.meeting.author
19
+ @project = @content.meeting.project
20
+ @watcher1 = FactoryGirl.create(:user)
21
+ @watcher2 = FactoryGirl.create(:user)
22
+ @project.add_member @author, [@role]
23
+ @project.add_member @watcher1, [@role]
24
+ @project.add_member @watcher2, [@role]
25
+
26
+ @participants = [@content.meeting.participants.build(:user => @watcher1, :invited => true, :attended => false),
27
+ @content.meeting.participants.build(:user => @watcher2, :invited => true, :attended => false)]
28
+
29
+ @project.save!
30
+ @content.meeting.save!
31
+
32
+ @content.meeting.watcher_users true
33
+ end
34
+
35
+ describe "content_for_review" do
36
+ let(:mail) { MeetingMailer.content_for_review @content, 'agenda' }
37
+ # this is needed to call module functions from Redmine::I18n
38
+ let(:i18n) do
39
+ class A
40
+ include Redmine::I18n
41
+ public :format_date, :format_time
42
+ end
43
+ A.new
44
+ end
45
+
46
+
47
+ it "renders the headers" do
48
+ mail.subject.should include(@content.meeting.project.name)
49
+ mail.subject.should include(@content.meeting.title)
50
+ mail.to.should include(@author.mail)
51
+ mail.from.should eq([Setting.mail_from])
52
+ mail.cc.should_not include(@author.mail)
53
+ mail.cc.should include(@watcher1.mail)
54
+ mail.cc.should include(@watcher2.mail)
55
+ end
56
+
57
+ it "renders the text body" do
58
+ check_meeting_mail_content mail.text_part.body
59
+ end
60
+
61
+ it "renders the html body" do
62
+ check_meeting_mail_content mail.html_part.body
63
+ end
64
+ end
65
+
66
+ def check_meeting_mail_content(body)
67
+ body.should include(@content.meeting.project.name)
68
+ body.should include(@content.meeting.title)
69
+ body.should include(i18n.format_date @content.meeting.start_date)
70
+ body.should include(i18n.format_time @content.meeting.start_time, false)
71
+ body.should include(i18n.format_time @content.meeting.end_time, false)
72
+ body.should include(@participants[0].name)
73
+ body.should include(@participants[1].name)
74
+ end
75
+
76
+ def save_and_open_mail_html_body(mail)
77
+ save_and_open_mail_part mail.html_part.body
78
+ end
79
+
80
+ def save_and_open_mail_text_body(mail)
81
+ save_and_open_mail_part mail.text_part.body
82
+ end
83
+
84
+ def save_and_open_mail_part(part)
85
+ FileUtils.mkdir_p(Rails.root.join('tmp/mails'))
86
+
87
+ page_path = Rails.root.join("tmp/mails/#{SecureRandom.hex(16)}.html").to_s
88
+ File.open(page_path, 'w') { |f| f.write(part) }
89
+
90
+ Launchy.open(page_path)
91
+
92
+ begin
93
+ binding.pry
94
+ rescue NoMethodError
95
+ debugger
96
+ end
97
+
98
+ FileUtils.rm(page_path)
99
+
100
+ end
101
+ end