From dc22a5bc7cb12f36d20291c4cbb0da7ff2aac8cf Mon Sep 17 00:00:00 2001 From: ricola Date: Sat, 7 Jun 2025 19:28:58 -0600 Subject: [PATCH 1/4] Hash reset codes in database Otherwise, they can be reused if the database gets leaked. --- vedia.rb | 13 ++++++++++--- views/reset_email.erb | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/vedia.rb b/vedia.rb index 0b5eb2c..56925e9 100644 --- a/vedia.rb +++ b/vedia.rb @@ -130,7 +130,8 @@ end post '/reset' do @user = User.find_by(email: params[:email]) if @user - @user.reset = SecureRandom.uuid + @reset = SecureRandom.uuid + @user.reset = hash_password(@reset) @user.save mail = Mail.new mail.from = settings.admin_email @@ -143,7 +144,10 @@ post '/reset' do end get '/reset/:uuid' do - @user = User.find_by(reset: params[:uuid]) + @user = nil + User.where.not(reset: nil).each do |user| + @user = user if verify_password(params[:uuid], user.reset) + end if @user erb :reset_change else @@ -152,7 +156,10 @@ get '/reset/:uuid' do end post '/reset/:uuid' do - @user = User.find_by(reset: params[:uuid]) + @user = nil + User.where.not(reset: nil).each do |user| + @user = user if verify_password(params[:uuid], user.reset) + end if @user @errors = [] if params[:password].empty? diff --git a/views/reset_email.erb b/views/reset_email.erb index 4a2bd5a..68467b4 100644 --- a/views/reset_email.erb +++ b/views/reset_email.erb @@ -1,3 +1,3 @@ <%= _("Visit the following link to reset your password:") %> -<%= "#{settings.base_url}reset/#{@user.reset}" %> +<%= "#{settings.base_url}reset/#{@reset}" %> From 0afc881b5dcefb45f1a55426b4dd5bc6b4d84ecc Mon Sep 17 00:00:00 2001 From: ricola Date: Sat, 7 Jun 2025 20:05:28 -0600 Subject: [PATCH 2/4] Clarify button labels --- po/ca/vedia.po | 35 ++++++++++++++++++----------------- po/vedia.pot | 32 ++++++++++++++------------------ views/votes_edit.erb | 4 ++-- views/votes_show_closed.erb | 4 ++-- views/votes_show_open.erb | 2 +- 5 files changed, 37 insertions(+), 40 deletions(-) diff --git a/po/ca/vedia.po b/po/ca/vedia.po index 0682d88..fb1599d 100644 --- a/po/ca/vedia.po +++ b/po/ca/vedia.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-06-07 18:22-0600\n" +"POT-Creation-Date: 2025-06-07 20:04-0600\n" "PO-Revision-Date: 2025-03-29 20:41-0600\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,35 +17,35 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: ../vedia.rb:64 +#: ../vedia.rb:69 msgid "Awful" msgstr "Molt malament" -#: ../vedia.rb:65 +#: ../vedia.rb:70 msgid "Very bad" msgstr "Malament" -#: ../vedia.rb:66 +#: ../vedia.rb:71 msgid "Bad" msgstr "Poc bé" -#: ../vedia.rb:67 +#: ../vedia.rb:72 msgid "Mediocre" msgstr "Mig bé" -#: ../vedia.rb:68 +#: ../vedia.rb:73 msgid "Good" msgstr "Bé" -#: ../vedia.rb:69 +#: ../vedia.rb:74 msgid "Very good" msgstr "Molt bé" -#: ../vedia.rb:121 +#: ../vedia.rb:126 msgid "Incorrect email or password." msgstr "Correu o contrasenya incorrecte." -#: ../vedia.rb:139 +#: ../vedia.rb:144 msgid "Reset your password" msgstr "Reiniciar contrasenya" @@ -178,7 +178,7 @@ msgstr "Opcions" msgid "Delete" msgstr "Suprimir" -#: ../views/votes_edit.erb:25 +#: ../views/votes_edit.erb:25 ../views/votes_edit.erb:36 msgid "Add candidate" msgstr "Afegeix una opció" @@ -186,18 +186,14 @@ msgstr "Afegeix una opció" msgid "Name" msgstr "Nom" -#: ../views/votes_edit.erb:36 ../views/votes_edit.erb:54 -#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 -#: ../views/votes_show_open.erb:61 -msgid "Add" -msgstr "Afegir" - #: ../views/votes_edit.erb:39 ../views/votes_show_closed.erb:65 #: ../views/votes_show_draft.erb:5 ../views/votes_show_open.erb:34 msgid "Organizers" msgstr "Organitzadores" -#: ../views/votes_edit.erb:47 ../views/votes_show_open.erb:54 +#: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54 +#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 +#: ../views/votes_show_open.erb:54 ../views/votes_show_open.erb:61 msgid "Add organizer" msgstr "Afegeix organitzadora" @@ -273,3 +269,8 @@ msgstr "Tornar a l'esborrany de votació" #: ../views/votes_show_open.erb:51 msgid "Close votes and show results" msgstr "Tancar la votació i veure els resultats" + +#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 +#: ../views/votes_show_open.erb:61 +#~ msgid "Add" +#~ msgstr "Afegir" diff --git a/po/vedia.pot b/po/vedia.pot index 089cb68..828aa54 100644 --- a/po/vedia.pot +++ b/po/vedia.pot @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-06-07 18:22-0600\n" -"PO-Revision-Date: 2025-06-07 18:22-0600\n" +"POT-Creation-Date: 2025-06-07 20:04-0600\n" +"PO-Revision-Date: 2025-06-07 20:04-0600\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" @@ -18,35 +18,35 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: ../vedia.rb:64 +#: ../vedia.rb:69 msgid "Awful" msgstr "" -#: ../vedia.rb:65 +#: ../vedia.rb:70 msgid "Very bad" msgstr "" -#: ../vedia.rb:66 +#: ../vedia.rb:71 msgid "Bad" msgstr "" -#: ../vedia.rb:67 +#: ../vedia.rb:72 msgid "Mediocre" msgstr "" -#: ../vedia.rb:68 +#: ../vedia.rb:73 msgid "Good" msgstr "" -#: ../vedia.rb:69 +#: ../vedia.rb:74 msgid "Very good" msgstr "" -#: ../vedia.rb:121 +#: ../vedia.rb:126 msgid "Incorrect email or password." msgstr "" -#: ../vedia.rb:139 +#: ../vedia.rb:144 msgid "Reset your password" msgstr "" @@ -177,7 +177,7 @@ msgstr "" msgid "Delete" msgstr "" -#: ../views/votes_edit.erb:25 +#: ../views/votes_edit.erb:25 ../views/votes_edit.erb:36 msgid "Add candidate" msgstr "" @@ -185,18 +185,14 @@ msgstr "" msgid "Name" msgstr "" -#: ../views/votes_edit.erb:36 ../views/votes_edit.erb:54 -#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 -#: ../views/votes_show_open.erb:61 -msgid "Add" -msgstr "" - #: ../views/votes_edit.erb:39 ../views/votes_show_closed.erb:65 #: ../views/votes_show_draft.erb:5 ../views/votes_show_open.erb:34 msgid "Organizers" msgstr "" -#: ../views/votes_edit.erb:47 ../views/votes_show_open.erb:54 +#: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54 +#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 +#: ../views/votes_show_open.erb:54 ../views/votes_show_open.erb:61 msgid "Add organizer" msgstr "" diff --git a/views/votes_edit.erb b/views/votes_edit.erb index e7fa5f6..3dac749 100644 --- a/views/votes_edit.erb +++ b/views/votes_edit.erb @@ -33,7 +33,7 @@

