ad74a1b211de62a91b37a41e46e5756ab6fe8709
[selectricity] / 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. Please see the COPYING file for
6 # details.
7
8 class ElectionController < ApplicationController
9   require_dependency "raw_voter_list"
10   require_dependency "voter"
11   require_dependency "vote"
12   require_dependency "candidate"
13   layout 'main'
14
15   ## methods for displaying, creating,
16   ## and manipulating election overview data
17   ####################################################################
18
19   def new
20     redirect_to :action => 'general_information'
21   end
22   
23   def general_information
24     @sidebar_content = render_to_string :partial => 'progress',
25                                         :locals => { :page => 'overview' }
26     @election = Election.new
27     render :action => 'general_information'
28   end
29   
30   def create_election
31     @election = Election.new(params[:election])
32     
33     # default options
34     @election.user = session[:user]
35     @election.anonymous = 1
36     @election.startdate = Time.now
37     @election.type = 'Election'
38     
39     holder = create_theme_hash
40     unless holder.values.all? {|v| v.has_value?("")}
41       token_generator = UniqueTokenGenerator.new( 16 )
42       @election.embed_custom_string = token_generator.token
43       add_theme(@election.embed_custom_string)
44     end
45     
46     if @election.save
47       flash[:notice] = 'Election was successfully created.'
48       redirect_to :action => 'edit_candidates', :id => @election.id
49     else
50       render :action => 'general_information'
51     end
52   end
53   
54   def create_theme_hash
55     target = Hash.new
56     params.each do |k,v|
57       target[k] = v if k=="top_bar" or k=="default_image" or k=="bg1" \
58                     or k=="bg2" or k=="bottom_bar"
59     end
60     return target
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     holder = create_theme_hash
73     unless holder.values.all? {|v| v.has_value?("")}
74       unless @election.embed_custom_string
75         token_generator = UniqueTokenGenerator.new( 16 )
76         @election.embed_custom_string = token_generator.token
77       end
78       
79       add_theme(@election.embed_custom_string)
80     end
81     
82     if @election.update_attributes(params[:election])
83       flash[:notice] = 'Election was successfully updated.'
84       redirect_to :action => 'show', :id => @election
85     else
86       render :action => 'edit'
87     end
88   end
89   
90   #Takes care of uploading custom images 
91   #unnecessarily long, how can I compress?
92   def add_theme(prefix)
93     holder = create_theme_hash
94     unless params[:top_bar][:uploaded_data].to_s.empty?
95       previous = SkinPicture.find(:first,
96       :conditions => ["filename = ?", @election.embed_custom_string + "top_bar.png"])
97       if previous
98         previous.destroy
99       end
100       top_bar = SkinPicture.new(params[:top_bar])
101       top_bar.filename = prefix + "top_bar." + params[:top_bar][:uploaded_data].content_type[6..-2]
102       top_bar.save
103     end
104     unless params[:default_image][:uploaded_data].to_s.empty?
105       previous = SkinPicture.find(:first,
106       :conditions => ["filename = ?", @election.embed_custom_string + "default_image.png"])
107       if previous
108         previous.destroy
109       end
110       default_image = SkinPicture.new(params[:default_image])
111       default_image.filename = prefix + "default_image." + params[:default_image][:uploaded_data].content_type[6..-2]
112       default_image.save
113     end
114     unless params[:bg1][:uploaded_data].to_s.empty?
115       previous = SkinPicture.find(:first,
116       :conditions => ["filename = ?", @election.embed_custom_string + "bg1.png"])
117       if previous
118         previous.destroy
119       end
120       bg1 = SkinPicture.new(params[:bg1])  
121       bg1.filename = prefix + "bg1." + params[:bg1][:uploaded_data].content_type[6..-2]
122       bg1.save
123     end
124     unless params[:bg2][:uploaded_data].to_s.empty?
125       previous = SkinPicture.find(:first,
126       :conditions => ["filename = ?", @election.embed_custom_string + "bg2.png"])
127       if previous
128         previous.destroy
129       end
130       bg2 = SkinPicture.new(params[:bg2]) 
131       bg2.filename = prefix + "bg2." + params[:bg2][:uploaded_data].content_type[6..-2]
132       bg2.save
133     end
134     unless params[:bottom_bar][:uploaded_data].to_s.empty?
135       previous = SkinPicture.find(:first,
136       :conditions => ["filename = ?", @election.embed_custom_string + "bottom_bar.png"])
137       if previous
138         previous.destroy
139       end
140       bottom_bar = SkinPicture.new(params[:bottom_bar])
141       bottom_bar.filename = prefix + "bottom_bar." + params[:bottom_bar][:uploaded_data].content_type[6..-2]
142       bottom_bar.save
143     end
144         
145   end
146   
147   def show
148     @sidebar_content = render_to_string :partial => 'progress',
149                                         :locals => { :page => 'review' }
150
151     @election = Election.find(params[:id])
152     if @election.class  == QuickVote
153       redirect_to(:controller => 'quickvote', :action => 'index', :ident => @election.id)
154     end
155       
156   end
157
158   def start_election
159     @election = Election.find(params[:id])
160     
161     @election.voters.each do |voter|
162       voter.vote = Vote.new
163       email_voter voter unless voter.email.nil?
164     end
165
166     @election.activate!
167     redirect_to :action => 'show', :id => @election.id
168   end
169
170   # methods fod display, adding, deleting, and manipulating candidate
171   # information for elections
172   ####################################################################
173   def edit_candidates
174     @sidebar_content = render_to_string :partial => 'progress',
175                                         :locals => { :page => 'candidates' }
176     @election = Election.find( params[:id] )
177   end
178
179   def add_candidate
180     @election = Election.find(params[:id])
181     @candidate = Candidate.new(params[:candidate])
182     @election.candidates << @candidate
183
184     if @candidate.save
185       # check to see if they've uploaded a picture
186       if params[:picture][:uploaded_data]
187         picture = Picture.new(params[:picture])
188         @candidate.picture = picture if picture.save
189       end
190
191       @candidate = Candidate.new
192       redirect_to :action => 'edit_candidates', :id => @election.id
193     else
194       render :action => 'edit_candidates', :id => @election.id
195     end
196   end
197   
198   def delete_candidate
199     candidate = Candidate.find( params[:id] )
200     candidate.destroy
201   end
202
203   def candidate_picture
204     candidate = Candidate.find( params[:id] )
205     send_data( candidate.picture.data,
206                :filename => candidate.picture.filename,
207                :type => candidate.picture.filetype,
208                :disposition => 'inline' )
209   end
210
211   ## methods for displaying, adding, deleting, and manipulating voters
212   ## for a particular election
213   ####################################################################
214   def new_voters
215     redirect_to :action => 'edit_voters', :id => params[:id]
216   end
217   
218   def edit_voters
219     @sidebar_content = render_to_string :partial => 'progress',
220                                         :locals => { :page => 'voters' }
221
222     @election = Election.find( params[:id] )
223     if params.has_key?( :raw_voter_list )
224       process_incoming_voters( params[:raw_voter_list] )
225     end
226     @edit = true
227     @raw_voter_list = RawVoterList.new
228   end
229   
230   def delete_voter
231     voter = FullVoter.find( params[:id] )
232     voter.destroy
233   end
234
235   def toggle_authenticated
236     @election = Election.find(params[:id])
237     if params[:authenticated] == "1"
238       @election.authenticated = true
239     else
240       @election.authenticated = false
241     end
242     @election.save
243   end
244   
245   ## methods for computing and printing results
246   ####################################################################
247   def results
248     @election = Election.find( params[:id] )
249     votes = []
250     
251     @election.voters.each do |voter|
252       if voter.vote and voter.vote.confirmed?
253         votes << voter.vote.rankings.sort.collect {|vote| vote.candidate_id}
254       end
255     end
256     
257     @voteobj = CloneproofSSDVote.new(votes)
258     @resultobj = @voteobj.result
259     @winners = @resultobj.winners
260     
261     @candidates_by_id = {}
262     @election.candidates.each {|cand| @candidates_by_id[cand.id] = cand}
263     
264   end
265   
266   def detailed_results
267    
268     self.results
269
270     @voter_list = []
271     @vote_list = []
272     
273     @election.voters.each do |voter|
274       if voter.vote and voter.vote.confirmed?
275         @voter_list << voter.email
276               @vote_list << voter.vote
277       end
278     end
279
280     @vote_list.sort!
281     @vote_list.sort! { |a,b| a.token <=> b.token }
282   end
283
284   ## private methods
285   ####################################################################
286   private
287
288     def process_incoming_voters(raw_voter_list)
289       incoming_voters = RawVoterList.new( raw_voter_list )
290
291       unless incoming_voters.entries.empty?
292         incoming_voters.each do |new_voter|
293           new_voter.email.strip! # There's a trailing \r on all but the last in
294                                  # the list!
295           if new_voter.valid?
296             # the new voter should be in good shape. save add to the election
297             @election.voters << new_voter
298                   new_voter.save
299           end
300           # TODO: Can we do some kind of AJAX error message for the voter being invalid?
301         end
302         @election.save
303       end
304  
305       # reset the next time to have a the same default value for emailing
306       @raw_voter_list = RawVoterList.new
307       @raw_voter_list.email = incoming_voters.email
308     end
309
310     def email_voter(voter=nil)
311       if voter
312         VoterNotify.deliver_votestart(voter)
313         voter.contacted=1
314         voter.save
315       end
316     end
317
318 end

Benjamin Mako Hill || Want to submit a patch?