diff options
Diffstat (limited to 'site')
26 files changed, 256 insertions, 54 deletions
diff --git a/site/app/controllers/agendas_controller.rb b/site/app/controllers/agendas_controller.rb index 669f364..f94d96c 100644 --- a/site/app/controllers/agendas_controller.rb +++ b/site/app/controllers/agendas_controller.rb @@ -14,7 +14,7 @@ class AgendasController < ApplicationController end def results - data = JSON.parse(request.env["rack.input"].read) + data = JSON.parse(request.env['rack.input'].read) Agenda.update_voting_options data['agenda'] Agenda.process_results data data['votes'] agenda = Agenda.current diff --git a/site/app/controllers/approvals_controller.rb b/site/app/controllers/approvals_controller.rb new file mode 100644 index 0000000..aeead3b --- /dev/null +++ b/site/app/controllers/approvals_controller.rb @@ -0,0 +1,7 @@ +class ApprovalsController < ApplicationController + + hobo_model_controller + + auto_actions :all, :except => [:new, :index] + +end diff --git a/site/app/controllers/voting_options_controller.rb b/site/app/controllers/voting_options_controller.rb index 14ec89f..73a0dfb 100644 --- a/site/app/controllers/voting_options_controller.rb +++ b/site/app/controllers/voting_options_controller.rb @@ -6,7 +6,10 @@ class VotingOptionsController < ApplicationController def community_vote option = VotingOption.find(params[:id]) - unless option.nil? + if option.nil? + flash[:notice] = "No such voting option" + redirect_to :controller => :agendas, :action => :index + else if current_user.signed_up? Vote.vote_for_option(current_user, option, false) flash[:notice] = "You voted for #{option.description}" @@ -14,9 +17,6 @@ class VotingOptionsController < ApplicationController flash[:notice] = "You must be logged in to vote" end redirect_to :controller => :agenda_items, :action => :show, :id => option.agenda_item_id - else - flash[:notice] = "No such voting option" - redirect_to :controller => :agendas, :action => :index end end end diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb index 680773a..4039d0c 100644 --- a/site/app/models/agenda.rb +++ b/site/app/models/agenda.rb @@ -6,12 +6,14 @@ class Agenda < ActiveRecord::Base meeting_time :datetime email_reminder_sent :boolean meeting_log :text + summary :text timestamps end has_many :agenda_items has_many :participations has_many :proxies + has_many :approvals lifecycle do state :open, :default => true @@ -44,7 +46,16 @@ class Agenda < ActiveRecord::Base end def view_permitted?(field) - true + return true unless field == :summary + return true if approvals.count >= 4 + return true if acting_user.council_member? + false + end + + after_update do |agenda| + if agenda.summary_changed? + agenda.approvals.each { |approval| approval.destroy } + end end before_create do |agenda| @@ -66,29 +77,18 @@ class Agenda < ActiveRecord::Base def self.update_voting_options(options) agenda = Agenda.current options.each do |item_info| - item = AgendaItem.first :conditions => { :agenda_id => agenda, :title => item_info.first } - new_descriptions = item_info[1] - old_descriptions = item.voting_options.*.description - - (old_descriptions - new_descriptions).each do |description| - option = VotingOption.first :conditions => { :agenda_item_id => item.id, - :description => description } - option.destroy - end - - (new_descriptions - old_descriptions ).each do |description| - VotingOption.create! :agenda_item => item, :description => description - end + item = AgendaItem.agenda_id_is(agenda).title_is(item_info.first).first + item.update_voting_options(item_info[1]) end end def self.process_results(results) agenda = Agenda.current for item_title in results.keys - item = AgendaItem.first :conditions => { :agenda_id => agenda, :title => item_title } + item = AgendaItem.agenda_id_is(agenda.id).title_is(item_title).first votes = results[item_title] for voter in votes.keys - option = VotingOption.first :conditions => { :agenda_item_id => item.id, :description => votes[voter] } + option = VotingOption.agenda_item_id_is(item.id).description_is(votes[voter]).first user = ::User.find_by_irc_nick voter Vote.vote_for_option(user, option, true) end diff --git a/site/app/models/agenda_item.rb b/site/app/models/agenda_item.rb index 938e2e1..e8874ff 100644 --- a/site/app/models/agenda_item.rb +++ b/site/app/models/agenda_item.rb @@ -56,6 +56,18 @@ class AgendaItem < ActiveRecord::Base return acting_user == user if [nil, :title, :discussion, :body].include?(field) end + def update_voting_options(new_descriptions) + old_descriptions = voting_options.*.description + + (old_descriptions - new_descriptions).each do |description| + option = VotingOption.agenda_item_id_is(id).description_is(description).first + option.destroy + end + + (new_descriptions - old_descriptions ).each do |description| + VotingOption.create! :agenda_item => self, :description => description + end + end protected # Updated discussion time for a single agenda item # protected because we want to call it only from diff --git a/site/app/models/approval.rb b/site/app/models/approval.rb new file mode 100644 index 0000000..923ba97 --- /dev/null +++ b/site/app/models/approval.rb @@ -0,0 +1,26 @@ +require 'permissions/set.rb' + +class Approval < ActiveRecord::Base + + hobo_model # Don't put anything above this + + fields do + timestamps + end + + belongs_to :user, :null => false + belongs_to :agenda, :null => false + + validates_presence_of :user_id, :agenda_id + validates_uniqueness_of :user_id, :scope => :agenda_id + + def view_permitted?(field) + true + end + + multi_permission(:create, :destroy, :update) do + return false unless user_is?(acting_user) + return false unless acting_user.council_member? + true + end +end diff --git a/site/app/models/proxy.rb b/site/app/models/proxy.rb index 00630f8..cb6052d 100644 --- a/site/app/models/proxy.rb +++ b/site/app/models/proxy.rb @@ -50,6 +50,7 @@ class Proxy < ActiveRecord::Base return if council_member.nil? errors.add(:council_member, 'must be council member') unless council_member.council_member? end + def proxy_must_not_be_council_member return if proxy.nil? errors.add(:proxy, 'must not be council member') if proxy.council_member? diff --git a/site/app/models/user.rb b/site/app/models/user.rb index bba0a6e..cbcd021 100644 --- a/site/app/models/user.rb +++ b/site/app/models/user.rb @@ -12,6 +12,10 @@ class User < ActiveRecord::Base end has_many :votes + + validates_presence_of :name, :irc_nick, :email + validates_uniqueness_of :name, :irc_nick, :email + # --- Signup lifecycle --- # lifecycle do @@ -62,15 +66,14 @@ class User < ActiveRecord::Base return 'There were no meetings in this term yet' if agendas.count.zero? for agenda in agendas - if Participation.participant_is(self).agenda_is(agenda).count == 0 + if Participation.participant_is(self).agenda_is(agenda).count.zero? num_status += 1 if num_status < 3 else num_status = 0 if num_status == 1 end end - text_statuses = ['Was on last meeting', 'Skipped last meeting', - 'Slacker', 'No more a council'] + text_statuses = ['Was on last meeting', 'Skipped last meeting', 'Slacker', 'No more a council'] text_statuses[num_status] end diff --git a/site/app/models/vote.rb b/site/app/models/vote.rb index 9307cce..a661c5e 100644 --- a/site/app/models/vote.rb +++ b/site/app/models/vote.rb @@ -13,8 +13,7 @@ class Vote < ActiveRecord::Base index [:voting_option_id, :user_id], :unique => true - validates_presence_of :voting_option - validates_presence_of :user + validates_presence_of :voting_option, :user validates_uniqueness_of :voting_option_id, :scope => :user_id validate :user_voted_only_once # --- Permissions --- # diff --git a/site/app/models/voting_option.rb b/site/app/models/voting_option.rb index 9ece560..78e2fd1 100644 --- a/site/app/models/voting_option.rb +++ b/site/app/models/voting_option.rb @@ -10,7 +10,7 @@ class VotingOption < ActiveRecord::Base belongs_to :agenda_item, :null => false has_many :votes - validates_presence_of :agenda_item + validates_presence_of :agenda_item, :description validates_uniqueness_of :description, :scope => :agenda_item_id def name diff --git a/site/app/views/agendas/show.dryml b/site/app/views/agendas/show.dryml index 40a413e..0471eb4 100644 --- a/site/app/views/agendas/show.dryml +++ b/site/app/views/agendas/show.dryml @@ -13,5 +13,21 @@ <a href="&send(this.second)"><view:first/></a> </collection> </div> + <if test="¤t_user.council_member? and not this.summary.nil? and not this.summary.empty?"> + <form action="&create_approval_path" if="&Approval.user_is(current_user).agenda_is(this).count.zero?"> + <input type="hidden" name="approval[user_id]" value="¤t_user.id"/> + <input type="hidden" name="approval[agenda_id]" value="&this.id"/> + <submit label="approve summary"/> + </form> + <else> + <with with="&Approval.agenda_is(this).user_is(current_user).first"> + <delete-button label="remove your approval for this summary" /> + </with> + </else> + </if> + <unless test="&this.approvals.count.zero?"> + Summary for this agenda was approved by <%= this.approvals.count %> council member(s): + <%= this.approvals.*.user.*.name.join(", ") %>. + </unless> </append-content-body:> </show-page> diff --git a/site/config/hobo_routes.rb b/site/config/hobo_routes.rb index 0e82a10..a223db4 100644 --- a/site/config/hobo_routes.rb +++ b/site/config/hobo_routes.rb @@ -15,6 +15,14 @@ Council::Application.routes.draw do delete 'proxies/:id(.:format)' => 'proxies#destroy', :as => 'destroy_proxy', :constraints => { :id => %r([^/.?]+) } + # Resource routes for controller "approvals" + get 'approvals/:id/edit(.:format)' => 'approvals#edit', :as => 'edit_approval' + get 'approvals/:id(.:format)' => 'approvals#show', :as => 'approval', :constraints => { :id => %r([^/.?]+) } + post 'approvals(.:format)' => 'approvals#create', :as => 'create_approval' + put 'approvals/:id(.:format)' => 'approvals#update', :as => 'update_approval', :constraints => { :id => %r([^/.?]+) } + delete 'approvals/:id(.:format)' => 'approvals#destroy', :as => 'destroy_approval', :constraints => { :id => %r([^/.?]+) } + + # Lifecycle routes for controller "users" post 'users/signup(.:format)' => 'users#do_signup', :as => 'do_user_signup' get 'users/signup(.:format)' => 'users#signup', :as => 'user_signup' diff --git a/site/db/schema.rb b/site/db/schema.rb index 641ac4b..48027f5 100644 --- a/site/db/schema.rb +++ b/site/db/schema.rb @@ -10,13 +10,13 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110711074750) do +ActiveRecord::Schema.define(:version => 20110721195225) do create_table "agenda_items", :force => true do |t| - t.string "title", :default => "", :null => false - t.string "discussion", :default => "", :null => false - t.text "body", :default => "", :null => false - t.boolean "rejected", :default => false, :null => false + t.string "title", :default => "", :null => false + t.string "discussion", :default => "", :null => false + t.text "body", :default => "", :null => false + t.boolean "rejected", :default => false, :null => false t.datetime "created_at" t.datetime "updated_at" t.integer "user_id" @@ -36,10 +36,21 @@ ActiveRecord::Schema.define(:version => 20110711074750) do t.datetime "key_timestamp" t.boolean "email_reminder_sent", :default => false, :null => false t.text "meeting_log", :default => "", :null => false + t.text "summary" end add_index "agendas", ["state"], :name => "index_agendas_on_state" + create_table "approvals", :force => true do |t| + t.datetime "created_at" + t.datetime "updated_at" + t.integer "user_id", :null => false + t.integer "agenda_id", :null => false + end + + add_index "approvals", ["agenda_id"], :name => "index_approvals_on_agenda_id" + add_index "approvals", ["user_id"], :name => "index_approvals_on_user_id" + create_table "delayed_jobs", :force => true do |t| t.integer "priority", :default => 0 t.integer "attempts", :default => 0 diff --git a/site/features/meeting_summary_approvals.feature b/site/features/meeting_summary_approvals.feature new file mode 100644 index 0000000..e49e467 --- /dev/null +++ b/site/features/meeting_summary_approvals.feature @@ -0,0 +1,28 @@ +Feature: Meeting summary approvals + As council member I want to prepare meeting summaries + And I want other council members to approve them + So they will become public only when majority of council approves + + Scenario: Write meeting summary + Given I am logged in as a council member + When I am on the current agenda page + And I follow "Edit" + And I fill in "agenda[summary]" with "some summary" + And I press "Save" + Then I should see "some summary" as summary + + Scenario: Approve meeting summary, then remove approval + Given I am logged in as a council member + When current agenda has a summary + And I am on the current agenda page + And I press "approve summary" + Then I should see "The Approval was created successfully" in the notices + + When I am on the current agenda page + Then I should see "Summary for this agenda was approved by 1 council member(s): Example." + + When I press "remove your approval for this summary" + And I confirm + + When I am on the current agenda page + Then I should not see "Summary for this agenda was approved" diff --git a/site/features/step_definitions/meeting_summary_approvals_steps.rb b/site/features/step_definitions/meeting_summary_approvals_steps.rb new file mode 100644 index 0000000..adf1481 --- /dev/null +++ b/site/features/step_definitions/meeting_summary_approvals_steps.rb @@ -0,0 +1,5 @@ +When /^current agenda has a summary$/ do + agenda = Agenda.current + agenda.summary = 'Summary' + agenda.save! +end diff --git a/site/features/step_definitions/within_steps.rb b/site/features/step_definitions/within_steps.rb index a36a4af..42f2ff6 100644 --- a/site/features/step_definitions/within_steps.rb +++ b/site/features/step_definitions/within_steps.rb @@ -11,6 +11,7 @@ 'as meeting time' => '.meeting-time-view', 'as proxy' => '.collection.proxies.proxies-collection', 'as the user nick' => '.user-irc-nick', + 'as summary' => '.agenda-summary', 'as voting option' => '.collection.voting-options', 'as voting option description' => '.voting-option-description' }. diff --git a/site/spec/factories.rb b/site/spec/factories.rb index 01a69f7..2664917 100644 --- a/site/spec/factories.rb +++ b/site/spec/factories.rb @@ -32,4 +32,9 @@ Factory.define :proxy do |p|; p.agenda {Factory(:agenda)} end +Factory.define :approval do |a|; + a.user {users_factory(:council)} + a.agenda {Agenda.current} +end + require File.expand_path("../support/users_factory.rb", __FILE__) diff --git a/site/spec/models/agenda_item_spec.rb b/site/spec/models/agenda_item_spec.rb index 961b663..fe4f906 100644 --- a/site/spec/models/agenda_item_spec.rb +++ b/site/spec/models/agenda_item_spec.rb @@ -52,7 +52,7 @@ describe AgendaItem do owner = Factory(:user) agenda = Factory(:agenda, :state => 'old') a = Factory(:agenda_item, :user => owner, :agenda => agenda) - for u in users_factory(AllRoles) + [owner] + for u in users_factory(:all_roles) + [owner] a.should_not be_editable_by(u) a.should_not be_updatable_by(u) end diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb index f0dc747..738db63 100644 --- a/site/spec/models/agenda_spec.rb +++ b/site/spec/models/agenda_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Agenda do it 'shouldn not allow anyone to create' do a = Agenda.new - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) a.should_not be_creatable_by(u) a.should_not be_destroyable_by(u) end @@ -11,18 +11,32 @@ describe Agenda do it 'shouldn not allow anyone to destory' do a = Factory(:agenda) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) a.should_not be_destroyable_by(u) end end it 'should allow everybody to view' do a = Factory(:agenda) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) a.should be_viewable_by(u) end end + it 'should allow everybody to view summaries after 4 council members approved it' do + agenda = Agenda.current + + for u in users_factory(:guest, :user, :admin) + agenda.should_not be_viewable_by(u, :summary) + end + + (1..4).each { |i| Factory(:approval, :agenda => agenda) } + + for u in users_factory(:all_roles) + agenda.should be_viewable_by(u, :summary) + end + end + it 'should allow only administrators and council members to edit and update' do a = Factory(:agenda) for u in users_factory(:guest, :user) @@ -35,12 +49,23 @@ describe Agenda do a = Factory(:agenda) a.meeting_log = 'changed' - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) a.should_not be_editable_by(u, :meeting_log) a.should_not be_updatable_by(u) end end + it 'should allow council memebers to change summaries of old meetings' do + a = Factory(:agenda, :state => 'old') + a.summary = 'changed' + + for u in users_factory(:council, :council_admin) + a.should be_editable_by(u) + a.should be_editable_by(u, :summary) + a.should be_updatable_by(u) + end + end + def test_migration(object, migration, prohibited, allowed, final_state) # object - object to migrate # migration - migration name @@ -299,4 +324,12 @@ describe Agenda do VotingOption.last.description.should == 'new option' end end + + it 'should remove approvals for summary, when summary changes' do + agenda = Agenda.current + Factory(:approval, :agenda => agenda) + agenda.summary = 'changed' + agenda.save! + Approval.count.should be_zero + end end diff --git a/site/spec/models/approval_spec.rb b/site/spec/models/approval_spec.rb new file mode 100644 index 0000000..701504e --- /dev/null +++ b/site/spec/models/approval_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Approval do + it 'should be viewable by everybody' do + approval = Factory(:approval) + for user in users_factory(:all_roles) + approval.should be_viewable_by(user) + end + end + + it 'only council members should be able to change it - and only for themselves' do + for user in users_factory(:council, :council_admin) + approval = Factory(:approval, :user => user) + approval.should be_creatable_by(user) + approval.should be_editable_by(user) + approval.should be_updatable_by(user) + approval.should be_destroyable_by(user) + end + + approval = Factory(:approval) + for user in users_factory(:council, :council_admin) + approval.should_not be_creatable_by(user) + approval.should_not be_editable_by(user) + approval.should_not be_updatable_by(user) + approval.should_not be_destroyable_by(user) + end + + for user in users_factory(:user, :admin) + approval = Approval.new :user => user, :agenda => Agenda.current + approval.should_not be_creatable_by(user) + approval.should_not be_editable_by(user) + approval.should_not be_updatable_by(user) + approval.should_not be_destroyable_by(user) + end + end +end diff --git a/site/spec/models/participation_spec.rb b/site/spec/models/participation_spec.rb index 395dc03..75485e6 100644 --- a/site/spec/models/participation_spec.rb +++ b/site/spec/models/participation_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Participation do it 'should not allow anyone to create, edit, update or destroy' do p = Factory(:participation) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) p.should_not be_creatable_by(u) p.should_not be_editable_by(u) p.should_not be_updatable_by(u) @@ -13,7 +13,7 @@ describe Participation do it 'should allow everybody to view' do p = Factory(:participation) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) p.should be_viewable_by(u) end end diff --git a/site/spec/models/proxy_spec.rb b/site/spec/models/proxy_spec.rb index a94aa06..995ebb1 100644 --- a/site/spec/models/proxy_spec.rb +++ b/site/spec/models/proxy_spec.rb @@ -63,7 +63,7 @@ describe Proxy do it 'should allow no one to update or edit' do p = Factory(:proxy) - for u in users_factory(AllRoles) + [p.council_member, p.proxy] + for u in users_factory(:all_roles) + [p.council_member, p.proxy] p.should_not be_editable_by(u) p.should_not be_updatable_by(u) end @@ -72,7 +72,7 @@ describe Proxy do it 'should allow everyone to view' do p = Factory(:proxy) - for u in users_factory(AllRoles) + [p.council_member, p.proxy] + for u in users_factory(:all_roles) + [p.council_member, p.proxy] p.should be_viewable_by(u) end end @@ -92,7 +92,7 @@ describe Proxy do it 'should not allow users to destoy someone else proxy' do a = Factory(:agenda) p = Factory(:proxy, :agenda => a) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) p.should_not be_destroyable_by(u) end end diff --git a/site/spec/models/user_spec.rb b/site/spec/models/user_spec.rb index ad36761..c9f36b9 100644 --- a/site/spec/models/user_spec.rb +++ b/site/spec/models/user_spec.rb @@ -90,7 +90,7 @@ describe User do end it 'should allow no one to create' do - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) User.new.should_not be_creatable_by(u) end end @@ -106,8 +106,8 @@ describe User do end it 'should allow everybody to view' do - for u1 in users_factory(AllRoles) - for u2 in users_factory(AllRoles - [:guest]) + for u1 in users_factory(:all_roles) + for u2 in users_factory(:registered) u2.should be_viewable_by(u1) end end diff --git a/site/spec/models/vote_spec.rb b/site/spec/models/vote_spec.rb index f2d529e..5f53a69 100644 --- a/site/spec/models/vote_spec.rb +++ b/site/spec/models/vote_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Vote do it 'should allow anyone to create, update and destroy their own votes' do - for u in users_factory(AllRoles - [:guest]) do + for u in users_factory(:registered) do vote = Factory(:vote, :user => u) vote.should be_creatable_by(u) vote.should be_updatable_by(u) @@ -12,7 +12,7 @@ describe Vote do it 'should not allow anyone to create, update and destroy vote of someone else' do vote = Factory(:vote) - for u in users_factory(AllRoles) do + for u in users_factory(:all_roles) do vote.should_not be_creatable_by(u) vote.should_not be_updatable_by(u) vote.should_not be_destroyable_by(u) @@ -21,13 +21,13 @@ describe Vote do it 'should allow anyone to view' do vote = Factory(:vote) - for u in users_factory(AllRoles) do + for u in users_factory(:all_roles) do vote.should be_viewable_by(u) end end it 'should allow all users to vote' do - for u in users_factory(AllRoles - [:guest]) do + for u in users_factory(:registered) do Vote.new(:user => u, :voting_option => Factory(:voting_option)).should be_valid end end @@ -39,7 +39,7 @@ describe Vote do end it 'should prevent users from setting council_vote to true' do - for u in users_factory(AllRoles - [:guest]) + for u in users_factory(:registered) v = Factory(:vote, :user => u, :council_vote => true) v.should_not be_editable_by(u) v.should_not be_updatable_by(u) diff --git a/site/spec/models/voting_option_spec.rb b/site/spec/models/voting_option_spec.rb index 87074c6..5054f43 100644 --- a/site/spec/models/voting_option_spec.rb +++ b/site/spec/models/voting_option_spec.rb @@ -31,7 +31,7 @@ describe VotingOption do a2 = Factory(:agenda, :state => 'old') i2 = Factory(:agenda_item, :agenda => a2) v2 = Factory(:voting_option, :agenda_item => i2) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) v1.should_not be_updatable_by(u) v1.should_not be_destroyable_by(u) v2.should_not be_updatable_by(u) @@ -41,7 +41,7 @@ describe VotingOption do it 'should allow everyone to view' do v = Factory(:voting_option) - for u in users_factory(AllRoles) + for u in users_factory(:all_roles) v.should be_viewable_by(u) end end diff --git a/site/spec/support/users_factory.rb b/site/spec/support/users_factory.rb index 40c715b..126a7b9 100644 --- a/site/spec/support/users_factory.rb +++ b/site/spec/support/users_factory.rb @@ -1,6 +1,19 @@ def users_factory(*roles) roles.flatten! + roles.collect! do |role| + case role + when :all_roles + [:guest, :user, :council, :admin, :council_admin] + when :registered + [:user, :council, :admin, :council_admin] + else + role + end + end + roles.flatten! + r = [] + roles for role in roles case role when :guest @@ -17,5 +30,3 @@ def users_factory(*roles) end (r.count < 2) ? r.first : r end - -AllRoles = [:guest, :user, :council, :admin, :council_admin] |