- +

<%= _("Organizers") %>

@@ -51,7 +51,7 @@

- +

<%= _("Actions for organizers") %>

diff --git a/views/votes_show_closed.erb b/views/votes_show_closed.erb index cb27423..8bc19e5 100644 --- a/views/votes_show_closed.erb +++ b/views/votes_show_closed.erb @@ -78,14 +78,14 @@ end -

<%= _("Add") %>

+

<%= _("Add organizer") %>

- +
<% end %> diff --git a/views/votes_show_open.erb b/views/votes_show_open.erb index 4e8cd49..c9e6091 100644 --- a/views/votes_show_open.erb +++ b/views/votes_show_open.erb @@ -58,7 +58,7 @@

- + <% end %> From cac94c23790fab6b3fd6f055fd34317f58a55e98 Mon Sep 17 00:00:00 2001 From: ricola Date: Sun, 8 Jun 2025 15:04:37 -0600 Subject: [PATCH 3/4] Allow voting on open votes only --- vedia.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/vedia.rb b/vedia.rb index 56925e9..1b2bffa 100644 --- a/vedia.rb +++ b/vedia.rb @@ -306,6 +306,7 @@ end post '/votes/:id/ratings' do require_login find_vote + require_open_vote @vote.candidates.each do |candidate| rating = Rating.find_or_initialize_by(vote: @vote, user: current_user, candidate: candidate) rating.value = params[candidate.id.to_s] From e474a9a8eadad4de283aac1f0c4b23ef3a025cff Mon Sep 17 00:00:00 2001 From: ricola Date: Sun, 8 Jun 2025 15:57:26 -0600 Subject: [PATCH 4/4] Close votes automatically after 7 days Closes #1 --- .gitignore | 1 + Rakefile | 11 +++- config/environments/defaults.rb | 8 +++ config/schedule.rb | 9 +++ po/ca/vedia.po | 113 ++++++++++++++++++++------------ po/vedia.pot | 108 +++++++++++++++++++----------- vedia.rb | 60 +++++++++++++---- views/layout.erb | 9 +++ views/reset_email.erb | 2 +- views/votes_close_email.erb | 7 ++ views/votes_edit.erb | 2 +- views/votes_open.erb | 28 ++++++++ views/votes_show_closed.erb | 16 ++++- views/votes_show_open.erb | 6 +- 14 files changed, 283 insertions(+), 97 deletions(-) create mode 100644 config/environments/defaults.rb create mode 100644 config/schedule.rb create mode 100644 views/votes_close_email.erb create mode 100644 views/votes_open.erb diff --git a/.gitignore b/.gitignore index 9020593..5142bb0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ locale db/*.sqlite3 db/*.sqlite3-* config/environments/development.rb +vedia.log diff --git a/Rakefile b/Rakefile index bfa6157..62ef4c3 100644 --- a/Rakefile +++ b/Rakefile @@ -2,9 +2,9 @@ require_relative 'vedia' require 'sinatra/activerecord/rake' desc "Update PO files" -task :updatepo do +task :update_po do require 'gettext/tools/xgettext' - GetText::Tools::XGetText.run(*["vedia.rb"] + Dir.glob("views/*.{rb,erb}") + ["-o", "po/vedia.pot"]) + GetText::Tools::XGetText.run(*["vedia.rb"] + Dir.glob("views/*.{rb,erb}") + ["config/environments/defaults.rb"] + ["-o", "po/vedia.pot"]) require 'gettext/tools/msgmerge' Dir.glob("po/**/vedia.po").collect { |po| /po\/(.+)\/vedia\.po/.match(po)[1] }.each do |locale| GetText::Tools::MsgMerge.run("po/#{locale}/vedia.po", "po/vedia.pot", "-o", "po/#{locale}/vedia.po") @@ -12,7 +12,7 @@ task :updatepo do end desc "Build MO files" -task :buildmo do +task :build_mo do require 'gettext/tools/msgfmt' Dir.mkdir("locale") unless Dir.exist?("locale") Dir.glob("po/**/vedia.po").collect { |po| /po\/(.+)\/vedia\.po/.match(po)[1] }.each do |locale| @@ -20,3 +20,8 @@ task :buildmo do GetText::Tools::MsgFmt.run("po/#{locale}/vedia.po", "-o", "locale/#{locale}/vedia.mo") end end + +desc "Close votes that have expired" +task :close_expired_votes do + close_expired_votes +end diff --git a/config/environments/defaults.rb b/config/environments/defaults.rb new file mode 100644 index 0000000..108f981 --- /dev/null +++ b/config/environments/defaults.rb @@ -0,0 +1,8 @@ +set :admin_email, 'vedia@potager.org' +set :values, [ { :id => 1, :label => _("Awful"), :color => '#ff4500' }, + { :id => 2, :label => _("Very bad"), :color => '#ffa500' }, + { :id => 3, :label => _("Bad"), :color => '#ffff00' }, + { :id => 4, :label => _("Mediocre"), :color => '#9acd32' }, + { :id => 5, :label => _("Good"), :color => '#228b22' }, + { :id => 6, :label => _("Very good"), :color => '#006400' } ] +set :expire_after, 7 * 24 * 60 * 60 # 7 days diff --git a/config/schedule.rb b/config/schedule.rb new file mode 100644 index 0000000..37830fe --- /dev/null +++ b/config/schedule.rb @@ -0,0 +1,9 @@ +if ENV['RAILS_ENV'] + set :environment, ENV['RAILS_ENV'] +end + +set :output, 'vedia.log' + +every 5.minutes do + rake "close_expired_votes" +end diff --git a/po/ca/vedia.po b/po/ca/vedia.po index fb1599d..492739d 100644 --- a/po/ca/vedia.po +++ b/po/ca/vedia.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-06-07 20:04-0600\n" +"POT-Creation-Date: 2025-06-08 15:49-0600\n" "PO-Revision-Date: 2025-03-29 20:41-0600\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,39 +17,39 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: ../vedia.rb:69 +#: ../config/environments/defaults.rb:2 msgid "Awful" msgstr "Molt malament" -#: ../vedia.rb:70 +#: ../config/environments/defaults.rb:3 msgid "Very bad" msgstr "Malament" -#: ../vedia.rb:71 +#: ../config/environments/defaults.rb:4 msgid "Bad" msgstr "Poc bé" -#: ../vedia.rb:72 +#: ../config/environments/defaults.rb:5 msgid "Mediocre" msgstr "Mig bé" -#: ../vedia.rb:73 +#: ../config/environments/defaults.rb:6 msgid "Good" msgstr "Bé" -#: ../vedia.rb:74 +#: ../config/environments/defaults.rb:7 msgid "Very good" msgstr "Molt bé" -#: ../vedia.rb:126 +#: ../vedia.rb:118 msgid "Incorrect email or password." msgstr "Correu o contrasenya incorrecte." -#: ../vedia.rb:144 +#: ../vedia.rb:140 msgid "Reset your password" msgstr "Reiniciar contrasenya" -#: ../views/home.erb:1 ../views/layout.erb:14 +#: ../views/home.erb:1 ../views/layout.erb:23 msgid "Home" msgstr "Inici" @@ -85,21 +85,21 @@ msgstr "(Tancada)" msgid "Create new vote" msgstr "Crear una nova votació" -#: ../views/layout.erb:13 +#: ../views/layout.erb:22 msgid "Logged in as %{email}." msgstr "Estàs connectada com a %{email}." -#: ../views/layout.erb:13 +#: ../views/layout.erb:22 msgid "Logout" msgstr "Desconnexió" -#: ../views/layout.erb:16 ../views/login.erb:1 ../views/login.erb:16 +#: ../views/layout.erb:25 ../views/login.erb:1 ../views/login.erb:16 msgid "Login" msgstr "Connexió" #: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13 #: ../views/signup.erb:24 ../views/votes_edit.erb:51 -#: ../views/votes_show_closed.erb:85 ../views/votes_show_open.erb:58 +#: ../views/votes_show_closed.erb:99 ../views/votes_show_open.erb:62 msgid "Email" msgstr "Correu" @@ -122,7 +122,7 @@ msgid "Specify a password." msgstr "Entra una contrasenya." #: ../views/reset_email.erb:1 -msgid "Visit the following link to reset your password:" +msgid "Visit this link to reset your password:" msgstr "Visita aquest enllaç per reiniciar la teva contrasenya:" #: ../views/reset_invalid.erb:3 @@ -153,6 +153,14 @@ msgstr "El correu no és una direcció de correu vàlida." msgid "An account already exists for %{email}." msgstr "Un compte ja existeix pel correu %{email}." +#: ../views/votes_close_email.erb:1 +msgid "Results:" +msgstr "Resultats:" + +#: ../views/votes_close_email.erb:5 +msgid "Visit this link to see the full results:" +msgstr "Visita aquest enllaç per veure els resultats complets:" + #: ../views/votes_edit.erb:1 msgid "Edit draft vote" msgstr "Editar un esborrany de votació" @@ -166,11 +174,12 @@ msgstr "Títol" msgid "Description" msgstr "Descripció" -#: ../views/votes_edit.erb:12 ../views/votes_show_open.erb:23 +#: ../views/votes_edit.erb:12 ../views/votes_show_open.erb:27 msgid "Save" msgstr "Guardar" -#: ../views/votes_edit.erb:15 ../views/votes_show_draft.erb:13 +#: ../views/votes_edit.erb:15 ../views/votes_open.erb:23 +#: ../views/votes_show_draft.erb:13 msgid "Candidates" msgstr "Opcions" @@ -186,23 +195,24 @@ msgstr "Afegeix una opció" msgid "Name" msgstr "Nom" -#: ../views/votes_edit.erb:39 ../views/votes_show_closed.erb:65 -#: ../views/votes_show_draft.erb:5 ../views/votes_show_open.erb:34 +#: ../views/votes_edit.erb:39 ../views/votes_show_closed.erb:79 +#: ../views/votes_show_draft.erb:5 ../views/votes_show_open.erb:38 msgid "Organizers" msgstr "Organitzadores" #: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54 -#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 -#: ../views/votes_show_open.erb:54 ../views/votes_show_open.erb:61 +#: ../views/votes_show_closed.erb:95 ../views/votes_show_closed.erb:102 +#: ../views/votes_show_open.erb:58 ../views/votes_show_open.erb:65 msgid "Add organizer" msgstr "Afegeix organitzadora" -#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:75 -#: ../views/votes_show_open.erb:44 +#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:89 +#: ../views/votes_show_open.erb:48 msgid "Actions for organizers" msgstr "Accions per les organitzadores" #: ../views/votes_edit.erb:61 ../views/votes_edit.erb:64 +#: ../views/votes_open.erb:12 msgid "Open vote to participants" msgstr "Obrir la votació als votants" @@ -218,59 +228,80 @@ msgstr "Suprimir la votació" msgid "New vote" msgstr "Nova votació" -#: ../views/votes_show_closed.erb:5 +#: ../views/votes_open.erb:1 +msgid "Open vote" +msgstr "Obrir la votació" + +#: ../views/votes_open.erb:5 +msgid "" +"The vote will automatically close on that date and the results will\n" +"be sent to all users by email." +msgstr "" +"La votació es tancarà automàticament en aquesta data i els resultats\n" +"s'enviaran a tothom per email." + +#: ../views/votes_open.erb:8 +msgid "" +"You won't be able to modify or delete this vote anymore after\n" +"opening it." +msgstr "Ja no podràs modificar o suprimir aquesta votació desprès d'obrir-la." + +#: ../views/votes_open.erb:16 +msgid "Cancel" +msgstr "Cancel·lar" + +#: ../views/votes_show_closed.erb:11 msgid "All ratings" msgstr "Totes les valoracions" -#: ../views/votes_show_closed.erb:9 +#: ../views/votes_show_closed.erb:15 msgid "Participant" msgstr "Votant" -#: ../views/votes_show_closed.erb:27 +#: ../views/votes_show_closed.erb:33 ../views/votes_show_closed.erb:73 msgid "Results" msgstr "Resultats" -#: ../views/votes_show_closed.erb:31 +#: ../views/votes_show_closed.erb:37 msgid "Rank" msgstr "Rang" -#: ../views/votes_show_closed.erb:32 +#: ../views/votes_show_closed.erb:38 msgid "Candidate" msgstr "Opció" -#: ../views/votes_show_closed.erb:33 +#: ../views/votes_show_closed.erb:39 msgid "Majority Judgment" msgstr "Judici majoritari" -#: ../views/votes_show_closed.erb:34 +#: ../views/votes_show_closed.erb:40 msgid "Proponents" msgstr "Defensores" -#: ../views/votes_show_closed.erb:35 +#: ../views/votes_show_closed.erb:41 msgid "Opponents" msgstr "Detractores" -#: ../views/votes_show_closed.erb:78 +#: ../views/votes_show_closed.erb:75 +msgid "No results are available because nobody voted on this vote." +msgstr "No hi ha cap resultat perquè ningú va votar en aquesta votació." + +#: ../views/votes_show_closed.erb:92 msgid "Reopen voting period" msgstr "Reobrir el període de votació" -#: ../views/votes_show_open.erb:5 +#: ../views/votes_show_open.erb:9 msgid "Your ratings" msgstr "Les teves valoracions" -#: ../views/votes_show_open.erb:26 +#: ../views/votes_show_open.erb:30 msgid "Participants" msgstr "Votants" -#: ../views/votes_show_open.erb:47 +#: ../views/votes_show_open.erb:51 msgid "Change back to draft vote" msgstr "Tornar a l'esborrany de votació" -#: ../views/votes_show_open.erb:51 +#: ../views/votes_show_open.erb:55 msgid "Close votes and show results" msgstr "Tancar la votació i veure els resultats" - -#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 -#: ../views/votes_show_open.erb:61 -#~ msgid "Add" -#~ msgstr "Afegir" diff --git a/po/vedia.pot b/po/vedia.pot index 828aa54..6218cdc 100644 --- a/po/vedia.pot +++ b/po/vedia.pot @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-06-07 20:04-0600\n" -"PO-Revision-Date: 2025-06-07 20:04-0600\n" +"POT-Creation-Date: 2025-06-08 15:49-0600\n" +"PO-Revision-Date: 2025-06-08 15:49-0600\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" @@ -18,39 +18,39 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: ../vedia.rb:69 +#: ../config/environments/defaults.rb:2 msgid "Awful" msgstr "" -#: ../vedia.rb:70 +#: ../config/environments/defaults.rb:3 msgid "Very bad" msgstr "" -#: ../vedia.rb:71 +#: ../config/environments/defaults.rb:4 msgid "Bad" msgstr "" -#: ../vedia.rb:72 +#: ../config/environments/defaults.rb:5 msgid "Mediocre" msgstr "" -#: ../vedia.rb:73 +#: ../config/environments/defaults.rb:6 msgid "Good" msgstr "" -#: ../vedia.rb:74 +#: ../config/environments/defaults.rb:7 msgid "Very good" msgstr "" -#: ../vedia.rb:126 +#: ../vedia.rb:118 msgid "Incorrect email or password." msgstr "" -#: ../vedia.rb:144 +#: ../vedia.rb:140 msgid "Reset your password" msgstr "" -#: ../views/home.erb:1 ../views/layout.erb:14 +#: ../views/home.erb:1 ../views/layout.erb:23 msgid "Home" msgstr "" @@ -86,21 +86,21 @@ msgstr "" msgid "Create new vote" msgstr "" -#: ../views/layout.erb:13 +#: ../views/layout.erb:22 msgid "Logged in as %{email}." msgstr "" -#: ../views/layout.erb:13 +#: ../views/layout.erb:22 msgid "Logout" msgstr "" -#: ../views/layout.erb:16 ../views/login.erb:1 ../views/login.erb:16 +#: ../views/layout.erb:25 ../views/login.erb:1 ../views/login.erb:16 msgid "Login" msgstr "" #: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13 #: ../views/signup.erb:24 ../views/votes_edit.erb:51 -#: ../views/votes_show_closed.erb:85 ../views/votes_show_open.erb:58 +#: ../views/votes_show_closed.erb:99 ../views/votes_show_open.erb:62 msgid "Email" msgstr "" @@ -123,7 +123,7 @@ msgid "Specify a password." msgstr "" #: ../views/reset_email.erb:1 -msgid "Visit the following link to reset your password:" +msgid "Visit this link to reset your password:" msgstr "" #: ../views/reset_invalid.erb:3 @@ -152,6 +152,14 @@ msgstr "" msgid "An account already exists for %{email}." msgstr "" +#: ../views/votes_close_email.erb:1 +msgid "Results:" +msgstr "" + +#: ../views/votes_close_email.erb:5 +msgid "Visit this link to see the full results:" +msgstr "" + #: ../views/votes_edit.erb:1 msgid "Edit draft vote" msgstr "" @@ -165,11 +173,12 @@ msgstr "" msgid "Description" msgstr "" -#: ../views/votes_edit.erb:12 ../views/votes_show_open.erb:23 +#: ../views/votes_edit.erb:12 ../views/votes_show_open.erb:27 msgid "Save" msgstr "" -#: ../views/votes_edit.erb:15 ../views/votes_show_draft.erb:13 +#: ../views/votes_edit.erb:15 ../views/votes_open.erb:23 +#: ../views/votes_show_draft.erb:13 msgid "Candidates" msgstr "" @@ -185,23 +194,24 @@ msgstr "" msgid "Name" msgstr "" -#: ../views/votes_edit.erb:39 ../views/votes_show_closed.erb:65 -#: ../views/votes_show_draft.erb:5 ../views/votes_show_open.erb:34 +#: ../views/votes_edit.erb:39 ../views/votes_show_closed.erb:79 +#: ../views/votes_show_draft.erb:5 ../views/votes_show_open.erb:38 msgid "Organizers" msgstr "" #: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54 -#: ../views/votes_show_closed.erb:81 ../views/votes_show_closed.erb:88 -#: ../views/votes_show_open.erb:54 ../views/votes_show_open.erb:61 +#: ../views/votes_show_closed.erb:95 ../views/votes_show_closed.erb:102 +#: ../views/votes_show_open.erb:58 ../views/votes_show_open.erb:65 msgid "Add organizer" msgstr "" -#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:75 -#: ../views/votes_show_open.erb:44 +#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:89 +#: ../views/votes_show_open.erb:48 msgid "Actions for organizers" msgstr "" #: ../views/votes_edit.erb:61 ../views/votes_edit.erb:64 +#: ../views/votes_open.erb:12 msgid "Open vote to participants" msgstr "" @@ -217,54 +227,78 @@ msgstr "" msgid "New vote" msgstr "" -#: ../views/votes_show_closed.erb:5 +#: ../views/votes_open.erb:1 +msgid "Open vote" +msgstr "" + +#: ../views/votes_open.erb:5 +msgid "" +"The vote will automatically close on that date and the results will\n" +"be sent to all users by email." +msgstr "" + +#: ../views/votes_open.erb:8 +msgid "" +"You won't be able to modify or delete this vote anymore after\n" +"opening it." +msgstr "" + +#: ../views/votes_open.erb:16 +msgid "Cancel" +msgstr "" + +#: ../views/votes_show_closed.erb:11 msgid "All ratings" msgstr "" -#: ../views/votes_show_closed.erb:9 +#: ../views/votes_show_closed.erb:15 msgid "Participant" msgstr "" -#: ../views/votes_show_closed.erb:27 +#: ../views/votes_show_closed.erb:33 ../views/votes_show_closed.erb:73 msgid "Results" msgstr "" -#: ../views/votes_show_closed.erb:31 +#: ../views/votes_show_closed.erb:37 msgid "Rank" msgstr "" -#: ../views/votes_show_closed.erb:32 +#: ../views/votes_show_closed.erb:38 msgid "Candidate" msgstr "" -#: ../views/votes_show_closed.erb:33 +#: ../views/votes_show_closed.erb:39 msgid "Majority Judgment" msgstr "" -#: ../views/votes_show_closed.erb:34 +#: ../views/votes_show_closed.erb:40 msgid "Proponents" msgstr "" -#: ../views/votes_show_closed.erb:35 +#: ../views/votes_show_closed.erb:41 msgid "Opponents" msgstr "" -#: ../views/votes_show_closed.erb:78 +#: ../views/votes_show_closed.erb:75 +msgid "No results are available because nobody voted on this vote." +msgstr "" + +#: ../views/votes_show_closed.erb:92 msgid "Reopen voting period" msgstr "" -#: ../views/votes_show_open.erb:5 +#: ../views/votes_show_open.erb:9 msgid "Your ratings" msgstr "" -#: ../views/votes_show_open.erb:26 +#: ../views/votes_show_open.erb:30 msgid "Participants" msgstr "" -#: ../views/votes_show_open.erb:47 +#: ../views/votes_show_open.erb:51 msgid "Change back to draft vote" msgstr "" -#: ../views/votes_show_open.erb:51 +#: ../views/votes_show_open.erb:55 msgid "Close votes and show results" msgstr "" diff --git a/vedia.rb b/vedia.rb index 1b2bffa..5c30dd2 100644 --- a/vedia.rb +++ b/vedia.rb @@ -5,7 +5,12 @@ require 'gettext' require 'securerandom' require 'chartkick' require 'mail' +require 'tzinfo' + +include GetText + require_relative 'mj' +require_relative 'config/environments/defaults.rb' require_relative "config/environments/#{settings.environment}" class Vote < ActiveRecord::Base @@ -55,19 +60,11 @@ def verify_password(password, hash) BCrypt::Password.new(hash) == password end -include GetText set_output_charset('UTF-8') bindtextdomain('vedia', 'locale') set_locale('ca') enable :sessions -set :values, [ { :id => 1, :label => _("Awful"), :color => '#ff4500' }, - { :id => 2, :label => _("Very bad"), :color => '#ffa500' }, - { :id => 3, :label => _("Bad"), :color => '#ffff00' }, - { :id => 4, :label => _("Mediocre"), :color => '#9acd32' }, - { :id => 5, :label => _("Good"), :color => '#228b22' }, - { :id => 6, :label => _("Very good"), :color => '#006400' } ] -set :admin_email, 'vedia@potager.org' MajorityJudgment.values = settings.values get '/' do @@ -123,6 +120,10 @@ post '/login' do end end +post '/timezone' do + session[:timezone] = JSON.parse(request.body.read)['timezone'] +end + get '/reset' do erb :reset end @@ -220,10 +221,6 @@ get '/votes/:id' do erb :votes_show_open when 'closed' erb :votes_show_closed - else - @vote.state = 'draft' - @vote.save - erb :votes_edit end end @@ -260,6 +257,15 @@ post '/votes/:id/candidates/:cid/delete' do redirect '/votes/' + @vote.secure_id end +get '/votes/:id/open' do + require_login + find_vote + require_organizer + require_draft_vote + @expire_on = Time.now.utc + settings.expire_after + erb :votes_open +end + post '/votes/:id/open' do require_login find_vote @@ -267,6 +273,7 @@ post '/votes/:id/open' do require_draft_vote if not @vote.candidates.length < 2 @vote.state = 'open' + @vote.expire_on = Time.now.utc + settings.expire_after @vote.save end redirect '/votes/' + @vote.secure_id @@ -277,6 +284,7 @@ post '/votes/:id/draft' do find_vote require_organizer require_open_vote + require_no_expire_on @vote.ratings.each {|r| r.destroy} @vote.state = 'draft' @vote.save @@ -288,6 +296,7 @@ post '/votes/:id/close' do find_vote require_organizer require_open_vote + require_no_expire_on @vote.state = 'closed' @vote.save redirect '/votes/' + @vote.secure_id @@ -298,6 +307,7 @@ post '/votes/:id/reopen' do find_vote require_organizer require_closed_vote + require_no_expire_on @vote.state = 'open' @vote.save redirect '/votes/' + @vote.secure_id @@ -332,6 +342,24 @@ post '/votes/:id/delete' do redirect '/' end +def close_expired_votes + Vote.where(state: 'open').where("expire_on < :now", { now: Time.now.utc }).each do |vote| + puts "#{Time.now.utc} Closing vote \"#{vote.title}\" because it expired on #{vote.expire_on}..." + vote.state = 'closed' + vote.save + User.all.each do |user| + puts "#{Time.now.utc} Sending results by email to #{user.email}..." + mail = Mail.new + mail.from = settings.admin_email + mail.to = user.email + mail.subject = _("Results of the vote: #{vote.title}") + template = ERB.new(File.read("views/votes_close_email.erb")) + mail.body = template.result(binding) + mail.deliver + end + end +end + helpers do def current_user if session[:user_id] @@ -364,4 +392,12 @@ helpers do def require_closed_vote redirect '/votes/' + @vote.secure_id unless @vote.state == 'closed' end + + def require_no_expire_on + redirect '/votes/' + @vote.secure_id unless @vote.expire_on.nil? + end + + def format_date(timestamp) + "#{TZInfo::Timezone.get(session[:timezone]).to_local(timestamp).strftime('%F %R')} (#{session[:timezone].gsub('_', ' ')})" + end end diff --git a/views/layout.erb b/views/layout.erb index da0085e..1041d63 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -8,6 +8,15 @@ + <% if current_user %>

