1 |
commit: 1a15725aad91a3b16928cb43f608379d6ade13e3 |
2 |
Author: Joachim Filip Ignacy Bartosik <jbartosik <AT> gmail <DOT> com> |
3 |
AuthorDate: Thu Jul 21 16:42:07 2011 +0000 |
4 |
Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Jul 26 10:09:47 2011 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/council-webapp.git;a=commit;h=1a15725a |
7 |
|
8 |
Council members can approve summaries |
9 |
|
10 |
Regular users can view summary only after 4 council members approve it. |
11 |
When someone changes summary all approvals for that summary are deleted. |
12 |
|
13 |
--- |
14 |
site/app/controllers/approvals_controller.rb | 7 ++++ |
15 |
site/app/models/agenda.rb | 12 ++++++- |
16 |
site/app/models/approval.rb | 27 +++++++++++++++ |
17 |
site/app/views/agendas/show.dryml | 16 +++++++++ |
18 |
site/config/hobo_routes.rb | 8 ++++ |
19 |
site/db/schema.rb | 12 ++++++- |
20 |
site/features/meeting_summary_approvals.feature | 28 +++++++++++++++ |
21 |
.../meeting_summary_approvals_steps.rb | 5 +++ |
22 |
site/features/step_definitions/within_steps.rb | 1 + |
23 |
site/spec/factories.rb | 5 +++ |
24 |
site/spec/models/agenda_spec.rb | 22 ++++++++++++ |
25 |
site/spec/models/approval_spec.rb | 36 ++++++++++++++++++++ |
26 |
12 files changed, 177 insertions(+), 2 deletions(-) |
27 |
|
28 |
diff --git a/site/app/controllers/approvals_controller.rb b/site/app/controllers/approvals_controller.rb |
29 |
new file mode 100644 |
30 |
index 0000000..aeead3b |
31 |
--- /dev/null |
32 |
+++ b/site/app/controllers/approvals_controller.rb |
33 |
@@ -0,0 +1,7 @@ |
34 |
+class ApprovalsController < ApplicationController |
35 |
+ |
36 |
+ hobo_model_controller |
37 |
+ |
38 |
+ auto_actions :all, :except => [:new, :index] |
39 |
+ |
40 |
+end |
41 |
|
42 |
diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb |
43 |
index bf38838..75a9a23 100644 |
44 |
--- a/site/app/models/agenda.rb |
45 |
+++ b/site/app/models/agenda.rb |
46 |
@@ -13,6 +13,7 @@ class Agenda < ActiveRecord::Base |
47 |
has_many :agenda_items |
48 |
has_many :participations |
49 |
has_many :proxies |
50 |
+ has_many :approvals |
51 |
|
52 |
lifecycle do |
53 |
state :open, :default => true |
54 |
@@ -45,7 +46,16 @@ class Agenda < ActiveRecord::Base |
55 |
end |
56 |
|
57 |
def view_permitted?(field) |
58 |
- true |
59 |
+ return true unless field == :summary |
60 |
+ return true if approvals.count >= 4 |
61 |
+ return true if acting_user.council_member? |
62 |
+ false |
63 |
+ end |
64 |
+ |
65 |
+ after_update do |agenda| |
66 |
+ if agenda.summary_changed? |
67 |
+ agenda.approvals.each { |approval| approval.destroy } |
68 |
+ end |
69 |
end |
70 |
|
71 |
before_create do |agenda| |
72 |
|
73 |
diff --git a/site/app/models/approval.rb b/site/app/models/approval.rb |
74 |
new file mode 100644 |
75 |
index 0000000..d36232c |
76 |
--- /dev/null |
77 |
+++ b/site/app/models/approval.rb |
78 |
@@ -0,0 +1,27 @@ |
79 |
+require 'permissions/set.rb' |
80 |
+ |
81 |
+class Approval < ActiveRecord::Base |
82 |
+ |
83 |
+ hobo_model # Don't put anything above this |
84 |
+ |
85 |
+ fields do |
86 |
+ timestamps |
87 |
+ end |
88 |
+ |
89 |
+ belongs_to :user, :null => false |
90 |
+ belongs_to :agenda, :null => false |
91 |
+ |
92 |
+ validates_presence_of :user_id |
93 |
+ validates_presence_of :agenda_id |
94 |
+ validates_uniqueness_of :user_id, :scope => :agenda_id |
95 |
+ |
96 |
+ def view_permitted?(field) |
97 |
+ true |
98 |
+ end |
99 |
+ |
100 |
+ multi_permission(:create, :destroy, :update) do |
101 |
+ return false unless user_is?(acting_user) |
102 |
+ return false unless acting_user.council_member? |
103 |
+ true |
104 |
+ end |
105 |
+end |
106 |
|
107 |
diff --git a/site/app/views/agendas/show.dryml b/site/app/views/agendas/show.dryml |
108 |
index 40a413e..0471eb4 100644 |
109 |
--- a/site/app/views/agendas/show.dryml |
110 |
+++ b/site/app/views/agendas/show.dryml |
111 |
@@ -13,5 +13,21 @@ |
112 |
<a href="&send(this.second)"><view:first/></a> |
113 |
</collection> |
114 |
</div> |
115 |
+ <if test="¤t_user.council_member? and not this.summary.nil? and not this.summary.empty?"> |
116 |
+ <form action="&create_approval_path" if="&Approval.user_is(current_user).agenda_is(this).count.zero?"> |
117 |
+ <input type="hidden" name="approval[user_id]" value="¤t_user.id"/> |
118 |
+ <input type="hidden" name="approval[agenda_id]" value="&this.id"/> |
119 |
+ <submit label="approve summary"/> |
120 |
+ </form> |
121 |
+ <else> |
122 |
+ <with with="&Approval.agenda_is(this).user_is(current_user).first"> |
123 |
+ <delete-button label="remove your approval for this summary" /> |
124 |
+ </with> |
125 |
+ </else> |
126 |
+ </if> |
127 |
+ <unless test="&this.approvals.count.zero?"> |
128 |
+ Summary for this agenda was approved by <%= this.approvals.count %> council member(s): |
129 |
+ <%= this.approvals.*.user.*.name.join(", ") %>. |
130 |
+ </unless> |
131 |
</append-content-body:> |
132 |
</show-page> |
133 |
|
134 |
diff --git a/site/config/hobo_routes.rb b/site/config/hobo_routes.rb |
135 |
index a426e53..97dced8 100644 |
136 |
--- a/site/config/hobo_routes.rb |
137 |
+++ b/site/config/hobo_routes.rb |
138 |
@@ -15,6 +15,14 @@ Council::Application.routes.draw do |
139 |
delete 'proxies/:id(.:format)' => 'proxies#destroy', :as => 'destroy_proxy', :constraints => { :id => %r([^/.?]+) } |
140 |
|
141 |
|
142 |
+ # Resource routes for controller "approvals" |
143 |
+ get 'approvals/:id/edit(.:format)' => 'approvals#edit', :as => 'edit_approval' |
144 |
+ get 'approvals/:id(.:format)' => 'approvals#show', :as => 'approval', :constraints => { :id => %r([^/.?]+) } |
145 |
+ post 'approvals(.:format)' => 'approvals#create', :as => 'create_approval' |
146 |
+ put 'approvals/:id(.:format)' => 'approvals#update', :as => 'update_approval', :constraints => { :id => %r([^/.?]+) } |
147 |
+ delete 'approvals/:id(.:format)' => 'approvals#destroy', :as => 'destroy_approval', :constraints => { :id => %r([^/.?]+) } |
148 |
+ |
149 |
+ |
150 |
# Lifecycle routes for controller "users" |
151 |
post 'users/signup(.:format)' => 'users#do_signup', :as => 'do_user_signup' |
152 |
get 'users/signup(.:format)' => 'users#signup', :as => 'user_signup' |
153 |
|
154 |
diff --git a/site/db/schema.rb b/site/db/schema.rb |
155 |
index a6232e1..48027f5 100644 |
156 |
--- a/site/db/schema.rb |
157 |
+++ b/site/db/schema.rb |
158 |
@@ -10,7 +10,7 @@ |
159 |
# |
160 |
# It's strongly recommended to check this file into your version control system. |
161 |
|
162 |
-ActiveRecord::Schema.define(:version => 20110721103758) do |
163 |
+ActiveRecord::Schema.define(:version => 20110721195225) do |
164 |
|
165 |
create_table "agenda_items", :force => true do |t| |
166 |
t.string "title", :default => "", :null => false |
167 |
@@ -41,6 +41,16 @@ ActiveRecord::Schema.define(:version => 20110721103758) do |
168 |
|
169 |
add_index "agendas", ["state"], :name => "index_agendas_on_state" |
170 |
|
171 |
+ create_table "approvals", :force => true do |t| |
172 |
+ t.datetime "created_at" |
173 |
+ t.datetime "updated_at" |
174 |
+ t.integer "user_id", :null => false |
175 |
+ t.integer "agenda_id", :null => false |
176 |
+ end |
177 |
+ |
178 |
+ add_index "approvals", ["agenda_id"], :name => "index_approvals_on_agenda_id" |
179 |
+ add_index "approvals", ["user_id"], :name => "index_approvals_on_user_id" |
180 |
+ |
181 |
create_table "delayed_jobs", :force => true do |t| |
182 |
t.integer "priority", :default => 0 |
183 |
t.integer "attempts", :default => 0 |
184 |
|
185 |
diff --git a/site/features/meeting_summary_approvals.feature b/site/features/meeting_summary_approvals.feature |
186 |
new file mode 100644 |
187 |
index 0000000..e49e467 |
188 |
--- /dev/null |
189 |
+++ b/site/features/meeting_summary_approvals.feature |
190 |
@@ -0,0 +1,28 @@ |
191 |
+Feature: Meeting summary approvals |
192 |
+ As council member I want to prepare meeting summaries |
193 |
+ And I want other council members to approve them |
194 |
+ So they will become public only when majority of council approves |
195 |
+ |
196 |
+ Scenario: Write meeting summary |
197 |
+ Given I am logged in as a council member |
198 |
+ When I am on the current agenda page |
199 |
+ And I follow "Edit" |
200 |
+ And I fill in "agenda[summary]" with "some summary" |
201 |
+ And I press "Save" |
202 |
+ Then I should see "some summary" as summary |
203 |
+ |
204 |
+ Scenario: Approve meeting summary, then remove approval |
205 |
+ Given I am logged in as a council member |
206 |
+ When current agenda has a summary |
207 |
+ And I am on the current agenda page |
208 |
+ And I press "approve summary" |
209 |
+ Then I should see "The Approval was created successfully" in the notices |
210 |
+ |
211 |
+ When I am on the current agenda page |
212 |
+ Then I should see "Summary for this agenda was approved by 1 council member(s): Example." |
213 |
+ |
214 |
+ When I press "remove your approval for this summary" |
215 |
+ And I confirm |
216 |
+ |
217 |
+ When I am on the current agenda page |
218 |
+ Then I should not see "Summary for this agenda was approved" |
219 |
|
220 |
diff --git a/site/features/step_definitions/meeting_summary_approvals_steps.rb b/site/features/step_definitions/meeting_summary_approvals_steps.rb |
221 |
new file mode 100644 |
222 |
index 0000000..adf1481 |
223 |
--- /dev/null |
224 |
+++ b/site/features/step_definitions/meeting_summary_approvals_steps.rb |
225 |
@@ -0,0 +1,5 @@ |
226 |
+When /^current agenda has a summary$/ do |
227 |
+ agenda = Agenda.current |
228 |
+ agenda.summary = 'Summary' |
229 |
+ agenda.save! |
230 |
+end |
231 |
|
232 |
diff --git a/site/features/step_definitions/within_steps.rb b/site/features/step_definitions/within_steps.rb |
233 |
index a36a4af..42f2ff6 100644 |
234 |
--- a/site/features/step_definitions/within_steps.rb |
235 |
+++ b/site/features/step_definitions/within_steps.rb |
236 |
@@ -11,6 +11,7 @@ |
237 |
'as meeting time' => '.meeting-time-view', |
238 |
'as proxy' => '.collection.proxies.proxies-collection', |
239 |
'as the user nick' => '.user-irc-nick', |
240 |
+ 'as summary' => '.agenda-summary', |
241 |
'as voting option' => '.collection.voting-options', |
242 |
'as voting option description' => '.voting-option-description' |
243 |
}. |
244 |
|
245 |
diff --git a/site/spec/factories.rb b/site/spec/factories.rb |
246 |
index 01a69f7..2664917 100644 |
247 |
--- a/site/spec/factories.rb |
248 |
+++ b/site/spec/factories.rb |
249 |
@@ -32,4 +32,9 @@ Factory.define :proxy do |p|; |
250 |
p.agenda {Factory(:agenda)} |
251 |
end |
252 |
|
253 |
+Factory.define :approval do |a|; |
254 |
+ a.user {users_factory(:council)} |
255 |
+ a.agenda {Agenda.current} |
256 |
+end |
257 |
+ |
258 |
require File.expand_path("../support/users_factory.rb", __FILE__) |
259 |
|
260 |
diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb |
261 |
index 00165d1..b9e564c 100644 |
262 |
--- a/site/spec/models/agenda_spec.rb |
263 |
+++ b/site/spec/models/agenda_spec.rb |
264 |
@@ -23,6 +23,20 @@ describe Agenda do |
265 |
end |
266 |
end |
267 |
|
268 |
+ it 'should allow everybody to view summaries after 4 council members approved it' do |
269 |
+ agenda = Agenda.current |
270 |
+ |
271 |
+ for u in users_factory(:guest, :user, :admin) |
272 |
+ agenda.should_not be_viewable_by(u, :summary) |
273 |
+ end |
274 |
+ |
275 |
+ (1..4).each { |i| Factory(:approval, :agenda => agenda) } |
276 |
+ |
277 |
+ for u in users_factory(AllRoles) |
278 |
+ agenda.should be_viewable_by(u, :summary) |
279 |
+ end |
280 |
+ end |
281 |
+ |
282 |
it 'should allow only administrators and council members to edit and update' do |
283 |
a = Factory(:agenda) |
284 |
for u in users_factory(:guest, :user) |
285 |
@@ -310,4 +324,12 @@ describe Agenda do |
286 |
VotingOption.last.description.should == 'new option' |
287 |
end |
288 |
end |
289 |
+ |
290 |
+ it 'should remove approvals for summary, when summary changes' do |
291 |
+ agenda = Agenda.current |
292 |
+ Factory(:approval, :agenda => agenda) |
293 |
+ agenda.summary = 'changed' |
294 |
+ agenda.save! |
295 |
+ Approval.count.should be_zero |
296 |
+ end |
297 |
end |
298 |
|
299 |
diff --git a/site/spec/models/approval_spec.rb b/site/spec/models/approval_spec.rb |
300 |
new file mode 100644 |
301 |
index 0000000..4057a6d |
302 |
--- /dev/null |
303 |
+++ b/site/spec/models/approval_spec.rb |
304 |
@@ -0,0 +1,36 @@ |
305 |
+require 'spec_helper' |
306 |
+ |
307 |
+describe Approval do |
308 |
+ it 'should be viewable by everybody' do |
309 |
+ approval = Factory(:approval) |
310 |
+ for user in users_factory(AllRoles) |
311 |
+ approval.should be_viewable_by(user) |
312 |
+ end |
313 |
+ end |
314 |
+ |
315 |
+ it 'only council members should be able to change it - and only for themselves' do |
316 |
+ for user in users_factory(:council, :council_admin) |
317 |
+ approval = Factory(:approval, :user => user) |
318 |
+ approval.should be_creatable_by(user) |
319 |
+ approval.should be_editable_by(user) |
320 |
+ approval.should be_updatable_by(user) |
321 |
+ approval.should be_destroyable_by(user) |
322 |
+ end |
323 |
+ |
324 |
+ approval = Factory(:approval) |
325 |
+ for user in users_factory(:council, :council_admin) |
326 |
+ approval.should_not be_creatable_by(user) |
327 |
+ approval.should_not be_editable_by(user) |
328 |
+ approval.should_not be_updatable_by(user) |
329 |
+ approval.should_not be_destroyable_by(user) |
330 |
+ end |
331 |
+ |
332 |
+ for user in users_factory(:user, :admin) |
333 |
+ approval = Approval.new :user => user, :agenda => Agenda.current |
334 |
+ approval.should_not be_creatable_by(user) |
335 |
+ approval.should_not be_editable_by(user) |
336 |
+ approval.should_not be_updatable_by(user) |
337 |
+ approval.should_not be_destroyable_by(user) |
338 |
+ end |
339 |
+ end |
340 |
+end |