From: John Dong Date: Tue, 28 Aug 2007 15:44:47 +0000 (-0400) Subject: Merge head X-Git-Url: https://projects.mako.cc/source/selectricity-live/commitdiff_plain/ffec26b00fc14b92f82137e1e3c62ce78c93ea24?hp=d6d44b7c8dc1d8ea3e425116caadca43c9412fed Merge head --- diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index bb614de..e8adcf0 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -10,7 +10,9 @@ class AccountController < ApplicationController def index redirect_to(:action => 'signup') unless logged_in? || User.count > 0 end - + + #these methods provide basic functionality for the user login system + #=================================================================== def login return unless request.post? self.current_user = User.authenticate(params[:login], params[:password]) @@ -42,4 +44,14 @@ class AccountController < ApplicationController flash[:notice] = "You have been logged out." redirect_back_or_default(:controller => '/site', :action => 'index') end + #====================================================================== + + #The following methods are for slectricity specific uses + def summary + @user = User.find(params[:id]) + end + + end + + diff --git a/app/controllers/election_controller.rb b/app/controllers/election_controller.rb index d333294..4ad8bac 100644 --- a/app/controllers/election_controller.rb +++ b/app/controllers/election_controller.rb @@ -12,7 +12,12 @@ class ElectionController < ApplicationController #################################################################### def new + redirect_to :action => 'general_information' + end + + def general_information @election = Election.new + render :action => 'general_information' end def create_election @@ -27,7 +32,7 @@ class ElectionController < ApplicationController flash[:notice] = 'Election was successfully created.' redirect_to :action => 'edit_candidates', :id => @election.id else - render :action => 'new' + render :action => 'general_information' end end @@ -62,6 +67,15 @@ class ElectionController < ApplicationController @election.activate! redirect_to :action => 'show', :id => @election.id end + + def change_notices + election = Election.find(params[:id]) + if election.notices == 0 + election.notices = 1 + else + election.notices = 0 + end + end # methods fod display, adding, deleting, and manipulating candidate # information for elections @@ -73,9 +87,9 @@ class ElectionController < ApplicationController def add_candidate @election = Election.find(params[:id]) @candidate = Candidate.new(params[:candidate]) - + @election.candidates << @candidate + if @candidate.save - @election.candidates << @candidate @candidate = Candidate.new redirect_to :action => 'edit_candidates', :id => @election.id else @@ -118,9 +132,9 @@ class ElectionController < ApplicationController def candidate_picture candidate = Candidate.find( params[:id] ) - send_data( candidate.picture_data, - :filename => candidate.picture_filename, - :type => candidate.picture_type, + send_data( candidate.picture.data, + :filename => candidate.picture.filename, + :type => candidate.picture.filetype, :disposition => 'inline' ) end @@ -128,12 +142,7 @@ class ElectionController < ApplicationController ## for a particular election #################################################################### def new_voters - @election = Election.find( params[:id] ) - if params.has_key?[:raw_voter_list] - process_incoming_voters( params[:raw_voter_list] ) - end - @raw_voter_list = RawVoterList.new - + edit_voters end def edit_voters @@ -207,8 +216,8 @@ class ElectionController < ApplicationController end # the new voter should be in good shape. save add to the election - new_voter.save @election.voters << new_voter + new_voter.save end end diff --git a/app/controllers/graph_controller.rb b/app/controllers/graph_controller.rb index 69577c6..814eb36 100644 --- a/app/controllers/graph_controller.rb +++ b/app/controllers/graph_controller.rb @@ -3,11 +3,13 @@ class GraphController < ApplicationController class GruffGraff def initialize(options) - size = "700x400" + size = options[:size] ? options[:size] : "400x300" #allow custom sizing @graph = options[:graph_type].new(size) - - @graph.theme = { :colors => ['#000000', '#00FFFF', '#FFCC00', '#990033'], - :background_colors => ['#74ce00', '#ffffff'] } + + @graph.no_data_message = "No Voters" + + @graph.theme = { :colors => ['#005CD9', '#DC0D13', '#131313', '#990033'], + :background_colors => ['#74CE00', '#FFFFFF'] } @graph.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf', RAILS_ROOT) @@ -72,6 +74,7 @@ class GraphController < ApplicationController :data => data, :interval_labels => labels, :title => "Voters Over Time", + :size => "700x400", :x_axis_label => scale, :y_axis_label => "Number of Votes") send_data(*graph.output) diff --git a/app/controllers/quickvote_controller.rb b/app/controllers/quickvote_controller.rb index 54784ef..cd63c2e 100644 --- a/app/controllers/quickvote_controller.rb +++ b/app/controllers/quickvote_controller.rb @@ -4,44 +4,52 @@ class QuickvoteController < ApplicationController require_dependency "quick_vote" require_dependency "vote" require_dependency "election" - + ############################################################# # the following methods pertain to creating quickvotes ############################################################# def create - if params[:quickvote] + if params[:quickvote] @quickvote = QuickVote.new(params[:quickvote]) # store the candidate grabbed through ajax and stored in flash - @quickvote.candidatelist = flash[:candlist] + @quickvote.candidate_names = flash[:candidate_names] @quickvote.description=@quickvote.description + #record who created the quickvote so that person can monitor it easily + @quickvote.quickuser = session.session_id + #Give registered users additional QuickVote functionality + @quickvote.user_id = session[:user][:id] if session[:user] # try to save, if it fails, show the page again (the flash should # still be intact if @quickvote.save @quickvote = @quickvote.reload render :action => 'success' else - flash.keep(:candlist) + flash.keep(:candidate_names) end else # if we don't have a quickvote param, it means that the person # here has not been hitting this page and we can clear any - # candlist in the flash - flash.delete(:candlist) if flash.has_key?(:candlist) + # candidate_names list in the flash + flash.delete(:candidate_names) if flash.has_key?(:candidate_names) + @quickvote = QuickVote.new end end def add_candidate candidate_name = params[:ajax][:newcandidate] unless candidate_name.strip.empty? - if flash.has_key?(:candlist) and flash[:candlist].instance_of?(Array) - flash[:candlist] << candidate_name unless flash[:candlist].index(candidate_name) + if flash.has_key?(:candidate_names) \ + and flash[:candidate_names].instance_of?(Array) + unless flash[:candidate_names].index(candidate_name) + flash[:candidate_names] << candidate_name + end else - flash[:candlist] = [ candidate_name ] + flash[:candidate_names] = [ candidate_name ] end end - flash.keep(:candlist) + flash.keep(:candidate_names) render_partial 'candidate_list' end @@ -56,8 +64,9 @@ class QuickvoteController < ApplicationController if @election # look to see that the voter has been created and has voted in # this election, and has confirmed their vote - @voter = QuickVoter.find(:all, :conditions => ["session_id = ? and election_id = ?", - session.session_id, @election.id])[0] + @voter = QuickVoter.find(:all, + :conditions => ["session_id = ? and election_id = ?", + session.session_id, @election.id])[0] # if the voter has not voted we destroy them if @voter and not @voter.voted? @@ -89,8 +98,9 @@ class QuickvoteController < ApplicationController election = QuickVote.ident_to_quickvote(params[:ident]) # find out who the voter is for this election - @voter = QuickVoter.find(:all, :conditions => ["session_id = ? and election_id = ?", - session.session_id, election.id])[0] + @voter = QuickVoter.find(:all, + :conditions => ["session_id = ? and election_id = ?", + session.session_id, election.id])[0] if not @voter # we have not seen this voter before. something is wrong, try @@ -119,7 +129,8 @@ class QuickvoteController < ApplicationController end def change - voter = QuickVoter.find(:all, :conditions => ["session_id = ?", session.session_id])[0] + voter = QuickVoter.find(:all, :conditions => ["session_id = ?", + session.session_id])[0] voter.destroy redirect_to quickvote_url( :ident => params[:ident] ) end @@ -137,19 +148,27 @@ class QuickvoteController < ApplicationController def mapvoters @map = GMap.new("map_div_id") @map.control_init(:large_map => true, :map_type => true) - center=nil + center = nil + QuickVote.ident_to_quickvote(params[:id]).voters.each do |voter| next unless voter.ipaddress + location = GeoKit::Geocoders::IpGeocoder.geocode(voter.ipaddress) next unless location.lng and location.lat + unless center - center=[location.lat,location.lng] - @map.center_zoom_init(center,4) + center = [location.lat, location.lng] + @map.center_zoom_init(center, 4) end - marker = GMarker.new([location.lat,location.lng], :title => "Voter", :info_window => (voter.ipaddress or "unknown")+" "+voter.vote.votestring) + + marker = GMarker.new([location.lat,location.lng], + :title => "Voter", + :info_window => (voter.ipaddress or "unknown") \ + + " " + voter.vote.votestring) @map.overlay_init(marker) end end + ############################################################### # the following method pertains to displaying the results of a # quickvote @@ -161,8 +180,18 @@ class QuickvoteController < ApplicationController redirect_to :controller => 'site' return end + if @election.viewable == 0 && @election.active == 1 + render :action => 'not_viewable' and return + end @results = @election.results @candidates = {} @election.candidates.each {|c| @candidates[c.id] = c} end + + def my_quickvotes + @myqvs = QuickVote.find(:all, :conditions => ["quickuser = ?", + session.session_id]) + end + end + diff --git a/app/controllers/site_controller.rb b/app/controllers/site_controller.rb index 80fdfc4..522bbb2 100644 --- a/app/controllers/site_controller.rb +++ b/app/controllers/site_controller.rb @@ -1,5 +1,5 @@ class SiteController < ApplicationController - layout 'main' + layout 'frontpage' require_dependency "user" require_dependency "election" require_dependency "account" diff --git a/app/models/candidate.rb b/app/models/candidate.rb index 430b6ab..eeba4d2 100644 --- a/app/models/candidate.rb +++ b/app/models/candidate.rb @@ -2,6 +2,10 @@ class Candidate < ActiveRecord::Base belongs_to :election validates_presence_of :name + # i have to call this picture_assoc because picture= does not overload + # the normal association method made by has_one + has_one :picture_obj, :class_name => "Picture" + # validate uniqueness of a name *within a given election* def <=>(other) @@ -12,24 +16,21 @@ class Candidate < ActiveRecord::Base name end - def picture=(picture_field) - if picture_field - unless picture_field.content_type.match(/^image/) - return false - end - self.picture_filename = base_part_of(picture_field.original_filename) - self.picture_type = picture_field.content_type.chomp - self.picture_data = picture_field.read - end + def picture + picture_obj end - def base_part_of(filename) - name = File.basename(filename) - name.gsub(/[^\w._-]/, '') + def picture=(field) + if field and field.length > 0 + self.picture_obj = Picture.new.set_from_field(field) + return picture_obj.save + else + return false + end end def picture? - !self.picture_filename.nil? + !self.picture_obj.nil? end end diff --git a/app/models/election.rb b/app/models/election.rb index 59a10bf..a77f445 100644 --- a/app/models/election.rb +++ b/app/models/election.rb @@ -15,6 +15,12 @@ class Election < ActiveRecord::Base require 'date' + def initialize(params={}) + super + self.enddate = read_attribute( :enddate ) || \ + Time.now + 14.days - 1.second + end + def other_methods if election_method @other_methods = ELECTION_TYPES.reject {|i| i == election_method} @@ -27,11 +33,6 @@ class Election < ActiveRecord::Base def startdate read_attribute( :startdate ) || Time.now end - - def enddate - date = read_attribute( :enddate ) || Time.now + 14 - date - 1.second - end def enddate=(date) date += 1.day @@ -56,7 +57,6 @@ class Election < ActiveRecord::Base def start_blockers reasons = [] - if self.candidates.length <= 1 reasons << "You must have at least two candidates." end diff --git a/app/models/picture.rb b/app/models/picture.rb new file mode 100644 index 0000000..e7df084 --- /dev/null +++ b/app/models/picture.rb @@ -0,0 +1,20 @@ +class Picture < ActiveRecord::Base + belongs_to :candidate + + def set_from_field(field) + unless field.content_type.match(/^image/) + return false + end + self.filename = base_part_of(field.original_filename) + self.filetype = field.content_type.chomp + self.data = field.read + self + end + + def base_part_of(filename) + name = File.basename(filename) + name.gsub(/[^\w._-]/, '') + end + +end + diff --git a/app/models/quick_vote.rb b/app/models/quick_vote.rb index c0367d6..bd39051 100644 --- a/app/models/quick_vote.rb +++ b/app/models/quick_vote.rb @@ -1,16 +1,27 @@ class QuickVote < Election + before_validation :build_candidate_names after_validation :create_candidates validates_uniqueness_of :name validates_presence_of :name - attr_accessor :raw_candidates + + attr_accessor :candidate_names attr_accessor :reviewed + def initialize(params={}) + super + self.startdate = Time.now + self.active = 1 + self.anonymous = 1 unless self.anonymous + self.enddate = read_attribute( :enddate ) || \ + Time.now + 30.days - 1.second + end + def validate - if not @raw_candidates or @raw_candidates.length < 2 + if @candidate_names.length < 2 errors.add(nil, "You must list at least two candidates.") end - - @raw_candidates.each do |c| + + @candidate_names.each do |c| unless c.instance_of? String errors.add(nil, "Candidates must be strings") next @@ -20,9 +31,11 @@ class QuickVote < Election errors.add(nil, "Candidate name must not be empty") next end - end if @raw_candidates - - errors.add(nil, "Candidates must all be unique") if @raw_candidates and @raw_candidates.uniq! + end if @candidate_names + + if @candidate_names and @candidate_names.uniq! + errors.add(nil, "Candidates must all be unique") + end if name =~ /[^A-Za-z0-9]/ errors.add(:name, "must only include numbers and letters.") @@ -34,18 +47,10 @@ class QuickVote < Election if name =~ /^(create|index|confirm|change|results)$/ errors.add(:name, " is a reserved word.") end - end - - def initialize(params={}) - super - self.startdate = Time.now - self.enddate = Time.now + 30.days - self.active = 1 - self.anonymous = 1 - end - def candidatelist=(candlist) - @raw_candidates = candlist + if enddate < startdate + errors.add(nil, "QuickVotes can't end before they start") + end end def name @@ -56,9 +61,21 @@ class QuickVote < Election reviewed.to_i == 1 end + def build_candidate_names + @candidate_names ||= [] + if @candidate_names.empty? and not candidates.empty? + @candidate_names = candidates.collect {|c| c.name} + end + end + def create_candidates return unless errors.empty? - @raw_candidates.each do |name| + + # delete the candidates + candidates.each {|c| c.destroy} + + # create the new list based on the names + @candidate_names.each do |name| candidate = Candidate.new({:name => name}) self.candidates << candidate end diff --git a/app/models/selectricity_service.rb b/app/models/selectricity_service.rb index 196161a..544b618 100644 --- a/app/models/selectricity_service.rb +++ b/app/models/selectricity_service.rb @@ -69,31 +69,47 @@ class SelectricityService < ActionWebService::Base result.candidate_names=candidates.values result end + def get_quickvote_votes(shortname) - qv=QuickVote.ident_to_quickvote(shortname) - votes=Array.new + qv = QuickVote.ident_to_quickvote(shortname) + unless qv raise ArgumentError.new("Cannot find QuickVote #{shortname}") end - qv.votes.each do |vote| - votes << VoteInfo.new(:voter_id => vote.voter.id, :voter_ipaddress => vote.voter.ipaddress, :vote_time => vote.time.to_i, :vote => vote.votes, :voter_session_id => vote.voter.session_id ) + + qv.votes.collect do |vote| + VoteInfo.new(:voter_id => vote.voter.id, + :voter_ipaddress => vote.voter.ipaddress, + :vote_time => vote.time.to_i, + :vote => vote.votes, + :voter_session_id => vote.voter.session_id) end - return votes end + def list_quickvotes() - all=Array.new - QuickVote.find(:all).each do |election| - all << get_quickvote(election.name) + QuickVote.find(:all).collect do |election| + get_quickvote(election.name) end - return all end + def get_quickvote(shortname) - raise ArgumentError.new("Cannot find QuickVote named #{shortname}") unless election=QuickVote.ident_to_quickvote(shortname) - return ElectionStruct.new(:id => election.id, :name => election.name, :description => election.description, :candidate_ids => election.candidates.collect {|c| c.id }, :candidate_names => election.candidates.collect {|c| c.name } ) + unless election = QuickVote.ident_to_quickvote(shortname) + raise ArgumentError.new("Cannot find QuickVote named #{shortname}") + end + + ElectionStruct.new( + :id => election.id, + :name => election.name, + :description => election.description, + :candidate_ids => election.candidates.collect {|c| c.id }, + :candidate_names => election.candidates.collect {|c| c.name } ) end + def create_quickvote(election) - qv=QuickVote.new(:name => election.name, :description => election.description) - qv.candidatelist=election.candidate_names + qv = QuickVote.new(:name => election.name, + :description => election.description) + qv.candidate_names = election.candidate_names + if qv.save return "" else diff --git a/app/views/account/signup.rhtml b/app/views/account/signup.rhtml index c0012a7..f5228d8 100644 --- a/app/views/account/signup.rhtml +++ b/app/views/account/signup.rhtml @@ -1,10 +1,13 @@ <%= error_messages_for :user %> <% form_for :user do |f| -%> +