<%= _("Logged in as %{email}.") % { email: current_user.email } %> <%= _("Logout") %>

diff --git a/views/reset_email.erb b/views/reset_email.erb index 68467b4..ded765c 100644 --- a/views/reset_email.erb +++ b/views/reset_email.erb @@ -1,3 +1,3 @@ -<%= _("Visit the following link to reset your password:") %> +<%= _("Visit this link to reset your password:") %> <%= "#{settings.base_url}reset/#{@reset}" %> diff --git a/views/votes_close_email.erb b/views/votes_close_email.erb new file mode 100644 index 0000000..065d130 --- /dev/null +++ b/views/votes_close_email.erb @@ -0,0 +1,7 @@ +<%= _("Results:") %> +<% i = 0 %><% vote.candidates.sort { |a, b| a.mj <=> b.mj }.reverse.each do |candidate| %><% i = i + 1 %> +<%= "#{i}. #{candidate.name}" %> +<% end %> +<%= _("Visit this link to see the full results:") %> + +<%= "#{settings.base_url}votes/#{vote.secure_id}" %> diff --git a/views/votes_edit.erb b/views/votes_edit.erb index 3dac749..7ee4e93 100644 --- a/views/votes_edit.erb +++ b/views/votes_edit.erb @@ -56,7 +56,7 @@

