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

Benjamin Mako Hill || Want to submit a patch?