]> projects.mako.cc - selectricity-live/blob - app/controllers/election_controller.rb
0b529b6f4630d76d8a7f6920c36bf24c15684d7c
[selectricity-live] / app / controllers / election_controller.rb
1 # Selectricity: Voting Machinery for the Masses
2 # Copyright (C) 2007, 2008 Benjamin Mako Hill <mako@atdot.cc>
3 # Copyright (C) 2007 Massachusetts Institute of Technology
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Affero General Public License for more details.
14 #
15 # You should have received a copy of the GNU Affero General Public
16 # License along with this program.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 class ElectionController < ApplicationController
20   require_dependency "raw_voter_list"
21   require_dependency "voter"
22   require_dependency "vote"
23   require_dependency "candidate"
24   layout 'main'
25
26   ## methods for displaying, creating,
27   ## and manipulating election overview data
28   ####################################################################
29
30   def new
31     redirect_to :action => 'general_information'
32   end
33   
34   def general_information
35     @sidebar_content = render_to_string :partial => 'progress',
36                                         :locals => { :page => 'overview' }
37     @election = Election.new
38     render :action => 'general_information'
39   end
40   
41   def create_election
42     @election = Election.new(params[:election])
43     
44     # default options
45     @election.user = session[:user]
46     @election.anonymous = 1
47     @election.startdate = Time.now
48     
49     unless params[:top_bar][:uploaded_data].to_s.empty?
50       token_generator = UniqueTokenGenerator.new( 16 )
51       @election.embed_custom_string = token_generator.token
52       add_theme(@election.embed_custom_string)
53     end
54     
55     if @election.save
56       flash[:notice] = 'Election was successfully created.'
57       redirect_to :action => 'edit_candidates', :id => @election.id
58     else
59       render :action => 'general_information'
60     end
61   end
62   
63   # TODO add filter to verify that the person working on or looking at
64   # something is the owner
65   def edit_general_information
66     @election = Election.find(params[:id])
67   end
68   
69   def update_general_information
70     @election = Election.find(params[:id])
71     
72     unless (params[:top_bar][:uploaded_data].to_s.empty? and params[:default_image][:uploaded_data].to_s.empty? and params[:bg1][:uploaded_data].to_s.empty? and params[:bg2][:uploaded_data].to_s.empty? and params[:bottom_bar][:uploaded_data].to_s.empty?)
73       unless @election.embed_custom_string
74         token_generator = UniqueTokenGenerator.new( 16 )
75         @election.embed_custom_string = token_generator.token
76       end
77       
78       add_theme(@election.embed_custom_string)
79     end
80     
81     if @election.update_attributes(params[:election])
82       flash[:notice] = 'Election was successfully updated.'
83       redirect_to :action => 'show', :id => @election
84     else
85       render :action => 'edit'
86     end
87   end
88   
89   def add_theme(prefix)
90     unless params[:top_bar][:uploaded_data].to_s.empty?
91       previous = SkinPicture.find(:first,
92       :conditions => ["filename = ?", @election.embed_custom_string + "top_bar.png"])
93       if previous
94         previous.destroy
95       end
96       top_bar = SkinPicture.new(params[:top_bar])
97       top_bar.filename = prefix + "top_bar." + params[:top_bar][:uploaded_data].content_type[6..-2]
98       top_bar.save
99     end
100     unless params[:default_image][:uploaded_data].to_s.empty?
101       previous = SkinPicture.find(:first,
102       :conditions => ["filename = ?", @election.embed_custom_string + "default_image.png"])
103       if previous
104         previous.destroy
105       end
106       default_image = SkinPicture.new(params[:default_image])
107       default_image.filename = prefix + "default_image." + params[:default_image][:uploaded_data].content_type[6..-2]
108       default_image.save
109     end
110     unless params[:bg1][:uploaded_data].to_s.empty?
111       previous = SkinPicture.find(:first,
112       :conditions => ["filename = ?", @election.embed_custom_string + "bg1.png"])
113       if previous
114         previous.destroy
115       end
116       bg1 = SkinPicture.new(params[:bg1])  
117       bg1.filename = prefix + "bg1." + params[:bg1][:uploaded_data].content_type[6..-2]
118       bg1.save
119     end
120     unless params[:bg2][:uploaded_data].to_s.empty?
121       previous = SkinPicture.find(:first,
122       :conditions => ["filename = ?", @election.embed_custom_string + "bg2.png"])
123       if previous
124         previous.destroy
125       end
126       bg2 = SkinPicture.new(params[:bg2]) 
127       bg2.filename = prefix + "bg2." + params[:bg2][:uploaded_data].content_type[6..-2]
128       bg2.save
129     end
130     unless params[:bottom_bar][:uploaded_data].to_s.empty?
131       previous = SkinPicture.find(:first,
132       :conditions => ["filename = ?", @election.embed_custom_string + "bottom_bar.png"])
133       if previous
134         previous.destroy
135       end
136       bottom_bar = SkinPicture.new(params[:bottom_bar])
137       bottom_bar.filename = prefix + "bottom_bar." + params[:bottom_bar][:uploaded_data].content_type[6..-2]
138       bottom_bar.save
139     end
140         
141   end
142   
143   def show
144     @sidebar_content = render_to_string :partial => 'progress',
145                                         :locals => { :page => 'review' }
146
147     @election = Election.find(params[:id])
148     if @election.type == QuickVote
149       redirect_to(:controller => 'quickvote', :action => 'index', :ident => @election.id)
150     end
151       
152   end
153
154   def start_election
155     @election = Election.find(params[:id])
156     
157     @election.voters.each do |voter|
158       voter.vote = Vote.new
159       email_voter voter unless voter.email.nil?
160     end
161
162     @election.activate!
163     redirect_to :action => 'show', :id => @election.id
164   end
165
166   # methods fod display, adding, deleting, and manipulating candidate
167   # information for elections
168   ####################################################################
169   def edit_candidates
170     @sidebar_content = render_to_string :partial => 'progress',
171                                         :locals => { :page => 'candidates' }
172     @election = Election.find( params[:id] )
173   end
174
175   def add_candidate
176     @election = Election.find(params[:id])
177     @candidate = Candidate.new(params[:candidate])
178     @election.candidates << @candidate
179
180     if @candidate.save
181       # check to see if they've uploaded a picture
182       if params[:picture][:uploaded_data]
183         picture = Picture.new(params[:picture])
184         @candidate.picture = picture if picture.save
185       end
186
187       @candidate = Candidate.new
188       redirect_to :action => 'edit_candidates', :id => @election.id
189     else
190       render :action => 'edit_candidates', :id => @election.id
191     end
192   end
193   
194   def delete_candidate
195     candidate = Candidate.find( params[:id] )
196     candidate.destroy
197   end
198
199   def candidate_picture
200     candidate = Candidate.find( params[:id] )
201     send_data( candidate.picture.data,
202                :filename => candidate.picture.filename,
203                :type => candidate.picture.filetype,
204                :disposition => 'inline' )
205   end
206
207   ## methods for displaying, adding, deleting, and manipulating voters
208   ## for a particular election
209   ####################################################################
210   def new_voters
211     redirect_to :action => 'edit_voters', :id => params[:id]
212   end
213   
214   def edit_voters
215     @sidebar_content = render_to_string :partial => 'progress',
216                                         :locals => { :page => 'voters' }
217
218     @election = Election.find( params[:id] )
219     if params.has_key?( :raw_voter_list )
220       process_incoming_voters( params[:raw_voter_list] )
221     end
222     @raw_voter_list = RawVoterList.new
223   end
224   
225   def delete_voter
226     voter = Voter.find( params[:id] )
227     voter.destroy
228   end
229
230   def toggle_authenticated
231     @election = Election.find(params[:id])
232     if params[:authenticated] == "1"
233       @election.authenticated = true
234     else
235       @election.authenticated = false
236     end
237     @election.save
238   end
239   
240   ## methods for computing and printing results
241   ####################################################################
242   def results
243     @election = Election.find( params[:id] )
244     votes = []
245     
246     @election.voters.each do |voter|
247       if voter.vote and voter.vote.confirmed?
248         votes << voter.vote.rankings.sort.collect {|vote| vote.candidate_id}
249       end
250     end
251     
252     @voteobj = CloneproofSSDVote.new(votes)
253     @resultobj = @voteobj.result
254     @winners = @resultobj.winners
255     
256     @candidates_by_id = {}
257     @election.candidates.each {|cand| @candidates_by_id[cand.id] = cand}
258     
259   end
260   
261   def detailed_results
262    
263     self.results
264
265     @voter_list = []
266     @vote_list = []
267     
268     @election.voters.each do |voter|
269       if voter.vote and voter.vote.confirmed?
270         @voter_list << voter.email
271               @vote_list << voter.vote
272       end
273     end
274
275     @vote_list.sort!
276     @vote_list.sort! { |a,b| a.token <=> b.token }
277   end
278
279   ## private methods
280   ####################################################################
281   private
282
283     def process_incoming_voters(raw_voter_list)
284       incoming_voters = RawVoterList.new( raw_voter_list )
285
286       unless incoming_voters.entries.empty?
287         incoming_voters.each do |new_voter|
288           new_voter.email.strip! # There's a trailing \r on all but the last in
289                                  # the list!
290           if new_voter.valid?
291             # the new voter should be in good shape. save add to the election
292             @election.voters << new_voter
293                   new_voter.save
294           end
295           # TODO: Can we do some kind of AJAX error message for the voter being invalid?
296         end
297         @election.save
298       end
299  
300       # reset the next time to have a the same default value for emailing
301       @raw_voter_list = RawVoterList.new
302       @raw_voter_list.email = incoming_voters.email
303     end
304
305     def email_voter(voter=nil)
306       if voter
307         VoterNotify.deliver_votestart(voter)
308         voter.contacted=1
309         voter.save
310       end
311     end
312
313 end

Benjamin Mako Hill || Want to submit a patch?