fixed a large number of bugs in the software (see wiki) over a days work
[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. 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
14   helper :sparklines
15   layout 'main'
16
17
18   before_filter :verify_owner,
19     :except => [:new, :general_information, :create_election]
20   before_filter :verify_not_active,
21     :except => [:new, :general_information, :create_election,
22                 :show, :results, :details, :pref_tables]
23
24   ## methods for displaying, creating,
25   ## and manipulating election overview data
26   ####################################################################
27
28   def new
29     redirect_to :action => 'general_information'
30   end
31   
32   def general_information
33     @sidebar_content = render_to_string :partial => 'progress',
34                                         :locals => { :page => 'overview' }
35     @election = Election.new
36     render :action => 'general_information'
37   end
38   
39   def create_election
40     @election = Election.new(params[:election])
41     
42     # default options
43     @election.user = session[:user]
44     @election.anonymous = 1
45     @election.startdate = Time.now
46     @election.type = 'Election'
47     
48     holder = create_theme_hash
49     unless holder.values.all? {|v| v.has_value?("")}
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   def edit_general_information
64     @sidebar_content = render_to_string :partial => 'progress',
65                                         :locals => { :page => 'overview' }
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_general_information'
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[:candidate] )
200     candidate.destroy
201   end
202
203   def candidate_picture
204     candidate = Candidate.find(params[:candidate])
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
224     if params.has_key?( :raw_voter_list )
225       process_incoming_voters( params[:raw_voter_list] )
226     end
227     @edit = true
228     @raw_voter_list = RawVoterList.new
229   end
230   
231   def delete_voter
232     voter = FullVoter.find(params[:voter])
233     voter.destroy
234   end
235
236   def toggle_authenticated
237     @election = Election.find(params[:id])
238     if params[:authenticated] == "1"
239       @election.authenticated = true
240     else
241       @election.authenticated = false
242     end
243     @election.save
244   end
245   
246   ## methods for computing and printing results
247   ####################################################################
248   def results
249     @election = Election.find(params[:id])
250
251     if @election.early_results? \
252        or @election.enddate < Time.now
253       
254       # render results
255       @sidebar_content = render_to_string(:partial => 'full_results_sidebar')
256       render :template => 'common/results'
257     else
258       redirect_to :action => 'index'
259     end
260   end
261   
262   def pref_tables
263     @election = Election.find(params[:id])
264     render :template => 'common/pref_tables_wrapper', :layout => 'basic'
265   end
266
267   def details
268     @election = Election.find(params[:id])
269     render :template => 'common/details'
270   end
271
272   ## private methods
273   ####################################################################
274   private
275
276     def process_incoming_voters(raw_voter_list)
277       incoming_voters = RawVoterList.new( raw_voter_list )
278
279       unless incoming_voters.entries.empty?
280         incoming_voters.each do |new_voter|
281           new_voter.email.strip! # There's a trailing \r on all but the last in
282                                  # the list!
283           if new_voter.valid?
284             # the new voter should be in good shape. save add to the election
285             @election.voters << new_voter
286                   new_voter.save
287           end
288           # TODO: Can we do some kind of AJAX error message for the voter being invalid?
289         end
290         @election.save
291       end
292  
293       # reset the next time to have a the same default value for emailing
294       @raw_voter_list = RawVoterList.new
295       @raw_voter_list.email = incoming_voters.email
296     end
297
298     def email_voter(voter=nil)
299       if voter
300         VoterNotify.deliver_votestart(voter)
301         voter.contacted=1
302         voter.save
303       end
304     end
305   
306     def create_theme_hash
307       target = Hash.new
308       params.each do |k,v|
309         target[k] = v if k=="top_bar" or k=="default_image" or k=="bg1" \
310                       or k=="bg2" or k=="bottom_bar"
311       end
312       return target
313     end
314
315     # verify that the person trying to edit the election is the owner
316     def verify_owner
317       election = Election.find(params[:id])
318       unless election.user == session[:user]
319         redirect_to :controller => 'front', :action => 'index' 
320       end
321     end
322
323     # verify that the election is not active
324     def verify_not_active
325       election = Election.find(params[:id])
326       unless election.active == 0
327         redirect_to :controller => 'front', :action => 'index' 
328       end
329     end
330
331 end

Benjamin Mako Hill || Want to submit a patch?