openproject-meeting 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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