decidim-elections 0.23.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/app/assets/config/admin/decidim_votings_manifest.css +0 -0
  4. data/app/assets/config/admin/decidim_votings_manifest.js +4 -0
  5. data/app/assets/config/decidim_elections_manifest.css +3 -0
  6. data/app/assets/config/decidim_elections_manifest.js +6 -0
  7. data/app/assets/config/decidim_votings_manifest.css +3 -0
  8. data/app/assets/config/decidim_votings_manifest.js +0 -0
  9. data/app/assets/images/decidim/votings/icon.svg +1 -0
  10. data/app/assets/javascripts/decidim/elections/admin/pending_action.js.es6 +16 -0
  11. data/app/assets/javascripts/decidim/elections/admin/vote_statistics.js.es6 +11 -0
  12. data/app/assets/javascripts/decidim/elections/trustee_zone.js.es6 +54 -0
  13. data/app/assets/javascripts/decidim/elections/trustee_zone/key_ceremony.js.es6 +126 -0
  14. data/app/assets/javascripts/decidim/elections/trustee_zone/tally.js.es6 +104 -0
  15. data/app/assets/javascripts/decidim/elections/vote.js.es6 +107 -160
  16. data/app/assets/javascripts/decidim/elections/vote_questions.component.js.es6 +129 -0
  17. data/app/assets/javascripts/decidim/elections/vote_verify.js.es6 +71 -0
  18. data/app/assets/javascripts/decidim/votings/admin/monitoring_committee_members_form.js.es6 +35 -0
  19. data/app/assets/javascripts/decidim/votings/admin/polling_officers_form.js.es6 +35 -0
  20. data/app/assets/javascripts/decidim/votings/admin/polling_officers_picker.js.es6 +35 -0
  21. data/app/assets/javascripts/decidim/votings/admin/polling_stations_form.js.es6 +5 -0
  22. data/app/assets/stylesheets/decidim/elections/elections.scss +0 -4
  23. data/app/assets/stylesheets/decidim/elections/focus/_accordion.scss +36 -0
  24. data/app/assets/stylesheets/decidim/elections/focus/_evote.scss +58 -0
  25. data/app/assets/stylesheets/decidim/elections/trustee_zone.scss +24 -0
  26. data/app/assets/stylesheets/decidim/votings/votings.scss +1 -0
  27. data/app/assets/stylesheets/decidim/votings/votings/_votings-header.scss +99 -0
  28. data/app/assets/stylesheets/decidim/votings/votings/_votings-home-banner.scss +11 -0
  29. data/app/cells/decidim/votings/content_block_cell.rb +17 -0
  30. data/app/cells/decidim/votings/content_blocks/highlighted_votings/show.erb +30 -0
  31. data/app/cells/decidim/votings/content_blocks/highlighted_votings_cell.rb +33 -0
  32. data/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form/show.erb +3 -0
  33. data/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell.rb +19 -0
  34. data/app/cells/decidim/votings/content_blocks/landing_page/attachments_and_folders_cell.rb +15 -0
  35. data/app/cells/decidim/votings/content_blocks/landing_page/description_cell.rb +15 -0
  36. data/app/cells/decidim/votings/content_blocks/landing_page/elections_cell.rb +15 -0
  37. data/app/cells/decidim/votings/content_blocks/landing_page/header_cell.rb +15 -0
  38. data/app/cells/decidim/votings/content_blocks/landing_page/metrics_cell.rb +15 -0
  39. data/app/cells/decidim/votings/content_blocks/landing_page/polling_stations_cell.rb +15 -0
  40. data/app/cells/decidim/votings/content_blocks/landing_page/stats_cell.rb +15 -0
  41. data/app/cells/decidim/votings/content_blocks/landing_page/timeline_cell.rb +15 -0
  42. data/app/cells/decidim/votings/polling_officers/polling_officers_picker/polling_officers.erb +12 -0
  43. data/app/cells/decidim/votings/polling_officers/polling_officers_picker/show.erb +14 -0
  44. data/app/cells/decidim/votings/polling_officers/polling_officers_picker_cell.rb +73 -0
  45. data/app/cells/decidim/votings/voting_cell.rb +19 -0
  46. data/app/cells/decidim/votings/voting_m/data.erb +21 -0
  47. data/app/cells/decidim/votings/voting_m/footer.erb +5 -0
  48. data/app/cells/decidim/votings/voting_m_cell.rb +94 -0
  49. data/app/commands/decidim/elections/admin/add_user_as_trustee.rb +81 -0
  50. data/app/commands/decidim/elections/admin/create_election.rb +2 -1
  51. data/app/commands/decidim/elections/admin/end_vote.rb +63 -0
  52. data/app/commands/decidim/elections/admin/publish_results.rb +59 -0
  53. data/app/commands/decidim/elections/admin/remove_trustee_from_participatory_space.rb +37 -0
  54. data/app/commands/decidim/elections/admin/setup_election.rb +160 -0
  55. data/app/commands/decidim/elections/admin/start_key_ceremony.rb +63 -0
  56. data/app/commands/decidim/elections/admin/start_tally.rb +63 -0
  57. data/app/commands/decidim/elections/admin/start_vote.rb +63 -0
  58. data/app/commands/decidim/elections/admin/update_action_status.rb +47 -0
  59. data/app/commands/decidim/elections/admin/update_answer_selection.rb +41 -0
  60. data/app/commands/decidim/elections/admin/update_trustee_participatory_space.rb +40 -0
  61. data/app/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status.rb +39 -0
  62. data/app/commands/decidim/elections/trustee_zone/update_trustee.rb +41 -0
  63. data/app/commands/decidim/elections/voter/cast_vote.rb +59 -0
  64. data/app/commands/decidim/elections/voter/update_vote_status.rb +70 -0
  65. data/app/commands/decidim/votings/admin/create_monitoring_committee_member.rb +99 -0
  66. data/app/commands/decidim/votings/admin/create_polling_officer.rb +99 -0
  67. data/app/commands/decidim/votings/admin/create_polling_station.rb +59 -0
  68. data/app/commands/decidim/votings/admin/create_voting.rb +59 -0
  69. data/app/commands/decidim/votings/admin/destroy_monitoring_committee_member.rb +51 -0
  70. data/app/commands/decidim/votings/admin/destroy_polling_officer.rb +52 -0
  71. data/app/commands/decidim/votings/admin/destroy_polling_station.rb +40 -0
  72. data/app/commands/decidim/votings/admin/manage_polling_station.rb +64 -0
  73. data/app/commands/decidim/votings/admin/publish_voting.rb +40 -0
  74. data/app/commands/decidim/votings/admin/unpublish_voting.rb +40 -0
  75. data/app/commands/decidim/votings/admin/update_polling_station.rb +50 -0
  76. data/app/commands/decidim/votings/admin/update_voting.rb +85 -0
  77. data/app/constraints/decidim/votings/current_component.rb +28 -0
  78. data/app/constraints/decidim/votings/current_voting.rb +43 -0
  79. data/app/controllers/concerns/decidim/elections/orderable.rb +2 -2
  80. data/app/controllers/concerns/decidim/polling_officers/admin/filterable.rb +58 -0
  81. data/app/controllers/concerns/decidim/polling_stations/admin/filterable.rb +57 -0
  82. data/app/controllers/concerns/decidim/votings/admin/filterable.rb +27 -0
  83. data/app/controllers/concerns/decidim/votings/admin/voting_admin.rb +48 -0
  84. data/app/controllers/concerns/decidim/votings/needs_voting.rb +44 -0
  85. data/app/controllers/concerns/decidim/votings/orderable.rb +32 -0
  86. data/app/controllers/decidim/elections/admin/answers_controller.rb +32 -0
  87. data/app/controllers/decidim/elections/admin/elections_controller.rb +2 -2
  88. data/app/controllers/decidim/elections/admin/feedback_forms_controller.rb +55 -0
  89. data/app/controllers/decidim/elections/admin/steps_controller.rb +96 -0
  90. data/app/controllers/decidim/elections/admin/trustees_participatory_spaces_controller.rb +89 -0
  91. data/app/controllers/decidim/elections/elections_controller.rb +5 -1
  92. data/app/controllers/decidim/elections/feedbacks_controller.rb +38 -0
  93. data/app/controllers/decidim/elections/trustee_zone/application_controller.rb +45 -0
  94. data/app/controllers/decidim/elections/trustee_zone/elections_controller.rb +35 -0
  95. data/app/controllers/decidim/elections/trustee_zone/trustees_controller.rb +36 -0
  96. data/app/controllers/decidim/elections/votes_controller.rb +54 -2
  97. data/app/controllers/decidim/votings/admin/application_controller.rb +26 -0
  98. data/app/controllers/decidim/votings/admin/component_permissions_controller.rb +13 -0
  99. data/app/controllers/decidim/votings/admin/components_controller.rb +15 -0
  100. data/app/controllers/decidim/votings/admin/monitoring_committee_members_controller.rb +62 -0
  101. data/app/controllers/decidim/votings/admin/polling_officers_controller.rb +66 -0
  102. data/app/controllers/decidim/votings/admin/polling_stations_controller.rb +92 -0
  103. data/app/controllers/decidim/votings/admin/voting_attachment_collections_controller.rb +22 -0
  104. data/app/controllers/decidim/votings/admin/voting_attachments_controller.rb +21 -0
  105. data/app/controllers/decidim/votings/admin/votings_controller.rb +124 -0
  106. data/app/controllers/decidim/votings/admin/votings_landing_page_content_blocks_controller.rb +44 -0
  107. data/app/controllers/decidim/votings/admin/votings_landing_page_controller.rb +46 -0
  108. data/app/controllers/decidim/votings/application_controller.rb +26 -0
  109. data/app/controllers/decidim/votings/polling_officer_zone/application_controller.rb +33 -0
  110. data/app/controllers/decidim/votings/polling_officer_zone/polling_officers_controller.rb +17 -0
  111. data/app/controllers/decidim/votings/polling_officer_zone/polling_stations_controller.rb +22 -0
  112. data/app/controllers/decidim/votings/votings_controller.rb +98 -0
  113. data/app/events/decidim/elections/trustees/notify_new_trustee_event.rb +28 -0
  114. data/app/events/decidim/elections/trustees/notify_trustee_new_election_event.rb +14 -0
  115. data/app/events/decidim/elections/votes/vote_accepted_event.rb +24 -0
  116. data/app/events/decidim/votings/polling_officers/polling_station_assigned_event.rb +44 -0
  117. data/app/forms/decidim/elections/admin/action_form.rb +34 -0
  118. data/app/forms/decidim/elections/admin/answer_form.rb +3 -2
  119. data/app/forms/decidim/elections/admin/election_form.rb +3 -2
  120. data/app/forms/decidim/elections/admin/setup_form.rb +79 -0
  121. data/app/forms/decidim/elections/admin/trustees_participatory_space_form.rb +22 -0
  122. data/app/forms/decidim/elections/admin/vote_period_form.rb +37 -0
  123. data/app/forms/decidim/elections/ballot/verify_vote_form.rb +29 -0
  124. data/app/forms/decidim/elections/trustee_zone/trustee_form.rb +26 -0
  125. data/app/forms/decidim/elections/voter/encrypted_vote_form.rb +49 -0
  126. data/app/forms/decidim/votings/admin/monitoring_committee_member_form.rb +27 -0
  127. data/app/forms/decidim/votings/admin/polling_officer_form.rb +27 -0
  128. data/app/forms/decidim/votings/admin/polling_station_form.rb +58 -0
  129. data/app/forms/decidim/votings/admin/voting_form.rb +71 -0
  130. data/app/helpers/decidim/elections/admin/answers_helper.rb +19 -0
  131. data/app/helpers/decidim/elections/admin/steps_helper.rb +23 -0
  132. data/app/helpers/decidim/elections/admin/trustees_participatory_spaces_helper.rb +31 -0
  133. data/app/helpers/decidim/elections/elections_helper.rb +29 -0
  134. data/app/helpers/decidim/votings/admin/polling_officers_picker_helper.rb +29 -0
  135. data/app/helpers/decidim/votings/votings_helper.rb +20 -0
  136. data/app/models/decidim/elections/action.rb +13 -0
  137. data/app/models/decidim/elections/election.rb +54 -3
  138. data/app/models/decidim/elections/elections_trustee.rb +11 -0
  139. data/app/models/decidim/elections/question.rb +19 -0
  140. data/app/models/decidim/elections/trustee.rb +26 -0
  141. data/app/models/decidim/elections/trustees_participatory_space.rb +13 -0
  142. data/app/models/decidim/elections/vote.rb +15 -0
  143. data/app/models/decidim/votings/monitoring_committee_member.rb +24 -0
  144. data/app/models/decidim/votings/polling_officer.rb +76 -0
  145. data/app/models/decidim/votings/polling_station.rb +64 -0
  146. data/app/models/decidim/votings/voting.rb +138 -0
  147. data/app/permissions/decidim/elections/admin/permissions.rb +50 -10
  148. data/app/permissions/decidim/elections/permissions.rb +20 -2
  149. data/app/permissions/decidim/elections/trustee_zone/permissions.rb +32 -0
  150. data/app/permissions/decidim/votings/admin/permissions.rb +127 -0
  151. data/app/permissions/decidim/votings/permissions.rb +40 -0
  152. data/app/permissions/decidim/votings/polling_officer_zone/permissions.rb +32 -0
  153. data/app/presenters/decidim/elections/admin_log/election_presenter.rb +1 -1
  154. data/app/presenters/decidim/elections/trustee_presenter.rb +24 -0
  155. data/app/presenters/decidim/votings/admin_log/voting_presenter.rb +45 -0
  156. data/app/presenters/decidim/votings/polling_station_presenter.rb +27 -0
  157. data/app/presenters/decidim/votings/voting_presenter.rb +36 -0
  158. data/app/queries/decidim/elections/admin/pending_actions.rb +20 -0
  159. data/app/queries/decidim/elections/admin/votes_for_statistics.rb +24 -0
  160. data/app/queries/decidim/elections/elections_finished_to_end.rb +19 -0
  161. data/app/queries/decidim/elections/elections_ready_to_start.rb +25 -0
  162. data/app/queries/decidim/elections/trustees/by_participatory_space.rb +24 -0
  163. data/app/queries/decidim/elections/trustees/by_participatory_space_trustee_ids.rb +24 -0
  164. data/app/queries/decidim/elections/votes/election_votes.rb +19 -0
  165. data/app/queries/decidim/elections/votes/pending_votes.rb +20 -0
  166. data/app/queries/decidim/elections/votes/user_election_last_vote.rb +26 -0
  167. data/app/queries/decidim/elections/votes/user_votes.rb +19 -0
  168. data/app/queries/decidim/votings/admin/admin_users.rb +39 -0
  169. data/app/queries/decidim/votings/admin/polling_officers_join_polling_stations.rb +33 -0
  170. data/app/queries/decidim/votings/admin/polling_officers_join_polling_stations_and_user.rb +34 -0
  171. data/app/queries/decidim/votings/admin/polling_officers_join_user.rb +32 -0
  172. data/app/queries/decidim/votings/organization_prioritized_votings.rb +21 -0
  173. data/app/queries/decidim/votings/organization_promoted_votings.rb +16 -0
  174. data/app/queries/decidim/votings/organization_published_votings.rb +19 -0
  175. data/app/queries/decidim/votings/organization_votings.rb +16 -0
  176. data/app/queries/decidim/votings/prioritized_votings.rb +13 -0
  177. data/app/queries/decidim/votings/published_votings.rb +12 -0
  178. data/app/services/decidim/votings/voting_search.rb +46 -0
  179. data/app/views/decidim/elections/admin/answers/index.html.erb +12 -0
  180. data/app/views/decidim/elections/admin/elections/index.html.erb +13 -1
  181. data/app/views/decidim/elections/admin/proposals_imports/new.html.erb +1 -1
  182. data/app/views/decidim/elections/admin/questions/index.html.erb +5 -1
  183. data/app/views/decidim/elections/admin/shared/_trustees_secondary_nav.html.erb +3 -0
  184. data/app/views/decidim/elections/admin/steps/_create_election.html.erb +38 -0
  185. data/app/views/decidim/elections/admin/steps/_created.html.erb +16 -0
  186. data/app/views/decidim/elections/admin/steps/_key_ceremony.html.erb +16 -0
  187. data/app/views/decidim/elections/admin/steps/_key_ceremony_ended.html.erb +15 -0
  188. data/app/views/decidim/elections/admin/steps/_tally.html.erb +17 -0
  189. data/app/views/decidim/elections/admin/steps/_tally_ended.html.erb +9 -0
  190. data/app/views/decidim/elections/admin/steps/_vote.html.erb +21 -0
  191. data/app/views/decidim/elections/admin/steps/_vote_ended.html.erb +15 -0
  192. data/app/views/decidim/elections/admin/steps/_vote_stats.html.erb +31 -0
  193. data/app/views/decidim/elections/admin/steps/index.html.erb +43 -0
  194. data/app/views/decidim/elections/admin/trustees_participatory_spaces/_form.html.erb +19 -0
  195. data/app/views/decidim/elections/admin/trustees_participatory_spaces/index.html.erb +64 -0
  196. data/app/views/decidim/elections/admin/trustees_participatory_spaces/new.html.erb +7 -0
  197. data/app/views/decidim/elections/elections/_filters_small_view.html.erb +1 -1
  198. data/app/views/decidim/elections/elections/_preview.html.erb +26 -0
  199. data/app/views/decidim/elections/elections/_results.html.erb +47 -0
  200. data/app/views/decidim/elections/elections/show.html.erb +43 -18
  201. data/app/views/decidim/elections/trustee_zone/elections/_backup_modal.html.erb +24 -0
  202. data/app/views/decidim/elections/trustee_zone/elections/_key_ceremony_steps.html.erb +44 -0
  203. data/app/views/decidim/elections/trustee_zone/elections/_restore_modal.html.erb +25 -0
  204. data/app/views/decidim/elections/trustee_zone/elections/_tally_steps.html.erb +43 -0
  205. data/app/views/decidim/elections/trustee_zone/elections/show.html.erb +38 -0
  206. data/app/views/decidim/elections/trustee_zone/elections/update.js.erb +5 -0
  207. data/app/views/decidim/elections/trustee_zone/trustees/show.html.erb +102 -0
  208. data/app/views/decidim/elections/votes/_election_votes_confirmed.html.erb +38 -17
  209. data/app/views/decidim/elections/votes/_election_votes_failed.html.erb +21 -0
  210. data/app/views/decidim/elections/votes/_election_votes_question.html.erb +4 -4
  211. data/app/views/decidim/elections/votes/cast_failed.js.erb +1 -0
  212. data/app/views/decidim/elections/votes/cast_success.js.erb +1 -0
  213. data/app/views/decidim/elections/votes/new.html.erb +41 -33
  214. data/app/views/decidim/elections/votes/verify.html.erb +78 -0
  215. data/app/views/decidim/votings/admin/monitoring_committee_members/_form.html.erb +27 -0
  216. data/app/views/decidim/votings/admin/monitoring_committee_members/index.html.erb +41 -0
  217. data/app/views/decidim/votings/admin/monitoring_committee_members/new.html.erb +7 -0
  218. data/app/views/decidim/votings/admin/polling_officers/_form.html.erb +27 -0
  219. data/app/views/decidim/votings/admin/polling_officers/index.html.erb +50 -0
  220. data/app/views/decidim/votings/admin/polling_officers/new.html.erb +7 -0
  221. data/app/views/decidim/votings/admin/polling_stations/_form.html.erb +40 -0
  222. data/app/views/decidim/votings/admin/polling_stations/edit.html.erb +7 -0
  223. data/app/views/decidim/votings/admin/polling_stations/index.html.erb +55 -0
  224. data/app/views/decidim/votings/admin/polling_stations/new.html.erb +7 -0
  225. data/app/views/decidim/votings/admin/votings/_form.html.erb +63 -0
  226. data/app/views/decidim/votings/admin/votings/edit.html.erb +31 -0
  227. data/app/views/decidim/votings/admin/votings/index.html.erb +87 -0
  228. data/app/views/decidim/votings/admin/votings/new.html.erb +13 -0
  229. data/app/views/decidim/votings/admin/votings/polling_officers_picker.html.erb +2 -0
  230. data/app/views/decidim/votings/polling_officer_zone/polling_officers/show.html.erb +42 -0
  231. data/app/views/decidim/votings/polling_officer_zone/polling_stations/show.html.erb +1 -0
  232. data/app/views/decidim/votings/votings/_count.html.erb +1 -0
  233. data/app/views/decidim/votings/votings/_filters.html.erb +25 -0
  234. data/app/views/decidim/votings/votings/_filters_small_view.html.erb +23 -0
  235. data/app/views/decidim/votings/votings/_promoted_voting.html.erb +27 -0
  236. data/app/views/decidim/votings/votings/_voting_details.html.erb +7 -0
  237. data/app/views/decidim/votings/votings/_votings.html.erb +25 -0
  238. data/app/views/decidim/votings/votings/index.html.erb +42 -0
  239. data/app/views/decidim/votings/votings/index.js.erb +10 -0
  240. data/app/views/decidim/votings/votings/show.html.erb +20 -0
  241. data/app/views/layouts/decidim/_voting_header.html.erb +16 -0
  242. data/app/views/layouts/decidim/admin/voting.html.erb +101 -0
  243. data/app/views/layouts/decidim/voting.html.erb +16 -0
  244. data/config/initializers/decidim_bulletin_board.rb +23 -0
  245. data/config/locales/ca.yml +635 -6
  246. data/config/locales/cs.yml +635 -6
  247. data/config/locales/de.yml +635 -6
  248. data/config/locales/el.yml +4 -2
  249. data/config/locales/en.yml +636 -6
  250. data/config/locales/es-MX.yml +636 -7
  251. data/config/locales/es-PY.yml +636 -7
  252. data/config/locales/es.yml +636 -7
  253. data/config/locales/fi-plain.yml +636 -7
  254. data/config/locales/fi.yml +636 -7
  255. data/config/locales/fr-CA.yml +419 -6
  256. data/config/locales/fr.yml +419 -6
  257. data/config/locales/gl.yml +81 -4
  258. data/config/locales/it.yml +98 -6
  259. data/config/locales/ja.yml +12 -6
  260. data/config/locales/lv.yml +4 -2
  261. data/config/locales/nl.yml +502 -6
  262. data/config/locales/no.yml +0 -6
  263. data/config/locales/pl.yml +656 -10
  264. data/config/locales/pt.yml +4 -6
  265. data/config/locales/ro-RO.yml +214 -6
  266. data/config/locales/sv.yml +12 -7
  267. data/config/locales/tr-TR.yml +111 -6
  268. data/config/locales/zh-CN.yml +4 -6
  269. data/db/migrate/20200915142713_add_questionnaire_to_existing_elections.rb +29 -0
  270. data/db/migrate/20200918153813_create_decidim_elections_trustees_participatory_spaces.rb +13 -0
  271. data/db/migrate/20200918153824_create_decidim_elections_elections_trustees.rb +10 -0
  272. data/db/migrate/20200918153835_create_decidim_elections_trustees.rb +12 -0
  273. data/db/migrate/20201026163334_add_blocked_at_and_bb_status_to_elections.rb +8 -0
  274. data/db/migrate/20201028090110_add_votes_to_decidim_elections_answers.rb +7 -0
  275. data/db/migrate/20201028135614_add_selected_to_decidim_elections_answers.rb +7 -0
  276. data/db/migrate/20201209110653_create_decidim_elections_votes.rb +14 -0
  277. data/db/migrate/20201216091123_add_name_to_trustees.rb +7 -0
  278. data/db/migrate/20210113113818_add_message_id_to_decidim_elections_votes.rb +7 -0
  279. data/db/migrate/20210113120115_create_decidim_elections_votings.rb +20 -0
  280. data/db/migrate/20210114100749_add_published_at_to_decidim_votings_votings.rb +7 -0
  281. data/db/migrate/20210114111850_add_user_to_decidim_elections_votes.rb +7 -0
  282. data/db/migrate/20210120164634_add_promoted_to_voting.rb +7 -0
  283. data/db/migrate/20210125124801_add_voting_type_to_voting_voting.rb +7 -0
  284. data/db/migrate/20210129124956_create_decidim_elections_actions.rb +14 -0
  285. data/db/migrate/20210204132111_add_votings_polling_stations.rb +19 -0
  286. data/db/migrate/20210208090441_add_polling_officers_to_votings.rb +12 -0
  287. data/db/migrate/20210210090738_add_votings_polling_station_references_to_votings_polling_officer.rb +8 -0
  288. data/db/migrate/20210216074707_add_monitoring_committee_member.rb +12 -0
  289. data/db/migrate/20210310120708_add_followable_counter_cache_to_votings.rb +16 -0
  290. data/db/seeds/city.jpeg +0 -0
  291. data/lib/decidim/api/election_answer_type.rb +23 -0
  292. data/lib/decidim/api/election_question_type.rb +22 -0
  293. data/lib/decidim/api/election_type.rb +27 -0
  294. data/lib/decidim/api/elections_type.rb +32 -0
  295. data/lib/decidim/api/trustee_type.rb +16 -0
  296. data/lib/decidim/api/voting_type.rb +20 -0
  297. data/lib/decidim/elections.rb +19 -2
  298. data/lib/decidim/elections/admin_engine.rb +42 -1
  299. data/lib/decidim/elections/answer_serializer.rb +48 -0
  300. data/lib/decidim/elections/api.rb +11 -0
  301. data/lib/decidim/elections/component.rb +367 -24
  302. data/lib/decidim/elections/engine.rb +8 -1
  303. data/lib/decidim/elections/test/factories.rb +201 -4
  304. data/lib/decidim/elections/trustee_zone.rb +10 -0
  305. data/lib/decidim/elections/trustee_zone_engine.rb +35 -0
  306. data/lib/decidim/elections/version.rb +1 -1
  307. data/lib/decidim/votings.rb +16 -0
  308. data/lib/decidim/votings/admin.rb +10 -0
  309. data/lib/decidim/votings/admin_engine.rb +71 -0
  310. data/lib/decidim/votings/api.rb +7 -0
  311. data/lib/decidim/votings/engine.rb +147 -0
  312. data/lib/decidim/votings/participatory_space.rb +92 -0
  313. data/lib/decidim/votings/polling_officer_zone.rb +10 -0
  314. data/lib/decidim/votings/polling_officer_zone_engine.rb +33 -0
  315. data/lib/decidim/votings/query_extensions.rb +43 -0
  316. data/lib/decidim/votings/test/capybara_polling_officers_picker.rb +49 -0
  317. data/lib/decidim/votings/test/factories.rb +83 -0
  318. data/lib/decidim/votings/voting_serializer.rb +54 -0
  319. data/lib/tasks/decidim_elections.rake +68 -5
  320. metadata +303 -19
  321. data/app/types/decidim/elections/election_answer_type.rb +0 -24
  322. data/app/types/decidim/elections/election_question_type.rb +0 -25
  323. data/app/types/decidim/elections/election_type.rb +0 -27
  324. data/app/types/decidim/elections/elections_type.rb +0 -32
  325. data/config/i18n-tasks.yml +0 -10
  326. data/lib/decidim/elections/bulletin_board_client.rb +0 -35
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Elections
5
+ # The data store for an Election-Trustee relation in the Decidim::Elections component.
6
+ class ElectionsTrustee < ApplicationRecord
7
+ belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election", optional: true
8
+ belongs_to :trustee, foreign_key: "decidim_elections_trustee_id", class_name: "Decidim::Elections::Trustee", optional: true
9
+ end
10
+ end
11
+ end
@@ -22,6 +22,25 @@ module Decidim
22
22
  def valid_max_selection?