<%= _("Actions for organizers") %>

-
+ <% if @vote.candidates.length < 2 %>

<%= _("Add at least 2 candidates before opening the vote to participants.") %>

diff --git a/views/votes_open.erb b/views/votes_open.erb new file mode 100644 index 0000000..bf7783a --- /dev/null +++ b/views/votes_open.erb @@ -0,0 +1,28 @@ +

<%= _("Open vote") %>

+ +

<%= _("Closing date: #{format_date(@expire_on)}") %>

+ +

<%= _("The vote will automatically close on that date and the results will +be sent to all users by email.") %>

+ +

<%= _("You won't be able to modify or delete this vote anymore after +opening it.") %>

+ + + +
+ +
+ +
+ +

<%= @vote.title %>

+ +

<%= @vote.description %>

+ +

<%= _("Candidates") %>

+ +<% @vote.candidates.each do |candidate| %> +

<%= candidate.name %>

+

<%= candidate.description %>

+<% end %> diff --git a/views/votes_show_closed.erb b/views/votes_show_closed.erb index 8bc19e5..e482275 100644 --- a/views/votes_show_closed.erb +++ b/views/votes_show_closed.erb @@ -1,7 +1,13 @@

<%= @vote.title %>

+<% if @vote.expire_on %> +

<%= _("Closing date: #{format_date(@vote.expire_on)}") %>

+<% end %> +

<%= @vote.description %>

+<% if @vote.ratings.length > 0 %> +

<%= _("All ratings") %>

@@ -62,6 +68,14 @@ end %> <%= bar_chart data, colors: colors, stacked: true, legend: 'bottom' %> +<% else %> + +

<%= _("Results") %>

+ +<%= _("No results are available because nobody voted on this vote.") %> + +<% end %> +

<%= _("Organizers") %>

    @@ -70,7 +84,7 @@ end <% end %>
-<% if @vote.users.exists?(current_user.id) %> +<% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %>

<%= _("Actions for organizers") %>

diff --git a/views/votes_show_open.erb b/views/votes_show_open.erb index c9e6091..c3c11c7 100644 --- a/views/votes_show_open.erb +++ b/views/votes_show_open.erb @@ -1,5 +1,9 @@

<%= @vote.title %>

+<% if @vote.expire_on %> +

<%= _("Closing date: #{format_date(@vote.expire_on)}") %>

+<% end %> +

<%= @vote.description %>

<%= _("Your ratings") %>

@@ -39,7 +43,7 @@ <% end %> -<% if @vote.users.exists?(current_user.id) %> +<% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %>

<%= _("Actions for organizers") %>