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
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzIwZjYwNThkYmVkNjllYWYzZDViN2I2MGU3MDVhMTljNDY1MTMwMA==
5
+ data.tar.gz: !binary |-
6
+ MjZhNzM5YzUwN2MwYTExZDgwNjFhMWYxMWFiZDIyMjNkYzBmMzYyZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZWNlY2VmODcwYWFjN2JlNzY5N2QxZGY1YjMxYmI1NDJkMjhmYWZkYmE2ZGFl
10
+ M2YxYjYxMjc1OGNlZjk1MjRlNTNhNDU4ZWU0YzRkY2UyMmQ1ODNmMWI5YzU3
11
+ MjI5YTA2NTU5NDUxOWY2OThlNzEwOTA5ZTQyZWM1YzkzZTQ1YjQ=
12
+ data.tar.gz: !binary |-
13
+ NDY3MzRkZDYyMTFhYzVkMWRlOTAwOGRhNWJhOTBlZmViNDQ0OTBhZDQxOTFl
14
+ MGQ4NzI4MDI1Njg5MTBhYzM1NjJkOTI1NGYwODg3NWEyYjAzMzdmNTIxNWEz
15
+ ZjBkYjgzNTk5NTNjOWFmNWM0YTk5ZWNiMGYyMTUyZDM2YTU4MWE=
data/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ ## 3.0.0
4
+
5
+ * `#1554` Execute release
6
+
7
+ ## 3.0.0.rc2
8
+
9
+ * `#1529` First public release
10
+ * `#1551` Fixed bug: Not possible to withdraw invitation
11
+
12
+ ## 2013-07-04 Christian Ratz <c.ratz@finn.de>
13
+
14
+ * fix time zones bug
15
+
16
+ ## 2013-06-27 Christian Ratz <c.ratz@finn.de>
17
+
18
+ * new pagination
19
+ * fix time zones bug
20
+
21
+ ## 2013-06-21 Christian Ratz <c.ratz@finn.de>
22
+
23
+ * Use final plugin name schema
24
+
25
+ ## 2013-06-14 Christian Ratz <c.ratz@finn.de>
26
+
27
+ * added dependency to OpenProject core >= 3.0.0beta1
28
+
29
+ ## 2013024 Christian Ratz <c.ratz@finn.de>
30
+
31
+ * RC1 of the Rails 3 version
32
+ * This version is no longer compatible with the Rails 2 core
33
+
34
+ ## 2013-03-22 Jens Ulferts <j.ulferts@finn.de>
35
+
36
+ * fixes routes
37
+ * removes unnecessary code
38
+ * shows only the users as possible participants who have the required
39
+ permissions
40
+ * adds missing html_safe
41
+ * fixes mailer i18n issue
42
+
43
+ ## 2013-03-18 Hagen Schink <h.schink@finn.de>
44
+
45
+ * Swtiched to version 0.2.5
46
+ * Rails 3.2-ified plug-in
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ OpenProject Meeting Plugin
2
+ ==========================
3
+
4
+ This plugin adds functions to support project meetings to
5
+ [OpenProject](https://www.openproject.org). Meetings
6
+ can be scheduled selecting invitees from the same project to take
7
+ part in the meeting. An agenda can be created and sent to the invitees.
8
+ After the meeting, attendants can be selected and minutes can be
9
+ created based on the agenda. Finally, the minutes can be sent to
10
+ all attendants and invitees.
11
+
12
+ A more detailed feature tour can be found [here](https://www.openproject.org/projects/openproject/wiki/Meetings).
13
+
14
+ Requirements
15
+ ------------
16
+
17
+ The meetings plugin currently requires the [OpenProject Core](https://github.com/opf/openproject/) in
18
+ version 3.0.0pre9 or newer.
19
+
20
+
21
+ Installation
22
+ ------------
23
+
24
+ Please follow the default [plugin installation instructions for
25
+ OpenProject](https://www.openproject.org/projects/openproject/wiki/Installation#222-Add-plugins),
26
+ adding the following line to the Gemfile.plugins:
27
+
28
+ `gem "openproject-meeting", :git => "https://github.com/finnlabs/openproject-meeting"`
29
+
30
+ Afterwards, run:
31
+
32
+ `bundle install`
33
+
34
+ This plugin contains migrations. To migrate the database, run:
35
+
36
+ `rake openproject_meeting:install:migrations`
37
+
38
+ `rake db:migrate`
39
+
40
+ Deinstallation
41
+ --------------
42
+
43
+ Remove the line
44
+
45
+ `gem "openproject-meeting", :git => "https://github.com/finnlabs/openproject-meeting"`
46
+
47
+ from the file `Gemfile.plugins` and run:
48
+
49
+ `bundle install`
50
+
51
+ Please not that this leaves plugin data in the database. Currently, we do not
52
+ support full uninstall of the plugin.
53
+
54
+ Bug Reporting
55
+ -------------
56
+
57
+ If you find any bugs, you can create a bug ticket at
58
+
59
+ https://www.openproject.org/projects/plugin-meetings
60
+
61
+ Development
62
+ -----------
63
+
64
+ To contribute, you can create pull request on the official repository at
65
+
66
+ `https://github.com/finnlabs/openproject-meeting`
67
+
68
+ Credits
69
+ -------
70
+
71
+ Special thanks go to
72
+
73
+ * Deutsche Telekom AG (opensource@telekom.de) for project sponsorhip
74
+ * Le Moign Vincent and his fabulous Minicons icons on [webalys.com](http://www.webalys.com/minicons/icons-free-pack.php)
75
+
76
+ License
77
+ -------
78
+
79
+ (c) 2011 - 2013 - Finn GmbH
80
+
81
+ This plugin is licensed under the GNU GPL v3. See doc/COPYRIGHT.rdoc and
82
+ doc/GPL.txt for details.
@@ -0,0 +1,39 @@
1
+ .meetings div.meeting {padding-bottom: 1em;}
2
+ div.meeting_content {padding-bottom: 2em;}
3
+ div.meetings_by_date {padding: 1em 0 1em 2em;}
4
+ dl.meetings p, div#activity dl.meetings dd, div#activity dl.meetings dt {padding: 0;}
5
+ dl.meetings {margin-bottom: 2em;}
6
+
7
+ div.tabular > div { margin: 0;
8
+ padding: 5px 0 8px 0;
9
+ padding-left: 180px; /*width of left column containing the label elements*/
10
+ height: 1%;
11
+ clear:left;
12
+ }
13
+
14
+ #meeting_agenda_preview fieldset {margin-top: 1em; background: url(<%= asset_path 'draft.png' %>);}
15
+ #meeting_minutes_preview fieldset {margin-top: 1em; background: url(<%= asset_path 'draft.png' %>);}
16
+
17
+ dt.meeting {
18
+ background-image: url(<%= asset_path 'meeting/meeting.png' %>);
19
+ }
20
+
21
+ dt.meeting-agenda {
22
+ background-image: url(<%= asset_path 'meeting/agenda.png' %>);
23
+ }
24
+
25
+ dt.meeting-minutes {
26
+ background-image: url(<%= asset_path 'meeting/minutes.png' %>);
27
+ }
28
+ div#activity dl.meetings dt span {
29
+ padding-left: 15px;
30
+ }
31
+
32
+ div.flash.notice a.link_to_profile {
33
+ cursor:pointer;
34
+ color: #008BD0;
35
+ }
36
+
37
+ div.flash.notice a.link_to_profile:hover {
38
+ text-decoration: underline;
39
+ }
@@ -0,0 +1,33 @@
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
+ class MeetingAgendasController < MeetingContentsController
13
+
14
+ menu_item :meetings
15
+
16
+ def close
17
+ @meeting.close_agenda_and_copy_to_minutes!
18
+
19
+ redirect_back_or_default :controller => '/meetings', :action => 'show', :id => @meeting
20
+ end
21
+
22
+ def open
23
+ @content.unlock!
24
+ redirect_back_or_default :controller => '/meetings', :action => 'show', :id => @meeting
25
+ end
26
+
27
+ private
28
+
29
+ def find_content
30
+ @content = @meeting.agenda || @meeting.build_agenda
31
+ @content_type = "meeting_agenda"
32
+ end
33
+ end
@@ -0,0 +1,95 @@
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
+ class MeetingContentsController < ApplicationController
13
+
14
+ include PaginationHelper
15
+
16
+ menu_item :meetings
17
+
18
+ helper :watchers
19
+ helper :wiki
20
+ helper :meetings
21
+ helper :meeting_contents
22
+ helper :watchers
23
+ helper :meetings
24
+
25
+ before_filter :find_meeting, :find_content
26
+ before_filter :authorize
27
+
28
+ def show
29
+ # Redirect links to the last version
30
+ (redirect_to :controller => '/meetings', :action => :show, :id => @meeting, :tab => @content_type.sub(/^meeting_/, '') and return) if params[:id].present? && @content.version == params[:id].to_i
31
+
32
+ @content = @content.journals.at params[:id].to_i unless params[:id].blank?
33
+ render 'meeting_contents/show'
34
+ end
35
+
36
+ def update
37
+ (render_403; return) unless @content.editable? # TODO: not tested!
38
+ @content.attributes = params[:"#{@content_type}"]
39
+ @content.author = User.current
40
+ if @content.save
41
+ flash[:notice] = l(:notice_successful_update)
42
+ redirect_back_or_default :controller => '/meetings', :action => 'show', :id => @meeting
43
+ else
44
+ end
45
+ rescue ActiveRecord::StaleObjectError
46
+ # Optimistic locking exception
47
+ flash.now[:error] = l(:notice_locking_conflict)
48
+ params[:tab] ||= "minutes" if @meeting.agenda.present? && @meeting.agenda.locked?
49
+ render 'meetings/show'
50
+ end
51
+
52
+ def history
53
+ # don't load text
54
+ @content_versions = @content.journals.select("id, user_id, notes, created_at, version")
55
+ .order('version DESC')
56
+ .page(page_param)
57
+ .per_page(per_page_param)
58
+
59
+ render 'meeting_contents/history', :layout => !request.xhr?
60
+ end
61
+
62
+ def diff
63
+ @diff = @content.diff(params[:version_to], params[:version_from])
64
+ render 'meeting_contents/diff'
65
+ rescue ActiveRecord::RecordNotFound
66
+ render_404
67
+ end
68
+
69
+ def notify
70
+ unless @content.new_record?
71
+ MeetingMailer.content_for_review(@content, @content_type).deliver
72
+ flash[:notice] = l(:notice_successful_notification)
73
+ end
74
+ redirect_back_or_default :controller => '/meetings', :action => 'show', :id => @meeting
75
+ end
76
+
77
+ def preview
78
+ (render_403; return) unless @content.editable?
79
+ @text = params[:text]
80
+ render :partial => 'common/preview'
81
+ end
82
+
83
+ def default_breadcrumb
84
+ MeetingsController.new.send(:default_breadcrumb)
85
+ end
86
+ private
87
+
88
+ def find_meeting
89
+ @meeting = Meeting.find(params[:meeting_id], :include => [:project, :author, :participants, :agenda, :minutes])
90
+ @project = @meeting.project
91
+ @author = User.current
92
+ rescue ActiveRecord::RecordNotFound
93
+ render_404
94
+ end
95
+ end
@@ -0,0 +1,22 @@
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
+ class MeetingMinutesController < MeetingContentsController
13
+
14
+ menu_item :meetings
15
+
16
+ private
17
+
18
+ def find_content
19
+ @content = @meeting.minutes || @meeting.build_minutes
20
+ @content_type = "meeting_minutes"
21
+ end
22
+ end
@@ -0,0 +1,150 @@
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
+ class MeetingsController < ApplicationController
13
+
14
+ around_filter :set_time_zone
15
+ before_filter :find_project, :only => [:index, :new, :create]
16
+ before_filter :find_meeting, :except => [:index, :new, :create]
17
+ before_filter :convert_params, :only => [:create, :update]
18
+ before_filter :authorize
19
+
20
+ helper :journals
21
+ helper :watchers
22
+ helper :meeting_contents
23
+ include WatchersHelper
24
+ include PaginationHelper
25
+
26
+ menu_item :new_meeting, :only => [:new, :create]
27
+
28
+ def index
29
+ scope = @project.meetings
30
+
31
+ # from params => today's page otherwise => first page as fallback
32
+ tomorrows_meetings_count = scope.from_tomorrow.count
33
+ @page_of_today = 1 + tomorrows_meetings_count / per_page_param
34
+
35
+ page = params['page'] ?
36
+ page_param :
37
+ @page_of_today
38
+
39
+ @meetings = scope.with_users_by_date
40
+ .page(page)
41
+ .per_page(per_page_param)
42
+
43
+ @meetings_by_start_year_month_date = Meeting.group_by_time(@meetings)
44
+ end
45
+
46
+ def show
47
+ params[:tab] ||= "minutes" if @meeting.agenda.present? && @meeting.agenda.locked?
48
+ end
49
+
50
+ def create
51
+ @meeting.participants.clear # Start with a clean set of participants
52
+ @meeting.participants_attributes = params[:meeting].delete(:participants_attributes)
53
+ @meeting.attributes = params[:meeting]
54
+ if params[:copied_from_meeting_id].present? && params[:copied_meeting_agenda_text].present?
55
+ @meeting.agenda = MeetingAgenda.new(
56
+ :text => params[:copied_meeting_agenda_text],
57
+ :comment => "Copied from Meeting ##{params[:copied_from_meeting_id]}")
58
+ @meeting.agenda.author = User.current
59
+ end
60
+ if @meeting.save
61
+ text = l(:notice_successful_create)
62
+ if User.current.time_zone.nil?
63
+ link = l(:notice_timezone_missing, :zone => Time.zone)
64
+ text += " #{view_context.link_to(link, {:controller => '/my', :action => :account},:class => "link_to_profile")}"
65
+ end
66
+ flash[:notice] = text.html_safe
67
+
68
+ redirect_to :action => 'show', :id => @meeting
69
+ else
70
+ render :action => 'new', :project_id => @project
71
+ end
72
+ end
73
+
74
+ def new
75
+ end
76
+
77
+ def copy
78
+ params[:copied_from_meeting_id] = @meeting.id
79
+ params[:copied_meeting_agenda_text] = @meeting.agenda.text if @meeting.agenda.present?
80
+ @meeting = @meeting.copy(:author => User.current)
81
+ render :action => 'new', :project_id => @project
82
+ end
83
+
84
+ def destroy
85
+ @meeting.destroy
86
+ flash[:notice] = l(:notice_successful_delete)
87
+ redirect_to :action => 'index', :project_id => @project
88
+ end
89
+
90
+ def edit
91
+ end
92
+
93
+ def update
94
+ @meeting.participants_attributes = params[:meeting].delete(:participants_attributes)
95
+ @meeting.attributes = params[:meeting]
96
+ if @meeting.save
97
+ flash[:notice] = l(:notice_successful_update)
98
+ redirect_to :action => 'show', :id => @meeting
99
+ else
100
+ render :action => 'edit'
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def set_time_zone
107
+ old_time_zone = Time.zone
108
+ zone = User.current.time_zone
109
+ if zone.nil?
110
+ localzone = Time.now.utc_offset
111
+ localzone-= 3600 if Time.now.dst?
112
+ zone = ::ActiveSupport::TimeZone[localzone]
113
+ end
114
+ Time.zone = zone
115
+ yield
116
+ ensure
117
+ Time.zone = old_time_zone
118
+ end
119
+
120
+ def find_project
121
+ @project = Project.find(params[:project_id])
122
+ @meeting = Meeting.new
123
+ @meeting.project = @project
124
+ @meeting.author = User.current
125
+ end
126
+
127
+ def find_meeting
128
+ @meeting = Meeting.find(params[:id], :include => [:project, :author, {:participants => :user}, :agenda, :minutes])
129
+ @project = @meeting.project
130
+ rescue ActiveRecord::RecordNotFound
131
+ render_404
132
+ end
133
+
134
+ def convert_params
135
+ start_date = params[:meeting].delete(:start_date)
136
+ start_time_4i = params[:meeting].delete(:"start_time(4i)")
137
+ start_time_5i = params[:meeting].delete(:"start_time(5i)")
138
+ begin
139
+ timestring = "#{start_date} #{start_time_4i}:#{start_time_5i}"
140
+ time = Time.zone.parse(timestring)
141
+ params[:meeting][:start_time] = time
142
+ rescue ArgumentError
143
+ params[:meeting][:start_time] = nil
144
+ end
145
+ params[:meeting][:duration] = params[:meeting][:duration].to_hours
146
+ # Force defaults on participants
147
+ params[:meeting][:participants_attributes] ||= {}
148
+ params[:meeting][:participants_attributes].each {|p| p.reverse_merge! :attended => false, :invited => false}
149
+ end
150
+ end