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
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