23
23
  max_selections <= answers.count
24
24
  end
25
+
26
+ def vote_variation
27
+ if max_selections == 1
28
+ "one_of_m"
29
+ else
30
+ "n_of_m"
31
+ end
32
+ end
33
+
34
+ def total_votes
35
+ answers.sum(:votes_count)
36
+ end
37
+
38
+ def votes_percentage(answer_votes)
39
+ return 0 unless answer_votes.positive?
40
+
41
+ result = answer_votes.to_f / total_votes * 100.0
42
+ result.round
43
+ end
25
44
  end
26
45
  end
27
46
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Elections
5
+ # The data store for a trustee in the Decidim::Elections component. It stores a
6
+ # public key and has a reference to Decidim::User.
7
+ class Trustee < ApplicationRecord
8
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
9
+ has_many :elections_trustees, foreign_key: "decidim_elections_trustee_id", dependent: :destroy
10
+ has_many :elections, through: :elections_trustees
11
+ has_many :trustees_participatory_spaces, inverse_of: :trustee, foreign_key: "decidim_elections_trustee_id", dependent: :destroy
12
+
13
+ def self.trustee?(user)
14
+ exists?(user: user)
15
+ end
16
+
17
+ def self.for(user)
18
+ find_by(user: user)
19
+ end
20
+
21
+ def unique_id
22
+ name.parameterize
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Elections
5
+ # The data store for a trustee participatory space in the Decidim::Elections component. It has a reference
6
+ # to a trustee (user), a polymorphic reference to participatory spaces and stores the status (considered)
7
+ # for the trustee.
8
+ class TrusteesParticipatorySpace < ApplicationRecord
9
+ belongs_to :trustee, foreign_key: "decidim_elections_trustee_id", class_name: "Decidim::Elections::Trustee", inverse_of: :trustees_participatory_spaces
10
+ belongs_to :participatory_space, foreign_type: "participatory_space_type", polymorphic: true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Elections
5
+ # The data store for a Vote in the Decidim::Election component. It stores the hash computed from the `encrypted_vote` associated to a particular `voter_id`.
6
+ class Vote < ApplicationRecord
7
+ enum status: [:pending, :accepted, :rejected].map { |status| [status, status.to_s] }.to_h
8
+
9
+ belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election"
10
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
11
+
12
+ validates :voter_id, :encrypted_vote_hash, presence: true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Votings
5
+ class MonitoringCommitteeMember < ApplicationRecord
6
+ include Traceable
7
+ include Loggable
8
+
9
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User", optional: true
10
+ belongs_to :voting, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::Voting", inverse_of: :monitoring_committee_members
11
+
12
+ validate :user_and_voting_same_organization
13
+
14
+ private
15
+
16
+ # Private: check if the voting and the user have the same organization
17
+ def user_and_voting_same_organization
18
+ return if !voting || !user
19
+
20
+ errors.add(:voting, :invalid) unless user.organization == voting.organization
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Votings
5
+ class PollingOfficer < ApplicationRecord
6
+ include Traceable
7
+ include Loggable
8
+
9
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
10
+ belongs_to :voting, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::Voting", inverse_of: :polling_officers
11
+ belongs_to :managed_polling_station,
12
+ class_name: "Decidim::Votings::PollingStation",
13
+ inverse_of: :polling_station_managers,
14
+ optional: true
15
+ belongs_to :presided_polling_station,
16
+ class_name: "Decidim::Votings::PollingStation",
17
+ inverse_of: :polling_station_president,
18
+ optional: true
19
+
20
+ validates :user, uniqueness: { scope: :voting }
21
+ validate :user_and_voting_same_organization
22
+ validate :either_president_or_manager
23
+
24
+ delegate :name, :nickname, :email, to: :user
25
+
26
+ # Allow ransacker to search by presided/managed polling station title
27
+ %w(managed_station presided_station).each do |table|
28
+ ransacker "#{table}_title".to_sym do
29
+ Arel::Nodes::InfixOperation.new("->>", Arel.sql("#{table}.title"), Arel::Nodes.build_quoted(I18n.locale.to_s))
30
+ end
31
+ end
32
+
33
+ # Allow ransacker to search by user attributes
34
+ [:name, :email, :nickname].each do |field|
35
+ ransacker field do
36
+ Arel.sql("decidim_users.#{field}")
37
+ end
38
+ end
39
+
40
+ def self.polling_officer?(user)
41
+ exists?(user: user)
42
+ end
43
+
44
+ def self.for(user)
45
+ where(user: user)
46
+ end
47
+
48
+ def role
49
+ return :president if presided_polling_station.present?
50
+ return :manager if managed_polling_station.present?
51
+
52
+ :unassigned
53
+ end
54
+
55
+ def polling_station
56
+ presided_polling_station || managed_polling_station
57
+ end
58
+
59
+ private
60
+
61
+ # Private: check if the voting and the user have the same organization
62
+ def user_and_voting_same_organization
63
+ return if voting.nil? || user.nil?
64
+
65
+ errors.add(:voting, :different_organization) unless user.organization == voting.organization
66
+ end
67
+
68
+ # Private: check if the voting and the user have the same organization
69
+ def either_president_or_manager
70
+ return if presided_polling_station.nil? || managed_polling_station.nil?
71
+
72
+ errors.add(:presided_polling_station, :president_and_manager)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Votings
5
+ # The data store for a PollingStation in the Votings::Voting partecipatory space.
6
+ class PollingStation < ApplicationRecord
7
+ include Traceable
8
+ include Loggable
9
+ include Decidim::TranslatableResource
10
+
11
+ translatable_fields :title, :location, :location_hints
12
+
13
+ belongs_to :voting, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::Voting", inverse_of: :polling_stations
14
+ has_many :polling_station_managers,
15
+ foreign_key: "managed_polling_station_id",
16
+ class_name: "Decidim::Votings::PollingOfficer",
17
+ inverse_of: :managed_polling_station,
18
+ dependent: :nullify
19
+ has_one :polling_station_president,
20
+ foreign_key: "presided_polling_station_id",
21
+ class_name: "Decidim::Votings::PollingOfficer",
22
+ inverse_of: :presided_polling_station,
23
+ dependent: :nullify
24
+
25
+ validate :polling_station_managers_same_voting
26
+ validate :polling_station_president_same_voting
27
+
28
+ # Allow ransacker to search for a key in a hstore column (`title`.`en`)
29
+ ransacker :title do |parent|
30
+ Arel::Nodes::InfixOperation.new("->>", parent.table[:title], Arel::Nodes.build_quoted(I18n.locale.to_s))
31
+ end
32
+
33
+ [:manager, :president].each do |role|
34
+ [:name, :email, :nickname].each do |field|
35
+ ransacker "#{role}_#{field}".to_sym do
36
+ Arel.sql("#{role}_user.#{field}")
37
+ end
38
+ end
39
+ end
40
+
41
+ geocoded_by :address
42
+
43
+ def missing_officers?
44
+ polling_station_president.nil? || polling_station_managers.empty?
45
+ end
46
+
47
+ private
48
+
49
+ # Private: check if the president is in the same voting
50
+ def polling_station_president_same_voting
51
+ return if polling_station_president.nil?
52
+
53
+ errors.add(:polling_station_president, :different_voting) unless voting == polling_station_president.voting
54
+ end
55
+
56
+ # Private: check if the managers are in the same voting
57
+ def polling_station_managers_same_voting
58
+ return if polling_station_managers.empty?
59
+
60
+ errors.add(:polling_station_managers, :different_voting) unless polling_station_managers.all? { |manager| manager.voting == voting }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Votings
5
+ class Voting < ApplicationRecord
6
+ include Traceable
7
+ include Loggable
8
+ include Decidim::Followable
9
+ include Decidim::Participable
10
+ include Decidim::ParticipatorySpaceResourceable
11
+ include Decidim::Randomable
12
+ include Decidim::Searchable
13
+ include Decidim::TranslatableResource
14
+ include Decidim::ScopableParticipatorySpace
15
+ include Decidim::Publicable
16
+ include Decidim::HasUploadValidations
17
+ include Decidim::HasAttachments
18
+ include Decidim::HasAttachmentCollections
19
+
20
+ enum voting_type: [:in_person, :online, :hybrid].map { |type| [type, type.to_s] }.to_h, _suffix: :voting
21
+
22
+ translatable_fields :title, :description
23
+
24
+ belongs_to :organization,
25
+ foreign_key: "decidim_organization_id",
26
+ class_name: "Decidim::Organization"
27
+
28
+ has_many :components, as: :participatory_space, dependent: :destroy
29
+ has_many :polling_stations, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::PollingStation", inverse_of: :voting, dependent: :destroy
30
+ has_many :polling_officers, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::PollingOfficer", inverse_of: :voting, dependent: :destroy
31
+ has_many :monitoring_committee_members,
32
+ foreign_key: "decidim_votings_voting_id",
33
+ class_name: "Decidim::Votings::MonitoringCommitteeMember",
34
+ inverse_of: :voting,
35
+ dependent: :destroy
36
+
37
+ validates :slug, uniqueness: { scope: :organization }
38
+ validates :slug, presence: true, format: { with: Decidim::Votings::Voting.slug_format }
39
+
40
+ validates_upload :banner_image
41
+ mount_uploader :banner_image, Decidim::BannerImageUploader
42
+
43
+ validates_upload :introductory_image
44
+ mount_uploader :introductory_image, Decidim::BannerImageUploader
45
+
46
+ scope :upcoming, -> { published.where("start_time > ?", Time.now.utc) }
47
+ scope :active, lambda {
48
+ published
49
+ .where("start_time <= ?", Time.now.utc)
50
+ .where("end_time >= ?", Time.now.utc)
51
+ }
52
+ scope :finished, -> { published.where("end_time < ?", Time.now.utc) }
53
+ scope :order_by_most_recent, -> { order(created_at: :desc) }
54
+ scope :promoted, -> { published.where(promoted: true) }
55
+
56
+ def upcoming?
57
+ start_time > Time.now.utc
58
+ end
59
+
60
+ def active?
61
+ start_time <= Time.now.utc && end_time >= Time.now.utc
62
+ end
63
+
64
+ def finished?
65
+ end_time < Time.now.utc
66
+ end
67
+
68
+ def period_status
69
+ if finished?
70
+ :finished
71
+ elsif active?
72
+ :ongoing
73
+ else
74
+ :upcoming
75
+ end
76
+ end
77
+
78
+ searchable_fields({
79
+ scope_id: :decidim_scope_id,
80
+ participatory_space: :itself,
81
+ A: :title,
82
+ B: :description,
83
+ datetime: :published_at
84
+ },
85
+ index_on_create: ->(_voting) { false },
86
+ index_on_update: ->(voting) { voting.visible? })
87
+
88
+ def self.log_presenter_class_for(_log)
89
+ Decidim::Votings::AdminLog::VotingPresenter
90
+ end
91
+
92
+ # Allow ransacker to search for a key in a hstore column (`title`.`en`)
93
+ ransacker :title do |parent|
94
+ Arel::Nodes::InfixOperation.new("->>", parent.table[:title], Arel::Nodes.build_quoted(I18n.locale.to_s))
95
+ end
96
+
97
+ def to_param
98
+ slug
99
+ end
100
+
101
+ def cta_button_text_key
102
+ return :vote if published? && active?
103
+
104
+ :more_info
105
+ end
106
+
107
+ def attachment_context
108
+ :admin
109
+ end
110
+
111
+ def scopes_enabled
112
+ true
113
+ end
114
+
115
+ def needs_elections?
116
+ !in_person_voting? && !has_elections?
117
+ end
118
+
119
+ def polling_stations_with_missing_officers?
120
+ !online_voting? && polling_stations.any?(&:missing_officers?)
121
+ end
122
+
123
+ def available_polling_officers
124
+ polling_officers
125
+ .where(presided_polling_station_id: nil)
126
+ .where(managed_polling_station_id: nil)
127
+ end
128
+
129
+ private
130
+
131
+ def has_elections?
132
+ components.where(manifest_name: :elections).any? do |component|
133
+ Decidim::Elections::Election.where(component: component).any?
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -10,19 +10,43 @@ module Decidim
10
10
  case permission_action.subject