<%= f.text_field :login %>


-<%= f.text_field :email %>

+<%= f.text_field :email %>
+People participating in elections you're administrating will contact you +at this address.


<%= f.password_field :password %>

diff --git a/app/views/account/summary.rhtml b/app/views/account/summary.rhtml new file mode 100644 index 0000000..2f24e28 --- /dev/null +++ b/app/views/account/summary.rhtml @@ -0,0 +1,84 @@ +

This is your user summary profile, <%=h @user.login.capitalize %>

+ +

+E-mail: <%=h @user.email %>
+Member since: <%=h @user.created_at.strftime("%x") %> +

+ +

Your Elections: + + + <% Election.content_columns.each do |column| -%> + <% next if column.name.eql?("viewable") || column.name.eql?("quickuser")\ + || column.name.eql?("active") %> + + <% end -%> + + + <% @user.elections.select {|e| e.instance_of?(Election)}.each do |election| %> + + + + + + + + + + <% end -%> +
<%= column.human_name %>
+ <% if election.active == 1 -%> + <%= link_to "#{election.name}", :controller => 'election', + :action => 'show', :id => election %> + <% else -%> + <%=h election.name %> + <% end -%> + <%=h election.description %> + <% if election.anonymous == 0 -%> + No + <% else -%> + Yes + <% end -%> + <%=h election.startdate.strftime("%x") %><%=h election.enddate.strftime("%x") %> + <% if election.notices == 0 -%> + No + <% else -%> + Yes + <% end -%> + <%=h election.election_method %>
+