11
11
  when :question, :answer
12
12
  case permission_action.action
13
- when :create, :update, :delete
14
- allow_if_not_started
15
- when :import_proposals
16
- allow_if_not_started
13
+ when :create, :update, :delete, :import_proposals
14
+ allow_if_not_blocked
15
+ when :select
16
+ allow_if_results
17
+ end
18
+ when :steps
19
+ case permission_action.action
20
+ when :read, :update
21
+ allow!
17
22
  end
18
23
  when :election
19
24
  case permission_action.action
20
25
  when :create, :read
21
26
  allow!
22
27
  when :delete, :update, :unpublish
23
- allow_if_not_started
28
+ allow_if_not_blocked
24
29
  when :publish
25
- allow_if_valid_and_not_started
30
+ allow_if_valid_and_not_blocked
31
+ end
32
+ when :trustee_participatory_space
33
+ case permission_action.action
34
+ when :create, :update
35
+ allow!
36
+ when :delete
37
+ allow_if_not_related_to_any_election
38
+ end
39
+ when :questionnaire
40
+ case permission_action.action
41
+ when :export_answers
42
+ permission_action.allow!
43
+ when :update
44
+ toggle_allow(feedback_form.present?)
45
+ end
46
+ when :questionnaire_answers
47
+ case permission_action.action
48
+ when :index, :show, :export_response
49
+ permission_action.allow!
26
50
  end
27
51
  end
28
52
 
@@ -39,12 +63,28 @@ module Decidim
39
63
  @question ||= context.fetch(:question, nil)
40
64
  end
41
65
 
42
- def allow_if_not_started
43
- toggle_allow(election && !election.started?)
66
+ def trustee_participatory_space
67
+ @trustee_participatory_space ||= context.fetch(:trustee_participatory_space, nil)
68
+ end
69
+
70
+ def allow_if_results
71
+ toggle_allow(election && election.results?)
72
+ end
73
+
74
+ def allow_if_not_blocked
75
+ toggle_allow(election && !election.blocked?)
76
+ end
77
+
78
+ def allow_if_valid_and_not_blocked
79
+ toggle_allow(election && !election.blocked? && election.valid_questions?)
80
+ end
81
+
82
+ def allow_if_not_related_to_any_election
83
+ toggle_allow(trustee_participatory_space.trustee.elections.empty?)
44
84
  end
45
85
 
46
- def allow_if_valid_and_not_started
47
- toggle_allow(election && !election.started? && election.valid_questions?)
86
+ def feedback_form
87
+ @feedback_form ||= context.fetch(:questionnaire, nil)
48
88
  end
49
89
  end
50
90
  end