+ +

+Your Quickvotes: + + + <% ["Name", "Description", "Start Date", "End Date", "Notices"].each do |column| %> + + <% end -%> + + + <% @user.elections.select {|e| e.instance_of?(QuickVote)}.each do |quickvote| + %> + + + + + + + +<% end %> +
<%= column %>
+ <% if quickvote.active == 1 %> + <%= link_to "#{quickvote.name}", + quickaction_url( :ident => quickvote.name, + :action => 'results' ) %> + <% else %> + <%=h quickvote.name %> + <% end %> + <%=h quickvote.description %><%=h quickvote.startdate.strftime("%x") %><%=h quickvote.enddate.strftime("%x") %> + <% if quickvote.notices == 0 -%> + No + <% else -%> + Yes + <% end -%> +
diff --git a/app/views/election/_progress.rhtml b/app/views/election/_progress.rhtml new file mode 100644 index 0000000..520894c --- /dev/null +++ b/app/views/election/_progress.rhtml @@ -0,0 +1,16 @@ +<% progress_steps = [ ['overview', 'General Information'], + ['candidates', 'Candidates'], + ['voters', 'Voters'], + ['review', 'Review'] ] %> +

+ + + +
diff --git a/app/views/election/edit_candidates.rhtml b/app/views/election/edit_candidates.rhtml index 99f2f12..837f755 100644 --- a/app/views/election/edit_candidates.rhtml +++ b/app/views/election/edit_candidates.rhtml @@ -1,9 +1,11 @@ +<%= render_partial 'progress', 'candidates' %>

Edit/Add Candidates

<%= error_messages_for :candidate %> <% unless @election.candidates.empty? %>

The following are valid options or candidates in this election:

+1;3A <% @election.candidates.each do |candidate| %> <% @current_candidate = candidate %> @@ -22,4 +24,4 @@ <%= submit_tag "Add Candidate" %> <% end %> -<%= button_to "Done!", :action => 'show', :id => @election %> +<%= button_to "Done!", :action => 'new_voters', :id => @election %> diff --git a/app/views/election/new.rhtml b/app/views/election/general_information.rhtml similarity index 80% rename from app/views/election/new.rhtml rename to app/views/election/general_information.rhtml index 7b71748..0a1a513 100644 --- a/app/views/election/new.rhtml +++ b/app/views/election/general_information.rhtml @@ -1,3 +1,5 @@ +<%= render_partial 'progress', 'overview' %> +

Create A New Vote

Vote Overview

diff --git a/app/views/election/new_voters.rhtml b/app/views/election/new_voters.rhtml index 9c3bad3..fc49e13 100644 --- a/app/views/election/new_voters.rhtml +++ b/app/views/election/new_voters.rhtml @@ -1,3 +1,4 @@ +<%= render_partial 'progress', 'voters' %> <% @edit = true %>

<%= @election.name %>: Enter List of Voter Email Addresses

@@ -6,3 +7,4 @@ <% form_tag(:action => 'new_voters', :id => @election.id) do %> <%= render :partial => 'voters_form' %> <% end %> +<%= button_to "Done", :action => 'show', :id => @election.id %> diff --git a/app/views/election/show.rhtml b/app/views/election/show.rhtml index 0051861..5d22f44 100644 --- a/app/views/election/show.rhtml +++ b/app/views/election/show.rhtml @@ -1,4 +1,4 @@ -<% %> +<%= render_partial 'progress', 'review' %>

Vote Information

<% if @election.active? %> @@ -34,7 +34,7 @@ <% end %>

Candidates

- +<% %> <% unless @election.candidates.empty? %> <%= render :partial => 'candidate_list' %> <% unless @election.active %> @@ -54,7 +54,7 @@

There are currently no voters registered. <%= link_to "Add some!", :action => 'edit_voters', :id => @election.id unless @election.active %>

<% end %> -<% unless @election.active %> +<% unless @election.active? %>

Start Election

<% if @election.start_blockers.length > 0 %> @@ -65,7 +65,7 @@ <% end %> <% else %> -

Please check eveything carefully on this page before starting this +

Please check everything carefully on this page before starting this vote. Once you begin the vote, you will not be able to add or change candidates, modify the voting lists, or change the end time.

diff --git a/app/views/layouts/_footer.rhtml b/app/views/layouts/_footer.rhtml new file mode 100644 index 0000000..b23900f --- /dev/null +++ b/app/views/layouts/_footer.rhtml @@ -0,0 +1,4 @@ + diff --git a/app/views/layouts/frontpage.rhtml b/app/views/layouts/frontpage.rhtml new file mode 100644 index 0000000..8f14c3a --- /dev/null +++ b/app/views/layouts/frontpage.rhtml @@ -0,0 +1,25 @@ + + + + + Selectricity + <%= stylesheet_link_tag "common", :media => "all" %> + <%= stylesheet_link_tag "front", :media => "all" %> + + + +
+ + + +
+ <%= @content_for_layout %> +
+ + <%= render_partial 'layouts/footer' %> +
+ + + diff --git a/app/views/layouts/main.rhtml b/app/views/layouts/main.rhtml index 0b9d5cb..c6a14a3 100644 --- a/app/views/layouts/main.rhtml +++ b/app/views/layouts/main.rhtml @@ -1,52 +1,44 @@ -<% %> - - <%= @page_title || "Selectricity" %> - <%= stylesheet_link_tag "main", :media => "all" %> - - <%= javascript_include_tag "prototype", "effects", "dragdrop", "controls" %> - - -
- <% if @page_title %> -

<%= @page_title %>

- <% else %> - <%= link_to('

Selectricity
- Voting Machinery for the Masses

', :controller => - 'site', :action => 'index')%>
- <% end %> + + <%= @page_title || "Selectricity" %> + <%= stylesheet_link_tag "main", :media => "all" %> + <%= javascript_include_tag "prototype", "effects", "dragdrop", "controls" %> + - -
- - <% if flash[:notice]%> -
<%= flash[:notice] %>
- <% end%> + +
+ <% if @page_title %> +

<%= @page_title %>

+ <% else %> + <%= link_to('

Selectricity
+ Voting Machinery for the Masses

', :controller => + 'site', :action => 'index')%>
+ <% end %> -
- <%= @content_for_layout %> -
+ +
+ + <% if flash[:notice]%> +
<%= flash[:notice] %>
+ <% end%> - +
+ <%= @content_for_layout %> +
- + <%= render_partial 'layouts/footer' %> + diff --git a/app/views/quickvote/_advanced.rhtml b/app/views/quickvote/_advanced.rhtml new file mode 100644 index 0000000..0255fbd --- /dev/null +++ b/app/views/quickvote/_advanced.rhtml @@ -0,0 +1,21 @@ + +<% fields_for 'quickvote', quickvote do |quickform| %> + +
+<%= quickform.select('election_method', + %w(ssd condorcet plurality approval borda) ) %>
+ +
+<%= quickform.date_select(:enddate, :start_year => Time.now.year) %>
+ +

Want the results to be visible while the election is active? +Yes <%= quickform.radio_button(:viewable, 1)%> +No <%= quickform.radio_button(:viewable, 0)%>

+ +<% if session[:user] %> +

Would you like to be e-mailed when this QuickVote concludes? +Yes <%= quickform.radio_button(:notices, 1)%> +No <%= quickform.radio_button(:notices, 0)%>

+<% end -%> + +<% end %> diff --git a/app/views/quickvote/_candidate_list.rhtml b/app/views/quickvote/_candidate_list.rhtml index ddb47e5..76de0ac 100644 --- a/app/views/quickvote/_candidate_list.rhtml +++ b/app/views/quickvote/_candidate_list.rhtml @@ -1,14 +1,14 @@ <% %> -<% if flash[:candlist] %> +<% if flash[:candidate_names] %> <% end %> -<% form_remote_tag(:update => 'candlist', +<% form_remote_tag(:update => 'candidate_names', :url => { :action => 'add_candidate' }, :complete => "Field.focus('ajax_newcandidate')") do %>

diff --git a/app/views/quickvote/_pref_tables.rhtml b/app/views/quickvote/_pref_tables.rhtml index 66e0cbf..0d2b020 100644 --- a/app/views/quickvote/_pref_tables.rhtml +++ b/app/views/quickvote/_pref_tables.rhtml @@ -4,7 +4,6 @@ <% victories = @election.condorcet_result.victories_and_ties %> <% names = @election.names_by_id %> - @@ -46,9 +45,8 @@ <% else -%> (<%= margin%>) <% end -%> - + <% end -%> <% end -%>
- diff --git a/app/views/quickvote/_result_approval.rhtml b/app/views/quickvote/_result_approval.rhtml index 12c7958..c93ce1d 100644 --- a/app/views/quickvote/_result_approval.rhtml +++ b/app/views/quickvote/_result_approval.rhtml @@ -1,7 +1,8 @@ -

Approval Result

(This algorithm assumes that top two choices are "approved.")

+<%= render :partial => 'result', :object => @election.approval_result %> +

About Approval Voting

@@ -12,6 +13,4 @@ chooses. Approval voting is a limited form of range voting, where the range that voters are allowed to express is extremely constrained: accept or not.

-
-
\ No newline at end of file diff --git a/app/views/quickvote/_result_borda.rhtml b/app/views/quickvote/_result_borda.rhtml index 97d82e5..f81cbdb 100644 --- a/app/views/quickvote/_result_borda.rhtml +++ b/app/views/quickvote/_result_borda.rhtml @@ -1,6 +1,7 @@ -

Borda Count Results

+<%= render :partial => 'result', :object => @election.borda_result %> +

About Borda Count

@@ -13,4 +14,4 @@ corresponding to the position in which he or she is ranked by each voter. Once all votes have been counted the candidate with the most points is the winner.

-
\ No newline at end of file +<%= image_tag( graph_url( :action => 'borda_bar', :id => @election ) ) %> diff --git a/app/views/quickvote/_result_condorcet.rhtml b/app/views/quickvote/_result_condorcet.rhtml index 4279bbd..77ce658 100644 --- a/app/views/quickvote/_result_condorcet.rhtml +++ b/app/views/quickvote/_result_condorcet.rhtml @@ -1,6 +1,7 @@ -

Simple Condorcet Results

+<%= render :partial => 'result', :object => @election.condorcet_result %> +

About Simple Cordorcet Voting

@@ -15,4 +16,3 @@ will be the winner.

another Condorcet system.

-
\ No newline at end of file diff --git a/app/views/quickvote/_result_plurality.rhtml b/app/views/quickvote/_result_plurality.rhtml index 121c1dc..86a9469 100644 --- a/app/views/quickvote/_result_plurality.rhtml +++ b/app/views/quickvote/_result_plurality.rhtml @@ -1,6 +1,7 @@ -

Plurality Results

+<%= render :partial => 'result', :object => @election.plurality_result %> +

About Plurality Voting

@@ -14,4 +15,4 @@ post," "winner-take-all," "majoritarian" or "simple majority" voting.

-
\ No newline at end of file +<%= image_tag(graph_url( :action => 'plurality_pie', :id => @election ) )%> diff --git a/app/views/quickvote/_result_ssd.rhtml b/app/views/quickvote/_result_ssd.rhtml index 436dd67..e181184 100644 --- a/app/views/quickvote/_result_ssd.rhtml +++ b/app/views/quickvote/_result_ssd.rhtml @@ -1,6 +1,7 @@ -

Schulze Method Results

+<%= render :partial => 'result', :object => @election.ssd_result %> +

About the Schulze Method

@@ -14,4 +15,5 @@ methods for resolving "circular" defeats.

Beatpath Winner, Path Voting, and Path Winner.

-
+<%= render :partial => 'pref_tables' %> + diff --git a/app/views/quickvote/create.rhtml b/app/views/quickvote/create.rhtml index cf91e19..7ccb644 100644 --- a/app/views/quickvote/create.rhtml +++ b/app/views/quickvote/create.rhtml @@ -3,10 +3,10 @@ <%= error_messages_for 'quickvote' %> -

+

-
+
<%= render :partial => 'candidate_list' %>
@@ -24,7 +24,19 @@ <%= text_area 'quickvote', 'description', :cols => 50, :rows => 4 %>

+<%= check_box('options', 'advanced', + :onclick => 'Element.toggle($("advanced")); false;' )%>Advanced + + +
+ <%= submit_tag "Create Quickvote" -%> <% end %> + + + + diff --git a/app/views/quickvote/my_quickvotes.rhtml b/app/views/quickvote/my_quickvotes.rhtml new file mode 100644 index 0000000..bb38836 --- /dev/null +++ b/app/views/quickvote/my_quickvotes.rhtml @@ -0,0 +1,24 @@ + + + <% ["Name", "Start Date", "End Date", "Description"].each do |column| %> + + <% end -%> + + + <% @myqvs.each do |quickvote| %> + + + + + + +<% end %> +
<%= column %>
+ <% if quickvote.active == 1 %> + <%= link_to "#{quickvote.name}", + quickaction_url( :ident => quickvote.name, + :action => 'results' ) %> + <% else %> + <%=h quickvote.name %> + <% end %> + <%=h quickvote.startdate.strftime("%x") %><%=h quickvote.enddate.strftime("%x") %><%=h quickvote.description %>
diff --git a/app/views/quickvote/not_viewable.rhtml b/app/views/quickvote/not_viewable.rhtml new file mode 100644 index 0000000..3c5532e --- /dev/null +++ b/app/views/quickvote/not_viewable.rhtml @@ -0,0 +1,3 @@ +

Sorry, the creator of this QuickVote has decided this election shouldn't + be visible while the QuickVote is running.

+<%= link_to "Selectricity Home", :controller => 'site' %> \ No newline at end of file diff --git a/app/views/quickvote/results.rhtml b/app/views/quickvote/results.rhtml index 9727d81..0383b5e 100644 --- a/app/views/quickvote/results.rhtml +++ b/app/views/quickvote/results.rhtml @@ -27,14 +27,17 @@

Winners

+
<%= render :partial => 'result_' + @election.election_method, :object => @results[@election.election_method] %> - +

Other Voting Methods

<% for result_type in @election.other_methods %> +
<%= render :partial => 'result_' + result_type, :object => @results[result_type] %> +
<% end %> @@ -75,10 +78,8 @@ <% end %> -<%= render :partial => 'pref_tables' %> - <%=image_tag( graph_url( :action => 'votes_per_interval', :id => @election ))%>
-<%= image_tag( graph_url( :action => 'borda_bar', :id => @election ) ) %>
+ <%= image_tag( graph_url( :action => 'choices_positions', :id => @election ) ) %>
-<%= image_tag(graph_url( :action => 'plurality_pie', :id => @election ) )%> + diff --git a/app/views/site/_basic_login.rhtml b/app/views/site/_basic_login.rhtml index f02d362..9c689fc 100644 --- a/app/views/site/_basic_login.rhtml +++ b/app/views/site/_basic_login.rhtml @@ -1,26 +1,13 @@ -<% -%> - <% form_tag :controller => 'account', :action => 'login' do %> - - - <%= text_field "Login", "login", :size => 30 %>
- - <%= password_field "Password", "password", :size => 30 %>
-
+<% form_tag :controller => 'account', :action => 'login' do %> -
-

<%= submit_tag 'Login' %>

-<% end %> -

<%= link_to 'Register for an account', :controller => -'account', :action => 'signup' %>

- -

<%= link_to 'Lost or forgot your password?', :controller => 'account', :action => 'forgot_password' %>

+

+

<%= text_field "Login", "login", :size => 30 %>

+

+

<%= password_field "Password", "password", :size => 30 %>

-

Unfortunately, Selectricity is currently being tested and new - accounts for full votes (i.e., non-QuickVotes) can not - be automatically created. If you are interested in using - Selectricity to - run an organizational election, contact Benjamin Mako Hill.

+

<%= submit_tag 'Login' %>

+<% end %> -
+

<%= link_to 'Register for an account', :controller => 'account', :action => 'signup' %>

+

<%= link_to 'Lost or forgot your password?', :controller => 'account', :action => 'forgot_password' %>

diff --git a/app/views/site/_user_summary.rhtml b/app/views/site/_user_summary.rhtml index 310d98c..e37cafb 100644 --- a/app/views/site/_user_summary.rhtml +++ b/app/views/site/_user_summary.rhtml @@ -1,7 +1,3 @@ -<% %> - -

Activity Summary

- <% if @current_elections.length < 1 %> You have not created any elections. <% else %> diff --git a/app/views/site/index.rhtml b/app/views/site/index.rhtml index 1b994b0..bb74bba 100644 --- a/app/views/site/index.rhtml +++ b/app/views/site/index.rhtml @@ -1,25 +1,65 @@ -<% %> +
+
+

Voters

- - -
+

If you have received an email with a token inviting you to vote in + an ongoing election, you can log in to vote using your token below.

-

QuickVotes

+ <%= form_tag :controller => 'voter', :action => 'index' %> +

<%= text_field :vote, :password %>

+

<%= submit_tag "Log In" %>

+ <%= end_form_tag %> -

QuickVotes are like polls: unstructured, non-anonymous and -without the complex features of Selectricity. They are the -quickest way to make a decision or to compare between voting -methods.

+

<%= link_to 'Lost or forgot your token?', :controller => 'voter', :action => 'forgot_password' %>

-

<%= link_to "Create a QuickVote", :controller => 'quickvote', :action => 'create' %>

+

SMS Interface

+

For information on accessing Selectricity over email or via SMS/text messages from your mobile phone, email <%= link_to "vote\@mako.cc", "mailto:vote@mako.cc" %> with "help" in the body or read the <%= link_to "Selectricity Anywhere documentation", "selectricity-anywhere.html"%>.

-

Recent QuickVotes include:

+ + -
    -<% for quickvote in @quickvotes %> -
  • <%= link_to (h(quickvote.shortdesc) || "Unnamed"), quickvote_url(:ident => quickvote.name) %>
  • -<% end %> -
+
+
+

Control Room

+ + <% if session[:user] %> + <%= render :partial => 'user_summary' %> + <% else %> + +

You must have an account to start a new vote or to administer an + existing vote. You can log in or create a new account below.

+ + <%= render_partial 'basic_login' %> + <% end %> + +
+
+ +
+
+ +

QuickVotes

+

QuickVotes are like polls: unstructured, non-anonymous and + without the complex features of Selectricity. They are the + quickest way to make a decision or to compare between voting + methods.

+ +
    +
  • <%= link_to( "Create a QuickVote", :controller => 'quickvote', :action => 'create') %>
  • +
  • <%= link_to( "Recent QuickVotes", :controller => 'quickvote', :action => 'my_quickvotes')%>
  • +
+ +

Recently created QuickVotes include:

+
    + <% for quickvote in @quickvotes %> +
  • <%= link_to (h(quickvote.shortdesc) || "Unnamed"), quickvote_url(:ident => quickvote.name) %>
  • + <% end %> +
+ +
+
+ + diff --git a/app/views/voter_notify/votestart.rhtml b/app/views/voter_notify/votestart.rhtml index b844ee2..b31f1ba 100644 --- a/app/views/voter_notify/votestart.rhtml +++ b/app/views/voter_notify/votestart.rhtml @@ -13,10 +13,10 @@ need to use the following token to log in to selectricity.media.mit.edu: Alternatively, you can just click this URL: <%= url_for :controller => 'election', :action => 'show', :id => @voter.election.id %> -If you have any questions or if you feel you have recieved this message +If you have any questions or if you feel you have received this message in error, you should contact: - <%= @voter.election.user.name %> <<%= @voter.election.user.email %>> + <%= @voter.election.user.login %> <<%= @voter.election.user.email %>> (The initiator of this election) Alternatively, if you feel there is a technical error, please contact: diff --git a/config/environment.rb b/config/environment.rb index 571638d..80f19b4 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -87,6 +87,10 @@ class String end end +#Change the session store key, so that it will not conflict with other webapps +ActionController::Base.session_options[:session_key] = 'selectricity_session_id' +CGI::Session.expire_after 1.year + # action mailer configuration ActionMailer::Base.delivery_method = :sendmail ActionMailer::Base.default_charset = "utf-8" diff --git a/config/environments/test.rb b/config/environments/test.rb index f0689b9..7c2f617 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -16,4 +16,9 @@ config.action_controller.perform_caching = false # Tell ActionMailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. -config.action_mailer.delivery_method = :test \ No newline at end of file +config.action_mailer.delivery_method = :test + +# start the debugger +require 'ruby-debug' +SCRIPT_LINES__ = {} +Debugger.start diff --git a/config/routes.rb b/config/routes.rb index 96beafd..735f5d7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,7 +15,7 @@ ActionController::Routing::Routes.draw do |map| map.connect 'quickvote/:action/:id', :controller => 'quickvote', - :requirements => { :action => /(create|add_candidate|sort_candidates)/ } + :requirements => { :action => /(create|add_candidate|sort_candidates|my_quickvotes)/ } map.quickaction 'quickvote/:ident/:action', :controller => 'quickvote', diff --git a/db/create.sql b/db/create.sql index 7b3fef5..2033ed1 100644 --- a/db/create.sql +++ b/db/create.sql @@ -3,14 +3,17 @@ drop table if exists elections; create table elections ( - id int NOT NULL auto_increment, + id int NOT NULL auto_increment, name varchar(100) NOT NULL, description TEXT NOT NULL, anonymous tinyint NOT NULL DEFAULT 1, startdate datetime, enddate datetime NOT NULL, active tinyint NOT NULL DEFAULT 0, + viewable tinyint NOT NULL DEFAULT 1, + notices tinyint NOT NULL DEFAULT 0, user_id int NULL, + quickuser varchar(255) NULL, #stores session_id for quickvote creators election_method varchar(100) DEFAULT 'ssd', `type` varchar(100) NOT NULL, primary key (id), @@ -26,9 +29,20 @@ create table candidates ( election_id int NOT NULL, name varchar(100) NOT NULL, description text NULL, - picture_filename varchar(200), - picture_data blob, - picture_type varchar(100), + primary key (id) +); + +# CREATE pictures TABLE +##################################### + +drop table if exists pictures; +create table pictures ( + id int NOT NULL auto_increment, + filename varchar(200), + data blob, + filetype varchar(100), + candidate_id int NULL, + constraint fk_candidate_picture foreign key (candidate_id) references candidates(id), primary key (id) ); diff --git a/db/schema.rb b/db/schema.rb index e69de29..4ea61b7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -0,0 +1,77 @@ +# This file is autogenerated. Instead of editing this file, please use the +# migrations feature of ActiveRecord to incrementally modify your database, and +# then regenerate this schema definition. + +ActiveRecord::Schema.define() do + + create_table "candidates", :force => true do |t| + t.column "election_id", :integer, :null => false + t.column "name", :string, :limit => 100, :default => "", :null => false + t.column "description", :text + t.column "picture_filename", :string, :limit => 200 + t.column "picture_data", :binary + t.column "picture_type", :string, :limit => 100 + end + + create_table "elections", :force => true do |t| + t.column "name", :string, :limit => 100, :default => "", :null => false + t.column "description", :text, :default => "", :null => false + t.column "anonymous", :integer, :limit => 4, :default => 1, :null => false + t.column "startdate", :datetime + t.column "enddate", :datetime, :null => false + t.column "active", :integer, :limit => 4, :default => 0, :null => false + t.column "viewable", :integer, :limit => 4, :default => 1, :null => false + t.column "notices", :integer, :limit => 4, :default => 0, :null => false + t.column "user_id", :integer + t.column "quickuser", :string + t.column "election_method", :string, :limit => 100, :default => "ssd" + t.column "type", :string, :limit => 100, :default => "", :null => false + end + + add_index "elections", ["user_id"], :name => "fk_user_election" + + create_table "rankings", :force => true do |t| + t.column "vote_id", :integer + t.column "candidate_id", :integer + t.column "rank", :integer + end + + create_table "tokens", :force => true do |t| + t.column "token", :string, :limit => 100, :default => "", :null => false + t.column "vote_id", :integer, :null => false + end + + add_index "tokens", ["vote_id"], :name => "fk_vote_token" + + create_table "users", :force => true do |t| + t.column "login", :text + t.column "ip", :text, :default => "", :null => false + t.column "email", :text + t.column "crypted_password", :string, :limit => 40 + t.column "salt", :string, :limit => 40 + t.column "created_at", :datetime + t.column "updated_at", :datetime + t.column "remember_token", :text + t.column "remember_token_expires_at", :datetime + end + + create_table "voters", :force => true do |t| + t.column "email", :string, :limit => 100 + t.column "password", :string, :limit => 100 + t.column "contacted", :integer, :limit => 4, :default => 0, :null => false + t.column "election_id", :integer, :null => false + t.column "session_id", :string, :limit => 32 + t.column "ipaddress", :string, :limit => 32 + end + + add_index "voters", ["election_id"], :name => "fk_election_voter" + + create_table "votes", :force => true do |t| + t.column "voter_id", :integer + t.column "confirmed", :integer, :limit => 4, :default => 0, :null => false + t.column "time", :datetime + end + + add_index "votes", ["voter_id"], :name => "fk_vote_voter" + +end diff --git a/public/images/bg_controlroom.png b/public/images/bg_controlroom.png new file mode 100644 index 0000000..5629933 Binary files /dev/null and b/public/images/bg_controlroom.png differ diff --git a/public/images/bg_index.png b/public/images/bg_index.png new file mode 100644 index 0000000..52ac075 Binary files /dev/null and b/public/images/bg_index.png differ diff --git a/public/images/bg_quickvotes.png b/public/images/bg_quickvotes.png new file mode 100644 index 0000000..fd844ec Binary files /dev/null and b/public/images/bg_quickvotes.png differ diff --git a/public/images/bg_resultblock.png b/public/images/bg_resultblock.png new file mode 100644 index 0000000..21a05bd Binary files /dev/null and b/public/images/bg_resultblock.png differ diff --git a/public/images/bg_results.png b/public/images/bg_results.png new file mode 100644 index 0000000..a94d556 Binary files /dev/null and b/public/images/bg_results.png differ diff --git a/public/images/bg_voters.png b/public/images/bg_voters.png new file mode 100644 index 0000000..8901917 Binary files /dev/null and b/public/images/bg_voters.png differ diff --git a/public/images/rails.png b/public/images/rails.png deleted file mode 100644 index b8441f1..0000000 Binary files a/public/images/rails.png and /dev/null differ diff --git a/public/images/title.png b/public/images/title.png new file mode 100644 index 0000000..acf328f Binary files /dev/null and b/public/images/title.png differ diff --git a/public/images/title_main.png b/public/images/title_main.png new file mode 100644 index 0000000..f3dd02d Binary files /dev/null and b/public/images/title_main.png differ diff --git a/public/images/top_bar_1.png b/public/images/top_bar_1.png new file mode 100644 index 0000000..3be6999 Binary files /dev/null and b/public/images/top_bar_1.png differ diff --git a/public/images/top_bar_2.png b/public/images/top_bar_2.png new file mode 100644 index 0000000..7242913 Binary files /dev/null and b/public/images/top_bar_2.png differ diff --git a/public/stylesheets/common.css b/public/stylesheets/common.css new file mode 100644 index 0000000..9bc6264 --- /dev/null +++ b/public/stylesheets/common.css @@ -0,0 +1,77 @@ +@charset "UTF-8"; +/* CSS Document */ + +/**************************************************************** + Selectricity || selectricity.org + Design by Courtland Allen + + "Reset Reloaded" + Thanks to Eric Meyer + http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ +*****************************************************************/ + +/* common css files */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +dl, dt, dd, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} + + +#body { + font-family: "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: white; + font-size: 12px; + text-align: left; + line-height: 1.5em; +} + +#body a { + text-decoration: underline; + color: inherit; + display: inline; + opacity: 0.8; +} + +#body a:hover { + opacity: .5; +} + +strong, h2, h3 { + font-weight: bold; +} + +h2, h3 { + text-transform: uppercase; + margin-bottom: 0.5em !important; +} + + +/* footer */ + +#footer { + font-size: 11px; + font-family: "Trebuchet MS", Trebuchet, Verdana, Arial, Helvetica, sans-serif; + margin-top: 40px; + text-align: center; +} + +#footer a { + text-decoration: none; + color: #dc0d13; +} + +#footer a:hover { + text-decoration: underline; +} diff --git a/public/stylesheets/front.css b/public/stylesheets/front.css new file mode 100644 index 0000000..1285e7f --- /dev/null +++ b/public/stylesheets/front.css @@ -0,0 +1,120 @@ +@charset "UTF-8"; +/* CSS Document */ + +/**************************************************************** + Selectricity || selectricity.org + Design by Courtland Allen +*****************************************************************/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +dl, dt, dd, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} + +body { + line-height: 1; + color: black; + background: #ffffff url(/images/bg_index.png) repeat-x top center; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: separate; +} + +caption, th, td { + text-align: left; + font-weight: normal; +} + +#page-wrapper { + width: 960px; + margin: 0 auto 0 auto; + text-align: center; +} + + +/* Header */ +#header h1 { + background: url(/images/title.png) center top no-repeat; + overflow: hidden; + padding-top: 189px; + height: 0px; + margin-top: 10px; +} + + +/* Body */ + +#body { + margin-top: 9px; + height: 609px; +} + +h2 { + margin-top: 0.3em; + font-size: 24px; +} + +h3 { + margin-top: 1em; + font-size: 16px; +} + +.main-section { + float: left; + padding-left: 120px; + padding-right: 15px; +} + +.main-section p { + font-size: 12px; + overflow: hidden; + line-height: 2em; + margin-bottom: 1em; +} + +#voters { + padding-top: 50px; + height: 559px; + width: 185px; + background: url(/images/bg_voters.png) top left no-repeat; +} + +#voters-content { + height: 516px; +} + +#control-room { + padding-top: 139px; + height: 470px; + width: 185px; + background: url(/images/bg_controlroom.png) top left no-repeat; +} + +#control-room-content { + height: 428px; +} + +#quickvotes { + padding-top: 80px; + height: 529px; + width: 185px; + background: url(/images/bg_quickvotes.png) top left no-repeat; +} + +#quickvotes-content { + height: 486px; +} + diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css index 86cf84b..76eff61 100644 --- a/public/stylesheets/main.css +++ b/public/stylesheets/main.css @@ -167,6 +167,7 @@ a:active { color: #FFFFFF; text-decoration: none; background: #0259C4; } color: #fff; border-bottom: 0px; } + .resultbox { text-align: center; width: 400px; @@ -312,3 +313,26 @@ li.moveable { padding: 5px; } +#election_creation_progress_bar ul li { + display: inline; + list-style: default; +} + +#election_creation_progress_bar ul li:after { + font-weight: normal; + color: #000; + content: " || "; +} + +#election_creation_progress_bar ul li.last:after { + content: ""; +} + +#election_creation_progress_bar li.step_selected { + font-weight: bold; +} + +#election_creation_progress_bar li.step_unselected { + color: #CCCCCC; + font-weight: bold; +} diff --git a/test/fixtures/candidates.yml b/test/fixtures/candidates.yml index 8794d28..4a651bf 100644 --- a/test/fixtures/candidates.yml +++ b/test/fixtures/candidates.yml @@ -1,5 +1,31 @@ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -first: - id: 1 -another: - id: 2 + +mika_normal: + id: 1 + name: mika + election_id: 1 + +mako_normal: + id: 2 + name: mako + election_id: 1 + +bettamax_normal: + id: 3 + name: bettamax + election_id: 1 + +mika_badend: + id: 4 + name: mika + election_id: 2 + +mako_badend: + id: 5 + name: mako + election_id: 2 + +bettamax_badend: + id: 6 + name: bettamax + election_id: 2 diff --git a/test/fixtures/elections.yml b/test/fixtures/elections.yml index 8794d28..814540f 100644 --- a/test/fixtures/elections.yml +++ b/test/fixtures/elections.yml @@ -1,5 +1,22 @@ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -first: - id: 1 -another: - id: 2 +quickvote_normal: + id: 1 + name: acetarium + description: who is the winner? + anonymous: 1 + startdate: 2007-08-22 12:00:00 + enddate: 2007-09-21 11:59:59 + active: 1 + type: QuickVote + election_method: ssd + +quickvote_invalid_endtime: + id: 2 + name: acetarium2 + description: who is the winner? + anonymous: 1 + startdate: 2007-08-22 12:00:00 + enddate: 2003-09-21 11:59:59 + active: 1 + type: QuickVote + election_method: ssd diff --git a/test/functional/quickvote_controller_test.rb b/test/functional/quickvote_controller_test.rb index 02c133e..ee79b3f 100644 --- a/test/functional/quickvote_controller_test.rb +++ b/test/functional/quickvote_controller_test.rb @@ -28,7 +28,7 @@ class QuickvoteControllerTest < Test::Unit::TestCase end def test_create_quickvote - post(:create, {'commit' =>"Create Quickvote", 'quickvote' =>{'name' =>"variable", 'description' =>"Favorite variable."}}, nil, {:candlist=>["foo", "bar", "foobar"]}) + post(:create, {'commit' =>"Create Quickvote", 'quickvote' =>{'name' =>"variable", 'description' =>"Favorite variable."}}, nil, {:candidate_names=>["foo", "bar", "foobar"]}) assert_template "quickvote/success" get :index, { 'ident' => "variable"} assert_response :success @@ -42,19 +42,19 @@ class QuickvoteControllerTest < Test::Unit::TestCase end def test_create_quickvote_badname - post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candlist => ["foo", "bar", "foobar"]}) + post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candidate_names => ["foo", "bar", "foobar"]}) assert_template "quickvote/create" end def test_create_quickvote_dupe_candidate - post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candlist => ["foo", "bar", "bar", "foobar"]}) + post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candidate_names => ["foo", "bar", "bar", "foobar"]}) assert_template "quickvote/create" end def test_create_quickvote_nil_candidate - post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candlist => nil}) + post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candidate_names => nil}) assert_template "quickvote/create" - post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candlist => []}) + post(:create, {'commit' => "Create Quickvote", 'quickvote' => {'name' => "has a space", 'description' => "Foobar"}}, nil, {:candidate_names => []}) assert_template "quickvote/create" end @@ -119,7 +119,7 @@ class QuickvoteControllerTest < Test::Unit::TestCase test_create_quickvote qv=QuickVote.ident_to_quickvote('variable') qv.description="foo" - qv.candidatelist = ["foo", "bar", ""] + qv.candidate_names = ["foo", "bar", ""] qv.save! get :index, { 'ident' => 'variable' } assert_response :success diff --git a/test/unit/candidate_test.rb b/test/unit/candidate_test.rb index b640f0c..86d190e 100644 --- a/test/unit/candidate_test.rb +++ b/test/unit/candidate_test.rb @@ -1,10 +1,8 @@ require File.dirname(__FILE__) + '/../test_helper' class CandidateTest < Test::Unit::TestCase - fixtures :candidates - # Replace this with your real tests. - def test_truth - assert_kind_of Candidate, candidates(:first) + def test_default + assert true end end diff --git a/test/unit/election_test.rb b/test/unit/election_test.rb index 60663cf..90a2d4f 100644 --- a/test/unit/election_test.rb +++ b/test/unit/election_test.rb @@ -1,10 +1,7 @@ require File.dirname(__FILE__) + '/../test_helper' class ElectionTest < Test::Unit::TestCase - fixtures :elections - - # Replace this with your real tests. - def test_truth - assert_kind_of Election, elections(:first) + def test_default + assert true end end diff --git a/test/unit/quickvote_test.rb b/test/unit/quickvote_test.rb new file mode 100644 index 0000000..69a7033 --- /dev/null +++ b/test/unit/quickvote_test.rb @@ -0,0 +1,38 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class QuickVoteTest < Test::Unit::TestCase + fixtures :elections, :candidates + + def setup + @quickvote_normal = QuickVote.find(1) + end + + def test_create_update_delete_quickvote_from_fixture + assert_kind_of QuickVote, @quickvote_normal + assert_equal 1, @quickvote_normal.id + assert_equal 'acetarium', @quickvote_normal.name + assert_equal 'who is the winner?', @quickvote_normal.description + #assert_equal QuickVote, @quickvote_normal.type + assert_equal 'ssd', @quickvote_normal.election_method + assert_equal '2007-08-22 12:00:00', @quickvote_normal.startdate_before_type_cast + assert_equal '2007-09-21 11:59:59', @quickvote_normal.enddate_before_type_cast + + # make sure that the each of the three andidates + (1..3).each do |i| + assert @quickvote_normal.candidates.include?(Candidate.find(i)) + end + + # update and save + @quickvote_normal.name = 'foobar' + assert @quickvote_normal.save, + @quickvote_normal.errors.full_messages.join("; ") + + @quickvote_normal.reload + assert_equal 'foobar', @quickvote_normal.name + end + + def test_create_invalid_enddate + qv = QuickVote.find(2) + assert_equal qv.save, false, "created vote with invalid enddate" + end +end diff --git a/test/unit/selectricityservice_test.rb b/test/unit/selectricityservice_test.rb index c0f73c0..1c28cf1 100644 --- a/test/unit/selectricityservice_test.rb +++ b/test/unit/selectricityservice_test.rb @@ -9,13 +9,16 @@ class SelectricityServiceTest < Test::Unit::TestCase end def test_list_quickvotes - result= invoke_delegated :vote, :list_quickvotes + result = invoke_delegated :vote, :list_quickvotes assert_instance_of Array, result - assert_equal result.length, 0 + assert result.length > 0 end def test_create_quickvote - election = ElectionStruct.new :name => "TestVote", :description => "Test Vote", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] + election = ElectionStruct.new( + { :name => "TestVote", + :description => "Test Vote", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] }) assert_create_quickvote_succeeds election end @@ -29,18 +32,22 @@ class SelectricityServiceTest < Test::Unit::TestCase def test_cast_nil_quickvote assert_cast_quickvote_fails nil, nil, nil assert_cast_quickvote_fails "foo", nil, nil - assert_cast_quickvote_fails "foo",33, [] + assert_cast_quickvote_fails "foo", 33, [] test_create_quickvote - assert_cast_quickvote_fails "TestVote",42,nil - assert_cast_quickvote_fails "TestVote",nil,[] + assert_cast_quickvote_fails "TestVote", 42,nil + assert_cast_quickvote_fails "TestVote", nil, [] end def test_cast_malformed_votelist test_create_quickvote election = invoke_delegated :vote, :get_quickvote, "TestVote" assert_cast_quickvote_fails "TestVote", 11, [election.candidate_ids[0]] - assert_cast_quickvote_fails "TestVote", 11, [1,2] - assert_cast_quickvote_fails "TestVote", 11, [election.candidate_ids[0],election.candidate_ids[0], election.candidate_ids[1], election.candidate_ids[1], election.candidate_ids[2]] + assert_cast_quickvote_fails "TestVote", 11, [1, 2] + assert_cast_quickvote_fails "TestVote", 11, [election.candidate_ids[0], + election.candidate_ids[0], + election.candidate_ids[1], + election.candidate_ids[1], + election.candidate_ids[2]] end def test_get_nonexistent_quickvote @@ -50,132 +57,188 @@ class SelectricityServiceTest < Test::Unit::TestCase end def test_get_voters_nonexistent_quickvote - assert_raises(ArgumentError) {invoke_delegated :vote, :get_quickvote_votes, "asdfasdf"} + assert_raises(ArgumentError) { invoke_delegated(:vote, + :get_quickvote_votes, "asdfasdf") } end def test_get_candidate_map_nonexistent_quickvote - assert_raises(ArgumentError) { invoke_delegated :vote, :get_quickvote_candidate_map, "asdfasdf"} + assert_raises(ArgumentError) { invoke_delegated(:vote, + :get_quickvote_candidate_map, + "asdfasdf") } end def test_cast_mass_quickvote test_create_quickvote - election = invoke_delegated :vote, :get_quickvote, "TestVote" + election = invoke_delegated(:vote, :get_quickvote, "TestVote") 20.times do |t| - casted_vote = election.candidate_ids.sort_by {rand} + casted_vote = election.candidate_ids.sort_by { rand } assert_cast_quickvote_succeeds "TestVote", t, [casted_vote] end end def test_cast_quickvote_nonexistent - assert_cast_quickvote_fails "ASDFJOFASF", "me", [1,2,3] + assert_cast_quickvote_fails "ASDFJOFASF", "me", [1, 2, 3] end def test_cast_quickvote_nonexistent_candidates test_create_quickvote election = invoke_delegated :vote, :get_quickvote, "TestVote" - assert_cast_quickvote_fails "TestVote", 42, [123,342314,5342,1,1,2] + assert_cast_quickvote_fails "TestVote", 42, [123, 342314, 5342, 1, 1, 2] end def test_create_mass_quickvote 10.times do |t| - election = ElectionStruct.new :name => "test#{t}", :description => "Test Vote", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_succeeds election + assert_create_quickvote_succeeds ElectionStruct.new({ + :name => "test#{t}", + :description => "Test Vote", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) end end def test_create_quickvote_bad_name - election = ElectionStruct.new :name => "invalid space", :description => "Test Vote", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "invalid space", + :description => "Test Vote", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) + end def test_create_quickvote_nil - election = ElectionStruct.new - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new() end def test_create_quickvote_name_nil - election = ElectionStruct.new :name => "", :description => "Test Vote", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "", + :description => "Test Vote", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) end def test_create_quickvote_description_nil - election = ElectionStruct.new :name => "foobar", :description => nil, :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_fails election - + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => nil, + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) end def test_create_quickvote_description_whitespace - election = ElectionStruct.new :name => "foobar", :description => " ", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_fails election - election = ElectionStruct.new :name => "foobar", :description => "\t\t", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => " ", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) + + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "\t\t", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) end def test_create_quickvote_candidates_nil - election = ElectionStruct.new :name => "foobar", :description => "valid", :candidate_names => nil - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "valid", + :candidate_names => nil }) end + def test_create_quickvote_insufficient_candidates - election = ElectionStruct.new :name => "foobar", :description => "valid", :candidate_names => ["Apple"] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "valid", + :candidate_names => ["Apple"] }) end + def test_create_quickvote_candidates_whitespace - election = ElectionStruct.new :name => "foobar", :description => "valid", :candidate_names => [" ", " ", " ", " "] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "valid", + :candidate_names => [" ", " ", " ", " "]}) end + def test_create_quickvote_dupe_candidates - election = ElectionStruct.new :name => "foobar", :description => "valid", :candidate_names => ["Apple", "Apple", "Apple", "Apple"] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "valid", + :candidate_names => ["Apple", "Apple", "Apple", "Apple"]}) - # Previous may pass coincidentally if a uniq! then a sizecheck reveals too few unique names - # We don't want this to happen. Dupe canidates should fail regardless of how many are left. + # TODO: Previous may pass coincidentally if a uniq! then a sizecheck + # reveals too few unique names + + # We don't want this to happen. Dupe canidates should fail + # regardless of how many are left. - election = ElectionStruct.new :name => "foobar", :description => "valid", :candidate_names => ["Apple", "Apple", "Orange", "Orange", "Pineapple" , "Pineapple"] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "valid", + :candidate_names => ["Apple", "Apple", "Orange", + "Orange", "Pineapple" , "Pineapple"]}) end + def test_create_quickvote_candidates_nil_mixed - election = ElectionStruct.new :name => "foobar", :description => "valid", :candidate_names => ["Apple", nil ] - assert_create_quickvote_fails election + assert_create_quickvote_fails ElectionStruct.new({ + :name => "foobar", + :description => "valid", + :candidate_names => ["Apple", nil ]}) + end + def test_create_quickvote_description_xmlescape # Will an embedded XML element bork the table? - election = ElectionStruct.new :name => "foobar", :description => "test ", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_succeeds election + assert_create_quickvote_succeeds ElectionStruct.new({ + :name => "foobar", + :description => "test ", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) end + def test_create_quickvote_unprintable_description - election = ElectionStruct.new :name => "foobar", :description => "test \x01\x02\x03\x04\x05\x06\x07\x08", :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"] - assert_create_quickvote_succeeds election + assert_create_quickvote_succeeds ElectionStruct.new ({ + :name => "foobar", + :description => "test \x01\x02\x03\x04\x05\x06\x07\x08", + :candidate_names => ["Apple", "Orange", "Banana", "Pineapple"]}) end + def test_quickvote_proper_results - election = ElectionStruct.new :name => "favdev", :description => "Who is your favorite developer?", :candidate_names => ["mako", "jdong", "justin"] - assert_create_quickvote_succeeds election - reflection = invoke_delegated :vote, :get_quickvote, "favdev" + assert_create_quickvote_succeeds ElectionStruct.new({ + :name => "favdev", + :description => "Who is your favorite developer?", + :candidate_names => ["mako", "jdong", "justin"]}) + + reflection = invoke_delegated(:vote, :get_quickvote, "favdev") + + # build the candidate list candidates = {} reflection.candidate_names.each_with_index do |name, index| - candidates[name] = reflection.candidate_ids[index] + candidates[name] = reflection.candidate_ids[index] end + 25.times do |t| vote = [candidates["jdong"], candidates["mako"], candidates["justin"]] assert_cast_quickvote_succeeds "favdev", "1:#{t}", [vote] end + 5.times do |t| vote = [candidates["mako"], candidates["justin"], candidates["jdong"]] assert_cast_quickvote_succeeds "favdev", "2:#{t}", [vote] end + 10.times do |t| vote = [candidates["justin"], candidates["mako"], candidates["jdong"]] assert_cast_quickvote_succeeds "favdev", "3:#{t}", [vote] end + results=nil - assert_nothing_raised {results=invoke_delegated(:vote, :get_quickvote_results, "favdev")} + + assert_nothing_raised { results = invoke_delegated(:vote, + :get_quickvote_results, "favdev") } assert_equal results.approval_winners, [candidates["mako"]] assert_equal results.borda_winners, [candidates["jdong"]] assert_equal results.plurality_winners, [candidates["jdong"]] assert_equal results.condorcet_winners, [candidates["jdong"]] assert_equal results.ssd_winners, [candidates["jdong"]] end - private + ## helper methods and non-tests used throughout this file + ######################################################### + private def assert_cast_quickvote_succeeds(shortname, id, vote) assert_nothing_raised do old_votes = invoke_delegated(:vote, :get_quickvote_votes, shortname) diff --git a/vendor/plugins/dynamic_sessions/init.rb b/vendor/plugins/dynamic_sessions/init.rb new file mode 100644 index 0000000..bc1da90 --- /dev/null +++ b/vendor/plugins/dynamic_sessions/init.rb @@ -0,0 +1 @@ +require 'dynamic_session_expiry' \ No newline at end of file diff --git a/vendor/plugins/dynamic_sessions/lib/dynamic_session_expiry.rb b/vendor/plugins/dynamic_sessions/lib/dynamic_session_expiry.rb new file mode 100644 index 0000000..8752365 --- /dev/null +++ b/vendor/plugins/dynamic_sessions/lib/dynamic_session_expiry.rb @@ -0,0 +1,38 @@ +# Provides the ability to have session cookies for your Rails app calculated +# relative to the current time. +# +# In your environment.rb file (or in the environments/*.rb file of your choice), +# do something like the following: +# +# CGI::Session.expire_after 1.month +# +# Session cookies will then expire one month after the session was created. This +# differs from the usual session cookie behavior in that the expiration date is +# not a fixed time, but rather relative to the current time. + +class CGI + class Session + @@session_expiration_offset = 0 + + def self.session_expiration_offset + @@session_expiration_offset + end + + def self.session_expiration_offset=(value) + @@session_expiration_offset = value + end + + def self.expire_after(value) + @@session_expiration_offset = value + end + + alias :initialize_without_dynamic_session_expiration :initialize #:nodoc: + def initialize(request, option={}) #:nodoc: + if @@session_expiration_offset && @@session_expiration_offset > 0 + option['session_expires'] = Time.now + @@session_expiration_offset + end + initialize_without_dynamic_session_expiration(request, option) + end + + end +end \ No newline at end of file