From 362d85bf8075e04bb2bdf3bf535e7c1140c8da52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pereira=20de=20Lucena?= Date: Wed, 31 Jan 2024 18:52:30 +0100 Subject: [PATCH] Add decidim-elections module --- decidim-elections/.byebug_history | 89 + decidim-elections/.gitignore | 17 + decidim-elections/README.md | 103 ++ decidim-elections/Rakefile | 3 + .../content_blocks/related_elections_cell.rb | 13 + .../cells/decidim/elections/election_cell.rb | 27 + .../decidim/elections/election_g_cell.rb | 23 + .../elections/election_metadata_cell.rb | 57 + .../elections/election_preview/show.erb | 28 + .../elections/election_preview_cell.rb | 13 + .../election_results/progress_bar.erb | 15 + .../elections/election_results/show.erb | 43 + .../elections/election_results_cell.rb | 13 + .../decidim/elections/election_s_cell.rb | 10 + .../elections/election_vote_cta/show.erb | 23 + .../elections/election_vote_cta_cell.rb | 79 + .../show.erb | 18 + ...ighlighted_elections_for_component_cell.rb | 50 + .../elections/remaining_time_callout/show.erb | 3 + .../elections/remaining_time_callout_cell.rb | 28 + .../elections/voting_step_navigation/show.erb | 25 + .../elections/voting_step_navigation_cell.rb | 42 + .../decidim/votings/content_block_cell.rb | 21 + .../votings/content_blocks/hero_cell.rb | 41 + .../hero_settings_form/show.erb | 4 + .../content_blocks/hero_settings_form_cell.rb | 15 + .../highlighted_votings_cell.rb | 33 + .../show.erb | 3 + .../highlighted_votings_settings_form_cell.rb | 19 + .../votings/content_blocks/main_data_cell.rb | 34 + .../votings/content_blocks/metrics_cell.rb | 17 + .../content_blocks/polling_stations/show.erb | 21 + .../content_blocks/polling_stations_cell.rb | 31 + .../votings/content_blocks/statistics_cell.rb | 18 + .../polling_officers_picker/show.erb | 15 + .../polling_officers_picker_cell.rb | 76 + .../show.erb | 10 + ...olling_station_closure_certificate_cell.rb | 12 + .../polling_station_closure_recount/show.erb | 72 + .../polling_station_closure_recount_cell.rb | 9 + .../app/cells/decidim/votings/voting_cell.rb | 24 + .../votings/voting_dropdown_metadata_cell.rb | 19 + .../cells/decidim/votings/voting_g_cell.rb | 19 + .../cells/decidim/votings/voting_map/show.erb | 16 + .../cells/decidim/votings/voting_map_cell.rb | 28 + .../decidim/votings/voting_metadata_cell.rb | 66 + .../cells/decidim/votings/voting_s_cell.rb | 10 + .../elections/admin/add_user_as_trustee.rb | 92 ++ .../decidim/elections/admin/create_answer.rb | 70 + .../elections/admin/create_election.rb | 60 + .../elections/admin/create_question.rb | 49 + .../decidim/elections/admin/destroy_answer.rb | 49 + .../elections/admin/destroy_election.rb | 49 + .../elections/admin/destroy_question.rb | 46 + .../decidim/elections/admin/end_vote.rb | 63 + .../admin/import_proposals_to_elections.rb | 90 ++ .../elections/admin/publish_election.rb | 54 + .../elections/admin/publish_results.rb | 63 + ...remove_trustee_from_participatory_space.rb | 37 + .../elections/admin/report_missing_trustee.rb | 68 + .../decidim/elections/admin/setup_election.rb | 168 ++ .../elections/admin/start_key_ceremony.rb | 63 + .../decidim/elections/admin/start_tally.rb | 76 + .../decidim/elections/admin/start_vote.rb | 63 + .../elections/admin/unpublish_election.rb | 43 + .../elections/admin/update_action_status.rb | 47 + .../decidim/elections/admin/update_answer.rb | 72 + .../admin/update_answer_selection.rb | 41 + .../elections/admin/update_election.rb | 59 + .../elections/admin/update_question.rb | 52 + .../update_trustee_participatory_space.rb | 40 + .../update_election_bulletin_board_status.rb | 98 ++ .../elections/trustee_zone/update_trustee.rb | 44 + .../decidim/elections/voter/cast_vote.rb | 56 + .../elections/voter/update_vote_status.rb | 86 + .../votings/admin/create_ballot_style.rb | 61 + .../create_monitoring_committee_member.rb | 99 ++ .../votings/admin/create_polling_officer.rb | 99 ++ .../votings/admin/create_polling_station.rb | 59 + .../decidim/votings/admin/create_voting.rb | 61 + .../votings/admin/destroy_ballot_style.rb | 41 + .../destroy_monitoring_committee_member.rb | 51 + .../votings/admin/destroy_polling_officer.rb | 52 + .../votings/admin/destroy_polling_station.rb | 40 + .../votings/admin/manage_polling_station.rb | 64 + ...mittee_validate_polling_station_closure.rb | 37 + .../votings/admin/update_ballot_style.rb | 75 + .../votings/admin/update_polling_station.rb | 50 + .../decidim/votings/admin/update_voting.rb | 77 + .../votings/census/admin/create_dataset.rb | 101 ++ .../votings/census/admin/create_datum.rb | 55 + .../votings/census/admin/destroy_dataset.rb | 45 + .../admin/increment_dataset_processed_rows.rb | 43 + .../admin/launch_access_codes_export.rb | 40 + .../admin/launch_access_codes_generation.rb | 44 + .../votings/census/admin/update_dataset.rb | 51 + .../certify_polling_station_closure.rb | 47 + .../commands/decidim/votings/check_census.rb | 35 + .../votings/create_polling_station_closure.rb | 53 + .../votings/create_polling_station_results.rb | 85 + .../destroy_polling_station_closure.rb | 50 + .../decidim/votings/send_access_code.rb | 52 + .../votings/sign_polling_station_closure.rb | 41 + .../decidim/votings/voter/in_person_vote.rb | 55 + .../voter/update_in_person_vote_status.rb | 52 + .../decidim/votings/current_component.rb | 28 + .../decidim/votings/current_voting.rb | 43 + .../elections/content_security_policy.rb | 23 + .../decidim/elections/has_vote_flow.rb | 43 + .../concerns/decidim/elections/orderable.rb | 36 + .../admin/filterable.rb | 72 + .../polling_officers/admin/filterable.rb | 58 + .../polling_stations/admin/filterable.rb | 57 + .../decidim/votings/admin/filterable.rb | 27 + .../decidim/votings/admin/voting_admin.rb | 49 + .../concerns/decidim/votings/needs_voting.rb | 44 + .../concerns/decidim/votings/orderable.rb | 32 + .../elections/admin/answers_controller.rb | 131 ++ .../elections/admin/application_controller.rb | 16 + .../elections/admin/elections_controller.rb | 108 ++ .../admin/feedback_forms_controller.rb | 55 + .../admin/proposals_imports_controller.rb | 53 + .../elections/admin/questions_controller.rb | 91 ++ .../elections/admin/steps_controller.rb | 118 ++ ...rustees_participatory_spaces_controller.rb | 89 + .../elections/application_controller.rb | 14 + .../decidim/elections/elections_controller.rb | 96 ++ .../decidim/elections/feedbacks_controller.rb | 72 + .../trustee_zone/application_controller.rb | 48 + .../trustee_zone/elections_controller.rb | 49 + .../trustee_zone/trustees_controller.rb | 36 + .../decidim/elections/votes_controller.rb | 168 ++ .../votings/admin/application_controller.rb | 26 + .../votings/admin/ballot_styles_controller.rb | 93 ++ .../admin/component_permissions_controller.rb | 13 + .../votings/admin/components_controller.rb | 15 + .../votings/admin/exports_controller.rb | 14 + .../votings/admin/imports_controller.rb | 14 + ...g_committee_election_results_controller.rb | 77 + ...monitoring_committee_members_controller.rb | 81 + ...tee_polling_station_closures_controller.rb | 73 + ...g_committee_verify_elections_controller.rb | 24 + .../admin/polling_officers_controller.rb | 66 + .../admin/polling_stations_controller.rb | 95 ++ .../votings/admin/reminders_controller.rb | 14 + ...oting_attachment_collections_controller.rb | 27 + .../admin/voting_attachments_controller.rb | 26 + .../admin/voting_publications_controller.rb | 25 + .../votings/admin/votings_controller.rb | 100 ++ ..._landing_page_content_blocks_controller.rb | 40 + .../admin/votings_landing_page_controller.rb | 46 + .../decidim/votings/application_controller.rb | 28 + .../census/admin/application_controller.rb | 19 + .../votings/census/admin/census_controller.rb | 195 +++ .../application_controller.rb | 33 + .../closures_controller.rb | 149 ++ .../in_person_votes_controller.rb | 163 ++ .../polling_officers_controller.rb | 25 + .../decidim/votings/votings_controller.rb | 180 +++ .../elections/election_published_event.rb | 8 + .../trustees/notify_new_trustee_event.rb | 28 + .../notify_trustee_new_election_event.rb | 14 + .../notify_trustee_tally_process_event.rb | 14 + .../elections/votes/vote_accepted_event.rb | 24 + .../polling_station_assigned_event.rb | 44 + .../decidim/elections/admin/action_form.rb | 43 + .../decidim/elections/admin/answer_form.rb | 54 + .../admin/answer_import_proposals_form.rb | 41 + .../decidim/elections/admin/election_form.rb | 38 + .../decidim/elections/admin/question_form.rb | 25 + .../admin/report_missing_trustee_form.rb | 22 + .../decidim/elections/admin/setup_form.rb | 133 ++ .../trustees_participatory_space_form.rb | 22 + .../elections/admin/vote_period_form.rb | 37 + .../elections/trustee_zone/trustee_form.rb | 26 + .../elections/voter/verify_vote_form.rb | 25 + .../decidim/elections/voter/vote_form.rb | 50 + .../votings/admin/ballot_style_form.rb | 19 + ..._committee_polling_station_closure_form.rb | 11 + .../votings/admin/polling_station_form.rb | 58 + .../votings/admin/publish_results_form.rb | 64 + .../decidim/votings/admin/voting_form.rb | 73 + .../votings/admin/voting_user_role_form.rb | 27 + .../decidim/votings/answer_result_form.rb | 30 + .../decidim/votings/ballot_result_form.rb | 41 + .../votings/census/admin/dataset_form.rb | 22 + .../votings/census/admin/datum_form.rb | 34 + .../decidim/votings/census/check_fields.rb | 28 + .../decidim/votings/census/check_form.rb | 13 + .../decidim/votings/census/frontend_fields.rb | 42 + .../votings/census/in_person_fields.rb | 52 + .../decidim/votings/census/in_person_form.rb | 13 + .../decidim/votings/census/login_form.rb | 13 + .../decidim/votings/census/online_fields.rb | 28 + .../decidim/votings/closure_certify_form.rb | 25 + .../decidim/votings/closure_result_form.rb | 100 ++ .../decidim/votings/closure_sign_form.rb | 11 + .../decidim/votings/envelopes_result_form.rb | 34 + .../decidim/votings/question_result_form.rb | 34 + .../votings/voter/in_person_vote_form.rb | 40 + .../decidim/elections/admin/answers_helper.rb | 19 + .../decidim/elections/admin/steps_helper.rb | 40 + .../trustees_participatory_spaces_helper.rb | 33 + .../decidim/elections/application_helper.rb | 30 + .../elections/election_cells_helper.rb | 12 + .../helpers/decidim/elections/votes_helper.rb | 23 + .../app/helpers/decidim/votings/map_helper.rb | 22 + .../helpers/decidim/votings/votings_helper.rb | 53 + .../votings/census/admin/create_datum_job.rb | 41 + .../census/admin/export_access_codes_job.rb | 39 + .../census/admin/generate_access_codes_job.rb | 44 + .../decidim/elections/trustee_mailer.rb | 28 + .../decidim/elections/vote_accepted_mailer.rb | 29 + .../decidim/votings/access_code_mailer.rb | 34 + .../decidim/votings/census/export_mailer.rb | 44 + .../app/models/decidim/elections/action.rb | 13 + .../app/models/decidim/elections/answer.rb | 46 + .../decidim/elections/application_record.rb | 10 + .../elections/bulletin_board_closure.rb | 19 + .../app/models/decidim/elections/election.rb | 154 ++ .../decidim/elections/elections_trustee.rb | 11 + .../app/models/decidim/elections/question.rb | 52 + .../app/models/decidim/elections/result.rb | 22 + .../app/models/decidim/elections/trustee.rb | 39 + .../elections/trustees_participatory_space.rb | 13 + .../app/models/decidim/elections/vote.rb | 15 + .../models/decidim/votings/ballot_style.rb | 35 + .../decidim/votings/ballot_style_question.rb | 11 + .../models/decidim/votings/census/dataset.rb | 34 + .../models/decidim/votings/census/datum.rb | 34 + .../models/decidim/votings/in_person_vote.rb | 22 + .../votings/monitoring_committee_member.rb | 30 + .../models/decidim/votings/polling_officer.rb | 81 + .../models/decidim/votings/polling_station.rb | 86 + .../votings/polling_station_closure.rb | 43 + .../app/models/decidim/votings/voting.rb | 176 ++ .../packs/entrypoints/decidim_elections.js | 14 + .../entrypoints/decidim_elections_admin.js | 9 + .../app/packs/entrypoints/decidim_votings.js | 11 + .../entrypoints/decidim_votings_admin.js | 10 + .../decidim/elections/decidim_elections.svg | 1 + .../decidim/votings/decidim_votings.svg | 1 + .../decidim/elections/admin/pending_action.js | 19 + .../elections/admin/trustees_process.js | 130 ++ .../elections/admin/vote_statistics.js | 14 + .../elections/broken_promises_handler.js | 13 + .../src/decidim/elections/election_log.js | 164 ++ .../decidim/elections/trustee/key_ceremony.js | 165 ++ .../src/decidim/elections/trustee/tally.js | 147 ++ .../decidim/elections/trustee/trustee_zone.js | 64 + .../decidim/elections/voter/casting-vote.js | 19 + .../src/decidim/elections/voter/new-vote.js | 104 ++ .../decidim/elections/voter/setup-preview.js | 75 + .../src/decidim/elections/voter/setup-vote.js | 49 + .../decidim/elections/voter/verify-vote.js | 77 + .../voter/vote_questions.component.js | 170 ++ .../votings/admin/polling_officers_picker.js | 20 + .../votings/admin/polling_stations_form.js | 9 + .../admin/update_census_dataset_status.js | 13 + .../votings/admin/voting_user_role_form.js | 35 + .../src/decidim/votings/in-person-vote.js | 23 + .../polling_officer_zone/edit-closure.js | 75 + .../polling_officer_zone/in-person-vote.js | 5 + .../polling_officer_zone/new-closure.js | 39 + .../polling_officer_zone/sign-closure.js | 13 + .../decidim/elections/admin/elections.scss | 24 + .../decidim/elections/elections.scss | 202 +++ .../decidim/votings/admin/votings.scss | 23 + .../votings/admin/votings/_ballot-styles.scss | 16 + ...ng-committee-polling-station-closures.scss | 9 + .../votings/admin/votings/_results.scss | 3 + .../stylesheets/decidim/votings/votings.scss | 53 + .../decidim/elections/admin/permissions.rb | 91 ++ .../decidim/elections/permissions.rb | 54 + .../elections/trustee_zone/permissions.rb | 32 + .../decidim/votings/admin/permissions.rb | 182 +++ .../decidim/votings/permissions.rb | 42 + .../polling_officer_zone/permissions.rb | 53 + .../elections/admin_log/election_presenter.rb | 66 + .../elections/admin_log/trustee_presenter.rb | 50 + .../decidim/elections/election_presenter.rb | 28 + .../decidim/elections/trustee_presenter.rb | 28 + .../admin_log/ballot_style_presenter.rb | 39 + .../monitoring_committee_member_presenter.rb | 50 + .../admin_log/polling_officer_presenter.rb | 50 + .../admin_log/polling_station_presenter.rb | 29 + .../votings/admin_log/voting_presenter.rb | 45 + .../census/admin_log/dataset_presenter.rb | 31 + .../votings/polling_station_presenter.rb | 27 + .../decidim/votings/voting_presenter.rb | 27 + .../decidim/votings/voting_stats_presenter.rb | 16 + .../elections/admin/pending_actions.rb | 20 + .../elections/admin/votes_for_statistics.rb | 24 + .../elections/elections_finished_to_end.rb | 19 + .../elections/elections_ready_to_start.rb | 25 + .../decidim/elections/filtered_elections.rb | 37 + .../trustees/by_participatory_space.rb | 24 + .../by_participatory_space_trustee_ids.rb | 24 + .../elections/votes/last_vote_for_voter.rb | 29 + .../decidim/elections/votes/pending_votes.rb | 20 + .../decidim/votings/admin/admin_users.rb | 44 + .../admin/ballot_style_by_voting_code.rb | 31 + .../polling_officers_join_polling_stations.rb | 33 + ...officers_join_polling_stations_and_user.rb | 34 + .../admin/polling_officers_join_user.rb | 32 + .../organization_prioritized_votings.rb | 21 + .../votings/organization_promoted_votings.rb | 16 + .../votings/organization_published_votings.rb | 19 + .../decidim/votings/organization_votings.rb | 16 + .../decidim/votings/prioritized_votings.rb | 13 + .../decidim/votings/published_votings.rb | 12 + .../votings/votes/in_person_vote_for_voter.rb | 27 + .../votings/votes/pending_in_person_votes.rb | 20 + .../votings/census/datum_serializer.rb | 29 + .../elections/current_user_vote_flow.rb | 68 + .../services/decidim/elections/vote_flow.rb | 109 ++ .../votings/census/access_codes_exporter.rb | 50 + .../decidim/votings/census_vote_flow.rb | 104 ++ .../elections/admin/answers/_form.html.erb | 23 + .../elections/admin/answers/edit.html.erb | 23 + .../elections/admin/answers/index.html.erb | 93 ++ .../elections/admin/answers/new.html.erb | 23 + .../elections/admin/elections/_form.html.erb | 27 + .../elections/admin/elections/edit.html.erb | 18 + .../elections/admin/elections/index.html.erb | 91 ++ .../elections/admin/elections/new.html.erb | 18 + .../admin/proposals_imports/new.html.erb | 39 + .../elections/admin/questions/_form.html.erb | 25 + .../elections/admin/questions/edit.html.erb | 21 + .../elections/admin/questions/index.html.erb | 60 + .../elections/admin/questions/new.html.erb | 22 + .../shared/_trustees_secondary_nav.html.erb | 3 + .../admin/steps/_create_election.html.erb | 81 + .../elections/admin/steps/_created.html.erb | 16 + .../admin/steps/_key_ceremony.html.erb | 51 + .../admin/steps/_key_ceremony_ended.html.erb | 15 + .../admin/steps/_results_published.html.erb | 62 + .../admin/steps/_tally_ended.html.erb | 55 + .../admin/steps/_tally_started.html.erb | 67 + .../elections/admin/steps/_vote.html.erb | 21 + .../admin/steps/_vote_ended.html.erb | 19 + .../admin/steps/_vote_stats.html.erb | 31 + .../elections/admin/steps/index.html.erb | 56 + .../_form.html.erb | 15 + .../index.html.erb | 66 + .../new.html.erb | 19 + .../elections/elections/_elections.html.erb | 22 + .../elections/elections/election_log.html.erb | 176 ++ .../elections/elections/index.html.erb | 24 + .../decidim/elections/elections/index.js.erb | 5 + .../decidim/elections/elections/show.html.erb | 87 + .../shared/_broken_promises_modal.html.erb | 24 + .../trustee_mailer/notification.html.erb | 5 + .../elections/_backup_modal.html.erb | 14 + .../elections/_key_ceremony_steps.html.erb | 51 + .../elections/_restore_modal.html.erb | 15 + .../elections/_tally_started_steps.html.erb | 48 + .../trustee_zone/elections/show.html.erb | 24 + .../trustee_zone/elections/update.js.erb | 5 + .../trustees/_no_public_keys.html.erb | 30 + .../trustees/_public_keys.html.erb | 17 + .../trustee_zone/trustees/_table.html.erb | 28 + .../trustee_zone/trustees/show.html.erb | 37 + .../notification.html.erb | 3 + .../votes/_new_ballot_decision_step.html.erb | 26 + .../votes/_new_confirm_step.html.erb | 65 + .../votes/_new_encrypting_step.html.erb | 9 + .../elections/votes/_new_question.html.erb | 33 + .../votes/_new_question_modal.html.erb | 43 + .../votes/_onboarding_modal.html.erb | 17 + .../elections/votes/_show_casted.html.erb | 34 + .../elections/votes/_show_casting.html.erb | 22 + .../elections/votes/_show_failed.html.erb | 26 + .../decidim/elections/votes/new.html.erb | 94 ++ .../decidim/elections/votes/show.html.erb | 12 + .../decidim/elections/votes/verify.html.erb | 57 + .../send_access_code.html.erb | 5 + .../admin/ballot_styles/_form.html.erb | 24 + .../votings/admin/ballot_styles/edit.html.erb | 18 + .../admin/ballot_styles/index.html.erb | 65 + .../votings/admin/ballot_styles/new.html.erb | 18 + .../_results.html.erb | 50 + .../index.html.erb | 30 + .../show.html.erb | 24 + .../_form.html.erb | 26 + .../index.html.erb | 39 + .../monitoring_committee_members/new.html.erb | 18 + .../_closure_certificate_results.erb | 9 + .../_closures.html.erb | 68 + .../_elections.html.erb | 29 + .../edit.html.erb | 30 + .../index.html.erb | 5 + .../show.html.erb | 10 + .../index.html.erb | 57 + .../admin/polling_officers/_form.html.erb | 26 + .../admin/polling_officers/index.html.erb | 49 + .../admin/polling_officers/new.html.erb | 18 + .../admin/polling_stations/_form.html.erb | 34 + .../admin/polling_stations/edit.html.erb | 18 + .../admin/polling_stations/index.html.erb | 54 + .../admin/polling_stations/new.html.erb | 18 + .../votings/admin/votings/_form.html.erb | 76 + .../votings/admin/votings/edit.html.erb | 39 + .../votings/admin/votings/index.html.erb | 65 + .../votings/admin/votings/new.html.erb | 19 + .../admin/census/_creating_data.html.erb | 10 + .../admin/census/_export_codes.html.erb | 8 + .../admin/census/_exporting_codes.html.erb | 3 + .../census/admin/census/_freeze.html.erb | 5 + .../admin/census/_generate_codes.html.erb | 35 + .../admin/census/_generating_codes.html.erb | 3 + .../census/admin/census/_new_census.html.erb | 23 + .../census/admin/census/_upload_info.html.erb | 17 + .../votings/census/admin/census/show.html.erb | 24 + .../votings/census/admin/census/status.js.erb | 1 + .../export_mailer/access_codes_export.erb | 7 + .../_answer_results_form_fields.html.erb | 12 + .../_ballot_results_form_fields.html.erb | 34 + .../closures/_certify_form.html.erb | 17 + .../_modal_ballots_count_error.html.erb | 48 + ...modal_ballots_results_count_error.html.erb | 35 + .../_question_results_form_fields.html.erb | 18 + .../closures/_sign_form.html.erb | 43 + .../closures/edit.html.erb | 68 + .../closures/new.html.erb | 69 + .../closures/show.html.erb | 50 + .../in_person_votes/_complete_voting.html.erb | 48 + .../in_person_votes/_in_person_form.html.erb | 37 + .../in_person_votes/_verify_document.html.erb | 20 + .../in_person_votes/new.html.erb | 25 + .../in_person_votes/show.html.erb | 30 + .../_polling_station.html.erb | 53 + .../polling_officers/index.html.erb | 19 + .../votings/_access_code_modal.html.erb | 28 + .../votings/votings/_check_fields.html.erb | 20 + .../votings/_polling_officers_picker.html.erb | 4 + .../decidim/votings/votings/_votings.html.erb | 19 + .../votings/votings/check_census.html.erb | 68 + .../votings/votings/elections_log.html.erb | 37 + .../decidim/votings/votings/index.html.erb | 39 + .../decidim/votings/votings/index.js.erb | 5 + .../decidim/votings/votings/login.html.erb | 57 + .../decidim/votings/votings/show.html.erb | 25 + .../layouts/decidim/admin/voting.html.erb | 23 + .../layouts/decidim/admin/votings.html.erb | 16 + decidim-elections/bin/rails | 17 + decidim-elections/config/assets.rb | 11 + decidim-elections/config/environment.rb | 3 + .../initializers/decidim_bulletin_board.rb | 29 + .../config/initializers/rack_attack.rb | 15 + decidim-elections/config/locales/am-ET.yml | 1 + decidim-elections/config/locales/ar.yml | 74 + decidim-elections/config/locales/bg-BG.yml | 7 + decidim-elections/config/locales/bg.yml | 7 + decidim-elections/config/locales/ca.yml | 1425 ++++++++++++++++ decidim-elections/config/locales/cs.yml | 1427 ++++++++++++++++ decidim-elections/config/locales/da-DK.yml | 1 + decidim-elections/config/locales/da.yml | 1 + decidim-elections/config/locales/de.yml | 1422 ++++++++++++++++ decidim-elections/config/locales/el.yml | 1298 +++++++++++++++ decidim-elections/config/locales/en.yml | 1429 +++++++++++++++++ decidim-elections/config/locales/eo-UY.yml | 1 + decidim-elections/config/locales/eo.yml | 1 + decidim-elections/config/locales/es-MX.yml | 1425 ++++++++++++++++ decidim-elections/config/locales/es-PY.yml | 1425 ++++++++++++++++ decidim-elections/config/locales/es.yml | 1425 ++++++++++++++++ decidim-elections/config/locales/et-EE.yml | 1 + decidim-elections/config/locales/et.yml | 1 + decidim-elections/config/locales/eu.yml | 1423 ++++++++++++++++ decidim-elections/config/locales/fa-IR.yml | 1 + decidim-elections/config/locales/fi-plain.yml | 1426 ++++++++++++++++ decidim-elections/config/locales/fi.yml | 1426 ++++++++++++++++ decidim-elections/config/locales/fr-CA.yml | 1426 ++++++++++++++++ decidim-elections/config/locales/fr-LU.yml | 890 ++++++++++ decidim-elections/config/locales/fr.yml | 1426 ++++++++++++++++ decidim-elections/config/locales/ga-IE.yml | 322 ++++ decidim-elections/config/locales/gl.yml | 545 +++++++ decidim-elections/config/locales/gn-PY.yml | 1 + decidim-elections/config/locales/hr-HR.yml | 1 + decidim-elections/config/locales/hr.yml | 1 + decidim-elections/config/locales/hu.yml | 228 +++ decidim-elections/config/locales/id-ID.yml | 1 + decidim-elections/config/locales/is-IS.yml | 1 + decidim-elections/config/locales/is.yml | 1 + decidim-elections/config/locales/it.yml | 1043 ++++++++++++ decidim-elections/config/locales/ja.yml | 1412 ++++++++++++++++ decidim-elections/config/locales/ka-GE.yml | 1 + decidim-elections/config/locales/kaa.yml | 1 + decidim-elections/config/locales/ko-KR.yml | 1 + decidim-elections/config/locales/ko.yml | 1 + decidim-elections/config/locales/lb-LU.yml | 25 + decidim-elections/config/locales/lb.yml | 25 + decidim-elections/config/locales/lo-LA.yml | 1 + decidim-elections/config/locales/lt-LT.yml | 1 + decidim-elections/config/locales/lt.yml | 1374 ++++++++++++++++ decidim-elections/config/locales/lv.yml | 104 ++ decidim-elections/config/locales/mt-MT.yml | 1 + decidim-elections/config/locales/mt.yml | 1 + decidim-elections/config/locales/nl.yml | 930 +++++++++++ decidim-elections/config/locales/no.yml | 442 +++++ decidim-elections/config/locales/oc-FR.yml | 1 + decidim-elections/config/locales/om-ET.yml | 1 + decidim-elections/config/locales/pl.yml | 1043 ++++++++++++ decidim-elections/config/locales/pt-BR.yml | 1225 ++++++++++++++ decidim-elections/config/locales/pt.yml | 1045 ++++++++++++ decidim-elections/config/locales/ro-RO.yml | 620 +++++++ decidim-elections/config/locales/ru.yml | 1 + decidim-elections/config/locales/si-LK.yml | 1 + decidim-elections/config/locales/sk.yml | 1 + decidim-elections/config/locales/sl.yml | 5 + decidim-elections/config/locales/so-SO.yml | 1 + decidim-elections/config/locales/sq-AL.yml | 1 + decidim-elections/config/locales/sr-CS.yml | 1 + decidim-elections/config/locales/sv.yml | 722 +++++++++ decidim-elections/config/locales/sw-KE.yml | 1 + decidim-elections/config/locales/th-TH.yml | 1 + decidim-elections/config/locales/ti-ER.yml | 1 + decidim-elections/config/locales/tr-TR.yml | 322 ++++ decidim-elections/config/locales/uk.yml | 1 + decidim-elections/config/locales/val-ES.yml | 1 + decidim-elections/config/locales/vi-VN.yml | 1 + decidim-elections/config/locales/vi.yml | 1 + decidim-elections/config/locales/zh-CN.yml | 182 +++ decidim-elections/config/locales/zh-TW.yml | 1298 +++++++++++++++ ...3618_create_decidim_elections_elections.rb | 15 + ...2327_create_decidim_elections_questions.rb | 14 + ...084144_create_decidim_elections_answers.rb | 12 + ...601141412_add_published_at_to_elections.rb | 7 + ..._remove_subtitle_from_decidim_elections.rb | 7 + ...lections_to_decidim_elections_questions.rb | 7 + ...add_questionnaire_to_existing_elections.rb | 29 + ...elections_trustees_participatory_spaces.rb | 13 + ...te_decidim_elections_elections_trustees.rb | 10 + ...53835_create_decidim_elections_trustees.rb | 12 + ...d_blocked_at_and_bb_status_to_elections.rb | 8 + ..._add_votes_to_decidim_elections_answers.rb | 7 + ...d_selected_to_decidim_elections_answers.rb | 7 + ...09110653_create_decidim_elections_votes.rb | 14 + .../20201216091123_add_name_to_trustees.rb | 7 + ...d_message_id_to_decidim_elections_votes.rb | 7 + ...120115_create_decidim_elections_votings.rb | 20 + ...published_at_to_decidim_votings_votings.rb | 7 + ...850_add_user_to_decidim_elections_votes.rb | 7 + .../20210120164634_add_promoted_to_voting.rb | 7 + ...124801_add_voting_type_to_voting_voting.rb | 7 + ...124956_create_decidim_elections_actions.rb | 14 + ...0204132111_add_votings_polling_stations.rb | 19 + ...8090441_add_polling_officers_to_votings.rb | 12 + ...n_references_to_votings_polling_officer.rb | 8 + ...6074707_add_monitoring_committee_member.rb | 12 + ...024_add_decidim_votings_census_datasets.rb | 17 + ...8104154_add_decidim_votings_census_data.rb | 23 + ...add_followable_counter_cache_to_votings.rb | 20 + ...20210326090435_create_elections_results.rb | 16 + ...30102348_remove_votes_count_from_answer.rb | 7 + .../20210330123606_add_voting_ballot_style.rb | 19 + .../20210330183204_add_email_to_votes.rb | 10 + ...d_census_contact_information_to_votings.rb | 7 + ...ganization_to_decidim_elections_trustee.rb | 11 + ...ot_style_to_decidim_votings_census_data.rb | 7 + .../20210402140402_add_salt_to_elections.rb | 22 + ...20210412144721_change_elections_results.rb | 24 + ...reate_elections_bulletin_board_closures.rb | 13 + ...create_votings_polling_station_closures.rb | 22 + ..._create_decidim_votings_in_person_votes.rb | 21 + ...e_results_to_decidim_elections_election.rb | 8 + ...dd_signed_at_to_polling_station_closure.rb | 7 + ..._at_to_votings_polling_station_closures.rb | 8 + ...rename_bb_status_tally_to_tally_started.rb | 19 + ...121541_add_show_check_census_to_votings.rb | 7 + ...ve_description_from_elections_questions.rb | 7 + ...1112802_rename_dataset_file_to_filename.rb | 7 + decidim-elections/decidim-elections.gemspec | 44 + .../docker/bulletin_board/docker-compose.yml | 22 + .../bulletin_board_test/docker-compose.yml | 23 + .../api/bulletin_board_closure_type.rb | 16 + .../lib/decidim/api/election_answer_type.rb | 24 + .../lib/decidim/api/election_question_type.rb | 21 + .../lib/decidim/api/election_result_type.rb | 19 + .../lib/decidim/api/election_type.rb | 27 + .../lib/decidim/api/elections_type.rb | 32 + .../api/polling_station_closure_type.rb | 19 + .../lib/decidim/api/polling_station_type.rb | 24 + .../lib/decidim/api/trustee_type.rb | 16 + .../lib/decidim/api/voting_type.rb | 20 + decidim-elections/lib/decidim/elections.rb | 49 + .../lib/decidim/elections/admin.rb | 10 + .../lib/decidim/elections/admin_engine.rb | 64 + .../decidim/elections/answer_serializer.rb | 49 + .../lib/decidim/elections/api.rb | 13 + .../lib/decidim/elections/component.rb | 78 + .../lib/decidim/elections/engine.rb | 61 + .../lib/decidim/elections/menu.rb | 39 + .../lib/decidim/elections/seeds.rb | 435 +++++ .../lib/decidim/elections/test/factories.rb | 368 +++++ .../lib/decidim/elections/trustee_zone.rb | 10 + .../decidim/elections/trustee_zone_engine.rb | 32 + .../lib/decidim/elections/version.rb | 10 + decidim-elections/lib/decidim/votings.rb | 33 + .../lib/decidim/votings/admin.rb | 10 + .../lib/decidim/votings/admin_engine.rb | 83 + decidim-elections/lib/decidim/votings/api.rb | 9 + .../lib/decidim/votings/census.rb | 16 + .../lib/decidim/votings/census_admin.rb | 12 + .../decidim/votings/census_admin_engine.rb | 18 + .../lib/decidim/votings/census_engine.rb | 19 + .../lib/decidim/votings/census_menu.rb | 18 + .../content_blocks/registry_manager.rb | 95 ++ .../lib/decidim/votings/engine.rb | 77 + decidim-elections/lib/decidim/votings/menu.rb | 161 ++ .../decidim/votings/participatory_space.rb | 55 + .../decidim/votings/polling_officer_zone.rb | 10 + .../votings/polling_officer_zone_engine.rb | 38 + .../votings/polling_officer_zone_menu.rb | 17 + .../lib/decidim/votings/query_extensions.rb | 43 + .../lib/decidim/votings/seeds.rb | 200 +++ .../lib/decidim/votings/test/factories.rb | 267 +++ .../lib/decidim/votings/voting_serializer.rb | 55 + .../lib/tasks/decidim_elections.rake | 79 + .../lib/tasks/decidim_voting_census.rake | 26 + .../decidim/elections/election_cell_spec.rb | 29 + .../decidim/elections/election_g_cell_spec.rb | 62 + .../highlighted_votings_cell_spec.rb | 36 + ...lighted_votings_settings_form_cell_spec.rb | 19 + .../cells/decidim/votings/voting_cell_spec.rb | 29 + .../voting_dropdown_metadata_cell_spec.rb | 17 + .../decidim/votings/voting_g_cell_spec.rb | 107 ++ .../admin/add_user_as_trustee_spec.rb | 72 + .../elections/admin/create_answer_spec.rb | 109 ++ .../elections/admin/create_election_spec.rb | 95 ++ .../elections/admin/create_question_spec.rb | 75 + .../elections/admin/destroy_answer_spec.rb | 46 + .../elections/admin/destroy_election_spec.rb | 44 + .../elections/admin/destroy_question_spec.rb | 36 + .../decidim/elections/admin/end_vote_spec.rb | 90 ++ .../import_proposals_to_elections_spec.rb | 140 ++ .../elections/admin/publish_election_spec.rb | 59 + .../elections/admin/publish_results_spec.rb | 100 ++ ...e_trustee_from_participatory_space_spec.rb | 25 + .../admin/report_missing_trustee_spec.rb | 97 ++ .../elections/admin/setup_election_spec.rb | 112 ++ .../admin/start_key_ceremony_spec.rb | 90 ++ .../elections/admin/start_tally_spec.rb | 104 ++ .../elections/admin/start_vote_spec.rb | 90 ++ .../admin/unpublish_election_spec.rb | 29 + .../admin/update_answer_selection_spec.rb | 25 + .../elections/admin/update_answer_spec.rb | 91 ++ .../elections/admin/update_election_spec.rb | 66 + .../elections/admin/update_question_spec.rb | 61 + ...update_trustee_participatory_space_spec.rb | 22 + ...ate_election_bulletin_board_status_spec.rb | 83 + .../trustee_zone/update_trustee_spec.rb | 46 + .../decidim/elections/voter/cast_vote_spec.rb | 106 ++ .../voter/update_vote_status_spec.rb | 122 ++ .../votings/admin/create_ballot_style_spec.rb | 97 ++ ...create_monitoring_committee_member_spec.rb | 91 ++ .../admin/create_polling_officer_spec.rb | 91 ++ .../admin/create_polling_station_spec.rb | 147 ++ .../votings/admin/create_voting_spec.rb | 96 ++ .../admin/destroy_ballot_style_spec.rb | 47 + ...estroy_monitoring_committee_member_spec.rb | 46 + .../admin/destroy_polling_officer_spec.rb | 46 + .../admin/destroy_polling_station_spec.rb | 35 + ...e_validate_polling_station_closure_spec.rb | 43 + .../votings/admin/publish_voting_spec.rb | 7 + .../votings/admin/unpublish_voting_spec.rb | 7 + .../votings/admin/update_ballot_style_spec.rb | 87 + .../admin/update_polling_station_spec.rb | 217 +++ .../votings/admin/update_voting_spec.rb | 129 ++ .../census/admin/create_dataset_spec.rb | 104 ++ .../votings/census/admin/create_datum_spec.rb | 76 + .../census/admin/destroy_dataset_spec.rb | 31 + .../increment_dataset_processed_rows_spec.rb | 21 + .../admin/launch_access_codes_export_spec.rb | 35 + .../launch_access_codes_generation_spec.rb | 41 + .../census/admin/update_dataset_spec.rb | 64 + .../certify_polling_station_closure_spec.rb | 26 + .../decidim/votings/check_census_spec.rb | 66 + .../create_polling_station_closure_spec.rb | 51 + .../create_polling_station_results_spec.rb | 101 ++ .../destroy_polling_station_closure_spec.rb | 44 + .../decidim/votings/send_access_code_spec.rb | 70 + .../sign_polling_station_closure_spec.rb | 47 + .../votings/voter/in_person_vote_spec.rb | 83 + .../update_in_person_vote_status_spec.rb | 33 + .../admin/answers_controller_spec.rb | 94 ++ .../admin/components_controller_spec.rb | 64 + .../admin/elections_controller_spec.rb | 67 + .../votings/exports_controller_spec.rb | 65 + .../votings/votings_controller_spec.rb | 50 + .../election_published_event_spec.rb | 19 + .../trustees/notify_new_trustee_event_spec.rb | 25 + .../notify_trustee_new_election_event_spec.rb | 19 + ...notify_trustee_tally_process_event_spec.rb | 38 + .../votes/vote_accepted_event_spec.rb | 23 + .../polling_station_assigned_event_spec.rb | 34 + decidim-elections/spec/factories.rb | 5 + .../elections/admin/action_form_spec.rb | 38 + .../elections/admin/answer_form_spec.rb | 89 + .../answer_import_proposals_form_spec.rb | 76 + .../elections/admin/election_form_spec.rb | 90 ++ .../elections/admin/question_form_spec.rb | 57 + .../admin/report_missing_trustee_form_spec.rb | 36 + .../elections/admin/setup_form_spec.rb | 182 +++ .../elections/admin/vote_period_form_spec.rb | 73 + .../trustee_zone/trustee_form_spec.rb | 51 + .../elections/voter/verify_vote_form_spec.rb | 39 + .../decidim/elections/voter/vote_form_spec.rb | 67 + .../admin/attachment_collection_form_spec.rb | 16 + .../votings/admin/attachment_form_spec.rb | 16 + .../votings/admin/ballot_style_form_spec.rb | 37 + .../admin/polling_station_form_spec.rb | 79 + .../decidim/votings/admin/voting_form_spec.rb | 148 ++ .../admin/voting_user_role_form_spec.rb | 83 + .../votings/answer_result_form_spec.rb | 48 + .../votings/ballot_result_form_spec.rb | 47 + .../votings/census/admin/dataset_form_spec.rb | 18 + .../votings/census/admin/datum_form_spec.rb | 102 ++ .../decidim/votings/census/check_form_spec.rb | 66 + .../votings/census/in_person_form_spec.rb | 64 + .../decidim/votings/census/login_form_spec.rb | 74 + .../votings/closure_certify_form_spec.rb | 30 + .../votings/closure_result_form_spec.rb | 89 + .../decidim/votings/closure_sign_form_spec.rb | 24 + .../votings/envelopes_result_form_spec.rb | 65 + .../votings/question_result_form_spec.rb | 42 + .../decidim/elections/steps_helper_spec.rb | 96 ++ .../decidim/elections/votes_helper_spec.rb | 73 + .../census/admin/create_datum_job_spec.rb | 54 + .../admin/export_access_codes_job_spec.rb | 43 + .../admin/generate_access_codes_job_spec.rb | 77 + .../votings/current_component_spec.rb | 85 + .../decidim/elections/admin_engine_spec.rb | 7 + .../elections/answer_serializer_spec.rb | 61 + .../lib/decidim/elections/component_spec.rb | 30 + .../spec/lib/decidim/elections/engine_spec.rb | 23 + .../lib/decidim/votings/admin_engine_spec.rb | 7 + .../lib/decidim/votings/census_engine_spec.rb | 7 + .../spec/lib/decidim/votings/engine_spec.rb | 7 + .../decidim/votings/voting_serializer_spec.rb | 59 + .../spec/lib/query_extensions_spec.rb | 50 + ...ction_generate_identification_keys_spec.rb | 27 + .../decidim_election_scheduled_tasks_spec.rb | 67 + ...ks_delete_download_your_data_files_spec.rb | 51 + decidim-elections/spec/lib/tasks/main_spec.rb | 7 + .../decidim/elections/trustee_mailer_spec.rb | 35 + .../elections/vote_accepted_mailer_spec.rb | 57 + .../votings/access_code_mailer_spec.rb | 50 + .../votings/census/export_mailer_spec.rb | 28 + .../models/decidim/elections/action_spec.rb | 9 + .../models/decidim/elections/answer_spec.rb | 32 + .../elections/bulletin_board_closure_spec.rb | 24 + .../models/decidim/elections/election_spec.rb | 68 + .../models/decidim/elections/question_spec.rb | 11 + .../models/decidim/elections/result_spec.rb | 23 + .../models/decidim/elections/trustee_spec.rb | 39 + .../models/decidim/elections/vote_spec.rb | 28 + .../decidim/votings/ballot_style_spec.rb | 55 + .../decidim/votings/census/dataset_spec.rb | 27 + .../decidim/votings/census/datum_spec.rb | 71 + .../decidim/votings/polling_officer_spec.rb | 39 + .../votings/polling_station_closure_spec.rb | 56 + .../decidim/votings/polling_station_spec.rb | 50 + .../models/decidim/votings/voting_spec.rb | 32 + .../elections/admin/permissions_spec.rb | 267 +++ .../decidim/elections/permissions_spec.rb | 198 +++ .../trustee_zone/permissions_spec.rb | 117 ++ .../decidim/votings/admin/permissions_spec.rb | 225 +++ .../decidim/votings/permissions_spec.rb | 38 + .../polling_officer_zone/permissions_spec.rb | 165 ++ .../admin_log/election_presenter_spec.rb | 134 ++ .../admin_log/trustee_presenter_spec.rb | 34 + .../elections/election_presenter_spec.rb | 51 + .../elections/trustee_presenter_spec.rb | 47 + .../admin_log/ballot_style_presenter_spec.rb | 58 + ...itoring_committee_member_presenter_spec.rb | 12 + .../polling_officer_presenter_spec.rb | 12 + .../polling_station_presenter_spec.rb | 12 + .../admin_log/voting_presenter_spec.rb | 12 + .../admin_log/dataset_presenter_spec.rb | 19 + .../decidim/votings/voting_presenter_spec.rb | 37 + .../votings/voting_stats_presenter_spec.rb | 106 ++ .../elections/admin/pending_actions_spec.rb | 13 + .../admin/votes_for_statistics_spec.rb | 19 + .../trustees/by_participatory_space_spec.rb | 33 + ...by_participatory_space_trustee_ids_spec.rb | 33 + .../elections/votes/pending_votes_spec.rb | 13 + .../votes/user_election_last_vote_spec.rb | 41 + .../decidim/votings/admin/admin_users_spec.rb | 23 + .../organization_prioritized_votings_spec.rb | 37 + .../organization_promoted_votings_spec.rb | 60 + .../organization_published_votings_spec.rb | 37 + .../votings/organization_votings_spec.rb | 30 + .../votes/in_person_vote_for_voter_spec.rb | 40 + .../spec/requests/election_search_spec.rb | 166 ++ .../spec/requests/voting_search_spec.rb | 128 ++ .../votings/census/datum_serializer_spec.rb | 28 + .../elections/current_user_vote_flow_spec.rb | 167 ++ .../census/access_codes_exporter_spec.rb | 51 + .../decidim/votings/census_vote_flow_spec.rb | 235 +++ .../admin_manages_election_shared_context.rb | 9 + .../shared/admin_manages_voting_examples.rb | 17 + .../full_election_process_shared_context.rb | 133 ++ ..._voting_attachment_collections_examples.rb | 17 + .../manage_voting_attachments_examples.rb | 19 + .../manage_voting_components_examples.rb | 223 +++ ...ommittee_member_manages_voting_examples.rb | 9 + .../test_bulletin_board_shared_context.rb | 10 + .../spec/shared/vote_examples.rb | 186 +++ decidim-elections/spec/spec_helper.rb | 12 + .../admin/admin_manages_answers_spec.rb | 194 +++ .../admin_manages_election_steps_spec.rb | 371 +++++ .../admin/admin_manages_elections_spec.rb | 221 +++ .../admin/admin_manages_questions_spec.rb | 122 ++ .../admin/admin_manages_trustees_spec.rb | 124 ++ ...ages_voting_attachment_collections_spec.rb | 9 + .../admin_manages_voting_attachments_spec.rb | 9 + ...admin_manages_voting_ballot_styles_spec.rb | 102 ++ .../admin/admin_manages_voting_census_spec.rb | 130 ++ ...nages_voting_component_permissions_spec.rb | 14 + .../admin_manages_voting_components_spec.rb | 9 + ...anages_voting_monitoring_committee_spec.rb | 79 + ...in_manages_voting_polling_officers_spec.rb | 136 ++ ...in_manages_voting_polling_stations_spec.rb | 241 +++ .../admin_manages_voting_publication_spec.rb | 14 + .../admin/admin_manages_votings_spec.rb | 309 ++++ ...es_voting_polling_station_closures_spec.rb | 88 + ...ttee_member_manages_voting_results_spec.rb | 73 + ...g_committee_member_manages_votings_spec.rb | 36 + ...ommittee_member_verifies_elections_spec.rb | 34 + .../spec/system/check_census_spec.rb | 209 +++ .../spec/system/election_log_spec.rb | 193 +++ .../spec/system/elections_log_spec.rb | 41 + .../spec/system/explore_elections_spec.rb | 233 +++ .../system/homepage_content_blocks_spec.rb | 40 + .../spec/system/key_ceremony_spec.rb | 48 + .../spec/system/polling_officer_zone_spec.rb | 179 +++ ...preview_elections_with_share_token_spec.rb | 10 + .../spec/system/sorting_elections_spec.rb | 44 + .../spec/system/trustee_zone_spec.rb | 145 ++ .../spec/system/vote_in_person_spec.rb | 157 ++ .../vote_online_inside_a_voting_spec.rb | 227 +++ .../spec/system/vote_online_spec.rb | 188 +++ decidim-elections/spec/system/voting_spec.rb | 94 ++ decidim-elections/spec/system/votings_spec.rb | 185 +++ .../bulletin_board_closure_type_spec.rb | 55 + .../elections/election_answer_type_spec.rb | 82 + .../elections/election_question_type_spec.rb | 78 + .../elections/election_result_type_spec.rb | 63 + .../decidim/elections/election_type_spec.rb | 118 ++ .../decidim/elections/elections_type_spec.rb | 60 + .../decidim/elections/trustee_type_spec.rb | 38 + .../polling_station_closure_type_spec.rb | 90 ++ .../votings/polling_station_type_spec.rb | 89 + .../types/decidim/votings/voting_type_spec.rb | 86 + .../spec/types/integration_schema_spec.rb | 231 +++ 856 files changed, 71717 insertions(+) create mode 100644 decidim-elections/.byebug_history create mode 100644 decidim-elections/.gitignore create mode 100644 decidim-elections/README.md create mode 100644 decidim-elections/Rakefile create mode 100644 decidim-elections/app/cells/decidim/elections/content_blocks/related_elections_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_g_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_metadata_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_preview/show.erb create mode 100644 decidim-elections/app/cells/decidim/elections/election_preview_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_results/progress_bar.erb create mode 100644 decidim-elections/app/cells/decidim/elections/election_results/show.erb create mode 100644 decidim-elections/app/cells/decidim/elections/election_results_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_s_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/election_vote_cta/show.erb create mode 100644 decidim-elections/app/cells/decidim/elections/election_vote_cta_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component/show.erb create mode 100644 decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/remaining_time_callout/show.erb create mode 100644 decidim-elections/app/cells/decidim/elections/remaining_time_callout_cell.rb create mode 100644 decidim-elections/app/cells/decidim/elections/voting_step_navigation/show.erb create mode 100644 decidim-elections/app/cells/decidim/elections/voting_step_navigation_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_block_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/hero_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/main_data_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/metrics_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/content_blocks/statistics_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/polling_station_closure_recount/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/polling_station_closure_recount_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_dropdown_metadata_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_g_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_map/show.erb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_map_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_metadata_cell.rb create mode 100644 decidim-elections/app/cells/decidim/votings/voting_s_cell.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/add_user_as_trustee.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/create_answer.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/create_election.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/create_question.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/destroy_answer.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/destroy_election.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/destroy_question.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/end_vote.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/import_proposals_to_elections.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/publish_election.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/publish_results.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/remove_trustee_from_participatory_space.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/report_missing_trustee.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/setup_election.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/start_key_ceremony.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/start_tally.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/start_vote.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/unpublish_election.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/update_action_status.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/update_answer.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/update_answer_selection.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/update_election.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/update_question.rb create mode 100644 decidim-elections/app/commands/decidim/elections/admin/update_trustee_participatory_space.rb create mode 100644 decidim-elections/app/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status.rb create mode 100644 decidim-elections/app/commands/decidim/elections/trustee_zone/update_trustee.rb create mode 100644 decidim-elections/app/commands/decidim/elections/voter/cast_vote.rb create mode 100644 decidim-elections/app/commands/decidim/elections/voter/update_vote_status.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/create_ballot_style.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/create_monitoring_committee_member.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/create_polling_officer.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/create_polling_station.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/create_voting.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/destroy_ballot_style.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/destroy_monitoring_committee_member.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/destroy_polling_officer.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/destroy_polling_station.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/manage_polling_station.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/update_ballot_style.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/update_polling_station.rb create mode 100644 decidim-elections/app/commands/decidim/votings/admin/update_voting.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/create_dataset.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/create_datum.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/destroy_dataset.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/increment_dataset_processed_rows.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_export.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_generation.rb create mode 100644 decidim-elections/app/commands/decidim/votings/census/admin/update_dataset.rb create mode 100644 decidim-elections/app/commands/decidim/votings/certify_polling_station_closure.rb create mode 100644 decidim-elections/app/commands/decidim/votings/check_census.rb create mode 100644 decidim-elections/app/commands/decidim/votings/create_polling_station_closure.rb create mode 100644 decidim-elections/app/commands/decidim/votings/create_polling_station_results.rb create mode 100644 decidim-elections/app/commands/decidim/votings/destroy_polling_station_closure.rb create mode 100644 decidim-elections/app/commands/decidim/votings/send_access_code.rb create mode 100644 decidim-elections/app/commands/decidim/votings/sign_polling_station_closure.rb create mode 100644 decidim-elections/app/commands/decidim/votings/voter/in_person_vote.rb create mode 100644 decidim-elections/app/commands/decidim/votings/voter/update_in_person_vote_status.rb create mode 100644 decidim-elections/app/constraints/decidim/votings/current_component.rb create mode 100644 decidim-elections/app/constraints/decidim/votings/current_voting.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/elections/content_security_policy.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/elections/has_vote_flow.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/elections/orderable.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/monitoring_committee_polling_station_closures/admin/filterable.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/polling_officers/admin/filterable.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/polling_stations/admin/filterable.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/votings/admin/filterable.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/votings/admin/voting_admin.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/votings/needs_voting.rb create mode 100644 decidim-elections/app/controllers/concerns/decidim/votings/orderable.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/answers_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/elections_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/feedback_forms_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/proposals_imports_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/questions_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/steps_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/admin/trustees_participatory_spaces_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/elections_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/feedbacks_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/trustee_zone/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/trustee_zone/elections_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/trustee_zone/trustees_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/elections/votes_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/ballot_styles_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/component_permissions_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/components_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/exports_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/imports_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_election_results_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_members_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_polling_station_closures_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_verify_elections_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/polling_officers_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/polling_stations_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/reminders_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/voting_attachment_collections_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/voting_attachments_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/voting_publications_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/votings_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_content_blocks_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/census/admin/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/census/admin/census_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/polling_officer_zone/application_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/polling_officer_zone/closures_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/polling_officer_zone/in_person_votes_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/polling_officer_zone/polling_officers_controller.rb create mode 100644 decidim-elections/app/controllers/decidim/votings/votings_controller.rb create mode 100644 decidim-elections/app/events/decidim/elections/election_published_event.rb create mode 100644 decidim-elections/app/events/decidim/elections/trustees/notify_new_trustee_event.rb create mode 100644 decidim-elections/app/events/decidim/elections/trustees/notify_trustee_new_election_event.rb create mode 100644 decidim-elections/app/events/decidim/elections/trustees/notify_trustee_tally_process_event.rb create mode 100644 decidim-elections/app/events/decidim/elections/votes/vote_accepted_event.rb create mode 100644 decidim-elections/app/events/decidim/votings/polling_officers/polling_station_assigned_event.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/action_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/answer_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/answer_import_proposals_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/election_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/question_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/report_missing_trustee_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/setup_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/trustees_participatory_space_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/admin/vote_period_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/trustee_zone/trustee_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/voter/verify_vote_form.rb create mode 100644 decidim-elections/app/forms/decidim/elections/voter/vote_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/admin/ballot_style_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/admin/monitoring_committee_polling_station_closure_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/admin/polling_station_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/admin/publish_results_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/admin/voting_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/admin/voting_user_role_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/answer_result_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/ballot_result_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/admin/dataset_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/admin/datum_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/check_fields.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/check_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/frontend_fields.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/in_person_fields.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/in_person_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/login_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/census/online_fields.rb create mode 100644 decidim-elections/app/forms/decidim/votings/closure_certify_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/closure_result_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/closure_sign_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/envelopes_result_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/question_result_form.rb create mode 100644 decidim-elections/app/forms/decidim/votings/voter/in_person_vote_form.rb create mode 100644 decidim-elections/app/helpers/decidim/elections/admin/answers_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/elections/admin/steps_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/elections/admin/trustees_participatory_spaces_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/elections/application_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/elections/election_cells_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/elections/votes_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/votings/map_helper.rb create mode 100644 decidim-elections/app/helpers/decidim/votings/votings_helper.rb create mode 100644 decidim-elections/app/jobs/decidim/votings/census/admin/create_datum_job.rb create mode 100644 decidim-elections/app/jobs/decidim/votings/census/admin/export_access_codes_job.rb create mode 100644 decidim-elections/app/jobs/decidim/votings/census/admin/generate_access_codes_job.rb create mode 100644 decidim-elections/app/mailers/decidim/elections/trustee_mailer.rb create mode 100644 decidim-elections/app/mailers/decidim/elections/vote_accepted_mailer.rb create mode 100644 decidim-elections/app/mailers/decidim/votings/access_code_mailer.rb create mode 100644 decidim-elections/app/mailers/decidim/votings/census/export_mailer.rb create mode 100644 decidim-elections/app/models/decidim/elections/action.rb create mode 100644 decidim-elections/app/models/decidim/elections/answer.rb create mode 100644 decidim-elections/app/models/decidim/elections/application_record.rb create mode 100644 decidim-elections/app/models/decidim/elections/bulletin_board_closure.rb create mode 100644 decidim-elections/app/models/decidim/elections/election.rb create mode 100644 decidim-elections/app/models/decidim/elections/elections_trustee.rb create mode 100644 decidim-elections/app/models/decidim/elections/question.rb create mode 100644 decidim-elections/app/models/decidim/elections/result.rb create mode 100644 decidim-elections/app/models/decidim/elections/trustee.rb create mode 100644 decidim-elections/app/models/decidim/elections/trustees_participatory_space.rb create mode 100644 decidim-elections/app/models/decidim/elections/vote.rb create mode 100644 decidim-elections/app/models/decidim/votings/ballot_style.rb create mode 100644 decidim-elections/app/models/decidim/votings/ballot_style_question.rb create mode 100644 decidim-elections/app/models/decidim/votings/census/dataset.rb create mode 100644 decidim-elections/app/models/decidim/votings/census/datum.rb create mode 100644 decidim-elections/app/models/decidim/votings/in_person_vote.rb create mode 100644 decidim-elections/app/models/decidim/votings/monitoring_committee_member.rb create mode 100644 decidim-elections/app/models/decidim/votings/polling_officer.rb create mode 100644 decidim-elections/app/models/decidim/votings/polling_station.rb create mode 100644 decidim-elections/app/models/decidim/votings/polling_station_closure.rb create mode 100644 decidim-elections/app/models/decidim/votings/voting.rb create mode 100644 decidim-elections/app/packs/entrypoints/decidim_elections.js create mode 100644 decidim-elections/app/packs/entrypoints/decidim_elections_admin.js create mode 100644 decidim-elections/app/packs/entrypoints/decidim_votings.js create mode 100644 decidim-elections/app/packs/entrypoints/decidim_votings_admin.js create mode 100644 decidim-elections/app/packs/images/decidim/elections/decidim_elections.svg create mode 100644 decidim-elections/app/packs/images/decidim/votings/decidim_votings.svg create mode 100644 decidim-elections/app/packs/src/decidim/elections/admin/pending_action.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/admin/trustees_process.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/admin/vote_statistics.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/broken_promises_handler.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/election_log.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/trustee/key_ceremony.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/trustee/tally.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/trustee/trustee_zone.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/voter/casting-vote.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/voter/new-vote.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/voter/setup-preview.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/voter/setup-vote.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/voter/verify-vote.js create mode 100644 decidim-elections/app/packs/src/decidim/elections/voter/vote_questions.component.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/admin/polling_officers_picker.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/admin/polling_stations_form.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/admin/update_census_dataset_status.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/admin/voting_user_role_form.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/in-person-vote.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/edit-closure.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/in-person-vote.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/new-closure.js create mode 100644 decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/sign-closure.js create mode 100644 decidim-elections/app/packs/stylesheets/decidim/elections/admin/elections.scss create mode 100644 decidim-elections/app/packs/stylesheets/decidim/elections/elections.scss create mode 100644 decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings.scss create mode 100644 decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_ballot-styles.scss create mode 100644 decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_monitoring-committee-polling-station-closures.scss create mode 100644 decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_results.scss create mode 100644 decidim-elections/app/packs/stylesheets/decidim/votings/votings.scss create mode 100644 decidim-elections/app/permissions/decidim/elections/admin/permissions.rb create mode 100644 decidim-elections/app/permissions/decidim/elections/permissions.rb create mode 100644 decidim-elections/app/permissions/decidim/elections/trustee_zone/permissions.rb create mode 100644 decidim-elections/app/permissions/decidim/votings/admin/permissions.rb create mode 100644 decidim-elections/app/permissions/decidim/votings/permissions.rb create mode 100644 decidim-elections/app/permissions/decidim/votings/polling_officer_zone/permissions.rb create mode 100644 decidim-elections/app/presenters/decidim/elections/admin_log/election_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/elections/admin_log/trustee_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/elections/election_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/elections/trustee_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/admin_log/ballot_style_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/admin_log/polling_officer_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/admin_log/polling_station_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/admin_log/voting_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/census/admin_log/dataset_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/polling_station_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/voting_presenter.rb create mode 100644 decidim-elections/app/presenters/decidim/votings/voting_stats_presenter.rb create mode 100644 decidim-elections/app/queries/decidim/elections/admin/pending_actions.rb create mode 100644 decidim-elections/app/queries/decidim/elections/admin/votes_for_statistics.rb create mode 100644 decidim-elections/app/queries/decidim/elections/elections_finished_to_end.rb create mode 100644 decidim-elections/app/queries/decidim/elections/elections_ready_to_start.rb create mode 100644 decidim-elections/app/queries/decidim/elections/filtered_elections.rb create mode 100644 decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space.rb create mode 100644 decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space_trustee_ids.rb create mode 100644 decidim-elections/app/queries/decidim/elections/votes/last_vote_for_voter.rb create mode 100644 decidim-elections/app/queries/decidim/elections/votes/pending_votes.rb create mode 100644 decidim-elections/app/queries/decidim/votings/admin/admin_users.rb create mode 100644 decidim-elections/app/queries/decidim/votings/admin/ballot_style_by_voting_code.rb create mode 100644 decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations.rb create mode 100644 decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations_and_user.rb create mode 100644 decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_user.rb create mode 100644 decidim-elections/app/queries/decidim/votings/organization_prioritized_votings.rb create mode 100644 decidim-elections/app/queries/decidim/votings/organization_promoted_votings.rb create mode 100644 decidim-elections/app/queries/decidim/votings/organization_published_votings.rb create mode 100644 decidim-elections/app/queries/decidim/votings/organization_votings.rb create mode 100644 decidim-elections/app/queries/decidim/votings/prioritized_votings.rb create mode 100644 decidim-elections/app/queries/decidim/votings/published_votings.rb create mode 100644 decidim-elections/app/queries/decidim/votings/votes/in_person_vote_for_voter.rb create mode 100644 decidim-elections/app/queries/decidim/votings/votes/pending_in_person_votes.rb create mode 100644 decidim-elections/app/serializers/decidim/votings/census/datum_serializer.rb create mode 100644 decidim-elections/app/services/decidim/elections/current_user_vote_flow.rb create mode 100644 decidim-elections/app/services/decidim/elections/vote_flow.rb create mode 100644 decidim-elections/app/services/decidim/votings/census/access_codes_exporter.rb create mode 100644 decidim-elections/app/services/decidim/votings/census_vote_flow.rb create mode 100644 decidim-elections/app/views/decidim/elections/admin/answers/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/answers/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/answers/index.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/answers/new.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/elections/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/elections/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/elections/index.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/elections/new.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/proposals_imports/new.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/questions/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/questions/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/questions/index.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/questions/new.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/shared/_trustees_secondary_nav.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_create_election.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_created.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony_ended.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_results_published.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_tally_ended.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_tally_started.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_vote.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_vote_ended.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/_vote_stats.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/steps/index.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/index.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/new.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/elections/_elections.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/elections/election_log.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/elections/index.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/elections/index.js.erb create mode 100644 decidim-elections/app/views/decidim/elections/elections/show.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/shared/_broken_promises_modal.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_mailer/notification.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/elections/_backup_modal.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/elections/_key_ceremony_steps.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/elections/_restore_modal.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/elections/_tally_started_steps.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/elections/show.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/elections/update.js.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_no_public_keys.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_public_keys.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_table.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/trustee_zone/trustees/show.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/vote_accepted_mailer/notification.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_new_ballot_decision_step.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_new_confirm_step.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_new_encrypting_step.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_new_question.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_new_question_modal.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_onboarding_modal.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_show_casted.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_show_casting.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/_show_failed.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/new.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/show.html.erb create mode 100644 decidim-elections/app/views/decidim/elections/votes/verify.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/access_code_mailer/send_access_code.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/ballot_styles/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/ballot_styles/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/ballot_styles/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/ballot_styles/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/_results.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/show.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closure_certificate_results.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closures.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_elections.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/show.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/monitoring_committee_verify_elections/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_officers/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_officers/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_officers/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_stations/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_stations/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_stations/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/polling_stations/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/votings/_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/votings/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/votings/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/admin/votings/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_creating_data.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_export_codes.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_exporting_codes.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_freeze.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_generate_codes.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_generating_codes.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_new_census.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/_upload_info.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/show.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/admin/census/status.js.erb create mode 100644 decidim-elections/app/views/decidim/votings/census/export_mailer/access_codes_export.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_answer_results_form_fields.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_ballot_results_form_fields.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_certify_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_count_error.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_results_count_error.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_question_results_form_fields.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_sign_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/edit.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/show.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_complete_voting.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_in_person_form.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_verify_document.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/new.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/show.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/_polling_station.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/_access_code_modal.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/_check_fields.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/_polling_officers_picker.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/_votings.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/check_census.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/elections_log.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/index.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/index.js.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/login.html.erb create mode 100644 decidim-elections/app/views/decidim/votings/votings/show.html.erb create mode 100644 decidim-elections/app/views/layouts/decidim/admin/voting.html.erb create mode 100644 decidim-elections/app/views/layouts/decidim/admin/votings.html.erb create mode 100755 decidim-elections/bin/rails create mode 100644 decidim-elections/config/assets.rb create mode 100644 decidim-elections/config/environment.rb create mode 100644 decidim-elections/config/initializers/decidim_bulletin_board.rb create mode 100644 decidim-elections/config/initializers/rack_attack.rb create mode 100644 decidim-elections/config/locales/am-ET.yml create mode 100644 decidim-elections/config/locales/ar.yml create mode 100644 decidim-elections/config/locales/bg-BG.yml create mode 100644 decidim-elections/config/locales/bg.yml create mode 100644 decidim-elections/config/locales/ca.yml create mode 100644 decidim-elections/config/locales/cs.yml create mode 100644 decidim-elections/config/locales/da-DK.yml create mode 100644 decidim-elections/config/locales/da.yml create mode 100644 decidim-elections/config/locales/de.yml create mode 100644 decidim-elections/config/locales/el.yml create mode 100644 decidim-elections/config/locales/en.yml create mode 100644 decidim-elections/config/locales/eo-UY.yml create mode 100644 decidim-elections/config/locales/eo.yml create mode 100644 decidim-elections/config/locales/es-MX.yml create mode 100644 decidim-elections/config/locales/es-PY.yml create mode 100644 decidim-elections/config/locales/es.yml create mode 100644 decidim-elections/config/locales/et-EE.yml create mode 100644 decidim-elections/config/locales/et.yml create mode 100644 decidim-elections/config/locales/eu.yml create mode 100644 decidim-elections/config/locales/fa-IR.yml create mode 100644 decidim-elections/config/locales/fi-plain.yml create mode 100644 decidim-elections/config/locales/fi.yml create mode 100644 decidim-elections/config/locales/fr-CA.yml create mode 100644 decidim-elections/config/locales/fr-LU.yml create mode 100644 decidim-elections/config/locales/fr.yml create mode 100644 decidim-elections/config/locales/ga-IE.yml create mode 100644 decidim-elections/config/locales/gl.yml create mode 100644 decidim-elections/config/locales/gn-PY.yml create mode 100644 decidim-elections/config/locales/hr-HR.yml create mode 100644 decidim-elections/config/locales/hr.yml create mode 100644 decidim-elections/config/locales/hu.yml create mode 100644 decidim-elections/config/locales/id-ID.yml create mode 100644 decidim-elections/config/locales/is-IS.yml create mode 100644 decidim-elections/config/locales/is.yml create mode 100644 decidim-elections/config/locales/it.yml create mode 100644 decidim-elections/config/locales/ja.yml create mode 100644 decidim-elections/config/locales/ka-GE.yml create mode 100644 decidim-elections/config/locales/kaa.yml create mode 100644 decidim-elections/config/locales/ko-KR.yml create mode 100644 decidim-elections/config/locales/ko.yml create mode 100644 decidim-elections/config/locales/lb-LU.yml create mode 100644 decidim-elections/config/locales/lb.yml create mode 100644 decidim-elections/config/locales/lo-LA.yml create mode 100644 decidim-elections/config/locales/lt-LT.yml create mode 100644 decidim-elections/config/locales/lt.yml create mode 100644 decidim-elections/config/locales/lv.yml create mode 100644 decidim-elections/config/locales/mt-MT.yml create mode 100644 decidim-elections/config/locales/mt.yml create mode 100644 decidim-elections/config/locales/nl.yml create mode 100644 decidim-elections/config/locales/no.yml create mode 100644 decidim-elections/config/locales/oc-FR.yml create mode 100644 decidim-elections/config/locales/om-ET.yml create mode 100644 decidim-elections/config/locales/pl.yml create mode 100644 decidim-elections/config/locales/pt-BR.yml create mode 100644 decidim-elections/config/locales/pt.yml create mode 100644 decidim-elections/config/locales/ro-RO.yml create mode 100644 decidim-elections/config/locales/ru.yml create mode 100644 decidim-elections/config/locales/si-LK.yml create mode 100644 decidim-elections/config/locales/sk.yml create mode 100644 decidim-elections/config/locales/sl.yml create mode 100644 decidim-elections/config/locales/so-SO.yml create mode 100644 decidim-elections/config/locales/sq-AL.yml create mode 100644 decidim-elections/config/locales/sr-CS.yml create mode 100644 decidim-elections/config/locales/sv.yml create mode 100644 decidim-elections/config/locales/sw-KE.yml create mode 100644 decidim-elections/config/locales/th-TH.yml create mode 100644 decidim-elections/config/locales/ti-ER.yml create mode 100644 decidim-elections/config/locales/tr-TR.yml create mode 100644 decidim-elections/config/locales/uk.yml create mode 100644 decidim-elections/config/locales/val-ES.yml create mode 100644 decidim-elections/config/locales/vi-VN.yml create mode 100644 decidim-elections/config/locales/vi.yml create mode 100644 decidim-elections/config/locales/zh-CN.yml create mode 100644 decidim-elections/config/locales/zh-TW.yml create mode 100644 decidim-elections/db/migrate/20200430083618_create_decidim_elections_elections.rb create mode 100644 decidim-elections/db/migrate/20200518082327_create_decidim_elections_questions.rb create mode 100644 decidim-elections/db/migrate/20200518084144_create_decidim_elections_answers.rb create mode 100644 decidim-elections/db/migrate/20200601141412_add_published_at_to_elections.rb create mode 100644 decidim-elections/db/migrate/20200807125040_remove_subtitle_from_decidim_elections.rb create mode 100644 decidim-elections/db/migrate/20200910103648_add_min_selections_to_decidim_elections_questions.rb create mode 100644 decidim-elections/db/migrate/20200915142713_add_questionnaire_to_existing_elections.rb create mode 100644 decidim-elections/db/migrate/20200918153813_create_decidim_elections_trustees_participatory_spaces.rb create mode 100644 decidim-elections/db/migrate/20200918153824_create_decidim_elections_elections_trustees.rb create mode 100644 decidim-elections/db/migrate/20200918153835_create_decidim_elections_trustees.rb create mode 100644 decidim-elections/db/migrate/20201026163334_add_blocked_at_and_bb_status_to_elections.rb create mode 100644 decidim-elections/db/migrate/20201028090110_add_votes_to_decidim_elections_answers.rb create mode 100644 decidim-elections/db/migrate/20201028135614_add_selected_to_decidim_elections_answers.rb create mode 100644 decidim-elections/db/migrate/20201209110653_create_decidim_elections_votes.rb create mode 100644 decidim-elections/db/migrate/20201216091123_add_name_to_trustees.rb create mode 100644 decidim-elections/db/migrate/20210113113818_add_message_id_to_decidim_elections_votes.rb create mode 100644 decidim-elections/db/migrate/20210113120115_create_decidim_elections_votings.rb create mode 100644 decidim-elections/db/migrate/20210114100749_add_published_at_to_decidim_votings_votings.rb create mode 100644 decidim-elections/db/migrate/20210114111850_add_user_to_decidim_elections_votes.rb create mode 100644 decidim-elections/db/migrate/20210120164634_add_promoted_to_voting.rb create mode 100644 decidim-elections/db/migrate/20210125124801_add_voting_type_to_voting_voting.rb create mode 100644 decidim-elections/db/migrate/20210129124956_create_decidim_elections_actions.rb create mode 100644 decidim-elections/db/migrate/20210204132111_add_votings_polling_stations.rb create mode 100644 decidim-elections/db/migrate/20210208090441_add_polling_officers_to_votings.rb create mode 100644 decidim-elections/db/migrate/20210210090738_add_votings_polling_station_references_to_votings_polling_officer.rb create mode 100644 decidim-elections/db/migrate/20210216074707_add_monitoring_committee_member.rb create mode 100644 decidim-elections/db/migrate/20210308104024_add_decidim_votings_census_datasets.rb create mode 100644 decidim-elections/db/migrate/20210308104154_add_decidim_votings_census_data.rb create mode 100644 decidim-elections/db/migrate/20210310120708_add_followable_counter_cache_to_votings.rb create mode 100644 decidim-elections/db/migrate/20210326090435_create_elections_results.rb create mode 100644 decidim-elections/db/migrate/20210330102348_remove_votes_count_from_answer.rb create mode 100644 decidim-elections/db/migrate/20210330123606_add_voting_ballot_style.rb create mode 100644 decidim-elections/db/migrate/20210330183204_add_email_to_votes.rb create mode 100644 decidim-elections/db/migrate/20210331152729_add_census_contact_information_to_votings.rb create mode 100644 decidim-elections/db/migrate/20210401095507_add_organization_to_decidim_elections_trustee.rb create mode 100644 decidim-elections/db/migrate/20210402102215_add_ballot_style_to_decidim_votings_census_data.rb create mode 100644 decidim-elections/db/migrate/20210402140402_add_salt_to_elections.rb create mode 100644 decidim-elections/db/migrate/20210412144721_change_elections_results.rb create mode 100644 decidim-elections/db/migrate/20210412144740_create_elections_bulletin_board_closures.rb create mode 100644 decidim-elections/db/migrate/20210412144741_create_votings_polling_station_closures.rb create mode 100644 decidim-elections/db/migrate/20210420112721_create_decidim_votings_in_person_votes.rb create mode 100644 decidim-elections/db/migrate/20210422124826_add_verifiable_results_to_decidim_elections_election.rb create mode 100644 decidim-elections/db/migrate/20210426072845_add_signed_at_to_polling_station_closure.rb create mode 100644 decidim-elections/db/migrate/20210427131742_add_validated_at_to_votings_polling_station_closures.rb create mode 100644 decidim-elections/db/migrate/20220404112802_rename_bb_status_tally_to_tally_started.rb create mode 100644 decidim-elections/db/migrate/20220424121541_add_show_check_census_to_votings.rb create mode 100644 decidim-elections/db/migrate/20220615102642_remove_description_from_elections_questions.rb create mode 100644 decidim-elections/db/migrate/20220711112802_rename_dataset_file_to_filename.rb create mode 100644 decidim-elections/decidim-elections.gemspec create mode 100644 decidim-elections/docs/docker/bulletin_board/docker-compose.yml create mode 100644 decidim-elections/docs/docker/bulletin_board_test/docker-compose.yml create mode 100644 decidim-elections/lib/decidim/api/bulletin_board_closure_type.rb create mode 100644 decidim-elections/lib/decidim/api/election_answer_type.rb create mode 100644 decidim-elections/lib/decidim/api/election_question_type.rb create mode 100644 decidim-elections/lib/decidim/api/election_result_type.rb create mode 100644 decidim-elections/lib/decidim/api/election_type.rb create mode 100644 decidim-elections/lib/decidim/api/elections_type.rb create mode 100644 decidim-elections/lib/decidim/api/polling_station_closure_type.rb create mode 100644 decidim-elections/lib/decidim/api/polling_station_type.rb create mode 100644 decidim-elections/lib/decidim/api/trustee_type.rb create mode 100644 decidim-elections/lib/decidim/api/voting_type.rb create mode 100644 decidim-elections/lib/decidim/elections.rb create mode 100644 decidim-elections/lib/decidim/elections/admin.rb create mode 100644 decidim-elections/lib/decidim/elections/admin_engine.rb create mode 100644 decidim-elections/lib/decidim/elections/answer_serializer.rb create mode 100644 decidim-elections/lib/decidim/elections/api.rb create mode 100644 decidim-elections/lib/decidim/elections/component.rb create mode 100644 decidim-elections/lib/decidim/elections/engine.rb create mode 100644 decidim-elections/lib/decidim/elections/menu.rb create mode 100644 decidim-elections/lib/decidim/elections/seeds.rb create mode 100644 decidim-elections/lib/decidim/elections/test/factories.rb create mode 100644 decidim-elections/lib/decidim/elections/trustee_zone.rb create mode 100644 decidim-elections/lib/decidim/elections/trustee_zone_engine.rb create mode 100644 decidim-elections/lib/decidim/elections/version.rb create mode 100644 decidim-elections/lib/decidim/votings.rb create mode 100644 decidim-elections/lib/decidim/votings/admin.rb create mode 100644 decidim-elections/lib/decidim/votings/admin_engine.rb create mode 100644 decidim-elections/lib/decidim/votings/api.rb create mode 100644 decidim-elections/lib/decidim/votings/census.rb create mode 100644 decidim-elections/lib/decidim/votings/census_admin.rb create mode 100644 decidim-elections/lib/decidim/votings/census_admin_engine.rb create mode 100644 decidim-elections/lib/decidim/votings/census_engine.rb create mode 100644 decidim-elections/lib/decidim/votings/census_menu.rb create mode 100644 decidim-elections/lib/decidim/votings/content_blocks/registry_manager.rb create mode 100644 decidim-elections/lib/decidim/votings/engine.rb create mode 100644 decidim-elections/lib/decidim/votings/menu.rb create mode 100644 decidim-elections/lib/decidim/votings/participatory_space.rb create mode 100644 decidim-elections/lib/decidim/votings/polling_officer_zone.rb create mode 100644 decidim-elections/lib/decidim/votings/polling_officer_zone_engine.rb create mode 100644 decidim-elections/lib/decidim/votings/polling_officer_zone_menu.rb create mode 100644 decidim-elections/lib/decidim/votings/query_extensions.rb create mode 100644 decidim-elections/lib/decidim/votings/seeds.rb create mode 100644 decidim-elections/lib/decidim/votings/test/factories.rb create mode 100644 decidim-elections/lib/decidim/votings/voting_serializer.rb create mode 100644 decidim-elections/lib/tasks/decidim_elections.rake create mode 100644 decidim-elections/lib/tasks/decidim_voting_census.rake create mode 100644 decidim-elections/spec/cells/decidim/elections/election_cell_spec.rb create mode 100644 decidim-elections/spec/cells/decidim/elections/election_g_cell_spec.rb create mode 100644 decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_cell_spec.rb create mode 100644 decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell_spec.rb create mode 100644 decidim-elections/spec/cells/decidim/votings/voting_cell_spec.rb create mode 100644 decidim-elections/spec/cells/decidim/votings/voting_dropdown_metadata_cell_spec.rb create mode 100644 decidim-elections/spec/cells/decidim/votings/voting_g_cell_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/add_user_as_trustee_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/create_answer_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/create_election_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/create_question_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/destroy_answer_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/destroy_election_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/destroy_question_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/end_vote_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/import_proposals_to_elections_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/publish_election_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/publish_results_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/remove_trustee_from_participatory_space_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/report_missing_trustee_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/setup_election_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/start_key_ceremony_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/start_tally_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/start_vote_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/unpublish_election_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/update_answer_selection_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/update_answer_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/update_election_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/update_question_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/admin/update_trustee_participatory_space_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/trustee_zone/update_trustee_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/voter/cast_vote_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/elections/voter/update_vote_status_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/create_ballot_style_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/create_monitoring_committee_member_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/create_polling_officer_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/create_polling_station_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/create_voting_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/destroy_ballot_style_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/destroy_monitoring_committee_member_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_officer_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_station_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/publish_voting_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/unpublish_voting_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/update_ballot_style_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/update_polling_station_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/admin/update_voting_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/create_dataset_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/create_datum_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/destroy_dataset_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/increment_dataset_processed_rows_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_export_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_generation_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/census/admin/update_dataset_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/certify_polling_station_closure_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/check_census_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/create_polling_station_closure_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/create_polling_station_results_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/destroy_polling_station_closure_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/send_access_code_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/sign_polling_station_closure_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/voter/in_person_vote_spec.rb create mode 100644 decidim-elections/spec/commands/decidim/votings/voter/update_in_person_vote_status_spec.rb create mode 100644 decidim-elections/spec/controllers/decidim/elections/admin/answers_controller_spec.rb create mode 100644 decidim-elections/spec/controllers/decidim/elections/admin/components_controller_spec.rb create mode 100644 decidim-elections/spec/controllers/decidim/elections/admin/elections_controller_spec.rb create mode 100644 decidim-elections/spec/controllers/decidim/votings/exports_controller_spec.rb create mode 100644 decidim-elections/spec/controllers/decidim/votings/votings_controller_spec.rb create mode 100644 decidim-elections/spec/events/decidim/elections/election_published_event_spec.rb create mode 100644 decidim-elections/spec/events/decidim/elections/trustees/notify_new_trustee_event_spec.rb create mode 100644 decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_new_election_event_spec.rb create mode 100644 decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_tally_process_event_spec.rb create mode 100644 decidim-elections/spec/events/decidim/elections/votes/vote_accepted_event_spec.rb create mode 100644 decidim-elections/spec/events/decidim/votings/polling_officers/polling_station_assigned_event_spec.rb create mode 100644 decidim-elections/spec/factories.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/action_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/answer_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/answer_import_proposals_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/election_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/question_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/report_missing_trustee_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/setup_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/admin/vote_period_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/trustee_zone/trustee_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/voter/verify_vote_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/elections/voter/vote_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/admin/attachment_collection_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/admin/attachment_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/admin/ballot_style_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/admin/polling_station_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/admin/voting_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/admin/voting_user_role_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/answer_result_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/ballot_result_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/census/admin/dataset_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/census/admin/datum_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/census/check_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/census/in_person_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/census/login_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/closure_certify_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/closure_result_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/closure_sign_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/envelopes_result_form_spec.rb create mode 100644 decidim-elections/spec/forms/decidim/votings/question_result_form_spec.rb create mode 100644 decidim-elections/spec/helpers/decidim/elections/steps_helper_spec.rb create mode 100644 decidim-elections/spec/helpers/decidim/elections/votes_helper_spec.rb create mode 100644 decidim-elections/spec/jobs/decidim/votings/census/admin/create_datum_job_spec.rb create mode 100644 decidim-elections/spec/jobs/decidim/votings/census/admin/export_access_codes_job_spec.rb create mode 100644 decidim-elections/spec/jobs/decidim/votings/census/admin/generate_access_codes_job_spec.rb create mode 100644 decidim-elections/spec/lib/components/votings/current_component_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/elections/admin_engine_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/elections/answer_serializer_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/elections/component_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/elections/engine_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/votings/admin_engine_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/votings/census_engine_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/votings/engine_spec.rb create mode 100644 decidim-elections/spec/lib/decidim/votings/voting_serializer_spec.rb create mode 100644 decidim-elections/spec/lib/query_extensions_spec.rb create mode 100644 decidim-elections/spec/lib/tasks/decidim_election_generate_identification_keys_spec.rb create mode 100644 decidim-elections/spec/lib/tasks/decidim_election_scheduled_tasks_spec.rb create mode 100644 decidim-elections/spec/lib/tasks/decidim_tasks_delete_download_your_data_files_spec.rb create mode 100644 decidim-elections/spec/lib/tasks/main_spec.rb create mode 100644 decidim-elections/spec/mailers/decidim/elections/trustee_mailer_spec.rb create mode 100644 decidim-elections/spec/mailers/decidim/elections/vote_accepted_mailer_spec.rb create mode 100644 decidim-elections/spec/mailers/decidim/votings/access_code_mailer_spec.rb create mode 100644 decidim-elections/spec/mailers/decidim/votings/census/export_mailer_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/action_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/answer_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/bulletin_board_closure_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/election_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/question_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/result_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/trustee_spec.rb create mode 100644 decidim-elections/spec/models/decidim/elections/vote_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/ballot_style_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/census/dataset_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/census/datum_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/polling_officer_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/polling_station_closure_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/polling_station_spec.rb create mode 100644 decidim-elections/spec/models/decidim/votings/voting_spec.rb create mode 100644 decidim-elections/spec/permissions/decidim/elections/admin/permissions_spec.rb create mode 100644 decidim-elections/spec/permissions/decidim/elections/permissions_spec.rb create mode 100644 decidim-elections/spec/permissions/decidim/elections/trustee_zone/permissions_spec.rb create mode 100644 decidim-elections/spec/permissions/decidim/votings/admin/permissions_spec.rb create mode 100644 decidim-elections/spec/permissions/decidim/votings/permissions_spec.rb create mode 100644 decidim-elections/spec/permissions/decidim/votings/polling_officer_zone/permissions_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/elections/admin_log/election_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/elections/admin_log/trustee_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/elections/election_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/elections/trustee_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/admin_log/ballot_style_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/admin_log/polling_officer_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/admin_log/polling_station_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/admin_log/voting_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/census/admin_log/dataset_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/voting_presenter_spec.rb create mode 100644 decidim-elections/spec/presenters/decidim/votings/voting_stats_presenter_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/elections/admin/pending_actions_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/elections/admin/votes_for_statistics_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_trustee_ids_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/elections/votes/pending_votes_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/elections/votes/user_election_last_vote_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/votings/admin/admin_users_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/votings/organization_prioritized_votings_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/votings/organization_promoted_votings_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/votings/organization_published_votings_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/votings/organization_votings_spec.rb create mode 100644 decidim-elections/spec/queries/decidim/votings/votes/in_person_vote_for_voter_spec.rb create mode 100644 decidim-elections/spec/requests/election_search_spec.rb create mode 100644 decidim-elections/spec/requests/voting_search_spec.rb create mode 100644 decidim-elections/spec/serializers/decidim/votings/census/datum_serializer_spec.rb create mode 100644 decidim-elections/spec/services/decidim/elections/current_user_vote_flow_spec.rb create mode 100644 decidim-elections/spec/services/decidim/votings/census/access_codes_exporter_spec.rb create mode 100644 decidim-elections/spec/services/decidim/votings/census_vote_flow_spec.rb create mode 100644 decidim-elections/spec/shared/admin_manages_election_shared_context.rb create mode 100644 decidim-elections/spec/shared/admin_manages_voting_examples.rb create mode 100644 decidim-elections/spec/shared/full_election_process_shared_context.rb create mode 100644 decidim-elections/spec/shared/manage_voting_attachment_collections_examples.rb create mode 100644 decidim-elections/spec/shared/manage_voting_attachments_examples.rb create mode 100644 decidim-elections/spec/shared/manage_voting_components_examples.rb create mode 100644 decidim-elections/spec/shared/monitoring_committee_member_manages_voting_examples.rb create mode 100644 decidim-elections/spec/shared/test_bulletin_board_shared_context.rb create mode 100644 decidim-elections/spec/shared/vote_examples.rb create mode 100644 decidim-elections/spec/spec_helper.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_answers_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_election_steps_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_elections_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_questions_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_trustees_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_attachment_collections_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_attachments_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_ballot_styles_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_census_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_component_permissions_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_components_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_monitoring_committee_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_polling_officers_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_polling_stations_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_voting_publication_spec.rb create mode 100644 decidim-elections/spec/system/admin/admin_manages_votings_spec.rb create mode 100644 decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_polling_station_closures_spec.rb create mode 100644 decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_results_spec.rb create mode 100644 decidim-elections/spec/system/admin/monitoring_committee_member_manages_votings_spec.rb create mode 100644 decidim-elections/spec/system/admin/monitoring_committee_member_verifies_elections_spec.rb create mode 100644 decidim-elections/spec/system/check_census_spec.rb create mode 100644 decidim-elections/spec/system/election_log_spec.rb create mode 100644 decidim-elections/spec/system/elections_log_spec.rb create mode 100644 decidim-elections/spec/system/explore_elections_spec.rb create mode 100644 decidim-elections/spec/system/homepage_content_blocks_spec.rb create mode 100644 decidim-elections/spec/system/key_ceremony_spec.rb create mode 100644 decidim-elections/spec/system/polling_officer_zone_spec.rb create mode 100644 decidim-elections/spec/system/preview_elections_with_share_token_spec.rb create mode 100644 decidim-elections/spec/system/sorting_elections_spec.rb create mode 100644 decidim-elections/spec/system/trustee_zone_spec.rb create mode 100644 decidim-elections/spec/system/vote_in_person_spec.rb create mode 100644 decidim-elections/spec/system/vote_online_inside_a_voting_spec.rb create mode 100644 decidim-elections/spec/system/vote_online_spec.rb create mode 100644 decidim-elections/spec/system/voting_spec.rb create mode 100644 decidim-elections/spec/system/votings_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/bulletin_board_closure_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/election_answer_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/election_question_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/election_result_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/election_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/elections_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/elections/trustee_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/votings/polling_station_closure_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/votings/polling_station_type_spec.rb create mode 100644 decidim-elections/spec/types/decidim/votings/voting_type_spec.rb create mode 100644 decidim-elections/spec/types/integration_schema_spec.rb diff --git a/decidim-elections/.byebug_history b/decidim-elections/.byebug_history new file mode 100644 index 00000000..950e620e --- /dev/null +++ b/decidim-elections/.byebug_history @@ -0,0 +1,89 @@ +continue +record +continue +record +record.model_name +continue +record.model_name +record.class +continue +record.class +record.model_name +continue +record.model_name +record.model +record.models +record.class +options +record +q +object +options +record +record[0] +record.class +q +continue +record.class +record[1] +record[0] +record +record.record +record.model +record.public_methods - Object.public_methods +Object.public_methods +record.public_methods +record.public_method +record.keys +record +options +q +continue +options +continue +record +q +record +q +current_user.roles +current_user.role +current_user +q +puts subject +continue +puts subject +continue +organization +voting +continue +puts subject +continue +puts subject +continue +puts subject +continue +expect(subject).to eq("") +subject.html +continue +pp voting +voting +voting.image +continue +voting.image +voting +n +voting.attachments +continue +voting.attachments.count +continue +page +continue +puts page.driver.to_s +page.driver +page.driver.browser.manage.logs.get(:browser) +continue +sleep 10 +take_screenshot +upload_keys +continue +puts Capybara.default_max_wait_time diff --git a/decidim-elections/.gitignore b/decidim-elections/.gitignore new file mode 100644 index 00000000..49649c5b --- /dev/null +++ b/decidim-elections/.gitignore @@ -0,0 +1,17 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +# rspec failure tracking +.rspec-failures + +# default test application +spec/decidim_dummy_app + +# default development application +development_app diff --git a/decidim-elections/README.md b/decidim-elections/README.md new file mode 100644 index 00000000..c4df3c3e --- /dev/null +++ b/decidim-elections/README.md @@ -0,0 +1,103 @@ +# Decidim::Elections + +:warning: This module is under development and is not ready to be used in production. + +The Elections module adds elections to any participatory space. + +## Usage + +Elections will be available as a Component for a Participatory Space. + +In order to celebrate [End-to-end auditable votings](https://en.wikipedia.org/wiki/End-to-end_auditable_voting_systems) using the Elections module, you will need to connect your Decidim instance with an instance of the [Decidim Bulletin Board application](https://github.com/decidim/decidim-bulletin-board/). To create this connection, please check the [instructions](https://docs.decidim.org/en/services/elections_bulletin_board/). + +## Development + +In the case that you only want to use this module for local development or testing purposes, you have an example docker-compose configuration in `docs/`. Mind that this setup is not recommended for production environments. It works with default seeds and configurations for a Decidim installation, so you should not do anything. + +```bash +cd docs/docker/bulletin_board +docker-compose up +``` + +One important caveat is that as the Trustees' key generation functionality uses the IndexedDB API, and at least in Firefox there is unsupported in the Private Browsing mode (see ticket [#1639542 in Mozilla's Bugzilla](https://bugzilla.mozilla.org/show_bug.cgi?id=1639542)). As a workaround there is the [Firefox Multi-Account Containers addon](https://addons.mozilla.org/es/firefox/addon/multi-account-containers/). + +## Testing + +Besides the [set-up typical for Decidim](https://docs.decidim.org/en/develop/develop/testing), for some of the specs a Bulletin Board installation is needed, running in port 5017 by default with the `DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL` environment variable set up with the "true" string. There is a working configuration on `docs`. + +```bash +cd docs/docker/bulletin_board_test +docker-compose up +``` + +As the Bulletin Board service is a necessary dependency for this module to work, if it is not running while executing the specs, the following exception will be shown. + +> Failure/Error: Decidim::Elections.bulletin_board.reset_test_database +> +> StandardError: +> Sorry, something went wrong +> +> ./spec/shared/test_bulletin_board_shared_context.rb:6:in 'block (2 levels) in ' +> (...) +> -- Caused by: +> Errno::ECONNREFUSED: +> Connection refused - connect(2) for 127.0.0.1:5017 +> ./spec/shared/test_bulletin_board_shared_context.rb:6:in 'block (2 levels) in ' + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem "decidim-elections" +``` + +And then execute: + +```bash +bundle +``` + +## Configuration + +### Identification numbers + +For the verification of the participants' data in the Voting's census, you can configure which type of documents a participant can have. By default these documents are `identification_number` and `passport`, but in some countries you may need to adapt these to your specifics needs. For instance, in Spain there are `dni`, `nie` and `passport`. + +For configuring these you can do so with the Environment Variable `ELECTIONS_DOCUMENT_TYPES`. + +```env +ELECTIONS_DOCUMENT_TYPES="dni,nie,passport" +``` + +You need to also add the following keys in your i18n files (i.e. `config/locales/en.yml`). + +```yaml +en: + decidim: + votings: + census: + document_types: + dni: DNI + nie: NIE + passport: Passport +``` + +### Scheduled tasks + +For the Elections module to function as expected, there are some background tasks that should be scheduled to be executed regularly. Alternatively you could use `whenever` gem or the scheduled jobs of your hosting provider. + +You can configure it with `crontab -e`, for instance if you have created your Decidim application on /home/user/decidim_application: + +```bash +# Remove census export files +0 0 * * * cd /home/user/decidim_application && RAILS_ENV=production bundle exec rake decidim_votings_census:delete_census_access_codes_export +``` + +## Contributing + +See [Decidim](https://github.com/decidim/decidim). + +## License + +See [Decidim](https://github.com/decidim/decidim). diff --git a/decidim-elections/Rakefile b/decidim-elections/Rakefile new file mode 100644 index 00000000..447b5c1b --- /dev/null +++ b/decidim-elections/Rakefile @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "decidim/dev/common_rake" diff --git a/decidim-elections/app/cells/decidim/elections/content_blocks/related_elections_cell.rb b/decidim-elections/app/cells/decidim/elections/content_blocks/related_elections_cell.rb new file mode 100644 index 00000000..0fed75aa --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/content_blocks/related_elections_cell.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module ContentBlocks + class RelatedElectionsCell < Decidim::ContentBlocks::HighlightedElementsWithCellForListCell + private + + def list_cell_path = "decidim/elections/highlighted_elections_for_component" + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_cell.rb b/decidim-elections/app/cells/decidim/elections/election_cell.rb new file mode 100644 index 00000000..60e30f2c --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_cell.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the election card for an instance of an election + # the default size is the Search Card (:s) + class ElectionCell < Decidim::ViewModel + include ElectionCellsHelper + include Cell::ViewModel::Partial + + def show + cell card_size, model, options + end + + private + + def card_size + case @options[:size] + when :s + "decidim/elections/election_s" + else + "decidim/elections/election_g" + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_g_cell.rb b/decidim-elections/app/cells/decidim/elections/election_g_cell.rb new file mode 100644 index 00000000..31f067fa --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_g_cell.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the Grid (:g) election card + # for a given instance of an Election + class ElectionGCell < Decidim::CardGCell + include ElectionCellsHelper + + def metadata_cell + "decidim/elections/election_metadata" + end + + def has_image? + model.photos.present? + end + + def resource_image_path + model.photos.first.url if has_image? + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_metadata_cell.rb b/decidim-elections/app/cells/decidim/elections/election_metadata_cell.rb new file mode 100644 index 00000000..f2e8249a --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_metadata_cell.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders metadata for an instance of an Election + class ElectionMetadataCell < Decidim::CardMetadataCell + def initialize(*) + super + + @items.prepend(*election_items) + end + + private + + def election_items + [dates_metadata_item, state_item] + end + + def state_item + return if model.voting_period_status.blank? + + { text: content_tag(:span, I18n.t(model.voting_period_status, scope: "decidim.elections.election_m.badge_name"), class: "label #{state_class}") } + end + + def state_class + case model.voting_period_status + when :ongoing + "success" + when :upcoming + "warning" + end + end + + def start_date + return unless model.start_time + + model.start_time.to_date + end + + def end_date + return unless model.end_time + + model.end_time.to_date + end + + def dates_metadata_item + { + icon: "calendar-todo-line", + text: [ + start_date.present? ? l(start_date, format: :decidim_short_with_month_name_short) : "?", + end_date.present? ? l(end_date, format: :decidim_short_with_month_name_short) : "?" + ].join(" → ") + } + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_preview/show.erb b/decidim-elections/app/cells/decidim/elections/election_preview/show.erb new file mode 100644 index 00000000..b10b6dfa --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_preview/show.erb @@ -0,0 +1,28 @@ +
+ <%= icon "question-mark" %> +

<%= t("decidim.elections.elections.preview.title") %>

+
+ +

<%= t("decidim.elections.elections.preview.description") %>

+ +
    + <% model.questions.each_with_index do |question, ix| %> +
  • + + + +
  • + <% end %> +
diff --git a/decidim-elections/app/cells/decidim/elections/election_preview_cell.rb b/decidim-elections/app/cells/decidim/elections/election_preview_cell.rb new file mode 100644 index 00000000..e6a7f85a --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_preview_cell.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the questions preview + # for a given instance of an Election + class ElectionPreviewCell < Decidim::ViewModel + def show + render unless model.finished? + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_results/progress_bar.erb b/decidim-elections/app/cells/decidim/elections/election_results/progress_bar.erb new file mode 100644 index 00000000..b9d52858 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_results/progress_bar.erb @@ -0,0 +1,15 @@ +
+
+
+
+
+ +
+ <%= t("decidim.elections.elections.results.percentage", count: results_percentage) %> +
+
+ +
+ <%= t("decidim.elections.elections.results.votes", count: results_total) %> +
+
diff --git a/decidim-elections/app/cells/decidim/elections/election_results/show.erb b/decidim-elections/app/cells/decidim/elections/election_results/show.erb new file mode 100644 index 00000000..f3ea8f9a --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_results/show.erb @@ -0,0 +1,43 @@ +
+ <%= icon "bar-chart-box-line" %> +

<%= t("decidim.elections.elections.results.title") %>

+
+ +

<%= t("decidim.elections.elections.results.description") %>

+ +
    + <% model.questions.each_with_index do |question, ix| %> +
  • + + + +
  • + <% end %> +
diff --git a/decidim-elections/app/cells/decidim/elections/election_results_cell.rb b/decidim-elections/app/cells/decidim/elections/election_results_cell.rb new file mode 100644 index 00000000..d2ac28d8 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_results_cell.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the results + # for a given instance of an Election + class ElectionResultsCell < Decidim::ViewModel + def show + render if model.results_published? + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_s_cell.rb b/decidim-elections/app/cells/decidim/elections/election_s_cell.rb new file mode 100644 index 00000000..bd1d91bf --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_s_cell.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the Search (:s) election card + # for a given instance of an Election + class ElectionSCell < Decidim::CardSCell + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/election_vote_cta/show.erb b/decidim-elections/app/cells/decidim/elections/election_vote_cta/show.erb new file mode 100644 index 00000000..7e472133 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_vote_cta/show.erb @@ -0,0 +1,23 @@ +<% if model.ongoing? && already_voted? %> + <%= cell("decidim/announcement", callout_text) %> +<% end %> + +<% if model.ongoing? %> + <% if vote_flow.login_path(new_election_vote_path) %> + <%= link_to vote_flow.login_path(new_election_vote_path), class: "button button__lg button__secondary w-full" do %> + <%= vote_action_button_text %> + <% end %> + <% else %> + <%= action_authorized_link_to :vote, new_election_vote_path, resource: model, class: "button button__lg button__secondary w-full" do %> + <%= vote_action_button_text %> + <% end %> + <% end %> +<% elsif !model.finished? %> + +<% end %> + +<% if preview_mode? && can_preview? %> + <%= link_to new_election_vote_path, class: "button button__lg button__secondary w-full mt-4" do %> + <%= t("preview", scope: "decidim.elections.elections.show") %> + <% end %> +<% end %> diff --git a/decidim-elections/app/cells/decidim/elections/election_vote_cta_cell.rb b/decidim-elections/app/cells/decidim/elections/election_vote_cta_cell.rb new file mode 100644 index 00000000..80c012b9 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/election_vote_cta_cell.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the results + # for a given instance of an Election + class ElectionVoteCtaCell < Decidim::ViewModel + include Decidim::Elections::HasVoteFlow + + delegate :current_user, + :current_participatory_space, + :allowed_to?, + to: :controller + + private + + # This is needed by HasVoteFlow + def election + model + end + + def last_vote + @last_vote ||= Decidim::Elections::Votes::LastVoteForVoter.for(model, vote_flow.voter_id) if vote_flow.has_voter? + end + + def new_election_vote_path + engine_router.new_election_vote_path( + "#{key_participatory_space_slug}": current_participatory_space.slug, + component_id: current_component.id, + election_id: model.id + ) + end + + def vote_action_button_text + if !already_voted? + t("action_button.vote", scope: "decidim.elections.elections.show") + elsif last_vote_accepted? + t("action_button.change_vote", scope: "decidim.elections.elections.show") + else + t("action_button.vote_again", scope: "decidim.elections.elections.show") + end + end + + def callout_text + if last_vote_pending? + t("callout.pending_vote", scope: "decidim.elections.elections.show") + elsif last_vote_accepted? + t("callout.already_voted", scope: "decidim.elections.elections.show") + else + t("callout.vote_rejected", scope: "decidim.elections.elections.show") + end + end + + def already_voted? + last_vote.present? + end + + def last_vote_pending? + !!last_vote&.pending? + end + + def last_vote_accepted? + !!last_vote&.accepted? + end + + def current_component + model.component + end + + def key_participatory_space_slug + "#{current_participatory_space.underscored_name}_slug".to_sym + end + + def engine_router + @engine_router ||= EngineRouter.main_proxy(current_component || model) + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component/show.erb b/decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component/show.erb new file mode 100644 index 00000000..73a8ca77 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component/show.erb @@ -0,0 +1,18 @@ +
+

+ <%= single_component? ? decidim_escape_translated(model.name) : t("decidim.components.elections.name") %> +

+
<%= elections_count %>
+ <% if see_all_path.present? %> + <%= link_to see_all_path, class: "button button__sm button__text-secondary" do %> + <%= t("decidim.participatory_spaces.related_elections.see_all") %> + <%= icon "arrow-right-line" %> + <% end %> + <% end %> +
+ +
+ <% elections.each do |election| %> + <%= card_for(election, link_whole_card: true, title_tag: :h3, **options.slice(:show_space)) %> + <% end %> +
diff --git a/decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component_cell.rb b/decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component_cell.rb new file mode 100644 index 00000000..b7be2b13 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/highlighted_elections_for_component_cell.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "cell/partial" + +module Decidim + module Elections + # This cell renders the highlighted elections for a given participatory + # space. It is intended to be used in the `participatory_space_highlighted_elements` + # view hook. + class HighlightedElectionsForComponentCell < Decidim::ViewModel + include ElectionCellsHelper + include Decidim::ComponentPathHelper + include Decidim::CardHelper + + def show + render unless items_blank? + end + + def items_blank? + elections_count.zero? + end + + private + + def elections_count + @elections_count ||= elections.size + end + + def elections + @elections ||= Decidim::Elections::Election.where(component: model).published + end + + def single_component? + @single_component ||= model.is_a?(Decidim::Component) + end + + def see_all_path + @see_all_path ||= options[:see_all_path] || (single_component? && main_component_path(model)) + end + + def cache_hash + hash = [] + hash << "decidim/elections/highlighted_elections_for_component" + hash << elections.cache_key_with_version + hash << I18n.locale.to_s + hash.join(Decidim.cache_key_separator) + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/remaining_time_callout/show.erb b/decidim-elections/app/cells/decidim/elections/remaining_time_callout/show.erb new file mode 100644 index 00000000..c99d3cb7 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/remaining_time_callout/show.erb @@ -0,0 +1,3 @@ +<% if needs_to_show_remaining_time? %> + <%= cell("decidim/announcement", remaining_time) %> +<% end %> diff --git a/decidim-elections/app/cells/decidim/elections/remaining_time_callout_cell.rb b/decidim-elections/app/cells/decidim/elections/remaining_time_callout_cell.rb new file mode 100644 index 00000000..93525f66 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/remaining_time_callout_cell.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Elections + class RemainingTimeCalloutCell < Decidim::ViewModel + helper_method :needs_to_show_remaining_time?, :remaining_time + + private + + def needs_to_show_remaining_time? + model.ongoing? && model.end_time < 12.hours.from_now + end + + def remaining_time + minutes_to_end = ((model.end_time - Time.current) / 60).floor + t("remaining_time", count: remaining_hours(minutes_to_end), minutes: remaining_minutes(minutes_to_end), scope: "decidim.elections.election_m.footer") + end + + def remaining_hours(minutes_to_end) + minutes_to_end / 60 + end + + def remaining_minutes(minutes_to_end) + minutes_to_end % 60 + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/elections/voting_step_navigation/show.erb b/decidim-elections/app/cells/decidim/elections/voting_step_navigation/show.erb new file mode 100644 index 00000000..18e7ea1c --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/voting_step_navigation/show.erb @@ -0,0 +1,25 @@ +
+ <% unless first_step? %> + <%= button_tag( + t("decidim.elections.votes.voting_step.back"), + type: "button", + id: "back-#{current_step_index}", + class: "button button__sm md:button__lg button__transparent-secondary", + data: { + toggle: [previous_step_dom_id, current_step_dom_id].join(" ") + } + ) %> + <% end %> + + <%= button_tag( + class: "button button__sm md:button__lg button__secondary", + type: "button", + id: "next-#{current_step_index}", + data: { + toggle: [next_step_dom_id, current_step_dom_id].join(" ") + } + ) do %> + <%= button_continue_text %> + <%= icon "arrow-right-line" %> + <% end %> +
diff --git a/decidim-elections/app/cells/decidim/elections/voting_step_navigation_cell.rb b/decidim-elections/app/cells/decidim/elections/voting_step_navigation_cell.rb new file mode 100644 index 00000000..370f3aa6 --- /dev/null +++ b/decidim-elections/app/cells/decidim/elections/voting_step_navigation_cell.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This cell renders the navigation of a questionnaire step. + class VotingStepNavigationCell < Decidim::ViewModel + include Decidim::LayoutHelper + + def current_step_index + model + end + + def first_step? + current_step_index.zero? + end + + def last_step? + current_step_index + 1 == total_steps + end + + def total_steps + options[:total_steps] + end + + def button_continue_text + t("decidim.elections.votes.voting_step.continue") + end + + def previous_step_dom_id + "step-#{current_step_index - 1}" + end + + def next_step_dom_id + last_step? ? "step-confirm" : "step-#{current_step_index + 1}" + end + + def current_step_dom_id + "step-#{current_step_index}" + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_block_cell.rb b/decidim-elections/app/cells/decidim/votings/content_block_cell.rb new file mode 100644 index 00000000..78476a21 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_block_cell.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class ContentBlockCell < Decidim::Admin::ContentBlockCell + delegate :scoped_resource, to: :controller + + def edit_content_block_path + decidim_votings.edit_voting_landing_page_content_block_path(scoped_resource, model) + end + + def content_block_path + decidim_votings.voting_landing_page_content_block_path(scoped_resource, model) + end + + def decidim_votings + Decidim::Votings::AdminEngine.routes.url_helpers + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/hero_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/hero_cell.rb new file mode 100644 index 00000000..0261d7cb --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/hero_cell.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class HeroCell < Decidim::ContentBlocks::ParticipatorySpaceHeroCell + delegate :start_time, :end_time, to: :resource + + def has_hashtag? + false + end + + def subtitle_text + "#{start_text} — #{end_text}" + end + + private + + def start_text + content_tag :span, title: t("activemodel.attributes.voting.start_time") do + format_date(start_time) + end + end + + def end_text + content_tag :span, title: t("activemodel.attributes.voting.end_time") do + format_date(end_time) + end + end + + def format_date(time) + if time + l(time.to_date, format: :decidim_short) + else + t("decidim.votings.votings_m.unspecified") + end + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form/show.erb b/decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form/show.erb new file mode 100644 index 00000000..d9e6e259 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form/show.erb @@ -0,0 +1,4 @@ +<% form.fields_for :settings, form.object.settings do |settings_fields| %> + <%= settings_fields.translated :text_field, :button_text, label: t("decidim.content_blocks.cta_settings_form.button_text") %> + <%= settings_fields.translated :text_field, :button_url, label: t("decidim.content_blocks.cta_settings_form.button_url") %> +<% end %> diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form_cell.rb new file mode 100644 index 00000000..8ec58226 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/hero_settings_form_cell.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class HeroSettingsFormCell < Decidim::ViewModel + alias form model + + def content_block + options[:content_block] + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_cell.rb new file mode 100644 index 00000000..ce7dafcc --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_cell.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class HighlightedVotingsCell < Decidim::ContentBlocks::HighlightedParticipatorySpacesCell + BLOCK_ID = "highlighted-votings" + + delegate :current_user, to: :controller + + def highlighted_spaces + @highlighted_spaces ||= OrganizationPrioritizedVotings.new(current_organization, current_user).query + end + + def i18n_scope + "decidim.votings.pages.home.highlighted_votings" + end + + def all_path + Decidim::Votings::Engine.routes.url_helpers.votings_path + end + + def max_results + model.settings.max_results + end + + private + + def block_id = BLOCK_ID + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form/show.erb b/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form/show.erb new file mode 100644 index 00000000..df3fc2eb --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form/show.erb @@ -0,0 +1,3 @@ +<% form.fields_for :settings, form.object.settings do |settings_fields| %> + <%= settings_fields.select :max_results, [3, 6, 9], prompt: "", label: %> +<% end %> diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell.rb new file mode 100644 index 00000000..6e16387c --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class HighlightedVotingsSettingsFormCell < Decidim::ViewModel + alias form model + + def content_block + options[:content_block] + end + + def label + I18n.t("decidim.votings.admin.content_blocks.highlighted_votings.max_results") + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/main_data_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/main_data_cell.rb new file mode 100644 index 00000000..56c30788 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/main_data_cell.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class MainDataCell < Decidim::ContentBlocks::ParticipatorySpaceMainDataCell + include VotingsHelper + include Decidim::ComponentPathHelper + include Decidim::SanitizeHelper + include ActiveLinkTo + + delegate :description, to: :resource + + private + + def title_text + t("title", scope: "decidim.votings.votings.show") + end + + def description_text + decidim_sanitize_editor_admin translated_attribute(description) + end + + def nav_items + voting_nav_items(resource) + end + + def decidim_votings + Decidim::Votings::Engine.routes.url_helpers + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/metrics_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/metrics_cell.rb new file mode 100644 index 00000000..160dd332 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/metrics_cell.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + # VotingsMetricChartsPresenter is not implemented yet. This cell will not + # display anything if metrics method is blank + class MetricsCell < Decidim::ContentBlocks::ParticipatorySpaceMetricsCell + def metrics + nil + + # @metrics ||= VotingsMetricChartsPresenter.new(participatory_process: resource) + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations/show.erb b/decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations/show.erb new file mode 100644 index 00000000..6ca67db6 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations/show.erb @@ -0,0 +1,21 @@ +
+

+ <%= t("heading", scope: "decidim.votings.content_blocks.landing_page.polling_stations") %> +

+ + <% if polling_stations.any? %> +
+
+ <% polling_stations.each do |polling_station| %> +
+ <%== cell("decidim/address", polling_station) %> +
+ <% end %> +
+ + <%= cell "decidim/votings/voting_map", polling_stations %> +
+ <% else %> + <%= t("no_polling_stations", scope: "decidim.votings.content_blocks.landing_page.polling_stations") %> + <% end %> +
diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations_cell.rb new file mode 100644 index 00000000..77032838 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/polling_stations_cell.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class PollingStationsCell < Decidim::ViewModel + include Decidim::MapHelper + include Decidim::SanitizeHelper + include Decidim::LayoutHelper + include Decidim::IconHelper + include Decidim::NeedsSnippets + + delegate :current_participatory_space, + :snippets, + to: :controller + + def show + return if current_participatory_space.online_voting? + + render + end + + private + + def polling_stations + @polling_stations ||= current_participatory_space.polling_stations + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/content_blocks/statistics_cell.rb b/decidim-elections/app/cells/decidim/votings/content_blocks/statistics_cell.rb new file mode 100644 index 00000000..57aa2293 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/content_blocks/statistics_cell.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class StatisticsCell < Decidim::ContentBlocks::ParticipatorySpaceStatsCell + private + + def stats + @stats ||= begin + voting = Decidim::Votings::Voting.find_by(id: model.scoped_resource_id) + Decidim::Votings::VotingStatsPresenter.new(voting:).collection + end + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker/show.erb b/decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker/show.erb new file mode 100644 index 00000000..d38b1cc4 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker/show.erb @@ -0,0 +1,15 @@ + diff --git a/decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker_cell.rb b/decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker_cell.rb new file mode 100644 index 00000000..d66eec68 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/polling_officers/polling_officers_picker_cell.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require "cell/partial" + +module Decidim + module Votings + module PollingOfficers + # This cell renders a polling officers picker. + class PollingOfficersPickerCell < Decidim::ViewModel + MAX_POLLING_OFFICERS = 1000 + + alias component model + + def form + options[:form] + end + + def field + options[:field] + end + + def form_name + "#{form.object_name}[#{method_name}]" + end + + def method_name + field.to_s.sub(/s$/, "_ids") + end + + def selected_ids + form.object.send(method_name) + end + + def filtered? + !search_text.nil? + end + + def picker_path + request.path + end + + def search_text + params[:q] + end + + def more_polling_officers? + @more_polling_officers ||= more_polling_officers_count.positive? + end + + def more_polling_officers_count + @more_polling_officers_count ||= polling_officers_count - MAX_POLLING_OFFICERS + end + + def polling_officers_count + @polling_officers_count ||= filtered_polling_officers.count + end + + def decorated_polling_officers + filtered_polling_officers.limit(MAX_POLLING_OFFICERS) + end + + def filtered_polling_officers + @filtered_polling_officers ||= polling_officers + end + + def polling_officers + @polling_officers ||= model.available_polling_officers + end + + def polling_officers_collection_name + Decidim::Votings::PollingOfficer.model_name.human(count: 2) + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate/show.erb b/decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate/show.erb new file mode 100644 index 00000000..a976cbde --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate/show.erb @@ -0,0 +1,10 @@ +<% if has_images? %> +

+ <%= content_tag :strong, t("current_certificate", scope: "decidim.votings.polling_station_closure_certificate") %> +

+ <% model.photos.each do |image| %> + + <%= translated_attribute(image.title) %> + + <% end %> +<% end %> diff --git a/decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate_cell.rb b/decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate_cell.rb new file mode 100644 index 00000000..9a58d104 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/polling_station_closure_certificate_cell.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This cell renders the image of the physical certificate of a Polling Station Closure + class PollingStationClosureCertificateCell < Decidim::ViewModel + def has_images? + model.photos.present? + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/polling_station_closure_recount/show.erb b/decidim-elections/app/cells/decidim/votings/polling_station_closure_recount/show.erb new file mode 100644 index 00000000..8be0bd8d --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/polling_station_closure_recount/show.erb @@ -0,0 +1,72 @@ +
+

+ <%= t("recount_summary", scope: "decidim.votings.polling_station_closure_recount") %> +

+ + + + <%= t("total_ballots", scope: "decidim.votings.polling_station_closure_recount") %> + <%= model.results.total_ballots&.first&.value %> + +
+ + <%= t("total_valid_ballots", scope: "decidim.votings.polling_station_closure_recount") %> + <%= model.results.valid_ballots&.first&.value %> + +
+ + <%= t("total_blank_ballots", scope: "decidim.votings.polling_station_closure_recount") %> + <%= model.results.blank_ballots&.first&.value %> + +
+ + <%= t("total_null_ballots", scope: "decidim.votings.polling_station_closure_recount") %> + <%= model.results.null_ballots&.first&.value %> + +
+ + <% model.election.questions.each do |question| %> + + <%= translated_attribute(question.title) %> + + +
+ <% question.answers.each.with_index(1) do |answer, index| %> + + <%= translated_attribute(answer.title) %>: + + <%= content_tag :span, answer.results&.valid_answers&.first&.value %> + + <% unless index == question.answers.count %> +
+ <% end %> + <% end %> + + <% if question.nota_option? %> +
+ <%= t("nota_option", scope: "decidim.votings.polling_station_closure_recount") %> + + <%= content_tag :span, question.results.blank_answers&.first&.value %> + <% end %> +
+ <% end %> +
+ +

+ <%= t("polling_officer_notes", scope: "decidim.votings.polling_station_closure_recount") %> +

+ + <% if model.polling_officer_notes.present? %> + <%= model.polling_officer_notes %> + <% else %> + <%= content_tag :span, t("polling_officer_notes_blank", scope: "decidim.votings.polling_station_closure_recount") %> + <% end %> + + + <% if model.signed? %> +

+ <%= icon "check-double-fill", class: "w-8 h-8 inline-block fill-secondary", role: "img", "aria-hidden": true %> + <%= t("signed", scope: "decidim.votings.polling_station_closure_recount") %> +

+ <% end %> +
diff --git a/decidim-elections/app/cells/decidim/votings/polling_station_closure_recount_cell.rb b/decidim-elections/app/cells/decidim/votings/polling_station_closure_recount_cell.rb new file mode 100644 index 00000000..edcf7232 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/polling_station_closure_recount_cell.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This cell renders the vote recount of a Polling Station Closure + class PollingStationClosureRecountCell < Decidim::ViewModel + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/voting_cell.rb b/decidim-elections/app/cells/decidim/votings/voting_cell.rb new file mode 100644 index 00000000..3b07a604 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_cell.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This cell renders the card for an instance of a Voting + # the default size is the Search Card (:s) + class VotingCell < Decidim::ViewModel + def show + cell card_size, model, options + end + + private + + def card_size + case @options[:size] + when :s + "decidim/votings/voting_s" + else + "decidim/votings/voting_g" + end + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/voting_dropdown_metadata_cell.rb b/decidim-elections/app/cells/decidim/votings/voting_dropdown_metadata_cell.rb new file mode 100644 index 00000000..84327e66 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_dropdown_metadata_cell.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class VotingDropdownMetadataCell < Decidim::ParticipatorySpaceDropdownMetadataCell + include VotingsHelper + include Decidim::ComponentPathHelper + include ActiveLinkTo + + def decidim_votings + Decidim::Votings::Engine.routes.url_helpers + end + + private + + def nav_items_method = :voting_nav_items + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/voting_g_cell.rb b/decidim-elections/app/cells/decidim/votings/voting_g_cell.rb new file mode 100644 index 00000000..546cbc8a --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_g_cell.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This cell renders the Grid (:g) voting card + # for a given instance of a Voting + class VotingGCell < Decidim::CardGCell + private + + def resource_image_path + model.attached_uploader(:banner_image).path + end + + def metadata_cell + "decidim/votings/voting_metadata" + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/voting_map/show.erb b/decidim-elections/app/cells/decidim/votings/voting_map/show.erb new file mode 100644 index 00000000..ef1e48bf --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_map/show.erb @@ -0,0 +1,16 @@ +<%= dynamic_map_for polling_station_data_for_map(polling_stations) do %> + +<% end.html_safe %> diff --git a/decidim-elections/app/cells/decidim/votings/voting_map_cell.rb b/decidim-elections/app/cells/decidim/votings/voting_map_cell.rb new file mode 100644 index 00000000..60b4b016 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_map_cell.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class VotingMapCell < VotingGCell + include Decidim::MapHelper + include Decidim::Votings::MapHelper + + delegate :snippets, to: :controller + + def show + return unless Decidim::Map.available?(:geocoding, :dynamic) + + render + end + + def polling_stations + model + end + + private + + def cache_hash + nil + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/voting_metadata_cell.rb b/decidim-elections/app/cells/decidim/votings/voting_metadata_cell.rb new file mode 100644 index 00000000..b23ff469 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_metadata_cell.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This cell renders metadata for an instance of a Voting + class VotingMetadataCell < Decidim::CardMetadataCell + delegate :voting_type, :period_status, :start_time, :end_time, to: :model + + def initialize(*) + super + + @items.prepend(*voting_items) + end + + private + + def voting_items + [dates_metadata_item, type_item, status_item] + end + + def type_item + { + icon: resource_type_icon_key(voting_type), + text: t(voting_type, scope: "decidim.votings.votings_m.voting_type") + } + end + + def status_item + return if period_status.blank? + + { text: content_tag(:span, t(period_status, scope: "decidim.votings.votings_m.badge_name"), class: "label #{state_class}") } + end + + def state_class + case period_status + when :ongoing + "success" + when :upcoming + "warning" + end + end + + def start_date + return unless start_time + + start_time.to_date + end + + def end_date + return unless end_time + + end_time.to_date + end + + def dates_metadata_item + { + icon: "calendar-todo-line", + text: [ + start_date.present? ? l(start_date, format: :decidim_short_with_month_name_short) : "?", + end_date.present? ? l(end_date, format: :decidim_short_with_month_name_short) : "?" + ].join(" → ") + } + end + end + end +end diff --git a/decidim-elections/app/cells/decidim/votings/voting_s_cell.rb b/decidim-elections/app/cells/decidim/votings/voting_s_cell.rb new file mode 100644 index 00000000..98bad829 --- /dev/null +++ b/decidim-elections/app/cells/decidim/votings/voting_s_cell.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This cell renders the Search (:s) voting card + # for a given instance of a Voting + class VotingSCell < Decidim::CardSCell + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/add_user_as_trustee.rb b/decidim-elections/app/commands/decidim/elections/admin/add_user_as_trustee.rb new file mode 100644 index 00000000..4d27f8e3 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/add_user_as_trustee.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the admin user creates a trustee + # from the admin panel. + class AddUserAsTrustee < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + def initialize(form, current_user) + @form = form + @user = form.user + @participatory_space = form.current_participatory_space + @current_user = current_user + end + + # Creates the trustee if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + return broadcast(:exists) if existing_trustee_participatory_spaces? + + transaction do + add_user_as_trustee! if new_trustee? + add_participatory_space + notify_user_about_trustee_role if new_trustee? + end + + send_email + + broadcast(:ok) + end + + private + + attr_reader :form, :current_user, :trustee, :participatory_space, :user + + def add_user_as_trustee! + @trustee = Decidim.traceability.create!( + Trustee, + current_user, + user:, + organization: user.organization + ) + end + + # If a trustee exists for this participatory space, it will not get created again + def existing_trustee_participatory_spaces? + trustees_space = TrusteesParticipatorySpace.where(participatory_space:).includes(:trustee) + @existing_trustee_participatory_spaces ||= Decidim::Elections::Trustee.joins(:trustees_participatory_spaces) + .includes([:user]) + .where(trustees_participatory_spaces: trustees_space) + .where(decidim_user_id: user.id).any? + end + + # if there is no user - trustee relation, the trustee gets created and the notification + # gets send. + def new_trustee? + return @new_trustee if defined?(@new_trustee) + + @new_trustee = Decidim::Elections::Trustee.where(decidim_user_id: user.id).empty? + end + + def add_participatory_space + trustee = Decidim::Elections::Trustee.find_by(decidim_user_id: user.id) + trustee.trustees_participatory_spaces.create!( + participatory_space: + ) + end + + def notify_user_about_trustee_role + data = { + event: "decidim.events.elections.trustees.new_trustee", + event_class: Decidim::Elections::Trustees::NotifyNewTrusteeEvent, + resource: participatory_space, + affected_users: [user] + } + Decidim::EventsManager.publish(**data) + end + + def send_email + Decidim::Elections::TrusteeMailer.notification( + user, participatory_space, I18n.locale.to_s + ).deliver_later + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/create_answer.rb b/decidim-elections/app/commands/decidim/elections/admin/create_answer.rb new file mode 100644 index 00000000..ce7bd519 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/create_answer.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user creates an Answer + # from the admin panel. + class CreateAnswer < Decidim::Command + include ::Decidim::GalleryMethods + + def initialize(form) + @form = form + end + + # Creates the answer if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + if process_gallery? + build_gallery + return broadcast(:invalid) if gallery_invalid? + end + + transaction do + create_answer + link_proposals + create_gallery if process_gallery? + end + + broadcast(:ok, answer) + end + + private + + attr_reader :form, :answer, :gallery + + def invalid? + form.election.blocked? || form.invalid? + end + + def create_answer + attributes = { + question: form.question, + title: form.title, + description: form.description, + weight: form.weight + } + + @answer = Decidim.traceability.create!( + Answer, + form.current_user, + attributes, + visibility: "all" + ) + @attached_to = @answer + end + + def proposals + @proposals ||= answer.sibling_scope(:proposals).where(id: form.proposal_ids) + end + + def link_proposals + answer.link_resources(proposals, "related_proposals") + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/create_election.rb b/decidim-elections/app/commands/decidim/elections/admin/create_election.rb new file mode 100644 index 00000000..74a4f9c6 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/create_election.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user creates an Election + # from the admin panel. + class CreateElection < Decidim::Command + include ::Decidim::GalleryMethods + + def initialize(form) + @form = form + end + + # Creates the election if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + if process_gallery? + build_gallery + return broadcast(:invalid) if gallery_invalid? + end + + transaction do + create_election! + create_gallery if process_gallery? + end + + broadcast(:ok, election) + end + + private + + attr_reader :form, :election, :gallery + + def create_election! + attributes = { + title: form.title, + description: form.description, + start_time: form.start_time, + end_time: form.end_time, + component: form.current_component, + questionnaire: Decidim::Forms::Questionnaire.new, + salt: Tokenizer.random_salt + } + + @election = Decidim.traceability.create!( + Election, + form.current_user, + attributes, + visibility: "all" + ) + @attached_to = @election + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/create_question.rb b/decidim-elections/app/commands/decidim/elections/admin/create_question.rb new file mode 100644 index 00000000..c7f6ee64 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/create_question.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user creates a Question + # from the admin panel. + class CreateQuestion < Decidim::Command + def initialize(form) + @form = form + end + + # Creates the question if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:election_started) if form.election.blocked? + return broadcast(:invalid) if form.invalid? + + create_question! + + broadcast(:ok, question) + end + + private + + attr_reader :form, :question + + def create_question! + attributes = { + election: form.election, + title: form.title, + max_selections: form.max_selections, + weight: form.weight, + random_answers_order: form.random_answers_order, + min_selections: form.min_selections + } + + @question = Decidim.traceability.create!( + Question, + form.current_user, + attributes, + visibility: "all" + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/destroy_answer.rb b/decidim-elections/app/commands/decidim/elections/admin/destroy_answer.rb new file mode 100644 index 00000000..c04472c9 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/destroy_answer.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user destroys an Answer + # from the admin panel. + class DestroyAnswer < Decidim::Command + include ::Decidim::GalleryMethods + + def initialize(answer, current_user) + @answer = answer + @current_user = current_user + @attached_to = answer + end + + # Destroys the answer if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + destroy_answer + + broadcast(:ok, answer) + end + + private + + attr_reader :answer, :current_user + + def invalid? + answer.question.election.blocked? + end + + def destroy_answer + Decidim.traceability.perform_action!( + :delete, + answer, + current_user, + visibility: "all" + ) do + answer.destroy! + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/destroy_election.rb b/decidim-elections/app/commands/decidim/elections/admin/destroy_election.rb new file mode 100644 index 00000000..22697869 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/destroy_election.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user destroys an Election + # from the admin panel. + class DestroyElection < Decidim::Command + include ::Decidim::GalleryMethods + + def initialize(election, current_user) + @election = election + @current_user = current_user + @attached_to = election + end + + # Destroys the election if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + destroy_election! + + broadcast(:ok, election) + end + + private + + attr_reader :election, :current_user + + def invalid? + election.blocked? + end + + def destroy_election! + Decidim.traceability.perform_action!( + :delete, + election, + current_user, + visibility: "all" + ) do + election.destroy! + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/destroy_question.rb b/decidim-elections/app/commands/decidim/elections/admin/destroy_question.rb new file mode 100644 index 00000000..d67326fc --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/destroy_question.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user destroys a Question + # from the admin panel. + class DestroyQuestion < Decidim::Command + def initialize(question, current_user) + @question = question + @current_user = current_user + end + + # Destroys the question if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + destroy_question! + + broadcast(:ok, question) + end + + private + + attr_reader :question, :current_user + + def invalid? + question.election.blocked? + end + + def destroy_question! + Decidim.traceability.perform_action!( + :delete, + question, + current_user, + visibility: "all" + ) do + question.destroy! + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/end_vote.rb b/decidim-elections/app/commands/decidim/elections/admin/end_vote.rb new file mode 100644 index 00000000..7b4563b8 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/end_vote.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called to end the voting period in the Bulletin Board. + class EndVote < Decidim::Command + # Public: Initializes the command. + # + # form - A VotePeriodForm object with the information needed to start or end the vote period + def initialize(form) + @form = form + end + + # Public: Ends the voting period for the Election. + # + # Broadcasts :ok if setup, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + end_vote + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_accessor :form + + delegate :election, :bulletin_board, to: :form + + def log_action + Decidim.traceability.perform_action!( + :end_vote, + election, + form.current_user, + visibility: "all" + ) + end + + def end_vote + bulletin_board.end_vote(election.id) do |message_id| + create_election_action(message_id) + end + end + + def create_election_action(message_id) + Decidim::Elections::Action.create!( + election:, + action: :end_vote, + message_id:, + status: :pending + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/import_proposals_to_elections.rb b/decidim-elections/app/commands/decidim/elections/admin/import_proposals_to_elections.rb new file mode 100644 index 00000000..723e581f --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/import_proposals_to_elections.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # A command is executed when an admin imports proposals from + # one component to answers of elections component. + class ImportProposalsToElections < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + def initialize(form) + @form = form + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if invalid? + + broadcast(:ok, create_answers_from_accepted_proposals) + end + + private + + attr_reader :form, :answer + + def invalid? + form.election.blocked? || form.invalid? + end + + def create_answers_from_accepted_proposals + transaction do + proposals.map do |original_proposal| + next if proposal_already_copied?(original_proposal) + + create_answer_from_proposal(original_proposal) + answer.link_resources([original_proposal], "related_proposals") + end.compact + end + end + + def create_answer_from_proposal(original_proposal) + params = { + question: form.question, + title: original_proposal.title, + description: original_proposal.body, + weight: form.weight + } + + @answer = Decidim.traceability.create!( + Answer, + form.current_user, + params, + visibility: "all" + ) + end + + def proposals + @proposals ||= if @form.import_all_accepted_proposals? + Decidim::Proposals::Proposal.where(component: origin_component).where(state: "accepted") + else + Decidim::Proposals::Proposal.where(component: origin_component) + end + end + + def origin_component + @form.origin_component + end + + def target_question + @form.question + end + + def proposal_already_copied?(original_proposal) + # Note: we are including also answers from unpublished components + # because otherwise duplicates could be created until the component is + # published. + original_proposal.linked_resources(:answers, "related_proposals", component_published: false).any? do |answer| + answer.question == target_question + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/publish_election.rb b/decidim-elections/app/commands/decidim/elections/admin/publish_election.rb new file mode 100644 index 00000000..95c7d24a --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/publish_election.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called when an election is published from the admin panel. + class PublishElection < Decidim::Command + # Public: Initializes the command. + # + # election - The election to publish. + # current_user - the user performing the action + def initialize(election, current_user) + @election = election + @current_user = current_user + end + + # Public: Publishes the Election. + # + # Broadcasts :ok if published, :invalid otherwise. + def call + publish_election + publish_event unless election.previously_published? + + broadcast(:ok, election) + end + + private + + attr_reader :election, :current_user + + def publish_election + Decidim.traceability.perform_action!( + :publish, + election, + current_user, + visibility: "all" + ) do + election.publish! + election + end + end + + def publish_event + Decidim::EventsManager.publish( + event: "decidim.events.elections.election_published", + event_class: ::Decidim::Elections::ElectionPublishedEvent, + resource: election, + followers: election.participatory_space.followers + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/publish_results.rb b/decidim-elections/app/commands/decidim/elections/admin/publish_results.rb new file mode 100644 index 00000000..79f45c0e --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/publish_results.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called to publish the election results in the Bulletin Board. + class PublishResults < Decidim::Command + # Public: Initializes the command. + # + # form - An ActionForm object with the information needed to publish the results + def initialize(form) + @form = form + end + + # Public: Publish the results for the Election. + # + # Broadcasts :ok if setup, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + publish_results + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_accessor :form + + delegate :election, :bulletin_board, to: :form + + def log_action + Decidim.traceability.perform_action!( + :publish_results, + election, + form.current_user, + visibility: "all" + ) + end + + def publish_results + bulletin_board.publish_results(election.id) do |message_id| + create_election_action(message_id) + end + end + + def create_election_action(message_id) + Decidim::Elections::Action.create!( + election:, + action: :publish_results, + message_id:, + status: :pending + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/remove_trustee_from_participatory_space.rb b/decidim-elections/app/commands/decidim/elections/admin/remove_trustee_from_participatory_space.rb new file mode 100644 index 00000000..7be488c2 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/remove_trustee_from_participatory_space.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the admin user removes a trustee + # from a participatory space from the admin panel. + class RemoveTrusteeFromParticipatorySpace < Decidim::Command + # Public: Initializes the command. + # + # trustee_participatory_space - A trustee_participatory_space + def initialize(trustee_participatory_space) + @trustee_participatory_space = trustee_participatory_space + end + + # Removes the trustee from participatory space if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if !trustee_participatory_space || trustee_participatory_space.trustee.elections.any? + + remove_trustee_from_participatory_space! + + broadcast(:ok, trustee_participatory_space.trustee) + end + + private + + attr_reader :trustee_participatory_space + + def remove_trustee_from_participatory_space! + trustee_participatory_space.trustee.delete + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/report_missing_trustee.rb b/decidim-elections/app/commands/decidim/elections/admin/report_missing_trustee.rb new file mode 100644 index 00000000..ad52c301 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/report_missing_trustee.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called to report a missing trustee during the tally process. + class ReportMissingTrustee < Decidim::Command + # Public: Initializes the command. + # + # form - A ReportMissingTrusteeForm object with the information needed to report the missing trustee. + def initialize(form) + @form = form + end + + # Public: Reports the missing trustee for the Election tally process. + # + # Broadcasts :ok if it worked, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + report_missing_trustee + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_accessor :form + + delegate :election, :bulletin_board, :trustee, to: :form + + def log_action + Decidim.traceability.perform_action!( + :report_missing_trustee, + election, + form.current_user, + extra: { + trustee_id: form.trustee_id, + name: trustee.name, + nickname: trustee.user.nickname + }, + visibility: "all" + ) + end + + def report_missing_trustee + bulletin_board.report_missing_trustee(election.id, form.trustee.slug) do |message_id| + create_election_action(message_id) + end + end + + def create_election_action(message_id) + Decidim::Elections::Action.create!( + election:, + action: :report_missing_trustee, + message_id:, + status: :pending + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/setup_election.rb b/decidim-elections/app/commands/decidim/elections/admin/setup_election.rb new file mode 100644 index 00000000..2ba56bdc --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/setup_election.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called when an election is setup from the admin panel. + class SetupElection < Decidim::Command + # Public: Initializes the command. + # + # form - A SetupForm object with the information needed to setup the election + def initialize(form) + @form = form + end + + # Public: Setup the Election. + # + # Broadcasts :ok if setup, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + update_election + notify_trustee_about_election + setup_election + end + + broadcast(:ok, election) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_reader :form + + delegate :election, :bulletin_board, :current_organization, to: :form + + def questions + @questions ||= election.questions + end + + def answers + Decidim::Elections::Answer.where(question: questions) + end + + def trustees + @trustees ||= Decidim::Elections::Trustee.where(id: form.trustee_ids).order(:id) + end + + def update_election + return if election.trustees.exists?(id: form.trustee_ids) + + election.trustees << trustees + election.blocked_at = Time.current + election.bb_created! + end + + def election_data + @election_data ||= begin + ret = base_election_data + election.participatory_space.try(:complete_election_data, election, ret) + ret + end + end + + def base_election_data + { + trustees: trustees_data, + default_locale: current_organization.default_locale, + title: flatten_translations(election.title), + start_date: election.start_time, + end_date: election.end_time, + questions: questions_data, + answers: answers_data, + ballot_styles: {} + } + end + + def trustees_data + trustees.map do |trustee| + { + name: trustee.name, + slug: trustee.bulletin_board_slug, + public_key: JSON.parse(trustee.public_key) + } + end + end + + def questions_data + questions.map do |question| + { + slug: question.slug, + weight: question.weight, + max_selections: question.max_selections, + title: flatten_translations(question.title), + # the bulletin_board gem (ruby client) expects a description for the question + # as development is in a separate repository, we send an empty content for the moment + description: {}, + answers: question_answers_data(question) + } + end + end + + def question_answers_data(question) + question.answers.map do |answer| + { + slug: answer.slug, + weight: answer.weight + } + end + end + + def answers_data + answers.map do |answer| + { + slug: answer.slug, + title: flatten_translations(answer.title) + } + end + end + + def setup_election + bb_election = bulletin_board.create_election(election.id, election_data) + + raise StandardError, "Wrong status for the created election" if bb_election.status != "created" + end + + def log_action + Decidim.traceability.perform_action!( + :setup, + election, + form.current_user, + visibility: "all" + ) + end + + def notify_trustee_about_election + trustee = trustees.collect(&:user) + data = { + event: "decidim.events.elections.trustees.new_election", + event_class: Decidim::Elections::Trustees::NotifyTrusteeNewElectionEvent, + resource: election, + affected_users: trustee + } + + Decidim::EventsManager.publish(**data) + end + + # Since machine_translations return a nested hash but Electionguard and other + # schemes expect the translations to be returned in a "simple" hash, we need to + # flatten the translations. + # { + # "language": "en", + # "value": "Jubilee Alliance" + # } + # You can read more about the Civics Common Standard Data Specification here: + # https://developers.google.com/civics-data/reference/internationalized-text + def flatten_translations(translated_attribute) + translated_attribute.deep_symbolize_keys! + machine_translations = translated_attribute.delete(:machine_translations) || {} + + machine_translations.merge(translated_attribute).reject { |_k, v| v.empty? } + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/start_key_ceremony.rb b/decidim-elections/app/commands/decidim/elections/admin/start_key_ceremony.rb new file mode 100644 index 00000000..2c191ba0 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/start_key_ceremony.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called to start the key ceremony in the Bulletin Board. + class StartKeyCeremony < Decidim::Command + # Public: Initializes the command. + # + # form - An ActionForm object with the information needed to perform an action + def initialize(form) + @form = form + end + + # Public: Starts the key ceremony for the Election. + # + # Broadcasts :ok if setup, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + start_key_ceremony + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_accessor :form + + delegate :election, :bulletin_board, to: :form + + def log_action + Decidim.traceability.perform_action!( + :start_key_ceremony, + election, + form.current_user, + visibility: "all" + ) + end + + def start_key_ceremony + bulletin_board.start_key_ceremony(election.id) do |message_id| + create_election_action(message_id) + end + end + + def create_election_action(message_id) + Decidim::Elections::Action.create!( + election:, + action: :start_key_ceremony, + message_id:, + status: :pending + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/start_tally.rb b/decidim-elections/app/commands/decidim/elections/admin/start_tally.rb new file mode 100644 index 00000000..c0bb5472 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/start_tally.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called to start the tally in the Bulletin Board. + class StartTally < Decidim::Command + # Public: Initializes the command. + # + # form - An ActionForm object with the information needed to perform an action + def initialize(form) + @form = form + end + + # Public: Starts the tally for the Election. + # + # Broadcasts :ok if setup, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + start_tally + notify_trustee_about_tally + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_accessor :form + + delegate :election, :bulletin_board, to: :form + + def log_action + Decidim.traceability.perform_action!( + :start_tally, + election, + form.current_user, + visibility: "all" + ) + end + + def start_tally + bulletin_board.start_tally(election.id) do |message_id| + create_election_action(message_id) + end + end + + def create_election_action(message_id) + Decidim::Elections::Action.create!( + election:, + action: :start_tally, + message_id:, + status: :pending + ) + end + + def notify_trustee_about_tally + trustees = election.trustees.collect(&:user) + data = { + event: "decidim.events.elections.trustees.start_tally", + event_class: Decidim::Elections::Trustees::NotifyTrusteeTallyProcessEvent, + resource: election, + affected_users: trustees + } + + Decidim::EventsManager.publish(**data) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/start_vote.rb b/decidim-elections/app/commands/decidim/elections/admin/start_vote.rb new file mode 100644 index 00000000..436d510a --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/start_vote.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called to start the voting period in the Bulletin Board. + class StartVote < Decidim::Command + # Public: Initializes the command. + # + # form - A VotePeriodForm object with the information needed to start or end the vote period + def initialize(form) + @form = form + end + + # Public: Starts the voting period for the Election. + # + # Broadcasts :ok if setup, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + log_action + start_vote + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_accessor :form + + delegate :election, :bulletin_board, to: :form + + def log_action + Decidim.traceability.perform_action!( + :start_vote, + election, + form.current_user, + visibility: "all" + ) + end + + def start_vote + bulletin_board.start_vote(election.id) do |message_id| + create_election_action(message_id) + end + end + + def create_election_action(message_id) + Decidim::Elections::Action.create!( + election:, + action: :start_vote, + message_id:, + status: :pending + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/unpublish_election.rb b/decidim-elections/app/commands/decidim/elections/admin/unpublish_election.rb new file mode 100644 index 00000000..6043a7de --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/unpublish_election.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command gets called when an election is unpublished from the admin panel. + class UnpublishElection < Decidim::Command + # Public: Initializes the command. + # + # election - The election to unpublish. + # current_user - the user performing the action + def initialize(election, current_user) + @election = election + @current_user = current_user + end + + # Public: Unpublishes the Election. + # + # Broadcasts :ok if unpublished, :invalid otherwise. + def call + unpublish_election + + broadcast(:ok) + end + + private + + attr_reader :election, :current_user + + def unpublish_election + Decidim.traceability.perform_action!( + :unpublish, + election, + current_user + ) do + election.unpublish! + election + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/update_action_status.rb b/decidim-elections/app/commands/decidim/elections/admin/update_action_status.rb new file mode 100644 index 00000000..f90afd15 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/update_action_status.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command updates the status of the action and the election if it got changed + class UpdateActionStatus < Decidim::Command + # Public: Initializes the command. + # + # action - The pending action to be updated + def initialize(action) + @action = action + end + + # Update the statuses of the action and the election if pending message status got changed. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:ok) unless action.pending? + + transaction do + update_pending_message_status + update_election_status if action.accepted? + end + + broadcast(:ok) + end + + private + + attr_reader :action + + delegate :election, to: :action + + def update_pending_message_status + action.status = Decidim::Elections.bulletin_board.get_pending_message_status(action.message_id) + action.save! + end + + def update_election_status + election.bb_status = Decidim::Elections.bulletin_board.get_election_status(election.id) + election.save! + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/update_answer.rb b/decidim-elections/app/commands/decidim/elections/admin/update_answer.rb new file mode 100644 index 00000000..c08442eb --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/update_answer.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user updates an Answer + # from the admin panel. + class UpdateAnswer < Decidim::Command + include ::Decidim::GalleryMethods + + def initialize(form, answer) + @form = form + @answer = answer + @attached_to = answer + end + + # Updates the answer if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + if process_gallery? + build_gallery + return broadcast(:invalid) if gallery_invalid? + end + + transaction do + update_answer + link_proposals + create_gallery if process_gallery? + photo_cleanup! + end + + broadcast(:ok, answer) + end + + private + + attr_reader :form, :answer, :gallery + + def invalid? + form.election.blocked? || form.invalid? + end + + def update_answer + attributes = { + question: form.question, + title: form.title, + description: form.description, + weight: form.weight + } + + Decidim.traceability.update!( + answer, + form.current_user, + attributes, + visibility: "all" + ) + end + + def proposals + @proposals ||= answer.sibling_scope(:proposals).where(id: form.proposal_ids) + end + + def link_proposals + answer.link_resources(proposals, "related_proposals") + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/update_answer_selection.rb b/decidim-elections/app/commands/decidim/elections/admin/update_answer_selection.rb new file mode 100644 index 00000000..9621a36e --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/update_answer_selection.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when an admin marks an answer + # as selected. + class UpdateAnswerSelection < Decidim::Command + def initialize(answer, selected) + @answer = answer + @selected = selected + end + + # Selects the answer if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + update_answer_selection! + + broadcast(:ok, answer) + end + + private + + attr_reader :answer, :selected + + def invalid? + !answer.question.results_total.positive? + end + + def update_answer_selection! + answer.update!( + selected: + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/update_election.rb b/decidim-elections/app/commands/decidim/elections/admin/update_election.rb new file mode 100644 index 00000000..898f76da --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/update_election.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user updates an Election + # from the admin panel. + class UpdateElection < Decidim::Command + include ::Decidim::GalleryMethods + + def initialize(form, election) + @form = form + @election = election + @attached_to = election + end + + # Updates the election if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + if process_gallery? + build_gallery + return broadcast(:invalid) if gallery_invalid? + end + + transaction do + update_election! + create_gallery if process_gallery? + photo_cleanup! + end + + broadcast(:ok, election) + end + + private + + attr_reader :form, :election, :gallery + + def update_election! + attributes = { + title: form.title, + description: form.description, + start_time: form.start_time, + end_time: form.end_time + } + + Decidim.traceability.update!( + election, + form.current_user, + attributes, + visibility: "all" + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/update_question.rb b/decidim-elections/app/commands/decidim/elections/admin/update_question.rb new file mode 100644 index 00000000..fd753eae --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/update_question.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user updates a Question + # from the admin panel. + class UpdateQuestion < Decidim::Command + def initialize(form, question) + @form = form + @question = question + end + + # Updates the question if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if invalid? + + update_question! + + broadcast(:ok, question) + end + + private + + attr_reader :form, :question + + def invalid? + question.election.blocked? || form.invalid? + end + + def update_question! + attributes = { + title: form.title, + max_selections: form.max_selections, + weight: form.weight, + random_answers_order: form.random_answers_order, + min_selections: form.min_selections + } + + Decidim.traceability.update!( + question, + form.current_user, + attributes, + visibility: "all" + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/admin/update_trustee_participatory_space.rb b/decidim-elections/app/commands/decidim/elections/admin/update_trustee_participatory_space.rb new file mode 100644 index 00000000..1aa431be --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/admin/update_trustee_participatory_space.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This command is executed when the user updates a trustee + # status from the admin panel. + class UpdateTrusteeParticipatorySpace < Decidim::Command + # Public: Initializes the command. + # + # trustee_participatory_space - A trustee_participatory_space + def initialize(trustee_participatory_space) + @trustee_participatory_space = trustee_participatory_space + end + + # Toggle the considered attr if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) unless trustee_participatory_space + + update_trustee_participatory_space! + + broadcast(:ok, trustee_participatory_space.trustee) + end + + private + + attr_reader :trustee_participatory_space + + # Toggle the considered attribute + def update_trustee_participatory_space! + trustee_participatory_space.update!( + considered: !trustee_participatory_space.considered + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status.rb b/decidim-elections/app/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status.rb new file mode 100644 index 00000000..dfc4d80f --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + # This command updates the election status if it got changed + class UpdateElectionBulletinBoardStatus < Decidim::Command + # Public: Initializes the command. + # + # status - The actual election status + def initialize(election, required_status) + @election = election + @required_status = required_status + end + + # Update the election if status got changed. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:ok, election) if election.bb_status.to_sym != required_status.to_sym + + transaction do + election.create_bb_closure! + update_election_status! + + if election.bb_tally_ended? + fetch_election_results + store_verifiable_results + end + end + + broadcast(:ok, election) + end + + private + + attr_reader :election, :required_status + + def results + @results ||= Decidim::Elections.bulletin_board.get_election_results(election.id) + end + + def election_results + results[:election_results] + end + + def verifiable_results + results[:verifiable_results] + end + + def fetch_election_results + answers = [] + election_results.values.map do |values| + values.each do |key, value| + result_key = get_answer_id_from_result(key) + answers = Decidim::Elections::Answer.where(id: result_key) + answers.each do |answer| + create_answer_result_for!(answer, value) + end + end + end + end + + def create_answer_result_for!(answer, value) + params = { + value:, + question: answer.question, + answer:, + result_type: :valid_answers + } + + election.bb_closure.results.create!(params) + end + + def get_answer_id_from_result(result_key) + result_key.match(/question-\d+_answer-(\d+)/).captures + end + + def store_verifiable_results + election.update!( + verifiable_results_file_url:, + verifiable_results_file_hash: verifiable_results[:hash] + ) + end + + def verifiable_results_file_url + URI.join(Decidim::Elections.bulletin_board.bulletin_board_server, verifiable_results[:url]) + end + + def update_election_status! + status = Decidim::Elections.bulletin_board.get_election_status(election.id) + election.bb_status = status + election.save! + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/trustee_zone/update_trustee.rb b/decidim-elections/app/commands/decidim/elections/trustee_zone/update_trustee.rb new file mode 100644 index 00000000..81a479eb --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/trustee_zone/update_trustee.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + # This command allows the user to update their trustee information + class UpdateTrustee < Decidim::Command + # Public: Initializes the command. + # + # form - A form with the new trustee information + def initialize(form) + @form = form + end + + # Update the trustee if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + update_trustee! + + broadcast(:ok, trustee) + rescue ActiveRecord::RecordNotUnique + form.errors.add(:name, :taken) + broadcast(:invalid) + end + + private + + attr_reader :form + + delegate :trustee, to: :form + + def update_trustee! + trustee.update!( + name: form.name, + public_key: form.public_key + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/voter/cast_vote.rb b/decidim-elections/app/commands/decidim/elections/voter/cast_vote.rb new file mode 100644 index 00000000..f6ffc820 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/voter/cast_vote.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Voter + # This command allows the user to store and cast their vote. + class CastVote < Decidim::Command + # Public: Initializes the command. + # + # form - A form with necessary info to cast a vote. + def initialize(form) + @form = form + end + + # Store and cast the vote + # + # Broadcasts :ok if successful, :invalid otherwise + def call + return broadcast(:invalid) if form.invalid? + + transaction do + cast_vote + end + + broadcast(:ok, vote) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_reader :form, :vote + + delegate :bulletin_board, to: :form + + def cast_vote + bulletin_board.cast_vote(form.election_id, form.voter_id, form.encrypted_data) do |message_id| + create_vote(message_id) + end + end + + def create_vote(message_id) + @vote = Vote.create!( + message_id:, + election: form.election, + voter_id: form.voter_id, + encrypted_vote_hash: form.encrypted_data_hash, + status: :pending, + user: form.user, + email: form.email + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/elections/voter/update_vote_status.rb b/decidim-elections/app/commands/decidim/elections/voter/update_vote_status.rb new file mode 100644 index 00000000..e1be55d1 --- /dev/null +++ b/decidim-elections/app/commands/decidim/elections/voter/update_vote_status.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Voter + # This command updates the vote status and sends a notification. + class UpdateVoteStatus < Decidim::Command + # Public: Initializes the command. + # + # vote - the vote that has been updated + def initialize(vote) + @vote = vote + end + + # Update status and send notification + # + # Broadcasts :ok if successful, :invalid otherwise + def call + return broadcast(:ok) unless status_changed? + + transaction do + update_vote_status + notify_voter + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_reader :vote, :locale + + def verify_url + @verify_url ||= Decidim::EngineRouter.main_proxy(vote.election.component).election_vote_verify_url(vote.election, vote_id: vote.encrypted_vote_hash) + end + + def status_changed? + vote.status != vote_status + end + + def bulletin_board + @bulletin_board ||= Decidim::Elections.bulletin_board + end + + def vote_status + @vote_status ||= bulletin_board.get_pending_message_status(vote.message_id) + end + + def update_vote_status + vote.update!(status: vote_status) + end + + def notify_voter + return unless vote.accepted? + + if vote.user + send_vote_notification + elsif vote.email + send_vote_email + end + end + + def send_vote_notification + data = { + event: "decidim.events.elections.votes.accepted_votes", + event_class: Decidim::Elections::Votes::VoteAcceptedEvent, + resource: vote.election, + affected_users: [vote.user], + extra: { + vote:, + verify_url: + } + } + + Decidim::EventsManager.publish(**data) + end + + def send_vote_email + Decidim::Elections::VoteAcceptedMailer.notification(vote, verify_url, I18n.locale.to_s).deliver_later + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/create_ballot_style.rb b/decidim-elections/app/commands/decidim/votings/admin/create_ballot_style.rb new file mode 100644 index 00000000..1d56be5e --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/create_ballot_style.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to create the ballot style + class CreateBallotStyle < Decidim::Command + def initialize(form) + @form = form + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed + # + # Returns nothing. + def call + return broadcast(:invalid) unless form.valid? + + begin + create_ballot_style! + rescue ActiveRecord::RecordNotUnique + form.errors.add(:code, :taken) + return broadcast(:invalid) + end + + create_ballot_style_questions! + + broadcast(:ok) + end + + private + + attr_reader :form, :ballot_style + + def create_ballot_style! + params = { + code: form.code, + voting: form.current_participatory_space + } + + @ballot_style = Decidim.traceability.create!( + Decidim::Votings::BallotStyle, + form.current_user, + params, + visibility: "all" + ) + end + + def create_ballot_style_questions! + form.question_ids.each do |question_id| + Decidim::Votings::BallotStyleQuestion.create!( + decidim_votings_ballot_style_id: ballot_style.id, + decidim_elections_question_id: question_id + ) + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/create_monitoring_committee_member.rb b/decidim-elections/app/commands/decidim/votings/admin/create_monitoring_committee_member.rb new file mode 100644 index 00000000..f8d8ad19 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/create_monitoring_committee_member.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to create a new monitoring committee member + class CreateMonitoringCommitteeMember < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params + # current_user - The user creating the monitoring committee member + # voting - The Voting that will hold the monitoring committee member + def initialize(form, current_user, voting) + @form = form + @current_user = current_user + @voting = voting + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + ActiveRecord::Base.transaction do + user = retrieve_or_invite_user + create_monitoring_committee_member(user) + end + + broadcast(:ok) + rescue ActiveRecord::RecordInvalid + form.errors.add(:email, :taken) + broadcast(:invalid) + end + + private + + attr_reader :form, :voting, :current_user + + def retrieve_or_invite_user + form.user || existing_user || new_user + end + + def create_monitoring_committee_member(user) + Decidim.traceability.perform_action!( + :create, + Decidim::Votings::MonitoringCommitteeMember, + current_user, + resource: { + title: user.name + } + ) do + Decidim::Votings::MonitoringCommitteeMember.find_or_create_by!( + user:, + voting: + ) + end + end + + def existing_user + @existing_user ||= begin + tentative_user = User.find_by( + email: form.email, + organization: voting.organization + ) + + InviteUserAgain.call(tentative_user, invitation_instructions) if tentative_user&.invitation_pending? + + tentative_user + end + end + + def new_user + @new_user ||= InviteUser.call(invite_user_form) do + on(:ok) do |invited_user| + return invited_user + end + end + end + + def invite_user_form + OpenStruct.new(name: form.name, + email: form.email.downcase, + organization: voting.organization, + admin: false, + invited_by: current_user, + invitation_instructions:) + end + + def invitation_instructions + "invite_collaborator" + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/create_polling_officer.rb b/decidim-elections/app/commands/decidim/votings/admin/create_polling_officer.rb new file mode 100644 index 00000000..d90d217e --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/create_polling_officer.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to create a new polling officer + class CreatePollingOfficer < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params + # current_user - The user creating the polling officer + # voting - The Voting that will hold the polling officer + def initialize(form, current_user, voting) + @form = form + @current_user = current_user + @voting = voting + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + ActiveRecord::Base.transaction do + user = retrieve_or_invite_user + create_polling_officer(user) + end + + broadcast(:ok) + rescue ActiveRecord::RecordInvalid + form.errors.add(:email, :taken) + broadcast(:invalid) + end + + private + + attr_reader :form, :voting, :current_user + + def retrieve_or_invite_user + form.user || existing_user || new_user + end + + def create_polling_officer(user) + Decidim.traceability.perform_action!( + :create, + Decidim::Votings::PollingOfficer, + current_user, + resource: { + title: user.name + } + ) do + Decidim::Votings::PollingOfficer.find_or_create_by!( + user:, + voting: + ) + end + end + + def existing_user + @existing_user ||= begin + tentative_user = User.find_by( + email: form.email, + organization: voting.organization + ) + + InviteUserAgain.call(tentative_user, invitation_instructions) if tentative_user&.invitation_pending? + + tentative_user + end + end + + def new_user + @new_user ||= InviteUser.call(invite_user_form) do + on(:ok) do |invited_user| + return invited_user + end + end + end + + def invite_user_form + OpenStruct.new(name: form.name, + email: form.email.downcase, + organization: voting.organization, + admin: false, + invited_by: current_user, + invitation_instructions:) + end + + def invitation_instructions + "invite_collaborator" + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/create_polling_station.rb b/decidim-elections/app/commands/decidim/votings/admin/create_polling_station.rb new file mode 100644 index 00000000..393a160c --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/create_polling_station.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with all the business logic when creating a new polling station + class CreatePollingStation < ManagePollingStation + # Public: Initializes the command. + # + # form - A form object with the params. + def initialize(form) + @form = form + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + polling_station = create_polling_station! + manage_polling_officers(polling_station, form.polling_station_president_id, form.polling_station_manager_ids) + + if polling_station.persisted? + broadcast(:ok, polling_station) + else + broadcast(:invalid) + end + end + + private + + attr_reader :form + + def create_polling_station! + params = { + voting: form.voting, + title: form.title, + address: form.address, + latitude: form.latitude, + longitude: form.longitude, + location: form.location, + location_hints: form.location_hints + } + + Decidim.traceability.create!( + PollingStation, + form.current_user, + params, + visibility: "all" + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/create_voting.rb b/decidim-elections/app/commands/decidim/votings/admin/create_voting.rb new file mode 100644 index 00000000..bde26b62 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/create_voting.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with all the business logic when creating a new voting space + class CreateVoting < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + def initialize(form) + @form = form + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + voting = create_voting! + + if voting.persisted? + broadcast(:ok, voting) + else + form.errors.add(:banner_image, voting.errors[:banner_image]) if voting.errors.include? :banner_image + form.errors.add(:introductory_image, voting.errors[:introductory_image]) if voting.errors.include? :introductory_image + broadcast(:invalid) + end + end + + private + + attr_reader :form + + def create_voting! + Decidim.traceability.create( + Voting, + form.current_user, + organization: form.current_organization, + title: form.title, + slug: form.slug, + description: form.description, + scope: form.scope, + start_time: form.start_time, + end_time: form.end_time, + promoted: form.promoted, + banner_image: form.banner_image, + introductory_image: form.introductory_image, + voting_type: form.voting_type, + census_contact_information: form.census_contact_information, + show_check_census: form.show_check_census + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/destroy_ballot_style.rb b/decidim-elections/app/commands/decidim/votings/admin/destroy_ballot_style.rb new file mode 100644 index 00000000..c56f7d3f --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/destroy_ballot_style.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to delete the ballot style + class DestroyBallotStyle < Decidim::Command + def initialize(ballot_style, current_user) + @ballot_style = ballot_style + @current_user = current_user + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed + # + # Returns nothing. + def call + destroy_ballot_style! + + broadcast(:ok) + end + + private + + attr_reader :ballot_style, :current_user + + def destroy_ballot_style! + Decidim.traceability.perform_action!( + :delete, + ballot_style, + current_user, + { visibility: "all", code: ballot_style.code } + ) do + ballot_style.destroy! + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/destroy_monitoring_committee_member.rb b/decidim-elections/app/commands/decidim/votings/admin/destroy_monitoring_committee_member.rb new file mode 100644 index 00000000..eadd75be --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/destroy_monitoring_committee_member.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to destroy a monitoring committee member + class DestroyMonitoringCommitteeMember < Decidim::Command + # Public: Initializes the command. + # + # monitoring_committee_member - the MonitoringCommitteeMember to destroy + # current_user - the user performing this action + def initialize(monitoring_committee_member, current_user) + @monitoring_committee_member = monitoring_committee_member + @current_user = current_user + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + destroy_monitoring_committee_member! + broadcast(:ok) + end + + private + + attr_reader :monitoring_committee_member, :current_user + + def destroy_monitoring_committee_member! + extra_info = { + resource: { + title: monitoring_committee_member.user.name + } + } + + Decidim.traceability.perform_action!( + "delete", + monitoring_committee_member, + current_user, + extra_info + ) do + monitoring_committee_member.destroy! + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/destroy_polling_officer.rb b/decidim-elections/app/commands/decidim/votings/admin/destroy_polling_officer.rb new file mode 100644 index 00000000..059cada1 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/destroy_polling_officer.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to destroy a poling officer + class DestroyPollingOfficer < Decidim::Command + # Public: Initializes the command. + # + # polling_officer - the PollingOfficer to destroy + # current_user - the user performing this action + def initialize(polling_officer, current_user) + @polling_officer = polling_officer + @current_user = current_user + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + destroy_polling_officer! + broadcast(:ok) + end + + private + + attr_reader :polling_officer, :current_user + + def destroy_polling_officer! + extra_info = { + resource: { + title: polling_officer.user.name + } + } + + Decidim.traceability.perform_action!( + "delete", + polling_officer, + current_user, + extra_info + ) do + polling_officer.destroy! + polling_officer + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/destroy_polling_station.rb b/decidim-elections/app/commands/decidim/votings/admin/destroy_polling_station.rb new file mode 100644 index 00000000..2327569c --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/destroy_polling_station.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This command is executed when the user destroys a polling station + # from the admin panel. + class DestroyPollingStation < Decidim::Command + def initialize(polling_station, current_user) + @polling_station = polling_station + @current_user = current_user + end + + # Destroys the polling station if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + destroy_polling_station! + + broadcast(:ok, polling_station) + end + + private + + attr_reader :polling_station, :current_user + + def destroy_polling_station! + Decidim.traceability.perform_action!( + :delete, + polling_station, + current_user, + visibility: "all" + ) do + polling_station.destroy! + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/manage_polling_station.rb b/decidim-elections/app/commands/decidim/votings/admin/manage_polling_station.rb new file mode 100644 index 00000000..a07e7df3 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/manage_polling_station.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the shared business logic to manage a polling station + class ManagePollingStation < Decidim::Command + def manage_polling_officers(polling_station, president_id, managers_ids) + manage_president(polling_station, president_id) + manage_managers(polling_station, managers_ids) + end + + private + + def manage_president(polling_station, president_id) + unassign_president(polling_station) + return if president_id.blank? + + assign_president(president_id, polling_station) + notify_officer(president_id, polling_station.voting) + end + + def unassign_president(polling_station) + assign_president(polling_station.polling_station_president.id, nil) if polling_station.polling_station_president.present? + end + + def assign_president(president_id, polling_station) + polling_officer_for(president_id).update(presided_polling_station: polling_station) + end + + def manage_managers(polling_station, managers_ids) + unassign_managers(polling_station.polling_station_manager_ids - managers_ids) + assign_managers(polling_station, managers_ids - polling_station.polling_station_manager_ids) + end + + def unassign_managers(managers_ids) + managers_ids.each { |manager_id| polling_officer_for(manager_id).update(managed_polling_station: nil) } + end + + def assign_managers(polling_station, managers_ids) + managers_ids.each do |manager_id| + polling_officer_for(manager_id).update(managed_polling_station: polling_station) + notify_officer(manager_id, polling_station.voting) + end + end + + def notify_officer(polling_officer_id, voting) + Decidim::EventsManager.publish( + event: "decidim.events.votings.polling_officers.polling_station_assigned", + event_class: ::Decidim::Votings::PollingOfficers::PollingStationAssignedEvent, + resource: voting, + affected_users: [polling_officer_for(polling_officer_id).user], + followers: [], + extra: { polling_officer_id: } + ) + end + + def polling_officer_for(polling_officer_id) + PollingOfficer.find_by(id: polling_officer_id) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure.rb b/decidim-elections/app/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure.rb new file mode 100644 index 00000000..a7cca4de --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with all the business logic for a monitornig committee member to validate a polling station closure + class MonitoringCommitteeValidatePollingStationClosure < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + # closure - A closure object. + def initialize(form, closure) + @form = form + @closure = closure + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + closure.update!(validated_at: Time.current, monitoring_committee_notes: form.monitoring_committee_notes) + + broadcast(:ok) + rescue ActiveRecord::RecordInvalid + broadcast(:invalid) + end + + attr_reader :form, :closure + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/update_ballot_style.rb b/decidim-elections/app/commands/decidim/votings/admin/update_ballot_style.rb new file mode 100644 index 00000000..7541108c --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/update_ballot_style.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with the business logic to update the ballot style + class UpdateBallotStyle < Decidim::Command + def initialize(form, ballot_style) + @form = form + @ballot_style = ballot_style + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed + # + # Returns nothing. + def call + return broadcast(:invalid) unless form.valid? + + begin + update_ballot_style! + rescue ActiveRecord::RecordNotUnique + form.errors.add(:code, :taken) + return broadcast(:invalid) + end + + transaction do + destroy_removed_ballot_style_questions! + create_added_ballot_style_questions! + end + + broadcast(:ok) + end + + private + + attr_reader :form, :ballot_style + + def update_ballot_style! + attributes = { + code: form.code + } + + Decidim.traceability.update!( + ballot_style, + form.current_user, + attributes, + visibility: "all" + ) + end + + def destroy_removed_ballot_style_questions! + (ballot_style.question_ids - form.question_ids).each do |question_id| + Decidim::Votings::BallotStyleQuestion + .where( + decidim_votings_ballot_style_id: ballot_style.id, + decidim_elections_question_id: question_id + ) + .delete_all + end + end + + def create_added_ballot_style_questions! + (form.question_ids - ballot_style.question_ids).each do |question_id| + Decidim::Votings::BallotStyleQuestion.create!( + decidim_votings_ballot_style_id: ballot_style.id, + decidim_elections_question_id: question_id + ) + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/update_polling_station.rb b/decidim-elections/app/commands/decidim/votings/admin/update_polling_station.rb new file mode 100644 index 00000000..b8377b33 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/update_polling_station.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This command is executed when the user updates a polling station + # from the admin panel. + class UpdatePollingStation < ManagePollingStation + def initialize(form, polling_station) + @form = form + @polling_station = polling_station + end + + # Updates the polling station if valid. + # + # Broadcasts :ok if successful, :invalid otherwise. + def call + return broadcast(:invalid) if form.invalid? + + update_polling_station! + manage_polling_officers(polling_station, form.polling_station_president_id, form.polling_station_manager_ids) + + broadcast(:ok, polling_station) + end + + private + + attr_reader :form, :polling_station + + def update_polling_station! + attributes = { + title: form.title, + address: form.address, + latitude: form.latitude, + longitude: form.longitude, + location: form.location, + location_hints: form.location_hints + } + + Decidim.traceability.update!( + polling_station, + form.current_user, + attributes, + visibility: "all" + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/admin/update_voting.rb b/decidim-elections/app/commands/decidim/votings/admin/update_voting.rb new file mode 100644 index 00000000..97300cbf --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/admin/update_voting.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A command with all the business logic when updating an existing + # voting in the system. + class UpdateVoting < Decidim::Command + include ::Decidim::AttachmentAttributesMethods + + # Public: Initializes the command. + # + # voting - the Voting to update + # form - A form object with the params. + def initialize(voting, form) + @voting = voting + @form = form + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + update_voting! + + if voting.valid? + broadcast(:ok, voting) + else + image_fields.each do |field| + form.errors.add(field, voting.errors[field]) if voting.errors.include? field + end + broadcast(:invalid) + end + end + + private + + attr_reader :form, :voting + + def image_fields + [:banner_image, :introductory_image] + end + + def update_voting! + voting.assign_attributes(attributes) + return unless voting.valid? + + voting.save! + + Decidim.traceability.perform_action!(:update, voting, form.current_user) do + voting + end + end + + def attributes + { + title: form.title, + description: form.description, + slug: form.slug, + start_time: form.start_time, + end_time: form.end_time, + scope: form.scope, + promoted: form.promoted, + voting_type: form.voting_type, + census_contact_information: form.census_contact_information, + show_check_census: form.show_check_census + }.merge(attachment_attributes(*image_fields)) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/create_dataset.rb b/decidim-elections/app/commands/decidim/votings/census/admin/create_dataset.rb new file mode 100644 index 00000000..3f50b5c9 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/create_dataset.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require "English" +require "csv" + +module Decidim + module Votings + module Census + module Admin + # A command with the business logic to create census dataset for a + # voting space. + class CreateDataset < Decidim::Command + include Decidim::ProcessesFileLocally + + def initialize(form, current_user) + @form = form + @current_user = current_user + @dataset = nil + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed- + # + # Returns nothing. + def call + return broadcast(:invalid) unless form.valid? + + process_file_locally(form.file) do |file_path| + @file_path = file_path + dataset = create_census_dataset! + + if csv_header_invalid? + dataset.destroy! + return broadcast(:invalid_csv_header) + end + + if dataset + CSV.foreach(file_path, col_sep: Decidim.default_csv_col_sep, headers: true, converters: ->(f) { f&.strip }) do |row| + CreateDatumJob.perform_later(current_user, dataset, row.fields) + end + end + end + + broadcast(:ok) + end + + private + + attr_reader :form, :current_user, :file_path + attr_accessor :dataset + + def create_census_dataset! + Decidim.traceability.create( + Decidim::Votings::Census::Dataset, + current_user, + { + voting: form.current_participatory_space, + filename: form.file.filename.to_s, + csv_row_raw_count: csv_row_count, + status: :creating_data + }, + visibility: "admin-only" + ) + end + + def csv_header_invalid? + headers.blank? || headers != expected_headers + end + + def headers + @headers ||= CSV.parse_line(File.open(file_path), col_sep: ";", headers: true, header_converters: :symbol)&.headers + end + + def no_ballot_headers + [:document_id, :document_type, :date_of_birth, :full_name, :full_address, :postal_code, :mobile_phone_number, :email_address] + end + + def ballot_style_headers + no_ballot_headers.push(:ballot_style_code) + end + + def expected_headers + @expected_headers ||= form.current_participatory_space.has_ballot_styles? ? ballot_style_headers : no_ballot_headers + end + + def csv_row_count + @csv_row_count ||= file_lines_count - 1 + end + + # count lines in the most resource-efficient way using ruby, handles milions of lines with minimal memory footprint + def file_lines_count + lines = 0 + File.foreach(file_path) { lines += 1 } + lines + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/create_datum.rb b/decidim-elections/app/commands/decidim/votings/census/admin/create_datum.rb new file mode 100644 index 00000000..f54352c7 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/create_datum.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # A command with the business logic to create the datum for a + # dataset row. + class CreateDatum < Decidim::Command + def initialize(form, dataset) + @form = form + @dataset = dataset + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed- + # + # Returns nothing. + def call + return broadcast(:invalid) unless form.valid? + + create_census_datum! + broadcast(:ok) + end + + attr_reader :form, :dataset + + def create_census_datum! + attributes = { + hashed_in_person_data: form.hashed_in_person_data, + hashed_check_data: form.hashed_check_data, + + full_name: form.full_name, + full_address: form.full_address, + postal_code: form.postal_code, + mobile_phone_number: form.mobile_phone_number, + email: form.email, + decidim_votings_ballot_style_id: ballot_style_for_code(dataset.voting, form.ballot_style_code)&.id + } + + Decidim::Votings::Census::Datum.create( + dataset:, + attributes: + ) + end + + def ballot_style_for_code(voting, code) + Decidim::Votings::Admin::BallotStyleByVotingCode.for(voting, code) + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/destroy_dataset.rb b/decidim-elections/app/commands/decidim/votings/census/admin/destroy_dataset.rb new file mode 100644 index 00000000..d16d5568 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/destroy_dataset.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "csv" + +module Decidim + module Votings + module Census + module Admin + # A command with the business logic to destroy a census dataset + # from the admin panel. + class DestroyDataset < Decidim::Command + def initialize(dataset, current_user) + @dataset = dataset + @current_user = current_user + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed- + # + # Returns nothing. + def call + return broadcast(:invalid) unless dataset || current_user + + destroy_census_dataset! + + broadcast(:ok) + end + + attr_reader :dataset, :current_user + + def destroy_census_dataset! + Decidim.traceability.perform_action!( + :delete, + dataset, + current_user + ) do + dataset.destroy! + end + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/increment_dataset_processed_rows.rb b/decidim-elections/app/commands/decidim/votings/census/admin/increment_dataset_processed_rows.rb new file mode 100644 index 00000000..5751b930 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/increment_dataset_processed_rows.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # A command with the business logic to create increment the dataset + # processed rows and change the state when the last is processed + class IncrementDatasetProcessedRows < Decidim::Command + def initialize(dataset) + @dataset = dataset + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the form was not valid and could not proceed- + # + # Returns nothing. + def call + return broadcast(:invalid) unless dataset + + # rubocop:disable Rails/SkipsModelValidations + Dataset.increment_counter(:csv_row_processed_count, dataset.id) + # rubocop:enable Rails/SkipsModelValidations + + dataset.data_created! if all_rows_processed? + + broadcast(:ok) + end + + attr_accessor :dataset + + def all_rows_processed? + dataset.reload + return unless dataset.creating_data? + + dataset.csv_row_raw_count == dataset.csv_row_processed_count + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_export.rb b/decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_export.rb new file mode 100644 index 00000000..a0ec9f7c --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_export.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # A command to launch the access codes export + class LaunchAccessCodesExport < Decidim::Command + def initialize(dataset, user) + @dataset = dataset + @user = user + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the user is not present + # + # Returns nothing. + def call + return broadcast(:invalid) unless valid? + + UpdateDataset.call(dataset, { status: :exporting_codes }, user) + + ExportAccessCodesJob.perform_later(dataset, user) + + broadcast(:ok) + end + + attr_reader :user, :dataset + + private + + def valid? + user.present? && dataset&.data&.exists? && dataset.codes_generated? + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_generation.rb b/decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_generation.rb new file mode 100644 index 00000000..164d7d81 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/launch_access_codes_generation.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # A command to launch the access codes generation + class LaunchAccessCodesGeneration < Decidim::Command + def initialize(dataset, user) + @dataset = dataset + @user = user + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the user is not present + # + # Returns nothing. + def call + return broadcast(:invalid) unless valid? + + update_dataset_status(dataset, :generating_codes, user) + + GenerateAccessCodesJob.perform_later(dataset, user) + + broadcast(:ok) + end + + attr_reader :user, :dataset + + private + + def valid? + user.present? && dataset&.data&.exists? && dataset.data_created? + end + + def update_dataset_status(dataset, status, user) + UpdateDataset.call(dataset, { status: }, user) + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/census/admin/update_dataset.rb b/decidim-elections/app/commands/decidim/votings/census/admin/update_dataset.rb new file mode 100644 index 00000000..c8fcd0de --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/census/admin/update_dataset.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "csv" + +module Decidim + module Votings + module Census + module Admin + # A command with the business logic to update a census dataset. + # + # dataset - the Decidim::Votings::Census::Dataset to update + # attributes - the hash of attibutes to update + # user - the user performing the action (used for tracing) + class UpdateDataset < Decidim::Command + def initialize(dataset, attributes, user) + @dataset = dataset + @attributes = attributes + @user = user + end + + # Executes the command. Broadcast this events: + # - :ok when everything is valid + # - :invalid when the input was not valid and could not proceed + # + # Returns nothing. + def call + return broadcast(:invalid) unless valid? + + update_census_dataset! + + broadcast(:ok) + end + + attr_reader :dataset, :attributes, :user + + def valid? + user.present? && dataset.present? && attributes.present? + end + + def update_census_dataset! + Decidim.traceability.update!( + dataset, + user, + attributes + ) + end + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/certify_polling_station_closure.rb b/decidim-elections/app/commands/decidim/votings/certify_polling_station_closure.rb new file mode 100644 index 00000000..e82934ba --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/certify_polling_station_closure.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command with all the business logic when signing a closure of a polling station + class CertifyPollingStationClosure < Decidim::Command + include ::Decidim::GalleryMethods + # Public: Initializes the command. + # + # form - A form object with the params. + # closure - A closure object. + def initialize(form, closure) + @form = form + @closure = closure + @attached_to = closure + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + if process_gallery? + build_gallery + return broadcast(:invalid) if gallery_invalid? + end + + transaction do + photo_cleanup! + + create_gallery if process_gallery? + closure.update!(phase: :signature) + end + + broadcast(:ok) + end + + private + + attr_reader :form, :closure, :attachment + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/check_census.rb b/decidim-elections/app/commands/decidim/votings/check_census.rb new file mode 100644 index 00000000..80f23726 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/check_census.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command to check if census data is given + class CheckCensus < Decidim::Command + def initialize(form) + @form = form + end + + # Executes the command. Broadcast this events: + # - :ok when census is found + # - :invalid when the form is not valid + # - :not_found when census is not found + # + # Returns nothing. + def call + return broadcast(:invalid) unless form.valid? + + check_census + end + + attr_reader :form, :session + + def check_census + datum = Decidim::Votings::Census::Datum.find_by(dataset: form.current_participatory_space.dataset, hashed_check_data: form.hashed_check_data) + if datum + broadcast(:ok, datum) + else + broadcast(:not_found) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/create_polling_station_closure.rb b/decidim-elections/app/commands/decidim/votings/create_polling_station_closure.rb new file mode 100644 index 00000000..bbeca41c --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/create_polling_station_closure.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command with all the business logic when creating a closure for a polling station + class CreatePollingStationClosure < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + def initialize(form) + @form = form + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + closure.save! + create_total_ballot_results! + end + + broadcast(:ok) + end + + private + + attr_reader :form + + def closure + @closure ||= PollingStationClosure.new( + phase: :results, + election: form.election, + polling_station: form.polling_station, + polling_officer: form.context.polling_officer, + polling_officer_notes: form.polling_officer_notes + ) + end + + def create_total_ballot_results! + closure.results.create!( + value: form.total_ballots_count, + result_type: :total_ballots + ) + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/create_polling_station_results.rb b/decidim-elections/app/commands/decidim/votings/create_polling_station_results.rb new file mode 100644 index 00000000..cf5a2c24 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/create_polling_station_results.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command with all the business logic when creating results for a polling station + class CreatePollingStationResults < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + # closure - A closure object. + def initialize(form, closure) + @form = form + @closure = closure + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + transaction do + closure.results.not_total_ballots.destroy_all + + form.ballot_results.attributes.compact.each do |ballot_result| + create_ballot_result_for!(ballot_result) + end + + form.answer_results.each do |answer_result| + create_answer_result_for!(answer_result) + end + + form.question_results.each do |question_result| + create_question_result_for!(question_result) + end + + closure.certificate_phase! + end + + broadcast(:ok) + end + + private + + attr_reader :form, :closure + + def create_ballot_result_for!(ballot_results) + params = { + value: ballot_results.last, + result_type: ballot_results.first.to_s.remove("_count") + } + + create_result!(params) + end + + def create_answer_result_for!(answer_result) + params = { + value: answer_result.value, + decidim_elections_question_id: answer_result.question_id, + decidim_elections_answer_id: answer_result.id, + result_type: :valid_answers + } + + create_result!(params) + end + + def create_question_result_for!(question_result) + params = { + value: question_result.value, + decidim_elections_question_id: question_result.id, + result_type: :blank_answers + } + + create_result!(params) + end + + def create_result!(params) + closure.results.create!(params) + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/destroy_polling_station_closure.rb b/decidim-elections/app/commands/decidim/votings/destroy_polling_station_closure.rb new file mode 100644 index 00000000..211d86a5 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/destroy_polling_station_closure.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command with all the business logic when deleting a closure for a polling station + class DestroyPollingStationClosure < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + def initialize(closure, current_user) + @closure = closure + @current_user = current_user + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if invalid? + + destroy_closure! + + broadcast(:ok, closure) + end + + private + + attr_reader :closure, :current_user + + def invalid? + closure.complete_phase? + end + + def destroy_closure! + Decidim.traceability.perform_action!( + :delete, + closure, + current_user, + visibility: "all" + ) do + closure.results.destroy_all + closure.destroy! + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/send_access_code.rb b/decidim-elections/app/commands/decidim/votings/send_access_code.rb new file mode 100644 index 00000000..2d568b81 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/send_access_code.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command to send the access code + class SendAccessCode < Decidim::Command + def initialize(datum, medium) + @datum = datum + @medium = medium + end + + # Executes the command. Broadcast this events: + # - :invalid when params are missing + # + # Returns nothing. + def call + return broadcast(:invalid) unless datum + + send_access_code + + broadcast(:ok) + end + + private + + attr_reader :datum, :medium + + def send_access_code + case medium + when "email" + AccessCodeMailer.send_access_code(datum).deliver_later + when "sms" + sms_gateway.new(datum.mobile_phone_number, access_code, sms_gateway_context).deliver_code + else + raise ArgumentError, "Medium parameter is invalid" + end + end + + def sms_gateway + Decidim.sms_gateway_service.to_s.safe_constantize + end + + def sms_gateway_context + { organization: try(:current_organization) } + end + + def access_code + @access_code ||= datum.access_code + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/sign_polling_station_closure.rb b/decidim-elections/app/commands/decidim/votings/sign_polling_station_closure.rb new file mode 100644 index 00000000..7764d172 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/sign_polling_station_closure.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A command with all the business logic when signing a closure of a polling station + class SignPollingStationClosure < Decidim::Command + # Public: Initializes the command. + # + # form - A form object with the params. + # closure - A closure object. + def initialize(form, closure) + @form = form + @closure = closure + end + + # Executes the command. Broadcasts these events: + # + # - :ok when everything is valid. + # - :invalid if the form was not valid and we could not proceed. + # + # Returns nothing. + def call + return broadcast(:invalid) if form.invalid? + + closure.update!(signed_at:, phase: :complete) + + broadcast(:ok) + end + + private + + attr_reader :form, :closure + + def signed_at + return unless form.signed + + Time.current + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/voter/in_person_vote.rb b/decidim-elections/app/commands/decidim/votings/voter/in_person_vote.rb new file mode 100644 index 00000000..6fb16574 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/voter/in_person_vote.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Voter + # This command allows the user to register an in person vote. + class InPersonVote < Decidim::Command + # Public: Initializes the command. + # + # form - A form with necessary info to register an in person vote. + def initialize(form) + @form = form + end + + # Store and register the in person vote in the bulletin board + # + # Broadcasts :ok if successful, :invalid otherwise + def call + return broadcast(:invalid) if form.invalid? + + transaction do + register_in_person_vote + end + + broadcast(:ok, in_person_vote) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_reader :form, :in_person_vote + + delegate :bulletin_board, to: :form + + def register_in_person_vote + bulletin_board.in_person_vote(form.election_id, form.voter_id, form.polling_station_slug) do |message_id| + create_in_person_vote(message_id) + end + end + + def create_in_person_vote(message_id) + @in_person_vote = Decidim::Votings::InPersonVote.create!( + election: form.election, + polling_station: form.polling_station, + polling_officer: form.polling_officer, + message_id:, + voter_id: form.voter_id, + status: :pending + ) + end + end + end + end +end diff --git a/decidim-elections/app/commands/decidim/votings/voter/update_in_person_vote_status.rb b/decidim-elections/app/commands/decidim/votings/voter/update_in_person_vote_status.rb new file mode 100644 index 00000000..4d06efe2 --- /dev/null +++ b/decidim-elections/app/commands/decidim/votings/voter/update_in_person_vote_status.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Voter + # This command updates the in person vote status + class UpdateInPersonVoteStatus < Decidim::Command + # Public: Initializes the command. + # + # in_person_vote - the in person vote that has been updated + def initialize(in_person_vote) + @in_person_vote = in_person_vote + end + + # Update status and send notification + # + # Broadcasts :ok if successful, :invalid otherwise + def call + return broadcast(:ok) unless status_changed? + + transaction do + update_vote_status + end + + broadcast(:ok) + rescue StandardError => e + broadcast(:invalid, e.message) + end + + private + + attr_reader :in_person_vote, :locale + + def status_changed? + in_person_vote.status != vote_status + end + + def bulletin_board + @bulletin_board ||= Decidim::Elections.bulletin_board + end + + def vote_status + @vote_status ||= bulletin_board.get_pending_message_status(in_person_vote.message_id) + end + + def update_vote_status + in_person_vote.update!(status: vote_status) + end + end + end + end +end diff --git a/decidim-elections/app/constraints/decidim/votings/current_component.rb b/decidim-elections/app/constraints/decidim/votings/current_component.rb new file mode 100644 index 00000000..1c92da1c --- /dev/null +++ b/decidim-elections/app/constraints/decidim/votings/current_component.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This class infers the current component on an voting context + # request parameters and injects it into the environment. + class CurrentComponent + # Public: Initializes the class. + # + # manifest - The manifest of the component to check against. + def initialize(manifest) + @manifest = manifest + end + + # Public: Matches the request against a component and injects it into the + # environment. + # + # request - The request that holds the current component relevant information. + # + # Returns a true if the request matches an voting and a + # component belonging to that voting, false otherwise + def matches?(request) + CurrentVoting.new.matches?(request) && + Decidim::CurrentComponent.new(@manifest).matches?(request) + end + end + end +end diff --git a/decidim-elections/app/constraints/decidim/votings/current_voting.rb b/decidim-elections/app/constraints/decidim/votings/current_voting.rb new file mode 100644 index 00000000..23110d5b --- /dev/null +++ b/decidim-elections/app/constraints/decidim/votings/current_voting.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This class infers the current voting we are scoped to by + # looking at the request parameters and the organization in the request + # environment, and injects it into the environment. + class CurrentVoting + # Public: Matches the request against an voting and injects it + # into the environment. + # + # request - The request that holds the voting relevant + # information. + # + # Returns a true if the request matched, false otherwise + def matches?(request) + env = request.env + + @organization = env["decidim.current_organization"] + return false unless @organization + + current_voting(env, request.params).present? + end + + private + + def current_voting(env, params) + env["decidim.current_participatory_space"] ||= + detect_current_voting(params) + end + + def detect_current_voting(params) + organization_votings.where(slug: params["voting_slug"]).or( + organization_votings.where(id: params["voting_slug"]) + ).first! + end + + def organization_votings + @organization_votings ||= OrganizationVotings.new(@organization).query + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/elections/content_security_policy.rb b/decidim-elections/app/controllers/concerns/decidim/elections/content_security_policy.rb new file mode 100644 index 00000000..e49e1b08 --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/elections/content_security_policy.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Elections + module ContentSecurityPolicy + extend ActiveSupport::Concern + + included do + before_action :append_csp_directives + end + + private + + def append_csp_directives + return unless Decidim::Elections.bulletin_board.configured? + + content_security_policy.append_csp_directive("connect-src", Decidim::Elections.bulletin_board.bulletin_board_server) + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/elections/has_vote_flow.rb b/decidim-elections/app/controllers/concerns/decidim/elections/has_vote_flow.rb new file mode 100644 index 00000000..251c45db --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/elections/has_vote_flow.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Elections + # Common logic for the vote flow + module HasVoteFlow + extend ActiveSupport::Concern + + included do + helper_method :voter_id, :voter_token, :voter_name, :preview_mode?, :ballot_style_id + delegate :voter_id, :ballot_style_id, :voter_token, :voter_name, :email, to: :vote_flow + end + + def vote_flow + @vote_flow ||= election.participatory_space.try(:vote_flow_for, election) || default_vote_flow + end + + def default_vote_flow + Decidim::Elections::CurrentUserVoteFlow.new(election, current_user) do + allowed_to?(:user_vote, :election, election:) + end + end + + def preview_mode? + return @preview_mode if defined?(@preview_mode) + + @preview_mode = !election.published? || !election.started? + end + + def can_preview? + return @can_preview if defined?(@can_preview) + + @preview_mode = allowed_to?(:preview, :election, election:) + end + + def ballot_questions + vote_flow.questions_for(election) + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/elections/orderable.rb b/decidim-elections/app/controllers/concerns/decidim/elections/orderable.rb new file mode 100644 index 00000000..4c35f939 --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/elections/orderable.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Elections + # Common logic to sorting resources + module Orderable + extend ActiveSupport::Concern + + included do + include Decidim::Orderable + + private + + # Available orders based on enabled settings + def available_orders + @available_orders ||= %w(recent older) + end + + def default_order + "recent" + end + + def reorder(elections) + case order + when "recent" + elections.order(start_time: :desc) + else + elections.order(start_time: :asc) + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/monitoring_committee_polling_station_closures/admin/filterable.rb b/decidim-elections/app/controllers/concerns/decidim/monitoring_committee_polling_station_closures/admin/filterable.rb new file mode 100644 index 00000000..2c76b9bd --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/monitoring_committee_polling_station_closures/admin/filterable.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module MonitoringCommitteePollingStationClosures + module Admin + module Filterable + extend ActiveSupport::Concern + + included do + include Decidim::Admin::Filterable + + private + + def base_query + # Includes the officers (president and managers) and their correspective decidim users when they(=officers) are present + query = + collection + .joins("LEFT JOIN decidim_votings_polling_officers president ON president.presided_polling_station_id = decidim_votings_polling_stations.id + LEFT JOIN decidim_users president_user ON president_user.id = president.decidim_user_id + LEFT JOIN decidim_votings_polling_officers managers ON managers.managed_polling_station_id = decidim_votings_polling_stations.id + LEFT JOIN decidim_users manager_user ON manager_user.id = managers.decidim_user_id + LEFT JOIN decidim_votings_polling_station_closures closure ON closure.decidim_votings_polling_station_id = decidim_votings_polling_stations.id + AND closure.decidim_elections_election_id = #{election_id}") + + query = filter_by_validated(query) + filter_by_signed(query) + end + + def election_id + params[:election_id] + end + + def search_field_predicate + :title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + end + + def extra_allowed_params + [:election_id, :per_page] + end + + def filters + [:validated_eq, :signed_eq] + end + + def filter_by_validated(query) + case ransack_params[:validated_eq] + when "false" + query.where(Arel.sql("closure.validated_at IS NOT NULL")) + when "true" + query.where(Arel.sql("closure.validated_at IS NULL")) + else + query + end + end + + def filter_by_signed(query) + case ransack_params[:signed_eq] + when "false" + query.where(Arel.sql("closure.signed_at IS NOT NULL")) + when "true" + query.where(Arel.sql("closure.signed_at IS NULL")) + else + query + end + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/polling_officers/admin/filterable.rb b/decidim-elections/app/controllers/concerns/decidim/polling_officers/admin/filterable.rb new file mode 100644 index 00000000..a870eb7c --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/polling_officers/admin/filterable.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module PollingOfficers + module Admin + module Filterable + extend ActiveSupport::Concern + + included do + include Decidim::Admin::Filterable + + private + + def base_query + query = Decidim::Votings::Admin::PollingOfficersJoinPollingStationsAndUser.for(collection) + + filter_by_role(query) + end + + def search_field_predicate + :name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont + end + + def filters + [ + :role_eq + ] + end + + def filters_with_values + { + role_eq: roles + } + end + + def roles + [:president, :manager, :unassigned] + end + + def filter_by_role(query) + case ransack_params[:role_eq] + when :president.to_s + query.where(Arel.sql("presided_station.id IS NOT NULL")) + when :manager.to_s + query.where(Arel.sql("managed_station.id IS NOT NULL")) + when :unassigned.to_s + query.where(Arel.sql("presided_station.id IS NULL")).where(Arel.sql("managed_station.id IS NULL")) + else + query + end + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/polling_stations/admin/filterable.rb b/decidim-elections/app/controllers/concerns/decidim/polling_stations/admin/filterable.rb new file mode 100644 index 00000000..e66a6a49 --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/polling_stations/admin/filterable.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module PollingStations + module Admin + module Filterable + extend ActiveSupport::Concern + + included do + include Decidim::Admin::Filterable + + private + + def base_query + # Includes the officers (president and managers) and their correspective decidim users when they(=officers) are present + query = collection + .joins("LEFT JOIN decidim_votings_polling_officers president ON president.presided_polling_station_id = decidim_votings_polling_stations.id + LEFT JOIN decidim_users president_user ON president_user.id = president.decidim_user_id + LEFT JOIN decidim_votings_polling_officers managers ON managers.managed_polling_station_id = decidim_votings_polling_stations.id + LEFT JOIN decidim_users manager_user ON manager_user.id = managers.decidim_user_id") + + filter_by_assigned(query) + end + + def search_field_predicate + :title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + end + + def filters + [ + :officers_assigned_eq + ] + end + + def filters_with_values + { + officers_assigned_eq: [:assigned, :unassigned] + } + end + + def filter_by_assigned(query) + case ransack_params[:officers_assigned_eq] + when :assigned.to_s + query.where(Arel.sql("president.id IS NOT NULL")).where(Arel.sql("managers.id IS NOT NULL")) + when :unassigned.to_s + query.where(Arel.sql("president.id IS NULL OR managers.id IS NULL")) + else + query + end + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/votings/admin/filterable.rb b/decidim-elections/app/controllers/concerns/decidim/votings/admin/filterable.rb new file mode 100644 index 00000000..79bb6f1c --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/votings/admin/filterable.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + module Admin + module Filterable + extend ActiveSupport::Concern + + included do + include Decidim::Admin::Filterable + + private + + def base_query + collection + end + + def filters + [:published_at_null] + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/votings/admin/voting_admin.rb b/decidim-elections/app/controllers/concerns/decidim/votings/admin/voting_admin.rb new file mode 100644 index 00000000..ef974d8c --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/votings/admin/voting_admin.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + module Admin + # This concern is meant to be included in all controllers that are scoped + # into a voting admin panel. It will override the layout so it shows + # the sidebar, preload the voting, etc. + module VotingAdmin + extend ActiveSupport::Concern + + RegistersPermissions + .register_permissions(::Decidim::Votings::Admin::VotingAdmin, + Decidim::Votings::Permissions, + Decidim::Admin::Permissions) + + included do + include Decidim::Admin::ParticipatorySpaceAdminContext + helper_method :current_voting + add_breadcrumb_item_from_menu :admin_voting_menu + + participatory_space_admin_layout + + def current_voting + @current_voting ||= organization_votings.find_by!( + slug: params[:voting_slug] || params[:slug] + ) + end + + alias_method :current_participatory_space, :current_voting + + def organization_votings + @organization_votings ||= OrganizationVotings.new(current_organization).query + end + + def permissions_context + super.merge(current_participatory_space:) + end + + def permission_class_chain + PermissionsRegistry.chain_for(VotingAdmin) + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/votings/needs_voting.rb b/decidim-elections/app/controllers/concerns/decidim/votings/needs_voting.rb new file mode 100644 index 00000000..bcb07220 --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/votings/needs_voting.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This module, when injected into a controller, ensures there is a + # voting available and deducts it from the context. + module NeedsVoting + def self.enhance_controller(instance_or_module) + instance_or_module.class_eval do + helper_method :current_participatory_space + end + end + + def self.included(base) + base.include Decidim::NeedsOrganization, InstanceMethods + + enhance_controller(base) + end + + module InstanceMethods + # Public: Finds the current Voting given this controller's + # context. + # + # Returns the current Voting. + def current_participatory_space + @current_participatory_space ||= detect_voting + end + + alias current_voting current_participatory_space + + private + + def detect_voting + request.env["current_participatory_space"] || + organization_votings.find_by(slug: params[:voting_slug] || params[:slug]) + end + + def organization_votings + @organization_votings ||= OrganizationVotings.new(current_organization).query + end + end + end + end +end diff --git a/decidim-elections/app/controllers/concerns/decidim/votings/orderable.rb b/decidim-elections/app/controllers/concerns/decidim/votings/orderable.rb new file mode 100644 index 00000000..49d6bc87 --- /dev/null +++ b/decidim-elections/app/controllers/concerns/decidim/votings/orderable.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + # Common logic to ordering resources + module Orderable + extend ActiveSupport::Concern + + included do + include Decidim::Orderable + + private + + # Available orders based on enabled settings + def available_orders + %w(random recent) + end + + def reorder(votings) + case order + when "recent" + votings.order_by_most_recent + else + votings.order_randomly(random_seed) + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/answers_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/answers_controller.rb new file mode 100644 index 00000000..453b659a --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/answers_controller.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows the create or update answers for a question. + class AnswersController < Admin::ApplicationController + helper Decidim::ApplicationHelper + helper_method :election, :question, :answers, :answers, :missing_answers + + def index + flash.now[:alert] = I18n.t("answers.index.invalid_max_selections", scope: "decidim.elections.admin", missing_answers:) if missing_answers.positive? + end + + def new + enforce_permission_to(:update, :answer, election:, question:) + @form = form(AnswerForm).instance + end + + def create + enforce_permission_to(:update, :answer, election:, question:) + @form = form(AnswerForm).from_params(params, election:, question:) + + CreateAnswer.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("answers.create.success", scope: "decidim.elections.admin") + redirect_to election_question_answers_path(election, question) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("answers.create.invalid", scope: "decidim.elections.admin") + render action: "new" + end + end + end + + def edit + enforce_permission_to(:update, :answer, election:, question:) + @form = form(AnswerForm).from_model(answer) + end + + def update + enforce_permission_to(:update, :answer, election:, question:) + @form = form(AnswerForm).from_params(params, election:, question:) + + UpdateAnswer.call(@form, answer) do + on(:ok) do + flash[:notice] = I18n.t("answers.update.success", scope: "decidim.elections.admin") + redirect_to election_question_answers_path(election, question) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("answers.update.invalid", scope: "decidim.elections.admin") + render action: "edit" + end + end + end + + def select + change_selected(true) + end + + def unselect + change_selected(false) + end + + def change_selected(selected) + enforce_permission_to(:select, :answer, election:, question:) + + UpdateAnswerSelection.call(answer, selected) do + on(:ok) do + flash[:notice] = if selected + I18n.t("answers.select.success", scope: "decidim.elections.admin") + else + I18n.t("answers.unselect.success", scope: "decidim.elections.admin") + end + end + + on(:invalid) do + flash.now[:alert] = if selected + I18n.t("answers.select.invalid", scope: "decidim.elections.admin") + else + I18n.t("answers.unselect.invalid", scope: "decidim.elections.admin") + end + end + end + + redirect_to election_question_answers_path(election, question) + end + + def destroy + enforce_permission_to(:update, :answer, election:, question:) + + DestroyAnswer.call(answer, current_user) do + on(:ok) do + flash[:notice] = I18n.t("answers.destroy.success", scope: "decidim.elections.admin") + end + + on(:invalid) do + flash.now[:alert] = I18n.t("answers.destroy.invalid", scope: "decidim.elections.admin") + end + end + + redirect_to election_question_answers_path(election, question) + end + + private + + def election + @election ||= Election.where(component: current_component).find_by(id: params[:election_id]) + end + + def question + @question ||= election.questions.find_by(id: params[:question_id]) + end + + def answers + @answers ||= question.answers + end + + def answer + answers.find(params[:id]) + end + + def missing_answers + question.max_selections - answers.count + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/application_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/application_controller.rb new file mode 100644 index 00000000..be9efe59 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/application_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller is the abstract class from which all other controllers of + # this engine inherit. + # + # Note that it inherits from `Decidim::Admin::Components::BaseController`, which + # override its layout and provide all kinds of useful methods. + class ApplicationController < Decidim::Admin::Components::BaseController + include Decidim::Elections::ContentSecurityPolicy + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/elections_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/elections_controller.rb new file mode 100644 index 00000000..ce9b221d --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/elections_controller.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows the create or update an election. + class ElectionsController < Admin::ApplicationController + helper_method :elections, :election + + def index + flash.now[:alert] ||= I18n.t("elections.index.no_bulletin_board", scope: "decidim.elections.admin").html_safe unless Decidim::Elections.bulletin_board.configured? + end + + def new + enforce_permission_to :create, :election + @form = form(ElectionForm).instance + end + + def create + enforce_permission_to :create, :election + @form = form(ElectionForm).from_params(params, current_component:) + + CreateElection.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("elections.create.success", scope: "decidim.elections.admin") + redirect_to elections_path + end + + on(:invalid) do + flash.now[:alert] = I18n.t("elections.create.invalid", scope: "decidim.elections.admin") + render action: "new" + end + end + end + + def edit + enforce_permission_to(:update, :election, election:) + @form = form(ElectionForm).from_model(election) + end + + def update + enforce_permission_to(:update, :election, election:) + @form = form(ElectionForm).from_params(params, current_component:) + + UpdateElection.call(@form, election) do + on(:ok) do + flash[:notice] = I18n.t("elections.update.success", scope: "decidim.elections.admin") + redirect_to elections_path + end + + on(:invalid) do + flash.now[:alert] = I18n.t("elections.update.invalid", scope: "decidim.elections.admin") + render action: "edit" + end + end + end + + def destroy + enforce_permission_to(:delete, :election, election:) + + DestroyElection.call(election, current_user) do + on(:ok) do + flash[:notice] = I18n.t("elections.destroy.success", scope: "decidim.elections.admin") + end + + on(:invalid) do + flash[:alert] = I18n.t("elections.destroy.invalid", scope: "decidim.elections.admin") + end + end + + redirect_to elections_path + end + + def publish + enforce_permission_to(:publish, :election, election:) + + PublishElection.call(election, current_user) do + on(:ok) do + flash[:notice] = I18n.t("admin.elections.publish.success", scope: "decidim.elections") + redirect_back(fallback_location: root_path) + end + end + end + + def unpublish + enforce_permission_to(:unpublish, :election, election:) + + UnpublishElection.call(election, current_user) do + on(:ok) do + flash[:notice] = I18n.t("admin.elections.unpublish.success", scope: "decidim.elections") + redirect_to elections_path + end + end + end + + private + + def elections + @elections ||= Election.where(component: current_component).order(start_time: :desc) + end + + def election + @election ||= elections.find_by(id: params[:id]) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/feedback_forms_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/feedback_forms_controller.rb new file mode 100644 index 00000000..d34c28f6 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/feedback_forms_controller.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows an admin to manage the form to be filled when a user finishes voting + class FeedbackFormsController < Admin::ApplicationController + include Decidim::Forms::Admin::Concerns::HasQuestionnaire + include Decidim::Forms::Admin::Concerns::HasQuestionnaireAnswers + + def questionnaire_for + election + end + + def update_url + feedback_form_path(election_id: election.id) + end + + def after_update_url + edit_feedback_form_path(id: election.id) + end + + def public_url + Decidim::EngineRouter.main_proxy(current_component).election_feedback_path(election) + end + + def answer_options_url(params) + answer_options_election_feedback_path(**params) + end + + def questionnaire_participants_url + answers_feedback_form_path(id: election.id) + end + + def questionnaire_participant_answers_url(session_token) + answer_feedback_form_path(id: election.id, session_token:) + end + + def questionnaire_export_response_url(session_token) + answer_export_feedback_form_path(id: election.id, session_token:, format: "pdf") + end + + def questionnaire_url + edit_feedback_form_path(id: election.id) + end + + private + + def election + @election ||= Election.where(component: current_component).find(params[:id]) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/proposals_imports_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/proposals_imports_controller.rb new file mode 100644 index 00000000..594fdc26 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/proposals_imports_controller.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows to import proposals as answers. + class ProposalsImportsController < Admin::ApplicationController + helper_method :election, :question, :answers, :answers + + def new + enforce_permission_to(:import_proposals, :answer, election:, question:) + @form = form(Admin::AnswerImportProposalsForm).instance + end + + def create + enforce_permission_to(:import_proposals, :answer, election:, question:) + + @form = form(Admin::AnswerImportProposalsForm).from_params(params, election:, question:) + + Admin::ImportProposalsToElections.call(@form) do + on(:ok) do |answers| + flash[:notice] = I18n.t("proposals_imports.create.success", scope: "decidim.elections.admin", number: answers.length) + redirect_to election_question_answers_path(election, question) + end + + on(:invalid) do + flash[:alert] = I18n.t("proposals_imports.create.invalid", scope: "decidim.elections.admin") + render action: "new" + end + end + end + + private + + def election + @election ||= Election.where(component: current_component).find_by(id: params[:election_id]) + end + + def question + @question ||= election.questions.find_by(id: params[:question_id]) + end + + def answers + @answers ||= question.answers + end + + def answer + answers.find(params[:id]) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/questions_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/questions_controller.rb new file mode 100644 index 00000000..b278554f --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/questions_controller.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows the create or update questions for an election. + class QuestionsController < Admin::ApplicationController + helper_method :election, :questions, :question + + def new + enforce_permission_to(:create, :question, election:) + @form = form(QuestionForm).instance + end + + def create + enforce_permission_to(:create, :question, election:) + @form = form(QuestionForm).from_params(params, election:) + + CreateQuestion.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("questions.create.success", scope: "decidim.elections.admin") + redirect_to election_questions_path(election) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("questions.create.invalid", scope: "decidim.elections.admin") + render action: "new" + end + + on(:election_started) do + flash.now[:alert] = I18n.t("questions.create.election_started", scope: "decidim.elections.admin") + render action: "new" + end + end + end + + def edit + enforce_permission_to(:update, :question, election:, question:) + @form = form(QuestionForm).from_model(question) + end + + def update + enforce_permission_to(:update, :question, election:, question:) + @form = form(QuestionForm).from_params(params, election:) + + UpdateQuestion.call(@form, question) do + on(:ok) do + flash[:notice] = I18n.t("questions.update.success", scope: "decidim.elections.admin") + redirect_to election_questions_path(election) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("questions.update.invalid", scope: "decidim.elections.admin") + render action: "edit" + end + end + end + + def destroy + enforce_permission_to(:update, :question, election:, question:) + + DestroyQuestion.call(question, current_user) do + on(:ok) do + flash[:notice] = I18n.t("questions.destroy.success", scope: "decidim.elections.admin") + end + + on(:invalid) do + flash.now[:alert] = I18n.t("questions.destroy.invalid", scope: "decidim.elections.admin") + end + end + + redirect_to election_questions_path(election) + end + + private + + def election + @election ||= Election.where(component: current_component).find_by(id: params[:election_id]) + end + + def questions + @questions ||= election.questions + end + + def question + questions.find(params[:id]) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/steps_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/steps_controller.rb new file mode 100644 index 00000000..2b977c91 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/steps_controller.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows to manage the steps of an election. + class StepsController < Admin::ApplicationController + helper Decidim::ApplicationHelper + helper StepsHelper + helper_method :elections, :election, :current_step, :vote_stats, :bulletin_board_server, :authority_public_key, :election_unique_id, :quorum, :missing_trustees_allowed + + def index + enforce_permission_to(:read, :steps, election:) + + if current_step_form_class + @form = form(current_step_form_class).instance(election:) + @form.valid? + end + end + + def update + enforce_permission_to(:update, :steps, election:) + redirect_to election_steps_path(election) && return unless params[:id] == current_step + + @form = form(current_step_form_class).from_params(params, election:) + Decidim::Elections::Admin::UpdateActionStatus.call(@form.pending_action) if @form.pending_action + + # check pending action status mode + return render json: { status: @form.pending_action&.status } if params[:check_pending_action] + + return redirect_to election_steps_path(election) if @form.pending_action + + current_step_command_class.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("steps.#{current_step}.success", scope: "decidim.elections.admin") + return redirect_to election_steps_path(election) + end + on(:invalid) do |message| + flash.now[:alert] = message || I18n.t("steps.#{current_step}.invalid", scope: "decidim.elections.admin") + end + end + render :index + end + + def stats + render "decidim/elections/admin/steps/_vote_stats", layout: false + end + + private + + delegate :bulletin_board_server, :authority_slug, :quorum, to: :bulletin_board_client + + def bulletin_board_client + Decidim::Elections.bulletin_board + end + + def missing_trustees_allowed + @missing_trustees_allowed ||= Decidim::Elections.bulletin_board.number_of_trustees - Decidim::Elections.bulletin_board.quorum + end + + def election_unique_id + @election_unique_id ||= Decidim::BulletinBoard::MessageIdentifier.unique_election_id(authority_slug, election.id) + end + + def authority_public_key + @authority_public_key ||= bulletin_board_client.authority_public_key.to_json + end + + def current_step_form_class + @current_step_form_class ||= { + "create_election" => SetupForm, + "created" => ActionForm, + "key_ceremony_ended" => VotePeriodForm, + "vote" => VotePeriodForm, + "vote_ended" => ActionForm, + "tally_started" => ReportMissingTrusteeForm, + "tally_ended" => ActionForm + }[current_step] + end + + def current_step_command_class + @current_step_command_class ||= { + "create_election" => SetupElection, + "created" => StartKeyCeremony, + "key_ceremony_ended" => StartVote, + "vote" => EndVote, + "vote_ended" => StartTally, + "tally_started" => ReportMissingTrustee, + "tally_ended" => PublishResults + }[current_step] + end + + def current_step + @current_step ||= election.bb_status || "create_election" + end + + def elections + @elections ||= Election.where(component: current_component) + end + + def election + @election ||= elections.find_by(id: params[:election_id]) + end + + def vote_stats + @vote_stats ||= { + votes: vote_counts.first, + voters: vote_counts.last + } + end + + def vote_counts + @vote_counts ||= Decidim::Elections::Admin::VotesForStatistics.for(election) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/admin/trustees_participatory_spaces_controller.rb b/decidim-elections/app/controllers/decidim/elections/admin/trustees_participatory_spaces_controller.rb new file mode 100644 index 00000000..107b894e --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/admin/trustees_participatory_spaces_controller.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This controller allows to add a user as trustee, update the status and remove a + # trustee from a participatory space. + class TrusteesParticipatorySpacesController < Admin::ApplicationController + helper Decidim::ApplicationHelper + + helper_method :trustees, :trustee + + def new + enforce_permission_to :create, :trustee_participatory_space + @form = form(TrusteesParticipatorySpaceForm).instance + end + + def create + enforce_permission_to :create, :trustee_participatory_space + @form = form(TrusteesParticipatorySpaceForm).from_params(params) + + AddUserAsTrustee.call(@form, current_user) do + on(:ok) do + flash[:notice] = I18n.t("trustees_participatory_spaces.create.success", scope: "decidim.elections.admin") + redirect_to trustees_path + end + + on(:invalid) do + flash.now[:alert] = I18n.t("trustees_participatory_spaces.create.invalid", scope: "decidim.elections.admin") + render action: "new" + end + + on(:exists) do + flash.now[:alert] = I18n.t("trustees_participatory_spaces.create.exists", scope: "decidim.elections.admin") + render action: "new" + end + end + end + + def edit + enforce_permission_to(:update, :trustee_participatory_space, trustee_participatory_space:) + + UpdateTrusteeParticipatorySpace.call(trustee_participatory_space) do + on(:ok) do |trustee| + flash[:notice] = I18n.t("trustees_participatory_spaces.update.success", scope: "decidim.elections.admin", trustee: trustee.user.name) + end + + on(:invalid) do |trustee| + flash.now[:alert] = I18n.t("trustees_participatory_spaces.update.invalid", scope: "decidim.elections.admin", trustee: trustee.user.name) + end + + redirect_to trustees_path + end + end + + def destroy + enforce_permission_to(:delete, :trustee_participatory_space, trustee_participatory_space:) + + RemoveTrusteeFromParticipatorySpace.call(trustee_participatory_space) do + on(:ok) do + flash[:notice] = I18n.t("trustees_participatory_spaces.delete.success", scope: "decidim.elections.admin") + end + + on(:invalid) do + flash.now[:alert] = I18n.t("trustees_participatory_spaces.delete.invalid", scope: "decidim.elections.admin") + end + end + + redirect_to trustees_path + end + + private + + def trustee_participatory_space + @trustee_participatory_space ||= TrusteesParticipatorySpace.find_by(id: params[:id], participatory_space: current_participatory_space) + end + + def trustees + trustees_space = TrusteesParticipatorySpace.where(participatory_space: current_participatory_space).includes(:trustee) + @trustees ||= Trustee.where(trustees_participatory_spaces: trustees_space).includes([:user]).page(params[:page]).per(15) + end + + def trustee + @trustee ||= trustees.find_by(id: params[:id]) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/application_controller.rb b/decidim-elections/app/controllers/decidim/elections/application_controller.rb new file mode 100644 index 00000000..14c6a86d --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/application_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This controller is the abstract class from which all other controllers of + # this engine inherit. + # + # Note that it inherits from `Decidim::Components::BaseController`, which + # override its layout and provide all kinds of useful methods. + class ApplicationController < Decidim::Components::BaseController + include Decidim::Elections::ContentSecurityPolicy + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/elections_controller.rb b/decidim-elections/app/controllers/decidim/elections/elections_controller.rb new file mode 100644 index 00000000..273869bf --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/elections_controller.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Exposes the elections resources so users can participate on them + class ElectionsController < Decidim::Elections::ApplicationController + include FilterResource + include Paginable + include Decidim::Elections::Orderable + include HasVoteFlow + include Decidim::IconHelper + + helper_method :elections, :election, :paginated_elections, :scheduled_elections, + :single?, :onboarding, :authority_public_key, :bulletin_board_server, + :authority_slug, :tabs, :panels + + def index + redirect_to election_path(single, single: true) if single? + + @forced_past_elections = true if elections.any? && scheduled_elections.none? + end + + def show + enforce_permission_to :view, :election, election: + end + + def election_log; end + + private + + delegate :bulletin_board_server, :authority_slug, to: :bulletin_board_client + + def elections + @elections ||= search_collection + end + + def election + # The single election is searched from non-published records on purpose + # to allow previewing for admins. + @election ||= Election.where(component: current_component).find(params[:id]) + end + + def onboarding + @onboarding ||= params[:onboarding].present? + end + + def bulletin_board_client + @bulletin_board_client ||= Decidim::Elections.bulletin_board + end + + def authority_public_key + @authority_public_key ||= bulletin_board_client.authority_public_key.to_json + end + + # Public: Checks if the component has only one election resource. + # + # Returns Boolean. + def single? + elections.one? + end + + def single + elections.first if single? + end + + def paginated_elections + @paginated_elections ||= paginate(reorder(search.result.published)) + end + + def scheduled_elections + @scheduled_elections ||= search_with(filter_params.merge(with_any_date: %w(active upcoming))).result + end + + def search_collection + Election.where(component: current_component).published + end + + def default_filter_params + { + search_text_cont: "", + with_any_date: default_filter_date_params + } + end + + def default_filter_date_params + if elections.active.any? + %w(active) + elsif elections.upcoming.any? + %w(upcoming) + else + %w() + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/feedbacks_controller.rb b/decidim-elections/app/controllers/decidim/elections/feedbacks_controller.rb new file mode 100644 index 00000000..349d9042 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/feedbacks_controller.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This controller allows a user to give feedback once finished voting + class FeedbacksController < Decidim::Elections::ApplicationController + include Decidim::Forms::Concerns::HasQuestionnaire + include HasVoteFlow + + helper_method :election + + def questionnaire_for + election + end + + # where the questionnaire will be submitted. + def update_url + answer_election_feedback_path(election, hash: params[:hash], token: params[:token]) + end + + # Overwrites the 'after_answer_path' that gets passed to 'redirect_to' + # after answering the questionnaire. By default it redirects to the questionnaire_for. + def after_answer_path + if current_user.nil? + election_path(election, onboarding: true) + else + election_path(election) + end + end + + private + + def election + @election ||= Election.where(component: current_component).includes(:questionnaire).find(params[:election_id]) + end + + def allow_answers? + can_preview? || (election.ongoing? && valid_token?) + end + + def visitor_already_answered? + election.questionnaire.answered_by?(session_token) + end + + def i18n_flashes_scope + "decidim.elections.feedback" + end + + def enforce_permission_to_answer_questionnaire + can_preview? || valid_token? + end + + def allow_unregistered? + true + end + + def valid_token? + return @valid_token if defined?(@valid_token) + + @valid_token = vote_flow.voter_id_token(vote.voter_id) == session_token + end + + def session_token + @session_token ||= params[:token] + end + + def vote + @vote ||= Decidim::Elections::Vote.find_by(encrypted_vote_hash: params[:hash]) + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/trustee_zone/application_controller.rb b/decidim-elections/app/controllers/decidim/elections/trustee_zone/application_controller.rb new file mode 100644 index 00000000..86ac0681 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/trustee_zone/application_controller.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + # This controller is the abstract class from which all trustee_zone + # controllers in their public engines should inherit from. + + class ApplicationController < ::Decidim::ApplicationController + include Decidim::UserProfile + include Decidim::Elections::ContentSecurityPolicy + + helper_method :trustee + + before_action :ensure_configured_bulletin_board! + + register_permissions(::Decidim::Elections::TrusteeZone::ApplicationController, + ::Decidim::Elections::Permissions, + ::Decidim::Admin::Permissions, + ::Decidim::Permissions) + + private + + def ensure_configured_bulletin_board! + return if Decidim::Elections.bulletin_board.configured? + + announcement = { + title: "#{t("no_bulletin_board.title", scope: "decidim.elections.trustee_zone")}", + body: t("no_bulletin_board.body", scope: "decidim.elections.trustee_zone") + } + render html: cell("decidim/announcement", announcement, callout_class: "alert"), layout: true + end + + def trustee + @trustee ||= Decidim::Elections::Trustee.for(current_user) + end + + def permission_scope + :trustee_zone + end + + def permission_class_chain + ::Decidim.permissions_registry.chain_for(::Decidim::Elections::TrusteeZone::ApplicationController) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/trustee_zone/elections_controller.rb b/decidim-elections/app/controllers/decidim/elections/trustee_zone/elections_controller.rb new file mode 100644 index 00000000..b8d9a0e0 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/trustee_zone/elections_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + # Handles the KeyCeremony for trustee users + class ElectionsController < Decidim::Elections::TrusteeZone::ApplicationController + helper_method :election, :bulletin_board_server, :authority_slug, :authority_public_key, :current_step, :scheme_name + + def show + enforce_permission_to :view, :election, trustee: + end + + def update + enforce_permission_to(:update, :election, trustee:) + + UpdateElectionBulletinBoardStatus.call(election, params[:status]) do + on(:ok) do + render :update + end + on(:invalid) do + flash[:alert] = I18n.t("elections.update.error", scope: "decidim.elections.trustee_zone") + end + end + end + + private + + delegate :bulletin_board_server, :authority_slug, :scheme_name, to: :bulletin_board_client + + def election + @election ||= Decidim::Elections::Election.find(params[:election_id]) + end + + def bulletin_board_client + @bulletin_board_client ||= Decidim::Elections.bulletin_board + end + + def authority_public_key + @authority_public_key ||= bulletin_board_client.authority_public_key.to_json + end + + def current_step + @current_step ||= election.bb_status if election.bb_key_ceremony? || election.bb_tally_started? + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/trustee_zone/trustees_controller.rb b/decidim-elections/app/controllers/decidim/elections/trustee_zone/trustees_controller.rb new file mode 100644 index 00000000..eae2a478 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/trustee_zone/trustees_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + # Exposes the trustee zone for trustee users + class TrusteesController < Decidim::Elections::TrusteeZone::ApplicationController + include Decidim::FormFactory + + def show + enforce_permission_to(:view, :trustee, trustee:) + + trustee.name ||= current_user.name + end + + def update + enforce_permission_to(:update, :trustee, trustee:) + + form = form(TrusteeForm).from_params(params, trustee:) + + UpdateTrustee.call(form) do + on(:ok) do + flash[:notice] = I18n.t("trustees.update.success", scope: "decidim.elections.trustee_zone") + end + + on(:invalid) do + flash[:alert] = form.errors.full_messages.to_sentence + end + end + + redirect_to trustee_path + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/elections/votes_controller.rb b/decidim-elections/app/controllers/decidim/elections/votes_controller.rb new file mode 100644 index 00000000..5e4931f7 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/elections/votes_controller.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Exposes the elections resources so users can participate on them + class VotesController < Decidim::Elections::ApplicationController + WIZARD_STEPS = %w(register election confirm ballot_decision).freeze + + include FormFactory + include HasVoteFlow + + helper VotesHelper + helper_method :bulletin_board_server, :authority_public_key, :scheme_name, :election_unique_id, + :exit_path, :elections, :election, :questions, :questions_count, :vote, :valid_questionnaire?, + :wizard_steps + + delegate :count, to: :questions, prefix: true + + def new + vote_flow.voter_login(params) + return unless vote_allowed? + + @form = form(Voter::VoteForm).from_params({ voter_token:, voter_id: }, + election:, user: vote_flow.user) + end + + def create + vote_flow.voter_from_token(params.require(:vote).permit(:voter_token, :voter_id)) + return unless valid_voter_token? + return unless vote_allowed? + + return redirect_to election_vote_path(election, id: params[:vote][:encrypted_data_hash], token: vote_flow.voter_id_token) if preview_mode? + + @form = form(Voter::VoteForm).from_params(params, election:, user: vote_flow.user, email: vote_flow.email) + Voter::CastVote.call(@form) do + on(:ok) do |vote| + redirect_to election_vote_path(election, id: vote.encrypted_vote_hash, token: vote_flow.voter_id_token) + end + on(:invalid) do + flash[:alert] = I18n.t("votes.create.error", scope: "decidim.elections") + redirect_to exit_path + end + end + end + + def show + enforce_permission_to :view, :election, election: + end + + def update + enforce_permission_to(:view, :election, election:) + + Voter::UpdateVoteStatus.call(vote) do + on(:ok) do + redirect_to election_vote_path(election, id: vote.encrypted_vote_hash, token: vote_flow.voter_id_token(vote.voter_id)) + end + on(:invalid) do + flash[:alert] = I18n.t("votes.update.error", scope: "decidim.elections") + redirect_to exit_path + end + end + end + + def verify + enforce_permission_to(:view, :election, election:) + + @form = form(Voter::VerifyVoteForm).instance(election:) + end + + private + + delegate :bulletin_board_server, :scheme_name, to: :bulletin_board_client + + def election_unique_id + @election_unique_id ||= Decidim::BulletinBoard::MessageIdentifier.unique_election_id(bulletin_board_client.authority_slug, election.id) + end + + def vote + @vote ||= Decidim::Elections::Vote.find_by(election:, encrypted_vote_hash: params[:id]) if params[:id] + end + + def exit_path + @exit_path ||= if allowed_to?(:view, :election, election:) + election_path(election) + else + elections_path + end + end + + def pending_vote + @pending_vote ||= Decidim::Elections::Votes::PendingVotes.for.find_by(voter_id:, election:) + end + + def bulletin_board_client + @bulletin_board_client ||= Decidim::Elections.bulletin_board + end + + def authority_public_key + @authority_public_key ||= bulletin_board_client.authority_public_key.to_json + end + + def elections + @elections ||= Election.where(component: current_component) + end + + def election + @election ||= elections.find(params[:election_id]) + end + + def questions + @questions ||= ballot_questions.includes(:answers).order(weight: :asc, id: :asc) + end + + def vote_allowed? + if preview_mode? + return true if can_preview? + + redirect_to( + exit_path, + alert: t("votes.messages.not_allowed", + scope: "decidim.elections") + ) + return false + end + + if pending_vote.present? + redirect_to( + election_vote_path(election, + id: pending_vote.encrypted_vote_hash, + token: vote_flow.voter_id_token) + ) + return false + end + + vote_check_result = vote_flow.vote_check(online_vote_path: new_election_vote_path) + unless vote_check_result.allowed? + redirect_to( + vote_check_result.exit_path || exit_path, + alert: vote_check_result.error_message, + status: :temporary_redirect + ) + + return false + end + + enforce_permission_to(:vote, :election, election:) + + true + end + + def valid_voter_token? + return true if preview_mode? || vote_flow.valid_received_data? + + redirect_to(exit_path, alert: t("votes.messages.invalid_token", scope: "decidim.elections")) + end + + def valid_questionnaire? + return @valid_questionnaire if defined?(@valid_questionnaire) + + @valid_questionnaire = election.questionnaire.questions.any? + end + + def wizard_steps + WIZARD_STEPS + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/application_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/application_controller.rb new file mode 100644 index 00000000..dfb10f20 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/application_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # The main admin application controller for the voting space + class ApplicationController < Decidim::Admin::ApplicationController + register_permissions(::Decidim::Votings::Admin::ApplicationController, + Decidim::Votings::Admin::Permissions, + Decidim::Admin::Permissions) + + private + + def permissions_context + super.merge( + current_participatory_space: try(:current_participatory_space) + ) + end + + def permission_class_chain + ::Decidim.permissions_registry.chain_for(::Decidim::Votings::Admin::ApplicationController) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/ballot_styles_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/ballot_styles_controller.rb new file mode 100644 index 00000000..f0207f4c --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/ballot_styles_controller.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows to create or update the ballot styles. + class BallotStylesController < Admin::ApplicationController + include VotingAdmin + helper_method :ballot_styles, :current_voting, :voting_elections, :current_ballot_style + + def index + enforce_permission_to :read, :ballot_styles, voting: current_voting + end + + def new + enforce_permission_to :create, :ballot_style, voting: current_voting + @form = form(BallotStyleForm).instance + end + + def create + enforce_permission_to :create, :ballot_style, voting: current_voting + + @form = form(BallotStyleForm).from_params(params, voting: current_voting) + + CreateBallotStyle.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("create.success", scope: "decidim.votings.admin.ballot_styles") + redirect_to voting_ballot_styles_path(current_voting) + end + + on(:invalid) do + flash[:alert] = t("create.error", scope: "decidim.votings.admin.ballot_styles") + render action: "new" + end + end + end + + def edit + enforce_permission_to :update, :ballot_style, ballot_style: current_ballot_style, voting: current_voting + @form = form(BallotStyleForm).from_model(current_ballot_style, voting: current_voting) + end + + def update + enforce_permission_to :update, :ballot_style, ballot_style: current_ballot_style, voting: current_voting + @form = form(BallotStyleForm).from_params(params, voting: current_voting, ballot_style_id: current_ballot_style.id) + + UpdateBallotStyle.call(@form, current_ballot_style) do + on(:ok) do + flash[:notice] = I18n.t("update.success", scope: "decidim.votings.admin.ballot_styles") + redirect_to voting_ballot_styles_path(current_voting) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("update.invalid", scope: "decidim.votings.admin.ballot_styles") + render action: "edit" + end + end + end + + def destroy + enforce_permission_to :delete, :ballot_style, ballot_style: current_ballot_style, voting: current_voting + + DestroyBallotStyle.call(current_ballot_style, current_user) do + on(:ok) do + flash[:notice] = t("destroy.success", scope: "decidim.votings.admin.ballot_styles") + end + + on(:invalid) do + flash[:alert] = t("destroy.invalid", scope: "decidim.votings.admin.ballot_styles") + end + end + + redirect_to voting_ballot_styles_path(current_voting) + end + + private + + def current_ballot_style + @current_ballot_style ||= BallotStyle.find(params[:id]) + end + + def ballot_styles + current_voting.ballot_styles + end + + def voting_elections + election_components = current_voting.components.where(manifest_name: "elections") + Decidim::Elections::Election.where(component: election_components) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/component_permissions_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/component_permissions_controller.rb new file mode 100644 index 00000000..845c80d2 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/component_permissions_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows managing the Voting's Component + # permissions in the admin panel. + class ComponentPermissionsController < Decidim::Admin::ComponentPermissionsController + include VotingAdmin + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/components_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/components_controller.rb new file mode 100644 index 00000000..6b054fa6 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/components_controller.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows managing the Voting's Components in the + # admin panel. + class ComponentsController < Decidim::Admin::ComponentsController + layout "decidim/admin/voting" + + include VotingAdmin + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/exports_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/exports_controller.rb new file mode 100644 index 00000000..30a56fe2 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/exports_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows exporting things. + # It is targeted for customizations for exporting things that lives under + # a participatory process. + class ExportsController < Decidim::Admin::ExportsController + include VotingAdmin + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/imports_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/imports_controller.rb new file mode 100644 index 00000000..6c5f2dd1 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/imports_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows importing things. + # It is targeted for customizations for importing things that lives under + # a voting space. + class ImportsController < Decidim::Admin::ImportsController + include VotingAdmin + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_election_results_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_election_results_controller.rb new file mode 100644 index 00000000..86853ef9 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_election_results_controller.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows a monitoring committee member to list and validate the polling stations closures. + class MonitoringCommitteeElectionResultsController < Admin::ApplicationController + include VotingAdmin + + helper_method :current_voting, :elections, :election, :publish_results_form, :bulletin_board_server + + before_action :append_csp_directives + + def index + enforce_permission_to :read, :monitoring_committee_election_results, voting: current_voting + + redirect_to voting_monitoring_committee_election_result_path(current_voting, elections.first) if elections.one? + end + + def show + enforce_permission_to :read, :monitoring_committee_election_result, voting: current_voting, election: + end + + def update + enforce_permission_to(:validate, :monitoring_committee_election_result, voting: current_voting, election:) + + if publish_results_form.pending_action + Decidim::Elections::Admin::UpdateActionStatus.call(publish_results_form.pending_action) + + if publish_results_form.pending_action.accepted? + flash[:notice] = I18n.t("monitoring_committee_election_results.update.success", scope: "decidim.votings.admin") + else + flash[:alert] = I18n.t("monitoring_committee_election_results.update.rejected", scope: "decidim.votings.admin") + end + + return redirect_to voting_monitoring_committee_election_result_path(current_voting, election) + end + + Decidim::Elections::Admin::PublishResults.call(publish_results_form) do + on(:invalid) do + flash.now[:alert] = I18n.t("monitoring_committee_election_results.update.invalid", scope: "decidim.votings.admin") + end + on(:ok) do + publish_results_form.refresh + end + end + + render :show + end + + private + + def append_csp_directives + return if bulletin_board_server.blank? + + content_security_policy.append_csp_directive("connect-src", bulletin_board_server) + end + + def bulletin_board_server + Decidim::Elections.bulletin_board.bulletin_board_server + end + + def elections + @elections = current_voting.published_elections + end + + def election + @election = elections.find_by(id: params[:id]) + end + + def publish_results_form + @publish_results_form ||= form(Decidim::Votings::Admin::PublishResultsForm).from_params(params, election:) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_members_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_members_controller.rb new file mode 100644 index 00000000..4d9d3ec2 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_members_controller.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows to create or delete a monitoring committee member. + class MonitoringCommitteeMembersController < Admin::ApplicationController + include VotingAdmin + + before_action :set_monitoring_commiteee_breadcrumb_item + + helper_method :current_voting, :monitoring_committee_members, :monitoring_committee_member + + def index + enforce_permission_to :read, :monitoring_committee_members, voting: current_voting + end + + def new + enforce_permission_to :create, :monitoring_committee_member, voting: current_voting + @form = form(VotingUserRoleForm).instance + end + + def create + enforce_permission_to :create, :monitoring_committee_member, voting: current_voting + @form = form(VotingUserRoleForm).from_params(params, voting: current_voting) + + CreateMonitoringCommitteeMember.call(@form, current_user, current_voting) do + on(:ok) do + flash[:notice] = I18n.t("monitoring_committee_members.create.success", scope: "decidim.votings.admin") + redirect_to voting_monitoring_committee_members_path(current_voting) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("monitoring_committee_members.create.invalid", scope: "decidim.votings.admin") + render action: "new" + end + end + end + + def destroy + enforce_permission_to(:delete, :monitoring_committee_member, voting: current_voting, monitoring_committee_member:) + + DestroyMonitoringCommitteeMember.call(monitoring_committee_member, current_user) do + on(:ok) do + flash[:notice] = I18n.t("monitoring_committee_members.destroy.success", scope: "decidim.votings.admin") + end + + on(:invalid) do + flash.now[:alert] = I18n.t("monitoring_committee_members.destroy.invalid", scope: "decidim.votings.admin") + end + end + + redirect_to voting_monitoring_committee_members_path(current_voting) + end + + private + + def set_monitoring_commiteee_breadcrumb_item + controller_breadcrumb_items << { + label: I18n.t("monitoring_committee", scope: "decidim.votings.admin.menu.votings_submenu"), + active: false + } + + controller_breadcrumb_items << { + label: I18n.t("monitoring_committee_members", scope: "decidim.votings.admin.menu.votings_submenu"), + url: decidim_admin_votings.voting_monitoring_committee_members_path(current_participatory_space), + active: true + } + end + + def monitoring_committee_members + @monitoring_committee_members ||= current_voting.monitoring_committee_members + end + + def monitoring_committee_member + @monitoring_committee_member ||= monitoring_committee_members.find(params[:id]) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_polling_station_closures_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_polling_station_closures_controller.rb new file mode 100644 index 00000000..f8170ba6 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_polling_station_closures_controller.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows a monitoring committee member to list and validate the polling stations closures. + class MonitoringCommitteePollingStationClosuresController < Admin::ApplicationController + include VotingAdmin + include Decidim::MonitoringCommitteePollingStationClosures::Admin::Filterable + + helper_method :current_voting, :closure, :elections, :election, :filtered_polling_stations + + def index + enforce_permission_to :read, :monitoring_committee_polling_station_closures, voting: current_voting + + redirect_to voting_monitoring_committee_polling_station_closures_path(current_voting, election_id: elections.first.id) if elections.one? && params[:election_id].blank? + end + + def show + enforce_permission_to :read, :monitoring_committee_polling_station_closure, voting: current_voting, closure: + end + + def edit + enforce_permission_to(:validate, :monitoring_committee_polling_station_closure, voting: current_voting, closure:) + + @form = form(MonitoringCommitteePollingStationClosureForm).from_model(closure) + end + + def validate + enforce_permission_to(:validate, :monitoring_committee_polling_station_closure, voting: current_voting, closure:) + + @form = form(MonitoringCommitteePollingStationClosureForm).from_params(params) + + MonitoringCommitteeValidatePollingStationClosure.call(@form, closure) do + on(:ok) do + flash[:notice] = t(".success") + end + + on(:invalid) do + flash[:alert] = t(".error") + end + end + + redirect_to voting_monitoring_committee_polling_station_closures_path(current_voting, election_id: closure.election.id) + end + + private + + def polling_stations + @polling_stations ||= current_voting.polling_stations + end + + def elections + @elections = current_voting.published_elections + end + + def election + elections.find { |e| e.id == params[:election_id].to_i } if params[:election_id].present? + end + + def closure + @closure ||= Decidim::Votings::PollingStationClosure.find(params[:id]) + end + + def filtered_polling_stations + filtered_collection.distinct + end + + alias collection polling_stations + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_verify_elections_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_verify_elections_controller.rb new file mode 100644 index 00000000..a94905ee --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/monitoring_committee_verify_elections_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows a monitoring committee member to list and validate the polling stations closures. + class MonitoringCommitteeVerifyElectionsController < Admin::ApplicationController + include VotingAdmin + + helper_method :current_voting, :elections + + def index + enforce_permission_to :read, :monitoring_committee_verify_elections, voting: current_voting + end + + private + + def elections + @elections = current_voting.published_elections + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/polling_officers_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/polling_officers_controller.rb new file mode 100644 index 00000000..4f4ed4f8 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/polling_officers_controller.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows to create or update a polling officer. + class PollingOfficersController < Admin::ApplicationController + include Decidim::PollingOfficers::Admin::Filterable + include VotingAdmin + + helper_method :current_voting, :polling_officers, :polling_officer, :filtered_polling_officers + + def new + enforce_permission_to :create, :polling_officer, voting: current_voting + @form = form(VotingUserRoleForm).instance + end + + def create + enforce_permission_to :create, :polling_officer, voting: current_voting + @form = form(VotingUserRoleForm).from_params(params, voting: current_voting) + + CreatePollingOfficer.call(@form, current_user, current_voting) do + on(:ok) do + flash[:notice] = I18n.t("polling_officers.create.success", scope: "decidim.votings.admin") + redirect_to voting_polling_officers_path(current_voting) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("polling_officers.create.invalid", scope: "decidim.votings.admin") + render action: "new" + end + end + end + + def destroy + enforce_permission_to(:delete, :polling_officer, voting: current_voting, polling_officer:) + + DestroyPollingOfficer.call(polling_officer, current_user) do + on(:ok) do + flash[:notice] = I18n.t("polling_officers.destroy.success", scope: "decidim.votings.admin") + end + + on(:invalid) do + flash.now[:alert] = I18n.t("polling_officers.destroy.invalid", scope: "decidim.votings.admin") + end + end + + redirect_to voting_polling_officers_path(current_voting) + end + + private + + def polling_officers + @polling_officers ||= current_voting.polling_officers + end + + def polling_officer + @polling_officer ||= polling_officers.find(params[:id]) + end + + alias collection polling_officers + alias filtered_polling_officers filtered_collection + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/polling_stations_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/polling_stations_controller.rb new file mode 100644 index 00000000..d2fb8aee --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/polling_stations_controller.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows to create or update a polling station. + class PollingStationsController < Admin::ApplicationController + include Decidim::PollingStations::Admin::Filterable + include VotingAdmin + + helper_method :current_voting, :polling_station, :filtered_polling_stations + + def index + enforce_permission_to :read, :polling_stations, voting: current_voting + end + + def new + enforce_permission_to :create, :polling_station, voting: current_voting + @form = form(PollingStationForm).instance + end + + def create + enforce_permission_to :create, :polling_station, voting: current_voting + @form = form(PollingStationForm).from_params(params, voting: current_voting) + + CreatePollingStation.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("polling_stations.create.success", scope: "decidim.votings.admin") + redirect_to voting_polling_stations_path(current_voting) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("polling_stations.create.invalid", scope: "decidim.votings.admin") + render action: "new" + end + end + end + + def edit + enforce_permission_to(:update, :polling_station, voting: current_voting, polling_station:) + @form = form(PollingStationForm).from_model(polling_station, voting: current_voting) + end + + def update + enforce_permission_to(:update, :polling_station, voting: current_voting, polling_station:) + @form = form(PollingStationForm).from_params(params, voting: current_voting) + + UpdatePollingStation.call(@form, polling_station) do + on(:ok) do + flash[:notice] = I18n.t("polling_stations.update.success", scope: "decidim.votings.admin") + redirect_to voting_polling_stations_path(current_voting) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("polling_stations.update.invalid", scope: "decidim.votings.admin") + render action: "edit" + end + end + end + + def destroy + enforce_permission_to(:delete, :polling_station, voting: current_voting, polling_station:) + + DestroyPollingStation.call(polling_station, current_user) do + on(:ok) do + flash[:notice] = I18n.t("polling_stations.destroy.success", scope: "decidim.votings.admin") + end + + on(:invalid) do + flash.now[:alert] = I18n.t("polling_stations.destroy.invalid", scope: "decidim.votings.admin") + end + end + + redirect_to voting_polling_stations_path(current_voting) + end + + private + + def polling_stations + @polling_stations ||= current_voting.polling_stations + end + + def polling_station + @polling_station ||= polling_stations.find(params[:id]) + end + + def filtered_polling_stations + filtered_collection.distinct + end + + alias collection polling_stations + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/reminders_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/reminders_controller.rb new file mode 100644 index 00000000..f99b2775 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/reminders_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows to send reminders. + # It is targeted for customizations for reminder things that lives under + # votings. + class RemindersController < Decidim::Admin::RemindersController + include VotingAdmin + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/voting_attachment_collections_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/voting_attachment_collections_controller.rb new file mode 100644 index 00000000..b77eb09c --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/voting_attachment_collections_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows managing all the attachment collections for + # a voting. + class VotingAttachmentCollectionsController < Decidim::Admin::ApplicationController + include Decidim::Admin::Concerns::HasAttachmentCollections + include Decidim::Admin::Concerns::HasTabbedMenu + include VotingAdmin + + def after_destroy_path + voting_attachment_collections_path(current_voting) + end + + def collection_for + current_voting + end + + private + + def tab_menu_name = :votings_admin_attachments_menu + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/voting_attachments_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/voting_attachments_controller.rb new file mode 100644 index 00000000..7e14b37a --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/voting_attachments_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows managing all the attachments for a voting. + class VotingAttachmentsController < Decidim::Admin::ApplicationController + include VotingAdmin + include Decidim::Admin::Concerns::HasAttachments + include Decidim::Admin::Concerns::HasTabbedMenu + + def after_destroy_path + voting_attachments_path(current_voting) + end + + def attached_to + current_voting + end + + private + + def tab_menu_name = :votings_admin_attachments_menu + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/voting_publications_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/voting_publications_controller.rb new file mode 100644 index 00000000..6dd1a4b3 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/voting_publications_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows managing voting publications. + # + # i18n-tasks-use t('decidim.admin.voting_publications.create.error') + # i18n-tasks-use t('decidim.admin.voting_publications.create.success') + # i18n-tasks-use t('decidim.admin.voting_publications.destroy.error') + # i18n-tasks-use t('decidim.admin.voting_publications.destroy.success') + class VotingPublicationsController < Decidim::Admin::SpacePublicationsController + include VotingAdmin + + private + + def enforce_permission_to_publish = enforce_permission_to(:publish, :voting, voting: current_voting) + + def i18n_scope = "decidim.admin.voting_publications" + + def fallback_location = votings_path + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/votings_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/votings_controller.rb new file mode 100644 index 00000000..608e3770 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/votings_controller.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This controller allows to create or update a voting. + class VotingsController < Admin::ApplicationController + include Decidim::Votings::Admin::Filterable + include Decidim::Admin::ParticipatorySpaceAdminBreadcrumb + helper_method :votings, :current_voting, :current_participatory_space + + def index + enforce_permission_to :read, :votings + @votings = filtered_collection + render layout: "decidim/admin/votings" + end + + def new + enforce_permission_to :create, :voting + @form = form(VotingForm).instance + end + + def create + enforce_permission_to :create, :voting + @form = form(VotingForm).from_params(params) + + CreateVoting.call(@form) do + on(:ok) do + flash[:notice] = I18n.t("votings.create.success", scope: "decidim.votings.admin") + redirect_to votings_path + end + + on(:invalid) do + flash.now[:alert] = I18n.t("votings.create.invalid", scope: "decidim.votings.admin") + render action: "new" + end + end + end + + def edit + enforce_permission_to :edit, :voting, voting: current_voting + @form = form(VotingForm).from_model(current_voting) + render layout: "decidim/admin/voting" + end + + def update + enforce_permission_to :update, :voting, voting: current_voting + @form = form(VotingForm).from_params(params, voting_id: current_voting.id) + + UpdateVoting.call(current_voting, @form) do + on(:ok) do |voting| + flash[:notice] = I18n.t("votings.update.success", scope: "decidim.votings.admin") + redirect_to edit_voting_path(voting) + end + + on(:invalid) do + flash.now[:alert] = I18n.t("votings.update.invalid", scope: "decidim.votings.admin") + render action: "edit" + end + end + end + + def available_polling_officers + respond_to do |format| + format.json do + if (term = params[:term].to_s).present? + query = current_voting.available_polling_officers.joins(:user) + query = if term.start_with?("@") + query.where("nickname ILIKE ?", "#{term.delete("@")}%") + else + query.where("name ILIKE ?", "%#{term}%").or( + query.where("email ILIKE ?", "%#{term}%") + ) + end + render json: query.all.collect { |u| { value: u.id, label: "#{u.name} (@#{u.nickname}) #{u.email}" } } + else + render json: [] + end + end + end + end + + private + + def votings + @votings ||= OrganizationVotings.new(current_user.organization).query + end + + def current_voting + @current_voting ||= votings.where(slug: params[:slug]).or( + votings.where(id: params[:slug]) + ).first + end + + alias collection votings + alias current_participatory_space current_voting + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_content_blocks_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_content_blocks_controller.rb new file mode 100644 index 00000000..9f1bf89e --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_content_blocks_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows to edit the content of the landing page content blocks + class VotingsLandingPageContentBlocksController < Decidim::Votings::Admin::ApplicationController + include Decidim::Admin::ContentBlocks::LandingPageContentBlocks + + layout "decidim/admin/voting" + + helper_method :current_participatory_space + + private + + def content_block_scope + :voting_landing_page + end + + def scoped_resource + @scoped_resource ||= Voting.find_by(slug: params[:voting_slug], organization: current_organization) + end + + def enforce_permission_to_update_resource + enforce_permission_to :manage_landing_page, :voting, voting: current_participatory_space + end + + def edit_resource_landing_page_path + edit_voting_landing_page_path(scoped_resource) + end + + def resource_landing_page_content_block_path + voting_landing_page_content_block_path(scoped_resource, params[:id]) + end + + alias current_participatory_space scoped_resource + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_controller.rb b/decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_controller.rb new file mode 100644 index 00000000..28f981a4 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/admin/votings_landing_page_controller.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # Controller that allows to (de)activate the content blocks from a voting landing page + class VotingsLandingPageController < Decidim::Votings::Admin::ApplicationController + include Decidim::Admin::ContentBlocks::LandingPage + include Decidim::Admin::ParticipatorySpaceAdminBreadcrumb + + layout "decidim/admin/voting" + + helper_method :current_participatory_space + + add_breadcrumb_item_from_menu :admin_voting_menu + + def content_block_scope + :voting_landing_page + end + + def scoped_resource + @scoped_resource ||= Voting.find_by(slug: params[:voting_slug], organization: current_organization) + end + + def enforce_permission_to_update_resource + enforce_permission_to :manage_landing_page, :voting, voting: scoped_resource + end + + def resource_sort_url + voting_landing_page_path(scoped_resource) + end + + def resource_create_url(manifest_name) + voting_landing_page_content_blocks_path(participatory_process_group_id: params[:participatory_process_group_id], + manifest_name:) + end + + def resource_content_block_cell + "decidim/votings/content_block" + end + + alias current_participatory_space scoped_resource + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/application_controller.rb b/decidim-elections/app/controllers/decidim/votings/application_controller.rb new file mode 100644 index 00000000..7cdd6daa --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/application_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # The main application controller for votings + # + # This controller is the abstract class from which all other controllers of + # this engine inherit. + class ApplicationController < Decidim::ApplicationController + include NeedsPermission + + register_permissions(::Decidim::Votings::ApplicationController, + Decidim::Votings::Permissions, + Decidim::Admin::Permissions, + Decidim::Permissions) + + private + + def permission_class_chain + ::Decidim.permissions_registry.chain_for(::Decidim::Votings::ApplicationController) + end + + def permission_scope + :public + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/census/admin/application_controller.rb b/decidim-elections/app/controllers/decidim/votings/census/admin/application_controller.rb new file mode 100644 index 00000000..323c0332 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/census/admin/application_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # The main admin application controller for the voting census + class ApplicationController < Decidim::Votings::Admin::ApplicationController + layout "decidim/admin/voting" + include Decidim::Votings::Admin::VotingAdmin + + def decidim_votings_admin + Decidim::Votings::AdminEngine.routes.url_helpers + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/census/admin/census_controller.rb b/decidim-elections/app/controllers/decidim/votings/census/admin/census_controller.rb new file mode 100644 index 00000000..df273ed5 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/census/admin/census_controller.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # This controller allows to create or update the census. + class CensusController < Admin::ApplicationController + helper_method :votings, :current_participatory_space, :current_census, :census_steps, :current_census_action_view, + :user_email, :ballot_style_callout_text, :ballot_style_callout_level, :ballot_style_code_header + helper_method :admin_voting_census_path, :admin_status_voting_census_path, :generate_access_codes_path, :export_access_codes_path, :waiting_status? + + def show + enforce_permission_to :manage, :census, voting: current_participatory_space + + flash[:notice] = "Finished processing #{current_census.filename}" if current_census.data_created? + + @form = form(DatasetForm).instance + end + + def create + enforce_permission_to :manage, :census, voting: current_participatory_space + + @form = form(DatasetForm).from_params(params).with_context( + current_participatory_space: + ) + + CreateDataset.call(@form, current_user) do + on(:invalid_csv_header) do + flash[:alert] = t("create.invalid_csv_header", scope: "decidim.votings.census.admin.census") + end + + on(:invalid) do + flash[:alert] = t("create.invalid", scope: "decidim.votings.census.admin.census") + end + end + + redirect_to admin_voting_census_path + end + + def destroy + enforce_permission_to :manage, :census, voting: current_participatory_space + + DestroyDataset.call(current_census, current_user) do + on(:ok) do + flash[:notice] = t("destroy.success", scope: "decidim.votings.census.admin.census") + end + + on(:invalid) do + flash[:alert] = t("destroy.error", scope: "decidim.votings.census.admin.census") + end + end + + redirect_to admin_voting_census_path + end + + def status + respond_to do |format| + format.js + end + end + + def generate_access_codes + enforce_permission_to :manage, :census, voting: current_participatory_space + + LaunchAccessCodesGeneration.call(current_census, current_user) do + on(:ok) do + flash[:notice] = t("generate_access_codes.launch_success", scope: "decidim.votings.census.admin.census") + end + + on(:invalid) do + flash[:alert] = t("generate_access_codes.launch_error", scope: "decidim.votings.census.admin.census") + end + end + + redirect_to admin_voting_census_path + end + + def export_access_codes + enforce_permission_to :manage, :census, voting: current_participatory_space + + LaunchAccessCodesExport.call(current_census, current_user) do + on(:ok) do + flash[:notice] = t("export_access_codes.launch_success", scope: "decidim.votings.census.admin.census", email: current_user.email) + end + + on(:invalid) do + flash[:alert] = t("export_access_codes.launch_error", scope: "decidim.votings.census.admin.census") + end + end + + redirect_to admin_voting_census_path + end + + def download_access_codes_file + enforce_permission_to :manage, :census, voting: current_participatory_space + + if current_census.access_codes_file.attached? + redirect_to Rails.application.routes.url_helpers.rails_blob_url(current_census.access_codes_file.blob, only_path: true) + else + flash[:error] = t("export_access_codes.file_not_exist", scope: "decidim.votings.census.admin.census") + redirect_to admin_voting_census_path + end + end + + private + + def votings + @votings ||= OrganizationVotings.new(current_user.organization).query + end + + def current_participatory_space + @current_participatory_space ||= votings.where(slug: params[:voting_slug]).or( + votings.where(id: params[:voting_slug]) + ).first + end + + def current_census + @current_census ||= Dataset.find_by( + voting: current_participatory_space + ) || Dataset.new(status: :init_data) + end + + def admin_voting_census_path + decidim_votings_admin.voting_census_path(current_participatory_space) + end + + def admin_status_voting_census_path + decidim_votings_admin.status_voting_census_path(current_participatory_space) + end + + def generate_access_codes_path + decidim_votings_admin.generate_access_codes_voting_census_path(current_participatory_space) + end + + def export_access_codes_path + decidim_votings_admin.export_access_codes_voting_census_path(current_participatory_space) + end + + def current_census_action_view + if current_census.init_data? + "new_census" + elsif current_census.creating_data? + "creating_data" + elsif current_census.data_created? + "generate_codes" + elsif current_census.generating_codes? + "generating_codes" + elsif current_census.codes_generated? + "export_codes" + elsif current_census.exporting_codes? + "exporting_codes" + elsif current_census.freeze? + "freeze" + else + raise "no view for this status" + end + end + + def user_email + current_user.email + end + + def ballot_style_callout_text + if current_participatory_space.has_ballot_styles? + t("has_ballot_styles_message", scope: "decidim.votings.census.admin.census.new", ballot_style_code_header:) + else + t("missing_ballot_styles_message", scope: "decidim.votings.census.admin.census.new", ballot_styles_admin_path: admin_voting_ballot_styles_path) + end + end + + def ballot_style_callout_level + if current_participatory_space.has_ballot_styles? + "warning" + else + "alert" + end + end + + def admin_voting_ballot_styles_path + decidim_votings_admin.voting_ballot_styles_path(current_participatory_space) + end + + def ballot_style_code_header + "Ballot Style Code" + end + + def waiting_status? + current_census.creating_data? || current_census.generating_codes? || current_census.exporting_codes? + end + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/application_controller.rb b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/application_controller.rb new file mode 100644 index 00000000..539d0b18 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/application_controller.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module PollingOfficerZone + # This controller is the abstract class from which all polling_officer_zone + # controllers in their public engines should inherit from. + + class ApplicationController < ::Decidim::ApplicationController + include Decidim::UserProfile + + helper_method :polling_officers + + private + + def polling_officers + @polling_officers ||= Decidim::Votings::PollingOfficer.for(current_user) + end + + def permission_scope + :polling_officer_zone + end + + def permission_class_chain + [ + Decidim::Votings::Permissions, + Decidim::Permissions + ] + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/closures_controller.rb b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/closures_controller.rb new file mode 100644 index 00000000..5a96a6d3 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/closures_controller.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module PollingOfficerZone + # Space to manage the election closure of a Polling Station + class ClosuresController < Decidim::Votings::PollingOfficerZone::ApplicationController + helper_method :election, :polling_officer, :polling_station, :closure + + def show + enforce_permission_to(:manage, :polling_station_results, polling_officer:) + + @form = if closure.certificate_phase? + form(ClosureCertifyForm).from_model(closure) + elsif closure.signature_phase? + form(ClosureSignForm).instance + end + end + + def new + enforce_permission_to(:create, :polling_station_results, polling_officer:, closure:) + + @form = EnvelopesResultForm.new( + polling_station_id: polling_station.id, + election_id: election.id, + election_votes_count: polling_station_election_votes_count + ) + end + + def create + enforce_permission_to(:create, :polling_station_results, polling_officer:, closure:) + @form = form(EnvelopesResultForm).from_params(params).with_context(polling_officer:) + + CreatePollingStationClosure.call(@form) do + on(:ok) do + flash[:notice] = t(".success") + redirect_to edit_polling_officer_election_closure_path(polling_officer, election) + end + + on(:invalid) do + flash[:alert] = t(".error") + + render :new + end + end + end + + def edit + enforce_permission_to(:edit, :polling_station_results, polling_officer:, closure:) + @form = form(ClosureResultForm).from_model(closure) + end + + def update + enforce_permission_to(:edit, :polling_station_results, polling_officer:, closure:) + @form = form(ClosureResultForm).from_params(params) + CreatePollingStationResults.call(@form, closure) do + on(:ok) do + flash[:notice] = t(".success") + redirect_to polling_officer_election_closure_path(polling_officer, election) + end + + on(:invalid) do + flash.now[:alert] = t(".error") + + render :edit + end + end + end + + def destroy + enforce_permission_to(:edit, :polling_station_results, polling_officer:, closure:) + + DestroyPollingStationClosure.call(closure, current_user) do + on(:ok) do + flash[:notice] = t(".success") + end + + on(:invalid) do + flash.now[:alert] = t(".error") + end + end + + redirect_to polling_officers_path + end + + def certify + enforce_permission_to(:edit, :polling_station_results, polling_officer:, closure:) + + @form = form(ClosureCertifyForm).from_params(params).with_context(closure:) + + CertifyPollingStationClosure.call(@form, closure) do + on(:ok) do + flash[:notice] = t(".success") + end + + on(:invalid) do + flash[:alert] = t(".error") + end + end + + redirect_to polling_officer_election_closure_path(polling_officer, election) + end + + def sign + enforce_permission_to(:edit, :polling_station_results, polling_officer:, closure:) + + @form = form(ClosureSignForm).from_params(params) + + SignPollingStationClosure.call(@form, closure) do + on(:ok) do + flash[:notice] = t(".success") + end + + on(:invalid) do + flash[:alert] = t(".error") + end + end + + redirect_to polling_officer_election_closure_path(polling_officer, election) + end + + private + + def polling_officer + @polling_officer ||= polling_officers.find_by(id: params[:polling_officer_id]) + end + + def election + @election ||= Decidim::Elections::Election.joins(:component) + .where(component: { participatory_space: current_organization.participatory_spaces }) + .includes(questions: :answers) + .find_by(id: params[:election_id]) + end + + def polling_station + @polling_station ||= polling_officer.polling_station + end + + def closure + @closure ||= polling_station.closures.find_by(election:) + end + + def polling_station_election_votes_count + @polling_station_election_votes_count ||= polling_station.in_person_votes.where(election:).count + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/in_person_votes_controller.rb b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/in_person_votes_controller.rb new file mode 100644 index 00000000..3a05d8a9 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/in_person_votes_controller.rb @@ -0,0 +1,163 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module PollingOfficerZone + # Space to manage the elections for a Polling Station Officer + class InPersonVotesController < Decidim::Votings::PollingOfficerZone::ApplicationController + include FormFactory + include Decidim::Elections::HasVoteFlow + before_action :append_csp_directives + + helper_method :polling_officer, :election, :in_person_form, :has_voter?, + :vote_check, :cant_vote_reason, :questions, :in_person_vote_form, :voted_online?, + :in_person_vote, :bulletin_board_server, :exit_path + + helper Decidim::Admin::IconLinkHelper + + def new + enforce_permission_to(:create, :in_person_vote, polling_officer:, closure:) + + has_pending_in_person_vote? + end + + def create + enforce_permission_to(:create, :in_person_vote, polling_officer:, closure:) + + return if has_pending_in_person_vote? + + if verified_voter? + store_in_person_vote + else + identify_voter + end + end + + def show + enforce_permission_to :manage, :in_person_vote, polling_officer:, closure: + end + + def update + enforce_permission_to(:manage, :in_person_vote, polling_officer:, closure:) + + Decidim::Votings::Voter::UpdateInPersonVoteStatus.call(in_person_vote) do + on(:ok) do + message_type = in_person_vote.accepted? ? :notice : :alert + flash[message_type] = I18n.t("in_person_votes.update.success.#{in_person_vote.status}", scope: "decidim.votings.polling_officer_zone") + end + on(:invalid) do + flash[:alert] = I18n.t("in_person_votes.update.error", scope: "decidim.votings.polling_officer_zone") + end + end + redirect_to exit_path + end + + private + + attr_reader :cant_vote_reason + + delegate :bulletin_board_server, to: :bulletin_board_client + delegate :has_voter?, to: :vote_flow + delegate :polling_station, to: :polling_officer + + def append_csp_directives + return if bulletin_board_server.blank? + + content_security_policy.append_csp_directive("connect-src", bulletin_board_server) + end + + def bulletin_board_client + @bulletin_board_client ||= Decidim::Elections.bulletin_board + end + + def has_pending_in_person_vote? + if pending_in_person_vote.present? + redirect_to(polling_officer_election_in_person_vote_path(polling_officer, election, id: pending_in_person_vote.id)) + true + end + end + + def identify_voter + vote_flow.voter_in_person(params) if in_person_form.valid? + + render :new + end + + def vote_check + @vote_check ||= vote_flow.vote_check + end + + def verified_voter? + params[:in_person_vote] && + vote_flow.voter_from_token(params.require(:in_person_vote).permit(:voter_token, :voter_id)) + end + + def store_in_person_vote + return redirect_to exit_path, alert: vote_check.error_message unless vote_check.allowed? + + Decidim::Votings::Voter::InPersonVote.call(in_person_vote_form) do + on(:ok) do |created_in_person_vote| + redirect_to polling_officer_election_in_person_vote_path(polling_officer, election, created_in_person_vote) + end + on(:invalid) do + flash[:alert] = I18n.t("in_person_votes.create.error", scope: "decidim.votings.polling_officer_zone") + redirect_to exit_path + end + end + end + + def in_person_form + @in_person_form ||= form(Decidim::Votings::Census::InPersonForm).from_params(params) + end + + def in_person_vote_form + @in_person_vote_form ||= form(Decidim::Votings::Voter::InPersonVoteForm).from_params( + { + voter_token:, + voter_id:, + voted: params.dig(:in_person_vote, :voted) + }, + election:, + polling_station:, + polling_officer: + ) + end + + def voted_online? + Decidim::Elections::Votes::LastVoteForVoter.for(election, vote_flow.voter_id) if vote_flow.has_voter? + end + + def election + @election ||= Decidim::Elections::Election.joins(:component) + .where(component: { participatory_space: current_organization.participatory_spaces }) + .includes(questions: :answers) + .find_by(id: params[:election_id]) + end + + def polling_officer + @polling_officer ||= Decidim::Votings::PollingOfficer.find(params[:polling_officer_id]) + end + + def questions + @questions ||= ballot_questions.includes(:answers).order(weight: :asc, id: :asc) + end + + def in_person_vote + @in_person_vote ||= Decidim::Votings::InPersonVote.find_by(id: params[:id]) if params[:id] + end + + def pending_in_person_vote + @pending_in_person_vote ||= Decidim::Votings::Votes::PendingInPersonVotes.for.find_by(polling_officer:, election:) + end + + def closure + @closure ||= polling_station.closures.find_by(election:) + end + + def exit_path + @exit_path ||= new_polling_officer_election_in_person_vote_path(polling_officer, election) + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/polling_officers_controller.rb b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/polling_officers_controller.rb new file mode 100644 index 00000000..740c9069 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/polling_officer_zone/polling_officers_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module PollingOfficerZone + # Exposes the polling officer zone for polling officer users + class PollingOfficersController < Decidim::Votings::PollingOfficerZone::ApplicationController + helper Decidim::Admin::IconLinkHelper + helper Decidim::ResourceHelper + + helper_method :polling_stations, :polling_officers_elections + + def index + enforce_permission_to :view, :polling_officers, polling_officers: + end + + private + + def polling_officers_elections + @polling_officers_elections ||= polling_officers.flat_map { |polling_officer| polling_officer.voting.published_elections } + end + end + end + end +end diff --git a/decidim-elections/app/controllers/decidim/votings/votings_controller.rb b/decidim-elections/app/controllers/decidim/votings/votings_controller.rb new file mode 100644 index 00000000..7038ce96 --- /dev/null +++ b/decidim-elections/app/controllers/decidim/votings/votings_controller.rb @@ -0,0 +1,180 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A controller that holds the logic to show votings in a + # public layout. + class VotingsController < Decidim::Votings::ApplicationController + include FormFactory + include ParticipatorySpaceContext + include NeedsVoting + include FilterResource + include Paginable + include Decidim::Votings::Orderable + include Decidim::Elections::HasVoteFlow + + helper_method :published_votings, :paginated_votings, :filter, :promoted_votings, :only_finished_votings?, :landing_content_blocks, :census_contact_information + + helper Decidim::FiltersHelper + helper Decidim::OrdersHelper + helper Decidim::SanitizeHelper + helper Decidim::PaginateHelper + helper Decidim::IconHelper + helper Decidim::ResourceHelper + helper Decidim::Admin::IconLinkHelper + + def index + raise ActionController::RoutingError, "Not Found" if published_votings.none? + + enforce_permission_to :read, :votings + redirect_to voting_path(single) if single? + end + + def show + raise ActionController::RoutingError, "Not Found" unless current_participatory_space + + enforce_permission_to :read, :voting, voting: current_participatory_space + end + + helper_method :election, :exit_path, :election_log_path, :elections + + def login + @form = form(Census::LoginForm).from_params(params, election:) + + render :login + end + + def show_check_census + raise ActionController::RoutingError, "Not Found" unless current_participatory_space.check_census_enabled? + + @form = form(Census::CheckForm).instance + render :check_census, locals: { success: false, not_found: false } + end + + def check_census + raise ActionController::RoutingError, "Not Found" unless current_participatory_space.check_census_enabled? + + @form = form(Census::CheckForm).from_params(params).with_context( + current_participatory_space: + ) + + success = not_found = false + datum = nil + CheckCensus.call(@form) do + on(:ok) do |census| + success = true + datum = census + end + on(:not_found) do + not_found = true + end + on(:invalid) do + flash[:alert] = t("check_census.invalid", scope: "decidim.votings.votings") + end + end + + render action: :check_census, locals: { success:, not_found:, datum: } + end + + def send_access_code + SendAccessCode.call(datum, params[:medium]) do + on(:ok) do + flash[:notice] = t("send_access_code.success", scope: "decidim.votings.votings") + end + on(:invalid) do + flash[:alert] = t("send_access_code.invalid", scope: "decidim.votings.votings") + end + end + render action: :check_census, locals: { success: true, not_found: false, datum: } + end + + def elections_log + redirect_to election_log_path(elections.first) if elections.length == 1 + end + + private + + def datum + @datum ||= Decidim::Votings::Census::Datum.find(params[:datum_id]) + end + + def election + @election ||= Decidim::Elections::Election.where(component: current_participatory_space.components).find(params[:election_id]) + end + + def elections + raise ActionController::RoutingError, "Not Found" unless current_participatory_space + + Decidim::Elections::Election.where(component: current_participatory_space.components).where.not(bb_status: nil) + end + + def exit_path + EngineRouter.main_proxy(election.component).election_path(election) + end + + def election_log_path(election) + EngineRouter.main_proxy(election.component).election_log_election_path(election) + end + + def census_contact_information + @census_contact_information ||= current_participatory_space.census_contact_information.presence || t("no_census_contact_information", scope: "decidim.votings.votings") + end + + def current_participatory_space_manifest + @current_participatory_space_manifest ||= Decidim.find_participatory_space_manifest(:votings) + end + + def published_votings + @published_votings ||= Voting.where(organization: current_organization).published + end + + def paginated_votings + @paginated_votings ||= reorder(search.result.published) + @paginated_votings = paginate(@paginated_votings) + end + + def promoted_votings + @promoted_votings ||= OrganizationPromotedVotings.new(current_organization) + end + + def finished_votings + @finished_votings ||= search_with(filter_params.merge(with_any_date: %w(finished))).result + end + + def only_finished_votings? + return if finished_votings.blank? + + published_votings.count == finished_votings.count + end + + def search_collection + Voting.where(organization: current_organization).published + end + + def default_filter_params + { + search_text_cont: "", + with_any_date: [""] + } + end + + # Public: Checks if the component has only one election resource. + # + # Returns Boolean. + def single? + published_votings.one? + end + + def single + published_votings.first if single? + end + + def landing_content_blocks + @landing_content_blocks ||= Decidim::ContentBlock.published + .for_scope(:voting_landing_page, organization: current_organization) + .where(scoped_resource_id: current_participatory_space.id) + .reject { |content_block| content_block.manifest.nil? } + end + end + end +end diff --git a/decidim-elections/app/events/decidim/elections/election_published_event.rb b/decidim-elections/app/events/decidim/elections/election_published_event.rb new file mode 100644 index 00000000..08ec5b95 --- /dev/null +++ b/decidim-elections/app/events/decidim/elections/election_published_event.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Decidim + module Elections + class ElectionPublishedEvent < Decidim::Events::SimpleEvent + end + end +end diff --git a/decidim-elections/app/events/decidim/elections/trustees/notify_new_trustee_event.rb b/decidim-elections/app/events/decidim/elections/trustees/notify_new_trustee_event.rb new file mode 100644 index 00000000..902eef32 --- /dev/null +++ b/decidim-elections/app/events/decidim/elections/trustees/notify_new_trustee_event.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Trustees + class NotifyNewTrusteeEvent < Decidim::Events::SimpleEvent + # This event sends a notification when a new trustee gets created. + + delegate :organization, to: :user, prefix: false + delegate :url_helpers, to: "Decidim::Core::Engine.routes" + + i18n_attributes :resource_name, :trustee_zone_url + + def resource_name + @resource_name ||= translated_attribute(participatory_space.title) + end + + def participatory_space + @participatory_space ||= resource + end + + def trustee_zone_url + url_helpers.decidim_elections_trustee_zone_url(host: organization.host) + end + end + end + end +end diff --git a/decidim-elections/app/events/decidim/elections/trustees/notify_trustee_new_election_event.rb b/decidim-elections/app/events/decidim/elections/trustees/notify_trustee_new_election_event.rb new file mode 100644 index 00000000..79dbf645 --- /dev/null +++ b/decidim-elections/app/events/decidim/elections/trustees/notify_trustee_new_election_event.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Trustees + class NotifyTrusteeNewElectionEvent < Decidim::Events::SimpleEvent + # This event sends a notification when a new trustee gets an election. + def resource_name + @resource_name ||= translated_attribute(election.title) + end + end + end + end +end diff --git a/decidim-elections/app/events/decidim/elections/trustees/notify_trustee_tally_process_event.rb b/decidim-elections/app/events/decidim/elections/trustees/notify_trustee_tally_process_event.rb new file mode 100644 index 00000000..5e4e79bf --- /dev/null +++ b/decidim-elections/app/events/decidim/elections/trustees/notify_trustee_tally_process_event.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Trustees + class NotifyTrusteeTallyProcessEvent < Decidim::Events::SimpleEvent + # This event sends a notification when an an admin started the tally process. + def resource_name + @resource_name ||= translated_attribute(election.title) + end + end + end + end +end diff --git a/decidim-elections/app/events/decidim/elections/votes/vote_accepted_event.rb b/decidim-elections/app/events/decidim/elections/votes/vote_accepted_event.rb new file mode 100644 index 00000000..ef1a3db4 --- /dev/null +++ b/decidim-elections/app/events/decidim/elections/votes/vote_accepted_event.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Votes + class VoteAcceptedEvent < Decidim::Events::SimpleEvent + # This event sends a notification with the vote hash and further instructions + i18n_attributes :resource_name, :encrypted_vote_hash, :verify_url + + def resource_name + @resource_name ||= translated_attribute(resource.title) + end + + def encrypted_vote_hash + extra[:vote]["encrypted_vote_hash"] + end + + def verify_url + extra[:verify_url] + end + end + end + end +end diff --git a/decidim-elections/app/events/decidim/votings/polling_officers/polling_station_assigned_event.rb b/decidim-elections/app/events/decidim/votings/polling_officers/polling_station_assigned_event.rb new file mode 100644 index 00000000..43c83cee --- /dev/null +++ b/decidim-elections/app/events/decidim/votings/polling_officers/polling_station_assigned_event.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module PollingOfficers + class PollingStationAssignedEvent < Decidim::Events::SimpleEvent + # This event sends a notification when a polling station is assigned to a polling officer + + delegate :organization, to: :user, prefix: false + delegate :url_helpers, to: "Decidim::Core::Engine.routes" + + i18n_attributes :polling_station_name, :polling_officer_zone_url, :role + + def polling_station_name + @polling_station_name ||= translated_attribute(polling_station.title) + end + + def polling_officer_zone_url + url_helpers.decidim_votings_polling_officer_zone_url(host: organization.host) + end + + def role + I18n.t(polling_officer.role, scope: "decidim.votings.polling_officers.roles") + end + + private + + def polling_officer + @polling_officer ||= Decidim::Votings::PollingOfficer.find_by(id: extra[:polling_officer_id]) + end + + def polling_station + @polling_station ||= + case polling_officer.role + when :president + polling_officer.presided_polling_station + when :manager + polling_officer.managed_polling_station + end + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/action_form.rb b/decidim-elections/app/forms/decidim/elections/admin/action_form.rb new file mode 100644 index 00000000..e2b050ee --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/action_form.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a form to perform an action on the BB. + class ActionForm < Decidim::Form + validates :pending_action, absence: true + + def main_button? + true + end + + def messages + @messages ||= {} + end + + def current_step + @current_step ||= election.bb_status + end + + def election + @election ||= context[:election] + end + + def pending_action + return @pending_action if defined?(@pending_action) + + @pending_action = election.actions.pending.first + end + + def bulletin_board + @bulletin_board ||= context[:bulletin_board] || Decidim::Elections.bulletin_board + end + + def refresh + remove_instance_variable(:@pending_action) + remove_instance_variable(:@current_step) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/answer_form.rb b/decidim-elections/app/forms/decidim/elections/admin/answer_form.rb new file mode 100644 index 00000000..aa375d1d --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/answer_form.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a Form to create/update answers from Decidim's admin panel. + class AnswerForm < Decidim::Form + include TranslatableAttributes + include AttachmentAttributes + + translatable_attribute :title, String + translatable_attribute :description, String + attribute :proposal_ids, Array[Integer] + attribute :attachment, AttachmentForm + attribute :weight, Integer, default: 0 + + attachments_attribute :photos + + validates :title, translatable_presence: true + validate :notify_missing_attachment_if_errored + + def map_model(model) + self.proposal_ids = model.linked_resources(:proposals, "related_proposals").pluck(:id) + end + + def proposals + @proposals ||= Decidim.find_resource_manifest(:proposals) + .try(:resource_scope, current_component) + &.where(id: proposal_ids) + &.order(title: :asc) + end + + def election + @election ||= context[:election] + end + + def question + @question ||= context[:question] + end + + private + + # This method will add an error to the `attachment` field only if there is + # any error in any other field. This is needed because when the form has + # an error, the attachment is lost, so we need a way to inform the user of + # this problem. + def notify_missing_attachment_if_errored + errors.add(:attachment, :needs_to_be_reattached) if errors.any? && attachment.present? + errors.add(:add_photos, :needs_to_be_reattached) if errors.any? && add_photos.present? + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/answer_import_proposals_form.rb b/decidim-elections/app/forms/decidim/elections/admin/answer_import_proposals_form.rb new file mode 100644 index 00000000..6ed4a1fe --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/answer_import_proposals_form.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # A form object to be used when admin users want to import a collection of proposals + # from another component into answers resource. + class AnswerImportProposalsForm < Decidim::Form + mimic :proposals_import + + attribute :origin_component_id, Integer + attribute :import_all_accepted_proposals, Boolean + attribute :weight, Integer, default: 0 + + validates :origin_component_id, :origin_component, :current_component, presence: true + + def origin_component + @origin_component ||= origin_components.find_by(id: origin_component_id) + end + + def origin_components + @origin_components ||= current_participatory_space.components.where(manifest_name: :proposals) + end + + def origin_components_collection + origin_components.map do |component| + [component.name[I18n.locale.to_s], component.id] + end + end + + def election + @election ||= context[:election] + end + + def question + @question ||= context[:question] + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/election_form.rb b/decidim-elections/app/forms/decidim/elections/admin/election_form.rb new file mode 100644 index 00000000..357dc20e --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/election_form.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a Form to create/update elections from Decidim's admin panel. + class ElectionForm < Decidim::Form + include TranslatableAttributes + include AttachmentAttributes + + translatable_attribute :title, String + translatable_attribute :description, String + attribute :start_time, Decidim::Attributes::TimeWithZone + attribute :end_time, Decidim::Attributes::TimeWithZone + attribute :attachment, AttachmentForm + + attachments_attribute :photos + + validates :title, translatable_presence: true + validates :description, translatable_presence: true + validates :start_time, presence: true, date: { before: :end_time } + validates :end_time, presence: true, date: { after: :start_time } + validate :notify_missing_attachment_if_errored + + private + + # This method will add an error to the `photos` field only if there is + # any error in any other field. This is needed because when the form has + # an error, the attachment is lost, so we need a way to inform the user of + # this problem. + def notify_missing_attachment_if_errored + errors.add(:attachment, :needs_to_be_reattached) if errors.any? && attachment.present? + errors.add(:add_photos, :needs_to_be_reattached) if errors.any? && add_photos.present? + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/question_form.rb b/decidim-elections/app/forms/decidim/elections/admin/question_form.rb new file mode 100644 index 00000000..84b7d211 --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/question_form.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a Form to create/update questions from Decidim's admin panel. + class QuestionForm < Decidim::Form + include TranslatableAttributes + + translatable_attribute :title, String + attribute :max_selections, Integer, default: 1 + attribute :weight, Integer, default: 0 + attribute :random_answers_order, Boolean, default: true + attribute :min_selections, Integer, default: 1 + + validates :title, translatable_presence: true + validates :max_selections, presence: true, numericality: { greater_than_or_equal_to: 1 } + + def election + @election ||= context[:election] + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/report_missing_trustee_form.rb b/decidim-elections/app/forms/decidim/elections/admin/report_missing_trustee_form.rb new file mode 100644 index 00000000..9aed1587 --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/report_missing_trustee_form.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a form to report a missing trustee during the tally process. + class ReportMissingTrusteeForm < ActionForm + attribute :trustee_id, Integer + + validates :trustee_id, presence: true + + def trustee + @trustee ||= Decidim::Elections::Trustee.find(trustee_id) + end + + def main_button? + false + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/setup_form.rb b/decidim-elections/app/forms/decidim/elections/admin/setup_form.rb new file mode 100644 index 00000000..95e65884 --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/setup_form.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a form to setup elections from Decidim's admin panel. + class SetupForm < Decidim::Form + mimic :setup + + attribute :trustee_ids, Array[Integer] + + validate do + validations.each do |message, t_args, valid| + errors.add(message, I18n.t("steps.create_election.errors.#{message}", **t_args, scope: "decidim.elections.admin")) unless valid + end + + if needs_census? + census_validations.each do |message, t_args, valid| + errors.add(message, I18n.t("steps.create_election.errors.#{message}", **t_args, scope: "decidim.elections.admin")) unless valid + end + end + end + + def current_step; end + + def pending_action; end + + def trustee_ids + choose_random_trustees + end + + def trustees + ids = trustee_ids + @trustees ||= Decidim::Elections::Trustees::ByParticipatorySpaceTrusteeIds.new(ids).to_a.sort_by(&:id) + end + + def validations + @validations ||= [ + [:minimum_questions, { link: router.election_questions_path(election) }, election.questions.any?], + [:minimum_answers, { link: router.election_questions_path(election) }, election.minimum_answers?], + [:max_selections, { link: router.election_questions_path(election) }, election.valid_questions?], + [:published, { link: router.publish_election_path(election) }, election.published_at.present?], + [:component_published, { link: EngineRouter.admin_proxy(election.participatory_space).components_path(election.participatory_space) }, election.component.published?], + [:time_before, + { link: router.edit_election_path(election), hours: I18n.t("datetime.distance_in_words.x_hours", + count: Decidim::Elections.setup_minimum_hours_before_start) }, election.minimum_hours_before_start?], + [:trustees_number, { link: router.trustees_path, number: bulletin_board.number_of_trustees }, + participatory_space_trustees_with_public_key.size >= bulletin_board.number_of_trustees] + ].freeze + end + + def census_validations + return [] unless needs_census? + + @census_validations ||= [ + [:census_uploaded, {}, census.present? && census.data.exists?], + [:census_codes_generated, {}, census_codes_generated?], + [:census_frozen, {}, census&.freeze?] + ].freeze + end + + def messages + @messages ||= validations.to_h do |message, t_args, _valid| + [message, { message: I18n.t("steps.create_election.requirements.#{message}", **t_args, scope: "decidim.elections.admin"), link: t_args[:link] }] + end + end + + def census_messages + @census_messages ||= census_validations.to_h do |message, t_args, _valid| + [message, I18n.t("steps.create_election.requirements.#{message}", **t_args, scope: "decidim.elections.admin")] + end + end + + def participatory_space_trustees + @participatory_space_trustees ||= Decidim::Elections::Trustees::ByParticipatorySpace.new(election.component.participatory_space).to_a + end + + def election + @election ||= context[:election] + end + + def bulletin_board + @bulletin_board ||= context[:bulletin_board] || Decidim::Elections.bulletin_board + end + + def needs_census? + vote_flow.is_a?(Decidim::Votings::CensusVoteFlow) + end + + def vote_flow + @vote_flow ||= election.participatory_space.try(:vote_flow_for, election) + end + + def census_codes_generated? + return unless needs_census? + + census&.codes_generated? || census&.exporting_codes? || census&.freeze? + end + + def census + return unless needs_census? + + @census ||= election.component.participatory_space.dataset + end + + def main_button? + true + end + + private + + def choose_random_trustees + return @trustee_ids if @trustee_ids&.any? || defined?(@trustees) + + @trustees = if participatory_space_trustees_with_public_key.count >= bulletin_board.number_of_trustees + participatory_space_trustees_with_public_key.sample(bulletin_board.number_of_trustees) + else + [] + end + @trustee_ids = @trustees.pluck(:id) + end + + def participatory_space_trustees_with_public_key + @participatory_space_trustees_with_public_key ||= participatory_space_trustees.filter { |trustee| trustee.public_key.present? } + end + + def router + @router ||= EngineRouter.admin_proxy(election.component) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/trustees_participatory_space_form.rb b/decidim-elections/app/forms/decidim/elections/admin/trustees_participatory_space_form.rb new file mode 100644 index 00000000..ccba8c1d --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/trustees_participatory_space_form.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a form to add users as trustees from Decidim's admin panel. + class TrusteesParticipatorySpaceForm < Decidim::Form + attribute :user_id, Integer + + validates :user_id, :user, presence: true + + def map_model(trustee) + self.user_id = trustee.decidim_user_id + end + + def user + @user ||= current_organization.users.find_by(id: user_id) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/admin/vote_period_form.rb b/decidim-elections/app/forms/decidim/elections/admin/vote_period_form.rb new file mode 100644 index 00000000..0eea9d1e --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/admin/vote_period_form.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # This class holds a form to start and end the voting period. + class VotePeriodForm < ActionForm + validate do + validations.each do |message, t_args, valid| + errors.add(message, I18n.t("steps.#{current_step}.errors.#{message}", **t_args, scope: "decidim.elections.admin")) unless valid + end + end + + def validations + @validations ||= if current_step == "key_ceremony_ended" + [ + [:time_before, + { start_time: I18n.l(election.start_time, format: :long), + hours: Decidim::Elections.start_vote_maximum_hours_before_start }, + election.maximum_hours_before_start?] + ].freeze + else + [ + [:time_after, { end_time: I18n.l(election.end_time, format: :long) }, election.finished?] + ].freeze + end + end + + def messages + @messages ||= validations.to_h do |message, t_args, _valid| + [message, I18n.t("steps.#{current_step}.requirements.#{message}", **t_args, scope: "decidim.elections.admin")] + end + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/trustee_zone/trustee_form.rb b/decidim-elections/app/forms/decidim/elections/trustee_zone/trustee_form.rb new file mode 100644 index 00000000..ef601a46 --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/trustee_zone/trustee_form.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + # This class holds a form to modify trustee information. + class TrusteeForm < Decidim::Form + mimic :trustee + + attribute :name, String + attribute :public_key, String + validates :name, :public_key, presence: true + validate :dont_change_data + + def dont_change_data + errors.add :name, :cant_be_changed if trustee.name.present? + errors.add :public_key, :cant_be_changed if trustee.public_key.present? + end + + def trustee + @trustee ||= context[:trustee] + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/voter/verify_vote_form.rb b/decidim-elections/app/forms/decidim/elections/voter/verify_vote_form.rb new file mode 100644 index 00000000..15144d4e --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/voter/verify_vote_form.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Voter + # This class holds the data to verify a vote. + class VerifyVoteForm < Decidim::Form + attribute :vote_identifier, String + + validates :vote_identifier, :election, presence: true + + delegate :id, to: :election, prefix: true + + # Public: returns the associated election for the vote. + def election + @election ||= context.election + end + + def bulletin_board + @bulletin_board ||= context[:bulletin_board] || Decidim::Elections.bulletin_board + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/elections/voter/vote_form.rb b/decidim-elections/app/forms/decidim/elections/voter/vote_form.rb new file mode 100644 index 00000000..f3978611 --- /dev/null +++ b/decidim-elections/app/forms/decidim/elections/voter/vote_form.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Voter + # This class holds the data to cast a vote. + class VoteForm < Decidim::Form + mimic :vote + + attribute :encrypted_data, String + attribute :encrypted_data_hash, String + attribute :voter_id, String + attribute :voter_token, String + + validates :encrypted_data, :encrypted_data_hash, :election, presence: true + validate :hash_is_valid + + delegate :id, to: :election, prefix: true + + # Public: returns the associated election for the vote. + def election + @election ||= context.election + end + + # Public: returns the user for the vote. + def user + @user ||= context.user + end + + # Public: returns the email for the voter. + def email + @email ||= context.email + end + + def bulletin_board + @bulletin_board ||= context.bulletin_board || Decidim::Elections.bulletin_board + end + + private + + # Private: check if the hash sent by the browser is correct. + def hash_is_valid + return if encrypted_data.blank? || encrypted_data_hash.blank? + + errors.add(:encrypted_data_hash, :invalid) if Digest::SHA256.hexdigest(encrypted_data) != encrypted_data_hash + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/admin/ballot_style_form.rb b/decidim-elections/app/forms/decidim/votings/admin/ballot_style_form.rb new file mode 100644 index 00000000..0cfe7732 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/admin/ballot_style_form.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A form to create/edit a ballot style + class BallotStyleForm < Form + attribute :code, String + attribute :question_ids, Array[Integer] + + validates :code, presence: true + + def code + super&.upcase + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/admin/monitoring_committee_polling_station_closure_form.rb b/decidim-elections/app/forms/decidim/votings/admin/monitoring_committee_polling_station_closure_form.rb new file mode 100644 index 00000000..e9b96a43 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/admin/monitoring_committee_polling_station_closure_form.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + class MonitoringCommitteePollingStationClosureForm < Decidim::Form + attribute :monitoring_committee_notes, String + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/admin/polling_station_form.rb b/decidim-elections/app/forms/decidim/votings/admin/polling_station_form.rb new file mode 100644 index 00000000..425d4c2f --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/admin/polling_station_form.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This class holds a Form to create/update votings from Decidim's admin panel. + class PollingStationForm < Decidim::Form + include TranslatableAttributes + + def map_model(model) + self.polling_station_president_id = model.polling_station_president&.id + self.polling_station_manager_ids = model.polling_station_managers.pluck(:id) + end + + def geocoding_enabled? + Decidim::Map.available?(:geocoding) + end + + def has_address? + geocoding_enabled? && address.present? + end + + def geocoded? + latitude.present? && longitude.present? + end + + def polling_station_president + @polling_station_president ||= PollingOfficer.find_by(id: polling_station_president_id) + end + + def polling_station_managers + @polling_station_managers ||= PollingOfficer.where(id: polling_station_manager_ids) + end + + def voting + @voting ||= context[:voting] + end + + translatable_attribute :title, String + translatable_attribute :location, String + translatable_attribute :location_hints, String + attribute :address, String + attribute :latitude, Float + attribute :longitude, Float + attribute :polling_station_president_id, Integer + attribute :polling_station_manager_ids, Array[Integer] + + validates :title, translatable_presence: true + validates :location, translatable_presence: true + validates :location_hints, translatable_presence: true + validates :address, presence: true + validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? } + + alias component voting + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/admin/publish_results_form.rb b/decidim-elections/app/forms/decidim/votings/admin/publish_results_form.rb new file mode 100644 index 00000000..3ec15474 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/admin/publish_results_form.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + class PublishResultsForm < Decidim::Elections::Admin::ActionForm + def groups + @groups ||= [nil, *election.questions].map { |question| groups_for(question) } + end + + private + + def closurables + @closurables ||= Decidim::Votings::PollingStationClosure.where(election:) + + Decidim::Elections::BulletinBoardClosure.where(election:) + end + + def groups_for(question) + groups = {} + + aggregate_results_for(question).each do |key, total| + update_group(groups, key[1, 2].join("."), key, total) + end + + { + question:, + results: groups.values + } + end + + def aggregate_results_for(question) + Decidim::Elections::Result.where(closurable: closurables, question:) + .group(:closurable_type, :result_type, :decidim_elections_answer_id) + .sum(:value) + end + + def update_group(groups, group_id, key, total) + closurable_type, result_type, answer_id = key + groups[group_id] ||= { + result_type:, + answer: all_answers[answer_id], + value: 0, + polling_station: 0, + bulletin_board: 0 + } + + groups[group_id][closurable_type_key[closurable_type]] = total + groups[group_id][:value] += total + end + + def closurable_type_key + @closurable_type_key ||= { + "Decidim::Votings::PollingStationClosure" => :polling_station, + "Decidim::Elections::BulletinBoardClosure" => :bulletin_board + }.freeze + end + + def all_answers + @all_answers ||= Decidim::Elections::Answer.where(question: election.questions).index_by(&:id) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/admin/voting_form.rb b/decidim-elections/app/forms/decidim/votings/admin/voting_form.rb new file mode 100644 index 00000000..725cbe2c --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/admin/voting_form.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # This class holds a Form to create/update votings from Decidim's admin panel. + class VotingForm < Decidim::Form + include TranslatableAttributes + + translatable_attribute :title, String + translatable_attribute :description, String + attribute :slug, String + attribute :start_time, Decidim::Attributes::TimeWithZone + attribute :end_time, Decidim::Attributes::TimeWithZone + attribute :scope_id, Integer + attribute :promoted, Boolean + attribute :remove_banner_image, Boolean, default: false + attribute :banner_image + attribute :remove_introductory_image, Boolean, default: false + attribute :introductory_image + attribute :voting_type, String + attribute :census_contact_information, String + attribute :show_check_census, Boolean, default: false + + validates :title, translatable_presence: true + validates :description, translatable_presence: true + validates :slug, presence: true, format: { with: Decidim::Votings::Voting.slug_format } + validate :slug_uniqueness + validates :start_time, presence: true, date: { before: :end_time } + validates :end_time, presence: true, date: { after: :start_time } + validates :banner_image, passthru: { to: Decidim::Votings::Voting } + validates :introductory_image, passthru: { to: Decidim::Votings::Voting } + validates :voting_type, presence: true, inclusion: { in: Votings::Voting.voting_types } + + validates :scope, presence: true, if: proc { |object| object.scope_id.present? } + + alias organization current_organization + + def map_model(model) + self.scope_id = model.decidim_scope_id + end + + def scope + @scope ||= current_organization.scopes.find_by(id: scope_id) + end + + def options_for_voting_type_select + Voting.voting_types.map do |key, value| + [ + I18n.t("voting_type.#{key}", scope: "decidim.votings.admin.votings.form"), + value + ] + end + end + + private + + def organization_votings + Voting.where(organization: current_organization) + end + + def slug_uniqueness + return unless organization_votings + .where(slug:) + .where.not(id: context[:voting_id]) + .any? + + errors.add(:slug, :taken) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/admin/voting_user_role_form.rb b/decidim-elections/app/forms/decidim/votings/admin/voting_user_role_form.rb new file mode 100644 index 00000000..04036c48 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/admin/voting_user_role_form.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + class VotingUserRoleForm < Decidim::Form + attribute :name, String + attribute :email, String + attribute :user_id, Integer + attribute :existing_user, Boolean, default: false + + validates :email, presence: true, format: { with: ::Devise.email_regexp }, unless: ->(form) { form.existing_user } + validates :name, presence: true, format: { with: UserBaseEntity::REGEXP_NAME }, unless: ->(form) { form.existing_user } + validates :user, presence: true, if: ->(form) { form.existing_user } + + def map_model(model) + self.user_id = model.decidim_user_id + self.existing_user = user_id.present? + end + + def user + @user ||= current_organization.users.find_by(id: user_id) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/answer_result_form.rb b/decidim-elections/app/forms/decidim/votings/answer_result_form.rb new file mode 100644 index 00000000..18ac7c85 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/answer_result_form.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class AnswerResultForm < Decidim::Form + include TranslatableAttributes + + attribute :id, Integer + translatable_attribute :title, String + attribute :question_id, Integer + attribute :value, Integer + + validates :id, :question_id, :value, presence: true + validates :value, numericality: true + + def map_model(model) + answer = model[:answer] + closure = model[:closure] + self.id = answer.id + self.title = answer.title + self.question_id = answer.question.id + self.value = closure&.results&.valid_answers&.find_by(answer:)&.value + end + + def answer + @answer ||= Decidim::Elections::Answer.find_by(id:) + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/ballot_result_form.rb b/decidim-elections/app/forms/decidim/votings/ballot_result_form.rb new file mode 100644 index 00000000..9027d0eb --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/ballot_result_form.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class BallotResultForm < Decidim::Form + attribute :valid_ballots_count, Integer + attribute :blank_ballots_count, Integer + attribute :null_ballots_count, Integer + attribute :total_ballots_count, Integer + + validates :valid_ballots_count, + :blank_ballots_count, + :null_ballots_count, + presence: true, + numericality: true + + validate :ballot_total_count + + def ballot_total_count + return if total_ballots_count == (valid_ballots_count.to_i + blank_ballots_count.to_i + null_ballots_count.to_i) + + errors.add(:base, :total_count_invalid) + errors.add(:total_ballots_count, :invalid) + errors.add(:valid_ballots_count, :invalid) + errors.add(:blank_ballots_count, :invalid) + errors.add(:null_ballots_count, :invalid) + end + + # rubocop:disable Metrics/PerceivedComplexity + # rubocop:disable Metrics/CyclomaticComplexity + def map_model(model) + self.total_ballots_count = model.results&.total_ballots&.first&.value || 0 + self.null_ballots_count = model.results&.null_ballots&.first&.value + self.blank_ballots_count = model.results&.blank_ballots&.first&.value + self.valid_ballots_count = model.results&.valid_ballots&.first&.value + end + # rubocop:enable Metrics/PerceivedComplexity + # rubocop:enable Metrics/CyclomaticComplexity + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/admin/dataset_form.rb b/decidim-elections/app/forms/decidim/votings/census/admin/dataset_form.rb new file mode 100644 index 00000000..6f900252 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/admin/dataset_form.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # A form to temporaly upload csv census data + class DatasetForm < Form + mimic :dataset + + attribute :file, Decidim::Attributes::Blob + + validates :file, presence: true, file_content_type: { allow: ["text/csv"] } + + def organization + context.current_participatory_space&.organization + end + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/admin/datum_form.rb b/decidim-elections/app/forms/decidim/votings/census/admin/datum_form.rb new file mode 100644 index 00000000..7cb746b5 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/admin/datum_form.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + # A form object used to create a datum record (participant) for + # a voting census + class DatumForm < Form + mimic :datum + + include Decidim::Votings::Census::CheckFields + + attribute :full_name, String + attribute :full_address, String + attribute :mobile_phone_number, String + attribute :email, String + attribute :ballot_style_code, String + + validates :birthdate, format: { with: /\A\d{8}\z/ } + + validates :full_name, + :full_address, + :birthdate, + presence: true + + def ballot_style_code + super&.upcase + end + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/check_fields.rb b/decidim-elections/app/forms/decidim/votings/census/check_fields.rb new file mode 100644 index 00000000..0cfaee7f --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/check_fields.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + module Census + # Definition of the fields required to be used on Datum forms for identity checking + module CheckFields + extend ActiveSupport::Concern + + included do + include Decidim::Votings::Census::InPersonFields + + attribute :postal_code, String + + validates :postal_code, presence: true + end + + # hash of postal code, birth, document type and number + # used by a person to check if present in dataset + def hashed_check_data + hash_for document_number, document_type, birthdate, postal_code + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/check_form.rb b/decidim-elections/app/forms/decidim/votings/census/check_form.rb new file mode 100644 index 00000000..70234c81 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/check_form.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + # A form to check if data matches census + class CheckForm < Form + include Decidim::Votings::Census::CheckFields + include Decidim::Votings::Census::FrontendFields + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/frontend_fields.rb b/decidim-elections/app/forms/decidim/votings/census/frontend_fields.rb new file mode 100644 index 00000000..6c05209e --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/frontend_fields.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + module Census + # Definition of the fields required for frontend forms + module FrontendFields + extend ActiveSupport::Concern + + included do + attribute :day, Integer + attribute :month, Integer + attribute :year, Integer + + validates :day, :month, :year, presence: true + + validates :day, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 31 } + validates :month, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 12 } + validates :year, numericality: { only_integer: true, greater_than_or_equal_to: 1900, less_than_or_equal_to: Time.zone.today.year } + + validate :check_birthdate + end + + def check_birthdate + return unless birthdate + + errors.add(:birthdate, :invalid) if Date.civil(year, month, day) > Time.zone.today + rescue Date::Error + errors.add(:birthdate, :invalid) + end + + def birthdate + return unless [year, month, day].all? { |part| part.is_a? Numeric } + + format("%04d%02d%02d", year, month, day) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/in_person_fields.rb b/decidim-elections/app/forms/decidim/votings/census/in_person_fields.rb new file mode 100644 index 00000000..b098304b --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/in_person_fields.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + module Census + # Definition of the fields required to be used on Datum forms for in person voting + module InPersonFields + extend ActiveSupport::Concern + + included do + attribute :document_number, String + attribute :document_type, String + attribute :birthdate, String + + validates :document_number, + :document_type, + :birthdate, + presence: true + + validates :document_type, inclusion: { in: :document_types } + end + + # hash of birth, document type and number + # used by the polling officer to identify a person + def hashed_in_person_data + hash_for document_number, document_type, birthdate + end + + def hash_for(*data) + Digest::SHA256.hexdigest(data.join(".")) + end + + def options_for_document_type_select + document_types.map do |document_type| + [ + I18n.t(document_type.downcase, scope: "decidim.votings.census.document_types"), + document_type + ] + end + end + + private + + def document_types + Decidim::Elections.document_types + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/in_person_form.rb b/decidim-elections/app/forms/decidim/votings/census/in_person_form.rb new file mode 100644 index 00000000..edd139ae --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/in_person_form.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + # A form to check if data matches census + class InPersonForm < Form + include Decidim::Votings::Census::InPersonFields + include Decidim::Votings::Census::FrontendFields + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/login_form.rb b/decidim-elections/app/forms/decidim/votings/census/login_form.rb new file mode 100644 index 00000000..8479c95b --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/login_form.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + # This class holds the data to login with census data + class LoginForm < Decidim::Form + include Decidim::Votings::Census::OnlineFields + include Decidim::Votings::Census::FrontendFields + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/census/online_fields.rb b/decidim-elections/app/forms/decidim/votings/census/online_fields.rb new file mode 100644 index 00000000..27a4dbb6 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/census/online_fields.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module Decidim + module Votings + module Census + # Definition of the fields required to be used on Datum forms for online voting + module OnlineFields + extend ActiveSupport::Concern + + included do + include Decidim::Votings::Census::CheckFields + include Decidim::Votings::Census::FrontendFields + + attribute :access_code, String + validates :access_code, presence: true + end + + # hash of hashed_check_data and access code + # used by a voter to identify before online voting + def hashed_online_data + hash_for hashed_check_data, access_code + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/closure_certify_form.rb b/decidim-elections/app/forms/decidim/votings/closure_certify_form.rb new file mode 100644 index 00000000..4b66aa2b --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/closure_certify_form.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class ClosureCertifyForm < Decidim::Form + include Decidim::AttachmentAttributes + + attribute :attachment, AttachmentForm + attachments_attribute :photos + + validates :add_photos, presence: true + validate :closure_phase + + private + + def closure_phase + errors.add(:base, :not_in_certificate_phase) unless closure.certificate_phase? + end + + def closure + context&.closure || self + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/closure_result_form.rb b/decidim-elections/app/forms/decidim/votings/closure_result_form.rb new file mode 100644 index 00000000..6ba215a0 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/closure_result_form.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class ClosureResultForm < Decidim::Form + attribute :id, Integer + attribute :polling_station_id, Integer + attribute :election_id, Integer + + attribute :ballot_results, BallotResultForm + attribute :question_results, Array[QuestionResultForm] + attribute :answer_results, Array[AnswerResultForm] + + validates :polling_station_id, + :election_id, + presence: true + validate :max_answers_and_votes + + def map_model(model) + self.id = model.id + self.polling_station_id = model.polling_station.id + self.election_id = model.election.id + + self.ballot_results = BallotResultForm.from_model(model) + + self.question_results = model.election.questions.flat_map do |question| + QuestionResultForm.from_model(question:, closure: model) + end + + self.answer_results = model.election.questions.flat_map do |question| + question.answers.map do |answer| + AnswerResultForm.from_model(answer:, closure: model) + end + end + end + + # rubocop:disable Metrics/PerceivedComplexity + # rubocop:disable Metrics/CyclomaticComplexity + def self.from_params(params) + form = super(params) + + # not all questions are send via PATCH/POST if no nota is enabled so we need to + # reconstruct the object and grab the user input value if exists + # It is necessary also to obtain the title of the question/answer + form.question_results = form.election&.questions&.flat_map do |question| + question_form = QuestionResultForm.from_model(question:, closure: form.closure) + question_form.value = params.dig(:closure_result, :question_results, question.id.to_s, :value) || + params.dig(:question_results, question.id.to_s, :value) || 0 + + question_form + end + + form.answer_results = form.election&.questions&.flat_map do |question| + question.answers.map do |answer| + answer_form = AnswerResultForm.from_model(answer:, closure: form.closure) + answer_form.value = params.dig(:closure_result, :answer_results, answer.id.to_s, :value) || + params.dig(:answer_results, answer.id.to_s, :value) || 0 + + answer_form + end + end + + form + end + # rubocop:enable Metrics/PerceivedComplexity + # rubocop:enable Metrics/CyclomaticComplexity + + def election + @election ||= Decidim::Elections::Election.find_by(id: election_id) + end + + def polling_station + @polling_station ||= Decidim::Votings::PollingStation.find_by(id: polling_station_id) + end + + def closure + @closure ||= Decidim::Votings::PollingStationClosure.find_by(election:) + end + + private + + def max_answers_and_votes + expected_blanks = question_results.sum { |q| q.value.to_i } + expected_answers = answer_results.sum { |a| a.value.to_i } + if ballot_results.blank_ballots_count != expected_blanks + ballot_results.errors.add(:blank_ballots_count, :invalid) + errors.add(:base, I18n.t("decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error.blank", + expected: ballot_results.blank_ballots_count, + current: expected_blanks)) + end + if ballot_results.valid_ballots_count != expected_answers + ballot_results.errors.add(:valid_ballots_count, :invalid) + errors.add(:base, I18n.t("decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error.valid", + expected: ballot_results.valid_ballots_count, + current: expected_answers)) + end + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/closure_sign_form.rb b/decidim-elections/app/forms/decidim/votings/closure_sign_form.rb new file mode 100644 index 00000000..294a8e1d --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/closure_sign_form.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class ClosureSignForm < Decidim::Form + attribute :signed, Boolean + + validates :signed, presence: true + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/envelopes_result_form.rb b/decidim-elections/app/forms/decidim/votings/envelopes_result_form.rb new file mode 100644 index 00000000..8cc0f321 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/envelopes_result_form.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class EnvelopesResultForm < Decidim::Form + attribute :polling_station_id, Integer + attribute :election_id, Integer + + attribute :total_ballots_count, Integer + attribute :polling_officer_notes, String + attribute :election_votes_count, Integer + + validates :polling_station_id, + :election_id, + :total_ballots_count, + presence: true + validates :polling_officer_notes, presence: true, if: :totals_differ? + + def totals_differ? + return unless total_ballots_count + + total_ballots_count != election_votes_count + end + + def election + @election ||= Decidim::Elections::Election.find_by(id: election_id) + end + + def polling_station + @polling_station ||= Decidim::Votings::PollingStation.find_by(id: polling_station_id) + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/question_result_form.rb b/decidim-elections/app/forms/decidim/votings/question_result_form.rb new file mode 100644 index 00000000..f8581404 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/question_result_form.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class QuestionResultForm < Decidim::Form + include TranslatableAttributes + + attribute :id, Integer + translatable_attribute :title, String + attribute :nota_option, Boolean + attribute :value, Integer + + validates :id, :value, presence: true + validates :value, numericality: true + + def map_model(model) + @question = model[:question] + @closure = model[:closure] + self.id = question.id + self.title = question.title + self.nota_option = question.nota_option? + self.value = closure&.results&.blank_answers&.find_by(question:)&.value + end + + def question + @question ||= Decidim::Elections::Question.find_by(id:) + end + + def closure + @closure ||= context&.closure + end + end + end +end diff --git a/decidim-elections/app/forms/decidim/votings/voter/in_person_vote_form.rb b/decidim-elections/app/forms/decidim/votings/voter/in_person_vote_form.rb new file mode 100644 index 00000000..04eda389 --- /dev/null +++ b/decidim-elections/app/forms/decidim/votings/voter/in_person_vote_form.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Voter + # This class holds the data to register an in person vote. + class InPersonVoteForm < Decidim::Form + mimic :in_person_vote + + attribute :voter_id, String + attribute :voter_token, String + attribute :voted, Boolean + + validates :polling_station, :election, :voted, presence: true + + delegate :id, to: :election, prefix: true + delegate :slug, to: :polling_station, prefix: true + + # Public: returns the associated election for the in person vote. + def election + @election ||= context.election + end + + # Public: returns the polling station for the in person vote. + def polling_station + @polling_station ||= context.polling_station + end + + # Public: returns the polling_officer registering the in person vote. + def polling_officer + @polling_officer ||= context.polling_officer + end + + def bulletin_board + @bulletin_board ||= context.bulletin_board || Decidim::Elections.bulletin_board + end + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/elections/admin/answers_helper.rb b/decidim-elections/app/helpers/decidim/elections/admin/answers_helper.rb new file mode 100644 index 00000000..71e20b50 --- /dev/null +++ b/decidim-elections/app/helpers/decidim/elections/admin/answers_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # Custom helpers for answers on admin dashboard. + # + module AnswersHelper + def selected_label_action_for(answer) + if answer.selected + t("answers.select.disable", scope: "decidim.elections.admin") + else + t("answers.select.enable", scope: "decidim.elections.admin") + end + end + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/elections/admin/steps_helper.rb b/decidim-elections/app/helpers/decidim/elections/admin/steps_helper.rb new file mode 100644 index 00000000..492dda64 --- /dev/null +++ b/decidim-elections/app/helpers/decidim/elections/admin/steps_helper.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # Custom helpers for election steps on admin dashboard. + # + module StepsHelper + def steps(current_step) + step_class = "text-success" + (["create_election"] + Decidim::Elections::Election.bb_statuses.keys).map do |step| + if step == current_step + step_class = "text-muted" + [step, "text-warning"] + else + [step, step_class] + end + end + end + + def fix_it_button_with_icon(link, icon_name, method = :get) + link_to(link, class: "button tiny button__secondary px-2 py-0", method:) do + "#{icon(icon_name, class: "fix-icon")} #{I18n.t("decidim.elections.admin.steps.create_election.errors.fix_it_text")}".html_safe + end + end + + # i18n-tasks-use t("decidim.elections.admin.steps.create_election.technical_configuration.bulletin_board_server") + # i18n-tasks-use t("decidim.elections.admin.steps.create_election.technical_configuration.authority_name") + # i18n-tasks-use t("decidim.elections.admin.steps.create_election.technical_configuration.scheme_name") + def technical_configuration_items + [ + { key: ".technical_configuration.bulletin_board_server", value: Decidim::BulletinBoard.config[:bulletin_board_server] }, + { key: ".technical_configuration.authority_name", value: Decidim::BulletinBoard.config[:authority_name] }, + { key: ".technical_configuration.scheme_name", value: Decidim::BulletinBoard.config[:scheme_name] } + ] + end + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/elections/admin/trustees_participatory_spaces_helper.rb b/decidim-elections/app/helpers/decidim/elections/admin/trustees_participatory_spaces_helper.rb new file mode 100644 index 00000000..c0ff6664 --- /dev/null +++ b/decidim-elections/app/helpers/decidim/elections/admin/trustees_participatory_spaces_helper.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # Custom helpers for trustees admin. + # + module TrusteesParticipatorySpacesHelper + include Decidim::PaginateHelper + + def trustee_current_participatory_space(trustee) + trustee.trustees_participatory_spaces.find_by(participatory_space: current_participatory_space) + end + + def considered_icon_action_for(trustee) + if trustee_current_participatory_space(trustee).considered + "close-line" + else + "check-line" + end + end + + def considered_label_action_for(trustee) + if trustee_current_participatory_space(trustee).considered + t("trustees_participatory_spaces.actions.disable", scope: "decidim.elections.admin") + else + t("trustees_participatory_spaces.actions.enable", scope: "decidim.elections.admin") + end + end + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/elections/application_helper.rb b/decidim-elections/app/helpers/decidim/elections/application_helper.rb new file mode 100644 index 00000000..9041b6d2 --- /dev/null +++ b/decidim-elections/app/helpers/decidim/elections/application_helper.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Custom helpers, scoped to the elections engine. + # + module ApplicationHelper + include Decidim::CheckBoxesTreeHelper + + def date_filter_values + TreeNode.new( + TreePoint.new("", t("elections.elections.filters.all", scope: "decidim")), + [ + TreePoint.new("active", t("elections.elections.filters.active", scope: "decidim")), + TreePoint.new("upcoming", t("elections.elections.filters.upcoming", scope: "decidim")), + TreePoint.new("finished", t("elections.elections.filters.finished", scope: "decidim")) + ] + ) + end + + def filter_sections + @filter_sections ||= [{ method: :with_any_date, collection: date_filter_values, label_scope: "decidim.elections.elections.filters", id: "date" }] + end + + def component_name + (defined?(current_component) && translated_attribute(current_component&.name).presence) || t("decidim.components.elections.name") + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/elections/election_cells_helper.rb b/decidim-elections/app/helpers/decidim/elections/election_cells_helper.rb new file mode 100644 index 00000000..f1c1f3f2 --- /dev/null +++ b/decidim-elections/app/helpers/decidim/elections/election_cells_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Custom helpers for election cells. + # + module ElectionCellsHelper + include PaginateHelper + include Decidim::Comments::CommentsHelper + end + end +end diff --git a/decidim-elections/app/helpers/decidim/elections/votes_helper.rb b/decidim-elections/app/helpers/decidim/elections/votes_helper.rb new file mode 100644 index 00000000..f447625d --- /dev/null +++ b/decidim-elections/app/helpers/decidim/elections/votes_helper.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Custom helpers for the voting booth views. + # + module VotesHelper + def ordered_answers(question) + if question.random_answers_order + question.answers.shuffle + else + question.answers.sort_by { |answer| [answer.weight, answer.id] } + end + end + + def more_information?(answer) + translated_attribute(answer.description).present? || + answer.proposals.any? || + answer.photos.any? + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/votings/map_helper.rb b/decidim-elections/app/helpers/decidim/votings/map_helper.rb new file mode 100644 index 00000000..4958ed1c --- /dev/null +++ b/decidim-elections/app/helpers/decidim/votings/map_helper.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This helper include some methods for rendering votings dynamic maps. + module MapHelper + include Decidim::SanitizeHelper + include Decidim::LayoutHelper + + def polling_station_data_for_map(polling_stations) + polling_stations_geocoded = polling_stations.select(&:geocoded_and_valid?) + polling_stations_geocoded.map do |polling_station| + polling_station.slice(:latitude, :longitude, :address) + .merge( + title: translated_attribute(polling_station.title), + items: [{ icon: icon("map-line").html_safe, text: polling_station.address }].to_json + ) + end + end + end + end +end diff --git a/decidim-elections/app/helpers/decidim/votings/votings_helper.rb b/decidim-elections/app/helpers/decidim/votings/votings_helper.rb new file mode 100644 index 00000000..0a5e9b8e --- /dev/null +++ b/decidim-elections/app/helpers/decidim/votings/votings_helper.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module VotingsHelper + include Decidim::CheckBoxesTreeHelper + + def date_filter_values + TreeNode.new( + TreePoint.new("", t("votings.filters.all", scope: "decidim.votings")), + [ + TreePoint.new("active", t("votings.filters.active", scope: "decidim.votings")), + TreePoint.new("upcoming", t("votings.filters.upcoming", scope: "decidim.votings")), + TreePoint.new("finished", t("votings.filters.finished", scope: "decidim.votings")) + ] + ) + end + + def filter_sections + @filter_sections ||= [{ method: :with_any_date, collection: date_filter_values, label_scope: "decidim.votings.votings.filters", id: "date" }] + end + + def voting_nav_items(participatory_space) + components = participatory_space.components.published.or(Decidim::Component.where(id: try(:current_component))) + + ( + [ + if participatory_space.check_census_enabled? + { + name: t("layouts.decidim.voting_navigation.check_census"), + url: decidim_votings.voting_check_census_path(participatory_space), + active: is_active_link?(decidim_votings.voting_check_census_path(participatory_space), :exclusive) + } + end + ] + components.map do |component| + { + name: decidim_escape_translated(component.name), + url: main_component_path(component), + active: is_active_link?(main_component_path(component), :inclusive) && !is_active_link?("election_log", /election_log$/) + } + end + + [ + { + name: t("layouts.decidim.voting_navigation.election_log"), + url: decidim_votings.voting_elections_log_path(participatory_space), + active: is_active_link?(decidim_votings.voting_elections_log_path(participatory_space), :exclusive) || is_active_link?("election_log", /election_log$/) + } + ] + ).compact + end + end + end +end diff --git a/decidim-elections/app/jobs/decidim/votings/census/admin/create_datum_job.rb b/decidim-elections/app/jobs/decidim/votings/census/admin/create_datum_job.rb new file mode 100644 index 00000000..27ea5dc5 --- /dev/null +++ b/decidim-elections/app/jobs/decidim/votings/census/admin/create_datum_job.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + class CreateDatumJob < ApplicationJob + queue_as :default + + def perform(user, dataset, csv_row) + return if user.blank? || dataset.blank? || csv_row.blank? + + params = { + document_number: csv_row[0], + document_type: csv_row[1], + birthdate: csv_row[2], + full_name: csv_row[3], + full_address: csv_row[4], + postal_code: csv_row[5], + mobile_phone_number: csv_row[6], + email: csv_row[7], + ballot_style_code: csv_row[8] + } + + datum_form = DatumForm.from_params(params) + .with_context( + current_user: user, + dataset: + ) + + CreateDatum.call(datum_form, dataset) + end + + after_perform do |job| + Decidim::Votings::Census::Admin::IncrementDatasetProcessedRows.call(job.arguments.second) + end + end + end + end + end +end diff --git a/decidim-elections/app/jobs/decidim/votings/census/admin/export_access_codes_job.rb b/decidim-elections/app/jobs/decidim/votings/census/admin/export_access_codes_job.rb new file mode 100644 index 00000000..e70eb1fc --- /dev/null +++ b/decidim-elections/app/jobs/decidim/votings/census/admin/export_access_codes_job.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + class ExportAccessCodesJob < ApplicationJob + queue_as :exports + + def perform(dataset, user) + filename = "#{SecureRandom.urlsafe_base64}.zip" + path = Rails.root.join("tmp/#{filename}") + password = SecureRandom.urlsafe_base64 + + ActiveRecord::Base.transaction do + UpdateDataset.call(dataset, { status: :exporting_codes }, user) + + generate_zip_file(dataset, path, password) + save_or_upload_file(dataset, path) + ExportMailer.access_codes_export(user, dataset.voting, filename, password).deliver_later + + UpdateDataset.call(dataset, { status: :freeze }, user) + end + end + + private + + def generate_zip_file(dataset, path, password) + AccessCodesExporter.new(dataset, path, password).export + end + + def save_or_upload_file(dataset, path) + dataset.access_codes_file.attach(io: File.open(path, "rb"), filename: File.basename(path)) + end + end + end + end + end +end diff --git a/decidim-elections/app/jobs/decidim/votings/census/admin/generate_access_codes_job.rb b/decidim-elections/app/jobs/decidim/votings/census/admin/generate_access_codes_job.rb new file mode 100644 index 00000000..32cb1122 --- /dev/null +++ b/decidim-elections/app/jobs/decidim/votings/census/admin/generate_access_codes_job.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module Admin + class GenerateAccessCodesJob < ApplicationJob + queue_as :default + + def perform(dataset, user) + return unless user_valid?(user) && dataset_valid?(dataset) + + generate_access_codes(dataset) + + update_dataset_status(dataset, :codes_generated, user) + end + + private + + def user_valid?(user) + user.present? + end + + def dataset_valid?(dataset) + dataset.present? && dataset.generating_codes? + end + + def generate_access_codes(dataset) + dataset.data.find_each do |datum| + access_code = SecureRandom.alphanumeric(8) + hashed_online_data = Digest::SHA256.hexdigest([datum.hashed_check_data, access_code].join(".")) + + datum.update!(access_code:, hashed_online_data:) + end + end + + def update_dataset_status(dataset, status, user) + UpdateDataset.call(dataset, { status: }, user) + end + end + end + end + end +end diff --git a/decidim-elections/app/mailers/decidim/elections/trustee_mailer.rb b/decidim-elections/app/mailers/decidim/elections/trustee_mailer.rb new file mode 100644 index 00000000..f2138a34 --- /dev/null +++ b/decidim-elections/app/mailers/decidim/elections/trustee_mailer.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This mailer sends a notification email to a recently added trustee + class TrusteeMailer < Decidim::ApplicationMailer + include TranslatableAttributes + + # Public: Sends an email to a trustee that just got added to a participatory space. + # + # user - The user to be notified + # participatory_space - The participatory space where the trustee was added. + # locale - The locale that will be used for the email content (optional). + # + # Returns nothing. + def notification(user, participatory_space, locale = nil) + @user = user + @participatory_space = participatory_space + @organization = user.organization + + I18n.with_locale(locale || @organization.default_locale) do + @participatory_space_title = translated_attribute(participatory_space.title) + mail(to: user.email, subject: I18n.t("subject", scope: "decidim.elections.admin.mailers.trustee_mailer", resource_name: @participatory_space_title)) + end + end + end + end +end diff --git a/decidim-elections/app/mailers/decidim/elections/vote_accepted_mailer.rb b/decidim-elections/app/mailers/decidim/elections/vote_accepted_mailer.rb new file mode 100644 index 00000000..9a7ef29c --- /dev/null +++ b/decidim-elections/app/mailers/decidim/elections/vote_accepted_mailer.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This mailer sends a notification email with the accepted vote information. + class VoteAcceptedMailer < Decidim::ApplicationMailer + include TranslatableAttributes + + # Public: Sends a notification email with the accepted vote information when there is no + # user to notify. + # + # vote - The vote to be notified. + # verify_url - The url to verify the vote. + # locale - The locale that will be used for the email content (optional). + # + # Returns nothing. + def notification(vote, verify_url, locale = nil) + @vote = vote + @verify_url = verify_url + @organization = vote.election.component.organization + + I18n.with_locale(locale || @organization.default_locale) do + @election_title = translated_attribute(vote.election.title) + mail(to: vote.email, subject: I18n.t("votes.accepted_votes.email_subject", scope: "decidim.events.elections", resource_name: @election_title)) + end + end + end + end +end diff --git a/decidim-elections/app/mailers/decidim/votings/access_code_mailer.rb b/decidim-elections/app/mailers/decidim/votings/access_code_mailer.rb new file mode 100644 index 00000000..18556794 --- /dev/null +++ b/decidim-elections/app/mailers/decidim/votings/access_code_mailer.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This mailer sends the access code via email. + class AccessCodeMailer < Decidim::ApplicationMailer + include TranslatableAttributes + + # Public: Sends an email with the access code. + # + # datum - The datum with the access code + # locale - The locale that will be used for the email content (optional). + # + # Returns nothing. + def send_access_code(datum, locale = nil) + @datum = datum + @organization = datum.dataset.voting.organization + @voting = translated_attribute(datum.dataset.voting.title) + + I18n.with_locale(locale || @organization.default_locale) do + @access_code = datum.access_code + + subject = I18n.t( + "send_access_code.subject", + scope: "decidim.events.votings", + voting: @voting + ) + + mail(to: datum.email, subject:) + end + end + end + end +end diff --git a/decidim-elections/app/mailers/decidim/votings/census/export_mailer.rb b/decidim-elections/app/mailers/decidim/votings/census/export_mailer.rb new file mode 100644 index 00000000..f97cc974 --- /dev/null +++ b/decidim-elections/app/mailers/decidim/votings/census/export_mailer.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + # This mailer sends a notification email containing the export as an + # attachment. + class ExportMailer < Decidim::ApplicationMailer + include TranslatableAttributes + # Public: Sends a notification email with a link to retrieve + # the result of a access codes export in a zipped file. + # + # user - The user to be notified. + # + # Returns nothing. + def access_codes_export(user, voting, filename, password) + @password = password + @user = user + @organization = user.organization + @file_url = export_file_url(user, voting, filename) + + with_user(user) do + mail( + to: "#{user.name} <#{user.email}>", + subject: I18n.t("export_mailer.access_codes_export.subject", scope: "decidim.votings.census", voting_title: translated_attribute(voting.title)) + ) + end + end + + private + + def export_file_url(user, voting, filename) + Decidim::Votings::AdminEngine + .routes + .url_helpers.download_access_codes_file_voting_census_url( + host: user.organization.host, + voting_slug: voting.slug, + filename: + ) + end + end + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/action.rb b/decidim-elections/app/models/decidim/elections/action.rb new file mode 100644 index 00000000..4bfe85f8 --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/action.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for tracking asynchronous Actions done in the Bulletin Board from the Decidim::Elections component. + class Action < ApplicationRecord + belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election" + + enum status: [:pending, :accepted, :rejected] + enum action: [:start_key_ceremony, :start_vote, :end_vote, :start_tally, :report_missing_trustee, :publish_results] + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/answer.rb b/decidim-elections/app/models/decidim/elections/answer.rb new file mode 100644 index 00000000..4e022d0d --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/answer.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for an Answer in the Decidim::Elections component. It stores a + # title, description and related resources and attachments. + class Answer < ApplicationRecord + include Decidim::Resourceable + include Decidim::HasAttachments + include Decidim::HasAttachmentCollections + include Traceable + include Loggable + + delegate :organization, :participatory_space, to: :component + + belongs_to :question, foreign_key: "decidim_elections_question_id", class_name: "Decidim::Elections::Question", inverse_of: :answers + has_one :election, through: :question, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election" + has_one :component, through: :election, foreign_key: "decidim_component_id", class_name: "Decidim::Component" + has_many :results, foreign_key: "decidim_elections_answer_id", class_name: "Decidim::Elections::Result", dependent: :destroy + + default_scope { order(weight: :asc, id: :asc) } + + # Public: Get all the proposals related to the answer + # + # Returns an ActiveRecord::Relation. + def proposals + linked_resources(:proposals, "related_proposals") + end + + def slug + "answer-#{id}" + end + + # Sum all valid results from different origins (PollingStations or BulletinBoard) + def results_total + @results_total ||= results.valid_answers.sum(:value) + end + + # A result percentage relative to the question + # Returns a Float. + def results_percentage + @results_percentage ||= results_total.positive? ? (results_total.to_f / question.results_total * 100.0).round : 0 + end + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/application_record.rb b/decidim-elections/app/models/decidim/elections/application_record.rb new file mode 100644 index 00000000..7e78a6d2 --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/application_record.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Abstract class from which all models in this engine inherit. + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/bulletin_board_closure.rb b/decidim-elections/app/models/decidim/elections/bulletin_board_closure.rb new file mode 100644 index 00000000..ac10df5e --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/bulletin_board_closure.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for an Election Closure. + class BulletinBoardClosure < ApplicationRecord + belongs_to :election, + foreign_key: "decidim_elections_election_id", + class_name: "Decidim::Elections::Election", + inverse_of: :bb_closure + + has_many :results, + foreign_type: "closurable_type", + class_name: "Decidim::Elections::Result", + dependent: :destroy, + as: :closurable + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/election.rb b/decidim-elections/app/models/decidim/elections/election.rb new file mode 100644 index 00000000..f65bac46 --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/election.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for an Election in the Decidim::Elections component. It stores a + # title, description and any other useful information to perform an election. + class Election < ApplicationRecord + include Decidim::HasAttachments + include Decidim::HasAttachmentCollections + include Decidim::Publicable + include Decidim::Resourceable + include Decidim::HasComponent + include Decidim::TranslatableResource + include Traceable + include Loggable + include Decidim::Forms::HasQuestionnaire + include Decidim::FilterableResource + + translatable_fields :title, :description + + enum bb_status: [:created, :key_ceremony, :key_ceremony_ended, :vote, :vote_ended, :tally_started, :tally_ended, :results_published].index_with(&:to_s), _prefix: :bb + + component_manifest_name "elections" + + has_many :questions, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Question", inverse_of: :election, dependent: :destroy + has_many :elections_trustees, foreign_key: "decidim_elections_election_id", dependent: :destroy + has_many :trustees, through: :elections_trustees + has_many :votes, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Vote", dependent: :restrict_with_exception + has_many :actions, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Action", dependent: :restrict_with_exception + has_one :bb_closure, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::BulletinBoardClosure", dependent: :destroy + + scope :active, lambda { + where("start_time <= ?", Time.current) + .where("end_time >= ?", Time.current) + } + + scope :upcoming, lambda { + where("start_time > ?", Time.current) + .where("end_time > ?", Time.current) + } + + scope :finished, lambda { + where("start_time < ?", Time.current) + .where("end_time < ?", Time.current) + } + + scope_search_multi :with_any_date, [:active, :upcoming, :finished] + + def self.log_presenter_class_for(_log) + Decidim::Elections::AdminLog::ElectionPresenter + end + + # Public: Checks if the election started + # + # Returns a boolean. + def started? + start_time <= Time.current + end + + # Public: Checks if the election finished + # + # Returns a boolean. + def finished? + end_time < Time.current + end + + # Public: Checks if the election ongoing now + # + # Returns a boolean. + def ongoing? + started? && !finished? + end + + # Public: Checks if the election start_time is minimum some hours later than the present time + # + # Returns a boolean. + def minimum_hours_before_start? + start_time > (Time.zone.at(Decidim::Elections.setup_minimum_hours_before_start.hours.from_now)) + end + + # Public: Checks if the election start_time is maximum some hours before than the present time + # + # Returns a boolean. + def maximum_hours_before_start? + start_time < (Time.zone.at(Decidim::Elections.start_vote_maximum_hours_before_start.hours.from_now)) + end + + # Public: Checks if the number of answers are minimum 2 for each question + # + # Returns a boolean. + def minimum_answers? + questions.any? && questions.all? { |question| question.answers.size > 1 } + end + + # Public: Checks if the election results are published and election finished + # + # Returns a boolean. + def results_published? + bb_results_published? + end + + # Public: Checks if the election results are present + # + # Returns a boolean. + def results? + bb_tally_ended? || results_published? + end + + # Public: Checks if the election questions are valid + # + # Returns a boolean. + def valid_questions? + questions.any? && questions.all?(&:valid_max_selection?) + end + + # Public: Gets the voting period status of the election + # + # Returns one of these symbols: upcoming, ongoing or finished + def voting_period_status + if finished? + :finished + elsif started? + :ongoing + else + :upcoming + end + end + + def trustee_action_required? + bb_key_ceremony? || bb_tally_started? + end + + # Public: Checks if the election has a blocked_at value + # + # Returns a boolean. + def blocked? + blocked_at.present? + end + + # Public: Overrides the Resourceable concern method to allow setting permissions at resource level + def allow_resource_permissions? + true + end + + # Create i18n ransackers for :title and :description. + # Create the :search_text ransacker alias for searching from both of these. + ransacker_i18n_multi :search_text, [:title, :description] + + def self.ransackable_scopes(_auth_object = nil) + [:with_any_date] + end + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/elections_trustee.rb b/decidim-elections/app/models/decidim/elections/elections_trustee.rb new file mode 100644 index 00000000..61e88550 --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/elections_trustee.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for an Election-Trustee relation in the Decidim::Elections component. + class ElectionsTrustee < ApplicationRecord + belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election", optional: true + belongs_to :trustee, foreign_key: "decidim_elections_trustee_id", class_name: "Decidim::Elections::Trustee", optional: true + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/question.rb b/decidim-elections/app/models/decidim/elections/question.rb new file mode 100644 index 00000000..38886aae --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/question.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for a Question in the Decidim::Elections component. It stores a + # title and a maximum number of selection that voters can choose. + class Question < ApplicationRecord + include Decidim::Resourceable + include Traceable + include Loggable + + belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election", inverse_of: :questions + has_many :answers, foreign_key: "decidim_elections_question_id", class_name: "Decidim::Elections::Answer", inverse_of: :question, dependent: :destroy + has_many :results, foreign_key: "decidim_elections_question_id", class_name: "Decidim::Elections::Result", dependent: :destroy + has_one :component, through: :election, foreign_key: "decidim_component_id", class_name: "Decidim::Component" + + default_scope { order(weight: :asc, id: :asc) } + + # Public: Checks if enough answers are given for max_selections attribute + # + # Returns a boolean. + def valid_max_selection? + max_selections <= answers.count + end + + # Public: Checks if the question accepts a blank/NOTA as an answer + # + # Returns a boolean. + def nota_option? + @nota_option ||= min_selections.zero? + end + + def blank_votes + @blank_votes ||= results.blank_answers.sum(:value) + end + + def results_total + @results_total ||= answers.sum(&:results_total) + blank_votes + end + + # A result percentage relative to the question + # Returns a Float. + def blank_votes_percentage + @blank_votes_percentage ||= results_total.positive? ? (blank_votes.to_f / results_total * 100.0).round : 0 + end + + def slug + "question-#{id}" + end + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/result.rb b/decidim-elections/app/models/decidim/elections/result.rb new file mode 100644 index 00000000..b8e65f8a --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/result.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for a Result in the Decidim::Elections component. + class Result < ApplicationRecord + enum result_type: [:valid_answers, :blank_answers, :valid_ballots, :blank_ballots, :null_ballots, :total_ballots] + + belongs_to :closurable, polymorphic: true + belongs_to :question, + foreign_key: "decidim_elections_question_id", + class_name: "Decidim::Elections::Question", + optional: true, + inverse_of: :results + belongs_to :answer, + foreign_key: "decidim_elections_answer_id", + class_name: "Decidim::Elections::Answer", + optional: true, + inverse_of: :results + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/trustee.rb b/decidim-elections/app/models/decidim/elections/trustee.rb new file mode 100644 index 00000000..105ad3cc --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/trustee.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for a trustee in the Decidim::Elections component. It stores a + # public key and has a reference to Decidim::User. + class Trustee < ApplicationRecord + belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User" + belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization" + has_many :elections_trustees, foreign_key: "decidim_elections_trustee_id", dependent: :destroy + has_many :elections, through: :elections_trustees + has_many :trustees_participatory_spaces, inverse_of: :trustee, foreign_key: "decidim_elections_trustee_id", dependent: :destroy + + def self.trustee?(user) + exists?(user:) + end + + def self.log_presenter_class_for(_log) + Decidim::Elections::AdminLog::TrusteePresenter + end + + def self.for(user) + find_by(user:) + end + + def slug + name.parameterize + end + + # The bulletin_board_slug is used as `unique_id` on the Bulletin Board, where + # the "authority.name" gets added as identification. If the organization + # name would be missing, it could result in an error, when two organizations + # inside the same "authority" have a trustee with the same name. + def bulletin_board_slug + "#{organization.name.parameterize}-#{slug}" + end + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/trustees_participatory_space.rb b/decidim-elections/app/models/decidim/elections/trustees_participatory_space.rb new file mode 100644 index 00000000..315714c2 --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/trustees_participatory_space.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # The data store for a trustee participatory space in the Decidim::Elections component. It has a reference + # to a trustee (user), a polymorphic reference to participatory spaces and stores the status (considered) + # for the trustee. + class TrusteesParticipatorySpace < ApplicationRecord + belongs_to :trustee, foreign_key: "decidim_elections_trustee_id", class_name: "Decidim::Elections::Trustee", inverse_of: :trustees_participatory_spaces + belongs_to :participatory_space, foreign_type: "participatory_space_type", polymorphic: true + end + end +end diff --git a/decidim-elections/app/models/decidim/elections/vote.rb b/decidim-elections/app/models/decidim/elections/vote.rb new file mode 100644 index 00000000..a6991610 --- /dev/null +++ b/decidim-elections/app/models/decidim/elections/vote.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # 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`. + class Vote < ApplicationRecord + enum status: [:pending, :accepted, :rejected].index_with(&:to_s) + + belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election" + belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User", optional: true + + validates :voter_id, :encrypted_vote_hash, presence: true + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/ballot_style.rb b/decidim-elections/app/models/decidim/votings/ballot_style.rb new file mode 100644 index 00000000..d349f320 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/ballot_style.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class BallotStyle < ApplicationRecord + belongs_to :voting, foreign_key: :decidim_votings_voting_id, class_name: "Decidim::Votings::Voting" + + has_many :ballot_style_questions, + class_name: "Decidim::Votings::BallotStyleQuestion", + foreign_key: :decidim_votings_ballot_style_id, + inverse_of: :ballot_style, + dependent: :delete_all + has_many :questions, through: :ballot_style_questions + has_many :census_data, + foreign_key: "decidim_votings_ballot_style_id", + class_name: "Decidim::Votings::Census::Datum", + inverse_of: :ballot_style, + dependent: :nullify + + alias participatory_space voting + + def slug + "#{voting.slug}_#{code.parameterize}-#{id}" + end + + def questions_for(election) + questions.where(election:) + end + + def self.log_presenter_class_for(_log) + Decidim::Votings::AdminLog::BallotStylePresenter + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/ballot_style_question.rb b/decidim-elections/app/models/decidim/votings/ballot_style_question.rb new file mode 100644 index 00000000..b6a424a3 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/ballot_style_question.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class BallotStyleQuestion < ApplicationRecord + # This is a join table between Decidim::Votings::BallotStyle and Decidim::Elections::Question + belongs_to :ballot_style, class_name: "Decidim::Votings::BallotStyle", foreign_key: :decidim_votings_ballot_style_id + belongs_to :question, class_name: "Decidim::Elections::Question", foreign_key: :decidim_elections_question_id + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/census/dataset.rb b/decidim-elections/app/models/decidim/votings/census/dataset.rb new file mode 100644 index 00000000..b513c423 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/census/dataset.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + class Dataset < ApplicationRecord + include Traceable + include Loggable + + # The data store for a whole Census for a voting. + belongs_to :voting, foreign_key: :decidim_votings_voting_id, + class_name: "Decidim::Votings::Voting" + has_many :data, + foreign_key: "decidim_votings_census_dataset_id", + class_name: "Decidim::Votings::Census::Datum", + dependent: :destroy + + has_one_attached :access_codes_file + + delegate :organization, to: :voting + + enum status: [:init_data, :creating_data, :data_created, :generating_codes, :codes_generated, :exporting_codes, :freeze] + + validates :filename, presence: true + + alias participatory_space voting + + def self.log_presenter_class_for(_log) + Decidim::Votings::Census::AdminLog::DatasetPresenter + end + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/census/datum.rb b/decidim-elections/app/models/decidim/votings/census/datum.rb new file mode 100644 index 00000000..c0b2fcea --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/census/datum.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + class Datum < ApplicationRecord + include Decidim::RecordEncryptor + + encrypt_attribute :full_name, type: :string + encrypt_attribute :full_address, type: :string + encrypt_attribute :postal_code, type: :string + encrypt_attribute :mobile_phone_number, type: :string + encrypt_attribute :email, type: :string + + belongs_to :dataset, counter_cache: :data_count, + foreign_key: "decidim_votings_census_dataset_id", + class_name: "Decidim::Votings::Census::Dataset" + belongs_to :ballot_style, optional: true, + class_name: "Decidim::Votings::BallotStyle", + foreign_key: :decidim_votings_ballot_style_id + + validates :full_name, + :full_address, + :postal_code, + :hashed_in_person_data, + :hashed_check_data, + presence: true + + validates :hashed_in_person_data, uniqueness: { scope: :dataset } + validates :hashed_check_data, uniqueness: { scope: :dataset } + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/in_person_vote.rb b/decidim-elections/app/models/decidim/votings/in_person_vote.rb new file mode 100644 index 00000000..442ca4a3 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/in_person_vote.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # The data store for in person Votes in the Decidim::Votings space. + class InPersonVote < ApplicationRecord + enum status: [:pending, :accepted, :rejected] + + belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election" + belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User", optional: true + + belongs_to :polling_station, + foreign_key: "decidim_votings_polling_station_id", + class_name: "Decidim::Votings::PollingStation" + belongs_to :polling_officer, + foreign_key: "decidim_votings_polling_officer_id", + class_name: "Decidim::Votings::PollingOfficer" + + validates :voter_id, presence: true + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/monitoring_committee_member.rb b/decidim-elections/app/models/decidim/votings/monitoring_committee_member.rb new file mode 100644 index 00000000..1b1591f6 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/monitoring_committee_member.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class MonitoringCommitteeMember < ApplicationRecord + include Traceable + include Loggable + + belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User", optional: true + belongs_to :voting, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::Voting", inverse_of: :monitoring_committee_members + + validate :user_and_voting_same_organization + + alias participatory_space voting + + def self.log_presenter_class_for(_log) + Decidim::Votings::AdminLog::MonitoringCommitteeMemberPresenter + end + + private + + # Private: check if the voting and the user have the same organization + def user_and_voting_same_organization + return if !voting || !user + + errors.add(:voting, :invalid) unless user.organization == voting.organization + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/polling_officer.rb b/decidim-elections/app/models/decidim/votings/polling_officer.rb new file mode 100644 index 00000000..5704f1e2 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/polling_officer.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class PollingOfficer < ApplicationRecord + include Traceable + include Loggable + + belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User" + belongs_to :voting, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::Voting", inverse_of: :polling_officers + belongs_to :managed_polling_station, + class_name: "Decidim::Votings::PollingStation", + inverse_of: :polling_station_managers, + optional: true + belongs_to :presided_polling_station, + class_name: "Decidim::Votings::PollingStation", + inverse_of: :polling_station_president, + optional: true + + validates :user, uniqueness: { scope: :voting } + validate :user_and_voting_same_organization + validate :either_president_or_manager + + delegate :name, :nickname, :email, to: :user + alias participatory_space voting + + # Allow ransacker to search by presided/managed polling station title + %w(managed_station presided_station).each do |table| + ransacker "#{table}_title".to_sym do + Arel::Nodes::InfixOperation.new("->>", Arel.sql("#{table}.title"), Arel::Nodes.build_quoted(I18n.locale.to_s)) + end + end + + # Allow ransacker to search by user attributes + [:name, :email, :nickname].each do |field| + ransacker field do + Arel.sql("decidim_users.#{field}") + end + end + + def self.polling_officer?(user) + exists?(user:) + end + + def self.for(user) + where(user:) + end + + def role + return :president if presided_polling_station.present? + return :manager if managed_polling_station.present? + + :unassigned + end + + def polling_station + presided_polling_station || managed_polling_station + end + + def self.log_presenter_class_for(_log) + Decidim::Votings::AdminLog::PollingOfficerPresenter + end + + private + + # Private: check if the voting and the user have the same organization + def user_and_voting_same_organization + return if voting.nil? || user.nil? + + errors.add(:voting, :different_organization) unless user.organization == voting.organization + end + + # Private: check if the voting and the user have the same organization + def either_president_or_manager + return if presided_polling_station.nil? || managed_polling_station.nil? + + errors.add(:presided_polling_station, :president_and_manager) + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/polling_station.rb b/decidim-elections/app/models/decidim/votings/polling_station.rb new file mode 100644 index 00000000..663e33cd --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/polling_station.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # The data store for a PollingStation in the Votings::Voting partecipatory space. + class PollingStation < ApplicationRecord + include Traceable + include Loggable + include Decidim::TranslatableResource + include Decidim::FilterableResource + + translatable_fields :title, :location, :location_hints + + belongs_to :voting, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::Voting", inverse_of: :polling_stations + has_many :polling_station_managers, + foreign_key: "managed_polling_station_id", + class_name: "Decidim::Votings::PollingOfficer", + inverse_of: :managed_polling_station, + dependent: :nullify + has_one :polling_station_president, + foreign_key: "presided_polling_station_id", + class_name: "Decidim::Votings::PollingOfficer", + inverse_of: :presided_polling_station, + dependent: :nullify + has_many :in_person_votes, + foreign_key: "decidim_votings_polling_station_id", + class_name: "Decidim::Votings::InPersonVote", + inverse_of: :polling_station, + dependent: :restrict_with_exception + has_many :closures, + foreign_key: "decidim_votings_polling_station_id", + class_name: "Decidim::Votings::PollingStationClosure", + inverse_of: :polling_station, + dependent: :restrict_with_exception + validate :polling_station_managers_same_voting + validate :polling_station_president_same_voting + + alias participatory_space voting + + # Allow ransacker to search for a key in a hstore column (`title`.`en`) + ransacker_i18n :title + + [:manager, :president].each do |role| + [:name, :email, :nickname].each do |field| + ransacker "#{role}_#{field}".to_sym do + Arel.sql("#{role}_user.#{field}") + end + end + end + + geocoded_by :address + + def missing_officers? + polling_station_president.nil? || polling_station_managers.empty? + end + + def slug + "polling_station_#{id}" + end + + def closure_for(election) + closures.find_by(election:) + end + + def self.log_presenter_class_for(_log) + Decidim::Votings::AdminLog::PollingStationPresenter + end + + private + + # Private: check if the president is in the same voting + def polling_station_president_same_voting + return if polling_station_president.nil? + + errors.add(:polling_station_president, :different_voting) unless voting == polling_station_president.voting + end + + # Private: check if the managers are in the same voting + def polling_station_managers_same_voting + return if polling_station_managers.empty? + + errors.add(:polling_station_managers, :different_voting) unless polling_station_managers.all? { |manager| manager.voting == voting } + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/polling_station_closure.rb b/decidim-elections/app/models/decidim/votings/polling_station_closure.rb new file mode 100644 index 00000000..de9fae2c --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/polling_station_closure.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # The data store for a Polling Station Closure. + class PollingStationClosure < ApplicationRecord + include Decidim::HasAttachments + enum phase: [:count, :results, :certificate, :signature, :complete], _suffix: true + + belongs_to :election, + foreign_key: "decidim_elections_election_id", + class_name: "Decidim::Elections::Election" + belongs_to :polling_station, + foreign_key: "decidim_votings_polling_station_id", + class_name: "Decidim::Votings::PollingStation" + belongs_to :polling_officer, + foreign_key: "decidim_votings_polling_officer_id", + class_name: "Decidim::Votings::PollingOfficer", + optional: true + has_many :results, + foreign_type: "closurable_type", + class_name: "Decidim::Elections::Result", + dependent: :destroy, + as: :closurable + + delegate :organization, to: :election + + # Public: Checks if the closure has been signed by the polling officer or not. + # + # Returns Boolean. + def signed? + signed_at.present? + end + + # Public: Checks if the closure has been validated by the monitoring committee or not. + # + # Returns Boolean. + def validated? + validated_at.present? + end + end + end +end diff --git a/decidim-elections/app/models/decidim/votings/voting.rb b/decidim-elections/app/models/decidim/votings/voting.rb new file mode 100644 index 00000000..45aff477 --- /dev/null +++ b/decidim-elections/app/models/decidim/votings/voting.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class Voting < ApplicationRecord + include Traceable + include Loggable + include Decidim::Followable + include Decidim::Participable + include Decidim::ParticipatorySpaceResourceable + include Decidim::Randomable + include Decidim::Searchable + include Decidim::TranslatableResource + include Decidim::ScopableParticipatorySpace + include Decidim::Publicable + include Decidim::HasUploadValidations + include Decidim::HasAttachments + include Decidim::HasAttachmentCollections + include Decidim::FilterableResource + + enum voting_type: [:in_person, :online, :hybrid].index_with(&:to_s), _suffix: :voting + + translatable_fields :title, :description + + belongs_to :organization, + foreign_key: "decidim_organization_id", + class_name: "Decidim::Organization" + has_one :dataset, + foreign_key: "decidim_votings_voting_id", + class_name: "Decidim::Votings::Census::Dataset", + dependent: :destroy + has_many :components, as: :participatory_space, dependent: :destroy + has_many :categories, + foreign_key: "decidim_participatory_space_id", + foreign_type: "decidim_participatory_space_type", + dependent: :destroy, + as: :participatory_space + has_many :polling_stations, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::PollingStation", inverse_of: :voting, dependent: :destroy + has_many :polling_officers, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::PollingOfficer", inverse_of: :voting, dependent: :destroy + has_many :monitoring_committee_members, + foreign_key: "decidim_votings_voting_id", + class_name: "Decidim::Votings::MonitoringCommitteeMember", + inverse_of: :voting, + dependent: :destroy + has_many :ballot_styles, foreign_key: "decidim_votings_voting_id", class_name: "Decidim::Votings::BallotStyle", inverse_of: :voting, dependent: :destroy + + validates :slug, uniqueness: { scope: :organization } + validates :slug, presence: true, format: { with: Decidim::Votings::Voting.slug_format } + + has_one_attached :banner_image + validates_upload :banner_image, uploader: Decidim::BannerImageUploader + + has_one_attached :introductory_image + validates_upload :introductory_image, uploader: Decidim::BannerImageUploader + + scope :upcoming, -> { published.where("start_time > ?", Time.now.utc) } + scope :active, lambda { + published + .where("start_time <= ?", Time.now.utc) + .where("end_time >= ?", Time.now.utc) + } + scope :finished, -> { published.where("end_time < ?", Time.now.utc) } + scope :order_by_most_recent, -> { order(created_at: :desc) } + scope :promoted, -> { published.where(promoted: true) } + + scope_search_multi :with_any_date, [:active, :upcoming, :finished] + + def upcoming? + start_time > Time.now.utc + end + + def active? + start_time <= Time.now.utc && end_time >= Time.now.utc + end + + def finished? + end_time < Time.now.utc + end + + def period_status + if finished? + :finished + elsif active? + :ongoing + else + :upcoming + end + end + + searchable_fields({ + scope_id: :decidim_scope_id, + participatory_space: :itself, + A: :title, + B: :description, + datetime: :published_at + }, + index_on_create: ->(_voting) { false }, + index_on_update: ->(voting) { voting.visible? }) + + def self.log_presenter_class_for(_log) + Decidim::Votings::AdminLog::VotingPresenter + end + + # Allow ransacker to search for a key in a hstore column (`title`.`en`) + ransacker :title do |parent| + Arel::Nodes::InfixOperation.new("->>", parent.table[:title], Arel::Nodes.build_quoted(I18n.locale.to_s)) + end + + def to_param + slug + end + + def cta_button_text_key + return :vote if published? && active? + + :more_info + end + + def attachment_context + :admin + end + + def scopes_enabled + true + end + + def polling_stations_with_missing_officers? + !online_voting? && polling_stations.any?(&:missing_officers?) + end + + def available_polling_officers + polling_officers + .where(presided_polling_station_id: nil) + .where(managed_polling_station_id: nil) + end + + def has_ballot_styles? + ballot_styles.exists? + end + + def check_census_enabled? + dataset.present? && show_check_census? + end + + def elections + Decidim::Elections::Election.where(component: components) + end + + def published_elections + Decidim::Elections::Election.where(component: components.published).published + end + + # Methods for Votings Space <-> Elections Component interaction + + def complete_election_data(election, election_data) + election_data[:polling_stations] = polling_stations.map(&:slug) + election_data[:ballot_styles] = ballot_styles.map do |ballot_style| + questions = ballot_style.questions_for(election) + [ballot_style.slug, questions.map(&:slug)] if questions.any? + end.compact.to_h + end + + def vote_flow_for(election) + Decidim::Votings::CensusVoteFlow.new(election) + end + + # Create i18n ransackers for :title and :description. + # Create the :search_text ransacker alias for searching from both of these. + ransacker_i18n_multi :search_text, [:title, :description] + + def self.ransackable_scopes(_auth_object = nil) + [:with_any_date] + end + end + end +end diff --git a/decidim-elections/app/packs/entrypoints/decidim_elections.js b/decidim-elections/app/packs/entrypoints/decidim_elections.js new file mode 100644 index 00000000..78ef859c --- /dev/null +++ b/decidim-elections/app/packs/entrypoints/decidim_elections.js @@ -0,0 +1,14 @@ +import "src/decidim/elections/election_log"; +import "src/decidim/elections/trustee/key_ceremony"; +import "src/decidim/elections/trustee/tally"; +import "src/decidim/elections/trustee/trustee_zone"; + +import "src/decidim/elections/voter/casting-vote"; +import "src/decidim/elections/voter/new-vote"; +import "src/decidim/elections/voter/verify-vote"; + +// Images +require.context("../images", true) + +// CSS +import "stylesheets/decidim/elections/elections.scss" diff --git a/decidim-elections/app/packs/entrypoints/decidim_elections_admin.js b/decidim-elections/app/packs/entrypoints/decidim_elections_admin.js new file mode 100644 index 00000000..5b7e2840 --- /dev/null +++ b/decidim-elections/app/packs/entrypoints/decidim_elections_admin.js @@ -0,0 +1,9 @@ +import "src/decidim/elections/admin/vote_statistics"; +import "src/decidim/elections/admin/trustees_process"; +import "src/decidim/elections/admin/pending_action"; + +// Images +require.context("../images", true) + +// CSS +import "stylesheets/decidim/elections/admin/elections.scss" diff --git a/decidim-elections/app/packs/entrypoints/decidim_votings.js b/decidim-elections/app/packs/entrypoints/decidim_votings.js new file mode 100644 index 00000000..c3eda012 --- /dev/null +++ b/decidim-elections/app/packs/entrypoints/decidim_votings.js @@ -0,0 +1,11 @@ +import "src/decidim/votings/in-person-vote"; +import "src/decidim/votings/polling_officer_zone/edit-closure"; +import "src/decidim/votings/polling_officer_zone/sign-closure"; +import "src/decidim/votings/polling_officer_zone/in-person-vote"; +import "src/decidim/votings/polling_officer_zone/new-closure"; + +// Images +require.context("../images", true) + +// CSS +import "stylesheets/decidim/votings/votings.scss" diff --git a/decidim-elections/app/packs/entrypoints/decidim_votings_admin.js b/decidim-elections/app/packs/entrypoints/decidim_votings_admin.js new file mode 100644 index 00000000..f2af003d --- /dev/null +++ b/decidim-elections/app/packs/entrypoints/decidim_votings_admin.js @@ -0,0 +1,10 @@ +import "src/decidim/votings/admin/update_census_dataset_status"; +import "src/decidim/votings/admin/polling_stations_form"; +import "src/decidim/votings/admin/polling_officers_picker"; +import "src/decidim/votings/admin/voting_user_role_form"; + +// Images +require.context("../images", true) + +// CSS +import "stylesheets/decidim/votings/admin/votings.scss" diff --git a/decidim-elections/app/packs/images/decidim/elections/decidim_elections.svg b/decidim-elections/app/packs/images/decidim/elections/decidim_elections.svg new file mode 100644 index 00000000..1db02938 --- /dev/null +++ b/decidim-elections/app/packs/images/decidim/elections/decidim_elections.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/decidim-elections/app/packs/images/decidim/votings/decidim_votings.svg b/decidim-elections/app/packs/images/decidim/votings/decidim_votings.svg new file mode 100644 index 00000000..c7f3740d --- /dev/null +++ b/decidim-elections/app/packs/images/decidim/votings/decidim_votings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/decidim-elections/app/packs/src/decidim/elections/admin/pending_action.js b/decidim-elections/app/packs/src/decidim/elections/admin/pending_action.js new file mode 100644 index 00000000..033fb2de --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/admin/pending_action.js @@ -0,0 +1,19 @@ +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; +import { Client } from "@decidim/decidim-bulletin_board"; + +$(() => { + const $form = $("form.step"); + const $pendingAction = $form.find("#pending_action"); + + if ($pendingAction.length) { + const bulletinBoardClient = new Client({ + apiEndpointUrl: $pendingAction.data("apiEndpointUrl") + }); + const messageId = $pendingAction.data("messageId"); + + bulletinBoardClient.waitForPendingMessageToBeProcessed(messageId).then(() => { + $form.trigger("submit"); + }); + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/admin/trustees_process.js b/decidim-elections/app/packs/src/decidim/elections/admin/trustees_process.js new file mode 100644 index 00000000..e9a25ea3 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/admin/trustees_process.js @@ -0,0 +1,130 @@ +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; +import { + Client, + Election, + MessageParser +} from "@decidim/decidim-bulletin_board"; + +const WAIT_TIME_MS = 10 * 1_000; + +$(async () => { + const $trusteesProcess = $("#trustees_process"); + + if ($trusteesProcess.length) { + const $checkingTrustees = $trusteesProcess.find(".trustee"); + const electionUniqueId = $trusteesProcess.data("electionUniqueId"); + const processType = $trusteesProcess.data("processType"); + const bulletinBoardClient = new Client({ + apiEndpointUrl: $trusteesProcess.data("apiEndpointUrl") + }); + const election = new Election({ + uniqueId: electionUniqueId, + bulletinBoardClient, + typesFilter: ["create_election", processType] + }); + + const authorityPublicKeyJSON = JSON.stringify( + $trusteesProcess.data("authorityPublicKey") + ); + const parser = new MessageParser({ authorityPublicKeyJSON }); + const trusteesStatuses = {}; + let lastMessageIndex = 0; + + const missingTrusteesAllowed = $trusteesProcess.data("missingTrusteesAllowed") || 0; + const checkPendingActionPath = $trusteesProcess.data("checkPendingActionPath"); + + // Fix buttons formaction, that is not working properly + const $form = $("form.step"); + $form.find("button").on("click", (event) => { + $form.attr("action", $(event.currentTarget).attr("formaction")); + $form.trigger("submit"); + }); + + const updateTrusteesStatuses = async () => { + await election.getLogEntries(); + + for ( + ; + lastMessageIndex < election.logEntries.length; + lastMessageIndex += 1 + ) { + const { messageIdentifier, decodedData } = await parser.parse( + election.logEntries[lastMessageIndex] + ); + + if (messageIdentifier.author.type === "t") { + trusteesStatuses[messageIdentifier.author.id] = true; + } else if ( + messageIdentifier.type === "tally" && + messageIdentifier.subtype === "missing_trustee" && + !(decodedData.trustee_id in trusteesStatuses) + ) { + trusteesStatuses[decodedData.trustee_id] = false; + } + } + } + + const checkPendingAction = async () => { + if (!checkPendingActionPath) { + return false + } + + try { + const response = await $.ajax({ + url: checkPendingActionPath, + method: "PATCH", + contentType: "application/json", + headers: { + "X-CSRF-Token": $("meta[name=csrf-token]").attr("content") + } + }) + + return response && response.status === "pending"; + } catch (err) { + return true; + } + } + + const checkTrusteesActivity = async () => { + await updateTrusteesStatuses(); + const pendingAction = await checkPendingAction(); + const missingTrustees = Object.values(trusteesStatuses).filter( + (present) => !present + ).length; + const allowReportMissing = missingTrustees < missingTrusteesAllowed; + + $checkingTrustees.each((_index, trustee) => { + const $trustee = $(trustee); + const trusteeSlug = $trustee.data("trusteeSlug"); + + if (trusteeSlug in trusteesStatuses) { + if (missingTrusteesAllowed > 0) { + $trustee.find(".js-report-missing-trustee").addClass("hide"); + } + $trustee.removeClass("loading"); + $trustee.find(".loading").hide(); + if (trusteesStatuses[trusteeSlug]) { + $trustee.find(".active").removeClass("hide"); + $trustee.find(".missing").addClass("hide"); + } else { + $trustee.find(".missing").removeClass("hide"); + } + } else if (allowReportMissing && !pendingAction) { + $trustee.find(".js-report-missing-trustee").removeClass("hide"); + } + }); + + if ( + Object.keys(trusteesStatuses).length === $checkingTrustees.length && + missingTrustees <= missingTrusteesAllowed && !pendingAction + ) { + $(".js-continue-link").removeClass("disabled"); + } else { + setTimeout(checkTrusteesActivity, WAIT_TIME_MS); + } + }; + + await checkTrusteesActivity(); + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/admin/vote_statistics.js b/decidim-elections/app/packs/src/decidim/elections/admin/vote_statistics.js new file mode 100644 index 00000000..7de7d28c --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/admin/vote_statistics.js @@ -0,0 +1,14 @@ +/* eslint-disable no-inline-comments */ +/* eslint-disable line-comment-position */ +// fetches Vote stats every 3 seconds + +$(() => { + const WAIT_TIME_MS = 3000; // 3s + const url = $("#vote-stats").data("refreshUrl"); + + if (url) { + setInterval(function() { + $("#vote-stats").load(url); + }, WAIT_TIME_MS); + } +}) diff --git a/decidim-elections/app/packs/src/decidim/elections/broken_promises_handler.js b/decidim-elections/app/packs/src/decidim/elections/broken_promises_handler.js new file mode 100644 index 00000000..ea1e02d9 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/broken_promises_handler.js @@ -0,0 +1,13 @@ +/* Fallback for non-handled failed promises */ +window.addEventListener("unhandledrejection", (event) => { + if (window.Decidim.currentDialogs["server-failure"]) { + document.getElementById("tech-info").innerHTML = event.reason + + if (event.reason.toString().indexOf("fetch") === -1) { + document.getElementById("communication_error").hidden = true + document.getElementById("generic_error").hidden = false + } + + window.Decidim.currentDialogs["server-failure"].open() + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/election_log.js b/decidim-elections/app/packs/src/decidim/elections/election_log.js new file mode 100644 index 00000000..f5dfd6a7 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/election_log.js @@ -0,0 +1,164 @@ +/** + * This file is responsible to get LogEntries + * for an election for the election log. + */ +import { Client, MessageParser } from "@decidim/decidim-bulletin_board"; + +$(async () => { + const isSafariBrowser = (/^((?!chrome|android).)*safari/i).test(navigator.userAgent); + if (isSafariBrowser) { + $("#not_supported_safari_browser").removeClass("hidden"); + } + + // UI Elements + const $electionLog = $("#election-log"); + + if ($electionLog.length) { + const $createElectionStep = $electionLog.find("#create-election-step"); + const $keyCeremonyStep = $electionLog.find("#key-ceremony-step"); + const $voteStep = $electionLog.find("#vote-step"); + const $tallyStep = $electionLog.find("#tally-step"); + const $resultStep = $electionLog.find("#results-step"); + + // Data + const authorityPublicKeyJSON = JSON.stringify( + $electionLog.data("authorityPublicKey") + ); + const bulletinBoardClient = new Client({ + apiEndpointUrl: $electionLog.data("apiEndpointUrl") + }); + const electionUniqueId = `${$electionLog.data( + "authoritySlug" + )}.${$electionLog.data("electionId")}`; + const parser = new MessageParser({ authorityPublicKeyJSON }); + const logEntries = await bulletinBoardClient.getElectionLogEntries({ + electionUniqueId: electionUniqueId, + types: [ + "create_election", + "start_key_ceremony", + "end_key_ceremony", + "start_vote", + "end_vote", + "start_tally", + "end_tally", + "publish_results" + ] + }); + + // Functions to be used for each step + + // adds the `iat` of the message to the UI + const setMessageTime = async (logEntryStep, uiStep) => { + if (!logEntryStep.signedData) { + uiStep.find("[data-time]").html(""); + return; + } + + const parsedData = await parser.parse(logEntryStep); + const messageTime = new Date(parsedData.decodedData.iat * 1000); + const year = messageTime.toDateString(); + const time = messageTime.toLocaleTimeString(); + + uiStep.find("[data-time]").html(`${year} ${time}`); + }; + + // adds the chained Hash of the message to the UI + const addChainedHash = (logEntryStep, uiStep) => { + const $hash = uiStep.find("[data-chained-hash]"); + + $hash.parent().attr("hidden", false); + $hash.html(logEntryStep.chainedHash); + }; + + // finds the logEntry for each step + const getLogEntryByMessageId = (step) => { + return logEntries.find((logEntry) => logEntry.messageId.includes(step)); + }; + + // CREATE ELECTION STEP + const createElectionLogEntry = getLogEntryByMessageId("create_election"); + if (createElectionLogEntry) { + $createElectionStep.find("[data-no-election-created]").attr("hidden", true); + $createElectionStep.find("[data-election-created]").attr("hidden", false); + + await setMessageTime(createElectionLogEntry, $createElectionStep); + + addChainedHash(createElectionLogEntry, $createElectionStep); + } + + // KEY CEREMONY STEP + const startKeyCeremonyLogEntry = getLogEntryByMessageId("start_key_ceremony"); + const endKeyCeremonyLogEntry = getLogEntryByMessageId("end_key_ceremony"); + + if (startKeyCeremonyLogEntry && !endKeyCeremonyLogEntry) { + $keyCeremonyStep.find("[data-key-ceremony-not-started]").attr("hidden", true); + + await setMessageTime(startKeyCeremonyLogEntry, $keyCeremonyStep); + + $keyCeremonyStep.find("[data-key-ceremony-started]").attr("hidden", false); + addChainedHash(startKeyCeremonyLogEntry, $keyCeremonyStep); + } else if (endKeyCeremonyLogEntry) { + $keyCeremonyStep.find("[data-key-ceremony-not-started]").attr("hidden", true); + + await setMessageTime(endKeyCeremonyLogEntry, $keyCeremonyStep); + + $keyCeremonyStep.find("[data-key-ceremony-started]").attr("hidden", true); + $keyCeremonyStep.find("[data-key-ceremony-completed]").attr("hidden", false); + addChainedHash(endKeyCeremonyLogEntry, $keyCeremonyStep); + } + + // VOTING STEP + const startVoteLogEntry = getLogEntryByMessageId("start_vote"); + const endVoteLogEntry = getLogEntryByMessageId("end_vote"); + + if (startVoteLogEntry && !endVoteLogEntry) { + $voteStep.find("[data-vote-not-started]").attr("hidden", true); + + await setMessageTime(startVoteLogEntry, $voteStep); + + $voteStep.find("[data-vote-started]").attr("hidden", false); + addChainedHash(startVoteLogEntry, $voteStep); + } else if (endVoteLogEntry) { + $voteStep.find("[data-vote-not-started]").attr("hidden", true); + + await setMessageTime(endVoteLogEntry, $voteStep); + + $voteStep.find("[data-vote-started]").attr("hidden", true); + $voteStep.find("[data-vote-completed]").attr("hidden", false); + addChainedHash(endVoteLogEntry, $voteStep); + } + + // TALLY STEP + const startTallyLogEntry = getLogEntryByMessageId("start_tally"); + const endTallyLogEntry = getLogEntryByMessageId("end_tally"); + + if (startTallyLogEntry && !endTallyLogEntry) { + $tallyStep.find("[data-tally-not-started]").attr("hidden", true); + + await setMessageTime(startTallyLogEntry, $tallyStep); + + $tallyStep.find("[data-tally-started]").attr("hidden", false); + addChainedHash(startTallyLogEntry, $tallyStep); + } else if (endTallyLogEntry) { + $tallyStep.find("[data-tally-not-started]").attr("hidden", true); + + await setMessageTime(endTallyLogEntry, $tallyStep); + + $tallyStep.find("[data-tally-started]").attr("hidden", true); + $tallyStep.find("[data-tally-completed]").attr("hidden", false); + addChainedHash(endTallyLogEntry, $tallyStep); + } + + // RESULTS STEP + const resultsLogEntry = getLogEntryByMessageId("publish_results"); + + if (resultsLogEntry) { + $resultStep.find("[data-results-not-published]").attr("hidden", true); + + await setMessageTime(resultsLogEntry, $resultStep); + + $resultStep.find("[data-results-published]").attr("hidden", false); + addChainedHash(resultsLogEntry, $resultStep); + } + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/trustee/key_ceremony.js b/decidim-elections/app/packs/src/decidim/elections/trustee/key_ceremony.js new file mode 100644 index 00000000..fe13ecb9 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/trustee/key_ceremony.js @@ -0,0 +1,165 @@ +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; + +import { + KeyCeremonyComponent, + MessageIdentifier, + IdentificationKeys, + MESSAGE_RECEIVED +} from "@decidim/decidim-bulletin_board"; + +import { TrusteeWrapperAdapter as DummyTrusteeWrapperAdapter } from "@decidim/voting_schemes-dummy"; +import { TrusteeWrapperAdapter as ElectionGuardTrusteeWrapperAdapter } from "@decidim/voting_schemes-electionguard"; + +/** + * This file is responsible to generate election keys, + * create a backup of keys for the trustee and + * update the election bulletin board status + */ +$(() => { + // UI Elements + const $keyCeremony = $("#trustee-step"); + const trusteeStep = $keyCeremony.data("currentStep") + + if ($keyCeremony.length && trusteeStep === "key_ceremony") { + const $startButton = $keyCeremony.find("#start"); + const $backButton = $keyCeremony.find("#back"); + + const getStepRow = (step) => { + return $(`#${step.replace(".", "-")}`); + }; + const TRUSTEE_AUTHORIZATION_EXPIRATION_TIME_IN_HOURS = 2; + + // Data + const bulletinBoardClientParams = { + apiEndpointUrl: $keyCeremony.data("apiEndpointUrl") + }; + const electionUniqueId = `${$keyCeremony.data( + "authoritySlug" + )}.${$keyCeremony.data("electionId")}`; + const authorityPublicKeyJSON = JSON.stringify( + $keyCeremony.data("authorityPublicKey") + ); + const schemeName = $keyCeremony.data("schemeName"); + + const trusteeContext = { + uniqueId: $keyCeremony.data("trusteeSlug"), + publicKeyJSON: JSON.stringify($keyCeremony.data("trusteePublicKey")) + }; + + let currentStep = null; + const trusteeIdentificationKeys = new IdentificationKeys( + trusteeContext.uniqueId, + trusteeContext.publicKeyJSON + ); + + // Use the correct trustee wrapper adapter + let trusteeWrapperAdapter = null; + + if (schemeName === "dummy") { + trusteeWrapperAdapter = new DummyTrusteeWrapperAdapter({ + trusteeId: trusteeContext.uniqueId + }); + } else if (schemeName === "electionguard") { + trusteeWrapperAdapter = new ElectionGuardTrusteeWrapperAdapter({ + trusteeId: trusteeContext.uniqueId, + workerUrl: "/assets/electionguard/webworker.js" + }); + } else { + throw new Error(`Voting scheme ${schemeName} not supported.`); + } + + // Use the key ceremony component and bind all UI events + const component = new KeyCeremonyComponent({ + authorityPublicKeyJSON, + trusteeUniqueId: trusteeContext.uniqueId, + trusteeIdentificationKeys, + trusteeWrapperAdapter + }); + + trusteeIdentificationKeys.present(async (exists) => { + if (exists) { + await component.setupElection({ + bulletinBoardClientParams, + electionUniqueId, + authorizationExpirationTimestamp: + Math.ceil(Number(new Date()) / 1000) + + TRUSTEE_AUTHORIZATION_EXPIRATION_TIME_IN_HOURS * 3600 + }); + + await component.bindEvents({ + onBindRestoreButton(onEventTriggered) { + $("#restore-button-input").on("change", onEventTriggered); + }, + onBindStartButton(onEventTriggered) { + $startButton.on("click", onEventTriggered); + }, + onBindBackupButton(backupData, backupFilename, onEventTriggered) { + const $backupButton = $("#download-election-keys"); + + $backupButton.attr( + "href", + `data:text/plain;charset=utf-8,${backupData}` + ); + $backupButton.attr("download", backupFilename); + $backupButton.on("click", onEventTriggered); + }, + onEvent(event) { + let messageIdentifier = MessageIdentifier.parse( + event.message.messageId + ); + + if (event.type === MESSAGE_RECEIVED) { + if (currentStep && currentStep !== messageIdentifier.typeSubtype) { + const $previousStep = getStepRow(currentStep); + $previousStep.attr("data-step-status", "completed"); + } + currentStep = messageIdentifier.typeSubtype; + + const $currentStep = getStepRow(currentStep); + if ($currentStep.data("step-status") !== "completed") { + $currentStep.attr("data-step-status", "processing"); + } + } + }, + onComplete() { + const $allSteps = $(".step_status"); + $allSteps.attr("data-step-status", "completed"); + + $startButton.attr("hidden", true); + $backButton.attr("hidden", false); + + $.ajax({ + method: "PATCH", + url: $keyCeremony.data("updateElectionStatusUrl"), + contentType: "application/json", + data: JSON.stringify({ + status: "key_ceremony" + }), + headers: { + "X-CSRF-Token": $("meta[name=csrf-token]").attr("content") + } + }); + }, + onStart() { + $startButton.prop("disabled", true); + }, + onRestore() { + window.Decidim.currentDialogs["show-restore-modal"].close() + }, + onTrusteeNeedsToBeRestored() { + window.Decidim.currentDialogs["show-restore-modal"].open() + }, + onBackupNeeded() { + window.Decidim.currentDialogs["show-backup-modal"].open() + }, + onBackupStarted() { + window.Decidim.currentDialogs["show-backup-modal"].close() + } + }); + + $startButton.prop("disabled", false); + } + }); + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/trustee/tally.js b/decidim-elections/app/packs/src/decidim/elections/trustee/tally.js new file mode 100644 index 00000000..0cc8929c --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/trustee/tally.js @@ -0,0 +1,147 @@ +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; + +import { + TallyComponent, + IdentificationKeys, + MessageIdentifier, + MESSAGE_RECEIVED +} from "@decidim/decidim-bulletin_board"; + +import { TrusteeWrapperAdapter as DummyTrusteeWrapperAdapter } from "@decidim/voting_schemes-dummy"; +import { TrusteeWrapperAdapter as ElectionGuardTrusteeWrapperAdapter } from "@decidim/voting_schemes-electionguard"; + +$(() => { + // UI Elements + const $tally = $("#trustee-step"); + const trusteeStep = $tally.data("currentStep") + + if ($tally.length && trusteeStep === "tally_started") { + const $startButton = $tally.find("#start"); + const $backButton = $tally.find("#back"); + + const getStepRow = (step) => { + return $(`#${step.replace(".", "-")}`); + }; + + const TRUSTEE_AUTHORIZATION_EXPIRATION_TIME_IN_HOURS = 2; + + // Data + const bulletinBoardClientParams = { + apiEndpointUrl: $tally.data("apiEndpointUrl") + }; + const electionUniqueId = `${$tally.data("authoritySlug")}.${$tally.data( + "electionId" + )}`; + const authorityPublicKeyJSON = JSON.stringify( + $tally.data("authorityPublicKey") + ); + const schemeName = $tally.data("schemeName"); + + const trusteeContext = { + uniqueId: $tally.data("trusteeSlug"), + publicKeyJSON: JSON.stringify($tally.data("trusteePublicKey")) + }; + const trusteeIdentificationKeys = new IdentificationKeys( + trusteeContext.uniqueId, + trusteeContext.publicKeyJSON + ); + let currentStep = null; + + // Use the correct trustee wrapper adapter + let trusteeWrapperAdapter = null; + + if (schemeName === "dummy") { + trusteeWrapperAdapter = new DummyTrusteeWrapperAdapter({ + trusteeId: trusteeContext.uniqueId + }); + } else if (schemeName === "electionguard") { + trusteeWrapperAdapter = new ElectionGuardTrusteeWrapperAdapter({ + trusteeId: trusteeContext.uniqueId, + workerUrl: "/assets/electionguard/webworker.js" + }); + } else { + throw new Error(`Voting scheme ${schemeName} not supported.`); + } + + // Use the tally component and bind all UI events + const component = new TallyComponent({ + authorityPublicKeyJSON, + trusteeUniqueId: trusteeContext.uniqueId, + trusteeIdentificationKeys, + trusteeWrapperAdapter + }); + + const bindComponentEvents = async () => { + await component.setupElection({ + bulletinBoardClientParams, + electionUniqueId, + authorizationExpirationTimestamp: + Math.ceil(Number(new Date()) / 1000) + + TRUSTEE_AUTHORIZATION_EXPIRATION_TIME_IN_HOURS * 3600 + }); + + await component.bindEvents({ + onEvent(event) { + let messageIdentifier = MessageIdentifier.parse( + event.message.messageId + ); + + if (event.type === MESSAGE_RECEIVED) { + if (currentStep && currentStep !== messageIdentifier.typeSubtype) { + const $previousStep = getStepRow(currentStep); + $previousStep.attr("data-step-status", "completed"); + } + currentStep = messageIdentifier.typeSubtype; + + const $currentStep = getStepRow(currentStep); + if ($currentStep.data("step-status") !== "completed") { + $currentStep.attr("data-step-status", "processing"); + } + } + }, + onBindStartButton(onEventTriggered) { + $startButton.on("click", onEventTriggered); + }, + onStart() { + $startButton.prop("disabled", true); + }, + onComplete() { + const $allSteps = $(".step_status"); + $allSteps.attr("data-step-status", "completed"); + + $startButton.attr("hidden", true); + $backButton.attr("hidden", false); + + $.ajax({ + method: "PATCH", + url: $tally.data("updateElectionStatusUrl"), + contentType: "application/json", + data: JSON.stringify({ + status: "tally_started" + }), + headers: { + "X-CSRF-Token": $("meta[name=csrf-token]").attr("content") + } + }); + }, + onTrusteeNeedsToBeRestored() { + window.Decidim.currentDialogs["show-restore-modal"].open() + }, + onBindRestoreButton(onEventTriggered) { + $("#restore-button-input").on("change", onEventTriggered); + }, + onRestore() { + window.Decidim.currentDialogs["show-restore-modal"].close() + } + }); + $startButton.prop("disabled", false); + }; + + trusteeIdentificationKeys.present(async (exists) => { + if (exists) { + await bindComponentEvents(); + } + }); + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/trustee/trustee_zone.js b/decidim-elections/app/packs/src/decidim/elections/trustee/trustee_zone.js new file mode 100644 index 00000000..c29f96bb --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/trustee/trustee_zone.js @@ -0,0 +1,64 @@ +/* eslint-disable require-jsdoc, no-alert, func-style */ + +import { IdentificationKeys } from "@decidim/decidim-bulletin_board"; + +$(() => { + function identificationKeys() { + const $form = $("#trustee_zone form"); + const $trusteeSlug = $("#trustee_slug", $form); + const $trusteePublicKey = $("#trustee_public_key", $form); + + window.trusteeIdentificationKeys = new IdentificationKeys( + $trusteeSlug.val(), + $trusteePublicKey.val() + ); + + if (!window.trusteeIdentificationKeys.browserSupport) { + $("#not_supported_browser").removeClass("hidden"); + return; + } + + const $submit = $("#submit_identification_keys"); + const $generate = $("#generate_identification_keys"); + const $upload = $("#upload_identification_keys"); + + $("button", $generate).on("click", () => { + window.trusteeIdentificationKeys. + generate(). + then(() => { + $trusteePublicKey.val( + JSON.stringify(window.trusteeIdentificationKeys.publicKey) + ); + $submit.attr("hidden", false); + $generate.attr("hidden", true); + }). + catch(() => { + alert($generate.data("error")); + }); + }); + + $("button", $submit).click(() => { + $trusteePublicKey.val(""); + $submit.attr("hidden", true); + }); + + $("button", $upload).click(() => { + window.trusteeIdentificationKeys. + upload(). + then(() => { + $upload.attr("hidden", true); + }). + catch((errorMessage) => { + alert($upload.data(errorMessage)); + }); + }); + + window.trusteeIdentificationKeys.present((result) => { + $upload.attr("hidden", result); + }); + } + + $(document).ready(() => { + identificationKeys(); + }); +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/voter/casting-vote.js b/decidim-elections/app/packs/src/decidim/elections/voter/casting-vote.js new file mode 100644 index 00000000..42bb238f --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/voter/casting-vote.js @@ -0,0 +1,19 @@ +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; + +import { Client } from "@decidim/decidim-bulletin_board"; + +$(async () => { + const $castingVoteWrapper = $("#casting-vote-wrapper"); + + if ($castingVoteWrapper.length) { + const bulletinBoardClient = new Client({ + apiEndpointUrl: $castingVoteWrapper.data("apiEndpointUrl") + }); + const messageId = $castingVoteWrapper.data("messageId"); + + await bulletinBoardClient.waitForPendingMessageToBeProcessed(messageId); + + $("form.update_vote_status").trigger("submit"); + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/voter/new-vote.js b/decidim-elections/app/packs/src/decidim/elections/voter/new-vote.js new file mode 100644 index 00000000..d23db294 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/voter/new-vote.js @@ -0,0 +1,104 @@ +/* eslint-disable no-inner-declarations */ +/* eslint-disable require-jsdoc */ +/* eslint-disable func-style */ +/* eslint-disable no-console */ +import VoteQuestionsComponent from "src/decidim/elections/voter/vote_questions.component"; + +$(async () => { + // UI Elements + const $voteWrapper = $("#vote-wrapper"); + + if ($voteWrapper.length) { + const $ballotHash = $voteWrapper.find("#ballot-hash"); + const ballotStyleId = $voteWrapper.data("ballotStyleId"); + const isPreview = $voteWrapper.data("previewMode"); + + // Use the questions component + const questionsComponent = new VoteQuestionsComponent($voteWrapper); + questionsComponent.init(); + + // Activates the events associated to the forms after show a new step + $(document).on("on:toggle", () => questionsComponent.init()); + + async function setup(component) { + // Get the vote component and bind it to all UI events + const voteComponent = component($voteWrapper); + + try { + await voteComponent.bindEvents({ + onBindEncryptButton(onEventTriggered) { + $("[id='next-encrypting']").on("click", onEventTriggered); + }, + onStart() {}, + onVoteEncryption(validVoteFn) { + const getFormData = (formData) => { + return formData.serializeArray().reduce((acc, { name, value }) => { + if (!acc[name]) { + acc[name] = []; + } + acc[name] = [...acc[name], `${name}_${value}`]; + return acc; + }, {}); + }; + const formData = getFormData($voteWrapper.find(".answer_input,.nota_input")); + validVoteFn(formData, ballotStyleId); + }, + castOrAuditBallot({ encryptedData, encryptedDataHash }) { + $voteWrapper.find("#step-encrypting").attr("hidden", true); + $ballotHash.text(encryptedDataHash); + + // show the next step + $voteWrapper.find("#step-ballot_decision").attr("hidden", false); + // simulates a toggle click, in order to update the wizard step + document.dispatchEvent(new Event("on:toggle")); + + const $form = $("form.new_vote"); + $("#vote_encrypted_data", $form).val(encryptedData); + $("#vote_encrypted_data_hash", $form).val(encryptedDataHash); + }, + onBindAuditBallotButton(onEventTriggered) { + $("#audit_ballot").on("click", onEventTriggered); + }, + onBindCastBallotButton(onEventTriggered) { + $("#cast_ballot").on("click", onEventTriggered); + }, + onAuditBallot(auditedData, auditedDataFileName) { + const vote = JSON.stringify(auditedData); + const link = document.createElement("a"); + $voteWrapper.find("#cast_ballot").attr("hidden", true); + $voteWrapper.find("#back").attr("hidden", false); + questionsComponent.voteCasted = true; + + link.setAttribute("href", `data:text/plain;charset=utf-8,${vote}`); + link.setAttribute("download", auditedDataFileName); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }, + onAuditComplete() { + console.log("Audit completed"); + }, + onCastBallot() { + questionsComponent.voteCasted = true; + $("#cast_ballot").prop("disabled", true); + }, + onCastComplete() { + console.log("Cast completed"); + }, + onInvalid() { + console.log("Something went wrong."); + } + }); + } catch (error) { + console.error(error); + questionsComponent.errored(); + } + } + + if (isPreview) { + import("./setup-preview").then(({ default: module }) => setup(module)) + } else { + import("./setup-vote").then(({ default: module }) => setup(module)) + } + } +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/voter/setup-preview.js b/decidim-elections/app/packs/src/decidim/elections/voter/setup-preview.js new file mode 100644 index 00000000..a1c7b2cc --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/voter/setup-preview.js @@ -0,0 +1,75 @@ +/* eslint-disable require-jsdoc */ +// The wait time used to simulate the encryption of the vote during the preview +const FAKE_ENCRYPTION_TIME = 1000; + +class PreviewVoteComponent { + constructor({ electionUniqueId, voterUniqueId }) { + this.electionUniqueId = electionUniqueId; + this.voterUniqueId = voterUniqueId; + } + + async bindEvents({ + onBindEncryptButton, + onStart, + onVoteEncryption, + castOrAuditBallot, + onBindAuditBallotButton, + onBindCastBallotButton, + onAuditBallot, + onCastBallot, + onAuditComplete, + onCastComplete, + onInvalid + }) { + onBindEncryptButton(async () => { + onStart(); + onVoteEncryption( + (plainVote) => { + this.fakeEncrypt(plainVote).then((ballot) => { + castOrAuditBallot(ballot); + onBindAuditBallotButton(() => { + onAuditBallot( + ballot, + `${this.voterUniqueId}-election-${this.electionUniqueId}.txt` + ); + onAuditComplete(); + }); + + onBindCastBallotButton(async () => { + await onCastBallot(ballot); + onCastComplete(); + }); + }); + }, + () => { + onInvalid(); + } + ); + }); + } + async fakeEncrypt(plainVote) { + await new Promise((resolve) => setTimeout(resolve, FAKE_ENCRYPTION_TIME)); + + return { + encryptedData: plainVote, + encryptedDataHash: this.generateHexString(64), + auditableData: plainVote + }; + } + generateHexString(length) { + return Array(length). + fill(""). + map(() => Math.random().toString(16).charAt(2)). + join(""); + } +} + +export default function setupVoteComponent($voteWrapper) { + const voterUniqueId = $voteWrapper.data("voterId"); + const electionUniqueId = $voteWrapper.data("electionUniqueId"); + + return new PreviewVoteComponent({ + electionUniqueId, + voterUniqueId + }); +} diff --git a/decidim-elections/app/packs/src/decidim/elections/voter/setup-vote.js b/decidim-elections/app/packs/src/decidim/elections/voter/setup-vote.js new file mode 100644 index 00000000..0af406e8 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/voter/setup-vote.js @@ -0,0 +1,49 @@ +/* eslint-disable require-jsdoc */ + +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; +import { VoteComponent } from "@decidim/decidim-bulletin_board"; + +import * as VotingSchemesDummy from "@decidim/voting_schemes-dummy"; +const DummyVoterWrapperAdapter = VotingSchemesDummy.VoterWrapperAdapter; +import * as VotingSchemesElectionGuard from "@decidim/voting_schemes-electionguard"; +const ElectionGuardVoterWrapperAdapter = + VotingSchemesElectionGuard.VoterWrapperAdapter; + +export default function setupVoteComponent($voteWrapper) { + // Data + const bulletinBoardClientParams = { + apiEndpointUrl: $voteWrapper.data("apiEndpointUrl") + }; + const electionUniqueId = $voteWrapper.data("electionUniqueId"); + const authorityPublicKeyJSON = JSON.stringify( + $voteWrapper.data("authorityPublicKey") + ); + const voterUniqueId = $voteWrapper.data("voterId"); + const schemeName = $voteWrapper.data("schemeName"); + + // Use the correct voter wrapper adapter + let voterWrapperAdapter = null; + + if (schemeName === "dummy") { + voterWrapperAdapter = new DummyVoterWrapperAdapter({ + voterId: voterUniqueId + }); + } else if (schemeName === "electionguard") { + voterWrapperAdapter = new ElectionGuardVoterWrapperAdapter({ + voterId: voterUniqueId, + workerUrl: "/assets/electionguard/webworker.js" + }); + } else { + throw new Error(`Voting scheme ${schemeName} not supported.`); + } + + // Returns the vote component + return new VoteComponent({ + bulletinBoardClientParams, + authorityPublicKeyJSON, + electionUniqueId, + voterUniqueId, + voterWrapperAdapter + }); +} diff --git a/decidim-elections/app/packs/src/decidim/elections/voter/verify-vote.js b/decidim-elections/app/packs/src/decidim/elections/voter/verify-vote.js new file mode 100644 index 00000000..b9069e9d --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/voter/verify-vote.js @@ -0,0 +1,77 @@ +/* eslint-disable require-jsdoc, prefer-template, func-style, id-length, no-use-before-define, init-declarations, no-invalid-this */ +/* eslint no-unused-vars: ["error", { "args": "none" }] */ + +import { Client } from "@decidim/decidim-bulletin_board"; + +$(() => { + const $voteVerifyWrapper = $("#verify-vote-wrapper"); + + if (!$voteVerifyWrapper.length) { + return + } + + const $verifySubmitButton = $voteVerifyWrapper.find("[type=submit]"); + + let $formData = $voteVerifyWrapper.find(".vote-identifier"); + + function initStep() { + toggleVerifyButton(); + onVoteIdentifierChange(); + } + + initStep(); + + function onVoteIdentifierChange() { + $formData.on("keyup input", (event) => { + toggleVerifyButton(); + hideSuccessCallout(); + hideErrorCallout(); + }); + } + + function toggleVerifyButton() { + if ($formData.val().length > 5) { + $($verifySubmitButton).removeClass("disabled"); + } else { + $($verifySubmitButton).addClass("disabled"); + } + } + + function hideSuccessCallout() { + $voteVerifyWrapper.find("#verify-vote-success").attr("hidden", true); + } + + function hideErrorCallout() { + $voteVerifyWrapper.find("#verify-vote-error").attr("hidden", true); + } + + $verifySubmitButton.on("click", (event) => { + event.preventDefault(); + verifyVoteIdentifier(); + }); + + function verifyVoteIdentifier() { + const bulletinBoardClient = new Client({ + apiEndpointUrl: $voteVerifyWrapper.data("apiEndpointUrl") + }); + + bulletinBoardClient. + getLogEntry({ + electionUniqueId: $voteVerifyWrapper.data("electionUniqueId"), + contentHash: $formData.val() + }). + then((result) => { + if (result) { + hideErrorCallout(); + $voteVerifyWrapper.find("#verify-vote-success").attr("hidden", false); + } else { + hideSuccessCallout(); + $voteVerifyWrapper.find("#verify-vote-error").attr("hidden", false); + } + }); + } + + $(document).on("on.zf.toggler", (event) => { + initStep(); + }); +}); diff --git a/decidim-elections/app/packs/src/decidim/elections/voter/vote_questions.component.js b/decidim-elections/app/packs/src/decidim/elections/voter/vote_questions.component.js new file mode 100644 index 00000000..7d6bf8c7 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/elections/voter/vote_questions.component.js @@ -0,0 +1,170 @@ +/** + * Vote Questions component. + */ + +export default class VoteQuestionsComponent { + constructor($voteWrapper) { + this.$voteWrapper = $voteWrapper; + this.$currentStep = ""; + this.$currentStepMaxSelection = ""; + this.$answerCounter = 0; + this.voteCasted = false; + window.onbeforeunload = () => { + if (this.voteCasted) { + return null; + } + return ""; + } + } + + init() { + this.setCurrentStep(); + this.toggleContinueButton(); + + $("[data-counter-selection]").text(this.$answerCounter); + this.answerCounter(); + this.disableCheckbox(); + } + + setCurrentStep() { + this.$currentStep = this.$voteWrapper.find('[id^="step"]:not([hidden])') + this.$confirmButton = this.$currentStep.find('[id^="next"]'); + + this.setSelections(); + this.onSelectionChange(); + this.updateWizardSteps(this.$currentStep.attr("id")); + } + + errored() { + this.$currentStep.attr("hidden", true); + this.$currentStep = this.$voteWrapper.find("#failed").attr("hidden", false); + } + + toggleContinueButton() { + // ignore the button if the step is not a question + if (!this.isQuestion(this.$currentStep.attr("id"))) { + return + } + + if (this.checkAnswers()) { + // next step enabled + this.$confirmButton.attr("disabled", false) + } else { + // next step disabled + this.$confirmButton.attr("disabled", true) + } + } + + // check if answers are correctly checked + checkAnswers() { + const currentAnswersChecked = $(`#${this.$currentStep.attr("id")} .answer_input:checked`).length + const notaAnswerChecked = $(`#${this.$currentStep.attr("id")} .nota_input:checked`).length + + return ((currentAnswersChecked >= 1 || notaAnswerChecked > 0) && (currentAnswersChecked <= this.$currentStepMaxSelection)); + } + + answerCounter() { + let checked = $(`#${this.$currentStep.attr("id")} .answer_input:checked`).length + $("[data-counter-selection]").text(checked); + } + + // disable checkboxes if NOTA option is selected + disableCheckbox() { + $("[data-disabled-by]").on("click", (event) => { + if ($(event.target).attr("aria-disabled") || $(event.target).hasClass("is-disabled")) { + event.preventDefault(); + } + }); + + $("[data-disable-check]").on("change", (event) => { + let checkId = $(event.target).attr("id"); + let checkStatus = event.target.checked; + + this.$currentStep.find(`[data-disabled-by='${checkId}']`).each((_index, element) => { + const $checkbox = $(element).find("input[type=checkbox]") + + if (checkStatus) { + $checkbox.prop("disabled", true); + $checkbox.prop("checked", false); + $(element).attr("aria-disabled", true); + } else { + $checkbox.prop("disabled", false); + $(element).removeAttr("aria-disabled"); + } + }); + }); + } + + setSelections() { + this.$currentStepMaxSelection = this.$currentStep.find('[id^="question"]').data("max-selection") + } + + onSelectionChange() { + let $voteOptions = this.$currentStep.find('[id^="question"]'); + $voteOptions.on("change", () => { + this.toggleContinueButton(); + this.toggleConfirmAnswers(); + this.answerCounter(); + }); + } + + updateWizardSteps(id) { + const wizard = document.getElementById("wizard-steps") + const heading = document.getElementById("heading") + + if (heading) { + // this step has no heading 🤷‍♀️ + if (id === "step-encrypting") { + heading.hidden = true + + return + } + + heading.hidden = false + } + + if (wizard) { + let selector = id + + if (this.isQuestion(id)) { + selector = "step-election" + } + + wizard.querySelectorAll("[data-step]").forEach((element) => { + if (element.dataset.step === selector) { + element.setAttribute("aria-current", "step") + } else { + element.removeAttribute("aria-current") + } + }) + } + } + + // the question ids always end with a number + isQuestion(id) { + return (/[0-9]+$/).test(id); + } + + // receive confirmed answers + toggleConfirmAnswers() { + $(".answer_input:checked").each((_index, element) => { + const confirmedAnswer = $("#step-confirm").find(`#${element.value}`); + $(confirmedAnswer).attr("hidden", false) + }) + + $(".answer_input").not(":checked").each((_index, element) => { + const confirmedAnswer = $("#step-confirm").find(`#${element.value}`); + $(confirmedAnswer).attr("hidden", true) + }) + + $(".nota_input:checked").each((_index, element) => { + const confirmedAnswer = $("#step-confirm").find(`#${element.value}`); + $(confirmedAnswer).attr("hidden", false) + }) + + $(".nota_input").not(":checked").each((_index, element) => { + const confirmedAnswer = $("#step-confirm").find(`#${element.value}`); + $(confirmedAnswer).attr("hidden", true) + }) + } +} diff --git a/decidim-elections/app/packs/src/decidim/votings/admin/polling_officers_picker.js b/decidim-elections/app/packs/src/decidim/votings/admin/polling_officers_picker.js new file mode 100644 index 00000000..0f79b59e --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/admin/polling_officers_picker.js @@ -0,0 +1,20 @@ +import TomSelect from "tom-select/dist/cjs/tom-select.popular"; + +document.addEventListener("DOMContentLoaded", () => { + const tagContainers = document.querySelectorAll("#polling_officers_filter"); + tagContainers.forEach((container) => { + const { tmName, tmItems, tmNoResults } = container.dataset + const config = { + plugins: ["remove_button", "dropdown_input"], + allowEmptyOption: true, + items: JSON.parse(tmItems), + render: { + item: (data, escape) => `
${escape(data.text)}
`, + // eslint-disable-next-line camelcase + ...(tmNoResults && { no_results: () => `
${tmNoResults}
` }) + } + }; + + return new TomSelect(container, config) + }) +}); diff --git a/decidim-elections/app/packs/src/decidim/votings/admin/polling_stations_form.js b/decidim-elections/app/packs/src/decidim/votings/admin/polling_stations_form.js new file mode 100644 index 00000000..804f752f --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/admin/polling_stations_form.js @@ -0,0 +1,9 @@ +import attachGeocoding from "src/decidim/geocoding/attach_input" + +$(() => { + const $form = $(".edit_polling_station, .new_polling_station"); + + if ($form.length > 0) { + attachGeocoding($form.find("#polling_station_address")); + } +}) diff --git a/decidim-elections/app/packs/src/decidim/votings/admin/update_census_dataset_status.js b/decidim-elections/app/packs/src/decidim/votings/admin/update_census_dataset_status.js new file mode 100644 index 00000000..a5bc0262 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/admin/update_census_dataset_status.js @@ -0,0 +1,13 @@ +$(() => { + const updateCensusDatasetStatus = () => { + const $wrapper = $("#census-creating-data-wrapper"); + const updateStatusUrl = $wrapper.data("updateStatusUrl") + + if ($wrapper.length > 0) { + $.get(updateStatusUrl); + } + } + + // 10 seconds + setInterval(updateCensusDatasetStatus, 10000); +}); diff --git a/decidim-elections/app/packs/src/decidim/votings/admin/voting_user_role_form.js b/decidim-elections/app/packs/src/decidim/votings/admin/voting_user_role_form.js new file mode 100644 index 00000000..740e2b76 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/admin/voting_user_role_form.js @@ -0,0 +1,35 @@ +import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component" + +$(() => { + const $participantType = $("#voting_user_role_existing_user"); + + createFieldDependentInputs({ + controllerField: $participantType, + wrapperSelector: ".user-fields", + dependentFieldsSelector: ".user-fields--email", + dependentInputSelector: "input", + enablingCondition: ($field) => { + return $field.val() === "false" + } + }); + + createFieldDependentInputs({ + controllerField: $participantType, + wrapperSelector: ".user-fields", + dependentFieldsSelector: ".user-fields--name", + dependentInputSelector: "input", + enablingCondition: ($field) => { + return $field.val() === "false" + } + }); + + createFieldDependentInputs({ + controllerField: $participantType, + wrapperSelector: ".user-fields", + dependentFieldsSelector: ".user-fields--user-picker", + dependentInputSelector: "input", + enablingCondition: ($field) => { + return $field.val() === "true" + } + }); +}) diff --git a/decidim-elections/app/packs/src/decidim/votings/in-person-vote.js b/decidim-elections/app/packs/src/decidim/votings/in-person-vote.js new file mode 100644 index 00000000..81f3a5aa --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/in-person-vote.js @@ -0,0 +1,23 @@ +// show a message to the user if comunication is lost +import "src/decidim/elections/broken_promises_handler"; + +import { Client } from "@decidim/decidim-bulletin_board"; + +$(async () => { + const $inPersonVoteWrapper = $("#in-person-vote-wrapper"); + if ($inPersonVoteWrapper.length > 0) { + const bulletinBoardClient = new Client({ + apiEndpointUrl: $inPersonVoteWrapper.data("apiEndpointUrl") + }); + const messageId = $inPersonVoteWrapper.data("messageId"); + + await bulletinBoardClient.waitForPendingMessageToBeProcessed(messageId); + + $("form.update_vote_status").trigger("submit"); + } + + $("#js-verify-document").on("click", () => { + $("#verify-document").attr("hidden", true); + $("#complete-voting").attr("hidden", false); + }); +}); diff --git a/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/edit-closure.js b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/edit-closure.js new file mode 100644 index 00000000..da08855f --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/edit-closure.js @@ -0,0 +1,75 @@ +$(() => { + const submitBtn = document.getElementById("submit-ballot-recount"); + const modalBtn = document.getElementById("btn-modal-closure-results-count-error"); + const totals = document.querySelectorAll(".form.edit_closure input.total-value"); + const answers = document.querySelectorAll(".form.edit_closure input.answer-value"); + const notas = document.querySelectorAll(".form.edit_closure input.nota-value"); + const modalTotal = document.getElementById("dialog-total-modal-closure-results-count-error"); + const modalValid = document.getElementById("dialog-valid-modal-closure-results-count-error"); + const modalBlank = document.getElementById("dialog-blank-modal-closure-results-count-error"); + + const setButtonState = (ok) => { + if (ok) { + submitBtn.removeAttribute("hidden"); + modalBtn.setAttribute("hidden", true); + } else { + submitBtn.setAttribute("hidden", true); + modalBtn.removeAttribute("hidden"); + } + }; + + const setModalElement = (element, recount, expected) => { + if (expected === recount) { + element.setAttribute("hidden", true); + } else { + element.removeAttribute("hidden"); + } + if (element.querySelector(".expected")) { + element.querySelector(".expected").innerText = expected; + } + if (element.querySelector(".current")) { + element.querySelector(".current").innerText = recount; + } + }; + + const checkTotals = () => { + const totalBallots = Number(document.getElementById("closure_result-total-ballots").dataset.totalBallots); + let recount = Array.from(totals).reduce((acc, el) => acc + Number(el.value), 0); + + setModalElement(modalTotal, recount, totalBallots); + return recount === totalBallots; + }; + + const checkValidTotals = () => { + const totalValid = Number(document.getElementById("closure_result__ballot_results__valid_ballots_count").value); + let recount = Array.from(answers).reduce((acc, el) => acc + Number(el.value), 0); + + setModalElement(modalValid, recount, totalValid); + + return recount === totalValid; + }; + + const checkBlankTotals = () => { + const totalBlanks = Number(document.getElementById("closure_result__ballot_results__blank_ballots_count").value); + let recount = Array.from(notas).reduce((acc, el) => acc + Number(el.value), 0); + + setModalElement(modalBlank, recount, totalBlanks); + return recount === totalBlanks; + }; + + const runChecks = () => { + const totalBallots = checkTotals(); + const validTotals = checkValidTotals(); + const blankTotals = checkBlankTotals(); + + setButtonState(totalBallots && validTotals && blankTotals); + }; + + if (submitBtn) { + runChecks(); + + [...totals].concat([...answers]).concat([...notas]).forEach((box) => { + box.addEventListener("blur", runChecks); + }); + } +}); diff --git a/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/in-person-vote.js b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/in-person-vote.js new file mode 100644 index 00000000..4f7f5eec --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/in-person-vote.js @@ -0,0 +1,5 @@ +$(() => { + $("#person_voted_checkbox").on("change", (event) => { + $("#submit_complete_voting").attr("disabled", !$(event.target).is(":checked")); + }); +}); diff --git a/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/new-closure.js b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/new-closure.js new file mode 100644 index 00000000..a8af4f7d --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/new-closure.js @@ -0,0 +1,39 @@ +$(() => { + const $submitBtn = $("#submit-verify-votes"); + const $modalBtn = $("#btn-modal-closure-count-error"); + const $totalBallotsInput = $("#envelopes_result_total_ballots_count"); + const $electionVotesInput = $("#envelopes_result_election_votes_count"); + const $formNotes = $("#envelopes_result_polling_officer_notes"); + const $modalNotes = $("#modal-polling-officer-notes"); + + const checkValues = () => { + const totalBallotsInputValue = parseInt($totalBallotsInput.val(), 10); + const electionVotesInputValue = parseInt($electionVotesInput.val(), 10); + + if (totalBallotsInputValue === electionVotesInputValue) { + $submitBtn.find("button").attr("disabled", false); + $submitBtn.attr("hidden", false); + $modalBtn.attr("hidden", true); + } else { + $submitBtn.attr("hidden", true); + $modalBtn.attr("hidden", false); + $("#modal-total-ballots-value").html(parseInt($totalBallotsInput.val(), 10)); + } + }; + checkValues(); + + $totalBallotsInput.on("blur", checkValues); + + $totalBallotsInput.on("keyup", () => { + $formNotes.val(""); + $modalNotes.val(""); + }); + + $modalNotes.on("keyup", () => { + $("#btn-submit-from-modal").attr("disabled", !$modalNotes.val().trim()); + }); + + $modalNotes.on("change", () => { + $formNotes.val($modalNotes.val()); + }); +}); diff --git a/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/sign-closure.js b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/sign-closure.js new file mode 100644 index 00000000..f7ec77e9 --- /dev/null +++ b/decidim-elections/app/packs/src/decidim/votings/polling_officer_zone/sign-closure.js @@ -0,0 +1,13 @@ +$(() => { + const $submitBtn = $("#btn-submit-from-modal"); + const $modalBtn = $("#btn-modal-closure-sign"); + const $signCheckbox = $("#closure_sign_signed"); + + const changeButtonProps = (event) => { + const notSigned = !$(event.target).is(":checked"); + $modalBtn.prop("disabled", notSigned); + $submitBtn.prop("disabled", notSigned); + }; + + $signCheckbox.on("change", changeButtonProps); +}); diff --git a/decidim-elections/app/packs/stylesheets/decidim/elections/admin/elections.scss b/decidim-elections/app/packs/stylesheets/decidim/elections/admin/elections.scss new file mode 100644 index 00000000..41db7084 --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/elections/admin/elections.scss @@ -0,0 +1,24 @@ +.create_election { + .card-section { + svg.fix-icon { + @apply fill-white mr-0.5; + } + } +} + +.accordion { + &.technical-configuration { + .accordion-title { + &::before { + content: "\25B6"; + font-size: 0.7em; + display: inline-block; + vertical-align: text-bottom; + } + } + + .is-active > .accordion-title::before { + content: "\25BC"; + } + } +} diff --git a/decidim-elections/app/packs/stylesheets/decidim/elections/elections.scss b/decidim-elections/app/packs/stylesheets/decidim/elections/elections.scss new file mode 100644 index 00000000..393146aa --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/elections/elections.scss @@ -0,0 +1,202 @@ +.election { + &__section { + &-title { + @apply flex gap-1 items-center; + + svg { + @apply w-6 h-6 text-tertiary fill-current; + } + } + + &-desc { + @apply text-gray-2 my-6; + } + } + + &__accordion { + @apply border-4 border-background rounded px-3 py-2; + + &-trigger { + @apply flex justify-between items-center w-full font-bold text-secondary hover:underline focus:outline-none focus:underline; + + > :last-child { + @apply flex-none; + } + + svg { + @apply w-8 h-8 text-secondary fill-current first:block last:hidden; + } + + &[role="button"][aria-expanded="true"] svg { + @apply first:hidden last:block; + } + } + + &-panel { + @apply border-t border-gray-3 mt-4 pt-4 space-y-4; + + ul { + @apply list-decimal px-4; + } + } + } + + &__aside { + &-heading { + @apply uppercase font-semibold text-gray-2 text-sm; + } + + &-link { + @apply flex items-center gap-2 text-sm; + + svg { + @apply w-4 h-4 flex-none text-gray fill-current; + } + + a { + @apply text-md text-secondary; + } + } + } +} + +.election-log { + &__entry { + @apply flex flex-col gap-2 rounded border-4 border-background p-4 mb-8 last:mb-0 text-gray-2; + } + + &__hash, + &__time { + @apply text-sm empty:hidden; + } + + &__pre { + @apply bg-background p-2 rounded; + } +} + +.election-question { + @apply block rounded p-4 border-4 border-background flex items-center text-lg text-gray-2 hover:border-tertiary focus:border-tertiary cursor-pointer; + + &[aria-disabled="true"] { + @apply text-gray cursor-not-allowed hover:border-background focus:border-background; + } + + &__counter { + @apply font-semibold text-black text-lg bg-tertiary inline-block; + } + + /* overwrite default accordion styles */ + [id^="step"][aria-hidden="true"] { + @apply hidden; + } + + &__container { + @apply space-y-4 mb-8; + } + + &__intro { + @apply py-8 border-t-2 border-background space-y-4; + } + + &__description { + @apply text-gray-2 text-lg; + } + + &__maxselections { + @apply flex items-center justify-between gap-2 text-gray-2; + } + + &__answer { + @apply flex flex-col items-start gap-4 py-8 text-gray-2 border-t-2 border-background; + + &-option { + @apply flex items-center justify-between w-full text-lg; + } + } + + &__ballot { + @apply flex flex-col items-start gap-2 py-8 border-t-2 border-background text-lg text-gray-2; + } + + &__modal { + &-explanation { + @apply space-y-4; + } + + &-grid { + @apply grid grid-cols-2 gap-4; + } + + img { + @apply aspect-video object-cover h-full w-full; + } + + ul { + @apply list-disc; + } + + li { + @apply ml-4; + } + + a { + @apply text-secondary; + } + } +} + +.election-trustee { + &__container { + @apply space-y-6 [&_p]:text-gray-2; + } + + &__list { + @apply list-disc pl-4 text-gray-2; + } + + &__table { + @apply text-left w-full border-2 border-background; + + th, + td { + @apply px-4 py-2; + } + + th { + @apply bg-background; + } + + tr:nth-child(even) td { + @apply bg-gray-3; + } + + a { + @apply text-secondary underline; + } + + .step_status { + span { + @apply hidden; + } + + &[data-step-status="pending"] .pending, + &[data-step-status="processing"] .processing, + &[data-step-status="completed"] .completed { + @apply block; + } + } + } + + &__submit { + @apply p-4 border-4 border-background space-y-4 rounded; + } + + &__buttons { + @apply flex items-center gap-4; + + [hidden] { + @apply hidden; + } + } +} diff --git a/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings.scss b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings.scss new file mode 100644 index 00000000..5bf994a8 --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings.scss @@ -0,0 +1,23 @@ +@import "stylesheets/decidim/votings/admin/votings/ballot-styles"; +@import "stylesheets/decidim/votings/admin/votings/monitoring-committee-polling-station-closures"; +@import "stylesheets/decidim/votings/admin/votings/results"; +@import "tom-select/dist/scss/tom-select"; + +/* overwrite tom-select defaults */ +.ts { + &-control { + @apply border-gray text-md min-h-[40px]; + + input { + @apply font-normal text-black text-md; + } + } + + &-dropdown { + @apply text-md text-gray-2 font-normal; + + .active { + @apply text-white bg-secondary; + } + } +} diff --git a/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_ballot-styles.scss b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_ballot-styles.scss new file mode 100644 index 00000000..96e40f3a --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_ballot-styles.scss @@ -0,0 +1,16 @@ +.ballot-style__question--checked { + color: green; +} + +.ballot-style__question--unchecked { + color: red; +} + +.ballot-style__question-header { + max-width: 100px; + display: table-cell; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; +} diff --git a/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_monitoring-committee-polling-station-closures.scss b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_monitoring-committee-polling-station-closures.scss new file mode 100644 index 00000000..3fc6af46 --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_monitoring-committee-polling-station-closures.scss @@ -0,0 +1,9 @@ +.polling_station_closure_certificate_results { + display: grid; + grid-template-columns: 1fr 1fr; + align-items: center; + + @media only screen and (max-width: 900px) { + grid-template-columns: 1fr; + } +} diff --git a/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_results.scss b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_results.scss new file mode 100644 index 00000000..4ad0f865 --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/votings/admin/votings/_results.scss @@ -0,0 +1,3 @@ +.result.not_answer td:first-child { + font-style: italic; +} diff --git a/decidim-elections/app/packs/stylesheets/decidim/votings/votings.scss b/decidim-elections/app/packs/stylesheets/decidim/votings/votings.scss new file mode 100644 index 00000000..13b1733e --- /dev/null +++ b/decidim-elections/app/packs/stylesheets/decidim/votings/votings.scss @@ -0,0 +1,53 @@ +.voting { + &__map { + @apply flex flex-col-reverse md:flex-row items-start gap-4 only:[&>*]:grow first:[&>*]:min-w-[40%]; + + & > :last-child:not(:only-child) { + @apply aspect-[4/3] w-full rounded overflow-hidden; + } + + &-address { + @apply border-4 border-background rounded p-4 mt-4 first:mt-0; + } + } + + &__polling-station { + @apply space-y-4 py-4; + + & > * { + @apply flex items-center gap-2; + } + + &__accordion { + @apply border-4 border-background rounded p-4; + + &-trigger { + @apply flex justify-between items-center w-full text-secondary hover:underline; + } + + &-arrow { + @apply w-8 h-8 text-secondary fill-current first-of-type:block last-of-type:hidden; + } + + [role="button"][aria-expanded="true"] &-arrow { + @apply first-of-type:hidden last-of-type:block; + } + + &-panel { + @apply border-t border-gray-3 mt-4 pt-4; + + a { + @apply hover:underline; + } + } + + & + & { + @apply mt-4; + } + } + + & + & { + @apply mt-4 border-t border-background; + } + } +} diff --git a/decidim-elections/app/permissions/decidim/elections/admin/permissions.rb b/decidim-elections/app/permissions/decidim/elections/admin/permissions.rb new file mode 100644 index 00000000..371fb26a --- /dev/null +++ b/decidim-elections/app/permissions/decidim/elections/admin/permissions.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + class Permissions < Decidim::DefaultPermissions + def permissions + return permission_action if permission_action.scope != :admin + + case permission_action.subject + when :question, :answer + case permission_action.action + when :create, :update, :delete, :import_proposals + allow_if_not_blocked + when :select + allow_if_results + end + when :steps + case permission_action.action + when :read, :update + allow! + end + when :election + case permission_action.action + when :create, :read + allow! + when :delete, :update, :unpublish, :publish + allow_if_not_blocked + end + when :trustees + case permission_action.action + when :manage + toggle_allow(user.admin?) + end + when :trustee_participatory_space + case permission_action.action + when :create, :update + allow! + when :delete + allow_if_not_related_to_any_election + end + when :questionnaire + case permission_action.action + when :export_answers + permission_action.allow! + when :update + toggle_allow(feedback_form.present?) + end + when :questionnaire_answers + case permission_action.action + when :index, :show, :export_response + permission_action.allow! + end + end + + permission_action + end + + private + + def election + @election ||= context.fetch(:election, nil) + end + + def question + @question ||= context.fetch(:question, nil) + end + + def trustee_participatory_space + @trustee_participatory_space ||= context.fetch(:trustee_participatory_space, nil) + end + + def allow_if_results + toggle_allow(election && election.results?) + end + + def allow_if_not_blocked + toggle_allow(election && !election.blocked?) + end + + def allow_if_not_related_to_any_election + toggle_allow(trustee_participatory_space.trustee.elections.empty?) + end + + def feedback_form + @feedback_form ||= context.fetch(:questionnaire, nil) + end + end + end + end +end diff --git a/decidim-elections/app/permissions/decidim/elections/permissions.rb b/decidim-elections/app/permissions/decidim/elections/permissions.rb new file mode 100644 index 00000000..602e6927 --- /dev/null +++ b/decidim-elections/app/permissions/decidim/elections/permissions.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Decidim + module Elections + class Permissions < Decidim::DefaultPermissions + def permissions + if permission_action.scope == :public && permission_action.subject == :election + case permission_action.action + when :preview + toggle_allow(can_preview?) + when :view + toggle_allow(can_view?) + when :vote + toggle_allow(can_vote?) + when :user_vote + toggle_allow(can_vote_with_user?) + end + end + + return permission_action unless user + + # Delegate the admin permission checks to the admin permissions class + return Decidim::Elections::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin + + # Delegate the trustee_zone permission checks to the trustee zone permissions class + return Decidim::Elections::TrusteeZone::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :trustee_zone + + permission_action + end + + private + + def can_view? + election.published? || can_preview? + end + + def can_vote? + election.published? && election.ongoing? + end + + def can_vote_with_user? + can_vote? && user && authorized?(:vote, resource: election) + end + + def can_preview? + user&.admin? + end + + def election + @election ||= context[:election] + end + end + end +end diff --git a/decidim-elections/app/permissions/decidim/elections/trustee_zone/permissions.rb b/decidim-elections/app/permissions/decidim/elections/trustee_zone/permissions.rb new file mode 100644 index 00000000..a9f1cdaf --- /dev/null +++ b/decidim-elections/app/permissions/decidim/elections/trustee_zone/permissions.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module TrusteeZone + class Permissions < Decidim::DefaultPermissions + def permissions + return permission_action unless permission_action.scope == :trustee_zone + + case permission_action.subject + when :trustee, :election + toggle_allow(trustee_for_user?) if [:view, :update].member?(permission_action.action) + when :user + allow! if permission_action.action == :update_profile + end + + permission_action + end + + private + + def trustee_for_user? + trustee && trustee.user == user + end + + def trustee + @trustee ||= context.fetch(:trustee, nil) + end + end + end + end +end diff --git a/decidim-elections/app/permissions/decidim/votings/admin/permissions.rb b/decidim-elections/app/permissions/decidim/votings/admin/permissions.rb new file mode 100644 index 00000000..0ee8df0c --- /dev/null +++ b/decidim-elections/app/permissions/decidim/votings/admin/permissions.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + class Permissions < Decidim::DefaultPermissions + def permissions + return permission_action unless user + return user_allowed_to_read_admin_dashboard? if read_admin_dashboard_action? + return permission_action unless permission_action.scope == :admin + + user_can_enter_space_area? + + return permission_action if voting && !voting.is_a?(Decidim::Votings::Voting) + + unless user_can_read_votings_admin_dashboard? + disallow! + return permission_action + end + + allowed_read_participatory_space? + allowed_voting_action? + + permission_action + end + + private + + def user_can_enter_space_area? + return unless permission_action.action == :enter && + permission_action.subject == :space_area && + context.fetch(:space_name, nil) == :votings + + allow! + end + + def read_admin_dashboard_action? + permission_action.action == :read && + permission_action.subject == :admin_dashboard + end + + def allowed_read_participatory_space? + return unless permission_action.action == :read && + permission_action.subject == :participatory_space + + allow! + end + + def allowed_voting_action? + return unless + [ + :votings, :voting, + :landing_page, + :components, + :polling_station, :polling_stations, + :polling_officer, :polling_officers, + :monitoring_committee_menu, :monitoring_committee_member, :monitoring_committee_members, + :monitoring_committee_polling_station_closure, :monitoring_committee_polling_station_closures, + :monitoring_committee_verify_elections, + :monitoring_committee_election_result, :monitoring_committee_election_results, + :census, + :ballot_style, :ballot_styles, + :component_data + ].member? permission_action.subject + + case permission_action.subject + when :votings + toggle_allow(user_can_read_votings_admin_dashboard?) if permission_action.action == :read + when :voting + case permission_action.action + when :read, :list, :edit + toggle_allow(user_can_read_voting?) + when :create, :publish, :unpublish, :update + toggle_allow(user.admin?) + when :preview + toggle_allow(user_can_read_voting? && voting.present?) + when :manage_landing_page + toggle_allow(user.admin? && voting.present?) + end + when :landing_page + toggle_allow(user.admin?) if permission_action.action == :update + when :ballot_styles, :components, :polling_stations, :polling_officers, :monitoring_committee_members + toggle_allow(user.admin?) if permission_action.action == :read + when :polling_station + case permission_action.action + when :create + toggle_allow(user.admin?) + when :update + toggle_allow(user.admin? && polling_station.present?) + when :delete + toggle_allow(user.admin? && polling_station.present? && polling_station.closures.blank?) + end + when :polling_officer + case permission_action.action + when :create + toggle_allow(user.admin?) + when :delete + toggle_allow(user.admin? && polling_officer.present?) + end + when :monitoring_committee_member + case permission_action.action + when :create + toggle_allow(user.admin?) + when :delete + toggle_allow(user.admin? && monitoring_committee_member.present?) + end + when :monitoring_committee_menu + toggle_allow(user_can_read_voting?) if permission_action.action == :read + when :monitoring_committee_polling_station_closure + toggle_allow(user_monitoring_committee_for_voting? && closure.present?) if [:read, :validate].member?(permission_action.action) + when :monitoring_committee_polling_station_closures, :monitoring_committee_verify_elections, :monitoring_committee_election_results + toggle_allow(user_monitoring_committee_for_voting?) if permission_action.action == :read + when :monitoring_committee_election_result + toggle_allow(user_monitoring_committee_for_voting? && election.present?) if [:read, :validate].member?(permission_action.action) + when :census + toggle_allow(user.admin?) if permission_action.action == :manage + when :ballot_style + case permission_action.action + when :create + toggle_allow(user.admin? && (voting.dataset.blank? || voting.dataset.init_data?)) + when :update, :delete + toggle_allow(user.admin? && (voting.dataset.blank? || voting.dataset.init_data?) && ballot_style.present?) + end + when :component_data + toggle_allow(user.admin?) if [:import, :export].member? permission_action.action + end + end + + # Monitoring committee members can access the admin dashboard to manage their votings. + def user_allowed_to_read_admin_dashboard? + toggle_allow(user_can_read_votings_admin_dashboard?) + + permission_action + end + + def user_can_read_votings_admin_dashboard? + user.admin? || user_monitoring_committee? + end + + def user_can_read_voting? + user.admin? || user_monitoring_committee_for_voting? + end + + def user_monitoring_committee? + Decidim::Votings::MonitoringCommitteeMember.exists?(user:) + end + + def user_monitoring_committee_for_voting? + Decidim::Votings::MonitoringCommitteeMember.exists?(user:, voting:) + end + + def voting + @voting ||= context.fetch(:voting, nil) || context.fetch(:participatory_space, nil) + end + + def polling_station + @polling_station ||= context.fetch(:polling_station, nil) + end + + def polling_officer + @polling_officer ||= context.fetch(:polling_officer, nil) + end + + def monitoring_committee_member + @monitoring_committee_member ||= context.fetch(:monitoring_committee_member, nil) + end + + def ballot_style + @ballot_style ||= context.fetch(:ballot_style, nil) + end + + def closure + @closure ||= context.fetch(:closure, nil) + end + + def election + @election ||= context.fetch(:election, nil) + end + end + end + end +end diff --git a/decidim-elections/app/permissions/decidim/votings/permissions.rb b/decidim-elections/app/permissions/decidim/votings/permissions.rb new file mode 100644 index 00000000..4f1be338 --- /dev/null +++ b/decidim-elections/app/permissions/decidim/votings/permissions.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class Permissions < Decidim::DefaultPermissions + def permissions + allowed_public_anonymous_action? + + return permission_action unless user + + return Decidim::Votings::Admin::Permissions.new(user, permission_action, context).permissions if admin_scope? + + # Delegate the polling_officer_zone permission checks to the polling officer zone permissions class + return Decidim::Votings::PollingOfficerZone::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :polling_officer_zone + + permission_action + end + + private + + def voting + @voting ||= context.fetch(:voting, nil) + end + + def allowed_public_anonymous_action? + return unless permission_action.action == :read + return unless permission_action.scope == :public + + case permission_action.subject + when :votings, :participatory_space + allow! + when :voting + toggle_allow(voting.published? || user&.admin?) + end + end + + def admin_scope? + permission_action.scope == :admin || permission_action.subject == :admin_dashboard + end + end + end +end diff --git a/decidim-elections/app/permissions/decidim/votings/polling_officer_zone/permissions.rb b/decidim-elections/app/permissions/decidim/votings/polling_officer_zone/permissions.rb new file mode 100644 index 00000000..63bd7561 --- /dev/null +++ b/decidim-elections/app/permissions/decidim/votings/polling_officer_zone/permissions.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module PollingOfficerZone + class Permissions < Decidim::DefaultPermissions + def permissions + return permission_action unless permission_action.scope == :polling_officer_zone + + case permission_action.subject + when :polling_officers + toggle_allow(polling_officers_for_user?) if permission_action.action == :view + when :polling_station_results, :in_person_vote + case permission_action.action + when :create + toggle_allow(polling_officer&.user == user && closure.blank?) + when :manage + toggle_allow(polling_officer&.user == user) + when :edit + toggle_allow(polling_officer&.user == user && closure.present? && !closure&.complete_phase?) + end + when :user + allow! if permission_action.action == :update_profile + end + + permission_action + end + + private + + def polling_officers_for_user? + polling_officers.any? && polling_officers.all? { |polling_officer| polling_officer.user == user } + end + + def polling_officers + @polling_officers ||= context.fetch(:polling_officers, []) + end + + def polling_officer + @polling_officer ||= context.fetch(:polling_officer, nil) + end + + def polling_station + @polling_station ||= context.fetch(:polling_station, nil) + end + + def closure + @closure ||= context.fetch(:closure, nil) + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/elections/admin_log/election_presenter.rb b/decidim-elections/app/presenters/decidim/elections/admin_log/election_presenter.rb new file mode 100644 index 00000000..5ebeb990 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/elections/admin_log/election_presenter.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module AdminLog + # This class holds the logic to present a `Decidim::Election` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # ElectionPresenter.new(action_log, view_helpers).present + class ElectionPresenter < Decidim::Log::BasePresenter + private + + def i18n_labels_scope + "activemodel.attributes.election" + end + + def action_string + case action + when "publish", "unpublish", "create", "delete", "update", + "setup", "start_key_ceremony", "start_vote", "end_vote", "start_tally", "report_missing_trustee", "publish_results" + "decidim.elections.admin_log.election.#{action}" + else + super + end + end + + def i18n_params + super.merge(trustee_info) + end + + def trustee_info + return {} unless action == "report_missing_trustee" + + { + trustee_name: if trustee + Decidim::Log::UserPresenter.new(trustee.user, h, trustee_extra).present + else + trustee_extra["name"] + end + } + end + + def trustee + @trustee ||= Decidim::Elections::Trustee.find(action_log.extra["extra"]["trustee_id"]) if action_log.extra["extra"]["trustee_id"] + end + + def trustee_extra + info = { + "name" => trustee.name, + "nickname" => trustee.user&.nickname + } + + info["name"] ||= action_log.extra["extra"]["name"] + info["nickname"] ||= action_log.extra["extra"]["nickname"] + + info + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/elections/admin_log/trustee_presenter.rb b/decidim-elections/app/presenters/decidim/elections/admin_log/trustee_presenter.rb new file mode 100644 index 00000000..ae541446 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/elections/admin_log/trustee_presenter.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module AdminLog + # This class holds the logic to present a `Decidim::Trustee` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # TrusteePresenter.new(action_log, view_helpers).present + class TrusteePresenter < Decidim::Log::BasePresenter + private + + def trustee_user + @trustee_user ||= action_log.resource&.user + end + + def trustee_user_extra + { + "name" => trustee_user.name, + "nickname" => trustee_user.nickname + } + end + + def trustee_user_presenter + @trustee_user_presenter ||= Decidim::Log::UserPresenter.new(trustee_user, h, trustee_user_extra) + end + + def i18n_params + super.merge( + trustee_user: trustee_user.present? ? trustee_user_presenter.present : resource_presenter.try(:present) + ) + end + + def action_string + case action + when "create" + "decidim.elections.admin_log.trustee.#{action}" + else + super + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/elections/election_presenter.rb b/decidim-elections/app/presenters/decidim/elections/election_presenter.rb new file mode 100644 index 00000000..7aaf8ade --- /dev/null +++ b/decidim-elections/app/presenters/decidim/elections/election_presenter.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # + # Decorator for election + # + class ElectionPresenter < SimpleDelegator + include Decidim::SanitizeHelper + include Decidim::TranslatableAttributes + + def election + __getobj__ + end + + def title + content = translated_attribute(election.title) + decidim_html_escape(content) + end + + def description(strip_tags: false) + content = translated_attribute(election.description) + content = strip_tags(content) if strip_tags + content + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/elections/trustee_presenter.rb b/decidim-elections/app/presenters/decidim/elections/trustee_presenter.rb new file mode 100644 index 00000000..1bae1ad9 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/elections/trustee_presenter.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # + # Decorator for trustee + # + class TrusteePresenter < SimpleDelegator + def trustee + __getobj__ + end + + def public_key_thumbprint + @public_key_thumbprint ||= format_thumbprint(jwk_thumbprint(JSON.parse(trustee.public_key))) if trustee.public_key.present? + end + + private + + def format_thumbprint(thumbprint) + "
#{thumbprint[0..14]}\n#{thumbprint[15..-16]}\n#{thumbprint[-15..-1]}
".html_safe + end + + def jwk_thumbprint(key) + Base64.urlsafe_encode64(Digest::SHA256.digest(key.slice("e", "kty", "n").to_json), padding: false) + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/admin_log/ballot_style_presenter.rb b/decidim-elections/app/presenters/decidim/votings/admin_log/ballot_style_presenter.rb new file mode 100644 index 00000000..0d7db9a2 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/admin_log/ballot_style_presenter.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module AdminLog + # This class holds the logic to present a `Decidim::Votings::Voting` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # BallotStylePresenter.new(action_log, view_helpers).present + class BallotStylePresenter < Decidim::Log::BasePresenter + private + + def i18n_params + super.merge( + ballot_style_code: ballot_style_code.to_s + ) + end + + def ballot_style_code + action_log&.resource&.code || action_log.extra["code"] + end + + def action_string + case action + when "create", "delete", "update" + "decidim.votings.admin_log.ballot_style.#{action}" + else + super + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter.rb b/decidim-elections/app/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter.rb new file mode 100644 index 00000000..b2de9bb3 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module AdminLog + # This class holds the logic to present a `Decidim::Votings::Voting` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # MonitoringCommitteeMemberPresenter.new(action_log, view_helpers).present + class MonitoringCommitteeMemberPresenter < Decidim::Log::BasePresenter + private + + def monitoring_committee_member_user + @monitoring_committee_member_user ||= action_log.resource&.user + end + + def monitoring_committee_member_user_extra + { + "name" => monitoring_committee_member_user.name, + "nickname" => monitoring_committee_member_user.nickname + } + end + + def monitoring_committee_member_user_presenter + @monitoring_committee_member_user_presenter ||= Decidim::Log::UserPresenter.new(monitoring_committee_member_user, h, monitoring_committee_member_user_extra) + end + + def i18n_params + super.merge( + monitoring_committee_member_user: monitoring_committee_member_user.present? ? monitoring_committee_member_user_presenter.present : resource_presenter.try(:present) + ) + end + + def action_string + case action + when "create", "delete" + "decidim.votings.admin_log.monitoring_committee_member.#{action}" + else + super + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/admin_log/polling_officer_presenter.rb b/decidim-elections/app/presenters/decidim/votings/admin_log/polling_officer_presenter.rb new file mode 100644 index 00000000..afd775bd --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/admin_log/polling_officer_presenter.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module AdminLog + # This class holds the logic to present a `Decidim::Votings::Voting` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # PollingOfficerPresenter.new(action_log, view_helpers).present + class PollingOfficerPresenter < Decidim::Log::BasePresenter + private + + def polling_officer_user + @polling_officer_user ||= action_log.resource&.user + end + + def polling_officer_user_extra + { + "name" => polling_officer_user.name, + "nickname" => polling_officer_user.nickname + } + end + + def polling_officer_user_presenter + @polling_officer_user_presenter ||= Decidim::Log::UserPresenter.new(polling_officer_user, h, polling_officer_user_extra) + end + + def i18n_params + super.merge( + polling_officer_user: polling_officer_user.present? ? polling_officer_user_presenter.present : resource_presenter.try(:present) + ) + end + + def action_string + case action + when "create", "delete" + "decidim.votings.admin_log.polling_officer.#{action}" + else + super + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/admin_log/polling_station_presenter.rb b/decidim-elections/app/presenters/decidim/votings/admin_log/polling_station_presenter.rb new file mode 100644 index 00000000..abaf6dfe --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/admin_log/polling_station_presenter.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module AdminLog + # This class holds the logic to present a `Decidim::Votings::Voting` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # PollingStationPresenter.new(action_log, view_helpers).present + class PollingStationPresenter < Decidim::Log::BasePresenter + private + + def action_string + case action + when "create", "delete", "update" + "decidim.votings.admin_log.polling_station.#{action}" + else + super + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/admin_log/voting_presenter.rb b/decidim-elections/app/presenters/decidim/votings/admin_log/voting_presenter.rb new file mode 100644 index 00000000..c69f5f2d --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/admin_log/voting_presenter.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module AdminLog + # This class holds the logic to present a `Decidim::Votings::Voting` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # VotingPresenter.new(action_log, view_helpers).present + class VotingPresenter < Decidim::Log::BasePresenter + private + + def diff_fields_mapping + { + title: :i18n, + description: :i18n, + slug: :string, + start_time: :date, + end_time: :date, + decidim_scope_id: :scope, + published_at: :date + } + end + + def i18n_labels_scope + "activemodel.attributes.voting" + end + + def action_string + case action + when "create", "publish", "unpublish" + "decidim.votings.admin_log.voting.#{action}" + else + super + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/census/admin_log/dataset_presenter.rb b/decidim-elections/app/presenters/decidim/votings/census/admin_log/dataset_presenter.rb new file mode 100644 index 00000000..d898648c --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/census/admin_log/dataset_presenter.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + module AdminLog + # This class holds the logic to present a `Decidim::Votings::Census::Dataset` + # for the `AdminLog` log. + # + # Usage should be automatic and you should not need to call this class + # directly, but here is an example: + # + # action_log = Decidim::ActionLog.last + # view_helpers # => this comes from the views + # DatasetPresenter.new(action_log, view_helpers).present + class DatasetPresenter < Decidim::Log::BasePresenter + private + + def action_string + case action + when "create", "delete", "update" + "decidim.votings.admin_log.census.#{action}" + else + super + end + end + end + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/polling_station_presenter.rb b/decidim-elections/app/presenters/decidim/votings/polling_station_presenter.rb new file mode 100644 index 00000000..555c9b42 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/polling_station_presenter.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # + # Decorator for polling station + # + class PollingStationPresenter < SimpleDelegator + include Decidim::SanitizeHelper + include Decidim::TranslatableAttributes + + def polling_station + __getobj__ + end + + def title + content = translated_attribute(polling_station.title) + decidim_html_escape(content) + end + + def address + content = translated_attribute(polling_station.address) + decidim_html_escape(content) + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/voting_presenter.rb b/decidim-elections/app/presenters/decidim/votings/voting_presenter.rb new file mode 100644 index 00000000..cc051351 --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/voting_presenter.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class VotingPresenter < SimpleDelegator + include Decidim::SanitizeHelper + include Decidim::TranslatableAttributes + + def title + content = translated_attribute(voting.title) + decidim_html_escape(content) + end + + def introductory_image_url + voting.attached_uploader(:introductory_image).url(host: voting.organization.host) + end + + def banner_image_url + voting.attached_uploader(:banner_image).url(host: voting.organization.host) + end + + def voting + __getobj__ + end + end + end +end diff --git a/decidim-elections/app/presenters/decidim/votings/voting_stats_presenter.rb b/decidim-elections/app/presenters/decidim/votings/voting_stats_presenter.rb new file mode 100644 index 00000000..11e3b73f --- /dev/null +++ b/decidim-elections/app/presenters/decidim/votings/voting_stats_presenter.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # A presenter to render statistics in a Voting. + class VotingStatsPresenter < Decidim::StatsPresenter + include Decidim::IconHelper + + private + + def participatory_space = __getobj__.fetch(:voting) + + def participatory_space_sym = :votings + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/admin/pending_actions.rb b/decidim-elections/app/queries/decidim/elections/admin/pending_actions.rb new file mode 100644 index 00000000..3be6aeee --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/admin/pending_actions.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # A class used to find actions with a pending status + class PendingActions < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + def self.for + new.query + end + + # Finds the votes with pending status + def query + Decidim::Elections::Action.pending + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/admin/votes_for_statistics.rb b/decidim-elections/app/queries/decidim/elections/admin/votes_for_statistics.rb new file mode 100644 index 00000000..839f1d7a --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/admin/votes_for_statistics.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Admin + # A class used to find election votes for statistics + class VotesForStatistics < Decidim::Query + # Syntactic sugar to initialize the class and return the queried object. + def self.for(election) + new(election).query + end + + def initialize(election) + @election = election + end + + # Finds the votes for an election which get count for the statistics + def query + @election.votes.accepted.pick(Arel.sql("COUNT(id)"), Arel.sql("COUNT(distinct voter_id)")) + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/elections_finished_to_end.rb b/decidim-elections/app/queries/decidim/elections/elections_finished_to_end.rb new file mode 100644 index 00000000..2ffb7acf --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/elections_finished_to_end.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # A class used to find elections finished to close their voting period + class ElectionsFinishedToEnd < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + def self.for + new.query + end + + # Finds the Elections that should be closed. + def query + Decidim::Elections::Election.bb_vote + .where("end_time <= ?", Time.current) + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/elections_ready_to_start.rb b/decidim-elections/app/queries/decidim/elections/elections_ready_to_start.rb new file mode 100644 index 00000000..2fd712e4 --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/elections_ready_to_start.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # A class used to find elections ready and near to start the voting period + class ElectionsReadyToStart < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + def self.for + new.query + end + + # Finds the Elections that should be opened. + def query + Decidim::Elections::Election.bb_key_ceremony_ended + .where("start_time <= ?", minimum_start_time) + end + + private + + def minimum_start_time + @minimum_start_time ||= Decidim::Elections.setup_minimum_hours_before_start.hours.from_now + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/filtered_elections.rb b/decidim-elections/app/queries/decidim/elections/filtered_elections.rb new file mode 100644 index 00000000..68e50421 --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/filtered_elections.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # A class used to find elections filtered by its state + class FilteredElections < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # components - An array of Decidim::Component + # start_at - A date to filter resources created after it + # end_at - A date to filter resources created before it. + def self.for(components, start_at = nil, end_at = nil) + new(components, start_at, end_at).query + end + + # Initializes the class. + # + # components - An array of Decidim::Component + # start_at - A date to filter resources created after it + # end_at - A date to filter resources created before it. + def initialize(components, start_at = nil, end_at = nil) + @components = components + @start_at = start_at + @end_at = end_at + end + + # Finds the Projects scoped to an array of components and filtered + # by a range of dates. + def query + elections = Decidim::Elections::Election.where(component: @components) + elections = elections.where("created_at >= ?", @start_at) if @start_at.present? + elections = elections.where("created_at <= ?", @end_at) if @end_at.present? + elections + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space.rb b/decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space.rb new file mode 100644 index 00000000..9fb48bdc --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Trustees + # A class used to find trustees by participatory space. + class ByParticipatorySpace < Decidim::Query + # Initializes the class. + # + def initialize(participatory_space) + @participatory_space = participatory_space + end + + # Gets trustees by participatory space. + def query + Decidim::Elections::Trustee + .includes([:user]) + .joins(:trustees_participatory_spaces) + .merge(Decidim::Elections::TrusteesParticipatorySpace.where(participatory_space: @participatory_space, considered: true)) + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space_trustee_ids.rb b/decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space_trustee_ids.rb new file mode 100644 index 00000000..6e69337e --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/trustees/by_participatory_space_trustee_ids.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Trustees + # A class used to find trustees by participatory space trustee ids. + class ByParticipatorySpaceTrusteeIds < Decidim::Query + # Initializes the class. + # + def initialize(trustee_ids) + @trustee_ids = trustee_ids + end + + # Gets trustees by participatory space trustee ids. + def query + Decidim::Elections::Trustee + .includes([:user]) + .joins(:trustees_participatory_spaces) + .merge(Decidim::Elections::TrusteesParticipatorySpace.where(decidim_elections_trustee_id: @trustee_ids, considered: true)) + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/votes/last_vote_for_voter.rb b/decidim-elections/app/queries/decidim/elections/votes/last_vote_for_voter.rb new file mode 100644 index 00000000..5cae54dc --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/votes/last_vote_for_voter.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Votes + # A class used to find the last vote casted by a voter in an election + class LastVoteForVoter < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # election - the election where the vote was casted + # voter_id - the identifier of the voter + def self.for(election, voter_id) + new(election, voter_id).query + end + + def initialize(election, voter_id) + @voter_id = voter_id + @election = election + end + + def query + Decidim::Elections::Vote.where(election: @election, voter_id: @voter_id) + .order("created_at") + .last + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/elections/votes/pending_votes.rb b/decidim-elections/app/queries/decidim/elections/votes/pending_votes.rb new file mode 100644 index 00000000..f743cfec --- /dev/null +++ b/decidim-elections/app/queries/decidim/elections/votes/pending_votes.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Decidim + module Elections + module Votes + # A class used to find votes with a pending status + class PendingVotes < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + def self.for + new.query + end + + # Finds the votes with pending status + def query + Decidim::Elections::Vote.pending + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/admin/admin_users.rb b/decidim-elections/app/queries/decidim/votings/admin/admin_users.rb new file mode 100644 index 00000000..a2f185b0 --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/admin/admin_users.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A class used to find the admins for a voting or an organization votings. + class AdminUsers < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # voting - a voting that needs to find its voting admins + def self.for(voting) + new(voting).query + end + + # Syntactic sugar to initialize the class and return the queried objects. + # + # organization - an organization that needs to find its voting admins + def self.for_organization(organization) + new(nil, organization).query + end + + # Initializes the class. + # + # voting - a voting that needs to find its voting admins + # organization - an organization that needs to find its voting admins + def initialize(voting, organization = nil) + @voting = voting + @organization = voting&.organization || organization + end + + # Finds organization admins and the users with role admin for the given voting. + # + # Returns an ActiveRecord::Relation. + def query + organization.admins + end + + private + + attr_reader :voting, :organization + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/admin/ballot_style_by_voting_code.rb b/decidim-elections/app/queries/decidim/votings/admin/ballot_style_by_voting_code.rb new file mode 100644 index 00000000..cfc41848 --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/admin/ballot_style_by_voting_code.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A class used to find trustees by participatory space. + class BallotStyleByVotingCode < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # voting - the voting of the Ballot Style + # code - the code of the Ballot Style + def self.for(voting, code) + new(voting, code).query + end + + # Initializes the class. + def initialize(voting, code) + @voting = voting + @code = code + end + + # Gets the ballot style with the specified code in this voting + def query + Decidim::Votings::BallotStyle + .where(voting: @voting) + .find_by(code: @code) + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations.rb b/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations.rb new file mode 100644 index 00000000..d17ea2bf --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A class join the polling officers with their respective polling stations. + class PollingOfficersJoinPollingStations < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # polling_officers - the collection of polling officers + def self.for(polling_officers) + new(polling_officers).query + end + + # Initializes the class. + # + # polling_officers - the collection of polling officers + def initialize(polling_officers) + @polling_officers = polling_officers + end + + # Finds joins the polling officers with their associated polling stations. + # + # Returns an ActiveRecord::Relation. + def query + @polling_officers + .joins("LEFT JOIN decidim_votings_polling_stations presided_station ON decidim_votings_polling_officers.presided_polling_station_id = presided_station.id + LEFT JOIN decidim_votings_polling_stations managed_station ON decidim_votings_polling_officers.managed_polling_station_id = managed_station.id") + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations_and_user.rb b/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations_and_user.rb new file mode 100644 index 00000000..94d13b3e --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_polling_stations_and_user.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A class join the polling officers with their respective polling stations and users. + class PollingOfficersJoinPollingStationsAndUser < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # polling_officers - the collection of polling officers + def self.for(polling_officers) + new(polling_officers).query + end + + # Initializes the class. + # + # polling_officers - the collection of polling officers + def initialize(polling_officers) + @polling_officers = polling_officers + end + + # Finds joins the polling officers with their associated polling stations and users. + # + # Returns an ActiveRecord::Relation. + def query + Decidim::Query.merge( + PollingOfficersJoinPollingStations.new(@polling_officers), + PollingOfficersJoinUser.new(@polling_officers) + ).query + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_user.rb b/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_user.rb new file mode 100644 index 00000000..2ecdb66f --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/admin/polling_officers_join_user.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Admin + # A class join the polling officers with their respective decidim user. + class PollingOfficersJoinUser < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # polling_officers - the collection of polling officers + def self.for(polling_officers) + new(polling_officers).query + end + + # Initializes the class. + # + # polling_officers - the collection of polling officers + def initialize(polling_officers) + @polling_officers = polling_officers + end + + # Joins the polling officers with their associated decidim user. + # + # Returns an ActiveRecord::Relation. + def query + @polling_officers + .joins("LEFT JOIN decidim_users ON decidim_users.id = decidim_votings_polling_officers.decidim_user_id") + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/organization_prioritized_votings.rb b/decidim-elections/app/queries/decidim/votings/organization_prioritized_votings.rb new file mode 100644 index 00000000..4423f961 --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/organization_prioritized_votings.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This query class filters public votings given an organization in a + # meaningful prioritized order. + class OrganizationPrioritizedVotings < Decidim::Query + def initialize(organization, user = nil) + @organization = organization + @user = user + end + + def query + Decidim::Query.merge( + OrganizationPublishedVotings.new(@organization), + PrioritizedVotings.new + ).query + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/organization_promoted_votings.rb b/decidim-elections/app/queries/decidim/votings/organization_promoted_votings.rb new file mode 100644 index 00000000..b3af0794 --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/organization_promoted_votings.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This query selects the promoted votings + class OrganizationPromotedVotings < Decidim::Query + def initialize(organization) + @organization = organization + end + + def query + Decidim::Votings::Voting.where(organization: @organization).promoted + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/organization_published_votings.rb b/decidim-elections/app/queries/decidim/votings/organization_published_votings.rb new file mode 100644 index 00000000..2f945b6d --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/organization_published_votings.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This query class filters published votings given an organization. + class OrganizationPublishedVotings < Decidim::Query + def initialize(organization) + @organization = organization + end + + def query + Decidim::Query.merge( + OrganizationVotings.new(@organization), + PublishedVotings.new + ).query + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/organization_votings.rb b/decidim-elections/app/queries/decidim/votings/organization_votings.rb new file mode 100644 index 00000000..7a9654ce --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/organization_votings.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This query class filters all votings given an organization. + class OrganizationVotings < Decidim::Query + def initialize(organization) + @organization = organization + end + + def query + Decidim::Votings::Voting.where(organization: @organization) + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/prioritized_votings.rb b/decidim-elections/app/queries/decidim/votings/prioritized_votings.rb new file mode 100644 index 00000000..247e3d53 --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/prioritized_votings.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This query orders votings by importance, prioritizing promoted + # votings. + class PrioritizedVotings < Decidim::Query + def query + Decidim::Votings::Voting.order(promoted: :desc) + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/published_votings.rb b/decidim-elections/app/queries/decidim/votings/published_votings.rb new file mode 100644 index 00000000..4415669d --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/published_votings.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This query filters published votings only. + class PublishedVotings < Decidim::Query + def query + Decidim::Votings::Voting.published + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/votes/in_person_vote_for_voter.rb b/decidim-elections/app/queries/decidim/votings/votes/in_person_vote_for_voter.rb new file mode 100644 index 00000000..a09618ae --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/votes/in_person_vote_for_voter.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Votes + # A class used to find a non-rejected in person vote registered for a voter in an election + class InPersonVoteForVoter < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + # + # election - the election where the vote was casted + # voter_id - the identifier of the voter + def self.for(election, voter_id) + new(election, voter_id).query + end + + def initialize(election, voter_id) + @voter_id = voter_id + @election = election + end + + def query + Decidim::Votings::InPersonVote.not_rejected.find_by(election: @election, voter_id: @voter_id) + end + end + end + end +end diff --git a/decidim-elections/app/queries/decidim/votings/votes/pending_in_person_votes.rb b/decidim-elections/app/queries/decidim/votings/votes/pending_in_person_votes.rb new file mode 100644 index 00000000..54ca2a2f --- /dev/null +++ b/decidim-elections/app/queries/decidim/votings/votes/pending_in_person_votes.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Votes + # A class used to find in person votes with a pending status + class PendingInPersonVotes < Decidim::Query + # Syntactic sugar to initialize the class and return the queried objects. + def self.for + new.query + end + + # Finds the in person votes with pending status + def query + Decidim::Votings::InPersonVote.pending + end + end + end + end +end diff --git a/decidim-elections/app/serializers/decidim/votings/census/datum_serializer.rb b/decidim-elections/app/serializers/decidim/votings/census/datum_serializer.rb new file mode 100644 index 00000000..5aecfd72 --- /dev/null +++ b/decidim-elections/app/serializers/decidim/votings/census/datum_serializer.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + # This class serializes a Voting::Census::Datum + class DatumSerializer < Decidim::Exporters::Serializer + include Decidim::ApplicationHelper + + # Public: Initializes the serializer with a Voting::Census::Datum. + def initialize(datum) + @datum = datum + end + + # Public: Exports a hash with the serialized data for this datum. + def serialize + { + full_name: datum.full_name, + full_address: datum.full_address, + postal_code: datum.postal_code, + access_code: datum.access_code + } + end + + attr_reader :datum + end + end + end +end diff --git a/decidim-elections/app/services/decidim/elections/current_user_vote_flow.rb b/decidim-elections/app/services/decidim/elections/current_user_vote_flow.rb new file mode 100644 index 00000000..1431f28b --- /dev/null +++ b/decidim-elections/app/services/decidim/elections/current_user_vote_flow.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Service that encapsulates the vote flow used for elections for registered users. + class CurrentUserVoteFlow < VoteFlow + def initialize(election, current_user, &can_vote_block) + super(election) + + @current_user = current_user + @can_vote_block = can_vote_block + end + + def voter_login(params) + # There is no previous login page for this vote flow + end + + def has_voter? + current_user.present? + end + + delegate :name, to: :current_user, prefix: :voter, allow_nil: true + delegate :email, to: :current_user, allow_nil: true + + def user + current_user + end + + def voter_data + return nil unless current_user + + { + id: current_user.id, + created: current_user.created_at.to_i + } + end + + def vote_check(*) + VoteCheckResult.new( + allowed: current_user && (received_voter_token || can_vote_block.call), + error_message: I18n.t("votes.messages.not_allowed", scope: "decidim.elections") + ) + end + + def login_path(online_vote_path); end + + def questions_for(election) + election.questions + end + + def ballot_style_id; end + + private + + attr_accessor :current_user, :can_vote_block + + def valid_token_flow_data? + return @valid_token_flow_data if defined?(@valid_token_flow_data) + + @valid_token_flow_data = received_voter_token && received_voter_token_user_id && current_user.id == received_voter_token_user_id + end + + def received_voter_token_user_id + @received_voter_token_user_id ||= received_voter_token_data.dig(:flow, :id)&.to_i + end + end + end +end diff --git a/decidim-elections/app/services/decidim/elections/vote_flow.rb b/decidim-elections/app/services/decidim/elections/vote_flow.rb new file mode 100644 index 00000000..635601a6 --- /dev/null +++ b/decidim-elections/app/services/decidim/elections/vote_flow.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # Service that encapsulates the vote flow used for elections + class VoteFlow + def initialize(election) + @election = election + end + + def voter_from_token(params) + @received_voter_token = params[:voter_token] + @received_voter_id = params[:voter_id] + + received_voter_token.present? && received_voter_id.present? + end + + def voter_id + @voter_id ||= calculate_voter_id(voter_token_data) + end + + def voter_id_token(a_voter_id = nil) + @voter_id_token ||= tokenizer.hex_digest(a_voter_id || voter_id) + end + + def voter_token + @voter_token ||= received_voter_token || + message_encryptor.encrypt_and_sign( + voter_token_data.to_json, + expires_at: Decidim::Elections.voter_token_expiration_minutes.minutes.from_now + ) + end + + def valid_received_data? + valid_token_common_data? && valid_token_flow_data? && valid_voter_id? + end + + private + + attr_accessor :election, :context, :received_voter_token, :received_voter_id + + def calculate_voter_id(data) + Digest::SHA256.hexdigest(data.to_json) + end + + def valid_voter_id? + received_voter_id && received_voter_id == calculate_voter_id(received_voter_token_data) + end + + def valid_token_common_data? + received_voter_token && received_voter_token_data[:common] == voter_common_data.as_json + end + + def voter_token_data + @voter_token_data = { + common: voter_common_data, + flow: voter_data + } + end + + def voter_common_data + @voter_common_data = { + salt: election.salt, + slug: Decidim::Elections.bulletin_board.authority_slug, + created: election.created_at.to_i, + election: election.id + } + end + + def received_voter_token_data + return {} unless verified_received_voter_token + + @received_voter_token_data ||= JSON.parse(verified_received_voter_token).with_indifferent_access + end + + def verified_received_voter_token + return @verified_received_voter_token if defined?(@verified_received_voter_token) + + @verified_received_voter_token = begin + message_encryptor.decrypt_and_verify(received_voter_token) + rescue ActiveSupport::MessageEncryptor::InvalidMessage + nil + end + end + + def message_encryptor + @message_encryptor ||= ActiveSupport::MessageEncryptor.new([election.salt].pack("H*")) + end + + def tokenizer + @tokenizer ||= Decidim::Tokenizer.new(salt: election.salt, length: 10) + end + + class VoteCheckResult + def initialize(allowed:, error_message:, exit_path: nil) + @allowed = allowed + @error_message = error_message unless allowed + @exit_path = exit_path + end + + attr_reader :error_message, :exit_path + + def allowed? + @allowed + end + end + end + end +end diff --git a/decidim-elections/app/services/decidim/votings/census/access_codes_exporter.rb b/decidim-elections/app/services/decidim/votings/census/access_codes_exporter.rb new file mode 100644 index 00000000..ca9244aa --- /dev/null +++ b/decidim-elections/app/services/decidim/votings/census/access_codes_exporter.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "seven_zip_ruby" +require "zip" + +module Decidim + module Votings + module Census + # Public: Generates a 7z(seven zip) file with data files ready to be persisted + # somewhere so users can download their data. + # + # The 7z file wraps a ZIP file which finally contains the data files. + class AccessCodesExporter + include TranslatableAttributes + + FILE_NAME_PATTERN = "%{voting_name}-voting-access-codes.csv" + + attr_reader :dataset, :path, :password + + # Public: Initializes the class. + # + # dataset - The Voting::Census::Dataset to export the access codes for. + # path - The String path where to write the zip file. + # password - The password to protect the zip file. + def initialize(dataset, path, password) + @dataset = dataset + @path = File.expand_path path + @password = password + end + + def export + dirname = File.dirname(@path) + FileUtils.mkdir_p(dirname) unless File.directory?(dirname) + File.open(@path, "wb") do |file| + SevenZipRuby::Writer.open(file, password:) do |szw| + szw.header_encryption = true + szw.add_data(csv_data.read, format(FILE_NAME_PATTERN, voting_name: translated_attribute(dataset.voting.title).parameterize)) + end + end + end + + private + + def csv_data + Decidim::Exporters::CSV.new(dataset.data, Decidim::Votings::Census::DatumSerializer).export + end + end + end + end +end diff --git a/decidim-elections/app/services/decidim/votings/census_vote_flow.rb b/decidim-elections/app/services/decidim/votings/census_vote_flow.rb new file mode 100644 index 00000000..4a970e5e --- /dev/null +++ b/decidim-elections/app/services/decidim/votings/census_vote_flow.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # Service that encapsulates the vote flow used for Votings, using a census instead of users. + class CensusVoteFlow < Decidim::Elections::VoteFlow + def voter_login(params) + @login_params = params + end + + def voter_in_person(params) + @in_person_params = params + end + + def has_voter? + datum.present? + end + + def voter_name + datum&.full_name + end + + delegate :email, to: :datum, allow_nil: true + + def user + nil + end + + def voter_data + return nil unless datum + + { + id: datum.id, + created: datum.created_at.to_i, + name: datum.full_name + } + end + + def vote_check(online_vote_path: nil) + VoteCheckResult.new( + allowed: has_voter? && !voted_in_person?, + error_message: I18n.t("vote_flow.#{voted_in_person? ? "already_voted_in_person" : "datum_not_found"}", scope: "decidim.votings.census"), + exit_path: login_path(online_vote_path) + ) + end + + def questions_for(election) + if ballot_style.present? + ballot_style.questions.where(election:) + else + election.questions + end + end + + def ballot_style_id + ballot_style&.slug + end + + def voted_in_person? + Decidim::Votings::Votes::InPersonVoteForVoter.for(election, voter_id) + end + + def login_path(online_vote_path) + EngineRouter.main_proxy(election.component.participatory_space).voting_login_path(election_id: election.id, vote_path: online_vote_path) if online_vote_path + end + + private + + attr_accessor :login_params, :in_person_params + + def ballot_style + return @ballot_style if defined?(@ballot_style) + + @ballot_style = datum&.ballot_style + end + + def datum + return @datum if defined?(@datum) + + @datum = if received_voter_token + Decidim::Votings::Census::Datum.find_by(id: received_voter_token_datum_id) if received_voter_token_datum_id + elsif login_params || in_person_params + Decidim::Votings::Census::Datum.find_by(dataset: election.participatory_space.dataset, **datum_query) + end + end + + def received_voter_token_datum_id + @received_voter_token_datum_id ||= received_voter_token_data.dig(:flow, :id)&.to_i + end + + def datum_query + if login_params + { hashed_online_data: Decidim::Votings::Census::LoginForm.from_params(login_params, election:).hashed_online_data } + elsif in_person_params + { hashed_in_person_data: Decidim::Votings::Census::InPersonForm.from_params(in_person_params, election:).hashed_in_person_data } + end + end + + def valid_token_flow_data? + @valid_token_flow_data ||= has_voter? && received_voter_token_data[:flow].as_json == voter_data.as_json + end + end + end +end diff --git a/decidim-elections/app/views/decidim/elections/admin/answers/_form.html.erb b/decidim-elections/app/views/decidim/elections/admin/answers/_form.html.erb new file mode 100644 index 00000000..bb52a2a4 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/answers/_form.html.erb @@ -0,0 +1,23 @@ +
+
+
+
+ <%= form.translated :text_field, :title, autofocus: true, aria: { label: :title } %> +
+ +
+ <%= form.translated :editor, :description, aria: { label: :description } %> +
+ +
+ <%= form.number_field :weight %> +
+ + <%= render partial: "decidim/admin/shared/gallery", locals: { form: } %> + +
+ <%= render partial: "decidim/proposals/proposals/proposals_picker", locals: { form:, field: :proposals } %> +
+
+
+
diff --git a/decidim-elections/app/views/decidim/elections/admin/answers/edit.html.erb b/decidim-elections/app/views/decidim/elections/admin/answers/edit.html.erb new file mode 100644 index 00000000..f9c35da1 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/answers/edit.html.erb @@ -0,0 +1,23 @@ +<% add_decidim_page_title(t(".title")) %> +
+

+
+ <%= link_to translated_attribute(election.title), elections_path %> > + <%= link_to translated_attribute(question.title), election_questions_path(election) %> > + <%= t(".title") %> +
+

+
+ +
+
+ <%= decidim_form_for([election, question, @form], html: { class: "form form-defaults edit_answer" }) do |f| %> + <%= render partial: "form", object: f %> +
+
+ <%= f.submit t(".update"), class: "button button__sm button__secondary" %> +
+
+ <% end %> +
+
diff --git a/decidim-elections/app/views/decidim/elections/admin/answers/index.html.erb b/decidim-elections/app/views/decidim/elections/admin/answers/index.html.erb new file mode 100644 index 00000000..6090213f --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/answers/index.html.erb @@ -0,0 +1,93 @@ +<% add_decidim_page_title(t(".title")) %> +
+
+

+
+ <%= link_to translated_attribute(election.title), elections_path %> > + <%= link_to translated_attribute(question.title), election_questions_path(election) %> > + <%= t(".title") %> +
+ +
+ <% if allowed_to? :import_proposals, :answer, election: election, question: question %> + <%= import_dropdown do %> +
  • + <%= link_to t("actions.import", scope: "decidim.elections", name: t("models.answer.name", scope: "decidim.elections.admin")), new_election_question_proposals_import_path(election, question) %> +
  • + <% end %> + <% end %> + <%= link_to t("actions.new_answer", scope: "decidim.elections"), new_election_question_answer_path(election, question), class: "button button__sm button__secondary" if allowed_to? :create, :answer, election: election, question: question %> +
    +

    +
    + +
    +
    + + + + + + <% if election.results? %> + + + <% end %> + + + + + <% answers.each do |answer| %> + + + + <% if election.results? %> + + <% if answer.selected %> + + <% else %> + + <% end %> + + <% end %> + + + <% end %> + +
    <%= t("models.answer.fields.title", scope: "decidim.elections") %><%= t("models.answer.fields.proposals", scope: "decidim.elections") %><%= t("models.answer.fields.votes", scope: "decidim.elections") %><%= t("models.answer.fields.selected", scope: "decidim.elections") %><%= t("actions.title", scope: "decidim.elections") %>
    + <% if allowed_to? :update, :answer, election: election, question: question, answer: answer %> + <%= link_to translated_attribute(answer.title), edit_election_question_answer_path(election, question, answer) %> + <% else %> + <%= translated_attribute(answer.title) %> + <% end %> + + <%= answer.linked_resources(:proposals, "related_proposals").count %> + + <%= answer.results_total %> + + <%= t("answers.selected", scope: "decidim.elections.admin") %> + + <%= t("answers.not_selected", scope: "decidim.elections.admin") %> + + <% if allowed_to? :update, :answer, election: election, question: question, answer: answer %> + <%= icon_link_to "pencil-line", edit_election_question_answer_path(election, question, answer), t("actions.edit", scope: "decidim.elections"), class: "action-icon--edit" %> + <% else %> + <%= icon "pencil-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.edit", scope: "decidim.elections") %> + <% end %> + + <% if election.results? %> + <% if answer.selected %> + <%= icon_link_to "close-circle-line", unselect_election_question_answer_path(election, question, answer), t("answers.select.disable", scope: "decidim.elections.admin"), class: "action-icon", method: :put %> + <% else %> + <%= icon_link_to "check-line", select_election_question_answer_path(election, question, answer), t("answers.select.enable", scope: "decidim.elections.admin"), class: "action-icon", method: :put %> + <% end %> + <% end %> + + <% if allowed_to? :delete, :answer, election: election, question: question, answer: answer %> + <%= icon_link_to "delete-bin-line", election_question_answer_path(election, question, answer), t("actions.destroy", scope: "decidim.elections"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.elections") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.elections") %> + <% end %> +
    +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/answers/new.html.erb b/decidim-elections/app/views/decidim/elections/admin/answers/new.html.erb new file mode 100644 index 00000000..60e1a57d --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/answers/new.html.erb @@ -0,0 +1,23 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    +
    + <%= link_to translated_attribute(election.title), elections_path %> > + <%= link_to translated_attribute(question.title), election_questions_path(election) %> > + <%= t(".title") %> +
    +

    +
    + +
    +
    + <%= decidim_form_for([election, question, @form], html: { class: "form form-defaults new_answer" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/elections/_form.html.erb b/decidim-elections/app/views/decidim/elections/admin/elections/_form.html.erb new file mode 100644 index 00000000..9b74cd4d --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/elections/_form.html.erb @@ -0,0 +1,27 @@ +
    +
    +
    +
    + <%= form.translated :text_field, :title, autofocus: true, aria: { label: :title } %> +
    + +
    + <%= form.translated :editor, :description, aria: { label: :description } %> +
    + +
    + <% announcement_message = t(".organization_time_zone", time_zone: current_organization.time_zone, time: Time.zone.now) %> + <%= cell("decidim/announcement", announcement_message, callout_class: "warning" ) %> + +
    + <%= form.datetime_field :start_time %> +
    +
    + <%= form.datetime_field :end_time %> +
    +
    + + <%= render partial: "decidim/admin/shared/gallery", locals: { form: } %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/elections/edit.html.erb b/decidim-elections/app/views/decidim/elections/admin/elections/edit.html.erb new file mode 100644 index 00000000..71dd3161 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/elections/edit.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for(@form, html: { class: "form-defaults form edit_election" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".update"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/elections/index.html.erb b/decidim-elections/app/views/decidim/elections/admin/elections/index.html.erb new file mode 100644 index 00000000..b0f3d69a --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/elections/index.html.erb @@ -0,0 +1,91 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> + + <%= link_to t("actions.new_election", scope: "decidim.elections"), new_election_path, class: "button button__sm button__secondary" if allowed_to? :create, :election %> + <%= render partial: "decidim/admin/components/resource_action" %> +

    +
    +
    + + + + + + + + + + + + <% elections.each do |election| %> + + + + + + + + <% end %> + +
    <%= t("models.election.fields.title", scope: "decidim.elections") %><%= t("models.election.fields.start_time", scope: "decidim.elections") %><%= t("models.election.fields.end_time", scope: "decidim.elections") %><%= t("models.election.fields.bb_status", scope: "decidim.elections") %><%= t("actions.title", scope: "decidim.elections") %>
    + <% if allowed_to? :update, :election, election: election %> + <%= link_to translated_attribute(election.title), edit_election_path(election) %> + <% else %> + <%= translated_attribute(election.title) %> + <% end %> + + <% if election.start_time %> + <%= l election.start_time, format: :long %> + <% end %> + + <% if election.end_time %> + <%= l election.end_time, format: :long %> + <% end %> + <%= t("steps.#{election.bb_status || "create_election"}.title", scope: "decidim.elections.admin") %> + <% if allowed_to? :update, :election, election: election %> + <%= icon_link_to "pencil-line", edit_election_path(election), t("actions.edit", scope: "decidim.elections"), class: "action-icon--edit" %> + <% else %> + <%= icon "pencil-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.edit", scope: "decidim.elections") %> + <% end %> + + <%= icon_link_to "list-check", election_questions_path(election), t("actions.manage_questions", scope: "decidim.elections"), class: "action-icon--manage-questions" %> + + <% if allowed_to? :read, :steps, election: election %> + <%= icon_link_to "guide-line", election_steps_path(election), t("actions.manage_steps", scope: "decidim.elections"), class: "action-icon--manage-steps" %> + <% end %> + + <% if allowed_to? :update, :questionnaire, questionnaire: election.questionnaire %> + <%= icon_link_to "group-line", edit_feedback_form_path(election), t("actions.feedback", scope: "decidim.elections"), class: "action-icon--survey" %> + <% else %> + <%= icon "group-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.feedback", scope: "decidim.elections") %> + <% end %> + + <% if election.published? %> + <% if allowed_to?(:unpublish, :election, election: election) %> + <%= icon_link_to "close-circle-line", url_for(action: :unpublish, id: election, controller: "elections"), t("actions.unpublish", scope: "decidim.elections"), class: "action-icon--unpublish", method: :put %> + <% else %> + <%= icon "close-circle-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.unpublish", scope: "decidim.elections") %> + <% end %> + <% else %> + <% if allowed_to?(:publish, :election, election: election) %> + <%= icon_link_to "check-line", url_for(action: :publish, id: election, controller: "elections"), t("actions.publish", scope: "decidim.elections"), class: "action-icon--publish", method: :put %> + <% else %> + <%= icon "check-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.publish", scope: "decidim.elections") %> + <% end %> + <% end %> + + <%= icon_link_to "eye-line", resource_locator(election).path, t("actions.preview", scope: "decidim.elections"), class: "action-icon--preview", target: :blank, data: { "external-link": false } %> + + <%= resource_permissions_link(election) %> + + <% if allowed_to? :delete, :election, election: election %> + <%= icon_link_to "delete-bin-line", election_path(election), t("actions.destroy", scope: "decidim.elections"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.elections") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.elections") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/elections/new.html.erb b/decidim-elections/app/views/decidim/elections/admin/elections/new.html.erb new file mode 100644 index 00000000..5524ec67 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/elections/new.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for(@form, html: { class: "form-defaults form new_election" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/proposals_imports/new.html.erb b/decidim-elections/app/views/decidim/elections/admin/proposals_imports/new.html.erb new file mode 100644 index 00000000..38b47a33 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/proposals_imports/new.html.erb @@ -0,0 +1,39 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    +
    + <%= link_to translated_attribute(election.title), elections_path %> > + <%= link_to translated_attribute(question.title), election_questions_path(election) %> > + <%= t(".title") %> +
    +

    +
    + +
    +
    + <%= decidim_form_for(@form, url: election_question_proposals_import_path, html: { class: "form form-defaults import_proposals" }) do |f| %> + <% if @form.origin_components.any? %> +
    +
    +
    +
    + <%= f.select :origin_component_id, @form.origin_components_collection, prompt: t(".select_component") %> +
    +
    + <%= f.check_box :import_all_accepted_proposals %> +
    +
    +
    +
    + +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% else %> +

    <%= t(".no_components") %>

    + <% end %> + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/questions/_form.html.erb b/decidim-elections/app/views/decidim/elections/admin/questions/_form.html.erb new file mode 100644 index 00000000..a3b151e1 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/questions/_form.html.erb @@ -0,0 +1,25 @@ +
    +
    +
    +
    + <%= form.translated :text_field, :title, autofocus: true, aria: { label: :title } %> +
    + +
    + <%= form.number_field :max_selections, step: 1, min: 0 %> +
    + +
    + <%= form.number_field :weight %> +
    + +
    + <%= form.check_box :random_answers_order %> +
    + +
    + <%= form.check_box :min_selections, {}, "0", "1" %> +
    +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/questions/edit.html.erb b/decidim-elections/app/views/decidim/elections/admin/questions/edit.html.erb new file mode 100644 index 00000000..a4e86d9d --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/questions/edit.html.erb @@ -0,0 +1,21 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    +
    + <%= link_to translated_attribute(election.title), elections_path %> > + <%= t(".title") %> +
    +

    +
    +
    +
    + <%= decidim_form_for([election, @form], html: { class: "form-defaults form edit_question" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".update"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/questions/index.html.erb b/decidim-elections/app/views/decidim/elections/admin/questions/index.html.erb new file mode 100644 index 00000000..561960b6 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/questions/index.html.erb @@ -0,0 +1,60 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> + + <%= link_to t("actions.new_question", scope: "decidim.elections"), new_election_question_path(election), class: "button button__sm button__secondary" if allowed_to? :create, :question, election: election %> +

    +
    +
    + + + + + + + + + + + <% questions.each do |question| %> + + + + + + + <% end %> + +
    <%= t("models.question.fields.title", scope: "decidim.elections") %><%= t("models.question.fields.answers", scope: "decidim.elections") %><%= t("models.question.fields.max_selections", scope: "decidim.elections") %><%= t("actions.title", scope: "decidim.elections") %>
    + <% if allowed_to? :update, :question, election: election, question: question %> + <%= link_to translated_attribute(question.title), edit_election_question_path(election, question) %> + <% else %> + <%= translated_attribute(question.title) %> + <% end %> + + <%= question.answers.count %> + + <% if question.max_selections.zero? %> + - + <% else %> + <%= question.max_selections %> + <% end %> + + <% if allowed_to? :update, :question, election: election, question: question %> + <%= icon_link_to "pencil-line", edit_election_question_path(election, question), t("actions.edit", scope: "decidim.elections"), class: "action-icon--edit" %> + <% else %> + <%= icon "pencil-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.edit", scope: "decidim.elections") %> + <% end %> + + <%= icon_link_to "list-check", election_question_answers_path(election, question), t("actions.manage_answers", scope: "decidim.elections"), class: "action-icon--manage-answers" %> + + <% if allowed_to? :delete, :question, election: election, question: question %> + <%= icon_link_to "delete-bin-line", election_question_path(election, question), t("actions.destroy", scope: "decidim.elections"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.elections") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.elections") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/questions/new.html.erb b/decidim-elections/app/views/decidim/elections/admin/questions/new.html.erb new file mode 100644 index 00000000..6dd7102f --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/questions/new.html.erb @@ -0,0 +1,22 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    +
    + <%= link_to translated_attribute(election.title), elections_path %> > + <%= t(".title") %> +
    +

    +
    + +
    +
    + <%= decidim_form_for([election, @form], html: { class: "form-defaults form new_question" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/shared/_trustees_secondary_nav.html.erb b/decidim-elections/app/views/decidim/elections/admin/shared/_trustees_secondary_nav.html.erb new file mode 100644 index 00000000..1a30843a --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/shared/_trustees_secondary_nav.html.erb @@ -0,0 +1,3 @@ +
  • class="is-active" <% end %>> + <%= aria_selected_link_to "Trustees", engine_router.trustees_path %> +
  • diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_create_election.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_create_election.html.erb new file mode 100644 index 00000000..fc1f2dd8 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_create_election.html.erb @@ -0,0 +1,81 @@ +
    +
    +

    + <%= t(".title") %> +

    +
    + +
    +
    +
      + <% form.messages.each do |key, value| %> + <% if form.errors.include?(key) %> + <% Array(form.errors[key]).each do |error| %> +
    • + <%= icon "close-line", class: "inline-block text-alert", role: "img", "aria-hidden": true %> <%= error.html_safe %> + <% method = key == :published ? :put : :get %> + <%= fix_it_button_with_icon(value[:link], "pencil-line", method) %> +
    • + <% end %> + <% else %> +
    • <%= icon "check-line", class: "inline-block text-success", role: "img", "aria-hidden": true %> <%= value[:message].html_safe %>
    • + <% end %> + <% end %> +
    + + <% if form.needs_census? %> +

    <%= t(".census") %>

    +
      + <% form.census_messages.each do |key, value| %> + <% if form.errors.include?(key) %> +
    • <%= icon "close-line", class: "inline-block text-alert", role: "img", "aria-hidden": true %> <%= form.errors.messages[key][0].html_safe %>
    • + <% else %> +
    • <%= icon "check-line", class: "inline-block text-success", role: "img", "aria-hidden": true %> <%= value.html_safe %>
    • + <% end %> + <% end %> +
    + <% end %> + +

    <%= t(".trustees") %>

    +
      + <% if form.participatory_space_trustees.none? %> +
    • <%= icon "close-line", class: "inline-block text-alert", role: "img", "aria-hidden": true %> <%= t(".no_trustees") %>
    • + <% end %> + + <% form.participatory_space_trustees + .map { |trustee| [trustee, form.trustees.none? || form.trustees.include?(trustee)] } + .sort_by { |_trustee, used| used ? 0 : 1 } + .each do |trustee, used| %> +
    • > + <%= icon trustee.public_key ? "check-line" : "close-line", class: "inline-block text-#{trustee.public_key ? "success" : "alert"}", role: "img", "aria-hidden": true %> + <%= trustee.user.name || trustee.name %> <%= t(".public_key.#{used}").html_safe %> + <% if used %> + <%= f.hidden_field :trustee_ids, multiple: true, value: trustee.id %> + <% else %> + <%= t(".not_used_trustee") %> + <% end %> +
    • + <% end %> +
    +
    +
    + +
    +
    +
    +
    + + <%= t(".technical_configuration.title") %> + + +
    +
    +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_created.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_created.html.erb new file mode 100644 index 00000000..638db880 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_created.html.erb @@ -0,0 +1,16 @@ +
    +
    +

    <%= t(".title") %>

    +
    + +
    +

    <%= t(".trustees") %>

    +
      + <% election.trustees.each do |trustee| %> +
    • + <%= trustee.name %> +
    • + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony.html.erb new file mode 100644 index 00000000..d79383f9 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony.html.erb @@ -0,0 +1,51 @@ +
    +
    +

    <%= t(".title") %>

    +
    + +
    +
    + + + + + + + + + + + + <% election.trustees.each do |trustee| %> + + + + + + + + <% end %> + +
    <%= t("models.trustees_participatory_space.fields.name", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.status", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.email", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.public_key", scope: "decidim.elections") %>
    + <%= trustee.name %> + +
    <%= icon "loader-4-line", class: "animate-spin" %>
    +
    <%= icon "list-check", class: "text-success" %>
    +
    + <%= trustee.user.email %> + + <%= present(trustee).public_key_thumbprint %> +
    +
    +
    +
    + +
    + <%= link_to t("steps.key_ceremony.continue", scope: "decidim.elections.admin"), election_steps_path(election), class: "button button__sm button__secondary disabled js-continue-link" %> +
    + +<%= append_javascript_pack_tag "decidim_elections_admin" %> diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony_ended.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony_ended.html.erb new file mode 100644 index 00000000..d7369cf3 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_key_ceremony_ended.html.erb @@ -0,0 +1,15 @@ +
    +
    +

    <%= t(".title") %>

    +
    + +
    + <% form.messages.each do |key, value| %> + <% if form.errors.include?(key) %> +

    <%= form.errors.messages[key][0].html_safe %>

    + <% else %> +

    <%= value.html_safe %>

    + <% end %> + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_results_published.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_results_published.html.erb new file mode 100644 index 00000000..3088b402 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_results_published.html.erb @@ -0,0 +1,62 @@ +
    +
    +

    + <%= t(".title") %> +

    +
    +
    +
      + <% election.questions.each_with_index do |question, i| %> +
    • " data-accordion-item> + <%= t(".question") %>: <%= translated_attribute(question.title) %> +
      +
      + + + + + + + + + + <% question.answers.each do |answer| %> + + + <% if answer.results_total > 0 %> + + <% else %> + + <% end %> + <% if answer.selected %> + + <% else %> + + <% end %> + + <% end %> + + <% if question.nota_option? %> + + + + + + <% end %> + + + + + + +
      <%= t(".answer") %><%= t(".result") %><%= t(".selected") %>
      <%= translated_attribute(answer.title) %><%= answer.results_total %><%= answer.results_total %><%= t(".selected") %><%= t(".not_selected") %>
      <%= t("decidim.elections.votes.new.nota_option") %><%= question.blank_votes %>
      <%= question.results_total %>
      +
      +
      +
    • + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_tally_ended.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_tally_ended.html.erb new file mode 100644 index 00000000..35103f9c --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_tally_ended.html.erb @@ -0,0 +1,55 @@ +
    +
    +

    + <%= t(".title") %> +

    +
    +
    +
      + <% election.questions.each_with_index do |question, i| %> +
    • " data-accordion-item> + <%= t(".question") %>: <%= translated_attribute(question.title) %> +
      +
      + + + + + + + + + + <% question.answers.each do |answer| %> + + + <% if answer.results_total > 0 %> + + <% else %> + + <% end %> + <% if answer.selected %> + + <% else %> + + <% end %> + + <% end %> + + + + + + + +
      <%= t(".answer") %><%= t(".result") %><%= t(".selected") %>
      <%= translated_attribute(answer.title) %><%= answer.results_total %><%= answer.results_total %><%= t(".selected") %><%= t(".not_selected") %>
      <%= question.results_total %>
      +
      +
      +
    • + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_tally_started.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_tally_started.html.erb new file mode 100644 index 00000000..45c150ff --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_tally_started.html.erb @@ -0,0 +1,67 @@ +
    +
    +

    <%= t(".title") %>

    +
    + +
    + + <% if missing_trustees_allowed > 0 %> +
      + <%= content_tag :li, t(".mark_as_missing_description") %> + <%= content_tag :li, t(".tally_completion", quorum:) %> + <%= content_tag :li, t(".undo_mark_as_missing") %> +
    + <% end %> +
    + + + + + + + + + + + + <% election.trustees.each do |trustee| %> + + + + + + + + <% end %> + +
    <%= t("models.trustees_participatory_space.fields.name", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.status", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.email", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.public_key", scope: "decidim.elections") %>
    + <%= trustee.name %> + +
    <%= icon "loader-4-line", class: "animate-spin" %>
    +
    <%= icon "list-check", class: "text-success" %>
    +
    <%= icon "delete-bin-line", class: "text-alert" %>
    +
    + <%= trustee.user.email %> + + <%= present(trustee).public_key_thumbprint %> + + <%= f.button type: :button, formaction: election_step_path(election, current_step, trustee_id: trustee.id), + class: "button button__sm button__secondary alert hollow hide js-report-missing-trustee" do %> + <%= icon "delete-bin-line" %> <%= t(".mark_as_missing") %> + <% end %> +
    +
    +
    +
    + +
    + <%= link_to t("steps.tally_started.continue", scope: "decidim.elections.admin"), election_steps_path(election), class: "button button__sm button__secondary disabled js-continue-link" %> +
    + +<%= append_javascript_pack_tag "decidim_elections_admin" %> diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_vote.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_vote.html.erb new file mode 100644 index 00000000..e0124a71 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_vote.html.erb @@ -0,0 +1,21 @@ +
    + <%= render(partial: "vote_stats") %> +
    + +
    +
    +

    <%= t(".title") %>

    +
    + +
    + <% form.messages.each do |key, value| %> + <% if form.errors.include?(key) %> +

    <%= form.errors.messages[key][0].html_safe %>

    + <% else %> +

    <%= value.html_safe %>

    + <% end %> + <% end %> +
    +
    + +<%= append_javascript_pack_tag "decidim_elections_admin" %> diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_vote_ended.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_vote_ended.html.erb new file mode 100644 index 00000000..061d5839 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_vote_ended.html.erb @@ -0,0 +1,19 @@ +
    +
    +

    <%= t(".title") %>

    +
    + +
    +

    <%= t(".text") %>

    + + <% form.messages.each do |key, value| %> + <% if form.errors.include?(key) %> +

    <%= form.errors.messages[key][0].html_safe %>

    + <% else %> +

    <%= value.html_safe %>

    + <% end %> + <% end %> +
    +
    + +<%= render(partial: "vote_stats") %> diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/_vote_stats.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/_vote_stats.html.erb new file mode 100644 index 00000000..c6744143 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/_vote_stats.html.erb @@ -0,0 +1,31 @@ +
    +
    +

    + <%= t(".title") %> +

    +
    +
    + <% if vote_stats.values.sum == 0 %> +
    +

    <%= t(".no_vote_statistics_yet") %>

    +
    + <% else %> + + + + + + + + + + + + + + + +
    <%= t(".votes") %><%= t(".voters") %>
    <%= vote_stats[:votes] %><%= vote_stats[:voters] %>
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/steps/index.html.erb b/decidim-elections/app/views/decidim/elections/admin/steps/index.html.erb new file mode 100644 index 00000000..76aeaec9 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/steps/index.html.erb @@ -0,0 +1,56 @@ +<% add_decidim_page_title(t("title", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps", election: translated_attribute(election.title))) %> +
    +

    + <%= t("title", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps", election: translated_attribute(election.title)) %> +

    +
    + +
    +
    +
    +
    + <% steps(current_step).each_with_index do |step, i| %> + <% if i > 0 %> > <% end %> + <%= t("steps.#{step.first}.title", scope: "decidim.elections.admin") %> + <% end %> +
    +
    + <% if @form %> + + <%= decidim_form_for(@form, url: election_step_path(election, current_step), method: :patch, html: { class: "form form-defaults step #{current_step}" }) do |f| %> +
    + <%= render partial: current_step.to_s, locals: { form: @form, f: } %> +
    + + <%= f.hidden_field :current_step %> + + <% if @form.main_button? %> +
    + <%= f.button type: :submit, class: "button button__sm button__secondary", disabled: @form.invalid? do %> + <% if @form.pending_action %> + <%= t("steps.processing", scope: "decidim.elections.admin") %> + <% else %> + <%= t("steps.#{current_step}.submit", scope: "decidim.elections.admin") %> + <% end %> + <% end %> +
    + <% if @form.pending_action %> +
    + + <%= append_javascript_pack_tag "decidim_elections_admin" %> + <% end %> + <% end %> + <% end %> + <% else %> +
    + <%= render partial: current_step.to_s %> +
    + <% end %> + + <%= render "decidim/elections/shared/broken_promises_modal" %> +
    +
    + +<%= append_stylesheet_pack_tag "decidim_elections_admin" %> diff --git a/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/_form.html.erb b/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/_form.html.erb new file mode 100644 index 00000000..043d3fdd --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/_form.html.erb @@ -0,0 +1,15 @@ +
    +
    + +
    +
    +
    + <% prompt_options = { url: decidim_admin.users_organization_url, placeholder: t(".select_user") } %> + <%= form.autocomplete_select(:user_id, form.object.user.presence, { multiple: false }, prompt_options) do |user| + { value: user.id, label: "#{user.name} (@#{user.nickname})" } + end %> +
    +
    +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/index.html.erb b/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/index.html.erb new file mode 100644 index 00000000..fb33fc6a --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/index.html.erb @@ -0,0 +1,66 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> + <%= link_to t("actions.new_trustee", scope: "decidim.elections"), new_trustee_path, class: "button button__sm button__secondary new ml-auto" %> +

    +
    +
    + + + + + + + + + + + + + <% trustees.each do |trustee| %> + + + + + + + + + <% end %> + +
    <%= t("models.trustees_participatory_space.fields.name", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.email", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.public_key", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.notification", scope: "decidim.elections") %><%= t("models.trustees_participatory_space.fields.status", scope: "decidim.elections") %><%= t("actions.title", scope: "decidim.elections") %>
    + <% if trustee.name.present? %> + <%= trustee.name %> + <% else %> + <%= trustee.user.name %> + <% end %> + + <%= trustee.user.email %> + + <%= present(trustee).public_key_thumbprint %> + + <%= trustee.created_at.ctime %> + + <% if trustee_current_participatory_space(trustee).considered %> + <%= t("models.trustees_participatory_space.fields.considered", scope: "decidim.elections") %> + <% else %> + <%= t("models.trustees_participatory_space.fields.inactive", scope: "decidim.elections") %> + <% end %> + + <% if allowed_to?(:update, :trustee_participatory_space, trustee_participatory_space: trustee_current_participatory_space(trustee)) %> + <%= icon_link_to "close-circle-line", edit_trustee_path(trustee_current_participatory_space(trustee)), considered_label_action_for(trustee), class: "action-icon--edit" %> + <% else %> + <%= icon "close-circle-line", class: "action-icon action-icon--disabled", role: "img", aria_label: considered_label_action_for(trustee) %> + <% end %> + + <% if allowed_to?(:delete, :trustee_participatory_space, trustee_participatory_space: trustee_current_participatory_space(trustee)) %> + <%= icon_link_to "delete-bin-line", trustee_path(trustee_current_participatory_space(trustee)), t("actions.destroy", scope: "decidim.elections"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.elections") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.elections") %> + <% end %> +
    +
    +
    +<%= decidim_paginate trustees %> diff --git a/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/new.html.erb b/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/new.html.erb new file mode 100644 index 00000000..777cfd2a --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/admin/trustees_participatory_spaces/new.html.erb @@ -0,0 +1,19 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    + +
    +
    + <%= decidim_form_for(@form, url: trustees_path, html: { class: "form-defaults form new_trustee" }) do |f| %> + <%= render partial: "form", object: f, locals: { title: t(".title") } %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/elections/elections/_elections.html.erb b/decidim-elections/app/views/decidim/elections/elections/_elections.html.erb new file mode 100644 index 00000000..bf1ad118 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/elections/_elections.html.erb @@ -0,0 +1,22 @@ +<% if elections.none? %> + <%= cell("decidim/announcement", t("decidim.elections.warnings.no_elections")) %> +<% else %> + + <% if paginated_elections.none? && params[:filter].present? %> + <%= cell("decidim/announcement", t("decidim.elections.warnings.empty_filters")) %> + <% else %> + <%= cell("decidim/announcement", t("decidim.elections.warnings.no_scheduled_elections")) if @forced_past_elections %> + +

    <%= t("elections_count", scope: "decidim.elections.elections.count", count: paginated_elections.total_count) %>

    + + <%= order_selector available_orders, i18n_scope: "decidim.elections.orders" %> + +
    + <% paginated_elections.each do |election| %> + <%= card_for election %> + <% end %> +
    + + <%= decidim_paginate paginated_elections %> + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/elections/election_log.html.erb b/decidim-elections/app/views/decidim/elections/elections/election_log.html.erb new file mode 100644 index 00000000..e1667dca --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/elections/election_log.html.erb @@ -0,0 +1,176 @@ +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> + +
    +

    + <%= t("decidim.elections.elections.election_log.title") %> +

    + +
    + <%= t("decidim.elections.elections.election_log.description") %> +
    +
    + +
    + + +
    + + +

    + <%= t("decidim.elections.elections.election_log.creation_title") %> + <%= t("decidim.elections.elections.election_log.not_created") %> + +

    + + + <%= t("decidim.elections.elections.election_log.creation_description.not_created") %> + + + + +
    + + +
    + + +

    + <%= t("decidim.elections.elections.election_log.key_ceremony_title") %> + <%= t("decidim.elections.elections.election_log.not_started") %> + + +

    + + + <%= t("decidim.elections.elections.election_log.key_ceremony_description.not_started") %> + + + + + +
    + + +
    + + +

    + <%= t("decidim.elections.elections.election_log.vote_title") %> + <%= t("decidim.elections.elections.election_log.not_started") %> + + +

    + + + <%= t("decidim.elections.elections.election_log.vote_description.not_started") %> + + + + + +
    + + +
    + + +

    + <%= t("decidim.elections.elections.election_log.tally_title") %> + <%= t("decidim.elections.elections.election_log.not_started") %> + + +

    + + + <%= t("decidim.elections.elections.election_log.tally_description.not_started") %> + + + + + +
    + + +
    + + +

    + <%= t("decidim.elections.elections.election_log.results_title") %> + <%= t("decidim.elections.elections.election_log.unpublished") %> + +

    + + + <%= t("decidim.elections.elections.election_log.results_description.not_published") %> + + + + +
    + + +
    +

    + <%= t("decidim.elections.elections.election_log.verifiable_results.title") %> + <% if election.results_published? %> + <%= t("decidim.elections.elections.election_log.verify") %> + <% else %> + <%= t("decidim.elections.elections.election_log.not_ready") %> + <% end %> +

    + + <% if election.results_published? %> + <%= content_tag :p, sanitize(t("decidim.elections.elections.election_log.verifiable_results.description.ready")) %> +
    sha256sum <verifiable_election_file_path>
    + <%= content_tag :p, sanitize(t("decidim.elections.elections.election_log.verifiable_results.how_to_verify")) %> +
    bin/verify <verifiable_election_file_path>
    + <% else %> + <%= t("decidim.elections.elections.election_log.verifiable_results.description.not_ready") %> + <% end %> + + <% if election.results_published? %> + + <%= t("decidim.elections.elections.election_log.verifiable_results.verifiable_file") %> + <% if election.verifiable_results_file_url.present? %> + <%= link_to t("decidim.elections.elections.election_log.download"), election.verifiable_results_file_url, class: "button button__sm button__transparent-secondary" %> + <% else %> + <%= t("decidim.elections.elections.election_log.not_available") %> + <% end %> + + + + <%= t("decidim.elections.elections.election_log.verifiable_results.checksum") %> + <% if election.verifiable_results_file_hash.present? %> + <%= election.verifiable_results_file_hash %> + <% else %> + <%= t("decidim.elections.elections.election_log.not_available") %> + <% end %> + + <% end %> +
    +
    + +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/elections/index.html.erb b/decidim-elections/app/views/decidim/elections/elections/index.html.erb new file mode 100644 index 00000000..12e21580 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/elections/index.html.erb @@ -0,0 +1,24 @@ +<% add_decidim_page_title(t("decidim.components.elections.name")) %> + +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<% content_for :aside do %> +

    <%= component_name %>

    + + <%= render layout: "decidim/shared/filters", locals: { + filter_sections: @forced_past_elections ? [] : filter_sections, + search_variable: :search_text_cont, + skip_to_id: "elections" + } do %> + <%= hidden_field_tag :order, order, id: nil, class: "order_filter" %> + <% end %> +<% end %> + +<%= render layout: "layouts/decidim/shared/layout_two_col" do %> + <%= render partial: "decidim/shared/component_announcement" %> + +
    + <%= render partial: "elections" %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/elections/index.js.erb b/decidim-elections/app/views/decidim/elections/elections/index.js.erb new file mode 100644 index 00000000..08365217 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/elections/index.js.erb @@ -0,0 +1,5 @@ +var $elections = $('#elections'); +var $orderFilterInput = $('.order_filter'); + +$elections.html('<%= j(render partial: "elections").strip.html_safe %>'); +$orderFilterInput.val('<%= order %>'); diff --git a/decidim-elections/app/views/decidim/elections/elections/show.html.erb b/decidim-elections/app/views/decidim/elections/elections/show.html.erb new file mode 100644 index 00000000..5a5a1717 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/elections/show.html.erb @@ -0,0 +1,87 @@ +<% add_decidim_meta_tags( + title: translated_attribute(election.title), + description: translated_attribute(election.description) +) %> + +<% +edit_link( + resource_locator(election).edit, + :update, + :election, + election: +) +%> + +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<% content_for :aside do %> +
    + <%= cell("decidim/elections/election_vote_cta", election) %> +
    + +
    +

    <%= t("verify.already_voted", scope: "decidim.elections.elections.show") %>

    + + + + +
    +<% end %> + +<%= render layout: "layouts/decidim/shared/layout_item" do %> + +
    + <%= render partial: "decidim/shared/component_announcement" %> + + <%= cell "decidim/elections/remaining_time_callout", election %> + + <%= cell("decidim/announcement", t("voting_period_status.#{election.voting_period_status}", + scope: "decidim.elections.elections.show", + start_time: "#{l election.start_time, format: :long}", + end_time: "#{l election.end_time, format: :long}").html_safe) %> + +

    <%== present(election).title %>

    +
    + +
    +
    + <%= decidim_sanitize_editor_admin(translated_attribute(election.description)) %> +
    +
    + + <%= attachments_for election %> + + <% if onboarding %> + <%= render partial: "decidim/elections/votes/onboarding_modal" %> + <% end %> + + <% unless election.finished? %> +
    + <%= cell("decidim/elections/election_preview", election) %> +
    + <% end %> + + <% if election.results_published? %> +
    + <%= cell("decidim/elections/election_results", election) %> +
    + <% end %> + +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/shared/_broken_promises_modal.html.erb b/decidim-elections/app/views/decidim/elections/shared/_broken_promises_modal.html.erb new file mode 100644 index 00000000..44f6f2e7 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/shared/_broken_promises_modal.html.erb @@ -0,0 +1,24 @@ +<% i18n_scope ||= "decidim.elections.connection.failed" %> + +<%= decidim_modal id: "server-failure" do %> +
    + <%= icon "error-warning-line" %> +

    <%= t("modal.title", scope: i18n_scope) %>

    +
    +

    + <%= t("modal.communication_lost", scope: i18n_scope).html_safe %> +

    + + + +
    +
    + +
    + +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_mailer/notification.html.erb b/decidim-elections/app/views/decidim/elections/trustee_mailer/notification.html.erb new file mode 100644 index 00000000..d17d90e2 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_mailer/notification.html.erb @@ -0,0 +1,5 @@ +<%== t("body.help_html", scope: "decidim.elections.admin.mailers.trustee_mailer", organization: @organization.name, user_name: @user.name) %> + + diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_backup_modal.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_backup_modal.html.erb new file mode 100644 index 00000000..aa1f6bb8 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_backup_modal.html.erb @@ -0,0 +1,14 @@ +<%= decidim_modal id: "show-backup-modal" do %> +
    +

    <%= t("title", scope: "decidim.elections.trustee_zone.elections.backup_modal", election: translated_attribute(election.title)) %>

    +
    +

    <%= t("description", scope: "decidim.elections.trustee_zone.elections.backup_modal") %>

    +
    +
    + +
    + + <%= t("download_election_keys", scope: "decidim.elections.trustee_zone.elections.backup_modal") %> + +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_key_ceremony_steps.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_key_ceremony_steps.html.erb new file mode 100644 index 00000000..8eef5b66 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_key_ceremony_steps.html.erb @@ -0,0 +1,51 @@ +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_user_profile" do %> + +
    +

    <%= t("title", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps", election: translated_attribute(election.title)) %>

    + +

    <%= t("description", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %>

    + + <%= cell("decidim/announcement", t("process_warning", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps"), callout_class: "alert" ) %> + + + + + + + + + + <% %w(create_election key_ceremony.step_1 key_ceremony.joint_election_key).each do |step| %> + + + + + <% end %> + +
    <%= t("list.task", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %><%= t("list.status", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %>
    <%= t("keys.#{step}", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %>" class="step_status" data-step-status="pending"> + <%= t("status.pending", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %> + <%= t("status.processing", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %> + <%= t("status.completed", scope: "decidim.elections.trustee_zone.elections.key_ceremony_steps") %> +
    + + <%= render("backup_modal") %> + <%= render("restore_modal") %> + +
    + + + +
    +
    + +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_restore_modal.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_restore_modal.html.erb new file mode 100644 index 00000000..ab2549c6 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_restore_modal.html.erb @@ -0,0 +1,15 @@ +<%= decidim_modal id: "show-restore-modal" do %> +
    +

    <%= t("title", scope: "decidim.elections.trustee_zone.elections.restore_modal", election: translated_attribute(election.title)) %>

    +
    +

    <%= t("description", scope: "decidim.elections.trustee_zone.elections.restore_modal") %>

    +
    +
    + +
    + +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_tally_started_steps.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_tally_started_steps.html.erb new file mode 100644 index 00000000..0131068f --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/_tally_started_steps.html.erb @@ -0,0 +1,48 @@ +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_user_profile" do %> + +
    +

    <%= t("title", scope: "decidim.elections.trustee_zone.elections.tally_started_steps", election: translated_attribute(election.title)) %>

    + +

    <%= t("description", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %>

    + + <%= cell("decidim/announcement", t("process_warning", scope: "decidim.elections.trustee_zone.elections.tally_started_steps"), callout_class: "alert" ) %> + + + + + + + + + + <% %w(tally.cast tally.share end_tally).each do |step| %> + + + + + <% end %> + +
    <%= t("list.task", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %><%= t("list.status", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %>
    <%= t("keys.#{step}", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %>" class="step_status" data-step-status="pending"> + <%= t("status.pending", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %> + <%= t("status.processing", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %> + <%= t("status.completed", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %> +
    + + <%= render("restore_modal") %> + +
    + + + <%= link_to trustee_path, id:"back", class: "button button__sm md:button__lg button__transparent-secondary", hidden: true do %> + <%= t("back", scope: "decidim.elections.trustee_zone.elections.tally_started_steps") %> + <% end %> +
    +
    + +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/elections/show.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/show.html.erb new file mode 100644 index 00000000..8a8ff79b --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/show.html.erb @@ -0,0 +1,24 @@ +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<% if current_step %> +
    + +
    +
    + + <%= render("#{current_step}_steps") %> +
    + + <%= render "decidim/elections/shared/broken_promises_modal" %> +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/elections/update.js.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/update.js.erb new file mode 100644 index 00000000..73f0301c --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/elections/update.js.erb @@ -0,0 +1,5 @@ +$(".js-step-result").html( + `
    + <%= j(I18n.t("elections.update.success", scope: "decidim.elections.trustee_zone", status: election.bb_status)) %> +
    ` +) diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_no_public_keys.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_no_public_keys.html.erb new file mode 100644 index 00000000..c78d4ce4 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_no_public_keys.html.erb @@ -0,0 +1,30 @@ +

    <%= t("identification_keys.title", scope: "decidim.elections.trustee_zone.trustees.show") %>

    + +

    <%= t("identification_keys.generate_legend", scope: "decidim.elections.trustee_zone.trustees.show") %>

    + +
      +
    1. <%= t("identification_keys.generate_legend_1", scope: "decidim.elections.trustee_zone.trustees.show") %>
    2. +
    3. <%= t("identification_keys.generate_legend_2", scope: "decidim.elections.trustee_zone.trustees.show") %>
    4. +
    5. <%= t("identification_keys.generate_legend_3", scope: "decidim.elections.trustee_zone.trustees.show") %>
    6. +
    7. <%= t("identification_keys.generate_legend_4", scope: "decidim.elections.trustee_zone.trustees.show") %>
    8. +
    + + + +
    + +
    diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_public_keys.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_public_keys.html.erb new file mode 100644 index 00000000..d4b13513 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_public_keys.html.erb @@ -0,0 +1,17 @@ +
    > +

    <%= t("identification_keys.title", scope: "decidim.elections.trustee_zone.trustees.show") %>

    + +

    <%= t("identification_keys.upload_legend", scope: "decidim.elections.trustee_zone.trustees.show") %>

    + + +
    + +

    <%= t("elections.title", scope: "decidim.elections.trustee_zone.trustees.show") %>

    + +<% if trustee.elections.any? %> + <%= render partial: "table" %> +<% else %> + <%= cell("decidim/announcement", t("elections.no_elections", scope: "decidim.elections.trustee_zone.trustees.show"), callout_class: "warning" ) %> +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_table.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_table.html.erb new file mode 100644 index 00000000..b9920500 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/_table.html.erb @@ -0,0 +1,28 @@ + + + + + + + + + + + <% trustee.elections.each do |election| %> + + + + + + + <% end %> + +
    <%= t("elections.list.election", scope: "decidim.elections.trustee_zone.trustees.show") %><%= t("elections.list.voting_period", scope: "decidim.elections.trustee_zone.trustees.show") %><%= t("elections.list.bb_status", scope: "decidim.elections.trustee_zone.trustees.show") %><%= t("elections.list.action_required.name", scope: "decidim.elections.trustee_zone.trustees.show") %>
    <%= present(election).title %><%= l(election.start_time, format: :decidim_short) %> - <%= l(election.end_time, format: :decidim_short) %><%= election.bb_status %> + <% if election.trustee_action_required? %> + <%= link_to trustee_election_elections_path(election) do %> + <%= t("elections.list.action_required.true", scope: "decidim.elections.trustee_zone.trustees.show") %> + <% end %> + <% else %> + <%= t("elections.list.action_required.false", scope: "decidim.elections.trustee_zone.trustees.show") %> + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/show.html.erb b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/show.html.erb new file mode 100644 index 00000000..d94bd8f6 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/trustee_zone/trustees/show.html.erb @@ -0,0 +1,37 @@ +<%= append_javascript_pack_tag "decidim_elections" %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_user_profile" do %> + + <%= cell("decidim/announcement", { + title: t("not_supported_browser_title", scope: "decidim.elections.trustee_zone.trustees.show"), + body: t("not_supported_browser_description", scope: "decidim.elections.trustee_zone.trustees.show") + }, callout_class: "alert hidden", id: "not_supported_browser") %> + <%= cell("decidim/announcement", { + title: t("safari_warning_title", scope: "decidim.elections.trustee_zone.trustees.show"), + body: t("safari_warning_description", scope: "decidim.elections.trustee_zone.trustees.show") + }, callout_class: "alert hidden", id: "not_supported_safari_browser") %> + + <% + # i18n-tasks-use t('decidim.elections.trustee_zone.trustees.show.trustee_role_description.without_keys') + # i18n-tasks-use t('decidim.elections.trustee_zone.trustees.show.trustee_role_description.with_keys') + %> + <%= cell("decidim/announcement", t(trustee.public_key.blank? ? "without_keys" : "with_keys", scope: "decidim.elections.trustee_zone.trustees.show.trustee_role_description"), callout_class: "success" ) %> + +
    + <%# This hidden form must be shared in both partials %> + <%= decidim_form_for(trustee, html: { hidden: true }) do |f| %> + <%= f.hidden_field :slug %> + <%= f.hidden_field :name %> + <%= f.hidden_field :public_key %> + <%= f.submit "", id: "submit_identification_public_key" %> + <% end %> + + <% if trustee.public_key.blank? %> + <%= render partial: "no_public_keys" %> + <% else %> + <%= render partial: "public_keys" %> + <% end %> +
    + +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/vote_accepted_mailer/notification.html.erb b/decidim-elections/app/views/decidim/elections/vote_accepted_mailer/notification.html.erb new file mode 100644 index 00000000..71bbcf7c --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/vote_accepted_mailer/notification.html.erb @@ -0,0 +1,3 @@ +

    <%= t("votes.accepted_votes.email_subject", scope: "decidim.events.elections", resource_name: @election_title) %>

    +

    <%= raw t("votes.accepted_votes.email_intro", scope: "decidim.events.elections", encrypted_vote_hash: @vote.encrypted_vote_hash, verify_url: @verify_url) %>

    +

    <%= t("votes.accepted_votes.email_outro", scope: "decidim.events.elections", resource_name: @election_title) %>

    diff --git a/decidim-elections/app/views/decidim/elections/votes/_new_ballot_decision_step.html.erb b/decidim-elections/app/views/decidim/elections/votes/_new_ballot_decision_step.html.erb new file mode 100644 index 00000000..213e6a43 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_new_ballot_decision_step.html.erb @@ -0,0 +1,26 @@ +
    +

    + <%= t("decidim.elections.votes.ballot_decision.header") %> +

    +

    <%= t("decidim.elections.votes.ballot_decision.description_html") %>

    +
    + +
    +

    <%= t("decidim.elections.votes.ballot_decision.ballot_hash") %>

    + + +
    + +
    + <%= link_to election_path(election), id: "back", class: "button button__sm md:button__lg button__transparent-secondary" do %> + <%= t("decidim.elections.votes.ballot_decision.back") %> + <% end %> + + <%= decidim_form_for(@form, url: election_votes_path(election), html: { autocomplete: "off" }) do |f| %> + <%= f.hidden_field :encrypted_data %> + <%= f.hidden_field :encrypted_data_hash %> + <%= f.hidden_field :voter_token %> + <%= f.hidden_field :voter_id %> + <%= f.submit t("decidim.elections.votes.ballot_decision.cast"), id: "cast_ballot", class: "button button__sm md:button__lg button__transparent-secondary" %> + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/elections/votes/_new_confirm_step.html.erb b/decidim-elections/app/views/decidim/elections/votes/_new_confirm_step.html.erb new file mode 100644 index 00000000..218f628a --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_new_confirm_step.html.erb @@ -0,0 +1,65 @@ +
    +

    + <%= t("decidim.elections.votes.confirm.header") %> +

    + +

    + <%= t("decidim.elections.votes.confirm.intro").html_safe %> +

    +
    + +<% questions.each_with_index do |question, question_index| %> +
    +

    + <%= t("decidim.elections.votes.new.question_steps", current_step: question_index + 1, total_steps: questions_count) %> +

    + +

    + <%= translated_attribute(question.title) %> +

    + +
    + + <% question.answers.each do |answer| %> + + <%= translated_attribute(answer.title) %> + + <% end %> + <% if question.nota_option? %> + "> + <%= t("decidim.elections.votes.confirm.nota_option") %> + + <% end %> + + + +
    +
    +<% end %> + +
    + <%= button_tag( + t("decidim.elections.votes.voting_step.back"), + type: "button", + class: "button button__sm md:button__lg button__transparent-secondary", + id: "back-step-#{questions_count - 1}", + data: { + toggle: ["step-#{questions_count - 1}", "step-confirm"].join(" ") + } + ) %> + + <%= button_tag( + class: "button button__sm md:button__lg button__secondary", + type: "button", + id: "next-encrypting", + data: { + toggle: ["step-encrypting", "step-confirm"].join(" ") + } + ) do %> + <%= t("decidim.elections.votes.confirm.confirm") %> + <%= icon "arrow-right-line" %> + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/elections/votes/_new_encrypting_step.html.erb b/decidim-elections/app/views/decidim/elections/votes/_new_encrypting_step.html.erb new file mode 100644 index 00000000..6f2baf7e --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_new_encrypting_step.html.erb @@ -0,0 +1,9 @@ +
    +

    + <%= t("decidim.elections.votes.encrypting.header") %> +

    + <%= icon "fingerprint-line", class: "w-40 h-40 text-tertiary animate-pulse fill-current" %> +

    + <%= t("decidim.elections.votes.encrypting.text") %> +

    +
    diff --git a/decidim-elections/app/views/decidim/elections/votes/_new_question.html.erb b/decidim-elections/app/views/decidim/elections/votes/_new_question.html.erb new file mode 100644 index 00000000..56422412 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_new_question.html.erb @@ -0,0 +1,33 @@ +
    + <% ordered_answers(question).each do |answer| %> + <%= label_tag nil, data: { "disabled-by": "check-nota" }, class: "election-question" do %> + <% if question.max_selections == 1 %> + <%= radio_button_tag question.slug, answer.slug, false, class: "answer_input" %> + <% else %> + <%= check_box_tag question.slug, answer.slug, false, class: "answer_input" %> + <% end %> + + <%= translated_attribute(answer.title) %> + + <% if more_information?(answer) %> + + <% end %> + <% end %> + + <%= render("new_question_modal", answer:) if more_information?(answer) %> + <% end %> + + <% if question.nota_option? %> + <%= label_tag nil, nil, class: "election-question" do %> + <% if question.max_selections == 1 %> + <%= radio_button_tag question.slug, "nota_input_#{question.slug}", false, class: "nota_input" %> + <% else %> + <%= check_box_tag question.slug, "nota_input_#{question.slug}", false, { class: "nota_input", id: "check-nota", data: { "disable-check": "" } } %> + <% end %> + + <%= t("decidim.elections.votes.new.nota_option") %> + <% end %> + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/elections/votes/_new_question_modal.html.erb b/decidim-elections/app/views/decidim/elections/votes/_new_question_modal.html.erb new file mode 100644 index 00000000..54e0beb9 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_new_question_modal.html.erb @@ -0,0 +1,43 @@ +<%= decidim_modal id: "show-focus-modal-#{answer.slug}", class: "election-question__modal" do %> +
    + <%= icon "arrow-right-line" %> + +

    + <%= translated_attribute(answer.title) %> +

    + +
    + <% if answer.photos.any? %> +
    + <% answer.photos.each do |photo| %> + <%= image_tag photo.thumbnail_url, alt: strip_tags(translated_attribute(photo.title)) %> + <% end %> +
    + <% end %> + + <% if answer.description %> +
    + <%== translated_attribute(answer.description) %> +
    + <% end %> + + <% if answer.proposals.any? %> +

    <%= t("decidim.elections.votes.modal.proposal_header") %>

    +
      + <% answer.proposals.each do |proposal| %> +
    • + <%= link_to resource_locator(proposal).path do %> + <%== decidim_html_escape(present(proposal).title) %> + <% end %> +
    • + <% end %> +
    + <% end %> +
    +
    +
    + +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/votes/_onboarding_modal.html.erb b/decidim-elections/app/views/decidim/elections/votes/_onboarding_modal.html.erb new file mode 100644 index 00000000..1dc6bc18 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_onboarding_modal.html.erb @@ -0,0 +1,17 @@ +<%= decidim_modal id: "onboarding-modal", data: { "is-open": true } do %> +
    + <%= icon "error-warning-line" %> +

    + <%= t("decidim.elections.votes.onboarding_modal.title") %> +

    +

    <%= t("decidim.elections.votes.onboarding_modal.description") %>

    +
    +
    + + <%= link_to decidim.new_user_registration_path, class: "button button__sm md:button__lg button__secondary" do %> + <%= t("decidim.elections.votes.onboarding_modal.create_account") %> + <% end %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/votes/_show_casted.html.erb b/decidim-elections/app/views/decidim/elections/votes/_show_casted.html.erb new file mode 100644 index 00000000..d8ddf4fd --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_show_casted.html.erb @@ -0,0 +1,34 @@ +
    +

    + <%= t("decidim.elections.votes.confirmed.header") %> +

    +
    + +
    +

    <%= t("decidim.elections.votes.confirmed.lead") %>

    + +

    + <%= t("decidim.elections.votes.confirmed.text") %> +

    + + <%= h(params[:id]) %> + +

    + <%= t("decidim.elections.votes.confirmed.verify_link", link: election_vote_verify_path(election, vote_id: params[:id])).html_safe %> +

    + + <% if params[:token] && valid_questionnaire? %> +

    + <%= t("decidim.elections.votes.confirmed.experience") %> + <%= link_to t("decidim.elections.votes.confirmed.feedback"), election_feedback_path(election, hash: params[:id], token: params[:token]) %> +

    + <% end %> + + <% if !valid_questionnaire? && current_user.nil? %> + <%= render partial: "decidim/elections/votes/onboarding_modal" %> + <% end %> + + <%= link_to :elections, class: "button button__lg button__secondary" do %> + <%= t("decidim.elections.votes.confirmed.back") %> + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/elections/votes/_show_casting.html.erb b/decidim-elections/app/views/decidim/elections/votes/_show_casting.html.erb new file mode 100644 index 00000000..936d1d65 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_show_casting.html.erb @@ -0,0 +1,22 @@ +
    + +
    +

    + <%= t("decidim.elections.votes.casting.header") %> +

    +
    + +
    + <%= icon "fingerprint-line", class: "w-40 h-40 text-tertiary animate-pulse fill-current mx-auto" %> + +

    + <%= t("decidim.elections.votes.casting.text") %> +

    + + <%= form_tag election_vote_path(election, id: vote.encrypted_vote_hash), method: :patch, class: "update_vote_status" %> +
    +
    + +<%= render "decidim/elections/shared/broken_promises_modal" %> diff --git a/decidim-elections/app/views/decidim/elections/votes/_show_failed.html.erb b/decidim-elections/app/views/decidim/elections/votes/_show_failed.html.erb new file mode 100644 index 00000000..2d933c0c --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/_show_failed.html.erb @@ -0,0 +1,26 @@ +
    +

    + <%= t("decidim.elections.votes.failed.header") %> +

    +
    + +
    +

    <%= t("decidim.elections.votes.failed.lead") %>

    + +

    + <%= t("decidim.elections.votes.failed.text") %> +

    + + <%= link_to new_election_vote_path(election), class: "button button__lg button__secondary" do %> + <%= t("decidim.elections.votes.failed.try_again") %> + <% end %> + +

    + <%= t("decidim.elections.votes.confirmed.experience") %> + <% if vote %> + <%= link_to t("decidim.elections.votes.confirmed.feedback"), election_feedback_path(election, hash: vote.encrypted_vote_hash, token: params[:token]) %> + <% else %> + <%= link_to t("decidim.elections.votes.confirmed.feedback"), election_feedback_path(election) %> + <% end %> +

    +
    diff --git a/decidim-elections/app/views/decidim/elections/votes/new.html.erb b/decidim-elections/app/views/decidim/elections/votes/new.html.erb new file mode 100644 index 00000000..07af71d8 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/new.html.erb @@ -0,0 +1,94 @@ +<%= append_stylesheet_pack_tag "decidim_elections" %> +<%= append_javascript_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> +
    + data-ballot-style-id="<%== ballot_style_id %>" + <% end %>> + +
    +
    +

    + <%= t("vote_for", scope: "decidim.elections.votes.header", title: translated_attribute(election.title) ) %> +

    +
    + +
      + <% wizard_steps.each_with_index do |wizard_step, i| %> +
    1. ><%= t(wizard_step, scope: "decidim.elections.votes.header") %>
    2. + <% end %> +
    +
    + + <%= cell("decidim/announcement", t("decidim.elections.votes.new.preview_alert"), callout_class: "warning mb-4") if preview_mode? %> + <%= cell("decidim/announcement", { + title: t("safari_warning_title", scope: "decidim.elections.trustee_zone.trustees.show"), + body: t("safari_warning_description", scope: "decidim.elections.trustee_zone.trustees.show") + }, callout_class: "alert hidden", id: "not_supported_safari_browser") %> + + <% questions.each_with_index do |step_question, step_index| %> +
    > + +
    +

    + <%= t("decidim.elections.votes.new.question_steps", current_step: step_index + 1, total_steps: questions_count) %> +

    + +

    + <%= translated_attribute(step_question.title) %> +

    + + <% if step_question.max_selections > 1 %> +
    +

    + <%= t("decidim.elections.votes.new.answer_choices", choices: step_question.max_selections) %> +

    + + + <%= t("votes.new.selections", scope:"decidim.elections", selected: "", max_selections: step_question.max_selections ).html_safe %> + +
    + <% end %> +
    + + <%= render( + "new_question", + question: step_question + ) %> + + <%= cell( + "decidim/elections/voting_step_navigation", + step_index, + total_steps: questions_count + ) %> + +
    + <% end %> + + + + + + + + +
    + + <%= render "decidim/elections/shared/broken_promises_modal" %> +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/votes/show.html.erb b/decidim-elections/app/views/decidim/elections/votes/show.html.erb new file mode 100644 index 00000000..35d75006 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/show.html.erb @@ -0,0 +1,12 @@ +<%= append_stylesheet_pack_tag "decidim_elections" %> +<%= append_javascript_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> + <% if vote&.pending? %> + <%= render("show_casting") %> + <% elsif vote&.accepted? || preview_mode? %> + <%= render("show_casted") %> + <% else %> + <%= render("show_failed") %> + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/elections/votes/verify.html.erb b/decidim-elections/app/views/decidim/elections/votes/verify.html.erb new file mode 100644 index 00000000..93423622 --- /dev/null +++ b/decidim-elections/app/views/decidim/elections/votes/verify.html.erb @@ -0,0 +1,57 @@ +<%= append_stylesheet_pack_tag "decidim_elections" %> +<%= append_javascript_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> + +
    +

    + <%= t("decidim.elections.votes.verify.header.title") %>: <%= translated_attribute(election.title) %> +

    +
    + +
    + +

    + <%= t("decidim.elections.votes.verify.content.heading") %> +

    + +

    + <%= t("decidim.elections.votes.verify.content.info") %> +

    + + + + + +
    +
    + +
    + +
    + <%= link_to election_path(election), class: "button button__sm md:button__lg button__transparent-secondary" do %> + <%= t("decidim.elections.votes.verify.form.back") %> + <% end %> + + +
    +
    + +
    + +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/access_code_mailer/send_access_code.html.erb b/decidim-elections/app/views/decidim/votings/access_code_mailer/send_access_code.html.erb new file mode 100644 index 00000000..738d01ca --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/access_code_mailer/send_access_code.html.erb @@ -0,0 +1,5 @@ + + + diff --git a/decidim-elections/app/views/decidim/votings/admin/ballot_styles/_form.html.erb b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/_form.html.erb new file mode 100644 index 00000000..5c3595b2 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/_form.html.erb @@ -0,0 +1,24 @@ +
    +
    +
    +
    + <%= form.text_field :code, autofocus: true, help_text: t(".code_help") %> +
    + +
    + <%= form.label :question_ids, t(".questions") %> +

    <%= t(".questions_help") %>

    +
    + + <% voting_elections.each do |election| %> +
    + <%= "#{t(".election")}: #{translated_attribute(election.title)}" %> + + <% election.questions.each do |question| %> + <%= form.check_box :question_ids, { multiple: true, label: translated_attribute(question.title), label_options: { for: nil } }, question.id, nil %> + <% end %> +
    + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/ballot_styles/edit.html.erb b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/edit.html.erb new file mode 100644 index 00000000..83399d68 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/edit.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for([current_voting, @form], url: { action: "update" }, html: { class: "form-defaults form edit_ballot_style" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".update"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/ballot_styles/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/index.html.erb new file mode 100644 index 00000000..6e1b7031 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/index.html.erb @@ -0,0 +1,65 @@ +<% add_decidim_page_title(t(".title")) %> + +<%= cell("decidim/announcement", t("explanation_callout", scope: "decidim.votings.admin.ballot_styles.index"), callout_class: "warning" ) %> +
    +
    +

    + <%= t(".title") %> + <%= link_to t("actions.new", scope: "decidim.votings.admin.ballot_styles.index"), new_voting_ballot_style_path(current_voting), class: "button button__sm button__secondary" if allowed_to? :create, :ballot_style, voting: current_voting %> +

    +
    +
    + + + + + <% voting_elections.each do |election| %> + + <% end %> + + + + + <% voting_elections.each do |election| %> + <% election.questions.each do |question| %> + + <% end %> + <% end %> + + + + <% ballot_styles.each do |ballot_style| %> + + + <% voting_elections.each do |election| %> + <% election.questions.each do |question| %> + <% icon_name, icon_style = ballot_style.questions.include?(question) ? ["check-line", "ballot-style__question--checked"] : ["close-circle-line", "ballot-style__question--unchecked"] %> + + <% end %> + <% end %> + + + + <% end %> + +
    <%= t("code", scope: "decidim.votings.admin.models.ballot_style.fields") %><%= translated_attribute(election.title) %><%= t("associated_census_data", scope: "decidim.votings.admin.ballot_styles.index") %><%= t("actions.title", scope: "decidim.votings.admin.ballot_styles.index") %>
    <%= translated_attribute(question.title) %>
    + <%= ballot_style.code %> + <%= icon icon_name, class: icon_style %> + <%= ballot_style.census_data.count %> + + <% if allowed_to? :update, :ballot_style, ballot_style: ballot_style, voting: current_voting %> + <%= icon_link_to "pencil-line", edit_voting_ballot_style_path(current_voting, ballot_style), t("actions.edit", scope: "decidim.votings.admin.ballot_styles.index"), class: "action-icon--edit" %> + <% else %> + <%= icon "pencil-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.edit", scope: "decidim.votings.admin.ballot_styles.index") %> + <% end %> + + <% if allowed_to? :delete, :ballot_style, ballot_style: ballot_style, voting: current_voting %> + <%= icon_link_to "delete-bin-line", voting_ballot_style_path(current_voting, ballot_style), t("actions.destroy", scope: "decidim.votings.admin.ballot_styles.index"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.votings.admin.ballot_styles.index") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.votings.admin.ballot_styles.index") %> + <% end %> +
    +
    +
    + +<%= append_stylesheet_pack_tag "decidim_votings_admin" %> diff --git a/decidim-elections/app/views/decidim/votings/admin/ballot_styles/new.html.erb b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/new.html.erb new file mode 100644 index 00000000..12739e61 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/ballot_styles/new.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for([current_participatory_space, @form], html: { class: "form-defaults form new_ballot_style" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/_results.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/_results.html.erb new file mode 100644 index 00000000..c2d1618a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/_results.html.erb @@ -0,0 +1,50 @@ + +
    +
    +

    + <%= t(".title", election_title: translated_attribute(election.title)).html_safe %> +

    +
    +
    + + + + + + + + + + + <% groups.each do |group| %> + + <% if group[:question].present? %> + + + + + <% end %> + <% group[:results].each do |result| %> + "> + + + + + + + <% end %> + + <% end %> +
    <%= t(".election_totals") %><%= t(".polling_stations") %><%= t(".bulletin_board") %><%= t(".totals") %>
    <%= translated_attribute(group[:question].title) %>
    + <% if result[:answer].present? %> + <%= translated_attribute(result[:answer].title) %> + <% else %> + <%= t(".result_types.#{result[:result_type]}") %> + <% end %> + <%= result[:polling_station] %><%= result[:bulletin_board] %><%= result[:value] %> + <% if result[:answer]&.selected %> + <%= t(".selected") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/index.html.erb new file mode 100644 index 00000000..1d6c9752 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/index.html.erb @@ -0,0 +1,30 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> +

    +
    +
    + + + + + + + + + <% elections.finished.each do |election| %> + + + + + <% end %> + +
    <%= t("title", scope: "decidim.elections.admin.elections.index") %><%= t("actions.title", scope: "decidim.votings.admin.monitoring_committee_election_results") %>
    + <%= link_to translated_attribute(election.title), voting_monitoring_committee_election_result_path(current_voting, election) %> + + <%= icon_link_to "eye-line", voting_monitoring_committee_election_result_path(current_voting, election), t("actions.view", scope: "decidim.votings.admin.monitoring_committee_election_results"), class: "action-icon--view", target: "_blank", data: { "external-link": false } %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/show.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/show.html.erb new file mode 100644 index 00000000..14d1abb3 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_election_results/show.html.erb @@ -0,0 +1,24 @@ +<% unless elections.one? %> + <%= link_to voting_monitoring_committee_election_results_path(current_voting) do %> + <%= icon "arrow-left-s-fill", class: "icon--small", role: "img" %> <%= t(".change_election") %> + <% end %> +<% end %> + +<%= render partial: "results", locals: { groups: publish_results_form.groups } %> + +<%= decidim_form_for publish_results_form, url: voting_monitoring_committee_election_result_path(current_voting, election), method: :put, html: { class: "form step" } do |f| %> + <% if publish_results_form.pending_action %> +
    + + <%= append_javascript_pack_tag "decidim_elections_admin" %> + <%= t(".publishing") %> + <% else %> +
    + <%= f.submit t(".publish_results"), class: "button button__sm button__secondary" %> +
    + <% end %> +<% end %> + +<%= append_stylesheet_pack_tag "decidim_votings_admin" %> diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/_form.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/_form.html.erb new file mode 100644 index 00000000..5beb9ce5 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/_form.html.erb @@ -0,0 +1,26 @@ +
    +
    +
    +
    + <%= form.select :existing_user, [[t(".non_user"), false], [t(".existing_user"), true]], label: t(".user_type") %> +
    + +
    + <% prompt_options = { url: decidim_admin.users_organization_url, placeholder: t(".select_user") } %> + <%= form.autocomplete_select(:user_id, form.object.user.presence, { multiple: false }, prompt_options) do |user| + { value: user.id, label: "#{user.name} (@#{user.nickname})" } + end %> +
    + + + +
    + <%= form.text_field :name %> +
    +
    +
    +
    + +<%= append_javascript_pack_tag "decidim_votings_admin" %> diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/index.html.erb new file mode 100644 index 00000000..d04d030a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/index.html.erb @@ -0,0 +1,39 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> + <%= link_to t("actions.new", scope: "decidim.votings.monitoring_committee_members"), new_voting_monitoring_committee_member_path(current_voting), class: "button button__sm button__secondary" if allowed_to? :create, :monitoring_committee_member, voting: current_voting %> +

    +
    +
    + + + + + + + + + + <% monitoring_committee_members.each do |monitoring_committee_member| %> + + + + + + <% end %> + +
    <%= t("models.monitoring_committee_member.fields.name", scope: "decidim.votings.admin") %><%= t("models.monitoring_committee_member.fields.email", scope: "decidim.votings.admin") %><%= t("actions.title", scope: "decidim.votings.monitoring_committee_members") %>
    + <%= monitoring_committee_member.user.name %> + + <%= monitoring_committee_member.user.email %> + + <% if allowed_to? :delete, :monitoring_committee_member, voting: current_voting, monitoring_committee_member: monitoring_committee_member %> + <%= icon_link_to "delete-bin-line", voting_monitoring_committee_member_path(current_voting, monitoring_committee_member), t("actions.destroy", scope: "decidim.votings.monitoring_committee_members"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.votings.monitoring_committee_members") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.votings.monitoring_committee_members") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/new.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/new.html.erb new file mode 100644 index 00000000..ab4e54ee --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_members/new.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for([current_voting, @form], url: voting_monitoring_committee_members_path(current_voting), html: { class: "form-defaults form new_monitoring_committee_member" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closure_certificate_results.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closure_certificate_results.erb new file mode 100644 index 00000000..2a2566ba --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closure_certificate_results.erb @@ -0,0 +1,9 @@ +
    + <%= cell("decidim/votings/polling_station_closure_certificate", closure) %> + +
    + <%= cell("decidim/votings/polling_station_closure_recount", closure) %> +
    +
    + +<%= append_stylesheet_pack_tag "decidim_votings_admin" %> diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closures.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closures.html.erb new file mode 100644 index 00000000..00ae4621 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_closures.html.erb @@ -0,0 +1,68 @@ +<% unless elections.one? %> + <%= link_to voting_monitoring_committee_polling_station_closures_path(current_voting) do %> + <%= icon "arrow-left-s-fill", class: "icon--small", role: "img" %> <%= t(".change_election") %> + <% end %> +<% end %> + +
    +
    +

    + <%= t(".title", election_title: translated_attribute(election.title)).html_safe %> +

    +
    + + <%= admin_filter_selector %> +
    + + + + + + + + + + + + + <% filtered_polling_stations.each do |polling_station| %> + <% + closure = polling_station.closure_for(election) + sign_icon_name, sign_icon_style = closure&.signed? ? ["check-line", "polling_station__closure--present"] : ["close-line", "polling_station__closure--not_present"] + validated_icon_name, validated_icon_style = closure&.validated? ? ["check-line", "polling_station__closure--validated"] : ["close-line", "polling_station__closure--not_validated"] + %> + > + + + + + + + + <% end %> + +
    <%= t("models.polling_station.fields.title", scope: "decidim.votings.admin") %><%= t("models.polling_station.fields.address", scope: "decidim.votings.admin") %><%= t("models.polling_station.fields.polling_station_president", scope: "decidim.votings.admin") %><%= t(".signed") %><%= t(".validated") %><%= t("actions.title", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures") %>
    + <%= translated_attribute(polling_station.title) %> + + <%= polling_station.address %> + + <%= polling_station.polling_station_president&.name %> + + <%= icon sign_icon_name, class: sign_icon_style %> + + <%= icon validated_icon_name, class: validated_icon_style %> + + <% if closure.present? && closure.signed? %> + <% if closure.validated? && allowed_to?(:read, :monitoring_committee_polling_station_closure, voting: current_voting, closure: closure) %> + <%= icon_link_to "eye-line", voting_monitoring_committee_polling_station_closure_path(current_voting, closure), t("actions.view", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures"), class: "action-icon--view", target: "_blank", data: { "external-link": false } %> + <% elsif closure.signed? && allowed_to?(:validate, :monitoring_committee_polling_station_closure, voting: current_voting, closure: closure) %> + <%= icon_link_to "check-line", edit_voting_monitoring_committee_polling_station_closure_path(current_voting, closure), t("actions.validate", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures"), class: "action-icon--validate" %> + <% else %> + <%= icon "check-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.validate", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures") %> + <% end %> + <% else %> + <%= icon "eye-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.view", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_elections.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_elections.html.erb new file mode 100644 index 00000000..b05691a3 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/_elections.html.erb @@ -0,0 +1,29 @@ +
    +
    +

    + <%= t(".title") %> +

    +
    +
    + + + + + + + + + <% elections.finished.each do |election| %> + + + + + <% end %> + +
    <%= t("title", scope: "decidim.elections.admin.elections.index") %><%= t("actions.title", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures") %>
    + <%= link_to translated_attribute(election.title), voting_monitoring_committee_polling_station_closures_path(current_voting, election_id: election.id) %> + + <%= icon_link_to "eye-line", voting_monitoring_committee_polling_station_closures_path(current_voting, election_id: election.id), t("actions.view", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures"), class: "action-icon--edit", target: "_blank", data: { "external-link": false } %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/edit.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/edit.html.erb new file mode 100644 index 00000000..c1a867a8 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/edit.html.erb @@ -0,0 +1,30 @@ +<% add_decidim_page_title(t(".title", polling_station_title: translated_attribute(closure.polling_station.title), election_title: translated_attribute(closure.election.title))) %> +<%= link_to voting_monitoring_committee_polling_station_closures_path(current_voting, election_id: closure.election.id) do %> + <%= icon "arrow-left-s-fill", class: "icon--small", role: "img" %> <%= t(".change_polling_station") %> +<% end %> + +
    +
    +

    + <%= t(".title", polling_station_title: translated_attribute(closure.polling_station.title), election_title: translated_attribute(closure.election.title)).html_safe %> +

    +
    + + <%= render partial: "closure_certificate_results" %> + +
    + <%= decidim_form_for @form, url: validate_voting_monitoring_committee_polling_station_closure_path(current_voting, closure), method: :post do |form| %> + <%= content_tag :div, + form.text_area( + :monitoring_committee_notes, + rows: 5, + label: t(".monitoring_committee_notes"), + placeholder: t(".monitoring_committee_notes_placeholder") + ) %> + +
    + <%= form.submit t("actions.validate", scope: "decidim.votings.admin.monitoring_committee_polling_station_closures"), class: "button button__sm button__secondary" %> +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/index.html.erb new file mode 100644 index 00000000..56d5f66b --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/index.html.erb @@ -0,0 +1,5 @@ +<% if election.present? %> + <%= render partial: "closures" %> +<% else %> + <%= render partial: "elections" %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/show.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/show.html.erb new file mode 100644 index 00000000..6526f5de --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_polling_station_closures/show.html.erb @@ -0,0 +1,10 @@ +<%= link_to voting_monitoring_committee_polling_station_closures_path(current_voting, election_id: closure.election.id) do %> + <%= icon "arrow-left-s-fill", class: "icon--small", role: "img" %> <%= t(".change_polling_station") %> +<% end %> + +<%= render partial: "closure_certificate_results" %> + +
    + <%= content_tag :strong, t(".monitoring_committee_notes") %>: + <%= closure.monitoring_committee_notes %> +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_verify_elections/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_verify_elections/index.html.erb new file mode 100644 index 00000000..44b38321 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/monitoring_committee_verify_elections/index.html.erb @@ -0,0 +1,57 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> +

    +
    +
    + + + + + + + + + + <% elections.each do |election| %> + + + + + + <% end %> + +
    <%= t("models.election.fields.title", scope: "decidim.elections") %><%= t("models.election.fields.verifiable_results_file_url", scope: "decidim.elections") %><%= t("models.election.fields.verifiable_results_file_hash", scope: "decidim.elections") %>
    + <%= translated_attribute(election.title) %> + + <% if election.verifiable_results_file_url.present? %> + <%= link_to t(".download"), election.verifiable_results_file_url %> + <% else %> + <%= t(".not_available") %> + <% end %> + + <% if election.verifiable_results_file_hash.present? %> + <%= election.verifiable_results_file_hash %> + <% else %> + <%= t(".not_available") %> + <% end %> +
    +
    +
    + +
    +
    +

    + <%= t(".how_to_title") %> +

    +
    +
    + <%= content_tag :p, sanitize(t(".how_to_download")) %> + <%= content_tag :p, sanitize(t(".how_to_checksum")) %> +
    sha256sum <verifiable_election_file_path>
    + <%= content_tag :p, sanitize(t(".how_to_run_verifier")) %> +
    bin/verify <verifiable_election_file_path>
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_officers/_form.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_officers/_form.html.erb new file mode 100644 index 00000000..ead9070f --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_officers/_form.html.erb @@ -0,0 +1,26 @@ +
    +
    +
    +
    + <%= form.select :existing_user, [[t(".non_user"), false], [t(".existing_user"), true]], label: t(".user_type") %> +
    + +
    + <% prompt_options = { url: decidim_admin.users_organization_url, placeholder: t(".select_user") } %> + <%= form.autocomplete_select(:user_id, form.object.user.presence, { multiple: false }, prompt_options) do |user| + { value: user.id, label: "#{user.name} (@#{user.nickname})" } + end %> +
    + + + +
    + <%= form.text_field :name %> +
    +
    +
    +
    + +<%= append_javascript_pack_tag "decidim_votings_admin" %> diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_officers/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_officers/index.html.erb new file mode 100644 index 00000000..5a0d6dae --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_officers/index.html.erb @@ -0,0 +1,49 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> + <%= link_to t("actions.new", scope: "decidim.votings.polling_officers"), new_voting_polling_officer_path(current_voting), class: "button button__sm button__secondary" if allowed_to? :create, :polling_officer, voting: current_voting %> +

    +
    + + <%= admin_filter_selector %> +
    + + + + + + + + + + + <% filtered_polling_officers.each do |polling_officer| %> + + + + + + + <% end %> + +
    <%= t("models.polling_officer.fields.name", scope: "decidim.votings.admin") %><%= t("models.polling_officer.fields.email", scope: "decidim.votings.admin") %><%= t("models.polling_officer.fields.polling_station", scope: "decidim.votings.admin") %><%= t("actions.title", scope: "decidim.votings.polling_officers") %>
    + <%= polling_officer.name %> + + <%= polling_officer.email %> + + <% if polling_officer.presided_polling_station.present? %> + <%= link_to "#{translated_attribute(polling_officer.presided_polling_station.title)} (#{t(".role_president")})", edit_voting_polling_station_path(current_voting, polling_officer.presided_polling_station) %> + <% elsif polling_officer.managed_polling_station.present? %> + <%= link_to "#{translated_attribute(polling_officer.managed_polling_station.title)} (#{t(".role_manager")})", edit_voting_polling_station_path(current_voting, polling_officer.managed_polling_station) %> + <% end %> + + <% if allowed_to? :delete, :polling_officer, voting: current_voting, polling_officer: polling_officer %> + <%= icon_link_to "delete-bin-line", voting_polling_officer_path(current_voting, polling_officer), t("actions.destroy", scope: "decidim.votings.polling_officers"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.votings.polling_officers") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.votings.polling_officers") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_officers/new.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_officers/new.html.erb new file mode 100644 index 00000000..27b91994 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_officers/new.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for([current_voting, @form], url: voting_polling_officers_path, html: { class: "form-defaults form new_polling_officer" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_stations/_form.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_stations/_form.html.erb new file mode 100644 index 00000000..26706531 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_stations/_form.html.erb @@ -0,0 +1,34 @@ +
    +
    +
    +
    + <%= form.translated :text_field, :title, autofocus: true, class: "js-hashtags", hashtaggable: true, aria: { label: :title } %> +
    + +
    + <%= form.geocoding_field :address, help_text: t(".address_help") %> +
    + +
    + <%= form.translated :text_area, :location, help_text: t(".location_help") %> +
    + +
    + <%= form.translated :text_area, :location_hints, help_text: t(".location_hints_help") %> +
    + +
    + <% prompt_options = { url: available_polling_officers_voting_url(current_voting), placeholder: t(".select_president"), help_text: t(".polling_station_president_help") } %> + <%= form.autocomplete_select(:polling_station_president_id, form.object.polling_station_president.presence, { multiple: false }, prompt_options) do |polling_officer| + { value: polling_officer.id, label: "#{polling_officer.name} (@#{polling_officer.nickname})" } + end %> +
    + +
    + <%= render partial: "decidim/votings/votings/polling_officers_picker", locals: { form:, current_component: current_voting, field: :polling_station_managers } %> +
    +
    +
    +
    + +<%= append_javascript_pack_tag "decidim_votings_admin" %> diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_stations/edit.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_stations/edit.html.erb new file mode 100644 index 00000000..8724a8f7 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_stations/edit.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for([current_voting, @form], url: { action: "update" }, html: { class: "form-defaults form edit_polling_station" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".update"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_stations/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_stations/index.html.erb new file mode 100644 index 00000000..f2441cc6 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_stations/index.html.erb @@ -0,0 +1,54 @@ +<% add_decidim_page_title(t(".title")) %> +
    +
    +

    + <%= t(".title") %> + <%= link_to t("actions.new", scope: "decidim.votings.polling_stations"), new_voting_polling_station_path(current_voting), class: "button button__sm button__secondary" if allowed_to? :create, :polling_station, voting: current_voting %> +

    +
    + <%= admin_filter_selector %> +
    + + + + + + + + + + + + <% filtered_polling_stations.each do |polling_station| %> + + + + + + + + <% end %> + +
    <%= t("models.polling_station.fields.title", scope: "decidim.votings.admin") %><%= t("models.polling_station.fields.address", scope: "decidim.votings.admin") %><%= t("models.polling_station.fields.polling_station_president", scope: "decidim.votings.admin") %><%= t("models.polling_station.fields.polling_station_managers", scope: "decidim.votings.admin") %><%= t("actions.title", scope: "decidim.votings.polling_stations") %>
    + <%= translated_attribute(polling_station.title) %> + + <%= polling_station.address %> + + <%= polling_station.polling_station_president&.name %> + + <%= safe_join(polling_station.polling_station_managers.map(&:name), ", ") %> + + <% if allowed_to? :update, :polling_station, voting: current_voting, polling_station: polling_station %> + <%= icon_link_to "pencil-line", edit_voting_polling_station_path(current_voting, polling_station), t("actions.edit", scope: "decidim.votings.polling_stations"), class: "action-icon--edit" %> + <% else %> + <%= icon "pencil-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.edit", scope: "decidim.votings.polling_stations") %> + <% end %> + + <% if allowed_to? :delete, :polling_station, voting: current_voting, polling_station: polling_station %> + <%= icon_link_to "delete-bin-line", voting_polling_station_path(current_voting, polling_station), t("actions.destroy", scope: "decidim.votings.polling_stations"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.votings.polling_stations") } %> + <% else %> + <%= icon "delete-bin-line", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.votings.polling_stations") %> + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/polling_stations/new.html.erb b/decidim-elections/app/views/decidim/votings/admin/polling_stations/new.html.erb new file mode 100644 index 00000000..900c52f1 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/polling_stations/new.html.erb @@ -0,0 +1,18 @@ +<% add_decidim_page_title(t(".title")) %> +
    +

    + <%= t(".title") %> +

    +
    +
    +
    + <%= decidim_form_for([current_voting, @form], html: { class: "form-defaults form new_polling_station" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t(".create"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/votings/_form.html.erb b/decidim-elections/app/views/decidim/votings/admin/votings/_form.html.erb new file mode 100644 index 00000000..c71064f7 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/votings/_form.html.erb @@ -0,0 +1,76 @@ +
    +
    +
    + +
    + +
    +
    + <%= form.translated :text_field, :title, autofocus: true, aria: { label: :title } %> +
    + +
    + <%= form.translated :editor, :description, aria: { label: :description } %> +
    + +
    +
    + <%= form.datetime_field :start_time %> +
    + +
    + <%= form.datetime_field :end_time %> +
    +
    + +
    + <%= form.select :voting_type, + @form.options_for_voting_type_select, + { include_blank: t(".select_a_voting_type"), label: t(".voting_type_label") }, + { multiple: false } %> +
    + +
    + <%= scopes_select_field form, :scope_id, root: nil %> +
    + +
    +
    + <%= form.text_field :slug, label: t(".slug"), help_text: t(".slug_help_html", url: decidim_form_slug_url(:votings, form.object.slug)) %> +
    +
    + +
    +
    + <%= form.check_box :show_check_census, help_text: t(".show_check_census_help") %> +
    +
    + +
    +
    + <%= form.text_field :census_contact_information, label: t(".census_contact_information"), help_text: t(".census_contact_information_help") %> +
    +
    + +
    + <%= form.check_box :promoted, label: t(".promoted") %> +
    + +
    +
    + <%= form.upload :banner_image, label: t(".banner_image"), button_class: "button button__sm button__transparent-secondary" %> +
    + +
    + <%= form.upload :introductory_image, label: t(".introductory_image"), button_class: "button button__sm button__transparent-secondary" %> +
    +
    +
    +
    + +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/votings/edit.html.erb b/decidim-elections/app/views/decidim/votings/admin/votings/edit.html.erb new file mode 100644 index 00000000..3b11c64e --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/votings/edit.html.erb @@ -0,0 +1,39 @@ +<% add_decidim_page_title(t("info", scope: "decidim.votings.admin.menu.votings_submenu")) %> +<% add_decidim_page_title(translated_attribute(current_participatory_space.title)) %> +
    +

    + <%= t("info", scope: "decidim.votings.admin.menu.votings_submenu") %> +

    +
    + +
    +
    + <%= decidim_form_for @form, + method: :patch, + url: voting_path(@current_voting), + html: { class: "form form-defaults edit_voting" } do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t("votings.edit.update", scope: "decidim.votings.admin"), class: "button button__sm button__secondary", disabled: !allowed_to?(:update, :voting, voting: current_voting) %> + + <% if allowed_to? :publish, :voting, voting: @current_voting %> + <% if @current_voting.published? %> + <%= link_to t("actions.unpublish", scope: "decidim.admin"), voting_publish_path(@current_voting), method: :delete, class: "button button__sm button__secondary" %> + <% else %> + <%= link_to t("actions.publish", scope: "decidim.admin"), voting_publish_path(@current_voting), method: :post, class: "button button__sm button__secondary hollow" %> + <% end %> + <% end %> + + <% if allowed_to? :destroy, :voting, voting: @current_voting %> + <%= link_to t("votings.actions.destroy", scope: "decidim.votings.admin"), + @current_voting, + method: :delete, + class: "button button__sm button__secondary", + data: { confirm: t("votings.actions.confirm_destroy", scope: "decidim.votings.admin") } %> + <% end %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/votings/index.html.erb b/decidim-elections/app/views/decidim/votings/admin/votings/index.html.erb new file mode 100644 index 00000000..a3efdf2a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/votings/index.html.erb @@ -0,0 +1,65 @@ +<% add_decidim_page_title(t("decidim.votings.admin.titles.votings")) %> +
    + <%= admin_filter_selector %> +
    + + + + + + + + + + + <% votings.each do |voting| %> + <% next unless allowed_to? :list, :voting, voting: voting %> + + + + + + + + <% end %> + +
    <%= t("models.voting.fields.title", scope: "decidim.votings.admin") %><%= sort_link(query, :created_at, t("models.voting.fields.created_at", scope: "decidim.votings.admin"), default_order: :desc) %> + <%= t("models.voting.fields.published", scope: "decidim.votings.admin") %> +
    + <% if voting.promoted? %> + <%= icon_with_tooltip "star-s-fill", t("models.assembly.fields.promoted", scope: "decidim.admin") %> + <% end %> + <% if allowed_to? :edit, :voting, voting: voting %> + <%= link_to translated_attribute(voting.title), edit_voting_path(voting) %> +
    + <% else %> + <%= translated_attribute(voting.title) %> + <% end %> +
    + <%= l voting.created_at, format: :short %> + + <% if voting.published? %> + + <%= t("index.published", scope: "decidim.votings.admin") %> + + <% else %> + + <%= t("index.unpublished", scope: "decidim.votings.admin") %> + + <% end %> + + <% if allowed_to? :edit, :voting, voting: voting %> + <%= icon_link_to "pencil-line", edit_voting_path(voting), t("actions.configure", scope: "decidim.admin"), class: "action-icon--edit" %> + <% else %> + + <% end %> + + <% if allowed_to? :preview, :voting, voting: voting %> + <%= icon_link_to "eye-line", decidim_votings.voting_path(voting), t("actions.preview", scope: "decidim.admin"), class: "action-icon--preview", target: "_blank", data: { "external-link": false } %> + <% else %> + + <% end %> +
    +
    + <%= decidim_paginate votings %> +
    diff --git a/decidim-elections/app/views/decidim/votings/admin/votings/new.html.erb b/decidim-elections/app/views/decidim/votings/admin/votings/new.html.erb new file mode 100644 index 00000000..4037fd36 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/admin/votings/new.html.erb @@ -0,0 +1,19 @@ +<% add_decidim_page_title(t("votings.new.title", scope: "decidim.votings.admin")) %> +
    +

    + <%= t("votings.new.title", scope: "decidim.votings.admin") %> +

    +
    + +
    +
    + <%= decidim_form_for(@form, html: { class: "form new_voting form-defaults" }) do |f| %> + <%= render partial: "form", object: f %> +
    +
    + <%= f.submit t("votings.new.create", scope: "decidim.votings.admin"), class: "button button__sm button__secondary" %> +
    +
    + <% end %> +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_creating_data.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_creating_data.html.erb new file mode 100644 index 00000000..2293ce81 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_creating_data.html.erb @@ -0,0 +1,10 @@ +
    + <% info_message = "   + #{t("creating_data.info_message", + scope: "decidim.votings.census.admin.census", + processed_count: current_census.csv_row_processed_count, + raw_count: current_census.csv_row_raw_count, + file: current_census.filename)}" %> + + <%= cell("decidim/announcement", info_message, callout_class: "warning") %> +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_export_codes.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_export_codes.html.erb new file mode 100644 index 00000000..26421e5b --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_export_codes.html.erb @@ -0,0 +1,8 @@ +
    + <%= cell("decidim/announcement", t("export_access_codes.callout", scope: "decidim.votings.census.admin.census", email: user_email), callout_class: "warning" ) %> + + <%= link_to t("export_access_codes.button", scope: "decidim.votings.census.admin.census"), + export_access_codes_path, + class: "button button__sm button__secondary", + data: { confirm: t("export_access_codes.confirm", scope: "decidim.votings.census.admin.census", email: user_email) } %> +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_exporting_codes.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_exporting_codes.html.erb new file mode 100644 index 00000000..c6677e60 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_exporting_codes.html.erb @@ -0,0 +1,3 @@ +
    + <%= cell("decidim/announcement", "  #{t("exporting_access_codes.info_message", scope: "decidim.votings.census.admin.census", email: user_email)}", callout_class: "warning") %> +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_freeze.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_freeze.html.erb new file mode 100644 index 00000000..4091d2be --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_freeze.html.erb @@ -0,0 +1,5 @@ +
    + <%= cell("decidim/announcement", t("freeze.callout", scope: "decidim.votings.census.admin.census", email: user_email), callout_class: "warning" ) %> + +

    <%= t("freeze.help_html", scope: "decidim.votings.census.admin.census") %>

    +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_generate_codes.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_generate_codes.html.erb new file mode 100644 index 00000000..e7417de1 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_generate_codes.html.erb @@ -0,0 +1,35 @@ +<% if current_census.csv_row_raw_count != current_census.data.size %> + <% info_message = t("generate_access_codes.info_message_warn", + scope: "decidim.votings.census.admin.census", + data_count: current_census.data.size, + raw_count: current_census.csv_row_raw_count, + file: current_census.filename) %> + <% callout_class = "warning" %> +<% else %> + <% info_message = t("generate_access_codes.info_message_all", + scope: "decidim.votings.census.admin.census", + data_count: current_census.data.size, + raw_count: current_census.csv_row_raw_count, + file: current_census.filename) %> + <% callout_class = "success" %> +<% end %> + +
    + <%= cell("decidim/announcement", info_message, callout_class: ) %> + + <% if current_census.data.size.zero? %> + <%= cell("decidim/announcement", t("generate_access_codes.start_over", scope: "decidim.votings.census.admin.census"), callout_class: "warning" ) %> + <%= link_to t("delete.button", scope: "decidim.votings.census.admin.census"), + admin_voting_census_path, + class: "button button__sm button__secondary alert destroy", + method: :delete, + data: { confirm: t("delete.confirm", scope: "decidim.votings.census.admin.census") } %> + + <% else %> + <%= cell("decidim/announcement", t("generate_access_codes.callout", scope: "decidim.votings.census.admin.census"), callout_class: "warning" ) %> + <%= link_to t("generate_access_codes.button", scope: "decidim.votings.census.admin.census"), + generate_access_codes_path, + class: "button button__sm button__secondary", + data: { confirm: t("generate_access_codes.confirm", scope: "decidim.votings.census.admin.census") } %> + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_generating_codes.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_generating_codes.html.erb new file mode 100644 index 00000000..46b5c338 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_generating_codes.html.erb @@ -0,0 +1,3 @@ +
    + <%= cell("decidim/announcement", "  #{t("generating_access_codes.info_message", scope: "decidim.votings.census.admin.census", email: user_email)}", callout_class: "warning") %> +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_new_census.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_new_census.html.erb new file mode 100644 index 00000000..bf800f2a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_new_census.html.erb @@ -0,0 +1,23 @@ +
    + <%= cell("decidim/announcement", t("info_message", scope: "decidim.votings.census.admin.census.new"), callout_class: "warning") %> + <%= cell("decidim/announcement", ballot_style_callout_text, callout_class: ballot_style_callout_level) %> + +
    +
    +

    <%= t("title", scope: "decidim.votings.census.admin.census.new") %>

    + + <%= render "upload_info", ballot_style_code_header: %> + + <%= decidim_form_for(@form, url: admin_voting_census_path, + method: :post, + html: { class: "form form-defaults new_census" }) do |form| %> +
    + <%= form.upload :file, help_i18n_scope: "decidim.votings.census.admin.census.new.file_help", button_class: "button button__sm button__transparent-secondary" %> +
    +
    + <%= submit_tag t("submit", scope: "decidim.votings.census.admin.census.new"), class: "button button__sm button__secondary" %> +
    + <% end %> +
    +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/_upload_info.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/_upload_info.html.erb new file mode 100644 index 00000000..c0349a3a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/_upload_info.html.erb @@ -0,0 +1,17 @@ +<%= content_tag :p, t("upload_info.csv_header_before", scope: "decidim.votings.census.admin.census") %> + +
    <%= "Document ID;Document Type;Date of Birth;Full name;Full address;Postal Code;Mobile phone number;Email address;#{ballot_style_code_header}" %>
    + +<%= content_tag :p, t("upload_info.csv_header_after", scope: "decidim.votings.census.admin.census", ballot_style_code_header:) %> + +<%= content_tag :p, sanitize(t("upload_info.csv_example_with_ballot_style", scope: "decidim.votings.census.admin.census")) %> +
    <%= "Document ID;Document Type;Date of Birth;Full name;Full address;Postal Code;Mobile phone number;Email address;#{ballot_style_code_header}
    +12345678X;passport;20011130;John Doe;The full address;08001;123456789;user@example.org;DISTRICT1
    +87654321X;passport;20010808;Alice Doe;The full address;08002;987654321;user2@example.org;DISTRICT2" %>
    +...
    + +<%= content_tag :p, sanitize(t("upload_info.csv_example_without_ballot_style", scope: "decidim.votings.census.admin.census")) %> +
    Document ID;Document Type;Date of Birth;Full name;Full address;Postal Code;Mobile phone number;Email address
    +12345678X;passport;20011130;John Doe;The full address;08001;123456789;user@example.org
    +87654321X;passport;20010808;Alice Doe;The full address;08002;987654321;user2@example.org
    +...
    diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/show.html.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/show.html.erb new file mode 100644 index 00000000..dc23025a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/show.html.erb @@ -0,0 +1,24 @@ +<% add_decidim_page_title(t("show.heading", scope: "decidim.votings.census.admin.census")) %> +
    +
    +

    + <%= t("show.heading", scope: "decidim.votings.census.admin.census") %> + + <% if current_census.data_created? %> + <%= link_to t("delete.button", scope: "decidim.votings.census.admin.census"), + admin_voting_census_path, + class: "button button__sm button__secondary alert hollow tiny button--title destroy", + method: :delete, + data: { confirm: t("delete.confirm", scope: "decidim.votings.census.admin.census") } %> + <% end %> +

    +
    + +
    + <%= render current_census_action_view %> +
    +
    + +<% if waiting_status? %> + <%= append_javascript_pack_tag "decidim_votings_admin" %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/census/admin/census/status.js.erb b/decidim-elections/app/views/decidim/votings/census/admin/census/status.js.erb new file mode 100644 index 00000000..6a452e8a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/admin/census/status.js.erb @@ -0,0 +1 @@ +$("#wrapper-action-view").html('<%= j(render partial: current_census_action_view).strip.html_safe %>'); diff --git a/decidim-elections/app/views/decidim/votings/census/export_mailer/access_codes_export.erb b/decidim-elections/app/views/decidim/votings/census/export_mailer/access_codes_export.erb new file mode 100644 index 00000000..072db5c8 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/census/export_mailer/access_codes_export.erb @@ -0,0 +1,7 @@ + + + diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_answer_results_form_fields.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_answer_results_form_fields.html.erb new file mode 100644 index 00000000..98d9df9a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_answer_results_form_fields.html.erb @@ -0,0 +1,12 @@ +<% form.object.answer_results.each do |answer| %> + <% next unless question.id == answer.question_id %> + + <%= form.fields_for "[answer_results][]", answer do |answer_form| %> + <%= answer_form.hidden_field :question_id %> + <%= answer_form.hidden_field :id %> + <%= answer_form.number_field(:value, + label: translated_attribute(answer.title), + class: "answer-value", + min: 0) %> + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_ballot_results_form_fields.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_ballot_results_form_fields.html.erb new file mode 100644 index 00000000..197b2851 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_ballot_results_form_fields.html.erb @@ -0,0 +1,34 @@ +<%= content_tag :h3, + t("edit.total_ballots", scope: "decidim.votings.polling_officer_zone.closures"), + class: "h4" %> + +<%= form.fields_for "[ballot_results]", form.object.ballot_results do |ballot_form| %> + <%= content_tag :span, + "", + id: "closure_result-total-ballots", + hidden: true, + data: { total_ballots: ballot_form.object.total_ballots_count } %> + + <%= ballot_form.hidden_field(:total_ballots_count) %> + + <%= ballot_form.number_field( + :valid_ballots_count, + label: t("edit.total_valid_ballots", scope: "decidim.votings.polling_officer_zone.closures"), + class: "total-value", + min: 0 + ) %> + + <%= ballot_form.number_field( + :blank_ballots_count, + label: t("edit.total_blank_ballots", scope: "decidim.votings.polling_officer_zone.closures"), + class: "total-value", + min: 0 + ) %> + + <%= ballot_form.number_field( + :null_ballots_count, + label: t("edit.total_null_ballots", scope: "decidim.votings.polling_officer_zone.closures"), + class: "total-value", + min: 0 + ) %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_certify_form.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_certify_form.html.erb new file mode 100644 index 00000000..af8ec84c --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_certify_form.html.erb @@ -0,0 +1,17 @@ +<%= decidim_form_for(@form, + method: :post, + url: certify_polling_officer_election_closure_path(polling_officer, election), + html: { multipart: true, class: "form certify_closure" }) do |form| %> + +
    + <%= form.attachment :photos, + multiple: true, + button_class: "button button__sm button__transparent-secondary", + label: t("decidim.votings.polling_officer_zone.closures.certify.upload_photos"), + button_label: t("decidim.votings.polling_officer_zone.closures.certify.add_photos"), + button_edit_label: t("decidim.votings.polling_officer_zone.closures.certify.edit_photos") %> + +
    + + <%= form.submit t("submit", scope: "decidim.votings.polling_officer_zone.closures.certify"), class: "button button__sm md:button__lg button__secondary" %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_count_error.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_count_error.html.erb new file mode 100644 index 00000000..4fa18583 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_count_error.html.erb @@ -0,0 +1,48 @@ +<%= decidim_modal id:"modal-closure-count-error" do %> +
    + <%= icon "error-warning-line" %> +

    + <%= t("title", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error") %> +

    +
    + <%= content_tag :p, + t("info_text", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error"), + id: "dialog-desc-modal-closure-count-error" %> + +
    +

    + <%= content_tag :strong, + t("total_people", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error") %> + <%= @form.election_votes_count %> +

    +

    + <%= content_tag :strong, + t("total_ballots", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error") %> + <%= content_tag :span, + "--", + id: "modal-total-ballots-value" %> +

    +
    + + <%= content_tag :p, + t("info_explanation_text", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error") %> + + <%= form.text_area( + :polling_officer_notes, + id: "modal-polling-officer-notes", + rows: 5, + label: t("message_for_monitoring_committee", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error"), + placeholder: t("text_area_placeholder", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error") + ) %> +
    +
    +
    + + + +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_results_count_error.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_results_count_error.html.erb new file mode 100644 index 00000000..1d1fe2dd --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_results_count_error.html.erb @@ -0,0 +1,35 @@ +<%= decidim_modal id:"modal-closure-results-count-error" do %> +
    + <%= icon "error-warning-line" %> +

    + <%= t("title", scope: "decidim.votings.polling_officer_zone.closures.new.modal_ballots_count_error") %> +

    + +
    +

    + <%= t("info_text", scope: "decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error") %> +

    +

    + <%= t("total", scope: "decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error", + expected: closure.results.total_ballots&.first&.value, + current: '0').html_safe %> +

    +

    + <%= t("valid", scope: "decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error", + expected: '0', + current: '0').html_safe %> +

    +

    + <%= t("blank", scope: "decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error", + expected: '0', + current: '0').html_safe %> +

    +
    +
    +
    + <%= content_tag :button, + t("close_modal", scope: "decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error"), + data: { "dialog-close": "modal-closure-results-count-error" }, + class: "button button__sm md:button__lg button__transparent-secondary" %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_question_results_form_fields.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_question_results_form_fields.html.erb new file mode 100644 index 00000000..87dc4fb2 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_question_results_form_fields.html.erb @@ -0,0 +1,18 @@ +<% form.object.question_results.each do |question| %> + <%= content_tag :h3, + translated_attribute(question.title), + class: "h4" %> + + <%= render partial: "answer_results_form_fields", locals: { form:, question: } %> + + <% if question.nota_option? %> + <%= form.fields_for "[question_results][]", question do |question_form| %> + <%= question_form.hidden_field :id %> + + <%= question_form.number_field(:value, + label: t("decidim.elections.votes.new.nota_option"), + class: "nota-value", + min: 0) %> + <% end %> + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_sign_form.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_sign_form.html.erb new file mode 100644 index 00000000..d2ddf7fc --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/_sign_form.html.erb @@ -0,0 +1,43 @@ +<%= decidim_form_for(@form, + url: sign_polling_officer_election_closure_path(polling_officer, election), + html: { class: "form sign_closure" }) do |form| %> + +
    + <%= form.check_box :signed, label: t("check_box", scope: "decidim.votings.polling_officer_zone.closures.sign"), label_options: { class: "form__wrapper-checkbox-label" } %> +
    + +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back_to_polling_stations", scope: "decidim.votings.polling_officer_zone.closures") %> + <% end %> + + <%= content_tag :button, + t("submit", scope: "decidim.votings.polling_officer_zone.closures.sign"), + id: "btn-modal-closure-sign", + class: "button button__sm md:button__lg button__secondary", + data: { "dialog-open": "modal-closure-sign" }, + disabled: true, + tabindex: "0" %> +
    + + <%= decidim_modal id:"modal-closure-sign" do %> +
    + <%= icon "article-line" %> +

    + <%= t("title", scope: "decidim.votings.polling_officer_zone.closures.edit.modal_ballots_results_count_error") %> +

    + +

    <%= t("info_text", scope: "decidim.votings.polling_officer_zone.closures.sign") %>

    +
    +
    + + + +
    + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/edit.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/edit.html.erb new file mode 100644 index 00000000..6c647f03 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/edit.html.erb @@ -0,0 +1,68 @@ +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<%= render layout: "layouts/decidim/shared/layout_center", locals: { columns: 8 } do %> + +

    + <%= t("heading", scope: "decidim.votings.polling_officer_zone.closures.edit") %> +

    + +
    +

    + <%= content_tag :span, + present(election).title, + title: t("polling_station.election", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %> + + <%= content_tag :span, + present(election.participatory_space).title, + class: "text-gray", + title: t("polling_station.voting", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %> +

    + +

    + <%= t("info_text", scope: "decidim.votings.polling_officer_zone.closures.edit", total: "#{closure.results.total_ballots&.first&.value}").html_safe %> +

    + +
    + <%= link_to(t("start_over", scope: "decidim.votings.polling_officer_zone.closures.edit"), polling_officer_election_closure_path(polling_officer, election), method: :delete, data: { confirm: t("confirm_start_over", scope: "decidim.votings.polling_officer_zone.closures.edit") }) %> +
    + +
    + + <%= decidim_form_for(@form, method: :patch, url: polling_officer_election_closure_path(polling_officer, election), + html: { class: "form edit_closure" }) do |form| %> + + <%= form_required_explanation %> + + <%= form.hidden_field :election_id %> + <%= form.hidden_field :polling_station_id %> + +
    + <%= render partial: "ballot_results_form_fields", locals: { form: } %> + + <%= render partial: "question_results_form_fields", locals: { form: } %> +
    + +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back_to_polling_stations", scope: "decidim.votings.polling_officer_zone.closures") %> + <% end %> + +
    + <%= form.submit t("save_recount", scope: "decidim.votings.polling_officer_zone.closures.edit"), class: "button button__sm md:button__lg button__secondary" %> +
    + + +
    + + <% end %> + + <%= render "modal_ballots_results_count_error" %> + +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/new.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/new.html.erb new file mode 100644 index 00000000..cdd07715 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/new.html.erb @@ -0,0 +1,69 @@ +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<%= render layout: "layouts/decidim/shared/layout_center", locals: { columns: 8 } do %> + +

    + <%= t("heading", scope: "decidim.votings.polling_officer_zone.closures.new") %> +

    + +
    +

    + <%= content_tag :strong, t("polling_station", scope: "decidim.votings.polling_officer_zone.closures.new") %> +
    + <%= present(polling_officer.polling_station).title %> +
    + <%= present(polling_officer.polling_station).address %> +

    + +

    + <%= content_tag :strong, t("election", scope: "decidim.votings.polling_officer_zone.closures.new") %> + <%= present(election).title %> +

    + +

    + <%= t("info_text", scope: "decidim.votings.polling_officer_zone.closures.new") %> +

    +
    + + <%= decidim_form_for(@form, url: polling_officer_election_closure_path(polling_officer, election), + html: { class: "form new_closure" }) do |form| %> + + <%= form_required_explanation %> + + <%= form.hidden_field :election_id %> + <%= form.hidden_field :polling_station_id %> + <%= form.hidden_field :election_votes_count %> + <%= form.hidden_field :polling_officer_notes %> + +
    + <%= form.number_field( + :total_ballots_count, + label: t("total_ballots_count", scope: "decidim.votings.polling_officer_zone.closures.new"), + min: 0 + ) %> +
    + +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back_to_polling_stations", scope: "decidim.votings.polling_officer_zone.closures") %> + <% end %> + + + +
    + <%= form.submit t("submit", scope: "decidim.votings.polling_officer_zone.closures.new"), + disabled: true, + class: "button button__sm md:button__lg button__secondary" %> +
    +
    + + <%= render "modal_ballots_count_error", form: %> + <% end %> + +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/show.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/show.html.erb new file mode 100644 index 00000000..18c59997 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/closures/show.html.erb @@ -0,0 +1,50 @@ +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<%= render layout: "layouts/decidim/shared/layout_center", locals: { columns: 8 } do %> + +

    + <%= if closure.certificate_phase? + t("heading", scope: "decidim.votings.polling_officer_zone.closures.certify") + elsif closure.signature_phase? + t("heading", scope: "decidim.votings.polling_officer_zone.closures.sign") + else + t("heading", scope: "decidim.votings.polling_officer_zone.closures.show") + end %> +

    +

    <%= if closure.complete_phase? + t("sub_heading", scope: "decidim.votings.polling_officer_zone.closures.completed") + else + t("sub_heading", scope: "decidim.votings.polling_officer_zone.closures.show") + end %> +

    + + <% unless closure.complete_phase? %> + <%= cell "decidim/announcement", link_to(t("edit_count_votes", scope: "decidim.votings.polling_officer_zone.closures.show"), + edit_polling_officer_election_closure_path(polling_officer, election)) %> + <% end %> + + <%= cell "decidim/announcement", if closure.certificate_phase? + t("info_text", scope: "decidim.votings.polling_officer_zone.closures.certify") + elsif closure.signature_phase? + t("info_text", scope: "decidim.votings.polling_officer_zone.closures.sign") + end %> + + <%= cell("decidim/votings/polling_station_closure_recount", closure) %> + + <%= cell("decidim/votings/polling_station_closure_certificate", closure) unless closure.certificate_phase? %> + + <%= render "certify_form" if closure.certificate_phase? %> + + <%= render "sign_form" if closure.signature_phase? && !closure.signed? %> + + <% unless closure.certificate_phase? || (closure.signature_phase? && !closure.signed?) %> +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back_to_polling_stations", scope: "decidim.votings.polling_officer_zone.closures") %> + <% end %> +
    + <% end %> + +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_complete_voting.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_complete_voting.html.erb new file mode 100644 index 00000000..9f580028 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_complete_voting.html.erb @@ -0,0 +1,48 @@ +<% if vote_check.allowed? %> + <%= cell("decidim/announcement", voted_online? ? t(".census_verified_with_online_vote") : t(".census_verified"), callout_class: "success") %> + +

    <%= voted_online? ? t(".questions_title_voted") : t(".questions_title") %>

    + +
    + <% questions.each_with_index do |question, ix| %> +
    + + + +
    + <% end %> +
    + + <%= decidim_form_for(in_person_vote_form, url: polling_officer_election_in_person_votes_path(polling_officer, election)) do |f| %> + <%= f.hidden_field :voter_token %> + <%= f.hidden_field :voter_id %> + +
    + <%= f.check_box :voted, label: t(".voted"), id: "person_voted_checkbox", label_options: { for: "person_voted_checkbox", class: "form__wrapper-checkbox-label" } %> +
    + +
    + <%= f.submit t(".complete_voting"), class: "button button__sm md:button__lg button__secondary", id: "submit_complete_voting" %> +
    + <% end %> + +<% else %> + <%= cell("decidim/announcement", vote_check.error_message, callout_class: "alert") %> + +
    + <%= link_to exit_path, class: "button button__sm md:button__lg button__secondary" do %> + <%= t(".identify_another") %> + <% end %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_in_person_form.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_in_person_form.html.erb new file mode 100644 index 00000000..52220c4a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_in_person_form.html.erb @@ -0,0 +1,37 @@ +<% if request.post? && in_person_form.valid? %> + <%= cell("decidim/announcement", { title: t(".census_not_present"), body: t(".census_not_present_description") }, callout_class: "alert") %> +<% end %> + +

    <%= t(".title") %>

    + +<%= decidim_form_for(in_person_form, url: polling_officer_election_in_person_votes_path(polling_officer, election), html: { class: "form", autocomplete: "off" }) do |f| %> + <%= form_required_explanation %> + +
    + <%= f.select :document_type, + in_person_form.options_for_document_type_select, + { include_blank: t(".select") }, + { multiple: false } %> + + <%= f.text_field :document_number, label: t(".document_number"), placeholder: t(".document_number_placeholder") %> + +
    + <%= f.label t(".date_of_birth") %> + +
    + <%= f.text_field :day, placeholder: t(".day_placeholder"), label: t(".day"), "data-autojump" => true, "data-max-length" => 2, "data-jump-next" => "#in_person_month" %> + <%= f.text_field :month, placeholder: t(".month_placeholder"), label: t(".month"), "data-autojump" => true, "data-max-length" => 2, "data-jump-prev" => "#in_person_day", "data-jump-next" => "#in_person_year" %> + <%= f.text_field :year, placeholder: t(".year_placeholder"), label: t(".year"), "data-autojump" => true, "data-max-length" => 4, "data-jump-prev" => "#in_person_month" %> +
    +
    +
    + +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back", scope: "decidim.votings.polling_officer_zone.in_person_votes.new") %> + <% end %> + + <%= f.submit t(".validate_document"), class: "button button__sm md:button__lg button__secondary" %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_verify_document.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_verify_document.html.erb new file mode 100644 index 00000000..f286038a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/_verify_document.html.erb @@ -0,0 +1,20 @@ +<%= cell("decidim/announcement", t(".census_present"), callout_class: "success") %> + +
    +

    <%= t(".title") %>

    + +

    + <%= t(".name") %>: <%= voter_name %> +

    +
    + +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back", scope: "decidim.votings.polling_officer_zone.in_person_votes.new") %> + <% end %> + + +
    diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/new.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/new.html.erb new file mode 100644 index 00000000..4e427056 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/new.html.erb @@ -0,0 +1,25 @@ +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> + +
    +

    + <%= t("title", scope: "decidim.votings.polling_officer_zone.in_person_votes.new") %> +

    +
    + + <% if has_voter? %> +
    + <%= render partial: "verify_document" %> +
    + + <% else %> + <%= render partial: "in_person_form" %> + <% end %> + +<% end %> + +<%= render "decidim/elections/shared/broken_promises_modal" %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/show.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/show.html.erb new file mode 100644 index 00000000..10a5cd68 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/in_person_votes/show.html.erb @@ -0,0 +1,30 @@ +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> +
    +

    + <%= t("title", scope: "decidim.votings.polling_officer_zone.in_person_votes.show") %> +

    +
    + +
    +
    + +
    + <%= icon "fingerprint-line", class: "w-40 h-40 text-tertiary animate-pulse fill-current" %> +
    + +
    + <%= link_to polling_officers_path, class: "button button__sm md:button__lg button__text-secondary" do %> + <%= icon "arrow-left-line" %> + <%= t("back", scope: "decidim.votings.polling_officer_zone.in_person_votes.show") %> + <% end %> + + <%= form_tag polling_officer_election_in_person_vote_path(polling_officer, election, in_person_vote), method: :patch, class: "update_vote_status" %> +
    +<% end %> + +<%= render "decidim/elections/shared/broken_promises_modal" %> diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/_polling_station.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/_polling_station.html.erb new file mode 100644 index 00000000..9de25f25 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/_polling_station.html.erb @@ -0,0 +1,53 @@ +
    + <%= link_to resource_locator(election).path do %> + <%= content_tag :h3, + present(election).title, + class: "h5 text-secondary hover:underline", + title: t("polling_station.election", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %> + <%= content_tag :span, + present(polling_officer.voting).title, + class: "text-sm text-gray-2", + title: t("polling_station.voting", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %> + <%= content_tag :span, + t(polling_officer.role, scope: "decidim.votings.polling_officers.roles"), + title: t("polling_station.role", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), + class: "ml-auto label#{" success" if polling_officer.role == :president}" %> + <% end %> + + <% if polling_officer.polling_station %> +
    + <%= content_tag :span, + present(polling_officer.polling_station).title, + class: "font-semibold", + title: t("polling_station.name", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %> + + <%= content_tag :span, + present(polling_officer.polling_station).address, + class: "text-sm text-gray-2", + title: t("polling_station.address", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %> +
    + +
    + <% closure = polling_officer.polling_station.closures.find_by(election:) %> + <%= link_to t("polling_station.identify_person", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), + new_polling_officer_election_in_person_vote_path(polling_officer, election), + class: "button button__sm button__transparent-secondary" if allowed_to?(:create, :in_person_vote, polling_officer:, closure:) %> + + <% if closure %> + <% if closure.results_phase? %> + <%= link_to t("polling_station.count_votes", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), + edit_polling_officer_election_closure_path(polling_officer, election), + class: "button button__sm button__transparent-secondary" %> + <% else %> + <%= link_to t("polling_station.show_closure", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), + polling_officer_election_closure_path(polling_officer, election), + class: "button button__sm button__secondary" %> + <% end %> + <% else %> + <%= link_to t("polling_station.count_votes", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), + new_polling_officer_election_closure_path(polling_officer, election), + class: "button button__sm button__secondary" %> + <% end %> +
    + <% end %> +
    diff --git a/decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/index.html.erb b/decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/index.html.erb new file mode 100644 index 00000000..95c1b3ca --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/polling_officer_zone/polling_officers/index.html.erb @@ -0,0 +1,19 @@ +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<%= render layout: "layouts/decidim/shared/layout_user_profile" do %> + + <%= cell("decidim/announcement", t("polling_officer_role_description", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), callout_class: "success") %> + +

    <%= t("polling_station.title", scope: "decidim.votings.polling_officer_zone.polling_officers.index") %>

    + + <% if polling_officers_elections.any? %> + <% polling_officers_elections.each do |election| %> + <% polling_officer = polling_officers.find_by(voting: election.participatory_space) %> + <%= render partial: "polling_station", locals: { polling_officer:, election: } %> + <% end %> + <% else %> + <%= cell("decidim/announcement", t("polling_station.no_polling_stations", scope: "decidim.votings.polling_officer_zone.polling_officers.index"), callout_class: "warning") %> + <% end %> + +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/_access_code_modal.html.erb b/decidim-elections/app/views/decidim/votings/votings/_access_code_modal.html.erb new file mode 100644 index 00000000..f3e8ab27 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/_access_code_modal.html.erb @@ -0,0 +1,28 @@ +<%= decidim_modal id: "access-code-modal" do %> +
    + <%= icon "lock-unlock-line" %> +

    + <%= t("title", scope: "decidim.votings.votings.access_code_modal") %> +

    +

    + <%= t("info", scope: "decidim.votings.votings.access_code_modal") %> +

    +
    +
    + <% if email.empty? %> + + <% else %> + <%= button_to t("email", scope: "decidim.votings.votings.access_code_modal", email:), voting_send_access_code_path(datum.dataset.voting, datum_id: datum, medium: "email"), class: "button button__sm md:button__lg button__secondary" %> + <% end %> + + <% if sms.empty? %> + + <% else %> + <%= button_to t("sms", scope: "decidim.votings.votings.access_code_modal", sms:), voting_send_access_code_path(datum.dataset.voting, datum_id: datum, medium: "sms"), class: "button button__sm md:button__lg button__secondary" %> + <% end %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/_check_fields.html.erb b/decidim-elections/app/views/decidim/votings/votings/_check_fields.html.erb new file mode 100644 index 00000000..3d9ae5c5 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/_check_fields.html.erb @@ -0,0 +1,20 @@ +<%= f.select :document_type, + form.options_for_document_type_select, + { include_blank: t(".select"), label: t(".document_type") }, + { multiple: false } %> + +<%= f.text_field :document_number, label: t(".document_number"), placeholder: t(".document_number_placeholder") %> + +<%= f.text_field :postal_code, label: t(".postal_code"), placeholder: t(".postal_code_placeholder") %> + +
    + <%= f.label t(".date_of_birth") %> + +
    + + <%= f.text_field :day, placeholder: t(".day_placeholder"), label: t(".day"), "data-autojump" => true, "data-max-length" => 2, "data-jump-next" => "##{f.object_name}_month" %> + <%= f.text_field :month, placeholder: t(".month_placeholder"), label: t(".month"), "data-autojump" => true, "data-max-length" => 2, "data-jump-prev" => "##{f.object_name}_day", "data-jump-next" => "##{f.object_name}_year" %> + <%= f.text_field :year, placeholder: t(".year_placeholder"), label: t(".year"), "data-autojump" => true, "data-max-length" => 4, "data-jump-prev" => "##{f.object_name}_month" %> + +
    +
    diff --git a/decidim-elections/app/views/decidim/votings/votings/_polling_officers_picker.html.erb b/decidim-elections/app/views/decidim/votings/votings/_polling_officers_picker.html.erb new file mode 100644 index 00000000..e59523ef --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/_polling_officers_picker.html.erb @@ -0,0 +1,4 @@ +<%= append_stylesheet_pack_tag "decidim_votings_admin", media: "all" %> +<%= append_javascript_pack_tag "decidim_votings_admin" %> + +<%= cell "decidim/votings/polling_officers/polling_officers_picker", current_component, form:, field: %> diff --git a/decidim-elections/app/views/decidim/votings/votings/_votings.html.erb b/decidim-elections/app/views/decidim/votings/votings/_votings.html.erb new file mode 100644 index 00000000..1a65b06b --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/_votings.html.erb @@ -0,0 +1,19 @@ +<% if paginated_votings.empty? %> + <%= cell("decidim/announcement", t(:no_votings, scope: "decidim.votings.votings.index")) %> +<% else %> + <% if only_finished_votings? %> + <%= cell("decidim/announcement", t(:only_finished, scope: "decidim.votings.votings.index"), callout_class: "warning") %> + <% end %> + +

    <%= t("title", scope: "decidim.votings.votings.count", count: paginated_votings.total_count) %>

    + + <%= order_selector available_orders, i18n_scope: "decidim.votings.votings.orders" %> + +
    + <% paginated_votings.each do |voting| %> + <%= card_for voting %> + <% end %> +
    + + <%= decidim_paginate paginated_votings %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/check_census.html.erb b/decidim-elections/app/views/decidim/votings/votings/check_census.html.erb new file mode 100644 index 00000000..eef2c372 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/check_census.html.erb @@ -0,0 +1,68 @@ +<% add_decidim_meta_tags(title: translated_attribute(current_participatory_space.title)) %> + +<% +edit_link( + resource_locator(current_participatory_space).edit, + :update, + :voting, + voting: current_participatory_space +) +%> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> +
    +

    + <%= t("title", scope: "decidim.votings.votings.check_census") %> +

    + +
    + <%= t("description", scope: "decidim.votings.votings.check_census") %> +
    +
    + + <% if success %> + + <%# NOTE: announcement cell clean_body method will purge the button %> +
    +
    + <%= t("decidim.votings.votings.check_census.success.title") %> +
    + + <%= t("decidim.votings.votings.check_census.success.info") %> + + +
    + + <% if datum.email.present? || datum.mobile_phone_number.present? %> + <%= render partial: "access_code_modal", locals: { datum:, email: datum.email ? datum.email.gsub!(/^.+@/,"****@") : "", sms: datum.mobile_phone_number ? datum.mobile_phone_number.gsub!(/.{3}\d$/,"***") : "" } %> + <% end %> + + <% elsif not_found %> + + <% announcement_not_found = { title: t("decidim.votings.votings.check_census.error.title"), body: t("decidim.votings.votings.check_census.error.info", census_contact_information: ) } %> + + <%= cell("decidim/announcement", announcement_not_found, callout_class: "alert" ) %> + <% end %> + + <% if !success %> + <%= decidim_form_for(@form, url: voting_check_census_path, html: { class: "form", autocomplete: "off" }) do |f| %> + <%= form_required_explanation %> + +
    +

    <%= t("form_title", scope: "decidim.votings.votings.check_census") %>

    + + <%= render partial: "check_fields", locals: { f:, form: @form } %> +
    + +
    + <%= f.submit t("check_status", scope: "decidim.votings.votings.check_census"), class: "button button__sm md:button__lg button__secondary" %> +
    + <% end %> + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/elections_log.html.erb b/decidim-elections/app/views/decidim/votings/votings/elections_log.html.erb new file mode 100644 index 00000000..5382a5b1 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/elections_log.html.erb @@ -0,0 +1,37 @@ +<% add_decidim_meta_tags(title: t("votings.index.title", scope: "decidim.votings")) %> + +<% +edit_link( + decidim_admin_votings.votings_path, + :read, + :voting +) +%> + +<%# NOTE: use elections pack since the styles are placed there %> +<%= append_stylesheet_pack_tag "decidim_elections" %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> +
    +

    + <%= t("title", scope: "decidim.votings.votings.elections_log") %> +

    + +
    + <%= t("description", scope: "decidim.votings.votings.elections_log") %> +
    +
    + + <% elections.each do |election| %> +
    + <%= election.start_time %> + +

    + <%= translated_attribute(election.title) %> + <%= election.bb_status.to_s.titlecase %> +

    + + <%= translated_attribute(election.description).html_safe %> +
    + <% end %> +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/index.html.erb b/decidim-elections/app/views/decidim/votings/votings/index.html.erb new file mode 100644 index 00000000..be86c5df --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/index.html.erb @@ -0,0 +1,39 @@ +<% add_decidim_meta_tags(title: t("votings.index.title", scope: "decidim.votings")) %> + +<% +edit_link( + decidim_admin_votings.votings_path, + :read, + :voting +) +%> + +<%= append_javascript_pack_tag "decidim_votings" %> +<%= append_stylesheet_pack_tag "decidim_votings" %> + +<% content_for :aside do %> +

    <%= t("decidim.menu.votings") %>

    + + <%= render layout: "decidim/shared/filters", locals: { filter_sections: , search_variable: :search_text_cont, skip_to_id: "votings" } do %> + <%= hidden_field_tag :order, order, id: nil, class: "order_filter" %> + <% end %> +<% end %> + +<%= render layout: "layouts/decidim/shared/layout_two_col" do %> + + <%= participatory_space_floating_help %> + + <% if promoted_votings.any? %> +
    +

    <%= t("votings.index.promoted_votings", scope: "layouts.decidim") %>

    + + <% promoted_votings.each do |promoted_item| %> + <%= card_for promoted_item, highlight: true, size: :g %> + <% end %> +
    + <% end %> + +
    + <%= render partial: "votings" %> +
    +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/index.js.erb b/decidim-elections/app/views/decidim/votings/votings/index.js.erb new file mode 100644 index 00000000..da864a5a --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/index.js.erb @@ -0,0 +1,5 @@ +var $votings = $('#votings'); +var $orderFilterInput = $('.order_filter'); + +$votings.html('<%= j(render partial: "votings") %>'); +$orderFilterInput.val('<%= order %>'); diff --git a/decidim-elections/app/views/decidim/votings/votings/login.html.erb b/decidim-elections/app/views/decidim/votings/votings/login.html.erb new file mode 100644 index 00000000..2e576b65 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/login.html.erb @@ -0,0 +1,57 @@ +<% add_decidim_meta_tags(title: translated_attribute(current_participatory_space.title)) %> + +<%= append_stylesheet_pack_tag "decidim_elections" %> +<%= append_javascript_pack_tag "decidim_elections" %> + +<%= display_flash_messages unless request.post? && @form.invalid? %> + +<%= render layout: "layouts/decidim/shared/layout_center" do %> + +
    +

    + <%= t("vote_for", scope: "decidim.elections.votes.header", title: translated_attribute(election.title) ) %> +

    +
    + +
      + <% + # i18n-tasks-use t('decidim.elections.votes.header.register') + # i18n-tasks-use t('decidim.elections.votes.header.election') + # i18n-tasks-use t('decidim.elections.votes.header.confirm') + # i18n-tasks-use t('decidim.elections.votes.header.ballot_decision') + %> + <% %w(register election confirm ballot_decision).each_with_index do |wizard_step, i| %> +
    1. ><%= t(wizard_step, scope: "decidim.elections.votes.header") %>
    2. + <% end %> +
    + +
    +

    + <%= t("votings.login.title", scope: "decidim.votings") %> +

    + +

    + <%= t("form_title", scope: "decidim.votings.votings.login") %> +

    +
    + + <%= decidim_form_for(@form, url: params[:vote_path], html: { class: "form", autocomplete: "off" }) do |f| %> + <%= form_required_explanation %> + +
    + <%= render partial: "check_fields", locals: { f:, form: @form } %> + + <%= f.text_field :access_code, label: t("access_code", scope: "decidim.votings.votings.login"), placeholder: t("access_code_placeholder", scope: "decidim.votings.votings.login") %> +
    + +
    + <%= f.submit t("start_voting", scope: "decidim.votings.votings.login"), class: "button button__sm md:button__lg button__secondary" %> +
    + +
    + <%= t("votings.login.dont_have_access_code", scope: "decidim.votings") %> + <%= link_to t("votings.login.ask_for_a_new_one", scope: "decidim.votings"), voting_check_census_path, class: "text-secondary hover:underline" %> +
    + <% end %> + +<% end %> diff --git a/decidim-elections/app/views/decidim/votings/votings/show.html.erb b/decidim-elections/app/views/decidim/votings/votings/show.html.erb new file mode 100644 index 00000000..6c6da275 --- /dev/null +++ b/decidim-elections/app/views/decidim/votings/votings/show.html.erb @@ -0,0 +1,25 @@ +<% add_decidim_meta_tags({ + title: translated_attribute(current_participatory_space.title), + image_url: current_participatory_space.attached_uploader(:banner_image).path, + description: translated_attribute(current_participatory_space.description), + url: voting_url(current_participatory_space) + }) %> + +<% +edit_link( + resource_locator(current_participatory_space).edit, + :update, + :voting, + voting: current_participatory_space +) +%> + +<%= render partial: "layouts/decidim/header/follow_space_menu_bar_button", locals: { participatory_space: current_participatory_space } %> + +
    + + <% landing_content_blocks.each do |content_block| %> + <%= cell content_block.manifest.cell, content_block %> + <% end %> + +
    diff --git a/decidim-elections/app/views/layouts/decidim/admin/voting.html.erb b/decidim-elections/app/views/layouts/decidim/admin/voting.html.erb new file mode 100644 index 00000000..38125c8e --- /dev/null +++ b/decidim-elections/app/views/layouts/decidim/admin/voting.html.erb @@ -0,0 +1,23 @@ +<% add_secondary_root_menu(:admin_voting_menu) %> + +<% content_for :breadcrumb_context_menu do %> +
    + <%= link_to decidim_votings.voting_path(current_participatory_space), class: "button button__sm button__transparent process-title-content-breadcrumb-container-right-link", target: "_blank", data: { "external-link": false } do %> + <%= icon "eye-line" %> + + <%= t("see_voting", scope: "decidim.votings.admin.menu.votings_submenu") %> + + <% end %> +
    +<% end %> +<%= render "layouts/decidim/admin/application" do %> +
    + <% if !current_participatory_space.elections.any? %> + <%= cell("decidim/announcement", t("votings.edit.add_election_component", scope: "decidim.votings.admin"), callout_class: "alert") %> + <% end %> + <% if current_participatory_space.polling_stations_with_missing_officers? %> + <%= cell("decidim/announcement", t("votings.edit.assign_missing_officers", scope: "decidim.votings.admin"), callout_class: "alert") %> + <% end %> + <%= yield %> +
    +<% end %> diff --git a/decidim-elections/app/views/layouts/decidim/admin/votings.html.erb b/decidim-elections/app/views/layouts/decidim/admin/votings.html.erb new file mode 100644 index 00000000..044771ae --- /dev/null +++ b/decidim-elections/app/views/layouts/decidim/admin/votings.html.erb @@ -0,0 +1,16 @@ +<% content_for :breadcrumb_context_menu do %> +
    + <% if allowed_to? :create, :voting %> + <%= link_to new_voting_path, class: "button button__sm button__transparent process-title-content-breadcrumb-container-right-link" do %> + <%= icon "add-line", class: "w-4 h-4" %> + + <%= t("votings.actions.new_voting", scope: "decidim.votings.admin") %> + + <% end %> + <% end %> +
    +<% end %> + +<%= render "layouts/decidim/admin/application" do %> + <%= yield %> +<% end %> diff --git a/decidim-elections/bin/rails b/decidim-elections/bin/rails new file mode 100755 index 00000000..1a4ddb51 --- /dev/null +++ b/decidim-elections/bin/rails @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path("..", __dir__) +ENGINE_PATH = File.expand_path("../lib/decidim/elections/engine", __dir__) + +# Set up gems listed in the Gemfile. + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __dir__) + +require "bundler/setup" + +require "rails/all" +require "rails/engine/commands" diff --git a/decidim-elections/config/assets.rb b/decidim-elections/config/assets.rb new file mode 100644 index 00000000..bb378999 --- /dev/null +++ b/decidim-elections/config/assets.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +base_path = File.expand_path("..", __dir__) + +Decidim::Webpacker.register_path("#{base_path}/app/packs") +Decidim::Webpacker.register_entrypoints( + decidim_elections: "#{base_path}/app/packs/entrypoints/decidim_elections.js", + decidim_elections_admin: "#{base_path}/app/packs/entrypoints/decidim_elections_admin.js", + decidim_votings: "#{base_path}/app/packs/entrypoints/decidim_votings.js", + decidim_votings_admin: "#{base_path}/app/packs/entrypoints/decidim_votings_admin.js" +) diff --git a/decidim-elections/config/environment.rb b/decidim-elections/config/environment.rb new file mode 100644 index 00000000..2cb043f1 --- /dev/null +++ b/decidim-elections/config/environment.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +# Empty line for playing nice with tpope/vim-rails diff --git a/decidim-elections/config/initializers/decidim_bulletin_board.rb b/decidim-elections/config/initializers/decidim_bulletin_board.rb new file mode 100644 index 00000000..516f5e24 --- /dev/null +++ b/decidim-elections/config/initializers/decidim_bulletin_board.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +return if Rails.application.secrets.elections.blank? + +Decidim::BulletinBoard.configure do |config| + # Exposes a configuration option: the bulletin board server + config.bulletin_board_server = Rails.application.secrets.elections[:bulletin_board_server] + + # Exposes a configuration option: the bulletin board public key + config.bulletin_board_public_key = Rails.application.secrets.elections[:bulletin_board_public_key] + + # Exposes a configuration option: the api key generated by the Bulletin Board for the Decidim instance + config.authority_api_key = Rails.application.secrets.elections[:authority_api_key] + + # Exposes a configuration option: the authority name String + config.authority_name = Rails.application.secrets.elections[:authority_name] + + # Exposes a configuration option: private key, that got generated by the Decidim instance + config.authority_private_key = Rails.application.secrets.elections[:authority_private_key] + + # Exposes a configuration option: the scheme name to be used for messages + config.scheme_name = Rails.application.secrets.elections[:scheme_name] + + # Exposes a configuration option: the quorum + config.quorum = Rails.application.secrets.elections[:quorum] + + # Exposes a configuration option: number of trustees for an election + config.number_of_trustees = Rails.application.secrets.elections[:number_of_trustees] +end diff --git a/decidim-elections/config/initializers/rack_attack.rb b/decidim-elections/config/initializers/rack_attack.rb new file mode 100644 index 00000000..47667984 --- /dev/null +++ b/decidim-elections/config/initializers/rack_attack.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +if Rails.env.production? || Rails.env.test? + require "rack/attack" + + # Throttle check census attempts by IP to 6 reqs/minute + # Return the IP as a discriminator on POST /check_census requests + Rack::Attack.throttle( + "limit check census data attempts per request by IP", + limit: Decidim::Votings.check_census_max_requests, + period: Decidim::Votings.throttling_period + ) do |request| + request.ip if request.path.include?("/check_census") && request.post? + end +end diff --git a/decidim-elections/config/locales/am-ET.yml b/decidim-elections/config/locales/am-ET.yml new file mode 100644 index 00000000..9e7679c0 --- /dev/null +++ b/decidim-elections/config/locales/am-ET.yml @@ -0,0 +1 @@ +am: diff --git a/decidim-elections/config/locales/ar.yml b/decidim-elections/config/locales/ar.yml new file mode 100644 index 00000000..022ff2fa --- /dev/null +++ b/decidim-elections/config/locales/ar.yml @@ -0,0 +1,74 @@ +ar: + activemodel: + attributes: + answer: + description: الوصف + election: + description: الوصف + monitoring_committee_member: + name: الاسم + polling_officer: + name: الاسم + decidim: + components: + elections: + name: الانتخابات + elections: + admin: + models: + answer: + name: الإجابة + steps: + tally_started: + mark_as_missing: وضع علامة مفقود + tally_completion: وستكتمل هذه العملية عندما يكون جميع الأمناء مُفعلين أو موسومين باعتبارهم مفقودين. مطلوب بحد أدنى %{quorum} من الأمناء لإكمال العملية. + undo_mark_as_missing: وسيتمكن الأمين الذي وُضع عليه علامة مفقودة عن طريق الخطأ من المشاركة قبل إتمام العملية. ويمكنهم المضي قدما كالمعتاد وسيتم تجاهل العلامة المفقودة. + elections: + election_log: + results_title: النتائج + results: + percentage: "%{count}%" + models: + trustees_participatory_space: + fields: + name: الاسم + trustee_zone: + trustees: + show: + identification_keys: + cancel: إلغاء + votings: + admin: + models: + monitoring_committee_member: + fields: + name: الاسم + polling_officer: + fields: + name: الاسم + monitoring_committee_election_results: + actions: + title: الإجراءات + monitoring_committee_members: + actions: + destroy: حذف + polling_officer_zone: + closures: + edit: + modal_ballots_results_count_error: + close_modal: غلق + sign: + cancel: إلغاء + in_person_votes: + verify_document: + name: الاسم + polling_officers: + index: + polling_station: + name: الاسم + polling_officers: + actions: + destroy: حذف + polling_stations: + actions: + destroy: حذف diff --git a/decidim-elections/config/locales/bg-BG.yml b/decidim-elections/config/locales/bg-BG.yml new file mode 100644 index 00000000..6101a77b --- /dev/null +++ b/decidim-elections/config/locales/bg-BG.yml @@ -0,0 +1,7 @@ +bg: + activemodel: + attributes: + answer: + description: Описание + image: Изображение + proposals: Свързани предложения diff --git a/decidim-elections/config/locales/bg.yml b/decidim-elections/config/locales/bg.yml new file mode 100644 index 00000000..6101a77b --- /dev/null +++ b/decidim-elections/config/locales/bg.yml @@ -0,0 +1,7 @@ +bg: + activemodel: + attributes: + answer: + description: Описание + image: Изображение + proposals: Свързани предложения diff --git a/decidim-elections/config/locales/ca.yml b/decidim-elections/config/locales/ca.yml new file mode 100644 index 00000000..09fd18cc --- /dev/null +++ b/decidim-elections/config/locales/ca.yml @@ -0,0 +1,1425 @@ +ca: + activemodel: + attributes: + answer: + description: Descripció + image: Imatge + proposals: Propostes relacionades + title: Títol + ballot_style: + code: Codi + election: + description: Descripció + end_time: La votació finalitza el + start_time: La votació comença el + title: Títol + monitoring_committee_member: + email: Correu electrònic + name: Nom + polling_officer: + email: Correu electrònic + name: Nom + polling_station: + address: Adreça + location: Ubicació + location_hints: Detalls d'ubicació + polling_station_managers: Gestores + polling_station_president_id: Presidència + title: Títol + question: + max_selections: Número màxim d'opcions + min_selections: Cap de les opcions anteriors + title: Títol + trustees_participatory_space: + user_id: Participant + voting: + banner_image: Imatge de capçalera + census_contact_information: Dades de contacte del cens + description: Descripció + end_time: La votació finalitza + introductory_image: Imatge de presentació + promoted: Destacada + scope_id: Àmbit + show_check_census: Mostrar la pàgina de "comprovar el cens" + start_time: La votació comença + title: Títol + voting_type: Tipus de votació + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: És necessari tornar a adjuntar l'arxiu + ballot_result: + attributes: + base: + total_count_invalid: El número total de respostes no coincideix amb el desglossament vàlid/blanc/nul. + election: + attributes: + attachment: + needs_to_be_reattached: És necessari tornar a adjuntar l'arxiu + question_result: + attributes: + base: + blank_count_invalid: El nombre total de respostes en blanc no pot ser superior al total de paperetes en blanc. + trustee: + attributes: + name: + cant_be_changed: no es pot canviar + public_key: + cant_be_changed: no es pot canviar + voting: + attributes: + voting_type: + inclusion: "%{value} no es un tipus de votació vàlid" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: El gestor electoral ja és president o gestor del punt de votació. + voting: + different_organization: La votació ha d'estar a la mateixa organització que la participant. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: L'oficial de votació ha d'estar a la mateixa votació que el punt de votació. + models: + decidim/elections/answer: + one: Resposta + other: Respostes + decidim/elections/election: + one: Votació + other: Votacions + decidim/elections/question: + one: Pregunta + other: Preguntes + decidim/voting: + one: Votació + other: Votacions + decidim/votings/census/dataset: + one: Conjunt de dades + other: Conjunts de dades + decidim/votings/census/datum: + one: Dada + other: Dades + decidim/votings/polling_officer: + one: Gestor de mesa + other: Gestors de mesa + decidim/votings/polling_station: + one: Punt de votació + other: Punts de votació + decidim/votings/voting: + one: Votació + other: Votacions + decidim: + admin: + filters: + officers_assigned_eq: + label: Responsables + values: + assigned: Assignat + unassigned: No assignat + role_eq: + label: Rol + values: + manager: Gestor + president: Presidència + unassigned: Sense assignar + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Cerca a %{collection} per nom, correu electrònic o àlies, o bé per punt de votació. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Cerca %{collection} per títol, adreça o bé per responsable, amb el seu nom, correu electrònic o àlies. + signed_eq: + label: Signat + values: + 'false': Signat + 'true': Sense signar + validated_eq: + label: Validat + values: + 'false': Sense validar + 'true': Validada + voting_publications: + create: + error: S'ha produït un error en publicar aquesta votació. + success: La votació s'ha publicat correctament. + destroy: + error: Hi ha hagut un problema en despublicar aquesta votació. + success: La votació s'ha despublicat correctament. + components: + elections: + actions: + vote: Votar + name: Votacions + settings: + global: + announcement: Avís + step: + announcement: Avís + elections: + actions: + confirm_destroy: N'estàs segura? + destroy: Destruir + edit: Editar + feedback: Retorn de la votant + import: Importar propostes com a respostes + manage_answers: Gestionar les respostes + manage_questions: Administrar preguntes + manage_steps: Administrar les fases + new_answer: Afegir resposta + new_election: Afegir elecció + new_question: Afegir pregunta + new_trustee: Afegir garant + preview: Previsualitzar + publish: Publicar + title: Accions + unpublish: Despublicar + admin: + answers: + create: + invalid: Hi ha hagut un problema en crear aquesta resposta. + success: Resposta creada amb èxit. + destroy: + invalid: Hi ha hagut un problema en esborrar aquesta resposta. + success: Resposta eliminada amb èxit. + edit: + title: Editar resposta + update: Actualitzar resposta + index: + invalid_max_selections: Cal %{missing_answers} resposta/es més per a arribar al màxim de seleccions. + title: Respostes + new: + create: Crear resposta + title: Nova resposta + not_selected: No seleccionada + select: + disable: Deseleccionar resposta + enable: Marcar resposta com seleccionada + invalid: S'ha produït un error en seleccionar aquesta resposta. + success: Resposta seleccionada correctament. + selected: Seleccionada + unselect: + invalid: Hi ha hagut un problema en deseleccionar la resposta. + success: Resposta deseleccionada correctament. + update: + invalid: Hi ha hagut un problema en actualitzar aquesta resposta. + success: Resposta actualitzada correctament. + elections: + create: + invalid: S'ha produït un error en crear l'elecció. + success: L'elecció s'ha creat correctament. + destroy: + invalid: S'ha produït un error en eliminar l'elecció. + success: S'ha eliminat l'elecció correctament. + edit: + title: Editar la votació + update: Actualitzar la votació + form: + organization_time_zone: Comprova que la zona horària és correcta a la configuració de l'organització. La configuració actual és %{time_zone} (%{time}). + index: + no_bulletin_board: No hi ha cap servidor de Bulletin Board configurat, el qual és necessari per utilitzar aquest mòdul. Aquesta tasca ha de ser realitzada per la persona administradora del sistema. + title: Votacions + new: + create: Crear una votació + title: Nova votació + publish: + success: La votació s'ha publicat correctament. + unpublish: + success: La votació s'ha despublicat correctament. + update: + invalid: S'ha produït un error en actualitzar aquesta votació. + success: La votació s'ha actualitzat correctament. + exports: + elections: Eleccions + feedback_form_answers: Feedback de les respostes + mailers: + trustee_mailer: + body: + help_html: |- +

    Hola %{user_name},


    +

    T'han afegit per actuar com a garant a algunes eleccions que tindran lloc a %{organization}.


    +

    S'ha creat al teu compte una nova secció anomenada. Des d'aquesta, podràs realitzar les tasques requerides. De moment, si us plau, genera les teves claus d'identificació.


    + subject: Se t'ha afegit com a garant de %{resource_name} + trustee_zone: Portant a l'espai de garants + menu: + trustees: Garants + models: + answer: + name: Resposta + proposals_imports: + create: + invalid: S'ha produït un error en importar les propostes a respostes. + success: "S'ha importat amb èxit %{number} propostes a respostes." + new: + create: Importa propostes a respostes + no_components: No hi ha cap altre component de propostes en aquest espai participatiu per importar les propostes a respostes. + select_component: Selecciona un component + title: Importar propostes + questions: + create: + election_started: L'elecció ja ha començat. + invalid: S'ha produït un error en crear aquesta pregunta. + success: S'ha creat una pregunta correctament. + destroy: + invalid: S'ha produït un error en esborrar aquesta votació. + success: S'ha eliminat la pregunta correctament. + edit: + title: Editar pregunta + update: Actualitzar la pregunta + index: + title: Preguntes + new: + create: Crear una pregunta + title: Nova pregunta + update: + invalid: S'ha produït un error en actualitzar aquesta pregunta. + success: La pregunta s'ha actualitzat correctament. + steps: + create_election: + census: Cens + errors: + census_codes_generated: Els codis d'accés al cens no s'han pogut generar. + census_frozen: Els codis d'accés al cens no s'han pogut exportar. + census_uploaded: No s'ha pujat el cens per a aquestes eleccions. + component_published: L'elecció no està publicada. + fix_it_text: Arreglar-ho + max_selections: Les preguntes no tenen un valor correcte per quantitat de respostes + minimum_answers: Les preguntes han de tenir almenys dues respostes. + minimum_questions: L'elecció ha de tenir almenys una pregunta. + published: L'elecció no està publicada. + time_before: L'hora d'inici és en menys de %{hours} hores abans de que comenci l'elecció. + trustees_number: L'espai de participació ha de tenir almenys %{number} garants amb clau pública. + invalid: S'ha produït un error en configurar aquesta elecció + no_trustees: No hi ha garants configurats per aquest espai de participació + not_used_trustee: "(no utilitzat)" + public_key: + 'false': no té una clau pública + 'true': té una clau pública + requirements: + census_codes_generated: S'han generat els codis d'accés al cens. + census_frozen: S'han exportat els codis per al cens i el cens ha quedat congelat. + census_uploaded: El cens s'ha pujat. + component_published: L'elecció està publicada. + max_selections: Totes les preguntes tenen un valor correcte per màxim de respostes. + minimum_answers: Cada pregunta té almenys 2 respostes. + minimum_questions: L'elecció té almenys 1 pregunta. + published: L'elecció està publicada. + time_before: La configuració s'està fent almenys %{hours} hores abans de que comenci l'elecció. + trustees_number: L'espai de participació té almenys %{number} garants amb clau pública. + submit: Configurar elecció + success: L'elecció s'ha enviat amb èxit al "Bulletin Board". + technical_configuration: + authority_name: "Nom de l'autoritat: %{value}" + bulletin_board_server: "Servidor del butlletí: %{value}" + scheme_name: "Nom de l'esquema: %{value}" + title: Veure la informació tècnica + title: Configurar elecció + trustees: Garants de l'elecció + created: + invalid: S'ha produït un error en començar la cerimònia de claus. + submit: Començar la cerimònia de claus + success: La sol·licitud per iniciar la cerimònia de claus s'ha enviat correctament al "Bulletin Board". + title: Elecció creada + trustees: Garants + key_ceremony: + continue: Continuar + title: Cerimònia de claus + key_ceremony_ended: + errors: + time_before: L'elecció està llesta per començar. Has d'esperar fins a %{hours} hores abans de l'hora d'inici (%{start_time}) per iniciar el període de votació. + invalid: S'ha produït un error en iniciar el període de votació. + requirements: + time_before: L'elecció començarà aviat. Pots iniciar el període de votació manualment, o començarà automàticament abans de l'hora d'inici, a les %{start_time}. + submit: Iniciar període de votació + success: La sol·licitud per iniciar el període de votació s'ha enviat correctament al "Bulletin Board". + title: A punt per començar + processing: Processant... + results_published: + answer: Resposta + not_selected: No seleccionat + question: Pregunta + result: Resultat + selected: Seleccionat + submit: Enviar + title: Resultats publicats + tally_ended: + answer: Resposta + not_selected: No seleccionat + question: Pregunta + result: Resultat + selected: Seleccionat + submit: Publicar resultats + success: La sol·licitud per publicar els resultats s'ha enviat correctament al "Bulletin Board". + title: Resultats calculats + tally_started: + continue: Continuar + invalid: S'ha produït un error en notificar l'absència de la garant. + mark_as_missing: Marcar com absent + mark_as_missing_description: Totes les garants haurien de participar en aquest procés, però si una garant no en pot prendre part, la pots marcar com a absent. + success: La notificació d'absència de la garant s'ha enviat amb èxit al "Bulletin Board". + tally_completion: El procés es completarà quan totes les garants estiguin actives o marcades com a absents. Fan falta com un mínim de %{quorum} garant per a completar el procés. + title: Procés de recompte + undo_mark_as_missing: Una garant marcada com a absent per error podrà participar abans de la finalització del procés. Pot actuar com de costum i la seva absència serà ignorada. + vote: + errors: + time_after: L'elecció està en curs encara. Has d'esperar fins l'hora de fi (%{end_time}) per finalitzar el període de votació. + invalid: S'ha produït un error en finalitzar el període de votació. + requirements: + time_after: L'elecció s'ha acabat. Pots finalitzar el període de votació manualment, o finalitzarà automàticament en breus instants. + submit: Finalitzar període de votació + success: La sol·licitud per finalitzar el període de votació s'ha enviat correctament al "Bulletin Board". + title: Període de votació + vote_ended: + invalid: S'ha produït un error en començar el recompte. + submit: Començar recompte + success: La sol·licitud per iniciar el recompte s'ha enviat correctament al "Bulletin Board". + text: La votació ha finalitzat. Pots iniciar el recompte ara. + title: Període de votació finalitzat + vote_stats: + no_vote_statistics_yet: Les estadístiques de votacions encara no estan disponibles + title: Estadístiques de les votacions + voters: Votants + votes: Vots + trustees_participatory_spaces: + actions: + disable: Desactivar + enable: Considerar + create: + exists: Existeix garant per aquest espai participatiu. + invalid: S'ha produït un error en crear una garant. + success: Garant creada correctament. + delete: + invalid: S'ha produït un error en eliminar aquesta garant. + success: Garant eliminada correctament. + form: + select_user: Seleccionar usuari + index: + title: Garants + new: + create: Crear garant + title: Nou garant + update: + invalid: S'ha produït un error en actualitzar a %{trustee} com a garant. + success: Garant %{trustee} actualitzada correctament. + admin_log: + election: + create: "%{user_name} ha creat l'elecció %{resource_name} a %{space_name}" + delete: "%{user_name} ha eliminat l'elecció %{resource_name} a %{space_name}" + end_vote: "%{user_name} ha tancat el període de votació per a l'elecció %{resource_name} a %{space_name}" + publish: "%{user_name} ha publicat l'elecció %{resource_name} a %{space_name}" + publish_results: "%{user_name} ha publicat els resultats de l'elecció %{resource_name} a %{space_name}" + report_missing_trustee: "%{user_name} ha notificat %{trustee_name} com a absent durant el recompte de les eleccions %{resource_name} a %{space_name}" + setup: "%{user_name} ha creat l'elecció %{resource_name} a %{space_name}" + start_key_ceremony: "%{user_name} ha iniciat la cerimònia de claus per l'elecció %{resource_name} a %{space_name}" + start_tally: "%{user_name} ha iniciat el recompte de l'elecció %{resource_name} a %{space_name}" + start_vote: "%{user_name} ha iniciat el període de votació per a l'elecció %{resource_name} a %{space_name}" + unpublish: "%{user_name} ha despublicat l'elecció %{resource_name} a %{space_name}" + update: "%{user_name} ha actualitzat l'elecció %{resource_name} a %{space_name}" + trustee: + create: "%{user_name} ha assignat a la usuària %{trustee_user} com a garant" + connection: + failed: + modal: + close: Tancar + communication_lost: Malauradament, sembla que la comunicació amb el servidor de votació (Bulletin Board) s'ha perdut.
    Pot ser que la connexió a Internet estigui avariada o que el servidor de destí estigui massa ocupat.
    Pots provar-ho més tard o posar-te en contacte amb el servei d'assistència si aquest problema persisteix. + generic_error: Malauradament, s'ha produït un error desconegut. És probable que el teu navegador no estigui suportat o que estiguis fent servir el mode "d'incògnit" o "privat" que no són compatibles. + title: Alguna cosa ha anat malament + election_m: + badge_name: + finished: Acabada + ongoing: Activa + upcoming: Properes + end_date: Finalitza + footer: + remaining_time: + one: "queda %{count} hora %{minutes} minuts per votar." + other: "queden %{count} hores %{minutes} minuts per votar." + view: Veure + vote: Votar + label: + date: Dates + questions: Preguntes %{count} + start_date: Inici + unspecified: Sense especificar + elections: + count: + elections_count: + one: "%{count} votació" + other: "%{count} votacions" + election_log: + chained_hash: El Hash encadenat d'aquest missatge + complete: Completar + creation_description: + complete: L'elecció s'ha creat i s'ha configurat amb èxit al Bulletin Board. + not_created: L'elecció no s'ha creat encara. + creation_title: Elecció creada + description: Aquest és el registre de l'elecció on pots comprovar l'estat de cada pas, per exemple, quan es va crear l'elecció, si el procés de recompte s'ha completat i quan s'ha tancat l'elecció. + download: Descarregar + key_ceremony_description: + complete: La cerimònia de claus s'ha completat. Tots els garants tenen claus vàlides i han descarregat les claus de seguretat necessàries. + not_started: La cerimònia de claus no ha començat encara. + started: La cerimònia de claus ha començat però no s'ha completat encara. + key_ceremony_title: Cerimònia de claus + not_available: Encara no està disponible + not_created: No s'ha creat + not_ready: No preparat + not_started: No s'ha iniciat + published: Publicat + results_description: + not_published: Els resultats no s'han publicat encara. + published: Els resultats s'han publicat. + results_title: Resultats + started: Iniciat + tally_description: + finished: El procés de recompte ha finalitzat. + not_started: El procés de recompte no ha començat encara. + started: El procés de recompte ha començat. + tally_title: Procés de recompte + title: Registre de l'elecció + unpublished: Despublicada + verifiable_results: + checksum: 'Suma de comprovació SHA256 de l''arxiu:' + description: + not_ready: L'arxiu verificable de l'elecció i la suma de comprovació SHA256 encara no estan disponibles. Quan es publiquin els resultats, podràs verificar aquesta elecció. + ready: 'Aquí tens l''opció de verificar l''elecció. En primer lloc, has de descarregar l''arxiu i assegurar-te que no s''ha corromput. Per a això, executa el següent comando i comprova que la sortida coincideix amb la suma de comprovació:' + how_to_verify: 'Una vegada que hagis descarregat l''arxiu i t''hagis assegurat que està bé, pots procedir a executar el verificador universal. Clona aquest repositori i, des de la carpeta arrel, executa el següent comando:' + title: Verificar resultats de l'elecció + verifiable_file: 'Arxiu verificable de l''elecció:' + verify: Verificar elecció + vote_description: + finished: El procés de votació ha finalitzat. + not_started: El procés de votació no ha començat encara. + started: El procés de votació ha començat. + vote_title: Procés de votació + filters: + active: Activa + all: Totes + date: Data + finished: Finalizada + upcoming: Properes + preview: + available_answers: 'Respostes disponibles:' + description: 'Aquestes són les preguntes que trobaràs al procés de votació:' + title: Preguntes de l'elecció + results: + description: 'Aquests són els resultats de la votació, per a cada pregunta:' + percentage: "%{count}%" + selected: Seleccionat + title: Resultats de l'elecció + votes: + one: "%{count} vot" + other: "%{count} vots" + show: + action_button: + change_vote: Canvia el teu vot + vote: Començar a votar + vote_again: Votar de nou + callout: + already_voted: Ja has votat en aquesta elecció. Pots canviar el teu vot o verificar-lo. + pending_vote: S'està dipositant el vot al servidor. + vote_rejected: No ha estat possible verificar el teu vot. Si us plau, fes-ho de nou. + election_log: Registre de l'elecció + preview: Previsualitzar + verify: + already_voted: Ja has votat? + verify_here: Comprova el teu vot aquí. + will_verify: Podràs verificar el teu vot una vegada comenci l'elecció. + voting_period_status: + finished: La votació va començar el %{start_time} i va acabar el %{end_time} + ongoing: 'Votació activa fins: %{end_time}' + upcoming: La votació comença el %{start_time} + feedback: + answer: + invalid: S'ha produït un error en enviar els teus comentaris. + spam_detected: Hi ha hagut un problema responent el formulari. Potser has anat massa ràpid, ho pots tornar a intentar? + success: Comentaris enviats amb èxit. + models: + answer: + fields: + proposals: Propostes + selected: Seleccionat + title: Títol + votes: Vots + election: + fields: + bb_status: Estat del tBulletin Board + end_time: Finalitza el + start_time: Comença el + title: Títol + verifiable_results_file_hash: Comprovació de suma SHA256 del fitxer + verifiable_results_file_url: Fitxer de verificació de l'elecció + question: + fields: + answers: Respostes + max_selections: Número màxim d'elements a seleccionar + title: Títol + trustees_participatory_space: + fields: + considered: considerat + email: Correu electrònic + inactive: inactiu + name: Nom + notification: Notificació enviada a les + public_key: Clau pública + status: Estat + orders: + label: Ordena votacions per + older: Més antiga + recent: Recent + trustee_zone: + elections: + backup_modal: + description: Aquesta elecció s'està creant al Bulletin Board. És molt important que cada garant que hi participa creï una còpia de seguretat d'aquestes claus i les guardi en un lloc segur. Després d'això, el procés continuarà. + download_election_keys: Descarregar claus + title: Còpia de seguretat de les claus per l'elecció %{election} + key_ceremony_steps: + back: Tornar + description: Aquesta elecció s'està creant al Bulletin Board. Per completar aquest procés, es necessita la teva participació com a garant. + keys: + create_election: Generació de claus + key_ceremony: + joint_election_key: Generació conjunta de claus + step_1: Publicant claus + list: + status: Estat + task: Tasca + process_warning: Un cop ha començat el procés, no hauries de sortir d'aquesta pàgina fins que aquest acabi. Trigarà varis minuts, ja que tots els garants han de connectar-se al mateix temps per completar-lo. + start: Començar + status: + completed: Completada + pending: Pendent + processing: Processant + title: Crear claus per l'elecció %{election} + restore_modal: + description: El Bulletin Board té informació teva com a garant d'aquesta elecció. Per continuar amb el procés, primer puja l'arxiu generat com a còpia de seguretat durant la sessió anterior. + title: Restaurar claus per l'elecció %{election} + upload_election_keys: Pujar claus de l'elecció + tally_started_steps: + back: Enrere + description: Els resultats d'aquesta elecció s'estan computant al Bulletin Board. Per completar aquest procés, és necessària la teva participació com a garant. + keys: + end_tally: Recompte finalitzat + tally: + cast: Enviament del recompte + share: Compartició del recompte + list: + status: Estat + task: Tasca + process_warning: Un cop ha començat el procés, no hauries de sortir d'aquesta pàgina fins que aquest acabi. Trigarà alguns minuts, ja que totes les garants han de connectar-se al mateix temps per completar-lo. + start: Començar + status: + completed: Completat + pending: Pendent + processing: Processant + title: Recompte per %{election} + update: + error: L'estat de l'elecció no s'havia actualitzat. + success: 'L''estat de l''elecció és: %{status}.' + menu: + trustee_zone: Zona de garants + no_bulletin_board: + body: Es requereix disposar d'un Bulletin Board configurat per a aquesta secció. Contacta amb l'administradora per més detalls. + title: Ho sentim, el Bulletin Board no s'ha configurat encara. + trustees: + show: + elections: + list: + action_required: + 'false': 'No' + name: Acció requerida? + 'true': Realitzar acció + bb_status: Estat + election: Elecció + voting_period: Període de votació + no_elections: Actualment, no tens assignada cap tasca com a garant. Rebràs una notificació quan sigui el moment d'actuar a les diferents fases. + title: Eleccions + identification_keys: + cancel: Cancel·lar + generate: Generar claus d'identificació + generate_error: Hi ha hagut un error generant les claus d'identificació. + generate_legend: Has de generar un parell de claus d'identificació per participar en les eleccions com a garant. + generate_legend_1: Després de prémer el botó has de descarregar l'arxiu amb les claus d'identificació generades. + generate_legend_2: Copia l'arxiu descarregat a un dispositiu USB net + generate_legend_3: Assegura't que el teu equip no tingui una còpia de l'arxiu (per exemple, comprova les carpetes de Baixades i d'Escriptori). + generate_legend_4: Fes una altra copia de l'arxiu en un dispositiu extern diferent i guarda'l a un lloc molt segur. + submit: Enviar + submit_legend: Després de seguir tots els passos explicats anteriorment, completa el procés enviant la clau d'identificació pública al servidor. + submit_title: Enviar la clau d'identificació pública + title: Claus d'identificació del garant + upload: Puja les teves claus d'identificació + upload_error: + invalid_format: L'arxiu pujat no conté cap clau d'identificació. + invalid_key: Les claus d'identificació a l'arxiu pujat no es poden carregar. + invalid_public_key: Les claus d'identificació a l'arxiu pujat no coincideixen amb la clau d'identificació pública emmagatzemada. + upload_legend: Al servidor hi consten les teves claus d'identificació públiques, però el teu navegador encara no les té. Cal que importis l'arxiu amb les teves claus d'identificació al teu ordinador des de la còpia de seguretat que has creat després de generar-les. + not_supported_browser_description: Sembla que estàs usant un navegador web que no pot ser utilitzat per a actuar com a garant. Assegura't que estàs usant la versió més recent del teu navegador, o intenta fer servir qualsevol altre dels navegadors més populars per a poder completar les teves tasques com a garant. + not_supported_browser_title: Actualitza el navegador per a actuar com a garant + safari_warning_description: Sembla que estàs fent servir Safari, que no està suportat per actuar com a garant o per xifrar un vot (això és degut a les restriccions de memòria que hi imposa Apple). Això es podria resoldre en el futur mitjançant un canvi de política per part d'Apple o la futura optimització de Decidim Elecciones. Mentrestant, si us plau, utilitzeu un altre navegador. + safari_warning_title: Navegador Safari detectat + trustee_role_description: + with_keys: Has estat assignada com a garant en algunes de les eleccions celebrades en aquesta plataforma. + without_keys: Has estat assignada per actuar com a Garant. Si us plau, genera i puja les teves claus d'identificació. + update: + success: La teva clau pública d'identificació va ser guardada amb èxit. + votes: + ballot_decision: + audit: ( Auditar papereta ) + back: Començar de nou el procés de votació + ballot_hash: 'L''identificador de la teva papereta és:' + cast: Dipositar la papereta per a finalitzar el teu vot + description_html: Aquí tens les opcions per emetre la teva butlleta perquè sigui comptada correctament o pots auditar que la teva butlleta ha estat xifrada correctament. Si vols auditar la votació, si us plau, llegeix les instruccions sobre com procedir. + header: 'La papereta està xifrada: pots dipositar-la o auditar-la' + casting: + header: El vot s'està dipositant... + text: La papereta s'està dipositant a l'urna. + confirm: + answer_number: resposta %{number} + confirm: Confirmar + edit: editar + header: Confirmar el teu vot + intro: Aquí trobarás un resum del vot que estàs a punt d'emetre.
    Si us plau, confirma el teu vot o edita les teves respostes. + nota_option: En blanc + confirmed: + back: Tornar a les votacions + experience: Com valores l'experiència? + feedback: Dona'ns la teva opinió + header: Vot confirmat + lead: El teu vot ha estat emès! + text: 'Pots comprovar que el teu vot s''ha afegit correctament a l''urna amb el següent identificador: ' + verify_link: Per comprovar-ho, copia l'identificador i enganxa'l a la pàgina de verificació de vot + create: + error: Hi ha hagut un problema en emetre el vot. Prova-ho de nou. + encrypting: + header: El vot s'està xifrant... + text: La teva papereta s'està xifrant per a garantir el secret de vot. + failed: + header: Vot fallit + lead: El teu vot no s'ha enviat! + text: Alguna cosa ha anat malament. Si us plau, torna-ho a provar. + try_again: Prova-ho de nou + header: + ballot_decision: Emetre + confirm: Confirma el teu vot + election: Elecció + register: Registra't + vote_for: Vota per %{title} + messages: + invalid_token: La sessió de la cabina de votació no és vàlida. Prova de votar de nou. + not_allowed: No pots votar en aquesta votació en aquest moment. + modal: + close: Tancar + proposal_header: 'Propostes:' + new: + answer_choices: Pots seleccionar fins a %{choices} respostes + more_information: Més informació + nota_option: En blanc / Cap de les anteriors + preview_alert: Aquesta és una vista prèvia de la cabina de votació. + question_steps: Pregunta %{current_step} de %{total_steps} + selections: "Seleccionada
    %{selected} de %{max_selections}" + onboarding_modal: + create_account: Crea un compte + description: Vols crear un nou compte? Podràs participar als processos i ser part activa de l'organització. + no_account: No, gràcies. + title: Ets nova a la plataforma? + update: + error: Hi ha hagut un error en actualitzar el vot. Prova-ho de nou. + verify: + content: + heading: Verifica el teu vot + info: Aquest verificador comprova que el teu vot, identificat amb una cadena de text encriptada, s'ha emès correctament i està dins de l'urna. + error: + header: Vot no localitzat! + info: El codi del vot no s'ha trobat a l'urna %{link}, intenta-ho de nou. + form: + back: Tornar a la plataforma + submit: Comprovar + vote_identifier: 'Codi identificador:' + vote_identifier_help: Aquest és l'indicador que se't va donar després d'emetre el teu vot (no és el codi per a entrar a la cabina de votació). + header: + title: Verifica el teu vot + success: + header: Vot localitzat! + info: El teu vot encriptat es troba a l'urna %{link}. + voting_step: + back: Enrere + continue: Següent + warnings: + empty_filters: No hi ha cap elecció amb aquest criteri. + no_elections: No hi ha cap elecció programada. + no_scheduled_elections: Actualment no hi ha votacions programades, però pots veure un llistat de les anteriors. + events: + elections: + election_published: + email_intro: 'La votació %{resource_title} ja està activa a %{participatory_space_title}. Pots veure-la des d''aquesta pàgina:' + email_outro: Has rebut aquesta notificació perquè estàs seguint l'espai %{participatory_space_title}. Pots deixar de rebre notificacions seguint l'enllaç anterior. + email_subject: La votació %{resource_title} ja està activa a %{participatory_space_title}. + notification_title: La votació %{resource_title} a %{participatory_space_title} ja està activa. + trustees: + new_election: + email_intro: Has estat afegit com a garant de l'elecció %{resource_title}. + email_outro: Has rebut aquesta notificació perquè t'han afegit com a garant de l'elecció %{resource_title}. + email_subject: Ets garant de l'elecció %{resource_title}. + notification_title: Has estat assignada per actuar com a Garant%{resource_title}. Si us plau, realitza la cerimònia de claus per a posar en marxa l'elecció. + new_trustee: + email_intro: Una administradora t'ha afegit com a garant de %{resource_name}. Hauries de crear la teva clau pública a la teva zona de garants + email_outro: Has rebut aquesta notificació perquè t'han afegit com a garant de %{resource_name}. + email_subject: Ets garant de %{resource_name}. + notification_title: T'han afegit per actuar com a garant a %{resource_name} per algunes eleccions que tindran lloc a aquesta plataforma.
    Caldrà que duguis a terme les tasques requerides. De moment, si us plau genera les teves claus d'identificació. + start_tally: + email_intro: El període de votació per a les eleccions %{resource_title} ha finalitzat. Ara, si us plau, realitza el recompte de les eleccions per publicar els resultats definitius. + email_outro: Has rebut aquesta notificació perquè ets una de les garants a l'elecció %{resource_title}. + email_subject: El procés de recompte per a les eleccions %{resource_title} ha començat. + notification_title: El període de votació per a les eleccions %{resource_title} ha finalitzat. Ara, si us plau, realitza el recompte de les eleccions per publicar el resultat final. + votes: + accepted_votes: + email_intro: 'El teu vot s''ha acceptat! Utilitzant el comprovant de vot: %{encrypted_vote_hash}, pots verificar-lo aquí.' + email_outro: Has rebut aquesta notificació perquè has votat a l'elecció %{resource_name}. + email_subject: El teu vot a %{resource_name} s'ha acceptat. + notification_title: 'El teu vot s''ha acceptat. Verifica''l aquí utilitzant el comprovant de vot: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: T'han assignat com %{role} del punt de votació %{polling_station_name} a %{resource_title}. Pots gestionar el punt de votació accedint a la Zona de gestors de mesa. + email_outro: Has rebut aquesta notificació perquè t'han assignat com %{role} de %{polling_station_name}. + email_subject: Ets %{role} del punt de votació %{polling_station_name}. + notification_title: Ets %{role} del punt de votació %{polling_station_name} a la votació %{resource_title}. + send_access_code: + instruction: 'Aquí tens el codi d''accés que havies demanat: %{access_code}. Amb això ja pots participar a %{voting}.' + subject: El teu codi d'accés per participar a %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Una votació és un espai que us permet fer una pregunta clara al conjunt de membres d'una organització, fer una crida a participar en la votació, generar i ordenar el debat a favor o en contra d'una resposta. Quan arriba la data de la votació, podeu votar i publicar-ne el resultat.

    Les votacions poden ser de pràcticament qualsevol aspecte que afecti una organització. Alguns exemples serien: canviar el nom o el logotip de l'organització oferint diverses alternatives, decidir si passar a formar part d'una organització més gran o no, validar o refusar un nou pla estratègic o el resultat d'un grup de treball, o definir si els càrrecs haurien de tenir una durada màxima d'un, dos o tres mandats.

    " + page: "

    Una votació és un espai que us permet fer una pregunta clara al conjunt de membres d'una organització, fer una crida a participar en la votació, generar i ordenar el debat a favor o en contra d'una resposta. Quan arriba la data de la votació, podeu votar i publicar-ne el resultat.

    Les votacions poden ser de pràcticament qualsevol aspecte que afecti una organització. Alguns exemples serien: canviar el nom o el logotip de l'organització oferint diverses alternatives, decidir si passar a formar part d'una organització més gran o no, validar o refusar un nou pla estratègic o el resultat d'un grup de treball, o definir si els càrrecs haurien de tenir una durada màxima d'un, dos o tres mandats.

    " + title: Què són les votacions? + menu: + votings: Votacions + participatory_spaces: + related_elections: + see_all: Veure totes les eleccions + statistics: + elections_count: Eleccions + votings_count: Votacions + votings: + admin: + ballot_styles: + create: + error: Hi ha hagut un error en crear l'estil de papereta. + success: L'estil de papereta s'ha creat correctament. + destroy: + invalid: Hi ha hagut un error en esborrar l'estil de papereta. + success: S'ha esborrat correctament l'estil de papereta. + edit: + title: Editar estil de papereta + update: Actualitzar + form: + code_help: 'Pista: El codi és l''enllaç entre el cens i l''estil de papereta. Quan puges les dades del cens, a cada entrada s''assigna un estil de papereta que coincideixi amb el codi.' + election: Elecció + questions: Preguntes per a l'estil de papereta + questions_help: 'Pista: selecciona les preguntes en el component d''elecció per presentar-les a les votants assignades a aquest estil de papereta.' + index: + actions: + confirm_destroy: N'estàs segura? + destroy: Esborrar + edit: Editar + new: Afegir estil de papereta + title: Accions + associated_census_data: Entrades del cens associades + explanation_callout: Un estil de papereta especifica quines són les preguntes que veuran les votants a la cabina. Així, en aquesta elecció pots escollir-ne quines formaran part de la papereta. El codi d'estil de papereta normalment es fa servir per fer coincidir a una votant del cens amb la papereta que se li ha de mostrar a la cabina. No creïs cap estil de papereta si vols que sempre es mostrin totes les preguntes. + title: Estils de papereta + new: + create: Crear + title: Crear un estil de papereta + update: + invalid: Hi ha hagut un error en actualitzar l'estil de papereta. + success: L'estil de papereta s'ha actualitzat correctament. + content_blocks: + attachments_and_folders: + name: Arxius i carpetes de la votació + header: + name: Capçalera de la votació + highlighted_votings: + max_results: Quantitat màxima d'elements per mostrar + html_block_1: + name: Bloc html 1 de la votació + html_block_2: + name: Bloc html 2 de la votació + html_block_3: + name: Bloc html 3 de la votació + main_data: + name: Títol i descripció + metrics: + name: Mètriques de la votació + polling_stations: + name: Punts de votació + related_elections: + name: Eleccions + stats: + name: Estadístiques de la votació + timeline: + name: Calendari de la votació + index: + published: Publicada + unpublished: Despublicada + menu: + votings: Votacions + votings_submenu: + attachment_collections: Carpetes + attachment_files: Arxius + attachments: Adjunts + ballot_styles: Estils de papereta + census: Cens + components: Components + info: Sobre aquesta votació + landing_page: Pàgina d'inici + monitoring_committee: Comitè de seguiment + monitoring_committee_election_results: Valida els resultats + monitoring_committee_members: Membres + monitoring_committee_polling_station_closures: Valida els certificats + monitoring_committee_verify_elections: Verificar eleccions + polling_officers: Gestors de mesa + polling_stations: Punts de votació + see_voting: Veure la votació + models: + ballot_style: + fields: + code: Codi + monitoring_committee_member: + fields: + email: Correu electrònic + name: Nom + polling_officer: + fields: + email: Correu electrònic + name: Nom + polling_station: Punt de votació (rol) + polling_station: + fields: + address: Adreça + polling_station_managers: Administradors + polling_station_president: President + title: Títol + voting: + fields: + created_at: Creada el + published: Publicada + title: Títol + monitoring_committee_election_results: + actions: + title: Accions + view: Mostrar + index: + title: Escull l'elecció per veure-hi els resultats + results: + bulletin_board: Bulletin Board + election_totals: Totals de l'elecció + polling_stations: Punts de votació + result_types: + blank_answers: Respostes en blanc + blank_ballots: Paperetes en blanc + null_ballots: Paperetes nul·les + total_ballots: Total de paperetes + valid_ballots: Paperetes vàlides + selected: Seleccionat + title: Resultats de l'elecció %{election_title} + totals: Totals + show: + change_election: Canviar elecció + publish_results: Publicar resultats + publishing: Publicant resultats... + update: + invalid: Hi ha hagut un problema en publicar els resultats. + rejected: El Bulletin Board ha rebutjat la publicació dels resultats. Prova-ho de nou, o contacta amb l'administrador del sistema. + success: Els resultats s'han publicat correctament. + monitoring_committee_members: + create: + invalid: Hi ha hagut un problema en crear aquesta membre de la comissió de seguiment. + success: Membre de la comissió de seguiment creada amb èxit. + destroy: + invalid: Hi ha hagut un problema en eliminar aquesta membre de la comissió de seguiment. + success: Membre de la comissió de seguiment eliminada amb èxit. + form: + existing_user: Participant existent + non_user: Convidar una nova participant + select_user: Cercar per correu electrònic, nom o àlies + user_type: Tipus de participant + index: + title: Comitè de seguiment + new: + create: Crear + title: Crear membre del comitè de seguiment + monitoring_committee_polling_station_closures: + actions: + title: Accions + validate: Validar + view: Mostrar + closures: + change_election: Canviar elecció + signed: Signat? + title: Punts de votació de l'elecció %{election_title} + validated: Validat? + edit: + change_polling_station: Tornar als punts de votació + monitoring_committee_notes: Observacions + monitoring_committee_notes_placeholder: Informa de qualsevol incidència aquí + title: Els resultats de l'elecció %{election_title} en el punt de votació %{polling_station_title} + elections: + title: Escull una elecció per validar + show: + change_polling_station: Tornar als punts de votació + monitoring_committee_notes: Annotacions del Comitè de Seguiment + validate: + error: Hi ha hagut un problema en validar el tancament. + success: El tancament s'ha validat correctament. + monitoring_committee_verify_elections: + index: + download: Descarregar + how_to_checksum: 'Per a assegurar-se que l''arxiu descarregat no ha estat danyat o manipulat durant el procés de descàrrega, executa el següent comando en la teva consola i comprova que la sortida coincideixi amb la suma de verificació reportada a dalt:' + how_to_download: Per verificar l'elecció, descarrega el fitxer verificable de la taula de dalt. + how_to_run_verifier: 'Una vegada que has descarregat el fitxer i te''n has assegurat que és correcte, pots executar-hi el verificador universal. Clona el repositori i, des de la carpeta d''arrel, executa la següent ordre:' + how_to_title: Com verificar la validesa d'una elecció + not_available: Encara no està disponible + title: Eleccions + polling_officers: + create: + invalid: S'ha produït un error en crear aquesta oficial de votació. + success: Oficial de votació creada amb èxit. + destroy: + invalid: S'ha produït un error en eliminar aquesta oficial de votació. + success: Oficial de votació eliminada amb èxit. + form: + existing_user: Participant existent + non_user: Convidar una nova participant + select_user: Cercar per correu electrònic, nom o àlies + user_type: Tipus de participant + index: + role_manager: administrador + role_president: president + title: Gestors de mesa + new: + create: Crear + title: Crear gestor de mesa + polling_officers_picker: + choose_polling_officers: Escollir gestors de mesa + no_polling_officers: No hi ha gestores de mesa que coincideixin amb els teus criteris de cerca o no existeix cap gestora de mesa. + polling_stations: + create: + invalid: S'ha produït un error en crear aquest punt de votació. + success: Punt de votació creat correctament. + destroy: + invalid: S'ha produït un error en eliminar aquest punt de votació. + success: Punt de votació eliminat correctament. + edit: + title: Editar punt de votació + update: Actualitzar punt de votació + form: + address_help: 'Adreça: que farà servir per Geocoder per a trobar la localització' + location_help: 'Ubicació: missatge dirigit als votants amb el lloc exacte del punt de votació' + location_hints_help: 'Detalls d''ubicació: informació addicional. Exemple: la planta de l''edifici on està situat el punt de votació.' + polling_station_managers_help: 'Gestors de mesa: els tècnics que actuaran com a responsables del punt de votació. Assegura''t que han estat creats en Gestors de mesa i que no estan ja assignats a un altre punt de votació' + polling_station_president_help: 'Presidència de mesa: oficials que faran les funcions de presidència al punt de votació. Assegura''t que estan habilitades com a gestores de mesa i que no estan ja assignades a un altre punt de votació.' + select_president: Selecciona un gestor com a president del punt de votació + index: + title: Punts de votació + new: + create: Crear + title: Crear punt de votació + update: + invalid: S'ha produït un error en actualitzar aquest punt de votació. + success: Punt de votació actualitzat correctament. + titles: + votings: Votacions + votings: + actions: + confirm_destroy: N'estàs segura? + destroy: Eliminar + new_voting: Nou espai de votació + create: + invalid: S'ha produït un error en crear aquesta votació. + success: Votació creada correctament. + edit: + add_election_component: No tens cap elecció configurada per a aquesta votació. Si us plau, afegeix-la a la secció de components. + assign_missing_officers: Hi ha punts de votació sense presidència i/o gestores. Si us plau, assigna-les des de la secció de punts de votació. + update: Actualitzar + form: + banner_image: Imatge de capçalera + census_contact_information: Dades de contacte del cens + census_contact_information_help: La informació de contacte és per a participants que vulguin avisar d'incidències amb el cens. Pot ser una adreça de correu electrònic, un formulari de contacte ubicat en un altre lloc web, una enquesta de Decidim per a visitants, etc. + introductory_image: Imatge de presentació + promoted: Destacada + select_a_voting_type: Si us plau selecciona un tipus de votació + show_check_census_help: Especifica si es mostra l'enllaç "Puc votar?" al menú públic de votacions. + slug: Nom curt d'URL + slug_help_html: 'Els noms curts d''URL s''utilitzen per generar els URL que apunten a aquesta votació. Només accepta lletres, números i guions, i ha de començar amb una lletra. Exemple: %{url}' + title: Títol + voting_type: + hybrid: Híbrida + in_person: Presencial + online: En línia + voting_type_label: Tipus de votació + new: + create: Crear + title: Nova votació + update: + invalid: S'ha produït un error en actualitzar aquesta votació. + success: La votació s'ha actualitzat correctament. + admin_log: + ballot_style: + create: "%{user_name} ha creat un estil de papereta amb codi %{ballot_style_code} a l'espai %{space_name}" + delete: "%{user_name} ha eliminat l'estil de papereta amb codi %{ballot_style_code} a l'espai %{space_name}" + update: "%{user_name} ha actualitzat l'estil de papereta amb codi %{ballot_style_code} a l'espai %{space_name}" + census: + create: "%{user_name} ha creat el cens per l'espai %{space_name}" + delete: "%{user_name} ha eliminat el cens per l'espai %{space_name}" + update: "%{user_name} ha actualitzat el cens per l'espai %{space_name}" + monitoring_committee_member: + create: "%{user_name} ha assignat a la usuària %{monitoring_committee_member_user} com a membre de la comissió de seguiment a l'espai %{space_name}" + delete: "%{user_name} desassignat a la usuària %{monitoring_committee_member_user} com a membre de la comissió de seguiment a l'espai %{space_name}" + polling_officer: + create: "%{user_name} ha assignat a la usuària %{polling_officer_user} com a gestora de mesa a l'espai %{space_name}" + delete: "%{user_name} ha desassignat a la usuària %{polling_officer_user} com a gestora de mesa a l'espai %{space_name}" + polling_station: + create: "%{user_name} ha creat el punt de votació %{resource_name} a l'espai %{space_name}" + delete: "%{user_name} ha eliminat el punt de votació %{resource_name} a l'espai %{space_name}" + update: "%{user_name} ha actualitzat el punt de votació %{resource_name} a l'espai %{space_name}" + voting: + create: "%{user_name} ha creat la votació %{resource_name}" + publish: "%{user_name} ha publicat la votació %{resource_name}" + unpublish: "%{user_name} ha despublicat la votació %{resource_name}" + census: + admin: + census: + create: + invalid: Hi ha hagut un error en pujar el cens, prova-ho de nou més tard. + invalid_csv_header: Les capçaleres al CSV manquen o no són correctes. Si us plau, llegeix atentament les instruccions. + creating_data: + info_message: "Si us plau espera, processades %{processed_count} de %{raw_count} fileres al fitxer de %{file} (això pot trigar alguns minuts)." + delete: + button: Eliminar totes les dades del cens + confirm: L'eliminació del cens no es pot desfer. Segur que vols continuar? + destroy: + error: Hi ha hagut un error en esborrar el cens, prova-ho de nou més tard. + success: Dades del cens eliminades. + export_access_codes: + button: Exportar els codis d'accés de la votació + callout: Ara ja pots exportar els codis d'accés. Això només es pot fer una sola vegada. Tan bon punt comencis amb l'exportació, rebràs un correu electrònic amb les instruccions a %{email} + confirm: Només pots exportar els codis d'accés una sola vegada. Assegura't de poder rebre'ls correctament a l'adreça de correu electrònic %{email}. + file_not_exist: Aquest arxiu no existeix. + launch_error: Hi ha hagut un error en iniciar l'exportació els codis d'accés. + launch_success: S'ha iniciat l'exportació dels codis d'accés. Ben aviat rebràs un correu a %{email}. + exporting_access_codes: + info_message: "Si us plau espera, s'està preparant l'exportació, aviat la rebràs a %{email} (això pot trigar alguns minuts)." + freeze: + callout: El cens està tancat i no es pot modificar. + help_html: | + Les dades del cens s'han pujat correctament, els codis s'han generat i exportat correctament.
    Ara ja estàs llesta per a començar les eleccions.
    + Fes servir el CSV exportat que inclou els codis individuals per a distribuir-les al cens fent servir els teus propis mitjans o activant la pestanya "Puc votar?" per a permetre que qualsevol pugui recuperar aquest codi fent servir les seves dades censals. + generate_access_codes: + button: Generar els codis d'accés de la votació + callout: Ara ja pots generar els codis d'accés. Tingues en compte que, un cop els hagis generat, ja no podràs modificar el cens. + confirm: Si continues, ja no podràs modificar el cens. + info_message_all: "S'han exportat correctament totes les fileres del fitxer %{file} (%{raw_count} de %{data_count})." + info_message_warn: Si us plau, comprova que no s'hagi perdut cap dada, s'han creat %{data_count} registres i el fitxer pujat %{file} té %{raw_count} fileres. + launch_error: Hi ha hagut un error en generar els codis d'accés + launch_success: S'ha iniciat la generació dels codis. + start_over: Si us plau, elimina el cens actual i torna a començar amb un arxiu CSV apropiat amb files vàlides. + generating_access_codes: + info_message: "Si us plau, espera, els codis d'accés a la votació s'estan generant (això pot trigar uns minuts)..." + new: + file_help: + explanation: 'Instruccions per al fitxer:' + message_1: Només es permeten fitxers CSV (.csv). + message_2: El separador entre columnes ha de ser un punt i coma (";"). + has_ballot_styles_message: Estàs configurant estils de papereta. Si us plau assegura't de que el camp "%{ballot_style_code_header}" del CSV correspongui al codi de l'estil de papereta que vols. + info_message: "Encara no hi ha cap cens. Si us plau, utilitza el següent formulari per a crear-lo important un fitxer CSV." + missing_ballot_styles_message: 'Encara no hi ha cap estil de papereta per a aquesta votació. Si vols disposar de preguntes condicionals (com ara que votants puguin rebre diferents preguntes segons el seu districte o la seva regió de residència, per exemple) cal que importis el cens abans de configurar l''estil de papereta. Si el que vols és mostrar les mateixes preguntes a tothom, pots iniciar el procès d''importació del cens.' + submit: Enviar CSV + title: Crear el cens + show: + heading: Cens de l'espai de votació + upload_info: + csv_example_with_ballot_style: 'Exemple del fitxer amb estils de papereta:' + csv_example_without_ballot_style: 'Exemple del fitxer sense estils de papereta:' + csv_header_after: No incloguis l'últim camp ("%{ballot_style_code_header}") si no necessites preguntes condicionals/estils de papereta + csv_header_before: 'El fitxer del cens ha de ser un fitxer CSV amb la següent capçalera:' + document_types: + identification_number: Número d'identificació + passport: Passaport + export_mailer: + access_codes_export: + click_button: 'Clica el següent enllaç per baixar les dades dels codis d''accés.
    El fitxer estarà disponible fins el %{date}.
    Per obrir-lo, cal que disposis de 7-Zip (per a Windows), Keka (per a MacOS) o bé PeaZip (per a Linux). Contrasenya: %{password}' + download: Descarregar + subject: L'exportació dels codis d'accés a la votació de %{voting_title} està disponible + vote_flow: + already_voted_in_person: La participant ja ha votat de forma presencial i no té dret a vot. + datum_not_found: Les dades introduïdes no coincideixen amb cap votant. + content_blocks: + highlighted_votings: + name: Votacions destacades + landing_page: + polling_stations: + heading: Punts de votació + no_polling_stations: Encara no hi ha cap punt de voració. + monitoring_committee_members: + actions: + confirm_destroy: N'estàs segura? + destroy: Esborrar + new: Nou membre + title: Accions + pages: + home: + highlighted_votings: + active_spaces: Votacions actives + see_all_spaces: Veure totes les votacions + polling_officer_zone: + closures: + back_to_polling_stations: Tornar als punts de votació + certify: + add_photos: Afegir foto + edit_photos: Editar fotos + error: S'ha produït un error en adjuntar el certificat. Si us plau, prova de nou. + heading: Recompte de vots - Pujar certificat + info_text: Si us plau, puja una imatge del certificat de tancament electoral. + submit: Pujar el certificat + success: El certificat s'ha pujat correctament. + upload_photos: Puja una imatge del Certificat de Tancament Electoral + completed: + sub_heading: Aquest recompte s'ha certificat i ja no es pot editar. + create: + error: S'ha produït un error en crear el tancament. Si us plau, intenta-ho més tard. + success: Tancament creat correctament. + destroy: + error: S'ha produït un erro en eliminar el tancament. + success: Tancament eliminat correctament. + edit: + confirm_start_over: Això eliminarà el nombre total de vots i les notes adjuntes. Segur que ho vols eliminar? + heading: Recompte de vots - recompte de respostes + info_text: Si us plau, detalla el número total de respostes per a cada pregunta. Aquest ha de coincidir amb el número de respostes introduïdes al pas anterior (%{total} respostes en total). + modal_ballots_results_count_error: + blank: El número de vots en blanc esperat és %{expected}, però la suma de les preguntes en blanc és %{current}. + close_modal: Tancar + info_text: El nombre total de paperetes no coincideix amb el nombre total de sobres. Si us plau, revisa el total de paperetes. + title: El total de paperetes no coincideix + total: El total esperat és %{expected}, però la suma de les paperetes vàlides, en blanc i nul·les és %{current}. + valid: El número de vots vàlids esperat és %{expected}, però la suma de les preguntes vàlides és %{current}. + save_recount: Desar recompte + start_over: Si hi ha un error al número total, pots esborrar-ho tot i tornar a començar. + total_ballots: Total de paperetes + total_blank_ballots: Total de paperetes en blanc + total_null_ballots: Total de paperetes nul·les + total_valid_ballots: Total de paperetes vàlides + new: + election: 'Elecció:' + heading: Recompte de vots + info_text: 'Si us plau, introdueix el nombre total de paperetes (sobres) recomptades en aquest punt de votació:' + modal_ballots_count_error: + btn_validate_total: Validar recompte total de paperetes + info_explanation_text: 'Si us plau, revisa el nombre total de paperetes. Si el número total és incorrecte, has de proporcionar una explicació per al Comitè de Seguiment:' + info_text: El nombre total de paperetes (sobres) introduïdes no coincideix amb el registre de persones que han votat en aquest punt de votació. + message_for_monitoring_committee: Missatge per al Comitè de Seguiment + review_recount: Revisar el recompte + text_area_placeholder: Si us plau, escriu el teu missatge + title: El total de registres no coincideix + total_ballots: 'Total de paperetes:' + total_people: 'Total de persones:' + polling_station: 'Punt de votació:' + submit: Verificar nombre total + total_ballots_count: Nombre de paperetes + show: + edit_count_votes: '¿Números incorrectes? Encara els pots editar.' + heading: Recompte de vots + sub_heading: Per tancar el recompte cal carregar un certificat. Un cop fet, el recompte se segellarà i ja no es podrà editar més. + sign: + cancel: Cancel·lar + check_box: Ho he revisat i és idèntic al certificat físic de tancament electoral + confirm: D'acord, continuar + error: Hi ha hagut un error. Si us plau, torna-ho a provar. + heading: Recompte de vots - Signar el tancament + info_text: Si continues ja no podràs modificar cap informació, aquesta acció no es pot desfer. + submit: Signar el tancament + success: Tancament signat correctament. + update: + error: S'ha produït un error en actualitzar els resultats del tancament. Intenta-ho de nou més tard. + success: Resultats de tancament actualitzats correctament. + in_person_votes: + complete_voting: + available_answers: 'Respostes disponibles:' + census_verified: Aquesta participant no ha votat presencialment encara. + census_verified_with_online_vote: Aquesta participant ja ha votat en línia. Si vota presencialment, la votació prèvia serà invalidada i aquest serà el seu vot definitiu. + complete_voting: Completar el vot + identify_another: Identificar una altra participant + questions_title: 'Tenen dret a vot a les següents preguntes:' + questions_title_voted: 'Aquesta participant ja ha votat en línia i té dret a votar a les següents preguntes:' + voted: La participant ha votat + create: + error: El vot no ha estat registrat. Si us plau, prova-ho de nou. + in_person_form: + census_not_present: La participant no és a la llista del cens. + census_not_present_description: Ha de dirigir-se a l'oficina de reclamacions del cens o al servei d'assistència tècnica. + date_of_birth: Data de naixement + day: Dia + day_placeholder: DD + document_number: Número de document + document_number_placeholder: Número d'ID + month: Mes + month_placeholder: MM + select: Selecciona el tipus de document + title: 'Selecciona el tipus de document i introdueix el número de document de la participant:' + validate_document: Validar el document + year: Any + year_placeholder: AAAA + new: + back: Tornar als punts de votació + title: Identificar i verificar una participant + show: + back: Tornar als punts de votació + title: En espera del registre de vot presencial + update: + error: Hi ha hagut un problema en registrar el vot. Si us plau, prova-ho de nou. + success: + accepted: El vot s'ha registrat correctament. + rejected: El Bulletin Board no ha acceptat el vot. Si us plau, contacta amb l'administrador del sistema. + verify_document: + census_present: La participant es troba al cens. + name: Nom + title: 'Comprova que les dades següents són correctes:' + verify_document: Verificar el document + menu: + polling_officer_zone: Zona del gestor de mesa + polling_officers: + index: + polling_officer_role_description: Has estat assignat per a actuar com a gestor de mesa (President o Administrador) en algunes de les eleccions celebrades en aquesta plataforma. + polling_station: + address: Adreça + count_votes: Comptar vots + election: Elecció + identify_person: Identificar una persona + name: Nom + no_polling_stations: Encara no has estat assignat a cap punt de votació. + role: El teu rol + show_closure: Veure tancament + title: Punts de votació + voting: Votacions + polling_officers: + actions: + confirm_destroy: N'estàs segura? + destroy: Esborrar + new: Afegir gestora de mesa + title: Accions + roles: + manager: Administrador + president: President + unassigned: Sense assignar + polling_station_closure_certificate: + current_certificate: 'Certificat actual:' + polling_station_closure_recount: + nota_option: En blanc / Cap de les anteriors + polling_officer_notes: 'Anotacions del gestor de mesa:' + polling_officer_notes_blank: Cap anotació + recount_summary: 'Resum del recompte:' + signed: Signat + total_ballots: 'Total de paperetes:' + total_blank_ballots: 'Total de paperetes en blanc:' + total_null_ballots: 'Total de paperetes nul·les:' + total_valid_ballots: 'Total de paperetes vàlides:' + polling_stations: + actions: + confirm_destroy: N'estàs segura? + destroy: Esborrar + edit: Editar + new: Afegir punt de votació + title: Accions + votings: + access_code_modal: + email: Envia un correu a %{email} + info: Necessites un codi d'accés per perticipar-hi. Si no n'has rebut cap per correu postal, podem enviar-te un de nou. + no_email: No hi ha cap correu disponible + no_sms: No hi ha cap número de telèfon disponible + sms: Envia un SMS a %{sms} + title: Obtenir un codi d'accés + check_census: + check_status: Comprovar l'estat + description: Comprova les dades del cens per saber si tens dret a participar en la votació. Si les dades són correctes, hauries de tenir ja un codi d'accés, però si l'has perdut pots demanar-lo de nou si les teves dades són correctes. + error: + info: 'Prova de nou, si us plau. Si creus que les dades que tenim al sistema no són correctes, pots avisar-nos a %{census_contact_information}.' + title: Les dades que has introduït no es troben al cens d'aquesta votació + form_title: 'Emplena el formulari següent per comprovar les dades del cens:' + invalid: Hi ha hagut un problema en comprovar el cens. + success: + access_link: per correu electrònic. + access_link_with_sms: via SMS o correu electrònic. + info: Ja hauries d'haver rebut el codi d'accés per correu postal. En cas que no l'hagis rebut, pots demanar-lo de nou aquí + title: Les teves dades del cens són correctes! + title: Puc votar? + check_fields: + date_of_birth: Data de naixement + day: Dia + day_placeholder: DD + document_number: Número de document + document_number_placeholder: Número d'ID + document_type: Tipus de document + month: Mes + month_placeholder: MM + postal_code: Codi postal + postal_code_placeholder: Número del codi postal + select: Selecciona el tipus de document + year: Any + year_placeholder: AAAA + count: + title: + one: "%{count} votació" + other: "%{count} votacions" + elections_log: + description: El registre de l'elecció et mostrarà tota la informació rellevant sobre cada votació. Per exemple, l'estat de la cerimònia de claus o el recompte o si els resultats ja estan publicats. Fes clic en l'elecció sobre la qual vols la informació del registre. + title: Registre de l'elecció + filters: + active: Actives + all: Totes + date: Data + finished: Finalitzades + search: Cercar + upcoming: Properes + index: + no_votings: Cap votació s'ajusta als vostres criteris de cerca. + only_finished: Actualment no hi ha votacions programades, però podeu trobar les votacions ja finalitzades. + title: Votacions + login: + access_code: Codi d'accés + access_code_placeholder: Codi d'accés + ask_for_a_new_one: Sol·licita'n un de nou. + dont_have_access_code: No tens un codi d'accès? + form_title: 'Emplena el formulari següent per accedir a la votació:' + start_voting: Començar a votar + title: Vull identificar-me amb les meves dades del cens de la votació + no_census_contact_information: Encara no hi ha cap informació de contacte. + orders: + label: 'Ordenar votacions per:' + random: Aleatori + recent: Més recents + send_access_code: + invalid: Hi ha hagut un problema en enviar el codi d'accés. + success: El teu codi d'accés s'ha enviat correctament. + show: + title: Quant a aquesta votació + votings_m: + badge_name: + finished: Finalitzades + ongoing: En curs + upcoming: Properes + unspecified: Sense especificar + voting_type: + hybrid: Híbrida + in_person: Presencial + online: En línia + layouts: + decidim: + voting_navigation: + check_census: Puc votar? + election_log: Registre de l'elecció + votings: + index: + promoted_votings: Votacions destacades + promoted_voting: + vote: Votar diff --git a/decidim-elections/config/locales/cs.yml b/decidim-elections/config/locales/cs.yml new file mode 100644 index 00000000..59474189 --- /dev/null +++ b/decidim-elections/config/locales/cs.yml @@ -0,0 +1,1427 @@ +cs: + activemodel: + attributes: + answer: + description: Popis + image: Obrázek + proposals: Související návrhy + title: Název + ballot_style: + code: Kód + election: + description: Popis + end_time: Hlasování končí v + start_time: Hlasování začíná v + title: Název + monitoring_committee_member: + email: E-mail + name: Jméno + polling_officer: + email: E-mail + name: Jméno + polling_station: + address: Adresa + location: Poloha + location_hints: Nápověda k poloze + polling_station_managers: Manažeři + polling_station_president_id: Předseda + title: Název + question: + max_selections: Maximální počet výběrů + min_selections: Žádná z výše uvedených možností + title: Název + trustees_participatory_space: + user_id: Účastník + voting: + banner_image: Obrázek banneru + census_contact_information: Kontaktní informace pro rejstřík obyvatel + description: Popis + end_time: Hlasování končí + introductory_image: Úvodní obrázek + promoted: Propagováno + scope_id: Oblast působnosti + show_check_census: Zobrazit stránku "zkontrolovat rejstřík" + start_time: Hlasování začíná + title: Název + voting_type: Typ hlasování + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Potřebuje být znovu připojen + election: + attributes: + attachment: + needs_to_be_reattached: Potřebuje být znovu připojen + question_result: + attributes: + base: + blank_count_invalid: Celkový počet prázdných odpovědí nemůže být větší než celkový počet prázdných hlasovacích lístků. + trustee: + attributes: + name: + cant_be_changed: nelze změnit + public_key: + cant_be_changed: nelze změnit + voting: + attributes: + voting_type: + inclusion: "%{value} není platný typ hlasování" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Volební komisař je již prezident/manažer volební místnosti. + voting: + different_organization: Hlasování musí být ve stejné organizaci jako je uživatel. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Volební komisař musí být ve stejném hlasování jako volební místnost. + models: + decidim/elections/answer: + one: Odpověď + few: Odpovědí + many: Odpovědí + other: Odpovědi + decidim/elections/election: + one: Volby + few: Voleb + many: Voleb + other: Volby + decidim/elections/question: + one: Otázka + few: Otázek + many: Otázek + other: Otázek + decidim/voting: + one: Hlasování + few: Hlasování + many: Hlasování + other: Hlasování + decidim/votings/census/dataset: + one: Datová sada + few: Datové sady + many: Datových sad + other: Datové sady + decidim/votings/census/datum: + one: Datum + few: Data + many: Datumů + other: Datumy + decidim/votings/polling_officer: + one: Volební komisař + few: Volební komisaři + many: Volebních komisařů + other: Volební komisaři + decidim/votings/polling_station: + one: Volební místnost + few: Volební místnosti + many: Volebních místností + other: Volebních místnostech + decidim/votings/voting: + one: Hlasování + few: Hlasování + many: Hlasování + other: Hlasování + decidim: + admin: + filters: + officers_assigned_eq: + label: Úředníci + values: + assigned: Přiřazeno + unassigned: Nepřiřazeno + role_eq: + label: Role + values: + manager: Manažer + president: Předseda + unassigned: Nepřiřazeno + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Vyhledejte %{collection} podle jména/e-mailu/přezdívky nebo volební místnosti. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Hledat %{collection} podle názvu, adresy nebo jména úředníka/e-mailu/přezdívky. + signed_eq: + label: Podepsáno + values: + 'false': Podepsáno + 'true': Nepodepsáno + validated_eq: + label: Ověřeno + values: + 'false': Neschváleno + 'true': Schváleno + voting_publications: + create: + error: Při publikování tohoto hlasování došlo k chybě. + success: Hlasování bylo úspěšně publikováno. + destroy: + error: Při rušení publikování tohoto hlasování došlo k chybě. + success: Hlasování bylo úspěšně nezveřejněno. + components: + elections: + actions: + vote: Hlasovat + name: Volby + settings: + global: + announcement: Oznámení + step: + announcement: Oznámení + elections: + actions: + confirm_destroy: Jste si jisti? + destroy: Zničit + edit: Upravit + feedback: Zpětná vazba voličů + import: Importovat návrhy na odpovědi + manage_answers: Spravovat odpovědi + manage_questions: Spravovat otázky + manage_steps: Spravovat kroky + new_answer: Nová odpověď + new_election: Nové volby + new_question: Nová otázka + new_trustee: Nový důvěrník + preview: Náhled + publish: Publikovat + title: Akce + unpublish: Zrušit publikování + admin: + answers: + create: + invalid: Při vytváření této odpovědi došlo k chybě. + success: Odpověď byla úspěšně vytvořena. + destroy: + invalid: Při odstraňování této odpovědi došlo k potížím. + success: Odpověď byla úspěšně odstraněna. + edit: + title: Upravit odpověď + update: Aktualizovat odpověď + index: + invalid_max_selections: Potřebujete ještě %{missing_answers} odpověd/í, aby počet odpovídal maximálnímu počtu výběrů. + title: Odpovědi + new: + create: Vytvořit odpověď + title: Nová odpověď + not_selected: Nevybráno + select: + disable: Zrušit výběr odpovědi + enable: Označit odpověď jako vybranou + invalid: Při výběru této odpovědi došlo k potížím. + success: Odpověď byla úspěšně vybrána. + selected: Vybráno + unselect: + invalid: Při zrušení výběru této odpovědi došlo k chybě. + success: Odpovědi byl úspěšně zrušen výběr. + update: + invalid: Při aktualizaci této odpovědi došlo k chybě. + success: Odpověď byla úspěšně aktualizována. + elections: + create: + invalid: Při vytváření těchto voleb došlo k chybě. + success: Volba byla úspěšně vytvořena. + destroy: + invalid: Při mazání této volby došlo k chybě. + success: Volba byla úspěšně odstraněna. + edit: + title: Upravit volbu + update: Aktualizovat volby + form: + organization_time_zone: Zkontrolujte, zda je časové pásmo organizace v nastavení organizace správné. Aktuální konfigurace je %{time_zone} (%{time}). + index: + no_bulletin_board: Není nastaven žádný server Vývěsky, který je potřebný pro použití tohoto modulu. Tento úkol by měl provést správce systému. + title: Volby + new: + create: Vytvořit volbu + title: Nové volby + publish: + success: Volba byla úspěšně publikována. + unpublish: + success: Volba byla úspěšně odebrána ze zveřejnění. + update: + invalid: Při aktualizaci těchto voleb došlo k chybě. + success: Volba byla úspěšně aktualizována. + exports: + elections: Volby + feedback_form_answers: Odpovědi na formulář zpětné vazby + mailers: + trustee_mailer: + subject: Byli jste přidáni jako důvěrník do %{resource_name} + trustee_zone: Přejít do zóny důvěrníků + menu: + trustees: Volební důvěrníci + models: + answer: + name: Odpověď + proposals_imports: + create: + invalid: Při importu návrhů do odpovědí došlo k potížím. + success: "%{number} návrhů bylo úspěšně importováno do odpovědí." + new: + create: Importovat návrhy na odpovědi + no_components: V tomto participačním prostoru nejsou žádné další prvky návrhu, které by mohly do odpovědí importovat návrhy. + select_component: Vyberte komponentu + title: Importovat návrhy + questions: + create: + election_started: Volby již byly zahájeny. + invalid: Při vytváření této otázky došlo k chybě. + success: Otázka byla úspěšně vytvořena. + destroy: + invalid: Při odstraňování této otázky došlo k chybě. + success: Úloha byla úspěšně odstraněna. + edit: + title: Upravit otázku + update: Aktualizovat otázku + index: + title: Otázky + new: + create: Vytvořit otázku + title: Nová otázka + update: + invalid: Při aktualizaci této otázky došlo k chybě. + success: Úloha byla úspěšně aktualizována. + steps: + create_election: + census: Rejstřík obyvatel + errors: + census_codes_generated: Přístupové kódy pro rejstřík osob nejsou vygenerovány. + census_frozen: Přístupové kódy pro rejstřík osob nejsou exportovány. + census_uploaded: Pro tyto volby není nahrán žádný rejstřík obyvatel. + component_published: Volební složka není publikována. + fix_it_text: Opravit + max_selections: Otázky nemají správnou hodnotu pro množství odpovědí + minimum_answers: Otázky musí mít alespoň dvě odpovědi. + minimum_questions: Volby musí mít alespoň jednu otázku. + published: Volby nejsou publikovány. + time_before: Čas zahájení je v méně než %{hours} hodin před začátkem voleb. + trustees_number: Participační prostor musí mít alespoň %{number} důvěrníků s veřejným klíčem. + invalid: Při zřizování těchto voleb došlo k chybě + no_trustees: Pro tento participační prostor nejsou nakonfigurováni žádní důvěrníci + not_used_trustee: "(nepoužito)" + public_key: + 'false': nemá veřejný klíč + 'true': má veřejný klíč + requirements: + census_codes_generated: Vygenerované přístupové kódy pro rejstřík osob. + census_frozen: Přístupové kódy pro rejstřík osob jsou exportovány a sčítání je zmrazeno. + census_uploaded: Rejstřík obyvatel je nahrán. + component_published: Volební komponenta je publikována. + max_selections: Všechny otázky mají správnou hodnotu pro maximum odpovědí. + minimum_answers: Každá otázka má alespoň 2 odpovědi. + minimum_questions: Volba má alespoň jednu otázku. + published: Volby jsou publikovány. + time_before: Nastavení se provádí alespoň %{hours} hodin před zahájením voleb. + trustees_number: Participační prostor má alespoň %{number} důvěrníků s veřejným klíčem. + submit: Nastavení voleb + success: Volby byly úspěšně odeslány do Vývěsky. + technical_configuration: + authority_name: "Název orgánu: %{value}" + bulletin_board_server: "Bulletin Board server: %{value}" + scheme_name: "název schématu: %{value}" + title: Zobrazit technické informace + title: Nastavení voleb + trustees: Volební důvěrníci + created: + invalid: Vyskytl se problém při zahájení ceremoniálu. + submit: Zahájit ceremoniál + success: Žádost o Úvodní ceremoniál byla úspěšně odeslána do Vývěsky. + title: Volba vytvořena + trustees: Důvěrníci + key_ceremony: + continue: Pokračovat + title: Slavnostní ceremoniál + key_ceremony_ended: + errors: + time_before: Volby jsou připraveny k zahájení. Musíte počkat %{hours} hodin před začátkem období hlasování (%{start_time}). + invalid: Vyskytl se problém při zahájení hlasovacího období. + requirements: + time_before: Volba začne brzy. Hlasovací období můžete začít ručně, nebo bude spuštěna automaticky před časem začátku v %{start_time}. + submit: Začátek doby hlasování + success: Žádost o Počátek voleb byla úspěšně odeslána do Vývěsky. + title: Připraveno k zahájení + processing: Zpracovávání... + results_published: + answer: Odpověď + not_selected: Nevybráno + question: Otázka + result: Výsledek + selected: Vybráno + submit: Odeslat + title: Výsledky publikovány + tally_ended: + answer: Odpověď + not_selected: Nevybráno + question: Otázka + result: Výsledek + selected: Vybráno + submit: Publikovat výsledky + success: Žádost o Zveřejnění výsledků bylo úspěšně odesláno na Vývěsku. + title: Vypočítané výsledky + tally_started: + continue: Pokračovat + invalid: Došlo k problému s nahlášením chybějícího důvěrníka. + mark_as_missing: Označit jako chybějící + mark_as_missing_description: Tohoto procesu by se měli účastnit všichni důvěrníci, ale pokud se některý důvěrník nemůže procesu účastnit, můžete ho označit jako chybějícího. + success: Zpráva o chybějícím důvěrníkovi byla úspěšně odeslána na vývěsku. + tally_completion: Proces bude dokončen, jakmile budou všichni důvěrníci aktivní nebo budou označeni jako chybějící. K dokončení procesu je zapotřebí alespoň %{quorum} důvěrníků. + title: Proces volebního přepočítání + undo_mark_as_missing: Správce, který byl omylem označen jako chybějící, se bude moci zúčastnit před dokončením procesu. Může postupovat jako obvykle a chybějící označení bude ignorováno. + vote: + errors: + time_after: Volby stále probíhají. Musíte počkat na ukončení hlasovacího období (%{end_time}). + invalid: Při ukončení hlasovacího období došlo k chybě. + requirements: + time_after: Volba skončila. Hlasovací dobu můžete ukončit ručně, nebo bude automaticky ukončena za několik minut. + submit: Konec doby hlasování + success: Žádost o ukončení voleb byla úspěšně odeslána do Vývěsky. + title: Období hlasování + vote_ended: + invalid: Vyskytl se problém při spuštění volebního přepočítávání. + submit: Začít Volební přepočítání + success: Požadavek zahájení Volebního přepočítání byl úspěšně odeslán do Vývěsky. + text: Hlasování bylo ukončeno. Nyní můžete začít volební přepočítání. + title: Období hlasování skončilo + vote_stats: + no_vote_statistics_yet: Zatím žádné statistiky hlasování + title: Statistiky hlasování + voters: Voliči + votes: Hlasy + trustees_participatory_spaces: + actions: + disable: Zakázat + enable: Zvážit + create: + exists: Důvěrník pro tento participační prostor existuje. + invalid: Při vytváření důvěrníka došlo k chybě. + success: Důvěrník úspěšně vytvořen. + delete: + invalid: Při odstraňování tohoto důvěrníka došlo k chybě. + success: Důvěrník byl úspěšně odstraněn. + form: + select_user: Vybrat uživatele + index: + title: Důvěrníci + new: + create: Vytvořit důvěrníka + title: Nový důvěrník + update: + invalid: Při aktualizaci důvěrníka %{trustee} došlo k chybě. + success: Důvěrník %{trustee} úspěšně aktualizován. + admin_log: + election: + create: "%{user_name} vytvořil volby %{resource_name} z %{space_name}" + delete: "%{user_name} odstranil volby %{resource_name} ze %{space_name}" + end_vote: "%{user_name} ukončil hlasovací období pro zvolení %{resource_name} ve %{space_name} na Vývěsce" + publish: "%{user_name} publikoval volbu %{resource_name} z %{space_name}" + publish_results: "%{user_name} zveřejnil výsledky voleb %{resource_name} v %{space_name} na Vývěsce" + report_missing_trustee: "%{user_name} nahlásil %{trustee_name} jako chybějícího správce během volby %{resource_name} %{space_name} na Vývěsce" + setup: "%{user_name} vytvořil volbu %{resource_name} z %{space_name} na Vývěsce" + start_key_ceremony: "%{user_name} zahájil klíčový ceremoniál pro volby %{resource_name} z %{space_name} na Vývěsce" + start_tally: "%{user_name} zahájil volební přepočítání pro volby %{resource_name} %{space_name} na Vývěsce" + start_vote: "%{user_name} zahájil hlasovací období pro volby %{resource_name} z %{space_name} na Vývěsce" + unpublish: "%{user_name} zrušil publikaci %{resource_name} z volby %{space_name}" + update: "%{user_name} aktualizoval volbu %{resource_name} z %{space_name}" + trustee: + create: "%{user_name} přiřadil uživatele %{trustee_user} jako důvěrníka" + connection: + failed: + modal: + close: Zavřít‏ + communication_lost: Bohužel se zdá, že komunikace s hlasovacím serverem (Bulletin Board) je ztracena.
    Může se stát, že připojení k internetu je přerušeno nebo že cílový server je příliš zaneprázdněný.
    Pokud tento problém přetrvává, můžete to zkusit později nebo se obraťte na podporu. + generic_error: Bohužel došlo k neznámé chybě. Je pravděpodobné, že váš prohlížeč není podporován nebo že používáte "incognito" nebo "privátní" režim, který není podporován. + title: Něco se pokazilo + election_m: + badge_name: + finished: Dokončeno + ongoing: Aktivní + upcoming: Nadcházející + end_date: Končí + footer: + remaining_time: + one: "Na hlasování zbývá %{count} hodina a %{minutes} minut." + few: "Na hlasování zbývá %{count} hodiny a %{minutes} minut." + many: "Na hlasování zbývá %{count} hodin a %{minutes} minut." + other: "Na hlasování zbývá %{count} hodin a %{minutes} minut." + view: Zobrazit + vote: Hlasovat + label: + date: Data + questions: Otázky %{count} + start_date: Začíná + unspecified: Nespecifikováno + elections: + count: + elections_count: + one: "%{count} zvolení" + few: "%{count} zvolení" + many: "%{count} zvolení" + other: "%{count} voleb" + election_log: + chained_hash: Získaný hash této zprávy + complete: Dokončeno + creation_description: + complete: Volby jsou vytvořené a úspěšně nastavené na Vývěsku. + not_created: Volby ještě nejsou vytvořeny. + creation_title: Volby vytvořeny + description: Toto je volební deník, kde můžete zkontrolovat stav každého kroku, např. když se volby vytvořily, pokud bude dokončen proces spojenectví a až budou ukončeny volby. + download: Stáhnout + key_ceremony_description: + complete: Úvodní ceremoniál je dokončen. Každý správce má platné klíče a stáhl potřebné záložní klíče. + not_started: Úvodní ceremoniál ještě nezačal. + started: Úvodní ceremoniál byl zahájen, ale ještě není dokončen. + key_ceremony_title: Úvodní ceremoniál + not_available: Ještě není k dispozici + not_created: Nevytvořeno + not_ready: Není připraveno + not_started: Nezahájeno + published: Publikováno + results_description: + not_published: Výsledky ještě nejsou publikovány. + published: Výsledky jsou publikovány. + results_title: Výsledky + started: Spuštěno + tally_description: + finished: Proces je dokončen. + not_started: Proces ještě nezačal. + started: Proces byl zahájen. + tally_title: Proces volebního přepočítání + title: Volební protokol + unpublished: Nezveřejněno + verifiable_results: + checksum: 'Kontrolní součet SHA256 souboru:' + description: + not_ready: Ověřitelný volební soubor a kontrolní součet SHA256 ještě nejsou k dispozici. Jakmile budou výsledky zveřejněny, budete moci tuto volbu ověřit. + ready: 'Zde máte možnost ověřit volby. Nejprve musíte stáhnout soubor a ujistěte se, že nebyl poškozen. Chcete-li tak učinit, spusťte následující příkaz a zkontrolujte, zda se výstup shoduje s kontrolním součtem:' + how_to_verify: 'Jakmile stáhnete soubor a ujistíte se, že je v pořádku, můžete pokračovat ve spuštění univerzálního ověřovače. Klonujte tento repozitář a z kořenové složky spusťte následující příkaz:' + title: Ověřit výsledky voleb + verifiable_file: 'Ověřitelný volební soubor:' + verify: Ověřit volby + vote_description: + finished: Proces hlasování je ukončen. + not_started: Proces hlasování ještě nezačal. + started: Proces hlasování byl zahájen. + vote_title: Proces hlasování + filters: + active: Aktivní + all: Vše + date: Datum + finished: Dokončeno + upcoming: Nadcházející + preview: + available_answers: 'Dostupné odpovědi:' + description: 'Toto jsou otázky, které najdete v procesu hlasování:' + title: Volební otázky + results: + description: 'To jsou výsledky hlasování pro každou otázku:' + percentage: "%{count}%" + selected: Vybrané + title: Výsledky voleb + votes: + one: "%{count} hlas" + few: "%{count} hlasy" + many: "%{count} hlasů" + other: "%{count} hlasů" + show: + action_button: + change_vote: Změňte svůj hlas + vote: Zahájit hlasování + vote_again: Znovu hlasovat + callout: + already_voted: V této volbě jste již hlasovali. Můžete změnit svůj hlas nebo jej ověřit. + pending_vote: Váš hlas se odehrává na serveru. + vote_rejected: Nebylo možné ověřit váš hlas. Prosím, pošlete jej znovu. + election_log: Volební protokol + preview: Náhled + verify: + already_voted: Již jste hlasovali? + verify_here: Zkontrolujte svůj hlas zde. + will_verify: Své hlasování budete moci ověřit hned po zahájení voleb. + voting_period_status: + finished: Hlasování začalo %{start_time} a skončilo %{end_time} + ongoing: 'Aktivní hlasování do: %{end_time}' + upcoming: Hlasování začíná %{start_time} + feedback: + answer: + invalid: Při odesílání vaší zpětné vazby došlo k chybě. + spam_detected: Vyskytl se problém s odpovědí na formulář. Možná jste byl příliš rychlý, můžete to zkusit znovu? + success: Zpětná vazba byla úspěšně odeslána. + models: + answer: + fields: + proposals: Návrhy + selected: Vybráno + title: Název + votes: Hlasy + election: + fields: + bb_status: Stav Vývěsky + end_time: Končí v + start_time: Začíná v + title: Název + verifiable_results_file_hash: Kontrolní součet SHA256 souboru + verifiable_results_file_url: Ověřitelný volební soubor + question: + fields: + answers: Odpovědi + max_selections: Max. výběr + title: Název + trustees_participatory_space: + fields: + considered: uváženo + email: E-mail + inactive: neaktivní + name: Jméno + notification: Oznámení odesláno + public_key: Veřejný klíč + status: Stav + orders: + label: Seřadit volby podle + older: Starší + recent: Nedávné + trustee_zone: + elections: + backup_modal: + description: Tyto volby jsou vytvořeny na Vývěsce. Je velmi důležité, aby každý důvěrník, který se na něm podílí, vytvořil záložní kopii těchto klíčů a uložil je na bezpečném místě. Poté proces pokračuje. + download_election_keys: Stáhnout klíče + title: Zálohovat volební klíče pro %{election} + key_ceremony_steps: + back: Zpět + description: Tyto volby jsou vytvořeny na Vývěsce. Pro dokončení tohoto procesu je nutná vaše účast jako důvěrníka. + keys: + create_election: Generování klíčů + key_ceremony: + joint_election_key: Vytvoření společného klíče + step_1: Vydávání klíčů + list: + status: Stav + task: Úkol + process_warning: Po spuštění procesu byste neměli tuto stránku ukončit, dokud proces neskončí. Trvá to několik minut, protože všichni důvěrníci by měli být k dokončení připojeni. + start: Začít + status: + completed: Dokončeno + pending: Čekající + processing: Zpracovávání + title: Vytvořit volební klíče pro %{election} + restore_modal: + description: Vývěska má od vás jako důvěrníka informace o těchto volbách. Chcete-li pokračovat v procesu, nejprve nahrajte záložní soubor generovaný během předchozí relace. + title: Obnovit volební klíče pro %{election} + upload_election_keys: Nahrát volební klíče + tally_started_steps: + back: Zpět + description: Výsledky těchto voleb jsou vyčísleny na Vývěsce. Pro dokončení tohoto procesu je zapotřebí vaší účasti jako Důvěrníka. + keys: + end_tally: Volební přepočítání uzavřeno + tally: + cast: Obsazení volebního přepočítávání + share: Poměry volebního přepočítávání + list: + status: Stav + task: Úkol + process_warning: Po spuštění procesu byste neměli tuto stránku ukončit, dokud proces neskončí. Trvá to několik minut, protože všichni důvěrníci by měli být k dokončení připojeni. + start: Začít + status: + completed: Dokončeno + pending: Čekající + processing: Zpracovávání + title: Volební přepočítání pro %{election} + update: + error: Stav voleb nebyl aktualizován. + success: 'Stav voleb je: %{status}.' + menu: + trustee_zone: Zóna důvěrníků + no_bulletin_board: + body: Konfigurovaná Vývěska je vyžadována pro tuto sekci. Pro více informací kontaktujte správce. + title: Omlouváme se, ale Vývěska ještě není nakonfigurována. + trustees: + show: + elections: + list: + action_required: + 'false': 'Ne' + name: Je vyžadována akce? + 'true': Provést akci + bb_status: Stav + election: Volby + voting_period: Hlasovací období + no_elections: Momentálně nejste přiřazeni k žádné akci jako důvěrník. Obdržíte upozornění, když je čas jednat v různých fázích. + title: Volby + identification_keys: + cancel: Zrušit + generate: Generovat identifikační klíče + generate_error: Při generování identifikačních klíčů došlo k chybě. + generate_legend: Musíte vytvořit pár identifikačních klíčů pro účast ve volbách jako důvěrník. + generate_legend_1: Po stisknutí tlačítka byste měli stáhnout soubor s vygenerovanými identifikačními klíči. + generate_legend_2: Kopírovat stažený soubor do čistého USB zařízení + generate_legend_3: Ujistěte se, že váš počítač nemá kopii souboru (např. zkontrolujte složky Stahování a plocha). + generate_legend_4: Vytvořit další kopii souboru na jiném externím zařízení a uložit jej na velmi bezpečném místě. + submit: Odeslat + submit_legend: Po splnění všech výše uvedených kroků dokončete proces odesílání veřejného identifikačního klíče na server. + submit_title: Odeslat veřejný identifikační klíč + title: Identifikační klíče důvěrníka + upload: Nahrajte své identifikační klíče + upload_error: + invalid_format: Nahraný soubor neobsahuje žádný identifikační klíč. + invalid_key: Identifikační klíče v nahraném souboru nelze načíst. + invalid_public_key: Identifikační klíče v nahraném souboru neodpovídají uloženému veřejnému identifikačnímu klíči. + upload_legend: Server má vaše veřejné identifikační klíče, ale váš prohlížeč jej stále nemá. Je třeba importovat soubor s identifikačními klíči do počítače ze zálohy, kterou jste vytvořili po jejich vygenerování. + not_supported_browser_description: Vypadá to, že používáte webový prohlížeč, který nelze použít jako správce. Ujistěte se, že používáte nejnovější verzi prohlížeče, nebo zkuste použít některý z nejpopulárnějších prohlížečů, abyste mohli dokončit své úkoly správce. + not_supported_browser_title: Upgradujte prohlížeč abyste mohl fungovat jako důvěrník + update: + success: Váš identifikační veřejný klíč byl úspěšně uložen. + votes: + ballot_decision: + audit: (Audit volebního lístku) + back: Zahájit proces hlasování znovu + ballot_hash: 'Identifikátor vašeho hlasování:' + cast: Uložte hlasování, abyste dokončili vaše hlasování + header: 'Hlasovací lístek je šifrovaný: zahlasujte s ním nebo jej auditujte' + casting: + header: Hlasování... + text: Váš hlasovací lístek byl vložen do volební urny. + confirm: + answer_number: odpověď %{number} + confirm: Potvrdit + edit: upravit + header: Potvrďte svůj hlas + intro: Zde je shrnutí hlasů, které se chystáte odeslat.
    Prosím potvrďte svůj hlas nebo upravte své odpovědi. + nota_option: Prázdné + confirmed: + back: Zpět k volbám + experience: Jaká byla vaše zkušenost? + feedback: Dejte nám zpětnou vazbu + header: Hlasování potvrzeno + lead: Váš hlas byl odeslán! + text: 'Můžete zkontrolovat, že váš hlas byl úspěšně přidán do volební urny s následujícím identifikátorem: %{e_vote_poll_id}' + verify_link: Chcete-li jej zkontrolovat, zkopírujte identifikátor a vložte jej na stránku pro ověření hlasování + create: + error: Při hlasování se vyskytl problém. Zkuste to prosím znovu. + encrypting: + header: Šifrování hlasování... + text: Váš hlasovací lístek je šifrován, aby bylo zajištěno že vaše hlasování je tajné. + failed: + header: Hlasování se nezdařilo + lead: Váš hlas nebyl odeslán! + text: Něco se pokazilo, zkuste to prosím znovu. + try_again: Zkuste to znovu + header: + ballot_decision: Zahlasování nebo audit vašeho hlasování + confirm: Potvrďte svůj hlas + election: Volby + register: Registrovat + vote_for: Hlasujte pro %{title} + messages: + invalid_token: Vaše relace u hlasovacího místa není platná. Zkuste hlasovat znovu. + not_allowed: V tuto chvíli vám není dovoleno hlasovat o těchto volbách. + modal: + close: Zavřít + proposal_header: 'Návrhy:' + new: + answer_choices: Můžete vybrat až %{choices} odpovědí + more_information: Více informací + nota_option: Prázdná/Nic z výše uvedených + preview_alert: To je náhled hlasovacího pultu. + question_steps: Otázka %{current_step} z %{total_steps} + selections: "%{selected} z %{max_selections}
    výběrů" + onboarding_modal: + create_account: Vytvořit účet + description: Chcete vytvořit nový účet na platformě? Budete se moci účastnit procesů a být aktivní součástí organizace. + no_account: Ne, děkuji. + title: Jste na platformě nový? + update: + error: Při aktualizaci statusu hlasování se vyskytl problém. Prosím, hlasujte znovu. + verify: + content: + heading: Ověřte svůj hlas + info: Tento ověřovatel kontroluje, že váš hlas, identifikovaný šifrovaným textovým řetězcem, byl odeslán správně a je uvnitř volební urny. + error: + header: Hlasování nenalezeno! + info: Kód hlasu nebyl nalezen v %{link} volebních urnám, zkuste to znovu. + form: + back: Zpět na platformu + submit: Ověřit + vote_identifier: 'Identifikační kód:' + vote_identifier_help: Toto je identifikátor, který vám byl poskytnut po hlasování (ne kód pro zadání hlasovacího pultu). + header: + title: Ověřte svůj hlas + success: + header: Hlas lokalizován! + info: Váš zašifrovaný hlas je v %{link} volebních urnách. + voting_step: + back: Zpět + continue: Další + warnings: + empty_filters: S těmito kritérii se nekonají volby. + no_elections: Nejsou naplánovány žádné volby. + events: + elections: + election_published: + email_intro: 'Volba %{resource_title} je nyní aktivní pro %{participatory_space_title}. Můžete ji vidět z této stránky:' + email_outro: Obdrželi jste toto oznámení, protože sledujete %{participatory_space_title}. Můžete přestat přijímat oznámení kliknutím na následující odkaz. + email_subject: Volba %{resource_title} je nyní aktivní pro %{participatory_space_title}. + notification_title: Volba %{resource_title} je nyní aktivní pro %{participatory_space_title}. + trustees: + new_election: + email_intro: Přidali jste jako důvěrník pro volby %{resource_title}. + email_outro: Obdrželi jste toto oznámení, protože jste byli přidáni jako správce pro %{resource_title} volby. + new_trustee: + email_intro: Administrátor vás přidal jako důvěrníka pro %{resource_name}. Měli byste vytvořit váš veřejný klíč ve vaší zóně důvěrníka + email_outro: Obdrželi jste toto oznámení, protože jste byli přidáni jako správce pro %{resource_name}. + email_subject: Jste správcem %{resource_name}. + notification_title: Byli jste přidáni, abyste jednali jako důvěrník %{resource_name} pro některé volby, které se budou konat na této platformě.
    Budete provádět úkoly podle potřeby. Zatím prosím vygenerujte své identifikační klíče. + start_tally: + email_intro: Hlasovací období pro volby %{resource_title} skončilo. Nyní prosím proveďte sčítání voleb, abyste zveřejnili konečné výsledky. + email_subject: Byl zahájen proces sčítání voleb %{resource_title}. + notification_title: Hlasovací období pro volby %{resource_title} skončilo. Nyní prosím proveďte sčítání voleb, abyste zveřejnili konečné výsledky. + votes: + accepted_votes: + email_intro: 'Váš hlas byl přijat! Pomocí vašeho hlasovacího tokenu: %{encrypted_vote_hash}, můžete ověřit svůj hlas zde.' + email_outro: Obdrželi jste toto oznámení, protože jste hlasovali pro %{resource_name} volby. + email_subject: Váš hlas pro %{resource_name} byl přijat. + notification_title: 'Váš hlas byl přijat. Ověřte svůj hlas zde pomocí hlasovacího tokenu: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Byli jste přiřazeni jako %{role} volební místnosti %{polling_station_name} v %{resource_title}. Můžete spravovat volební místnost z vyhrazené zóny volebního komisaře. + email_outro: Obdrželi jste toto oznámení, protože jste byli přiřazeni jako %{role} v %{polling_station_name}. + email_subject: Jste %{role} z volební místnosti %{polling_station_name}. + notification_title: Jste %{role} z volební místnosti %{polling_station_name} při hlasování %{resource_title}. + send_access_code: + instruction: 'Zde je váš přístupový kód, o který jste požádali: %{access_code}. S tím se budete moci podílet na %{voting}.' + subject: Váš přístupový kód pro účast v %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Hlasování je prostor, který vám umožňuje položit jasnou otázku všem lidem, kteří tvoří organizaci, vyzvat k účasti na hlasování, rozdmýchání a uspořádání rozpravy pro nebo proti reakci. Až nastane datum hlasování, můžete hlasovat a zveřejnit výsledky hlasování.

    Příklady: Hlasování se může týkat téměř jakéhokoliv aspektu, který ovlivňuje organizaci: některé příklady mění název nebo logo organizace nabízející několik alternativ, rozhodne, zda se stane součástí větší organizace, potvrdí nebo odmítne nový strategický plán nebo výsledek pracovní skupiny, nebo definovat, zda by pozice měly zůstat nejvýše v délce 1, 2 nebo 3 mandátů.

    \n" + page: "

    Hlasování je prostor, který vám umožňuje položit jasnou otázku všem lidem, kteří tvoří organizaci, vyzvat k účasti na hlasování, rozdmýchání a uspořádání rozpravy pro nebo proti reakci. Až nastane datum hlasování, můžete hlasovat a zveřejnit výsledky hlasování.

    Příklady: Hlasování se může týkat téměř jakéhokoliv aspektu, který ovlivňuje organizaci: některé příklady mění název nebo logo organizace nabízející několik alternativ, rozhodne, zda se stane součástí větší organizace, potvrdí nebo odmítne nový strategický plán nebo výsledek pracovní skupiny, nebo definovat, zda by pozice měly zůstat nejvýše v délce 1, 2 nebo 3 mandátů.

    \n" + title: Co jsou hlasování? + menu: + votings: Hlasování + participatory_spaces: + related_elections: + see_all: Zobrazit všechny volby + statistics: + elections_count: Volby + votings_count: Hlasování + votings: + admin: + ballot_styles: + create: + error: Při vytváření tohoto stylu hlasování došlo k chybě. + success: Styl hlasování byl úspěšně vytvořen. + destroy: + invalid: Při mazání tohoto stylu hlasování došlo k chybě. + success: Styl hlasování byl úspěšně smazán. + edit: + title: Upravit styl hlasování + update: Aktualizovat + form: + code_help: 'Tip: kód je spojitost mezi seznamem členů a stylem hlasování. Při nahrávání dat ze seznamu členů bude každému záznamu přiřazen styl hlasovacích lístků podle odpovídajícího kódu.' + election: Volby + questions: Otázky pro tento styl voleb + questions_help: 'Tip: vyberte otázky z volebních složek, které budou předloženy voličům přiřazeným k tomuto stylu hlasování.' + index: + actions: + confirm_destroy: Jste si jist? + destroy: Smazat + edit: Upravit + new: Nový styl hlasování + title: Akce + associated_census_data: Související záznamy ze seznamu obyvatel + explanation_callout: Styl hlasování specifikuje, jaké otázky budou předloženy u přítomného voliče za plentou. Ve stylu hlasování si můžete vybrat, jaké otázky z volebních složek tohoto hlasování patří k volbám. Kód stylu hlasování se používá k tomu, aby volič ze seznamu členů odpovídal na hlasování, které bude prezentováno za plentou. Pokud chcete vždy prezentovat všechny otázky, nevytvářejte žádný styl hlasování. + title: Styly hlasování + new: + create: Vytvořit + title: Vytvořit styl hlasování + update: + invalid: Při aktualizaci tohoto stylu hlasování došlo k chybě. + success: Styl hlasování byl úspěšně aktualizován. + content_blocks: + attachments_and_folders: + name: Přílohy a složky hlasování + header: + name: Záhlaví hlasování + highlighted_votings: + max_results: Maximální počet prvků k zobrazení + html_block_1: + name: Html blok 1 hlasování + html_block_2: + name: Html blok 2 hlasování + html_block_3: + name: Html blok 3 hlasování + main_data: + name: Název a popis + metrics: + name: Metriky hlasování + polling_stations: + name: Volební místnosti hlasování + related_elections: + name: Hlasovací volby + stats: + name: Statistiky hlasování + timeline: + name: Časová osa hlasování + index: + published: Zveřejněno + unpublished: Nezveřejněno + menu: + votings: Hlasování + votings_submenu: + attachment_collections: Složky + attachment_files: Soubory + attachments: Přílohy + ballot_styles: Styly hlasování + census: Seznam obyvatel + components: Komponenty + info: O tomto hlasování + landing_page: Vstupní stránka + monitoring_committee: Monitorovací výbor + monitoring_committee_election_results: Ověřit výsledky + monitoring_committee_members: Členové + monitoring_committee_polling_station_closures: Potvrdit certifikáty + monitoring_committee_verify_elections: Ověřit volby + polling_officers: Volební komisaři + polling_stations: Volební místnost + see_voting: Zobrazit hlasování + models: + ballot_style: + fields: + code: Kód + monitoring_committee_member: + fields: + email: E-mail + name: Název + polling_officer: + fields: + email: E-mail + name: Jméno + polling_station: Volební místnost (role) + polling_station: + fields: + address: Adresa + polling_station_managers: Manažeři + polling_station_president: Předseda + title: Název + voting: + fields: + created_at: Vytvořeno v + published: Publikováno + title: Název + monitoring_committee_election_results: + actions: + title: Akce + view: Zobrazit + index: + title: Vyberte si volby, u kterých chcete vidět výsledky + results: + bulletin_board: Vývěska + election_totals: Volební součet + polling_stations: Volební místnosti + result_types: + blank_answers: Prázdné odpovědi + blank_ballots: Prázdné hlasovací lístky + null_ballots: Prázdné hlasovací lístky + total_ballots: Celkový počet hlasovacích lístků + valid_ballots: Platné hlasovací lístky + selected: Vybráno + title: Výsledky voleb %{election_title} + totals: Celkem + show: + change_election: Změnit volbu + publish_results: Zveřejnit výsledky + publishing: Zveřejňování výsledků... + update: + invalid: Při publikování výsledků došlo k chybě. + rejected: Zveřejnění výsledků bylo výborem Vývěsky zamítnuto. Zkuste to znovu nebo se obraťte na správce systému. + success: Výsledky byly úspěšně publikovány. + monitoring_committee_members: + create: + invalid: Při vytváření tohoto člena monitorovacího výboru došlo k chybě. + success: Člen monitorovacího výboru byl úspěšně vytvořen. + destroy: + invalid: Při mazání tohoto člena monitorovacího výboru došlo k potížím. + success: Člen monitorovacího výboru byl úspěšně odstraněn. + form: + existing_user: Existující účastník + non_user: Pozvat nového účastníka + select_user: Hledat podle jména, e-mailu nebo přezdívky + user_type: Typ účastníka + index: + title: Monitorovací výbor + new: + create: Vytvořit + title: Vytvořit člena monitorovacího výboru + monitoring_committee_polling_station_closures: + actions: + title: Akce + validate: Ověřit + view: Zobrazit + closures: + change_election: Změnit volbu + signed: Podepsáno? + title: Hlasovací stanice pro volby %{election_title} + validated: Ověřeno? + edit: + change_polling_station: Zpět do volebních místností + monitoring_committee_notes: Poznámky + monitoring_committee_notes_placeholder: Zde nahlaste jakýkoliv incident + title: Výsledky voleb %{election_title} ve volební místnosti%{polling_station_title} + elections: + title: Vyberte volbu, kterou chcete ověřit + show: + change_polling_station: Zpět do volebních místností + monitoring_committee_notes: Poznámky monitorovacího výboru + validate: + error: Při ověřování uzavření došlo k chybě. + success: Uzavření bylo správně ověřeno. + monitoring_committee_verify_elections: + index: + download: Stáhnout + how_to_checksum: 'Chcete-li se ujistit, že stažený soubor nebyl během procesu stahování poškozen nebo zmanipulován, spusťte následující příkaz v konzoli a zkontrolujte, zda výstup odpovídá výše uvedenému kontrolním součtu:' + how_to_download: Chcete-li ověřit volby, stáhněte si ověřitelný soubor z tabulky výše. + how_to_run_verifier: 'Jakmile stáhnete soubor a ujistíte se, že je v pořádku, můžete pokračovat ve spuštění univerzálního ověřovače. Klonujte tento repozitář a z kořenové složky spusťte následující příkaz:' + how_to_title: Jak ověřit platnost voleb + not_available: Ještě není k dispozici + title: Volby + polling_officers: + create: + invalid: Při vytváření tohoto volebního komisaře došlo k chybě. + success: Volební komisař byla úspěšně vytvořen. + destroy: + invalid: Při mazání tohoto volebního komisaře došlo k chybě. + success: Volební komisař byla úspěšně smazán. + form: + existing_user: Existující účastník + non_user: Pozvat nového účastníka + select_user: Hledat podle jména, e-mailu nebo přezdívky + user_type: Typ účastníka + index: + role_manager: manažer + role_president: předseda + title: Volební komisaři + new: + create: Vytvořit + title: Vytvořit volebního komisaře + polling_officers_picker: + choose_polling_officers: Vybrat volební komisaře + no_polling_officers: Žádní volební komisaři neodpovídají vašim kritériím vyhledávání, nebo zde není žádný volební komisař. + polling_stations: + create: + invalid: Při vytváření této volební místnosti došlo k chybě. + success: Volební místnost byla úspěšně vytvořena. + destroy: + invalid: Při mazání této volební místnosti došlo k chybě. + success: Volební místnost byla úspěšně odstraněna. + edit: + title: Upravit volební místnost + update: Aktualizovat volební místnost + form: + address_help: 'Adresa: použita Geocoderem k nalezení polohy' + location_help: 'Umístění: zpráva určená voličům s uvedením přesného místa volební místnosti' + location_hints_help: 'Tipy k umístění: další informace. Příklad: patro budovy, kde se nachází volební místnost.' + polling_station_managers_help: 'Manažeři volební místnosti: úředníci, kteří budou působit jako správci volebních místností. Ujistěte se, že úředníci již byli vytvořeni pro volební místnosti a zda již nejsou přiděleni do jiné volební místnosti' + polling_station_president_help: 'Komisař volební místnosti: úředník, který bude předsedat volební místnosti. Ujistěte se, že úředník již byl vytvořen ve volební místnosti a zda již není přiřazen(a) k jiné volební místnosti' + select_president: Vyberte volebního komisaře jako předsedu volební místnosti + index: + title: Volební místnosti + new: + create: Vytvořit + title: Vytvořit volební místnost + update: + invalid: Při aktualizaci této volební místnosti došlo k chybě. + success: Volební místnost byla úspěšně aktualizována. + titles: + votings: Hlasování + votings: + actions: + confirm_destroy: Jsi si jist? + destroy: Zničit + new_voting: Nové hlasovací místo + create: + invalid: Při vytváření tohoto hlasování došlo k chybě. + success: Hlasování bylo úspěšně vytvořeno. + edit: + add_election_component: Pro toto hlasování nemáte nakonfigurovány žádné volby. Přidejte jej prosím do sekce Komponenty. + assign_missing_officers: Existují volební místnosti bez předsedy a/nebo manažerů. Prosím, přiřaďte je ze sekce volebních místností. + update: Aktualizovat + form: + banner_image: Obrázek banneru + census_contact_information: Kontaktní informace pro rejstřík obyvatel + census_contact_information_help: Tato kontaktní informace je určena pro účastníka, který chce nahlásit problémy se sčítáním. Může to být e-mailová adresa, kontaktní formulář na jiné stránce, průzkum pro návštěvníky atd. + introductory_image: Úvodní obrázek + promoted: Propagováno + select_a_voting_type: Vyberte prosím typ hlasování + show_check_census_help: Zda zobrazit odkaz "Mohu hlasovat?" v nabídce veřejného hlasování. + slug: Slug + slug_help_html: 'URL slug se používá k generování URL, které odkazují na toto hlasování. přijímá pouze písmena, čísla a pomlčky a musí začínat písmenem. Příklad: %{url}' + title: Název + voting_type: + hybrid: Hybridní + in_person: Osobní + online: Online + voting_type_label: Typ hlasování + new: + create: Vytvořit + title: Nové hlasování + update: + invalid: Při aktualizaci tohoto hlasování došlo k chybě. + success: Hlasování bylo úspěšně aktualizováno. + admin_log: + ballot_style: + create: "%{user_name} vytvořil styl voleb s kódem %{ballot_style_code} ve %{space_name}" + delete: "%{user_name} odstranil styl voleb s kódem %{ballot_style_code} ve %{space_name}" + update: "%{user_name} aktualizoval styl voleb s kódem %{ballot_style_code} ve %{space_name}" + census: + create: "%{user_name} vytvořil sčítání pro %{space_name}" + delete: "%{user_name} odstranil sčítání pro %{space_name}" + update: "%{user_name} aktualizoval sčítání pro %{space_name}" + monitoring_committee_member: + create: "%{user_name} přiřadil uživatele %{monitoring_committee_member_user} jako člena monitorovacího výboru ve %{space_name}" + delete: "%{user_name} odebral uživatele %{monitoring_committee_member_user} jako člena monitorovacího výboru ve %{space_name}" + polling_officer: + create: "%{user_name} přiřadil uživatele %{polling_officer_user} jako volebního úředníka ve %{space_name}" + delete: "%{user_name} zrušil přiřazení uživatele %{polling_officer_user} jako volebního úředníka ve %{space_name}" + polling_station: + create: "%{user_name} vytvořil hlasovací místo %{resource_name} ve %{space_name}" + delete: "%{user_name} odstranil hlasovací místo %{resource_name} ve %{space_name}" + update: "%{user_name} aktualizoval hlasovací místo %{resource_name} ve %{space_name}" + voting: + create: "%{user_name} vytvořil %{resource_name} hlasování" + publish: "%{user_name} zveřejnil %{resource_name} hlasování" + unpublish: "%{user_name} zrušil publikování %{resource_name} hlasování" + census: + admin: + census: + create: + invalid: Při nahrávání seznamu osob došlo k chybě, opakujte akci později. + invalid_csv_header: Hlavičky CSV chybí nebo nejsou správné - přečtěte si prosím pečlivě pokyny. + creating_data: + info_message: "Počkejte prosím, zpracováno %{processed_count} z %{raw_count} řádků ze souboru %{file} (to může trvat několik minut)." + delete: + button: Odstranit všechna data ze seznamu obyvatel + confirm: Odstranění všech dat z rejstříku osob nelze vrátit zpět. Opravdu chcete pokračovat? + destroy: + error: Došlo k chybě při odstraňování seznamu osob, opakujte akci později. + success: Seznam členů byl smazán. + export_access_codes: + button: Generovat hlasovací přístupové kódy + callout: Nyní můžete pokračovat v exportu přístupových kódů. Lze to provést pouze jednou. Jakmile zahájíte export, obdržíte e-mail s pokyny pro %{email} + confirm: Exportovat přístupové kódy můžete pouze jednou. Ujistěte se, že máte přístup k e-mailovému účtu %{email}. + file_not_exist: Tento soubor neexistuje. + launch_error: Problém se spuštěním exportu přístupových kódů. + launch_success: Export přístupových kódů byl spuštěn. Brzy obdržíte e-mail na %{email}. + exporting_access_codes: + info_message: "Počkejte prosím, export se připravuje, obdržíte ho brzy na %{email} (to může trvat několik minut)." + freeze: + callout: Seznam osob je zmrazen a nelze jej upravit. + help_html: | + Data z registru obyvatel byla nahrána, kódy byly úspěšně vygenerovány a exportovány.
    + Nyní jste připraveni zahájit volbu.
    + Použijte exportovaný CSV s individuálními kódy k distribuci ve vašem sčítání pomocí vlastních prostředků nebo aktivujte záložku "Můžu hlasovat", aby kdokoliv mohl tento kód načíst pomocí vlastních dat ze sčítání. + generate_access_codes: + button: Generovat hlasovací přístupové kódy + callout: Nyní můžete přejít k vygenerování přístupových kódů. Pamatujte, že po vygenerování přístupových kódů již nebudete moci měnit seznam osob. + confirm: Budete-li pokračovat, nebudete moci měnit seznam členů. + info_message_all: "Všechny řádky úspěšně importovány ze souboru %{file} (%{raw_count} z %{data_count})." + info_message_warn: Zkontrolujte, že žádná data nechybí, protože byly vytvořeno %{data_count} záznamů a nahraný soubor %{file} měl %{raw_count} řádků. + launch_error: Problém při spuštění generování přístupových kódů + launch_success: Vytváření kódů bylo spuštěno. + start_over: Vymažte prosím aktuální rejstřík obyvatel a začněte znovu se správným CSV souborem s platnými řádky. + generating_access_codes: + info_message: "Počkejte prosím, přístupové kódy pro hlasování jsou vytvářeny (toto může trvat několik minut)..." + new: + file_help: + explanation: 'Pokyny pro soubor:' + message_1: Jsou povoleny pouze soubory CSV (.csv). + message_2: Oddělovač sloupců musí být středník (";"). + has_ballot_styles_message: Nastavili jste styl hlasování. Ujistěte se, že pole "%{ballot_style_code_header}" v CSV odpovídá požadovanému kódu Stylu hlasování. + info_message: "Zatím neexistuje žádný seznam osob. Použijte prosím formulář níže pro vytvoření importu souboru CSV." + missing_ballot_styles_message: 'Pro toto hlasování zatím neexistuje žádný styl hlasování. Pokud chcete mít podmíněné otázky (tj.: představit voliče s různými otázkami podle např. okresu / regionu bydliště, musíte nastavit Styly pro hlasování před importováním seznamu osob. Pokud chcete všem voličům položit stejné otázky, můžete pokračovat v proceduře importu seznamu osob.' + submit: Odeslat CSV + title: Vytvořit seznam osob + show: + heading: Hlasovací prostor seznamu osob + upload_info: + csv_example_with_ballot_style: 'Příklad souboru se styly hlasování:' + csv_example_without_ballot_style: 'Příklad souboru bez stylů hlasování:' + csv_header_after: Nezahrnujte poslední pole ("%{ballot_style_code_header}"), pokud nepotřebujete styly hlasování / podmíněné otázky + csv_header_before: 'Soubor seznamu osob musí být soubor CSV s následujícím záhlavím:' + document_types: + identification_number: Identifikační číslo + passport: Cestovní pas + export_mailer: + access_codes_export: + click_button: 'Klikněte na následující odkaz pro stažení přístupových kódů.
    Soubor bude k dispozici do %{date}.
    Budete potřebovat 7-Zip (pro Windows), Keka (pro MacOS) nebo PeaZip (pro Linux) pro jeho otevření. Heslo: %{password}' + download: Stáhnout + subject: Export hlasovacích kódů pro %{voting_title} je k dispozici + vote_flow: + already_voted_in_person: Tento uživatel již hlasoval osobně a nemá právo hlasovat. + datum_not_found: Poskytnutá data neodpovídají žádnému voliči. + content_blocks: + highlighted_votings: + name: Zvýrazněné hlasování + landing_page: + polling_stations: + heading: Volební místnosti + no_polling_stations: Zatím nejsou žádné volební místnosti. + monitoring_committee_members: + actions: + confirm_destroy: Jste si jisti? + destroy: Smazat + new: Nový člen + title: Akce + pages: + home: + highlighted_votings: + active_spaces: Aktivní hlasování + see_all_spaces: Zobrazit všechna hlasování + polling_officer_zone: + closures: + back_to_polling_stations: Zpět do volební místnosti + certify: + add_photos: Přidat fotografie + edit_photos: Upravit fotografie + error: Došlo k chybě při připojování certifikátu, zkuste to prosím znovu. + heading: Přepočet hlasů - Nahrát certifikát + info_text: Nahrajte prosím fotografii volebního certifikátu o uzavření. + submit: Nahrát certifikát + success: Certifikát byl úspěšně nahrán. + completed: + sub_heading: Tento přepočet byl certifikován a nelze jej dále upravovat. + create: + error: Při vytváření uzavření došlo k chybě, opakujte akci později. + success: Uzavření bylo úspěšně vytvořeno. + edit: + confirm_start_over: Tímto odstraníte celkový počet hlasů a připojených poznámek. Jste si jisti? + heading: Přepočet hlasů - přepočet odpovědí + info_text: Uveďte prosím celkový počet odpovědí pro každou otázku. Toto musí odpovídat celkovému počtu odpovědí vložených v předchozím kroku (celkem %{total} odpovědí). + modal_ballots_results_count_error: + close_modal: Zavřít + info_text: Celkový počet hlasovacích lístků neodpovídá celkovému počtu obálek. Zkontrolujte prosím celkový počet hlasovacích lístků. + title: Součet hlasovacích lístků se nesčítá + valid: Očekávaný součet platných hlasů je %{expected}, ale součet platných otázek je %{current}. + save_recount: Uložit přepočet + start_over: Pokud je chyba v celkovém počtu, můžete vše smazat a začít znovu. + total_ballots: Celkový počet hlasovacích lístků + total_blank_ballots: Celkový počet prázdných hlasovacích lístků + total_null_ballots: Celkový počet prázdných hlasovacích lístků + total_valid_ballots: Celkový počet platných hlasovacích lístků + new: + election: 'Volby:' + heading: Přepočet hlasů + info_text: 'Uveďte celkový počet hlasovacích lístků (obálek) přepočtených v této volební místnosti:' + modal_ballots_count_error: + btn_validate_total: Ověření celkového přepočtu hlasovacích lístků + info_explanation_text: 'Zkontrolujte celkový počet hlasovacích lístků. Pokud je celkové číslo nesprávné, musíte monitorovacímu výboru poskytnout vysvětlení:' + info_text: Celkový počet vložených hlasovacích lístků (obálek) neodpovídá počtu osob, které v této volební místnosti hlasovaly. + message_for_monitoring_committee: Zpráva pro monitorovací výbor + review_recount: Zkontrolovat přepočet + text_area_placeholder: Zadejte prosím vaši zprávu + title: Celkový počet záznamů se nesčítá + total_ballots: 'Celkový počet hlasovacích lístků:' + total_people: 'Celkem lidí:' + polling_station: 'Volební místnost:' + submit: Ověřit celkové číslo + total_ballots_count: Počet hlasovacích lístků + show: + edit_count_votes: Špatná čísla? Můžete je stále upravovat. + heading: Počet hlasů + sub_heading: Přepočet musí být uzavřen nahráním certifikátu. Jakmile je to hotovo, bude přepočet zapečetěn a již ho nelze upravovat. + sign: + cancel: Zrušit + check_box: Přezkoumal jsem to a je to stejné jako fyzický certifikát o ukončení voleb + confirm: Ok, pokračovat + error: Došlo k chybě. Prosím zkuste to znovu. + heading: Přepočet hlasů - uzavření podpisu + info_text: Pokud budete pokračovat, již nemůžete upravovat žádné informace, tuto akci nelze vrátit zpět. + submit: Podepsat uzavření + success: Uzavření úspěšně podepsáno. + update: + error: Při aktualizaci výsledků uzávěrky došlo k chybě, zkuste to prosím později. + success: Výsledky uzávěrky byly úspěšně aktualizovány. + in_person_votes: + complete_voting: + available_answers: 'Dostupné odpovědi:' + census_verified: Tento uživatel zatím nehlasoval osobně. + census_verified_with_online_vote: Tento uživatel již hlasoval online. Pokud hlasují osobně, předchozí hlasy budou zneplatněny a bude to konečné hlasování. + complete_voting: Dokončené hlasování + identify_another: Identifikovat jiného účastníka + questions_title: 'Jsou oprávněni hlasovat v těchto otázkách:' + questions_title_voted: 'Tento uživatel již hlasoval on-line a je oprávněn hlasovat v následujících otázkách:' + voted: Uživatel hlasoval + create: + error: Hlasování nebylo zaregistrováno. Zkuste to prosím znovu. + in_person_form: + census_not_present: Tento uživatel není uveden v seznamu osob. + census_not_present_description: Musí jít do úřadu pro stížnosti ze sčítání nebo kontaktovat podporu. + date_of_birth: Datum narození + day: Den + day_placeholder: DD + document_number: Číslo dokumentu + document_number_placeholder: Číslo občanského průkazu + month: Měsíc + month_placeholder: MM + select: Vyberte typ dokumentu + title: 'Vyberte typ dokumentu a zadejte číslo dokladu účastníka:' + validate_document: Ověřit platnost dokladu + year: Rok + year_placeholder: RRRR + new: + back: Zpět do volební místnosti + title: Identifikovat a ověřit účastníka + show: + back: Zpět do volební místnosti + title: Čekání na registraci osobního hlasu + update: + error: Při registraci hlasování došlo k chybě. Zkuste to znovu. + success: + accepted: Hlasování bylo úspěšně zaregistrováno. + rejected: Hlasování nebylo přijato Volební vývěskou. Kontaktujte prosím správce systému. + verify_document: + census_present: Tento uživatel je uveden v seznamu osob. + name: Jméno + title: 'Zkontrolujte, zda jsou tyto údaje správné:' + verify_document: Ověřit doklad + menu: + polling_officer_zone: Zóna volebního komisaře + polling_officers: + index: + polling_officer_role_description: V některých volbách, které se na této platformě provádí, vám byly přiděleny funkce úředníka volební místnosti (předsedy nebo ředitele). + polling_station: + address: Adresa + count_votes: Počítat hlasy + election: Volby + identify_person: Identifikovat osobu + name: Jméno + no_polling_stations: Zatím nejste přiřazeni k žádnému volební místnosti. + role: Vaše role + show_closure: Zobrazit uzavření + title: Volební místnosti + voting: Hlasování + polling_officers: + actions: + confirm_destroy: Jste si jisti? + destroy: Smazat + new: Nový volební komisař + title: Akce + roles: + manager: Manažer + president: Předseda + unassigned: Nepřiřazen + polling_station_closure_certificate: + current_certificate: 'Aktuální certifikát:' + polling_station_closure_recount: + nota_option: Prázdná/Nic z výše uvedených + polling_officer_notes: 'Poznámky volebního komisaře:' + polling_officer_notes_blank: Nejsou žádné poznámky + recount_summary: 'Přepočet shrnutí:' + signed: Podepsáno + total_ballots: 'Celkový počet hlasovacích lístků:' + total_blank_ballots: 'Celkový počet nevyplněných hlasovacích lístků:' + total_null_ballots: 'Celkový počet prázdných hlasovacích lístků:' + total_valid_ballots: 'Celkový počet platných hlasovacích lístků:' + polling_stations: + actions: + confirm_destroy: Jste si jist? + destroy: Smazat + edit: Upravit + new: Nová volební místnost + title: Akce + votings: + access_code_modal: + email: Poslat e-mailem na %{email} + info: K účasti potřebujete přístupový kód. Pokud jste ho nedostali poštou, můžeme Vám poslat nový. + no_email: E-mail není k dispozici + no_sms: Žádné telefonní číslo není k dispozici + sms: Poslat SMS do %{sms} + title: Získat přístupový kód + check_census: + check_status: Zkontrolovat stav + description: Zde máte možnost zkontrolovat údaje ze seznamu osob, abyste věděli, zda máte právo účastnit se tohoto hlasování. Měli byste mít přístupový kód již k dispozici, ale pokud ho ztratíte, můžete ho znovu vyžádat, až budou vaše údaje správné. + error: + info: 'Zkuste to prosím znovu. Pokud si myslíte, že data v systému jsou nesprávná, můžete je nahlásit zde: %{census_contact_information}.' + title: Údaje, které jste zadali, nejsou v rejstříku členů pro toto hlasování + form_title: 'Vyplňte následující formulář pro kontrolu dat ze seznamu osob:' + invalid: Při kontrole seznamu osob došlo k chybě. + success: + access_link: e-mailem. + access_link_with_sms: prostřednictvím SMS nebo e-mailu. + info: Váš přístupový kód jste již měli obdržet poštou. V případě, že ho nemáte k dispozici, můžete o něj požádat zde + title: Vaše data seznamu osob jsou nesprávná! + title: Mohu hlasovat? + check_fields: + date_of_birth: Datum narození + day: Den + day_placeholder: DD + document_number: Číslo dokumentu + document_number_placeholder: Číslo občanského průkazu + document_type: Typ dokumentu + month: Měsíc + month_placeholder: MM + postal_code: PSČ + postal_code_placeholder: Poštovní směrovací číslo + select: Vyberte typ dokumentu + year: Rok + year_placeholder: RRRR + count: + title: + one: "%{count} hlasování" + few: "%{count} hlasování" + many: "%{count} hlasování" + other: "%{count} hlasů" + elections_log: + description: Volební protokol vám zobrazí všechny příslušné informace o každém hlasování. Například status úvodního ceremoniálu nebo tally nebo pokud jsou výsledky již zveřejněny. Klikněte na volby, o kterých chcete protokolem informovat. + title: Volební protokol + filters: + active: Aktivní + all: Vše + date: Datum + finished: Dokončeno + search: Hledat + upcoming: Nadcházející + index: + no_votings: Žádné hlasování neodpovídá vašim kritériím hledání. + only_finished: Momentálně nejsou žádné naplánované hlasování, ale zde můžete najít dokončené hlasování uvedené v seznamu. + title: Hlasování + login: + access_code: Přístupový kód + access_code_placeholder: Přístupový kód + ask_for_a_new_one: Požádejte o nový. + dont_have_access_code: Nemáte přístupový kód? + form_title: 'Pro přístup k hlasování vyplňte následující formulář:' + start_voting: Zahájit hlasování + title: Identifikujte se podle dat mého seznamu osob + no_census_contact_information: Zatím nejsou žádné kontaktní informace. + orders: + label: 'Seřadit hlasy podle:' + random: Náhodně + recent: Nejnovější + send_access_code: + invalid: Při odesílání přístupového kódu došlo k chybě. + success: Váš přístupový kód byl úspěšně odeslán. + show: + title: O tomto hlasování + votings_m: + badge_name: + finished: Dokončeno + ongoing: Probíhající + upcoming: Nadcházející + unspecified: Nespecifikováno + voting_type: + hybrid: Hybridní + in_person: Osobně + online: Online + layouts: + decidim: + voting_navigation: + check_census: Mohu hlasovat? + election_log: Volební protokol + votings: + index: + promoted_votings: Zvýrazněné hlasování + promoted_voting: + vote: Hlasovat diff --git a/decidim-elections/config/locales/da-DK.yml b/decidim-elections/config/locales/da-DK.yml new file mode 100644 index 00000000..347c94d5 --- /dev/null +++ b/decidim-elections/config/locales/da-DK.yml @@ -0,0 +1 @@ +da: diff --git a/decidim-elections/config/locales/da.yml b/decidim-elections/config/locales/da.yml new file mode 100644 index 00000000..347c94d5 --- /dev/null +++ b/decidim-elections/config/locales/da.yml @@ -0,0 +1 @@ +da: diff --git a/decidim-elections/config/locales/de.yml b/decidim-elections/config/locales/de.yml new file mode 100644 index 00000000..f1c8813d --- /dev/null +++ b/decidim-elections/config/locales/de.yml @@ -0,0 +1,1422 @@ +de: + activemodel: + attributes: + answer: + description: Beschreibung + image: Bild + proposals: Ähnliche Vorschläge + title: Titel + ballot_style: + code: Code + election: + description: Beschreibung + end_time: Die Abstimmung endet um + start_time: Die Abstimmung beginnt um + title: Titel + monitoring_committee_member: + email: E-Mail + name: Name + polling_officer: + email: E-Mail + name: Name + polling_station: + address: Adresse + location: Ort + location_hints: Standorthinweise + polling_station_managers: Vorgesetzte + polling_station_president_id: Präsident + title: Titel + question: + max_selections: Maximale Anzahl Antworten + min_selections: Nichts davon + title: Titel + trustees_participatory_space: + user_id: Teilnehmer + voting: + banner_image: Kopf-Bild + census_contact_information: Kontaktinformation der Erhebung + description: Beschreibung + end_time: Abstimmung endet + introductory_image: Einleitendes Bild + promoted: Hervorgehoben + scope_id: Themenbereich + show_check_census: '"Überprüfe Erhebung"-Seite anzeigen' + start_time: Abstimmung beginnt + title: Titel + voting_type: Art der Abstimmung + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Der Anhang muss erneut angehängt werden + ballot_result: + attributes: + base: + total_count_invalid: Die Gesamtzahl der Antworten stimmt nicht mit der gültig/leer/null Aufschlüsselung überein. + election: + attributes: + attachment: + needs_to_be_reattached: Der Anhang muss erneut angehängt werden + question_result: + attributes: + base: + blank_count_invalid: Die Gesamtzahl der leeren Antworten kann nicht größer sein als die Gesamtzahl der leeren Stimmzettel. + trustee: + attributes: + name: + cant_be_changed: kann nicht geändert werden + public_key: + cant_be_changed: kann nicht geändert werden + voting: + attributes: + voting_type: + inclusion: "%{value} ist kein gültiger Abstimmungstyp" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Wahllokal-Mitarbeitender ist bereits Präsident/Leiter eines Wahllokals. + voting: + different_organization: Die Abstimmung muss in der selben Organisation wie der Benutzer sein. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Der Wahllokal-Mitarbeiter und das Wahllokal müssen in der gleichen Abstimmung sein. + models: + decidim/elections/answer: + one: Antwort + other: Antworten + decidim/elections/election: + one: Wahl + other: Wahlen + decidim/elections/question: + one: Frage + other: Fragen + decidim/voting: + one: Abstimmung + other: Abstimmungen + decidim/votings/census/dataset: + one: Datensatz + other: Datensätze + decidim/votings/census/datum: + one: Daten + other: Daten + decidim/votings/polling_officer: + one: Wahllokal-Mitarbeiter + other: Wahllokal-Mitarbeitende + decidim/votings/polling_station: + one: Wahllokal + other: Wahllokale + decidim/votings/voting: + one: Abstimmung + other: Abstimmungen + decidim: + admin: + filters: + officers_assigned_eq: + label: Wahlvorstand + values: + assigned: Zugewiesen + unassigned: Nicht zugewiesen + role_eq: + label: Rolle + values: + manager: Manager + president: Präsident + unassigned: Nicht zugewiesen + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: In %{collection} nach Name/E-Mail/Nickname oder Wahllokal suchen. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : In %{collection} suchen nach Titel, Adresse oder Wahllokal-Mitarbeiter. + signed_eq: + label: Signiert + values: + 'false': Signiert + 'true': Nicht signiert + validated_eq: + label: Überprüft + values: + 'false': Nicht überprüft + 'true': Validiert + voting_publications: + create: + error: Bei der Veröffentlichung der Abstimmung ist ein Fehler aufgetreten. + success: Abstimmung erfolgreich veröffentlicht. + destroy: + error: Beim Aufheben der Veröffentlichung der Abstimmung ist ein Fehler aufgetreten. + success: Veröffentlichung der Abstimmung erfolgreich aufgehoben. + components: + elections: + actions: + vote: Abstimmung + name: Wahlen + settings: + global: + announcement: Ankündigung + step: + announcement: Ankündigung + elections: + actions: + confirm_destroy: Sind Sie sicher? + destroy: Löschen + edit: Bearbeiten + feedback: Feedback der Wähler + import: Importieren Sie Vorschläge in Projekte + manage_answers: Antworten verwalten + manage_questions: Fragen verwalten + manage_steps: Schritte verwalten + new_answer: Neue Antwort + new_election: Neue Wahl + new_question: Neue Frage + new_trustee: Neuer Wahlhelfer + preview: Vorschau + publish: Veröffentlichen + title: Aktionen + unpublish: Veröffentlichung rückgängig machen + admin: + answers: + create: + invalid: Beim Erstellen dieser Antwort ist ein Fehler aufgetreten. + success: Antwort erfolgreich erstellt. + destroy: + invalid: Beim Löschen dieser Antwort ist ein Fehler aufgetreten. + success: Antwort erfolgreich gelöscht. + edit: + title: Antwort bearbeiten + update: Antwort aktualisieren + index: + invalid_max_selections: Sie benötigen %{missing_answers} weitere Antwort/en, um die maximale Auswahl zu erfüllen. + title: Antworten + new: + create: Antwort erstellen + title: Neue Antwort + not_selected: Nicht ausgewählt + select: + disable: Antwort abwählen + enable: Antwort als ausgewählt markieren + invalid: Beim Auswählen dieser Antwort ist ein Fehler aufgetreten. + success: Antwort erfolgreich ausgewählt. + selected: Ausgewählt + unselect: + invalid: Beim Abwählen dieser Antwort ist ein Fehler aufgetreten. + success: Antwort erfolgreich abgwählt. + update: + invalid: Beim Aktualisieren dieser Antwort ist ein Fehler aufgetreten. + success: Antwort wurde erfolgreich aktualisiert. + elections: + create: + invalid: Beim Erstellen dieser Wahl ist ein Fehler aufgetreten. + success: Wahl wurde erfolgreich erstellt. + destroy: + invalid: Beim Löschen dieser Wahl ist ein Fehler aufgetreten. + success: Wahl wurde erfolgreich gelöscht. + edit: + title: Wahl bearbeiten + update: Wahl aktualisieren + form: + organization_time_zone: Überprüfen Sie, ob die Zeitzone in den Organisationseinstellungen korrekt ist. Die aktuelle Konfiguration ist %{time_zone} (%{time}). + index: + no_bulletin_board: Es ist kein Bulletin Board Server konfiguriert, das benötigt wird, um dieses Modul zu verwenden. Diese Aufgabe sollte vom Systemadministrator erledigt werden. + title: Wahlen + new: + create: Wahl erstellen + title: Neue Wahl + publish: + success: Die Wahl wurde erfolgreich veröffentlicht. + unpublish: + success: Die Veröffentlichung der Wahl wurde erfolgreich zurückgezogen. + update: + invalid: Beim Aktualisieren dieser Wahl ist ein Fehler aufgetreten. + success: Wahl wurde erfolgreich aktualisiert. + exports: + elections: Wahlen + feedback_form_answers: Antworten auf Feedback-Formular + mailers: + trustee_mailer: + body: + help_html: |- +

    Hallo %{user_name},


    +

    Sie wurden bei einigen Wahlen auf %{organization} als Wahlhelfer hinzugefügt.


    +

    Ein neuer Abschnitt "Wahlhelfer-Bereich" wurde Ihrem Konto hinzugefügt. Sie können von dort aus weitere Aktionen durchführen. Bitte generieren Sie als nächstes Ihre Identifikationsschlüssel.


    + subject: Sie wurden als Wahlhelfer zu %{resource_name} hinzugefügt + trustee_zone: Bring mich zum Wahlhelferbereich + menu: + trustees: Wahlhelfer + models: + answer: + name: Antwort + proposals_imports: + create: + invalid: Beim Importieren der Vorschläge in Antworten ist ein Problem aufgetreten. + success: "%{number} Vorschläge wurden erfolgreich in Antworten importiert." + new: + create: Importieren Sie Vorschläge in Projekte + no_components: Es gibt keine weiteren Vorschlagskomponenten in diesem partizipativen Raum, um die Vorschläge in Projekte zu importieren. + select_component: Bitte wählen Sie ein Objekt + title: Vorschläge importieren + questions: + create: + election_started: Die Wahl hat bereits begonnen. + invalid: Beim Erstellen dieser Frage ist ein Fehler aufgetreten. + success: Frage erfolgreich erstellt. + destroy: + invalid: Beim Löschen dieser Frage ist ein Fehler aufgetreten. + success: Frage erfolgreich gelöscht. + edit: + title: Frage bearbeiten + update: Frage aktualisieren + index: + title: Fragen + new: + create: Frage erstellen + title: Neue Frage + update: + invalid: Beim Aktualisieren dieser Frage ist ein Fehler aufgetreten. + success: Frage erfolgreich aktualisiert. + steps: + create_election: + census: Erhebung + errors: + census_codes_generated: Zugangscodes für die Erhebung wurden nicht generiert. + census_frozen: Zugangscodes für die Erhebung wurden nicht exportiert. + census_uploaded: Es wurde keine Erhebung für diese Wahl hochgeladen. + component_published: Die Wahlkomponente ist nicht veröffentlicht. + fix_it_text: Beheben + max_selections: Die maximale Anzahl Antworten für die Fragen ist inkorrekt + minimum_answers: Fragen müssen mindestens zwei Antworten haben. + minimum_questions: Die Wahl muss mindestens eine Frage haben. + published: Die Wahl ist nicht veröffentlicht. + time_before: Die Startzeit ist weniger als %{hours} vor Beginn der Wahl. + trustees_number: Mindestens %{number} Wahlhelfer mit Public Key werden benötigt. + invalid: Beim Erstellen dieser Wahl ist ein Fehler aufgetreten + no_trustees: Es sind keine Wahlhelfer eingetragen + not_used_trustee: "(nicht verwendet)" + public_key: + 'false': hat keinen Public Key + 'true': hat einen Public Key + requirements: + census_codes_generated: Zugangscodes für die Erhebung wurden generiert. + census_frozen: Zugangscodes für die Erhebung wurden exportiert und die Erhebung ist eingefroren. + census_uploaded: Erhebung wird hochgeladen. + component_published: Die Wahlkomponente ist veröffentlicht. + max_selections: Die maximale Anzahl Antworten aller Fragen sind korrekt. + minimum_answers: Jede Frage hat mindestens 2 Antworten. + minimum_questions: Die Wahl hat mindestens 1 Frage. + published: Die Wahl ist veröffentlicht. + time_before: Die Wahl wird mindestens %{hours} Stunden vor Beginn eingerichtet. + trustees_number: Mindestens %{number} Wahlhelfer mit Public Key sind vorhanden. + submit: Wahl einrichten + success: Wahl erfolgreich an das Bulletin Board gesendet. + technical_configuration: + authority_name: "Behördenname: %{value}" + bulletin_board_server: "Bulletin Board Server: %{value}" + scheme_name: "Schemaname: %{value}" + title: Technische Informationen anzeigen + title: Wahl einrichten + trustees: Wahlhelfer + created: + invalid: Beim Starten der Schlüssel-Zeremonie ist ein Fehler aufgetreten. + submit: Schlüssel-Zeremonie starten + success: Die Anfrage zum Starten der Schlüssel-Zeremonie wurde erfolgreich ans Bulletin Board gesendet. + title: Wahl erstellt + trustees: Wahlhelfer + key_ceremony: + continue: Fortfahren + title: Schlüsselzeremonie + key_ceremony_ended: + errors: + time_before: Die Wahl ist startbereit. Sie können die Abstimmungsperiode frühestens %{hours} Stunden vor der Startzeit (%{start_time}) eröffnen. + invalid: Beim Starten der Abstimmungsperiode ist ein Fehler aufgetreten. + requirements: + time_before: Die Wahl wird bald beginnen. Sie können die Abstimmungsperiode manuell eröffnen, oder sie wird automatisch zur Startzeit um %{start_time} eröffnet. + submit: Abstimmungsperiode eröffnen + success: Die Anfrage zum Starten der Abstimmungsperiode wurde erfolgreich an das Bulletin Board gesendet. + title: Startbereit + processing: In Verarbeitung... + results_published: + answer: Antwort + not_selected: Nicht ausgewählt + question: Frage + result: Ergebnis + selected: Ausgewählt + submit: Absenden + title: Ergebnisse veröffentlicht + tally_ended: + answer: Antwort + not_selected: Nicht ausgewählt + question: Frage + result: Ergebnis + selected: Ausgewählt + submit: Resultate veröffentlichen + success: Die Anfrage zur Veröffentlichung der Resultate wurde erfolgreich das Bulletin Board gesendet. + title: Berechnete Ergebnisse + tally_started: + continue: Fortfahren + invalid: Es ist ein Problem beim Melden des fehlenden Wahlhelfers aufgetreten. + mark_as_missing: Als fehlend markieren + mark_as_missing_description: Alle Wahlhelfer sollten an diesem Prozess teilnehmen. Wenn ein Wahlhelfer nicht am Prozess teilnehmen kann, können Sie ihn als fehlend markieren. + success: Eine Meldung über das Fehlen des Wahlhelfers wurde erfolgreich ans Forum gesendet. + tally_completion: Der Prozess wird abgeschlossen, wenn alle Wahlhelfer aktiv sind oder als fehlend markiert wurden. Mindestens %{quorum} Wahlhelfer sind erforderlich, um den Prozess abzuschließen. + title: Stimmenzähl-Prozess + undo_mark_as_missing: Ein Wahlhelfer, der versehentlich als fehlend markiert wurde, kann vor Abschluss des Prozesses teilnehmen. Sie können wie gewohnt fortfahren und die fehlend-Markierung wird ignoriert. + vote: + errors: + time_after: Die Wahl läuft noch. Sie müssen bis zum Ende (%{end_time}) warten, um die Abstimmungsperiode zu schließen. + invalid: Beim Schließen der Abstimmungsperiode ist ein Fehler aufgetreten. + requirements: + time_after: Die Wahl ist beendet. Sie können die Abstimmungsperiode manuell schließen, oder sie wird in wenigen Minuten automatisch geschlossen. + submit: Abstimmungsperiode schließen + success: Die Anfrage zum Schließen der Abstimmungsperiode wurde erfolgreich an das Bulletin Board gesendet. + title: Abstimmungsperiode + vote_ended: + invalid: Beim Starten der Stimmenzählung ist ein Fehler aufgetreten. + submit: Stimmenzählung starten + success: Die Anfrage zum Starten der Stimmenzählung wurde erfolgreich an das Bulletin Board gesendet. + text: Die Abstimmung ist beendet. Sie können jetzt die Stimmenzählung starten. + title: Abstimmungsperiode abgeschlossen + vote_stats: + no_vote_statistics_yet: Noch keine Statistiken zur Abstimmung + title: Statistiken zur Abstimmung + voters: Wähler + votes: Stimmen + trustees_participatory_spaces: + actions: + disable: Deaktivieren + enable: Berücksichtigen + create: + exists: Ein Wahlhelfer existiert für diesen partizipativen Raum. + invalid: Beim Erstellen des Wahlhelfers ist ein Problem aufgetreten. + success: Wahlhelfer erfolgreich erstellt. + delete: + invalid: Beim Entfernen dieses Wahlhelfers ist ein Fehler aufgetreten. + success: Wahlhelfer erfolgreich entfernt. + form: + select_user: Nutzer wählen + index: + title: Wahlhelfer + new: + create: Wahlhelfer erstellen + title: Neuer Wahlhelfer + update: + invalid: Es ist ein Problem beim Aktualisieren des Wahlhelfers %{trustee} aufgetreten. + success: Wahlhelfer %{trustee} erfolgreich aktualisiert. + admin_log: + election: + create: "%{user_name} hat das Wahl %{resource_name} in %{space_name} erstellt" + delete: "%{user_name} hat die Wahl %{resource_name} in %{space_name} gelöscht" + end_vote: "%{user_name} hat die Abstimmungsperiode für die Wahl %{resource_name} im Bulletin Board beendet" + publish: "%{user_name} hat die Wahl %{resource_name} in %{space_name} veröffentlicht" + publish_results: "%{user_name} hat die Resultate für die Wahl %{resource_name} in %{space_name} im Bulletin Board veröffentlicht" + report_missing_trustee: "%{user_name} meldete %{trustee_name} als fehlender Wahlhelfer während der Zählung für die Wahl %{resource_name} auf %{space_name} auf der Pinwand" + setup: "%{user_name} hat die Wahl %{resource_name} in %{space_name} auf dem Bulletin Board erstellt" + start_key_ceremony: "%{user_name} hat die Schlüssel-Zeremonie für die Wahl %{resource_name} auf %{space_name} im Bulletin Board eröffnet" + start_tally: "%{user_name} hat die Stimmenzählung für die Wahl %{resource_name} auf %{space_name} im Bulletin Board eröffnet" + start_vote: "%{user_name} hat die Abstimmungsperiode für die Wahl %{resource_name} auf %{space_name} im Bulletin Board gestartet" + unpublish: "%{user_name} hat die Veröffentlichung von %{resource_name} in %{space_name} aufgehoben" + update: "%{user_name} hat die Wahl %{resource_name} in %{space_name} erstellt" + trustee: + create: "%{user_name} hat den Benutzer %{trustee_user} als Wahlhelfer zugewiesen" + connection: + failed: + modal: + close: Schliessen + communication_lost: Leider scheint die Kommunikation mit dem Abstimmungsserver (Bulletin Board) unterbrochen.
    Möglicherweise ist die Internetverbindung gestört oder der Server ist überlastet.
    Bitte versuche es später noch einmal oder kontaktiere den Support, falls das Problem weiterhin besteht. + generic_error: Leider ist ein unbekannter Fehler aufgetreten. Es ist wahrscheinlich, dass Ihr Browser nicht unterstützt wird oder dass Sie den Modus "incognito" oder "private" verwenden, der nicht unterstützt wird. + title: Etwas ist schiefgelaufen + election_m: + badge_name: + finished: Abgeschlossen + ongoing: Aktiv + upcoming: Anstehend + end_date: Endet am + footer: + remaining_time: + one: "%{count} Stunde %{minutes} Minuten verbleiben zur Abstimmung." + other: "%{count} Stunden %{minutes} Minuten verbleiben zur Abstimmung." + view: Ansicht + vote: Abstimmung + label: + date: Daten + questions: Fragen %{count} + start_date: Startet am + unspecified: Nicht angegeben + elections: + count: + elections_count: + one: "%{count} Wahlen" + other: "%{count} Wahlen" + election_log: + chained_hash: Die verkettete Prüfsumme dieser Nachricht + complete: Abgeschlossen + creation_description: + complete: Die Wahl wurde erstellt und erfolgreich im Bulletin Board eingerichtet. + not_created: Die Wahl wurde noch nicht erstellt. + creation_title: Wahl erstellt + description: Dies ist das Wahl-Protokoll, in dem Sie den Status jedes Schritts überprüfen können, z.B. wann die Wahl erstellt wurde, ob die Stimmenzählung abgeschlossen ist und wann die Wahl geschlossen wird. + download: Herunterladen + key_ceremony_description: + complete: Die Schlüsselzeremonie wurde abgeschlossen. Alle Wahlhelfer haben gültige Schlüssel und haben die nötigen Backup-Schlüssel heruntergeladen. + not_started: Die Schlüsselzeremonie hat noch nicht begonnen. + started: Die Schlüsselzeremonie hat begonnen aber ist noch nicht abgeschlossen. + key_ceremony_title: Schlüsselzeremonie + not_available: Noch nicht verfügbar + not_created: Nicht erstellt + not_ready: Nicht bereit + not_started: Nicht begonnen + published: Veröffentlicht + results_description: + not_published: Die Resultate wurden noch nicht veröffentlicht. + published: Die Resultate wurden veröffentlicht. + results_title: Resultate + started: Begonnen + tally_description: + finished: Die Stimmenzählung wurde abgeschlossen. + not_started: Die Stimmenzählung hat noch nicht begonnen. + started: Die Stimmenzählung hat begonnen. + tally_title: Stimmenzählung + title: Wahl-Protokoll + unpublished: Nicht veröffentlicht + verifiable_results: + checksum: 'SHA256-Prüfsumme der Datei:' + description: + not_ready: Die überprüfbare Wahl-Datei und ihre SHA-256-Prüfsumme sind noch nicht verfügbar. Sobald die Resultate veröffentlicht sind, werden Sie diese Wahl verifizieren können. + ready: 'Hier können Sie die Wahl verifizieren. Zuerst müssen Sie die Datei herunterladen und sicherstellen, dass sie nicht beschädigt wurde. Führen Sie dazu den folgenden Befehl aus und überprüfen Sie dass die Ausgabe mit der Prüfsumme übereinstimmt:' + how_to_verify: 'Wenn Sie die Datei heruntergeladen haben und sicher gestellt haben, dass sie in Ordnung ist, können Sie den universellen Überprüfer ausführen. Klonen Sie dieses Repository und führen Sie den folgenden Befehl im Root-Verzeichnis aus:' + title: Wahlergebnisse überprüfen + verifiable_file: 'Überprüfbare Wahldatei:' + verify: Wahl überprüfen + vote_description: + finished: Der Abstimmungsprozess wurde abgeschlossen. + not_started: Der Abstimmungsprozess hat noch nicht begonnen. + started: Der Abstimmungsprozess hat begonnen. + vote_title: Abstimmungsprozess + filters: + active: Aktiv + all: Alle + date: Datum + finished: Abgeschlossen + upcoming: Anstehend + preview: + available_answers: 'Verfügbare Antworten:' + description: 'Dies sind die Fragen, die Sie im Abstimmungsverfahren finden werden:' + title: Wahlfragen + results: + description: 'Dies sind die Abstimmungsergebnisse für jede Frage:' + percentage: "%{count}%" + selected: Ausgewählt + title: Wahlergebnisse + votes: + one: "%{count} Stimme" + other: "%{count} Stimmen" + show: + action_button: + change_vote: Stimme ändern + vote: Jetzt abstimmen + vote_again: Erneut abstimmen + callout: + already_voted: Sie haben bei dieser Wahl bereits abgestimmt. Sie können Ihre Stimme ändern oder sie verifizieren. + pending_vote: Deine Stimme wird auf dem Server abgegeben. + vote_rejected: Es war nicht möglich, Ihre Stimme zu verifizieren. Bitte geben Sie ihre Stimme erneut ab. + election_log: Wahl-Protokoll + preview: Vorschau + verify: + already_voted: Bereits abgestimmt? + verify_here: Stimme hier verifizieren. + will_verify: Sie können ihre Stimme nach dem Start der Wahl verifizieren. + voting_period_status: + finished: Die Abstimmung begann am %{start_time} und endete am %{end_time} + ongoing: 'Abstimmung aktiv bis: %{end_time}' + upcoming: Die Abstimmung beginnt am %{start_time} + feedback: + answer: + invalid: Beim Senden Ihrer Meldung ist ein Fehler aufgetreten. + spam_detected: Bei der Beantwortung der Umfrage ist ein Fehler aufgetreten. Möglicherweise waren Sie zu schnell, können Sie es erneut versuchen? + success: Feedback erfolgreich versand. + models: + answer: + fields: + proposals: Vorschläge + selected: Ausgewählt + title: Titel + votes: Stimmen + election: + fields: + bb_status: Bulletin Board Status + end_time: Endet um + start_time: Beginnt um + title: Titel + verifiable_results_file_hash: SHA256-Prüfsumme der Datei + verifiable_results_file_url: Überprüfbare Wahldatei + question: + fields: + answers: Antworten + max_selections: Max. Antworten + title: Titel + trustees_participatory_space: + fields: + considered: berücksichtigt + email: E-Mail + inactive: inaktiv + name: Name + notification: Benachrichtigung versendet um + public_key: Öffentlicher Schlüssel + status: Status + orders: + label: Wahlen ordnen nach + older: Chronologisch + recent: Kürzlich hinzugefügt + trustee_zone: + elections: + backup_modal: + description: Diese Wahl wird im Bulletin Board erstellt. Es ist sehr wichtig dass alle Wahlhelfer, welche daran teilnehmen, eine Sicherungskopie dieser Schlüssel erstellen und sie an einem sicheren Ort speichern. Danach wird der Prozess fortgesetzt. + download_election_keys: Schlüssel herunterladen + title: Wahl-Schlüssel für %{election} sichern + key_ceremony_steps: + back: Zurück + description: Diese Wahl wird im Bulletin Board erstellt. Um diesen Prozess abzuschliessen, ist Ihre Teilnahme als Wahlhelfer nötig. + keys: + create_election: Schlüssel erstellen + key_ceremony: + joint_election_key: Gemeinsame Schlüssel erstellen + step_1: Schlüssel veröffentlichen + list: + status: Status + task: Aufgabe + process_warning: Sobald der Prozess gestartet ist, sollten Sie diese Seite nicht verlassen, bis der Prozess endet. Dies kann einige Minuten dauern, da alle Wahlhelfer verbunden sein müssen um den Prozess abzuschliessen. + start: Start + status: + completed: Abgeschlossen + pending: Ausstehend + processing: In Verarbeitung + title: Wahl-Schlüssel für %{election} erstellen + restore_modal: + description: Das Bulletin Board hat Informationen von Ihnen als Wahlhelfer zu dieser Wahl. Um den Vorgang fortzusetzen, laden Sie zuerst die Sicherungsdatei hoch, die während der vorherigen Sitzung erstellt wurde. + title: Wahl-Schlüssel für %{election} wiederherstellen + upload_election_keys: Wahl-Schlüssel hochladen + tally_started_steps: + back: Zurück + description: Die Ergebnisse dieser Wahl werden im Bulletin Board berechnet. Um diesen Prozess abzuschließen ist Ihre Teilnahme als Wahlhelfer erforderlich. + keys: + end_tally: Stimmenzählung abgeschloßen + tally: + cast: Stimmzählung abgeben + share: Stimmzählung austauschen + list: + status: Status + task: Aufgabe + process_warning: Sobald der Prozess gestartet ist, sollten Sie diese Seite nicht verlassen, bis der Prozess endet. Dies kann einige Minuten dauern, da alle Wahlhelfer verbunden sein müssen um den Prozess abzuschliessen. + start: Starten + status: + completed: Abgeschlossen + pending: Ausstehend + processing: In Verarbeitung + title: Stimmenzählung für %{election} + update: + error: Der Wahlstatus wurde nicht aktualisiert. + success: 'Der Wahlstatus ist: %{status}.' + menu: + trustee_zone: Wahlhelfer Zone + no_bulletin_board: + body: Ein konfiguriertes Bulletin-Board ist für diesen Bereich erforderlich. Kontaktieren Sie den Administrator für weitere Informationen. + title: Leider ist das Bulletin Board noch nicht eingerichtet. + trustees: + show: + elections: + list: + action_required: + 'false': 'Nein' + name: Aktion erforderlich? + 'true': Aktion ausführen + bb_status: Status + election: Wahl + voting_period: Abstimmungsperiode + no_elections: Zur Zeit sind Ihnen keine Aktionen als Wahlhelfer zugewiesen. Sie erhalten eine Benachrichtigung, wenn Ihre Mitarbeit erfordert wird. + title: Wahlen + identification_keys: + cancel: Abbrechen + generate: Identifikationsschlüssel generieren + generate_error: Beim Generieren der Identifikationsschlüssel ist ein Fehler aufgetreten. + generate_legend: Sie müssen ein Schlüsselpaar generieren, um an Wahlen als Wahlhelfer teilzunehmen. + generate_legend_1: Nach Drücken des Buttons sollten Sie die Datei mit den generierten Identifikationsschlüssel herunterladen. + generate_legend_2: Kopiere die heruntergeladene Datei auf ein sauberen USB-Stick + generate_legend_3: Stellen Sie sicher, dass Ihr Computer keine Kopie der Datei hat (z.B. überprüfen Sie die Download- und Desktop-Ordner). + generate_legend_4: Machen Sie eine weitere Kopie der Datei auf einem anderen externen Gerät und speichern Sie sie an einem sehr sicheren Ort. + submit: Einreichen + submit_legend: Nachdem Sie alle oben beschriebenen Schritte befolgt haben, führen Sie den Prozess durch, der den öffentlichen Identifikationsschlüssel an den Server sendet. + submit_title: Öffentlichen Identifikationsschlüssel einreichen + title: Wahlhelfer-Identifikationsschlüssel + upload: Lade deine Identifikationsschlüssel hoch + upload_error: + invalid_format: Die hochgeladene Datei enthält keinen Identifikationsschlüssel. + invalid_key: Die Identifikationsschlüssel in der hochgeladenen Datei können nicht geladen werden. + invalid_public_key: Die Identifikationsschlüssel in der hochgeladenen Datei stimmen nicht mit dem gespeicherten, öffentlichen Identifikationsschlüssel überein. + upload_legend: Der Server hat Ihren öffentlichen Identifikationsschlüssel, aber Ihr Browser hat ihn immer noch nicht. Sie müssen die Datei mit Ihren Identifikationsschlüssel aus dem Backup auf Ihren Computer importieren, den Sie nach der Generierung der Schlüssel gespeichert haben. + not_supported_browser_description: Es sieht so aus, als ob Sie einen Webbrowser verwenden, der als Wahlhelfer nicht verwendet werden kann. Stellen Sie sicher, dass Sie die neueste Version Ihres Browsers verwenden, oder versuchen Sie, einen der üblichen Browser zu verwenden, um Ihre Wahlhelfer-Aufgaben zu erfüllen. + not_supported_browser_title: Browser aktualisieren, um als Wahlhelfer zu agieren + trustee_role_description: + with_keys: Sie wurden als Wahlhelfer für einige Wahlen auf dieser Plattform bestimmt. + without_keys: Sie wurden als Wahlhelfer bestimmt. Bitte erstellen Sie Ihre Identifikationsschlüssel und laden sie hoch. + update: + success: Ihr öffentlicher Identifikationsschlüssel wurde erfolgreich gespeichert. + votes: + ballot_decision: + audit: (Stimmzettel überprüfen) + back: Abstimmungsvorgang erneut starten + ballot_hash: 'Ihre Wahlurnen-ID ist:' + cast: Stimmzettel abgeben, um Ihre Stimme abzuschließen + header: 'Stimmzettel ist verschlüsselt: Stimmzettel absenden oder überprüfen' + casting: + header: Stimme wird abgegeben... + text: Ihr Stimmzettel wird an der Wahlurne abgegeben. + confirm: + answer_number: Antwort %{number} + confirm: Bestätigen + edit: bearbeiten + header: Bestätige deine Stimme + intro: Hier ist eine Zusammenfassung der Wahl, die Sie abgeben möchten.
    Bitte bestätigen Sie Ihre Stimme oder bearbeiten Sie Ihre Antworten. + nota_option: Leer + confirmed: + back: Zurück zu den Wahlen + experience: Wie war Ihre Erfahrung mit uns? + feedback: Gib uns dein Feedback + header: Abstimmung bestätigt + lead: Ihre Stimme wurde abgegeben! + text: 'Sie können überprüfen, ob Ihre Stimme erfolgreich zur Wahlurne mit folgendem Identifikator hinzugefügt wurde: %{e_vote_poll_id}' + verify_link: Um dies zu überprüfen, kopieren Sie die Kennung und fügen Sie sie auf der Bestätigungsseite ein + create: + error: Beim Abgeben dieser Stimme ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut. + encrypting: + header: Stimme wird verschlüsselt... + text: Ihr Stimmzettel wird verschlüsselt, um ihre Abstimmungsfreiheit zu gewährleisten. + failed: + header: Abstimmung fehlgeschlagen + lead: Ihre Stimme wurde nicht abgegeben! + text: Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut. + try_again: Erneut versuchen + header: + ballot_decision: Stimme abgeben oder überprüfen + confirm: Bestätige deine Stimme + election: Wahl + register: Registrieren + vote_for: Über %{title} abstimmen + messages: + invalid_token: Ihre Sitzung in der Abstimmung ist nicht gültig. Versuchen Sie, erneut abzustimmen. + not_allowed: Sie dürfen zur Zeit nicht an dieser Wahl teilnehmen. + modal: + close: Schließen + proposal_header: 'Vorschläge:' + new: + answer_choices: Sie können bis zu %{choices} Antworten auswählen + more_information: Weitere Informationen + nota_option: Nichts davon + preview_alert: Dies ist eine Vorschau des Abstimmungsstandes. + question_steps: Frage %{current_step} von %{total_steps} + selections: "%{selected} von %{max_selections}
    ausgewählt" + onboarding_modal: + create_account: Konto erstellen + description: Möchten Sie ein neues Konto auf der Plattform erstellen? Sie können an den Prozessen teilnehmen und aktiv an der Organisation teilnehmen. + no_account: Nein, danke. + title: Neu auf dieser Plattform? + update: + error: Beim Aktualisieren der Abstimmungsstatus ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal. + verify: + content: + heading: Stimme verifizieren + info: Dieser Verifikator prüft, dass Ihre Stimme, welche durch eine verschlüsselte Kennung identifiziert wird, korrekt abgegeben wurde und sich in der Wahlurne befindet. + error: + header: Stimme nicht gefunden! + info: Diese Kennung wurde nicht in der Wahlurne %{link} gefunden. Versuchen Sie es erneut. + form: + back: Zurück zur Plattform + submit: Überprüfen + vote_identifier: 'Kennung:' + vote_identifier_help: Dies ist der Identifikator, der Ihnen gegeben wurde, nachdem Sie Ihre Stimme abgegeben haben (nicht der Code, um die Abstimmungskabine zu betreten). + header: + title: Stimme verifizieren + success: + header: Stimme gefunden! + info: Ihre verschlüsselte Stimme ist in der Wahlurne %{link}. + voting_step: + back: Zurück + continue: Weiter + warnings: + empty_filters: Es liegen noch keine Wahlen mit diesen Kriterien vor. + no_elections: Es gibt keine geplante Wahlen. + no_scheduled_elections: Derzeit sind keine Wahlen geplant. Sie finden hier alle vergangenen Wahlen aufgelistet. + events: + elections: + election_published: + email_intro: 'Die Wahl %{resource_title} ist jetzt für %{participatory_space_title} aktiv. Sie können es von dieser Seite sehen:' + email_outro: Sie haben diese Benachrichtigung erhalten, weil Sie %{participatory_space_title} folgen. Falls Sie keine solchen Benachrichtigungen mehr erhalten möchten, besuchen Sie den obigen Link. + email_subject: Die Wahl %{resource_title} ist jetzt aktiv für %{participatory_space_title}. + notification_title: Die Wahl %{resource_title} ist jetzt aktiv für %{participatory_space_title}. + trustees: + new_election: + email_intro: Sie sind Wahlhelfer für die %{resource_title} -Wahl. + email_outro: Sie haben diese Benachrichtigung erhalten, weil Sie als Wahlhelfer bei der Wahl %{resource_title} hinzugefügt wurden. + notification_title: Sie wurden als Wahlhelfer in der Abstimmung %{resource_title}. ausgewählt. Bitte führen Sie die Schlüsselzeremonie durch um die Wahl aufzusetzen. + new_trustee: + email_intro: Ein Administrator hat Sie als Wahlhelfer für %{resource_name} hinzugefügt. Sie sollten Ihren öffentlichen Schlüssel in Ihrer Wahlhelfer Zone anlegen + email_outro: Sie haben diese Benachrichtigung erhalten, weil Sie als Wahlhelfer für %{resource_name} hinzugefügt wurden. + email_subject: Sie sind Wahlhelfer für %{resource_name}. + notification_title: Sie wurden als Wahlhelfer auf %{resource_name} für einige Wahlen auf dieser Plattform hinzugefügt.
    Sie führen Aufgaben nach Bedarf aus. Bitte generieren Sie als nächstes Ihre Identifikationsschlüssel. + start_tally: + email_intro: Die Wahlperiode für die Wahl %{resource_title} ist zu Ende. Führen Sie nun bitte die Stimmenauszählung durch, um die Endergebnisse zu veröffentlichen. + email_outro: Sie haben diese Benachrichtigung erhalten, weil Sie ein Wahlhelfer für die Wahl %{resource_title} sind. + email_subject: Der Zählprozess für die Wahl %{resource_title} hat begonnen. + notification_title: Die Wahlperiode für die Wahl %{resource_title} ist zu Ende. Führen Sie nun bitte die Stimmenauszählung durch, um die Endergebnisse zu veröffentlichen. + votes: + accepted_votes: + email_intro: 'Deine Stimme wurde angenommen! Benutze deinen Stimm-Token: %{encrypted_vote_hash}, du kannst deine Stimme hier verifizieren.' + email_outro: Sie haben diese Benachrichtigung erhalten, weil Sie für die %{resource_name} -Wahl gestimmt haben. + email_subject: Deine Stimme für %{resource_name} wurde angenommen. + notification_title: 'Deine Stimme wurde akzeptiert. Verifiziere deine Stimme hier mit deinem Voting-Token: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Ihnen wurde die Rolle %{role} im Wahllokal %{polling_station_name} in %{resource_title} zugewiesen. Sie können das Wahllokal im dazu vorgesehenen Bereich verwalten. + email_outro: Sie haben diese Benachrichtigung erhalten, weil Ihnen die Rolle %{role} in %{polling_station_name} zugewiesen wurde. + email_subject: Sie haben die Rolle %{role} im Wahllokal %{polling_station_name}. + notification_title: Sie haben die Rolle %{role} im Wahllokal %{polling_station_name} der Abstimmung %{resource_title}. + send_access_code: + instruction: 'Hier ist der Zugangscode, den Sie angefordert haben: %{access_code}. Damit können Sie an %{voting} teilnehmen.' + subject: Ihr Zugangscode zur Teilnahme an %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Eine Abstimmung erlaubt es Ihnen, eine klare Frage an alle Personen einer Organisation zu stellen, zur Teilnahme an der Abstimmung aufzurufen und die Debatte für oder gegen eine Antwort zu eröffnen. Am Abstimmungsdatum können Sie abstimmen und die Resultate der Abstimmung veröffentlichen.

    Beispiele: Die Abstimmungen können fast beliebige Aspekte einer Organisation betreffen. Zum Beispiel kann über verschiedene Varianten des Namens oder des Logos der Organisation abgestimmt werden, Sie können sich dafür oder dagegen entscheiden, Teil einer grösseren Organisation zu werden, eine neue Strategie oder das Resultat einer Arbeitsgruppe bestätigen oder ablehnen oder die Anzahl Mandate in der Organisation definieren.

    \n" + page: "

    Eine Abstimmung erlaubt es Ihnen, eine klare Frage an alle Personen einer Organisation zu stellen, zur Teilnahme an der Abstimmung aufzurufen und die Debatte für oder gegen eine Antwort zu eröffnen. Am Abstimmungsdatum können Sie abstimmen und die Resultate der Abstimmung veröffentlichen.

    Beispiele: Die Abstimmungen können fast beliebige Aspekte einer Organisation betreffen. Zum Beispiel kann über verschiedene Varianten des Namens oder des Logos der Organisation abgestimmt werden, Sie können sich dafür oder dagegen entscheiden, Teil einer grösseren Organisation zu werden, eine neue Strategie oder das Resultat einer Arbeitsgruppe bestätigen oder ablehnen oder die Anzahl Mandate in der Organisation definieren.

    \n" + title: Was sind Abstimmungen? + menu: + votings: Abstimmungen + participatory_spaces: + related_elections: + see_all: Alle Wahlen anzeigen + statistics: + elections_count: Wahlen + votings_count: Abstimmungen + votings: + admin: + ballot_styles: + create: + error: Beim Erstellen dieser Wahlkategorie ist ein Problem aufgetreten. + success: Wahlkategorie erfolgreich erstellt. + destroy: + invalid: Beim Löschen dieser Wahlkategorie ist ein Problem aufgetreten. + success: Wahlkategorie erfolgreich gelöscht. + edit: + title: Wahlkategorie bearbeiten + update: Aktualisieren + form: + code_help: 'Hinweis: Der Code ist die Verbindung zwischen Erhebungsdaten und Wahlkategorie. Wenn die Erhebungsdaten hochgeladen werden, wird jedem Eintrag anhand dem Wahlkategorie-Code eine Wahlkategorie zugeteilt.' + election: Wahl + questions: Fragen für diese Wahlkategorie + questions_help: 'Hinweis: Wählen Sie die Fragen aus den Wahlkomponenten aus, welche Wählern angezeigt werden sollen, die dieser Wahlkategorie zugeordnet werden.' + index: + actions: + confirm_destroy: Sind Sie sicher? + destroy: Löschen + edit: Bearbeiten + new: Neue Wahlkategorie + title: Aktionen + associated_census_data: Zugehörige Erhebungseinträge + explanation_callout: Eine Wahlkategorie legt fest, welche Fragen einem Wähler bei der Abstimmung gestellt werden. In einer Wahlkategorie können Sie auswählen, welche Fragen aus den Wahlkomponenten dieser Wahl zu einer Wahlurne gehören. Anhand dem Wahlkategorie-Code aus den Erhebungsdaten des Wählers wird die Wahlkategorie ausgewählt, die einem Wähler angezeigt wird. Wenn Sie immer alle Fragen anzeigen wollen, erstellen Sie keine Wahlkategorien. + title: Wahlkategorien + new: + create: Erstellen + title: Wahlkategorie erstellen + update: + invalid: Beim Aktualisieren dieser Wahlkategorie ist ein Problem aufgetreten. + success: Wahlkategorie erfolgreich aktualisiert. + content_blocks: + attachments_and_folders: + name: Abstimmungsanhänge und Ordner + header: + name: Abstimmungsheader + highlighted_votings: + max_results: Maximale Anzahl angezeigte Elemente + html_block_1: + name: Abstimmung HTML Block 1 + html_block_2: + name: Abstimmung HTML Block 2 + html_block_3: + name: Abstimmung HTML Block 3 + main_data: + name: Titel und Beschreibung + metrics: + name: Abstimmungsmetriken + polling_stations: + name: Stimmlokale + related_elections: + name: Wahlen der Abstimmung + stats: + name: Abstimmungsstatistiken + timeline: + name: Abstimmungszeitfolge + index: + published: Veröffentlicht + unpublished: Nicht veröffentlicht + menu: + votings: Abstimmungen + votings_submenu: + attachment_collections: Ordner + attachment_files: Dateien + attachments: Dateianhänge + ballot_styles: Wahlkategorien + census: Erhebung + components: Komponenten + info: Über diese Abstimmung + landing_page: Landing-Page + monitoring_committee: Überwachungskomitee + monitoring_committee_election_results: Ergebnisse überprüfen + monitoring_committee_members: Mitglieder + monitoring_committee_polling_station_closures: Zertifikate überprüfen + monitoring_committee_verify_elections: Wahlen überprüfen + polling_officers: Wahllokal-Mitarbeitende + polling_stations: Wahllokale + see_voting: Abstimmung ansehen + models: + ballot_style: + fields: + code: Code + monitoring_committee_member: + fields: + email: E-Mail + name: Name + polling_officer: + fields: + email: E-Mail + name: Name + polling_station: Wahllokal (Rolle) + polling_station: + fields: + address: Adresse + polling_station_managers: Manager + polling_station_president: Präsident + title: Titel + voting: + fields: + created_at: Erstellt am + published: Veröffentlicht + title: Titel + monitoring_committee_election_results: + actions: + title: Aktionen + view: Anzeigen + index: + title: Wählen Sie eine Wahl aus, für die die Ergebnisse angezeigt werden sollen + results: + bulletin_board: Bulletin Board + election_totals: Wahltotale + polling_stations: Wahllokale + result_types: + blank_answers: Leere Antworten + blank_ballots: Leere Stimmzettel + null_ballots: Ungültige Stimmzettel + total_ballots: Total Stimmzettel + valid_ballots: Gültige Stimmzettel + selected: Ausgewählt + title: Ergebnisse der Wahl %{election_title} + totals: Gesamt + show: + change_election: Wahl ändern + publish_results: Ergebnisse veröffentlichen + publishing: Ergebnisse werden veröffentlicht... + update: + invalid: Beim Veröffentlichen der Ergebnisse ist ein Fehler aufgetreten. + rejected: Die Veröffentlichung der Ergebnisse wurde vom Bulletin Board abgelehnt. Versuchen Sie es erneut oder kontaktieren Sie den Systemadministrator. + success: Die Ergebnisse wurden erfolgreich veröffentlicht. + monitoring_committee_members: + create: + invalid: Beim Erfassen des Mitglieds des Wahlbeobachtungskomitees ist ein Fehler aufgetreten. + success: Mitglied des Wahlbeobachtungskomitees erfolgreich erstellt. + destroy: + invalid: Beim Löschen des Mitglieds des Wahlbeobachtungskomitees ist ein Fehler aufgetreten. + success: Mitglied des Wahlbeobachtungskomitees erfolgreich gelöscht. + form: + existing_user: Existierender Benutzer + non_user: Neuen Benutzer einladen + select_user: Suchen nach Name, E-Mail oder Kontoname + user_type: Benutzertyp + index: + title: Überwachungskomitee + new: + create: Hinzufügen + title: Überwachungskomitee-Mitglied erfassen + monitoring_committee_polling_station_closures: + actions: + title: Aktionen + validate: Überprüfen + view: Anzeigen + closures: + change_election: Wahl ändern + signed: Signiert? + title: Wahlbüros für die Wahl %{election_title} + validated: Überprüft? + edit: + change_polling_station: Zurück zu den Wahllokalen + monitoring_committee_notes: Bemerkungen + monitoring_committee_notes_placeholder: Vorfall melden + title: Ergebnisse für die Wahl %{election_title} im Wahllokal %{polling_station_title} + elections: + title: Wählen Sie eine Wahl aus, die Sie überprüfen möchten + show: + change_polling_station: Zurück zu den Wahllokalen + monitoring_committee_notes: Anmerkungen vom Überwachungsausschuss + validate: + error: Beim Überprüfen der Schliessung ist ein Problem aufgetreten. + success: Die Schliessung wurde korrekt überprüft. + monitoring_committee_verify_elections: + index: + download: Download + how_to_checksum: 'Um sicherzustellen, dass die heruntergeladene Datei während des Downloadprozesses nicht beschädigt oder manipuliert wurde, führen Sie den folgenden Befehl in Ihrer Konsole aus und überprüfen Sie, ob die Ausgabe der oben genannten Prüfsumme entspricht:' + how_to_download: Um eine Wahl zu überprüfen, laden Sie die überprüfbare Datei aus der obigen Tabelle herunter. + how_to_run_verifier: 'Wenn Sie die Datei heruntergeladen haben und sicher gestellt haben, dass sie in Ordnung ist, können Sie den universellen Überprüfer ausführen. Klonen Sie dieses Repository und führen Sie den folgenden Befehl im Root-Verzeichnis aus:' + how_to_title: Wie überprüfe ich die Gültigkeit einer Wahl + not_available: Noch nicht verfügbar + title: Wahlen + polling_officers: + create: + invalid: Beim Erfassen dieses Stimmenzählers ist ein Fehler aufgetreten. + success: Stimmenzähler erfolgreich erstellt. + destroy: + invalid: Beim Entfernen dieses Stimmenzählers ist ein Fehler aufgetreten. + success: Stimmenzähler erfolgreich gelöscht. + form: + existing_user: Existierender Benutzer + non_user: Neuen Benutzer einladen + select_user: Suchen nach Name, E-Mail oder Kontoname + user_type: Benutzertyp + index: + role_manager: Manager + role_president: Präsident + title: Wahllokal-Mitarbeitende + new: + create: Erfassen + title: Wahllokal-Mitarbeiter erfassen + polling_officers_picker: + choose_polling_officers: Wahllokal-Mitarbeitende auswählen + no_polling_officers: Keine Stimmenzähler passen zu Ihren Suchkriterien, oder es gibt keine Stimmenzähler. + polling_stations: + create: + invalid: Beim Erstellen dieses Wahllokals ist ein Fehler aufgetreten. + success: Wahllokal erfolgreich erfasst. + destroy: + invalid: Beim Löschen dieses Wahllokals ist ein Fehler aufgetreten. + success: Wahllokal erfolgreich gelöscht. + edit: + title: Wahllokal bearbeiten + update: Wahllokal aktualisieren + form: + address_help: 'Adresse: Wird vom Geocoder zur Lokalisierung verwendet' + location_help: 'Ort: Beschreibung des genauen Orts des Wahllokals für die Wähler' + location_hints_help: 'Ortshinweise: Zusätzliche Informationen. Beispiel: Das Stockwerk des Gebäudes in dem sich das Wahllokal befindet.' + polling_station_managers_help: 'Wahllokal-Manager: Die Mitarbeitenden die als Verwalter des Wahllokals agieren werden. Stellen Sie sicher dass diese Personen bereits als Wahllokal-Mitarbeitende erfasst wurden, und dass sie nicht bereits zu einem anderen Wahllokal zugewiesen sind' + polling_station_president_help: 'Wahllokal-Präsident: Der Stimmenzähler, der als Präsident des Wahllokals agiert. Stellen Sie sicher dass diese Person bereits als Stimmenzähler erfasst wurde, und dass er/sie nicht bereits zu einem anderen Wahllokal zugewiesen ist' + select_president: Wählen Sie einen Wahllokal-Mitarbeiter als Präsident für das Wahllokal + index: + title: Wahllokale + new: + create: Erstellen + title: Wahllokal erstellen + update: + invalid: Beim Aktualisieren dieses Wahllokals ist ein Fehler aufgetreten. + success: Wahllokal erfolgreich aktualisiert. + titles: + votings: Abstimmungen + votings: + actions: + confirm_destroy: Sind Sie sicher? + destroy: Löschen + new_voting: Neue Abstimmung + create: + invalid: Beim Erstellen dieser Abstimmung ist ein Problem aufgetreten. + success: Abstimmung erfolgreich erstellt. + edit: + add_election_component: Sie haben keine Wahl für diese Abstimmung konfiguriert. Bitte fügen Sie sie im Abschnitt "Komponenten" hinzu. + assign_missing_officers: Es gibt noch Wahllokale ohne Präsident und/oder Wahlleiter. Bitte ordnen Sie diese im Wahllokal-Abschnitt zu. + update: Aktualisieren + form: + banner_image: Kopf-Bild + census_contact_information: Kontaktinformation der Erhebung + census_contact_information_help: Die Kontaktinformationen richten sich an einen Teilnehmer, die Probleme mit einer Erhebung melden möchte. Es kann eine E-Mail-Adresse, ein Kontaktformular auf einer anderen Seite, eine Decidim-Umfrage für Besucher o.ä. sein. + introductory_image: Einleitendes Bild + promoted: Hervorgehoben + select_a_voting_type: Bitte einen Abstimmungs-Typ auswählen + show_check_census_help: Soll der Link "Kann ich abstimmen?" im Menü "öffentliche Abstimmung" angezeigt werden. + slug: URL-Segment + slug_help_html: 'URL-Slugs werden zum Generieren der URLs verwendet, die auf diese Abstimmung verweisen. Akzeptiert werden nur Buchstaben, Zahlen und Bindestriche und es muss mit einem Buchstaben beginnen. Beispiel: %{url}' + title: Titel + voting_type: + hybrid: Hybrid + in_person: Persönlich + online: Online + voting_type_label: Art der Abstimmung + new: + create: Erstellen + title: Neue Abstimmung + update: + invalid: Beim Aktualisieren dieser Abstimmung ist ein Problem aufgetreten. + success: Abstimmung erfolgreich aktualisiert. + admin_log: + ballot_style: + create: "%{user_name} hat eine Wahlkategorie mit dem Code %{ballot_style_code} in der Gruppe %{space_name} erstellt" + delete: "%{user_name} hat eine Wahlkategorie mit dem Code %{ballot_style_code} in der Gruppe %{space_name} gelöscht" + update: "%{user_name} hat eine Wahlkategorie mit dem Code %{ballot_style_code} in der Gruppe %{space_name} aktualisiert" + census: + create: "%{user_name} hat die Erhebung für die Gruppe %{space_name} erstellt" + delete: "%{user_name} hat die Erhebung für die Gruppe %{space_name} gelöscht" + update: "%{user_name} hat die Erhebung für die Gruppe %{space_name} aktualisiert" + monitoring_committee_member: + create: "%{user_name} hat den Benutzer %{monitoring_committee_member_user} als Mitglied des Wahlbeobachtungskomitees auf %{space_name} zugewiesen" + delete: "%{user_name} hat die Zuweisung des Benutzers %{monitoring_committee_member_user} als Mitglied des Wahlbeobachtungskomitees auf %{space_name} zurückgezogen" + polling_officer: + create: "%{user_name} hat den Benutzer %{polling_officer_user} als Stimmenzähler auf %{space_name} zugewiesen" + delete: "%{user_name} hat den die Zuweisung des Benutzers %{polling_officer_user} als Stimmenzähler auf %{space_name} zurückgezogen" + polling_station: + create: "%{user_name} hat das Wahllokal %{resource_name} in %{space_name} erstellt" + delete: "%{user_name} hat das Wahllokal %{resource_name} in %{space_name} gelöscht" + update: "%{user_name} hat das Wahllokal %{resource_name} in %{space_name} aktualisiert" + voting: + create: "%{user_name} hat die Abstimmung %{resource_name} erstellt" + publish: "%{user_name} hat die Abstimmung %{resource_name} veröffentlicht" + unpublish: "%{user_name} hat die Abstimmung %{resource_name} auf \"unveröffentlicht\" gesetzt" + census: + admin: + census: + create: + invalid: Beim Hochladen der Erhebung ist ein Problem aufgetreten, bitte versuchen Sie es später erneut. + invalid_csv_header: Die CSV-Kopfzeilen fehlen oder sind nicht korrekt - Bitte lesen Sie die Anweisungen sorgfältig. + creating_data: + info_message: "Bitte warten Sie, %{processed_count} von %{raw_count} Zeilen aus der Datei %{file} wurden verarbeitet (dies kann einige Minuten dauern)." + delete: + button: Alle Erhebungsdaten löschen + confirm: Das Löschen der Erhebungsdaten kann nicht rückgängig gemacht werden. Sind Sie sicher, dass Sie fortfahren möchten? + destroy: + error: Beim Löschen der Erhebungsdaten ist ein Fehler aufgetreten, bitte versuchen Sie es später erneut. + success: Erhebungsdaten gelöscht. + export_access_codes: + button: Zugangscodes für die Abstimmung exportieren + callout: Sie können jetzt die Zugangscodes exportieren. Dies kann nur einmal geschehen. Sobald Sie den Export gestartet haben, werden Sie eine E-Mail mit den Anweisungen unter %{email} erhalten + confirm: Sie können die Zugangscodes nur einmal exportieren. Stellen Sie sicher, dass Sie Zugang zum E-Mail-Konto %{email} haben. + file_not_exist: Diese Datei existiert nicht. + launch_error: Beim Starten des Zugangscode-Exports ist ein Problem aufgetreten. + launch_success: Der Zugangscode-Export wurde gestartet. Sie werden in Kürze eine E-Mail an %{email} erhalten. + exporting_access_codes: + info_message: "Bitte warten Sie, der Export wird gerade vorbereitet. Sie werden ihn in Kürze an %{email} erhalten (dies kann einige Minuten dauern)." + freeze: + callout: Die Erhebung ist geschlossen und kann nicht verändert werden. + help_html: | + Die Erhebungsdaten wurden hochgeladen, die Codes wurden erfolgreich erstellt und exportiert.
    + Sie sind jetzt bereit, die Wahl zu starten.
    + Benutzen Sie das exportierte CSV mit den einzelnen Codes, um es mit eigenen Mitteln entlang Ihrer Erhebung zu verteilen, oder aktivieren Sie die Registerkarte "Kann ich abstimmen", damit jeder diesen Code mittels eigener Zählungsdaten abrufen kann. + generate_access_codes: + button: Zugangscodes für die Abstimmung generieren + callout: Sie können nun mit der Generierung der Zugangscodes fortfahren. Beachten Sie, dass Sie nach der Generierung der Zugangscodes nicht mehr in der Lage sein werden, die Erhebung zu ändern. + confirm: Wenn Sie fortfahren, werden Sie die Erhebungsdaten nicht mehr ändern können. + info_message_all: "Alle Zeilen aus der Datei %{file} wurden erfolgreich importiert (%{raw_count} von %{data_count})." + info_message_warn: Bitte überprüfen Sie, dass keine Daten fehlen, denn es wurden %{data_count} Datensätze erstellt und die hochgeladene Datei %{file} enthielt %{raw_count} Zeilen. + launch_error: Problem beim Starten der Zugangscodes Generierung + launch_success: Codegenerierung gestartet. + start_over: Bitte löschen Sie die aktuelle Erhebung und starten Sie erneut mit einer korrekten CSV-Datei mit gültigen Zeilen. + generating_access_codes: + info_message: "Bitte warten Sie, die Stimmzugriffscodes werden generiert (dies kann einige Minuten dauern)..." + new: + file_help: + explanation: 'Hinweise für die Datei:' + message_1: Nur CSV-Dateien (.csv) sind erlaubt. + message_2: Das Trennzeichen zwischen den Spalten muss ein Semikolon (";") sein. + has_ballot_styles_message: Sie haben Wahlkategorien eingerichtet. Bitte stellen Sie sicher dass das Feld "%{ballot_style_code_header}" im CSV dem Code der gewünschten Wahlkategorie entspricht. + info_message: "Es gibt noch keine Erhebungsdaten. Bitte verwenden Sie das untenstehende Formular um die Erhebungsdaten aus einer CSV-Datei zu importieren." + missing_ballot_styles_message: 'Es gibt noch keine Wahlkategorien für diese Abstimmung. Wenn Sie variable Fragen haben möchten (d.h. dem Wähler unterschiedliche Fragen anzeigen, je nach z.B. Wohnort), müssen Sie die Wahlkategorien einrichten, bevor Sie die Erhebungsdaten importieren. Wenn Sie allen Wählern die gleichen Fragen stellen wollen, können Sie mit dem Import für Erhebungsdaten fortfahren.' + submit: CSV absenden + title: Erhebungsdaten erfassen + show: + heading: Erhebungsdaten der Abstimmung + upload_info: + csv_example_with_ballot_style: 'Ein Beispiel für die Datei mit Wahlkategorien:' + csv_example_without_ballot_style: 'Ein Beispiel für die Datei ohne Wahlkategorien:' + csv_header_after: Lassen Sie das letzte Feld ("%{ballot_style_code_header}") weg, wenn Sie keine Wahlkategorien / variablen Fragen benötigen + csv_header_before: 'Die Erhebungsdatei muss eine CSV-Datei mit folgender Kopfzeile sein:' + document_types: + identification_number: Identifikationsnummer + passport: Reisepass + export_mailer: + access_codes_export: + click_button: 'Klicken Sie auf den Link, um die Zugangscodes herunterzuladen.
    Die Datei steht bis zum %{date} zur Verfügung.
    Sie benötigen 7-Zip (für Windows), Keka (für MacOS) oder PeaZip (für Linux) um die Datei zu öffnen. Passwort: %{password}' + download: Herunterladen + subject: Der Export der Zugangscodes für die Abstimmung %{voting_title} ist verfügbar + vote_flow: + already_voted_in_person: Dieser Teilnehmer hat bereits persönlich abgestimmt und hat kein Stimmrecht mehr. + datum_not_found: Zu den eingegebenen Daten stimmen mit keinem Wähler überein. + content_blocks: + highlighted_votings: + name: Hervorgehobene Abstimmungen + landing_page: + polling_stations: + heading: Wahllokale + no_polling_stations: Es gibt noch keine Wahllokale. + monitoring_committee_members: + actions: + confirm_destroy: Sind Sie sicher? + destroy: Entfernen + new: Neues Mitglied + title: Aktionen + pages: + home: + highlighted_votings: + active_spaces: Aktive Abstimmungen + see_all_spaces: Alle Abstimmungen ansehen + polling_officer_zone: + closures: + back_to_polling_stations: Zurück zu den Wahllokalen + certify: + add_photos: Fotos hinzufügen + edit_photos: Fotos bearbeiten + error: Beim Anhängen des Zertifikats ist ein Fehler aufgetreten, bitte versuchen Sie es erneut. + heading: Stimmen-Neuzählung - Zertifikat hochladen + info_text: Bitte laden Sie ein Bild des Wahlabschlusszertifikats hoch. + submit: Zertifikat hochladen + success: Zertifikat erfolgreich hochgeladen. + upload_photos: Ein Bild des Wahlabschlusszertifikats hochladen + completed: + sub_heading: Diese Neuauszählung wurde zertifiziert und kann nicht mehr bearbeitet werden. + create: + error: Beim Abschließen ist ein Fehler aufgetreten, bitte versuchen Sie es später erneut. + success: Erfolgreich abgeschlossen. + destroy: + error: Ein Fehler trat auf beim Entfernen der Schließung. + success: Schließung wurde erfolgreich gelöscht. + edit: + confirm_start_over: Dadurch werden die Gesamtzahl der Stimmen und die angehängten Notizen entfernt. Sind Sie sicher? + heading: Nachzählung der Stimmen - Nachzählung der Antworten + info_text: Bitte beschreiben Sie die Gesamtzahl der Antworten für jede Frage. Dies muss mit der Gesamtzahl der im vorherigen Schritt eingeführten Antworten übereinstimmen (%{total} Antworten insgesamt). + modal_ballots_results_count_error: + blank: Erwartete insgesamt leere Bewertungen sind %{expected}, aber die Summe der leeren Fragen ist %{current}. + close_modal: Schließen + info_text: Die Gesamtzahl der Stimmzettel stimmt nicht mit der Gesamtzahl der Umschläge überein. Bitte überprüfen Sie die Anzahl der Stimmzettel. + title: Gesamtzahl der Stimmzettel stimmt nicht + total: Erwartete Gesamtsumme ist %{expected}, aber die Summe der gültigen, leeren und null Stimmzettel ist %{current}. + valid: Erwartete insgesamt leere Bewertungen ist %{expected}, aber die Summe der leeren Fragen ist %{current}. + save_recount: Neuzählung speichern + start_over: Wenn es einen Fehler in der Gesamtzahl gibt, können Sie alles löschen und neu starten. + total_ballots: Gesamtzahl der Stimmen + total_blank_ballots: Anzahl leere Stimmzettel + total_null_ballots: Anzahl ungültige Stimmzettel + total_valid_ballots: Anzahl gültige Stimmzettel + new: + election: 'Wahl:' + heading: Neuzählung + info_text: 'Bitte geben Sie die Gesamtzahl der Umschläge ein, die in diesem Wahllokal neu gezählt wurden:' + modal_ballots_count_error: + btn_validate_total: Neue Gesamtzahl der Stimmen überprüfen + info_explanation_text: 'Bitte überprüfen Sie die Gesamtzahl der Stimmzettel. Wenn die Gesamtzahl falsch ist, müssen Sie dem Wahlausschuss eine Erklärung abgeben:' + info_text: Die Gesamtzahl eingehender Stimmen (Umschläge) stimmt nicht mit der Liste der Personen überein, welche in diesem Wahllokal abgestimmt haben. + message_for_monitoring_committee: Nachricht für den Überwachungsausschuss + review_recount: Neuzählung überprüfen + text_area_placeholder: Bitte geben Sie Ihre Nachricht ein + title: Gesamtzahl stimmt nicht überein + total_ballots: 'Gesamtzahl der Stimmen:' + total_people: 'Gesamtzahl Personen:' + polling_station: 'Wahllokal:' + submit: Gesamtzahl überprüfen + total_ballots_count: Anzahl Stimmen + show: + edit_count_votes: Falsche Zahlen? Sie können sie trotzdem bearbeiten. + heading: Neuzählung + sub_heading: Die Neuauszählung muss durch das Hochladen eines Zertifikats geschlossen werden. Sobald dies erledigt ist, wird die Neuzählung versiegelt und kann nicht mehr bearbeitet werden. + sign: + cancel: Abbrechen + check_box: Ich habe dies geprüft und es ist dasselbe wie das physische Abschlusszertifikat + confirm: OK, fortfahren + error: Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut. + heading: Neuzählung - Schliessung signieren + info_text: Wenn Sie fortfahren, können Sie keine der Informationen mehr ändern. Diese Aktion kann nicht rückgängig gemacht werden. + submit: Schliessung signieren + success: Schliessung erfolgreich signiert. + update: + error: Beim Aktualisieren der Abschluss-Resultate ist ein Fehler aufgetreten, bitte versuchen Sie es später erneut. + success: Abschluss-Resultate erfolgreich aktualisiert. + in_person_votes: + complete_voting: + available_answers: 'Verfügbare Antworten:' + census_verified: Dieser Benutzer hat noch nicht persönlich gewählt. + census_verified_with_online_vote: Dieser Teilnehmer hat bereits online abgestimmt. Wenn er persönlich abstimmt, wird die vorherigen Abstimmung ungültig und dies wird die endgültige Abstimmung sein. + complete_voting: Abstimmung abschliessen + identify_another: Einen anderen Teilnehmer identifizieren + questions_title: 'Sie haben das Recht, bei folgenden Fragen abzustimmen:' + questions_title_voted: 'Dieser Teilnehmer hat bereits online abgestimmt und kann bei folgenden Fragen abstimmen:' + voted: Der Teilnehmer hat abgestimmt + create: + error: Die Stimme wurde nicht registriert. Bitte versuchen Sie es erneut. + in_person_form: + census_not_present: Dieser Teilnehmer ist nicht in der Erhebung aufgeführt. + census_not_present_description: Er/Sie muss zum Beschwerdenbüro gehen oder den Support kontaktieren. + date_of_birth: Geburtsdatum + day: Tag + day_placeholder: TT + document_number: Dokumentnummer + document_number_placeholder: ID-Nummer + month: Monat + month_placeholder: MM + select: Wählen Sie die Art des Dokuments aus + title: 'Wählen Sie den Dokumententyp und geben Sie die Dokumentennummer des Teilnehmers ein:' + validate_document: Dokument validieren + year: Jahr + year_placeholder: JJJJ + new: + back: Zurück zu den Wahllokalen + title: Einen Teilnehmer identifizieren und verifizieren + show: + back: Zurück zu den Wahllokalen + title: Warte auf die Registrierung der persönlichen Stimme + update: + error: Beim Registrieren dieser Stimme ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut. + success: + accepted: Die Stimme wurde erfolgreich registriert. + rejected: Die Abstimmung wurde vom Bulletin Board nicht akzeptiert. Bitte kontaktieren Sie den Systemadministrator. + verify_document: + census_present: Dieser Teilnehmer ist in der Erhebung aufgeführt. + name: Name + title: 'Überprüfen Sie, dass die folgenden Daten korrekt sind:' + verify_document: Dokument verifizieren + menu: + polling_officer_zone: Wahllokal-Mitarbeiter-Bereich + polling_officers: + index: + polling_officer_role_description: Ihnen wurde eine Rolle als Wahlhelfer (Präsident oder Manager) in Wahlen dieser Plattform zugewiesen. + polling_station: + address: Adresse + count_votes: Stimmen zählen + election: Wahl + identify_person: Eine Person identifizieren + name: Name + no_polling_stations: Sie sind noch zu keinem Wahllokal zugewiesen. + role: Ihre Rolle + show_closure: Abschluss ansehen + title: Wahllokale + voting: Abstimmung + polling_officers: + actions: + confirm_destroy: Sind Sie sicher? + destroy: Entfernen + new: Neuer Wahlhelfer + title: Aktionen + roles: + manager: Manager + president: Präsident + unassigned: Nicht zugewiesen + polling_station_closure_certificate: + current_certificate: 'Aktuelles Zertifikat:' + polling_station_closure_recount: + nota_option: Leer / Keine der oben genannten + polling_officer_notes: 'Notizen der Wahllokal-Mitarbeitenden:' + polling_officer_notes_blank: Es gibt keine Notizen + recount_summary: 'Zusammenfassung der Neuzählung:' + signed: Signiert + total_ballots: 'Gesamtzahl der Stimmzettel:' + total_blank_ballots: 'Anzahl leere Stimmzettel:' + total_null_ballots: 'Anzahl ungültige Stimmzettel:' + total_valid_ballots: 'Anzahl gültige Stimmzettel:' + polling_stations: + actions: + confirm_destroy: Sind Sie sicher? + destroy: Löschen + edit: Bearbeiten + new: Neues Wahllokal + title: Aktionen + votings: + access_code_modal: + email: Per E-Mail an %{email} senden + info: Sie benötigen einen Zugangscode um teilzunehmen. Wenn Sie keinen per Briefpost bekommen haben, können wir Ihnen einen neuen senden. + no_email: Keine E-Mail-Adresse verfügbar + no_sms: Keine Telefonnummer verfügbar + sms: Per SMS an %{sms} senden + title: Zugangscode + check_census: + check_status: Status überprüfen + description: Hier haben Sie die Möglichkeit, Ihre Erhebungsdaten zu überprüfen, um herauszufinden ob Sie das Recht haben, an dieser Abstimmung teilzunehmen. Sie sollten bereits einen Zugangscode haben, aber wenn Sie ihn verloren haben können Sie ihn erneut anfragen, sobald Ihre Daten korrekt sind. + error: + info: 'Bitte versuchen Sie es erneut. Wenn Sie denken, dass die Daten im System nicht korrekt sind, können Sie das hier melden: %{census_contact_information}.' + title: Die von Ihnen eingegebenen Daten sind nicht in der Erhebung für diese Abstimmung + form_title: 'Füllen Sie das folgende Formular aus, um Ihre Erhebungsdaten zu überprüfen:' + invalid: Bei der Überprüfung der Erhebung ist ein Problem aufgetreten. + success: + access_link: per E-Mail. + access_link_with_sms: via SMS oder E-Mail. + info: Sie sollten Ihren Zugangscode bereits brieflich bekommen haben. Falls nicht, können Sie ihn hier anfordern + title: Ihre Erhebungsdaten sind korrekt! + title: Kann ich abstimmen? + check_fields: + date_of_birth: Geburtsdatum + day: Tag + day_placeholder: TT + document_number: Dokumentennummer + document_number_placeholder: ID-Nummer + document_type: Dokumenttyp + month: Monat + month_placeholder: MM + postal_code: Postleitzahl + postal_code_placeholder: Postleitzahl + select: Wählen Sie die Art des Dokuments aus + year: Jahr + year_placeholder: JJJJ + count: + title: + one: "%{count} Abstimmung" + other: "%{count} Abstimmungen" + elections_log: + description: Das Wahlprotokoll zeigt Ihnen alle relevanten Informationen über jede Abstimmung. Zum Beispiel den Status der Schlüsselzeremonie oder der Zählung, oder ob die Ergebnisse bereits veröffentlicht wurden. Klicken Sie auf die Wahl, deren Protokoll-Informationen Sie einsehen möchten. + title: Wahl-Protokoll + filters: + active: Aktiv + all: Alle + date: Datum + finished: Abgeschlossen + search: Suchen + upcoming: Zukünftig + index: + no_votings: Keine Abstimmung gefunden, die Ihren Suchkriterien entspricht. + only_finished: Momentan gibt es keine geplanten Abstimmungen, aber hier finden Sie alle abgeschlossenen Abstimmungen. + title: Abstimmungen + login: + access_code: Zugangscode + access_code_placeholder: Zugangscode + ask_for_a_new_one: Erneut anfordern. + dont_have_access_code: Sie haben keinen Zugangscode? + form_title: 'Füllen Sie das folgende Formular aus um auf die Abstimmung zuzugreifen:' + start_voting: Jetzt abstimmen + title: Mich via Erhebungsdaten identifizieren + no_census_contact_information: Es sind noch keine Kontaktinformationen vorhanden. + orders: + label: 'Abstimmungen sortieren nach:' + random: Zufällig + recent: Neueste zuerst + send_access_code: + invalid: Beim Versenden des Zugangscodes ist ein Problem aufgetreten. + success: Ihr Zugangscode wurde erfolgreich versandt. + show: + title: Über diese Abstimmung + votings_m: + badge_name: + finished: Abgeschlossen + ongoing: Aktuell + upcoming: Zukünftig + unspecified: Keine Angabe + voting_type: + hybrid: Hybrid + in_person: Persönlich + online: Online + layouts: + decidim: + voting_navigation: + check_census: Kann ich abstimmen? + election_log: Wahl-Protokoll + votings: + index: + promoted_votings: Hervorgehobene Abstimmungen + promoted_voting: + vote: Abstimmen diff --git a/decidim-elections/config/locales/el.yml b/decidim-elections/config/locales/el.yml new file mode 100644 index 00000000..d31c6ed6 --- /dev/null +++ b/decidim-elections/config/locales/el.yml @@ -0,0 +1,1298 @@ +el: + activemodel: + attributes: + answer: + description: Περιγραφή + image: Εικόνα + proposals: Σχετικές προτάσεις + title: Τίτλος + ballot_style: + code: Κωδικός + election: + description: Περιγραφή + end_time: Η ψηφοφορία λήγει στις + start_time: Η ψηφοφορία ξεκινά στις + title: Τίτλος + monitoring_committee_member: + email: Email + name: Όνομα + polling_officer: + email: Email + name: Όνομα + polling_station: + address: Διεύθυνση + location: Τοποθεσία + location_hints: Υποδείξεις τοποθεσίας + polling_station_managers: Διαχειριστές + polling_station_president_id: Πρόεδρος + title: Τίτλος + question: + max_selections: Μέγιστος αριθμός επιλογών + min_selections: Καμία από τις παραπάνω επιλογές + title: Τίτλος + trustees_participatory_space: + user_id: Συμμετέχοντας + voting: + banner_image: Εικόνα banner + census_contact_information: Στοιχεία επικοινωνίας απογραφής + description: Περιγραφή + end_time: Η ψηφοφορία λήγει + introductory_image: Εισαγωγική εικόνα + promoted: Προβιβάστηκε + show_check_census: Εμφάνιση σελίδας "έλεγχος απογραφής" + start_time: Η ψηφοφορία αρχίζει + title: Τίτλος + voting_type: Τύπος ψηφοφορίας + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Πρέπει να επισυναφθεί ξανά + election: + attributes: + attachment: + needs_to_be_reattached: Πρέπει να επισυναφθεί ξανά + trustee: + attributes: + name: + cant_be_changed: δεν μπορεί να αλλαχθεί + public_key: + cant_be_changed: δεν μπορεί να αλλαχθεί + voting: + attributes: + voting_type: + inclusion: "%{value} δεν είναι έγκυρος τύπος ψηφοφορίας" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Ο αξιωματούχος εκλογών είναι ήδη πρόεδρος/διαχειριστής του εκλογικού τμήματος. + voting: + different_organization: Η ψηφοφορία πρέπει να είναι στον ίδιο οργανισμό με τον χρήστη. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Ο αξιωματούχος εκλογών πρέπει να είναι στην ίδια ψηφοφορία με το εκλογικό τμήμα. + models: + decidim/elections/answer: + one: Απάντηση + other: Απαντήσεις + decidim/elections/election: + one: Εκλογή + other: Εκλογές + decidim/elections/question: + one: Ερώτηση + other: Ερωτήσεις + decidim/voting: + one: Ψηφοφορία + other: Ψηφοφορίες + decidim/votings/census/dataset: + one: Σύνολο δεδομένων + other: Σύνολα δεδομένων + decidim/votings/census/datum: + one: Δεδομένο + other: Δεδομένα + decidim/votings/polling_officer: + one: Αξιωματούχος εκλογών + other: Αξιωματούχοι εκλογών + decidim/votings/polling_station: + one: Εκλογικό κέντρο + other: Εκλογικά κέντρα + decidim/votings/voting: + one: Ψηφοφορία + other: Ψηφοφορίες + decidim: + admin: + filters: + officers_assigned_eq: + label: Αξιωματούχοι + values: + assigned: Ανατεθειμένο + unassigned: Δεν έχει ανατεθεί + role_eq: + label: Ρόλος + values: + manager: Διαχειριστής + president: Πρόεδρος + unassigned: Μη αναθετημένο + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Αναζήτηση στο %{collection} με όνομα/email/ψευδώνυμο ή εκλογικό σταθμό. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Αναζήτηση στο %{collection} ανά τίτλο, διεύθυνση ή όνομα/email/ψευδώνυμο. + signed_eq: + label: Υπογεγραμμένο + values: + 'false': Υπογεγραμμένο + 'true': Δεν έχει υπογραφεί + validated_eq: + label: Επιβεβαιωμένο + values: + 'false': Μη επιβεβαιωμένο + 'true': Επικυρωμένες + components: + elections: + actions: + vote: Ψήφος + name: Εκλογές + settings: + global: + announcement: Ανακοίνωση + step: + announcement: Ανακοίνωση + elections: + actions: + confirm_destroy: Είστε βέβαιοι; + destroy: Καταστροφή + edit: Επεξεργασία + feedback: Σχόλια ψηφοφόρων + import: Εισαγωγή προτάσεων σε απαντήσεις + manage_answers: Διαχείριση απαντήσεων + manage_questions: Διαχείριση ερωτήσεων + manage_steps: Διαχείριση βημάτων + preview: Προεπισκόπηση + publish: Δημοσίευση + title: Ενέργειες + unpublish: Κατάργηση δημοσίευσης + admin: + answers: + create: + invalid: Υπήρξε ένα πρόβλημα κατά τη δημιουργία αυτής της απάντησης. + success: Η απάντηση δημιουργήθηκε επιτυχώς. + destroy: + invalid: Υπήρξε ένα πρόβλημα κατά τη διαγραφή αυτής της απάντησης. + success: Η απάντηση διαγράφηκε επιτυχώς. + edit: + title: Επεξεργασία απάντησης + update: Ενημέρωση απάντησης + index: + invalid_max_selections: Χρειάζεστε %{missing_answers} περισσότερες απαντήσεις για ταίριασμα με τις μέγιστες επιλογές. + title: Απαντήσεις + new: + create: Δημιουργία απάντησης + title: Νέα απάντηση + not_selected: Δεν έχει επιλεγεί + select: + disable: Αποεπιλογή απάντησης + enable: Σήμανση απάντησης ως επιλεγμένη + invalid: Παρουσιάστηκε πρόβλημα κατά την επιλογή αυτής της απάντησης. + success: Η απάντηση επιλέχθηκε επιτυχώς. + selected: Επιλεγμένο + unselect: + invalid: Παρουσιάστηκε πρόβλημα κατά την αποεπιλογή αυτής της απάντησης. + success: Η απάντηση αποεπιλέχθηκε επιτυχώς. + update: + invalid: Υπήρξε ένα πρόβλημα κατά την ενημέρωση αυτής της απάντησης. + success: Η απάντηση ενημερώθηκε επιτυχώς. + elections: + create: + invalid: Υπήρξε ένα πρόβλημα κατά τη δημιουργία αυτών των εκλογών. + success: Οι εκλογές δημιουργήθηκαν επιτυχώς. + destroy: + invalid: Υπήρξε ένα πρόβλημα κατά τη διαγραφή αυτών των εκλογών. + success: Οι εκλογές διαγράφηκαν επιτυχώς. + edit: + title: Επεξεργασία εκλογής + update: Ενημέρωση εκλογής + form: + organization_time_zone: Ελέγξτε ότι η ζώνη ώρας του οργανισμού είναι σωστή στις ρυθμίσεις του οργανισμού. Οι τρέχουσες ρυθμίσεις είναι %{time_zone} (%{time}). + index: + no_bulletin_board: Δεν υπάρχει ρύθμιση Bulletin Board server, ο οποίος είναι απαραίτητος για τη χρήση αυτής της ενότητας. Αυτή η εργασία θα πρέπει να γίνει από τον διαχειριστή συστήματος. + title: Εκλογές + new: + create: Δημιουργία εκλογής + title: Νέα εκλογή + publish: + success: Οι εκλογές έχουν δημοσιευθεί με επιτυχία. + unpublish: + success: Η δημοσίευση των εκλογών καταργήθηκε με επιτυχία. + update: + invalid: Υπήρξε ένα πρόβλημα κατά την ενημέρωση αυτών των εκλογών. + success: Οι εκλογές ενημερώθηκαν επιτυχώς. + exports: + elections: Εκλογές + feedback_form_answers: Απαντήσεις φόρμας σχολίων + mailers: + trustee_mailer: + subject: Έχετε προστεθεί ως επίτροπος στο %{resource_name} + trustee_zone: Πήγαινέ με στη ζώνη επιτρόπου + menu: + trustees: Επίτροποι + models: + answer: + name: Απάντηση + proposals_imports: + create: + invalid: Υπήρξε ένα πρόβλημα κατά την εισαγωγή των προτάσεων σε απαντήσεις. + success: "%{number} προτάσεις εισήχθησαν επιτυχώς σε απαντήσεις." + new: + create: Εισαγωγή προτάσεων σε απαντήσεις + no_components: Δεν υπάρχουν άλλα στοιχεία προτάσεων σε αυτόν τον χώρο συμμετοχής για εισαγωγή των προτάσεων σε απαντήσεις. + select_component: Επιλέξτε ένα στοιχείο + title: Εισαγωγή προτάσεων + questions: + create: + election_started: Οι εκλογές έχουν ήδη ξεκινήσει. + invalid: Υπήρξε ένα πρόβλημα κατά τη δημιουργία αυτής της ερώτησης. + success: Η ερώτηση δημιουργήθηκε επιτυχώς. + destroy: + invalid: Υπήρξε ένα πρόβλημα κατά τη διαγραφή αυτής της ερώτησης. + success: Η ερώτηση διαγράφηκε επιτυχώς. + edit: + title: Επεξεργασία ερώτησης + update: Ενημέρωση ερώτησης + index: + title: Ερωτήσεις + new: + create: Δημιουργία ερώτησης + title: Νέα ερώτηση + update: + invalid: Υπήρξε ένα πρόβλημα κατά την ενημέρωση αυτής της ερώτησης. + success: Η ερώτηση ενημερώθηκε επιτυχώς. + steps: + create_election: + census: Απογραφή + errors: + census_codes_generated: Δεν δημιουργούνται κωδικοί πρόσβασης για την απογραφή. + census_frozen: Δεν εξάγονται κωδικοί πρόσβασης για την απογραφή. + census_uploaded: Δεν έχει ανέβει απογραφή για αυτές τις εκλογές. + component_published: Το στοιχείο των εκλογών δεν έχει δημοσιευθεί. + max_selections: Οι ερωτήσεις δεν έχουν σωστή τιμή για το ποσό των απαντήσεων + minimum_answers: Οι ερωτήσεις πρέπει να έχουν τουλάχιστον δύο απαντήσεις. + minimum_questions: Οι εκλογές πρέπει να έχουν τουλάχιστον μια ερώτηση. + published: Οι εκλογές δεν έχουν δημοσιευθεί. + time_before: Η ώρα έναρξης είναι σε λιγότερο από %{hours} πριν από την έναρξη των εκλογών. + trustees_number: Ο συμμετοχικός χώρος πρέπει να έχει τουλάχιστον %{number} επιτρόπους με δημόσιο κλειδί. + invalid: Υπήρξε ένα πρόβλημα κατά τη δημιουργία αυτών των εκλογών + no_trustees: Δεν υπάρχουν Επίτροποι για αυτόν τον συμμετοχικό χώρο + not_used_trustee: "(δεν χρησιμοποιείται)" + public_key: + 'false': δεν έχει δημόσιο κλειδί + 'true': έχει δημόσιο κλειδί + requirements: + census_codes_generated: Δημιουργούνται κωδικοί πρόσβασης για την απογραφή. + census_frozen: Οι κωδικοί πρόσβασης για την απογραφή εξάγονται και η απογραφή παγώνει. + census_uploaded: Η απογραφή φορτώθηκε. + component_published: Το στοιχείο των εκλογών δεν έχει δημοσιευθεί. + max_selections: Όλες οι ερωτήσεις έχουν σωστή τιμή για το μέγιστο των απαντήσεων. + minimum_answers: Κάθε ερώτηση έχει τουλάχιστον 2 απαντήσεις. + minimum_questions: Οι εκλογές έχουν τουλάχιστον μια ερώτηση. + published: Οι εκλογές έχουν δημοσιευθεί. + time_before: Η ρύθμιση γίνεται τουλάχιστον %{hours} πριν ξεκινήσουν οι εκλογές. + trustees_number: Ο συμμετοχικός χώρος πρέπει να έχει τουλάχιστον %{number} επιτρόπους με δημόσιο κλειδί. + submit: Ρύθμιση εκλογών + success: Οι εκλογές απεστάλησαν επιτυχώς στον Πίνακα Ανακοινώσεων. + title: Ρύθμιση εκλογών + trustees: Επίτροποι Εκλογών + created: + invalid: Υπήρξε ένα πρόβλημα κατά την έναρξη της διαδικασίας του κλειδιού. + submit: Ξεκινήστε την διαδικασίας του κλειδιού + success: Το αίτημα έναρξης της διαδικασίας κλειδιού εστάλη επιτυχώς στον Πίνακα Ανακοινώσεων. + title: Οι εκλογές δημιουργήθηκαν + trustees: Επίτροποι + key_ceremony: + continue: Συνέχεια + title: Διαδικασία κλειδιού + key_ceremony_ended: + errors: + time_before: Οι εκλογές είναι έτοιμες να ξεκινήσουν. Πρέπει να περιμένετε μέχρι %{hours} ώρες πριν την ώρα έναρξης (%{start_time}) για να ξεκινήσετε την περίοδο της ψηφοφορίας. + invalid: Υπήρξε ένα πρόβλημα κατά την έναρξη της περιόδου ψηφοφορίας. + requirements: + time_before: Οι εκλογές θα ξεκινήσουν σύντομα. Μπορείτε να ξεκινήσετε την περίοδο ψηφοφορίας χειροκίνητα, ή θα ξεκινήσει αυτόματα πριν από την ώρα έναρξης, στις %{start_time}. + submit: Έναρξη περιόδου ψηφοφορίας + success: Το αίτημα έναρξης της διαδικασίας κλειδιού εστάλη επιτυχώς στον Πίνακα Ανακοινώσεων. + title: Έτοιμο να ξεκινήσει + processing: Επεξεργασία... + results_published: + answer: Απάντηση + not_selected: Δεν έχει επιλεγεί + question: Ερώτηση + result: Αποτέλεσμα + selected: Επιλεγμένο + submit: Υποβολή + title: Τα αποτελέσματα δημοσιεύτηκαν + tally_ended: + answer: Απάντηση + not_selected: Μη επιλεγμένο + question: Ερώτηση + result: Αποτέλεσμα + selected: Επιλεγμένο + submit: Δημοσίευση αποτελεσμάτων + success: Το αίτημα δημοσίευσης των αποτελεσμάτων εστάλη με επιτυχία στον Πίνακα Ανακοινώσεων. + title: Υπολογισμένα αποτελέσματα + tally_started: + continue: Συνέχεια + invalid: Υπήρξε ένα πρόβλημα κατά την αναφορά του απόντα επιτρόπου. + mark_as_missing_description: Όλοι οι επίτροποι θα πρέπει να συμμετέχουν σε αυτήν τη διαδικασία, αλλά αν ένας επίτροπος δεν μπορεί να συμμετάσχει στη διαδικασία, μπορείτε να τον επισημάνετε ως απόντα. + success: Η αναφορά απόντα επιτρόπου εστάλη με επιτυχία στον Πίνακα Ανακοινώσεων. + title: Διαδικασία καταμέτρησης + vote: + errors: + time_after: Οι εκλογές συνεχίζονται. Πρέπει να περιμένετε μέχρι την ώρα λήξης (%{end_time}) για να τερματίσετε την περίοδο ψηφοφορίας. + invalid: Υπήρξε ένα πρόβλημα κατά τον τερματισμό της περιόδου ψηφοφορίας. + requirements: + time_after: Οι εκλογές τέλειωσαν. Μπορείτε να τερματίσετε την περίοδο ψηφοφορίας χειροκίνητα, ή θα τερματιστούν αυτόματα σε λίγα λεπτά. + submit: Τερματισμός περιόδου ψηφοφορίας + success: Το αίτημα για τερματισμό της περιόδου ψηφοφορίας εστάλη με επιτυχία στον Πίνακα Ανακοινώσεων. + title: Περίοδος ψηφοφορίας + vote_ended: + invalid: Υπήρχε ένα πρόβλημα κατά την έναρξη της καταμέτρησης. + submit: Έναρξη καταμέτρησης + success: Το αίτημα έναρξης καταμέτρησης εστάλη επιτυχώς στον Πίνακα Ανακοινώσεων. + text: Η ψηφοφορία έχει λήξει. Μπορείτε να ξεκινήσετε την καταμέτρηση τώρα. + title: Λήξη περιόδου ψηφοφορίας + vote_stats: + no_vote_statistics_yet: Δεν υπάρχουν στατιστικά στοιχεία ψήφου ακόμα + title: Στατιστικά Ψηφοφορίας + voters: Ψηφοφόροι + votes: Ψήφοι + trustees_participatory_spaces: + actions: + disable: Ανενεργό + enable: Εξετάστε + create: + exists: Ο επίτροπος υπάρχει για αυτόν τον συμμετοχικό χώρο. + invalid: Υπήρξε ένα πρόβλημα κατά τη δημιουργία ενός επιτρόπου. + success: Ο επίτροπος δημιουργήθηκε επιτυχώς. + delete: + invalid: Παρουσιάστηκε ένα πρόβλημα κατά την αφαίρεση αυτού του επιτρόπου. + success: Ο επίτροπος αφαιρέθηκε επιτυχώς. + form: + select_user: Επιλογή χρήστη + index: + title: Επίτροποι + new: + create: Δημιουργία Επιτρόπου + title: Νέος Επίτροπος + update: + invalid: Υπήρξε ένα πρόβλημα κατά την ενημέρωση του επιτρόπου %{trustee}. + success: Ο επίτροπος %{trustee} ενημερώθηκε με επιτυχία. + admin_log: + election: + create: "Ο/Η %{user_name} ενημέρωσε τις εκλογές %{resource_name} του %{space_name}" + delete: "%{user_name} διέγραψε την εκλογή %{resource_name} από %{space_name}" + end_vote: "Ο/Η %{user_name} τερμάτισε την περίοδο ψηφοφορίας για την εκλογή %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + publish: "Ο/Η %{user_name} δημοσίευσε τις εκλογές %{resource_name} του %{space_name}" + publish_results: "%{user_name} δημοσίευσε τα αποτελέσματα για τις εκλογές %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + report_missing_trustee: "Ο/Η %{user_name} ανέφερε τον %{trustee_name} ως απόντα επίτροπο κατά τη διάρκεια της καταμέτρησης για τις εκλογές %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + setup: "Ο/Η %{user_name} δημιούργησε τις εκλογές %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + start_key_ceremony: "Ο/Η %{user_name} ξεκίνησε την διαδικασία κλειδιού για τις εκλογές %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + start_tally: "Ο/Η %{user_name} ξεκίνησε την καταμέτρηση για τις εκλογές %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + start_vote: "Ο/Η %{user_name} ξεκίνησε την περίοδο ψηφοφορίας για τις εκλογές %{resource_name} του %{space_name} στον Πίνακα Ανακοινώσεων" + unpublish: "Ο/Η %{user_name} κατάργησε τη δημοσίευση του %{resource_name} των εκλογών %{space_name}" + update: "Ο/Η %{user_name} ενημέρωσε τις εκλογές %{resource_name} του %{space_name}" + trustee: + create: "Ο/Η %{user_name} έθεσε τον χρήστη %{trustee_user} ως Επίτροπο" + connection: + failed: + modal: + close: Κλείσιμο + communication_lost: Δυστυχώς, φαίνεται ότι η επικοινωνία με τον διακομιστή ψηφοφορίας (Bulletin Board) έχει χαθεί.
    Μπορεί η σύνδεση στο Διαδίκτυο να έχει διακοπεί ή ο διακομιστής προορισμού είναι πολύ απασχολημένος.
    Μπορείτε να δοκιμάσετε ξανά αργότερα ή να επικοινωνήσετε με την υποστήριξη αν αυτό το πρόβλημα επιμείνει. + generic_error: Δυστυχώς, συνέβη ένα άγνωστο σφάλμα. Είναι πιθανό ότι το πρόγραμμα περιήγησής σας δεν υποστηρίζεται ή ότι χρησιμοποιείτε τη λειτουργία "incognito" ή "private" που δεν υποστηρίζεται. + title: Κάτι πήγε στραβά + election_m: + badge_name: + finished: Ολοκληρώθηκε + ongoing: Ενεργό + upcoming: Προσεχείς + end_date: Τελειώνει + footer: + remaining_time: + one: "%{count} ώρα %{minutes} λεπτά απομένουν να ψηφίσετε." + other: "%{count} ώρες %{minutes} λεπτά απομένουν να ψηφίσετε." + view: Προβολή + vote: Ψήφος + label: + date: Ημερομηνίες + questions: Ερωτήσεις %{count} + start_date: Αρχίζει + unspecified: Δεν προσδιορίστηκε + elections: + count: + elections_count: + one: "%{count} εκλογή" + other: "%{count} εκλογές" + election_log: + chained_hash: Το chained Hash αυτού του μηνύματος + complete: Ολοκλήρωση + creation_description: + complete: Οι εκλογές δημιουργήθηκαν και ρυθμίστηκαν με επιτυχία στον Πίνακα Ανακοινώσεων. + not_created: Οι εκλογές δεν έχουν δημιουργηθεί ακόμα. + creation_title: Οι εκλογές δημιουργήθηκαν + description: Αυτό είναι το αρχείο καταγραφής των εκλογών όπου μπορείτε να ελέγξετε την κατάσταση κάθε βήματος, π.χ. πότε δημιουργήθηκαν οι εκλογές, εάν ολοκληρωθεί η διαδικασία, και πότε έκλεισαν οι εκλογές. + download: Λήψη + key_ceremony_description: + complete: Η διαδικασία κλειδιού ολοκληρώθηκε. Κάθε επίτροπος έχει έγκυρα κλειδιά και έχει κατεβάσει τα απαραίτητα αντίγραφα ασφαλείας των κλειδιών. + not_started: Η διαδικασία κλειδιού δεν έχει ξεκινήσει ακόμα. + started: Η διαδικασία κλειδιού έχει ξεκινήσει αλλά δεν έχει ολοκληρωθεί ακόμα. + key_ceremony_title: Διαδικασία κλειδιού + not_available: Μη διαθέσιμο ακόμα + not_created: Δεν δημιουργήθηκε + not_ready: Δεν είναι έτοιμο + not_started: Δεν ξεκίνησε + published: Δημοσιεύτηκε + results_description: + not_published: Τα αποτελέσματα δεν έχουν δημοσιευθεί ακόμα. + published: Τα αποτελέσματα δημοσιεύονται. + results_title: Αποτελέσματα + started: Ξεκίνησε + tally_description: + finished: Η διαδικασία καταμέτρησης έχει ολοκληρωθεί. + not_started: Η διαδικασία καταμέτρησης δεν έχει ξεκινήσει ακόμα. + started: Η διαδικασία καταμέτρησης έχει ξεκινήσει. + tally_title: Διαδικασία καταμέτρησης + title: Καταγραφή Εκλογών + verifiable_results: + checksum: 'SHA256 checksum αρχείου:' + description: + not_ready: Το επαληθεύσιμο αρχείο εκλογών και το SHA256 checksum δεν είναι ακόμα διαθέσιμα. Μόλις δημοσιευθούν τα αποτελέσματα, θα είστε σε θέση να επαληθεύσετε αυτές τις εκλογές. + ready: 'Εδώ, έχετε την επιλογή να επαληθεύσετε τις εκλογές. Πρώτα, πρέπει να κατεβάσετε το αρχείο και να βεβαιωθείτε ότι δεν έχει καταστραφεί. Για να το κάνετε αυτό, εκτελέστε την ακόλουθη εντολή και ελέγξτε ότι η έξοδος ταιριάζει με το checksum:' + how_to_verify: 'Μόλις κατεβάσετε το αρχείο και βεβαιωθείτε ότι είναι εντάξει, μπορείτε να προχωρήσετε στην εκτέλεση της καθολικής επαλήθευσης. Κλωνοποιήστε αυτό το αποθετήριο και, από το ριζικό φάκελο, εκτελέστε την ακόλουθη εντολή:' + title: Επαλήθευση αποτελεσμάτων εκλογών + verifiable_file: 'Επαληθεύσιμο αρχείο εκλογών:' + verify: Επαλήθευση Εκλογών + vote_description: + finished: Η διαδικασία ψηφοφορίας έχει ολοκληρωθεί. + not_started: Η διαδικασία ψηφοφορίας δεν έχει ξεκινήσει ακόμα. + started: Η διαδικασία ψηφοφορίας έχει ξεκινήσει. + vote_title: Διαδικασία ψηφοφορίας + filters: + active: Ενεργό + all: Όλα + date: Ημερομηνία + finished: Ολοκληρώθηκε + upcoming: Προσεχείς + preview: + available_answers: 'Διαθέσιμες απαντήσεις:' + description: 'Αυτές είναι οι ερωτήσεις που θα βρείτε στη διαδικασία ψηφοφορίας:' + title: Ερωτήσεις εκλογών + results: + description: 'Αυτά είναι τα αποτελέσματα της ψηφοφορίας, για κάθε ερώτηση:' + percentage: "%{count}%" + selected: Επιλέχθηκε + title: Αποτελέσματα εκλογών + votes: + one: "%{count} ψήφος" + other: "%{count} ψήφοι" + show: + action_button: + change_vote: Αλλάξτε την ψήφο σας + vote: Αρχίστε να ψηφίζετε + vote_again: Ψηφίστε πάλι + callout: + already_voted: Έχετε ήδη ψηφίσει σε αυτές τις εκλογές. Μπορείτε να αλλάξετε την ψήφο σας ή να την επαληθεύσετε. + pending_vote: Η ψήφος σας μεταδίδεται στον διακομιστή. + vote_rejected: Δεν ήταν δυνατόν να επαληθευτεί η ψήφος σας. Σας παρακαλώ ξαναπροσπαθήστε. + election_log: Αρχείο καταγραφής εκλογών + preview: Προεπισκόπηση + verify: + already_voted: Ψηφίστηκε ήδη; + verify_here: Ελέγξτε την ψήφο σας εδώ. + will_verify: Θα είστε σε θέση να επαληθεύσετε την ψήφο σας μόλις ξεκινήσουν οι εκλογές. + voting_period_status: + finished: Η ψηφοφορία ξεκίνησε στις %{start_time} και ολοκληρώθηκε στις %{end_time} + ongoing: 'Ενεργή ψηφοφορία μέχρι: %{end_time}' + upcoming: Η ψηφοφορία ξεκινά στις %{start_time} + feedback: + answer: + invalid: Υπήρξε ένα πρόβλημα κατά την υποβολή των σχολίων σας. + spam_detected: Υπήρχε ένα πρόβλημα στην απάντηση της φόρμας. Ίσως είστε πολύ γρήγοροι, μπορείτε να δοκιμάσετε ξανά; + success: Η γνώμη σας στάλθηκε επιτυχώς. + models: + answer: + fields: + proposals: Προτάσεις + selected: Επιλέχθηκε + title: Τίτλος + votes: Ψήφοι + election: + fields: + bb_status: Κατάσταση Πίνακα Ανακοινώσεων + end_time: Λήγει στις + start_time: Ξεκινά στις + title: Τίτλος + verifiable_results_file_hash: SHA256 checksum αρχείου + verifiable_results_file_url: Επαληθεύσιμο αρχείο εκλογών + question: + fields: + answers: Απαντήσεις + max_selections: Μέγιστες επιλογές + title: Τίτλος + trustees_participatory_space: + fields: + considered: εξετάστηκε + email: Email + inactive: ανενεργό + name: Όνομα + notification: Η ειδοποίηση απεστάλη στις + public_key: Δημόσιο Κλειδί + status: Κατάσταση + orders: + label: Ταξινόμηση εκλογών κατά + older: Παλαιότερη + recent: Πρόσφατη + trustee_zone: + elections: + backup_modal: + description: Αυτές οι εκλογές δημιουργούνται στον Πίνακα Ανακοινώσεων. Είναι πολύ σημαντικό κάθε Επίτροπος που συμμετέχει να δημιουργεί ένα αντίγραφο ασφαλείας αυτών των κλειδιών και να τα αποθηκεύει σε ασφαλές μέρος. Μετά από αυτό, η διαδικασία συνεχίζεται. + download_election_keys: Λήψη κλειδιών + title: Αντίγραφο ασφαλείας των κλειδιών εκλογών για %{election} + key_ceremony_steps: + back: Πίσω + description: Αυτές οι εκλογές δημιουργούνται στον Πίνακα Ανακοινώσεων. Για να ολοκληρωθεί αυτή η διαδικασία, απαιτείται η συμμετοχή σας ως Επίτροπος. + keys: + create_election: Δημιουργία κλειδιών + key_ceremony: + joint_election_key: Δημιουργία κοινού κλειδιού + step_1: Δημοσίευση κλειδιών + list: + status: Κατάσταση + task: Εργασία + process_warning: Μόλις ξεκινήσει η διαδικασία, δεν πρέπει να βγείτε από αυτή τη σελίδα μέχρι να τελειώσει η διαδικασία. Θα χρειαστούν αρκετά λεπτά, καθώς όλοι οι Επίτροποι θα πρέπει να συνδεθούν για να την ολοκληρώσουν. + start: Έναρξη + status: + completed: Ολοκληρώθηκε + pending: Εκκρεμεί + processing: Σε επεξεργασία + title: Δημιουργία κλειδιών εκλογών για %{election} + restore_modal: + description: Ο Πίνακας Ανακοινώσεων έχει πληροφορίες από εσάς ως Επίτροπος για αυτές τις εκλογές. Για να συνεχίσετε τη διαδικασία, ανεβάστε πρώτα το αρχείο αντιγράφου ασφαλείας που δημιουργήθηκε κατά την προηγούμενη συνεδρία. + title: Επαναφορά των κλειδιών εκλογών για %{election} + upload_election_keys: Μεταφόρτωση κλειδιών εκλογών + tally_started_steps: + back: Πίσω + list: + status: Κατάσταση + task: Εργασία + process_warning: Μόλις ξεκινήσει η διαδικασία, δεν πρέπει να βγείτε από αυτή τη σελίδα μέχρι να τελειώσει η διαδικασία. Θα χρειαστούν αρκετά λεπτά, καθώς όλοι οι Επίτροποι θα πρέπει να συνδεθούν για να την ολοκληρώσουν. + start: Έναρξη + status: + completed: Ολοκληρώθηκε + pending: Εκκρεμεί + processing: Σε επεξεργασία + update: + error: Η κατάσταση των εκλογών δεν ενημερώθηκε. + success: 'Η κατάσταση των εκλογών είναι: %{status}.' + menu: + trustee_zone: Ζώνη Επιτρόπου + no_bulletin_board: + body: Απαιτείται ένας καθορισμένος Πίνακας Ανακοινώσεων για αυτήν την ενότητα. Επικοινωνήστε με τον Διαχειριστή για περισσότερες λεπτομέρειες. + title: Συγγνώμη, ο Πίνακας Ανακοινώσεων δεν έχει ρυθμιστεί ακόμα. + trustees: + show: + elections: + list: + action_required: + 'false': 'Όχι' + name: Απαιτείται ενέργεια; + 'true': Εκτέλεση ενέργειας + bb_status: Κατάσταση + election: Εκλογές + voting_period: Περίοδος ψηφοφορίας + title: Εκλογές + identification_keys: + cancel: Ακύρωση + generate: Δημιουργία κλειδιών ταυτοποίησης + generate_error: Παρουσιάστηκε σφάλμα κατά τη δημιουργία των κλειδιών ταυτοποίησης. + generate_legend: Πρέπει να δημιουργήσετε ένα ζεύγος κλειδιών ταυτοποίησης για να συμμετάσχετε στις εκλογές ως επίτροπος. + generate_legend_1: Μετά το πάτημα του κουμπιού θα πρέπει να κατεβάσετε το αρχείο με τα παραγόμενα κλειδιά ταυτοποίησης. + generate_legend_2: Αντιγραφή του ληφθέντος αρχείου σε μια άδεια συσκευή USB + generate_legend_3: Βεβαιωθείτε ότι ο υπολογιστής σας δεν έχει ένα αντίγραφο του αρχείου (π.χ. ελέγξτε τους φακέλους Λήψεις και Επιφάνεια εργασίας). + generate_legend_4: Δημιουργήστε ένα άλλο αντίγραφο του αρχείου σε μια διαφορετική εξωτερική συσκευή και αποθηκεύστε το σε ένα πολύ ασφαλές μέρος. + submit: Υποβολή + submit_legend: Αφού ακολουθήσετε όλα τα βήματα που εξηγήθηκαν παραπάνω, ολοκληρώστε τη διαδικασία αποστολής του δημόσιου κλειδιού αναγνώρισης στο διακομιστή. + submit_title: Υποβάλετε το δημόσιο κλειδί ταυτοποίησης + title: Κλειδιά ταυτοποίησης επιτρόπων + upload: Ανεβάστε τα κλειδιά ταυτοποίησής σας + upload_error: + invalid_format: Το αρχείο που ανεβάσατε δεν περιέχει κανένα κλειδί ταυτοποίησης. + invalid_key: Δεν είναι δυνατή η φόρτωση των κλειδιών ταυτοποίησης στο αρχείο που μεταφορτώθηκε. + invalid_public_key: Τα κλειδιά αναγνώρισης στο φορτωμένο αρχείο δεν ταιριάζουν με το δημόσιο κλειδί ταυτοποίησης που αποθηκεύτηκε. + upload_legend: Ο διακομιστής έχει τα δημόσια κλειδιά ταυτοποίησης σας, αλλά το πρόγραμμα περιήγησής σας εξακολουθεί να μην το έχει. Θα πρέπει να εισάγετε το αρχείο με τα κλειδιά ταυτοποίησης στον υπολογιστή σας από το αντίγραφο ασφαλείας που δημιουργήσατε μετά τη δημιουργία τους. + not_supported_browser_description: Φαίνεται ότι χρησιμοποιείτε ένα πρόγραμμα περιήγησης ιστού που δεν μπορεί να χρησιμοποιηθεί για να λειτουργήσει ως Επίτροπος. Βεβαιωθείτε ότι χρησιμοποιείτε την πιο πρόσφατη έκδοση του προγράμματος περιήγησής σας, ή δοκιμάστε να χρησιμοποιήσετε οποιοδήποτε από τα πιο δημοφιλή προγράμματα περιήγησης για να ολοκληρώσετε τις εργασίες Επιτρόπου. + not_supported_browser_title: Αναβαθμίστε το πρόγραμμα περιήγησης για να ενεργήσετε ως Επίτροπος + update: + success: Το δημόσιο κλειδί ταυτοποίησής σας αποθηκεύτηκε με επιτυχία. + votes: + ballot_decision: + back: Έναρξη διαδικασίας ψηφοφορίας ξανά + ballot_hash: 'Το αναγνωριστικό του ψηφοδελτίου σας είναι:' + cast: Καταχωρήστε το ψηφοδέλτιο για να ολοκληρώσετε την ψήφο σας + header: 'Το ψηφοδέλτιο είναι κρυπτογραφημένο: καταχωρήστε το ή έλεγξε το' + casting: + header: Ρίξτε την ψήφο... + text: Η ψήφος σας μπαίνει στην κάλπη. + confirm: + answer_number: απάντηση %{number} + confirm: Επιβεβαίωση + edit: επεξεργασία + header: Επιβεβαιώστε την ψήφο σας + intro: Ακολουθεί μια σύνοψη της ψήφου που πρόκειται να δώσετε.
    Επιβεβαιώστε την ψήφο σας ή επεξεργαστείτε τις απαντήσεις σας. + nota_option: Κενό + confirmed: + back: Επιστροφή στις εκλογές + experience: Πώς ήταν η εμπειρία σας; + feedback: Πείτε μας μερικά σχόλια + header: Η ψήφος επιβεβαιώθηκε + lead: Η ψήφος σας έχει προστεθεί! + verify_link: Για να την ελέγξετε, αντιγράψτε το αναγνωριστικό και επικολλήστε το στη σελίδα επαλήθευσης ψήφου + create: + error: Παρουσιάστηκε σφάλμα κατά την εγγραφή της ψήφου. Παρακαλώ, προσπαθήστε ξανά. + encrypting: + header: Κρυπτογράφηση της ψήφου... + text: Το ψηφοδέλτιό σας είναι κρυπτογραφημένο για να διασφαλιστεί το μυστικό της ψήφου σας. + failed: + header: Η ψήφος απέτυχε + lead: Η ψήφος σας δεν έχει καταχωρηθεί! + text: Παρουσιάστηκε σφάλμα, δοκιμάστε ξανά. + try_again: Προσπαθήστε ξανά + messages: + invalid_token: Η συνεδρία σας στο παραβάν ψηφοφορίας δεν είναι έγκυρη. Προσπαθήστε να ψηφίσετε ξανά. + not_allowed: Αυτή τη στιγμή δεν επιτρέπεται να ψηφίσετε σε αυτές τις εκλογές. + modal: + close: Κλείσιμο + proposal_header: 'Προτάσεις:' + new: + answer_choices: Μπορείτε να επιλέξετε έως και %{choices} απαντήσεις + more_information: Περισσότερες πληροφορίες + nota_option: Κενό/ Κανένα από τα παραπάνω + preview_alert: Αυτή είναι μια προεπισκόπηση του παραβάν ψηφοφορίας. + question_steps: Ερώτηση %{current_step} από %{total_steps} + onboarding_modal: + create_account: Δημιουργία λογαριασμού + description: Θέλετε να δημιουργήσετε ένα νέο λογαριασμό στην πλατφόρμα; Θα είστε σε θέση να συμμετέχετε στις διαδικασίες και να είστε ενεργό μέρος του οργανισμού. + no_account: Όχι, ευχαριστώ. + title: Νέος στην πλατφόρμα; + update: + error: Υπήρξε πρόβλημα με την ενημέρωση της κατάστασης της ψήφου. Παρακαλώ, ψηφίστε ξανά. + verify: + content: + heading: Επαληθεύστε την ψήφο σας + info: Αυτός ο ελεγκτής ελέγχει ότι η ψήφος σας, που ταυτοποιείται με ένα κρυπτογραφημένο κείμενο, έχει ριχθεί σωστά και βρίσκεται μέσα στην κάλπη. + error: + header: Η ψήφος δεν βρέθηκε! + info: Ο κωδικός ψήφου δεν βρέθηκε στην %{link} κάλπη, προσπαθήστε ξανά. + form: + back: Πίσω στην πλατφόρμα + submit: Έλεγχος + vote_identifier: 'Κωδικός αναγνώρισης:' + vote_identifier_help: Αυτό είναι το αναγνωριστικό που σας δόθηκε μετά την ψήφο σας (όχι ο κωδικός για να μπείτε στο παραβάν). + header: + title: Επαληθεύστε την ψήφο σας + success: + header: Η ψήφος εντοπίστηκε! + info: Η κρυπτογραφημένη ψήφος σας βρίσκεται στην %{link} κάλπη. + voting_step: + back: Πίσω + continue: Επόμενο + events: + elections: + election_published: + email_intro: 'Οι εκλογές %{resource_title} είναι πλέον ενεργές για τον χώρο %{participatory_space_title}. Μπορείτε να το δείτε από αυτήν τη σελίδα:' + email_outro: Λάβατε αυτή την ειδοποίηση επειδή ακολουθείτε το %{participatory_space_title}. Μπορείτε να σταματήσετε να λαμβάνετε ειδοποιήσεις ακολουθώντας τον προηγούμενο σύνδεσμο. + email_subject: Οι εκλογές %{resource_title} είναι πλέον ενεργές για τον χώρο %{participatory_space_title}. + notification_title: Οι εκλογές %{resource_title} είναι πλέον ενεργές για τον χώρο %{participatory_space_title}. + trustees: + new_election: + email_intro: Έχετε προστεθεί ως επίτροπος για τις εκλογές %{resource_title}. + email_outro: Έχετε λάβει αυτήν την ειδοποίηση επειδή έχετε προστεθεί ως επίτροπος για τις εκλογές %{resource_title}. + new_trustee: + email_intro: Ένας διαχειριστής σας πρόσθεσε ως επίτροπο για το %{resource_name}. Θα πρέπει να δημιουργήσετε το δημόσιο κλειδί σας στη ζώνη εμπιστοσύνης + email_outro: Έχετε λάβει αυτήν την ειδοποίηση επειδή έχετε προστεθεί ως επίτροπος για τις εκλογές %{resource_name}. + email_subject: Είστε επίτροπος για το %{resource_name}. + votes: + accepted_votes: + email_intro: 'Η ψήφος σας έγινε δεκτή! Χρησιμοποιώντας το διακριτικό ψήφου: %{encrypted_vote_hash}, μπορείτε να επαληθεύσετε την ψήφο σας εδώ.' + email_outro: Έχετε λάβει αυτήν την ειδοποίηση επειδή έχετε ψηφίσει για τις %{resource_name} εκλογές. + email_subject: Η ψήφος σας για το %{resource_name} έγινε δεκτή. + notification_title: 'Η ψήφος σας έγινε δεκτή. Επαληθεύστε την ψήφο σας εδώ χρησιμοποιώντας το διακριτικό ψήφου: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Έχετε τεθεί ως %{role} του Εκλογικού Τμήματος %{polling_station_name} στο %{resource_title}. Μπορείτε να διαχειριστείτε το Εκλογικό Τμήμα από την ειδική Ζώνη Αξιωματούχου Εκλογών. + email_outro: Έχετε λάβει αυτήν την ειδοποίηση επειδή έχετε προστεθεί ως %{role} για το %{resource_name}. + email_subject: Είστε %{role} του Εκλογικού Τμήματος %{polling_station_name}. + notification_title: Είστε %{role} του Εκλογικού Τμήματος %{polling_station_name} στην ψηφοφορία %{resource_title}. + send_access_code: + instruction: 'Εδώ είναι ο Κωδικός Πρόσβασης που ζητήσατε: %{access_code}. Με αυτόν θα μπορείτε να συμμετάσχετε στο %{voting}.' + subject: Ο Κωδικός Πρόσβασής σας για συμμετοχή στο %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Οι ψηφοφορίες είναι ένας χώρος που σας δίνει τη δυνατότητα να θέσετε μια σαφή ερώτηση σε όλους όσοι συνθέτουν έναν οργανισμό, να υποβάλετε αίτημα συμμετοχής στην ψηφοφορία, να τροφοδοτήσετε τον διάλογο και να επιχειρηματολογήσετε υπέρ ή κατά μιας απάντησης. Όταν φτάσει η ημερομηνία της ψηφοφορίας, μπορείτε να ψηφίσετε και να δημοσιεύσετε τα αποτελέσματα των ψήφων.

    Παραδείγματα: Οι ψηφοφορίες μπορούν να σχετίζονται σχεδόν με οτιδήποτε επηρεάζει έναν οργανισμό, για παράδειγμα με την αντικατάσταση του ονόματος ή του λογότυπου του οργανισμού κατόπιν πρότασης διάφορων εναλλακτικών, τη συναίνεση στην ένταξη του οργανισμού σε μεγαλύτερο οργανισμό, την έγκριση ή απόρριψη ενός νέου στρατηγικού σχεδίου ή του αποτελέσματος μιας ομάδας εργασίας ή τον ορισμό μέγιστου αριθμού 1, 2 ή 3 θητειών για τις θέσεις ευθύνης.

    \n" + page: "

    Οι ψηφοφορίες είναι ένας χώρος που σας δίνει τη δυνατότητα να θέσετε μια σαφή ερώτηση σε όλους όσοι συνθέτουν έναν οργανισμό, να υποβάλετε αίτημα συμμετοχής στην ψηφοφορία, να τροφοδοτήσετε τον διάλογο και να επιχειρηματολογήσετε υπέρ ή κατά μιας απάντησης. Όταν φτάσει η ημερομηνία της ψηφοφορίας, μπορείτε να ψηφίσετε και να δημοσιεύσετε τα αποτελέσματα των ψήφων.

    Παραδείγματα: Οι ψηφοφορίες μπορούν να σχετίζονται σχεδόν με οτιδήποτε επηρεάζει έναν οργανισμό, για παράδειγμα με την αντικατάσταση του ονόματος ή του λογότυπου του οργανισμού κατόπιν πρότασης διάφορων εναλλακτικών, τη συναίνεση στην ένταξη του οργανισμού σε μεγαλύτερο οργανισμό, την έγκριση ή απόρριψη ενός νέου στρατηγικού σχεδίου ή του αποτελέσματος μιας ομάδας εργασίας ή τον ορισμό μέγιστου αριθμού 1, 2 ή 3 θητειών για τις θέσεις ευθύνης.

    \n" + title: Τι είναι οι ψηφοφορίες; + menu: + votings: Ψηφοφορίες + statistics: + elections_count: Εκλογές + votings_count: Ψηφοφορίες + votings: + admin: + ballot_styles: + create: + error: Υπήρξε ένα πρόβλημα κατά τη δημιουργία αυτού του στυλ ψηφοδελτίου. + success: Το στυλ ψηφοδελτίου δημιουργήθηκε με επιτυχία. + destroy: + invalid: Υπήρξε ένα πρόβλημα κατά τη διαγραφή αυτού του στυλ ψηφοδελτίου. + success: Το στυλ ψηφοδελτίου διαγράφηκε με επιτυχία. + edit: + title: Επεξεργασία στυλ ψηφοδελτίου + update: Ενημέρωση + form: + code_help: 'Υπόδειξη: ο κωδικός είναι ο σύνδεσμος μεταξύ της απογραφής και του στυλ ψηφοδελτίου. Κατά το ανέβασμα των δεδομένων της απογραφής, σε κάθε καταχώρηση θα ανατεθεί ένα στυλ ψηφοδελτίου που ταιριάζει με τον κωδικό.' + election: Εκλογές + questions: Ερωτήσεις για αυτό το στυλ ψηφοδελτίου + questions_help: 'Συμβουλή: επιλέξτε τις ερωτήσεις από τα εκλογικά στοιχεία που θα παρουσιαστούν στους ψηφοφόρους που έχουν ανατεθεί σε αυτό το στυλ ψηφοδελτίου.' + index: + actions: + confirm_destroy: Είστε σίγουροι; + destroy: Διαγραφή + edit: Επεξεργασία + title: Ενέργειες + associated_census_data: Συσχετιζόμενες καταχωρήσεις απογραφής + explanation_callout: Το στυλ της ψηφοδελτίου καθορίζει τις ερωτήσεις που θα παρουσιαστούν σε ένα ψηφοφόρος στο παραβάν. Σε ένα στυλ ψηφοδελτίου, μπορείτε να επιλέξετε ποιες ερωτήσεις από τα εκλογικά στοιχεία αυτής της ψηφοφορίας ανήκουν σε ένα ψηφοδέλτιο. Ο κωδικός στυλ ψηφοδελτίου χρησιμοποιείται για να ταιριάζει έναν ψηφοφόρο από την απογραφή με το ψηφοδέλτιο που θα παρουσιαστεί στο παραβάν. Μην δημιουργείτε κανένα στυλ ψηφοφορίας αν θέλετε να παρουσιάσετε πάντα όλες τις ερωτήσεις. + title: Στυλ ψηφοδελτίων + new: + create: Δημιουργία + title: Επεξεργασία στυλ ψηφοδελτίου + update: + invalid: Υπήρξε ένα πρόβλημα κατά την ενημέρωση αυτού του στυλ ψηφοδελτίου. + success: Το στυλ ψηφοδελτίου ενημερώθηκε επιτυχώς. + content_blocks: + highlighted_votings: + max_results: Μέγιστος αριθμός στοιχείων προς εμφάνιση + index: + published: Δημοσιεύθηκε + menu: + votings: Ψηφοφορίες + votings_submenu: + attachment_collections: Φάκελοι + attachment_files: Αρχεία + attachments: Συνημμένα + ballot_styles: Στυλ ψηφοδελτίων + census: Απογραφή + components: Στοιχεία + landing_page: Σελίδα άφιξης + monitoring_committee: Επιτροπή Παρακολούθησης + monitoring_committee_election_results: Επικύρωση Αποτελεσμάτων + monitoring_committee_members: Μέλη + monitoring_committee_polling_station_closures: Επικύρωση πιστοποιητικών + monitoring_committee_verify_elections: Επαλήθευση Εκλογών + polling_officers: Αξιωματούχοι εκλογών + polling_stations: Εκλογικά κέντρα + models: + ballot_style: + fields: + code: Κωδικός + monitoring_committee_member: + fields: + email: Email + name: Όνομα + polling_officer: + fields: + email: Email + name: Όνομα + polling_station: Εκλογικό κέντρο (ρόλος) + polling_station: + fields: + address: Διεύθυνση + polling_station_managers: Διαχειριστές + polling_station_president: Πρόεδρος + title: Τίτλος + voting: + fields: + created_at: Δημιουργήθηκε στις + published: Δημοσιεύθηκε + title: Τίτλος + monitoring_committee_election_results: + actions: + title: Ενέργειες + view: Προβολή + index: + title: Επιλέξτε μια εκλογή για την οποία θέλετε να δείτε τα αποτελέσματα + results: + bulletin_board: Πίνακα Ανακοινώσεων + election_totals: Σύνολα εκλογών + polling_stations: Εκλογικά τμήματα + result_types: + blank_answers: Κενές απαντήσεις + blank_ballots: Κενά ψηφοδέλτια + null_ballots: Μηδενικά ψηφοδέλτια + total_ballots: Συνολικά ψηφοδέλτια + valid_ballots: Έγκυρα ψηφοδέλτια + selected: Επιλέχθηκε + title: Αποτελέσματα για τις εκλογές %{election_title} + totals: Σύνολα + show: + change_election: Αλλαγή εκλογών + publish_results: Δημοσίευση αποτελεσμάτων + publishing: Δημοσίευση αποτελεσμάτων... + update: + invalid: Υπήρξε ένα πρόβλημα στη δημοσίευση των αποτελεσμάτων. + rejected: Η δημοσίευση των αποτελεσμάτων απορρίφθηκε από τον Πίνακα Ανακοινώσεων. Δοκιμάστε ξανά ή επικοινωνήστε με το διαχειριστή του συστήματος. + success: Τα αποτελέσματα δημοσιεύτηκαν επιτυχώς. + monitoring_committee_members: + create: + invalid: Υπήρξε ένα πρόβλημα με τη δημιουργία αυτού του μέλους της επιτροπής παρακολούθησης. + success: Το μέλος της επιτροπής παρακολούθησης δημιουργήθηκε επιτυχώς. + destroy: + invalid: Υπήρξε ένα πρόβλημα κατά τη διαγραφή αυτού του μέλους της επιτροπής παρακολούθησης. + success: Το μέλος της επιτροπής παρακολούθησης διαγράφηκε με επιτυχία. + form: + existing_user: Υπάρχων συμμετέχων + non_user: Προσκαλέστε νέο συμμετέχοντα + select_user: Αναζήτηση με όνομα, email ή ψευδώνυμο + user_type: Τύπος συμμετέχοντα + index: + title: Επιτροπή Παρακολούθησης + new: + create: Δημιουργία + title: Δημιουργία μέλους επιτροπής παρακολούθησης + monitoring_committee_polling_station_closures: + actions: + title: Ενέργειες + validate: Επικύρωση + view: Προβολή + closures: + change_election: Αλλαγή εκλογών + signed: Υπογεγραμμένο; + title: Εκλογικά Τμήματα για τις εκλογές %{election_title} + validated: Επικυρωμένο; + edit: + change_polling_station: Πίσω στα εκλογικά τμήματα + monitoring_committee_notes: Παρατηρήσεις + monitoring_committee_notes_placeholder: Αναφέρετε οποιοδήποτε περιστατικό εδώ + title: Αποτελέσματα για τις εκλογές %{election_title} στο εκλογικό τμήμα %{polling_station_title} + elections: + title: Επιλέξτε μια εκλογή που θέλετε να επικυρώσετε + show: + change_polling_station: Πίσω στα εκλογικά τμήματα + monitoring_committee_notes: Παρατηρήσεις της επιτροπής παρακολούθησης + validate: + error: Υπήρξε ένα πρόβλημα κατά την επικύρωση του κλεισίματος. + success: Το κλείσιμο έχει επικυρωθεί σωστά. + monitoring_committee_verify_elections: + index: + download: Λήψη + how_to_checksum: 'Για να βεβαιωθείτε ότι το αρχείο που κατεβάσατε δεν έχει καταστραφεί ή παραποιηθεί κατά τη διάρκεια της διαδικασίας λήψης, εκτελέστε την ακόλουθη εντολή στην κονσόλα σας και ελέγξτε ότι η έξοδος αντιστοιχεί στο checksum που αναφέρθηκε παραπάνω:' + how_to_download: Για να επαληθεύσετε τις εκλογές, κατεβάστε το επαληθεύσιμο αρχείο του από τον παραπάνω πίνακα. + how_to_run_verifier: 'Μόλις κατεβάσετε το αρχείο και βεβαιωθείτε ότι είναι εντάξει, μπορείτε να προχωρήσετε στην εκτέλεση της καθολικής επαλήθευσης. Κλωνοποιήστε αυτό το αποθετήριο και, από το ριζικό φάκελο, εκτελέστε την ακόλουθη εντολή:' + how_to_title: Πώς να επαληθεύσετε την εγκυρότητα των εκλογών + not_available: Μη διαθέσιμο ακόμα + title: Εκλογές + polling_officers: + create: + invalid: Υπήρξε πρόβλημα με τη δημιουργία αυτού του αξιωματούχου εκλογών. + success: Επιτυχής δημιουργία αξιωματούχου εκλογών. + destroy: + invalid: Υπήρξε ένα πρόβλημα κατά τη διαγραφή αυτού του αξιωματούχου εκλογών. + success: Ο αξιωματούχος εκλογών διαγράφηκε επιτυχώς. + form: + existing_user: Υπάρχων συμμετέχων + non_user: Προσκαλέστε νέο συμμετέχοντα + select_user: Αναζήτηση με όνομα, email ή ψευδώνυμο + user_type: Τύπος συμμετέχοντα + index: + role_manager: διαχειριστής + role_president: πρόεδρος + title: Αξιωματούχοι εκλογών + new: + create: Δημιουργία + title: Δημιουργία αξιωματούχου εκλογών + polling_officers_picker: + choose_polling_officers: Επιλέξτε αξιωματούχους εκλογών + no_polling_officers: Κανένας αξιωματούχος εκλογών δεν ταιριάζει με τα κριτήρια αναζήτησής σας ή δεν υπάρχει κάποιος αξιωματούχος εκλογών. + polling_stations: + create: + invalid: Υπήρξε πρόβλημα κατά τη δημιουργία αυτού του εκλογικού τμήματος. + success: Το εκλογικό τμήμα δημιουργήθηκε επιτυχώς. + destroy: + invalid: Υπήρξε πρόβλημα κατά τη διαγραφή του εκλογικού τμήματος. + success: Το εκλογικό τμήμα διαγράφηκε επιτυχώς. + edit: + title: Επεξεργασία εκλογικού τμήματος + update: Ενημέρωση εκλογικού τμήματος + form: + address_help: 'Διεύθυνση: χρησιμοποιείται από το Geocoder για τον εντοπισμό της τοποθεσίας' + location_help: 'Τοποθεσία: μήνυμα που απευθύνεται στους ψηφοφόρους δείχνοντας την ακριβή θέση του εκλογικού τμήματος' + location_hints_help: 'Υποδείξεις τοποθεσίας: πρόσθετες πληροφορίες. Παράδειγμα: ο όροφος του κτιρίου όπου βρίσκεται το εκλογικό τμήμα.' + polling_station_managers_help: 'Διαχειριστές εκλογικού τμήματος: οι υπεύθυνοι που θα ενεργήσουν ως διαχειριστές εκλογικών τμημάτων. Βεβαιωθείτε ότι οι αξιωματούχοι έχουν ήδη δημιουργηθεί στους Αξιωματούχους Εκλογών και ότι δεν έχουν ήδη ανατεθεί σε άλλο εκλογικό τμήμα' + polling_station_president_help: 'Πρόεδρος εκλογικού τμήματος: οι υπεύθυνοι που θα ενεργήσουν ως πρόεδροι εκλογικών τμημάτων. Βεβαιωθείτε ότι οι αξιωματούχοι έχουν ήδη δημιουργηθεί στους Αξιωματούχους Εκλογών και ότι δεν έχουν ήδη ανατεθεί σε άλλο εκλογικό τμήμα' + select_president: Επιλέξτε έναν αξιωματούχο εκλογών ως πρόεδρο του εκλογικού τμήματος + index: + title: Εκλογικά τμήματα + new: + create: Δημιουργία + title: Δημιουργία εκλογικού τμήματος + update: + invalid: Υπήρξε πρόβλημα κατά την ενημέρωση του εκλογικού τμήματος. + success: Το εκλογικό τμήμα δημιουργήθηκε επιτυχώς. + titles: + votings: Ψηφοφορίες + votings: + actions: + confirm_destroy: Είστε σίγουροι; + destroy: Καταστροφή + new_voting: Νέος Χώρος Ψηφοφορίας + create: + invalid: Υπήρξε πρόβλημα κατά τη δημιουργία της ψηφοφορίας. + success: Η ψηφοφορία δημιουργήθηκε επιτυχώς. + edit: + add_election_component: Δεν έχετε ρυθμίσει καμία εκλογή για αυτή την ψηφοφορία. Παρακαλώ προσθέστε την στην ενότητα Εφαρμογές. + assign_missing_officers: Υπάρχουν Εκλογικά Τμήματα χωρίς Πρόεδρο ή/και Διαχειριστές. Παρακαλώ εκχωρήστε τους από το τμήμα Εκλογικά Τμήματα. + update: Ενημέρωση + form: + banner_image: Εικόνα banner + census_contact_information: Στοιχεία επικοινωνίας απογραφής + census_contact_information_help: Αυτά τα στοιχεία επικοινωνίας είναι για έναν συμμετέχοντα που θέλει να αναφέρει θέματα με την απογραφή. Μπορεί να είναι μια διεύθυνση email, μια φόρμα επικοινωνίας σε μια άλλη ιστοσελίδα, μια έρευνα για τους επισκέπτες κλπ. + introductory_image: Εισαγωγική εικόνα + promoted: Προβιβάστηκε + select_a_voting_type: Παρακαλώ επιλέξτε έναν τύπο ψηφοφορίας + show_check_census_help: Αν θα εμφανίζεται ο σύνδεσμος "Μπορώ να ψηφίσω;" στο μενού δημοσίων εκλογών. + slug: Slug + title: Τίτλος + voting_type: + hybrid: Υβριδικός + in_person: Αυτοπροσώπως + online: Ηλεκτρονικά + voting_type_label: Τύπος ψηφοφορίας + new: + create: Δημιουργία + title: Νέα Ψηφοφορία + update: + invalid: Υπήρξε πρόβλημα κατά την ενημέρωση της ψηφοφορίας. + success: Η ψηφοφορία ενημερώθηκε επιτυχώς. + admin_log: + ballot_style: + create: "Ο/Η %{user_name} δημιούργησε ένα στυλ ψηφοδελτίου με κωδικό %{ballot_style_code} στο χώρο %{space_name}" + delete: "Ο/Η %{user_name} διέγραψε ένα στυλ ψηφοδελτίου με κωδικό %{ballot_style_code} στο χώρο %{space_name}" + update: "Ο/Η %{user_name} ενημέρωσε ένα στυλ ψηφοδελτίου με κωδικό %{ballot_style_code} στο χώρο %{space_name}" + census: + create: "Ο/Η %{user_name} δημιούργησε την απογραφή για το χώρο %{space_name}" + delete: "Ο/Η %{user_name} διέγραψε την απογραφή για το χώρο %{space_name}" + update: "Ο/Η %{user_name} ενημέρωσε την απογραφή για το χώρο %{space_name}" + monitoring_committee_member: + create: "Ο/Η %{user_name} έθεσε τον χρήστη %{monitoring_committee_member_user} ως μέλος της επιτροπής παρακολούθησης στο χώρο %{space_name}" + delete: "Ο/Η %{user_name} αφαίρεσε τον χρήστη %{monitoring_committee_member_user} ως μέλος της επιτροπής παρακολούθησης στο χώρο %{space_name}" + polling_officer: + create: "Ο/Η %{user_name} έθεσε τον χρήστη %{polling_officer_user} ως αξιωματούχο εκλογών στο χώρο %{space_name}" + delete: "Ο/Η %{user_name} αφαίρεσε τον χρήστη %{polling_officer_user} ως αξιωματούχο εκλογών από το χώρο %{space_name}" + polling_station: + create: "Ο/Η %{user_name} δημιούργησε το εκλογικό τμήμα %{resource_name} στο χώρο %{space_name}" + delete: "Ο/Η %{user_name} διέργαψε το εκλογικό τμήμα %{resource_name} στο χώρο %{space_name}" + update: "Ο/Η %{user_name} ενημέρωσε το εκλογικό τμήμα %{resource_name} στο χώρο %{space_name}" + voting: + create: "Ο/Η %{user_name} δημιούργησε την ψηφοφορία %{resource_name}" + publish: "Ο/Η %{user_name} δημοσίευσε τις εκλογές %{resource_name}" + unpublish: "Ο/Η %{user_name} απέσυρε την δημοσίευση των εκλογών %{resource_name}" + census: + admin: + census: + create: + invalid: Παρουσιάστηκε σφάλμα κατά τη φόρτωση της απογραφής, παρακαλώ προσπαθήστε ξανά αργότερα. + invalid_csv_header: Οι κεφαλίδες CSV λείπουν ή δεν είναι σωστές - διαβάστε προσεκτικά τις οδηγίες. + creating_data: + info_message: "Παρακαλώ περιμένετε, επεξεργάστηκε %{processed_count} από %{raw_count} σειρές από το %{file} αρχείο (αυτό μπορεί να διαρκέσει αρκετά λεπτά)." + delete: + button: Διαγραφή όλων των δεδομένων απογραφής + confirm: Η διαγραφή όλων των απογραφών δεν μπορεί να αναιρεθεί. Είστε βέβαιοι ότι θέλετε να συνεχίσετε; + destroy: + error: Παρουσιάστηκε σφάλμα κατά τη διαγραφή της απογραφής, παρακαλώ προσπαθήστε ξανά αργότερα. + success: Τα δεδομένα απογραφής διαγράφηκαν. + export_access_codes: + button: Εξαγωγή κωδικών πρόσβασης ψηφοφορίας + callout: Τώρα μπορείτε να προχωρήσετε στην εξαγωγή των κωδικών πρόσβασης. Αυτό μπορεί να γίνει μόνο μία φορά. Μόλις ξεκινήσετε την εξαγωγή, θα λάβετε ένα email με τις οδηγίες στο %{email} + confirm: Μπορείτε να εξαγάγετε τους κωδικούς πρόσβασης μόνο μία φορά. Βεβαιωθείτε ότι έχετε πρόσβαση στο λογαριασμό email %{email}. + file_not_exist: Το αρχείο δεν υπάρχει. + launch_error: Πρόβλημα κατά την έναρξη της εξαγωγής κωδικών πρόσβασης. + launch_success: Ξεκίνησε η εξαγωγή κωδικών πρόσβασης. Σύντομα θα λάβετε ένα email στο %{email}. + exporting_access_codes: + info_message: "Παρακαλώ περιμένετε, η εξαγωγή προετοιμάζεται, θα την λάβετε σύντομα στο %{email} (αυτό μπορεί να διαρκέσει αρκετά λεπτά)." + freeze: + callout: Η απογραφή έχει παγώσει και δεν μπορεί να τροποποιηθεί. + help_html: | + Τα δεδομένα της απογραφής έχουν φορτωθεί, οι κωδικοί έχουν δημιουργηθεί και εξαχθεί επιτυχώς.
    + Τώρα είστε έτοιμοι να ξεκινήσετε τις εκλογές.
    + Χρησιμοποιήστε το εξαγόμενο CSV με τους ατομικούς κωδικούς για να το διανείμετε κατά μήκος της απογραφής σας χρησιμοποιώντας τα δικά σας μέσα ή ενεργοποιήστε την καρτέλα "Μπορώ να ψηφίσω" για να επιτρέψετε σε οποιονδήποτε να ανακτήσει αυτόν τον κωδικό χρησιμοποιώντας τα δικά του δεδομένα απογραφής. + generate_access_codes: + button: Δημιουργία κωδικών πρόσβασης ψηφοφορίας + callout: Τώρα μπορείτε να προχωρήσετε στη δημιουργία των κωδικών πρόσβασης. Προσέξτε ότι μετά τη δημιουργία των κωδικών πρόσβασης δεν θα μπορείτε πλέον να τροποποιήσετε την απογραφή. + confirm: Αν συνεχίσετε, δεν θα μπορείτε να τροποποιήσετε την απογραφή. + info_message_all: "Όλες οι σειρές εισήχθησαν επιτυχώς από το αρχείο %{file} (%{raw_count} από %{data_count})." + info_message_warn: Παρακαλώ ελέγξτε ότι δεν λείπουν δεδομένα, επειδή δημιουργήθηκαν %{data_count} εγγραφές και το αρχείο %{file} είχε %{raw_count} σειρές. + launch_error: Πρόβλημα κατά την έναρξη της παραγωγής κωδικών πρόσβασης + launch_success: Έναρξη παραγωγής κωδίκων. + start_over: Παρακαλώ διαγράψτε την τρέχουσα απογραφή και ξεκινήστε ξανά με ένα σωστό αρχείο CSV με έγκυρες γραμμές. + generating_access_codes: + info_message: "Παρακαλώ περιμένετε, οι κωδικοί πρόσβασης ψήφου δημιουργούνται (αυτό μπορεί να διαρκέσει αρκετά λεπτά)..." + new: + file_help: + explanation: 'Οδηγίες για το αρχείο:' + message_1: Επιτρέπονται μόνο αρχεία CSV (.csv). + message_2: Το διαχωριστικό μεταξύ των στηλών πρέπει να είναι ερωτηματικό (";"). + has_ballot_styles_message: Μπορείτε να ρυθμίσετε τα Στυλ Ψηφοδελτίων. Παρακαλώ σιγουρευτείτε ότι το πεδίο "%{ballot_style_code_header}" στο CSV αντιστοιχεί στον κωδικό του Στυλ Ψηφοδελτίου που επιθυμείτε. + info_message: "Δεν υπάρχει απογραφή ακόμα. Παρακαλούμε χρησιμοποιήστε την παρακάτω φόρμα για να τη δημιουργήσετε εισάγοντας ένα αρχείο CSV." + missing_ballot_styles_message: 'Δεν υπάρχει Στυλ Ψηφοδελτίου για αυτή την ψηφοφορία ακόμα. Αν θέλετε να έχετε ερωτήσεις υπό όρους (δηλ. να παρουσιάστε στον ψηφοφόρο διαφορετικές ερωτήσεις ανάλογα, π.χ. με την περιοχή κατοικίας), θα πρέπει να ρυθμίσετε το Στυλ Ψηφοδελτίου πριν εισάγετε την απογραφή. Αν θέλετε να παρουσιάσετε σε όλους τους ψηφοφόρους τις ίδιες ερωτήσεις, μπορείτε να προχωρήσετε με τη διαδικασία εισαγωγής της απογραφής.' + submit: Υποβολή CSV + title: Δημιουργία της απογραφής + show: + heading: Απογραφή χώρου ψηφοφορίας + upload_info: + csv_example_with_ballot_style: 'Ένα παράδειγμα του αρχείου με στυλ ψηφοφορίας:' + csv_example_without_ballot_style: 'Ένα παράδειγμα του αρχείου χωρίς στυλ ψηφοφορίας:' + csv_header_after: Μην συμπεριλάβετε το τελευταίο πεδίο ("%{ballot_style_code_header}") αν δεν χρειάζεστε στυλ ψηφοφορίας/ερωτήσεις υπό όρους + csv_header_before: 'Το αρχείο απογραφής πρέπει να είναι αρχείο CSV με την ακόλουθη κεφαλίδα:' + document_types: + passport: Διαβατήριο + export_mailer: + access_codes_export: + click_button: 'Κάντε κλικ στον επόμενο σύνδεσμο για να κατεβάσετε τα δεδομένα κωδικών πρόσβασης.
    Το αρχείο θα είναι διαθέσιμο μέχρι %{date}.
    Θα χρειαστείτε το 7-Zip (για Windows), Keka (για MacOS) ή PeaZip (για Linux) για να το ανοίξετε. Κωδικός: %{password}' + download: Λήψη + subject: Η εξαγωγή των κωδικών πρόσβασης ψήφου για το %{voting_title} είναι διαθέσιμη + vote_flow: + already_voted_in_person: Αυτός ο συμμετέχων έχει ήδη ψηφίσει αυτοπροσώπως και δεν έχει δικαίωμα ψήφου. + datum_not_found: Τα δεδομένα δεν ταιριάζουν με κανέναν ψηφοφόρο. + content_blocks: + highlighted_votings: + name: Επισημασμένες ψηφοφορίες + landing_page: + polling_stations: + heading: Εκλογικά τμήματα + no_polling_stations: Δεν υπάρχουν ακόμα εκλογικά τμήματα. + monitoring_committee_members: + actions: + confirm_destroy: Είστε σίγουροι; + destroy: Διαγραφή + new: Νέο μέλος + title: Ενέργειες + polling_officer_zone: + closures: + back_to_polling_stations: Πίσω στα εκλογικά τμήματα + certify: + error: Παρουσιάστηκε σφάλμα κατά την επισύναψη του πιστοποιητικού, παρακαλώ προσπαθήστε ξανά. + heading: Επανακαταμέτρηση ψηφοφορίας - Μεταφόρτωση πιστοποιητικού + info_text: Ανεβάστε μια φωτογραφία του Πιστοποιητικού Κλεισίματος Εκλογών. + submit: Ανέβασμα Πιστοποιητικού + success: Το πιστοποιητικό ανέβηκε επιτυχώς. + create: + error: Παρουσιάστηκε σφάλμα κατά τη δημιουργία του κλεισίματος, παρακαλώ προσπαθήστε ξανά αργότερα. + success: Το κλείσιμο δημιουργήθηκε επιτυχώς. + edit: + heading: Επανακαταμέτρηση ψήφων - απαντήσεων + modal_ballots_results_count_error: + close_modal: Κλείσιμο + info_text: Ο συνολικός αριθμός των ψήφων δεν ταιριάζει με τον συνολικό αριθμό των φακέλων. Παρακαλώ εξετάστε το σύνολο των ψήφων. + title: Το σύνολο των ψηφοδελτίων δεν είναι σωστό + save_recount: Αποθήκευση επανακαταμέτρησης + total_ballots: Συνολικά ψηφοδέλτια + total_blank_ballots: Συνολικά κενά ψηφοδέλτια + total_null_ballots: Συνολικά μηδενικά ψηφοδέλτια + total_valid_ballots: Σύνολο έγκυρα ψηφοδέλτια + new: + election: 'Εκλογές:' + heading: Επανακαταμέτρηση ψήφων + info_text: 'Εισαγάγετε τον συνολικό αριθμό των ψήφων (φακέλων) που επανακαταμετρήθηκαν σε αυτόν το Εκλογικό Τμήμα:' + modal_ballots_count_error: + btn_validate_total: Επικύρωση συνολικής επανακαταμέτρησης των ψήφων + info_explanation_text: 'Εξετάσετε τον συνολικό αριθμό φήφων. Αν ο συνολικός αριθμός είναι εσφαλμένος, πρέπει να δώσετε μια εξήγηση για την επιτροπή παρακολούθησης:' + info_text: Ο συνολικός αριθμός των ψήφων που εισήχθησαν (φάκελοι) δεν ταιριάζει με την καταγραφή των ανθρώπων που ψήφισαν σε αυτό το εκλογικό τμήμα. + message_for_monitoring_committee: Μήνυμα για την Επιτροπή Παρακολούθησης + review_recount: Αναθεώρηση της επανακαταμέτρησης + text_area_placeholder: Παρακαλώ πληκτρολογήστε το μήνυμά σας + title: Οι συνολικές εγγραφές δεν είναι σωστές + total_ballots: 'Συνολικά ψηφοδέλτια:' + total_people: 'Σύνολο ανθρώπων:' + polling_station: 'Εκλογικό τμήμα:' + submit: Επαλήθευση συνολικού αριθμού + total_ballots_count: Αριθμός ψήφων + show: + heading: Επανακαταμέτρηση ψήφων + sign: + cancel: Ακύρωση + check_box: Το έχω επανεξετάσει και είναι το ίδιο με το φυσικό πιστοποιητικό κλεισίματος των εκλογών + confirm: Οκ, συνέχεια + error: Παρουσιάστηκε σφάλμα. Παρακαλώ προσπαθήστε ξανά. + heading: Επανακαταμέτρηση ψήφων - Υπογραφής κλεισίματος + info_text: Αν συνεχίσετε, δεν μπορείτε πλέον να τροποποιήσετε καμία από τις πληροφορίες, αυτή η ενέργεια δεν μπορεί να αναιρεθεί. + submit: Υπογράψτε το κλείσιμο + success: Το κλείσιμο υπογράφηκε επιτυχώς. + update: + error: Παρουσιάστηκε σφάλμα κατά την ενημέρωση των αποτελεσμάτων κλεισίματος, παρακαλώ προσπαθήστε ξανά αργότερα. + success: Τα αποτελέσματα κλεισίματος ενημερώθηκαν επιτυχώς. + in_person_votes: + complete_voting: + available_answers: 'Διαθέσιμες απαντήσεις:' + census_verified: Αυτός ο συμμετέχων δεν έχει ψηφίσει αυτοπροσώπως ακόμα. + census_verified_with_online_vote: Αυτός ο συμμετέχων ψήφισε ηλεκτρονικά. Εάν ψηφίσουν αυτοπροσώπως, οι προηγούμενες ψήφοι θα ακυρωθούν και αυτή θα είναι η οριστική ψήφος. + complete_voting: Ολοκλήρωση ψηφοφορίας + identify_another: Ταυτοποίηση άλλου συμμετέχοντα + questions_title: 'Έχουν δικαίωμα ψήφου στις ακόλουθες ερωτήσεις:' + questions_title_voted: 'Αυτός ο συμμετέχων έχει ήδη ψηφίσει online και δικαιούται να ψηφίσει στις ακόλουθες ερωτήσεις:' + voted: Ο συμμετέχων έχει ψηφίσει + create: + error: Η ψήφος δεν καταχωρήθηκε. Παρακαλώ δοκιμάστε ξανά. + in_person_form: + census_not_present: Αυτός ο συμμετέχων δεν περιλαμβάνεται στην απογραφή. + census_not_present_description: Πρέπει να μεταβούν στο γραφείο παραπόνων της απογραφής ή να επικοινωνήσουν με την υποστήριξη. + date_of_birth: Ημερομηνία γέννησης + day: Ημέρα + day_placeholder: ΗΗ + document_number: Αριθμός εγγράφου + document_number_placeholder: Αριθμός ταυτότητας + month: Μήνας + month_placeholder: MM + select: Επιλέξτε τον τύπο του εγγράφου + title: 'Επιλέξτε τον τύπο εγγράφου και εισάγετε τον αριθμό εγγράφου του συμμετέχοντα:' + validate_document: Επικύρωση εγγράφου + year: Έτος + year_placeholder: ΕΕΕΕ + new: + back: Πίσω στα εκλογικά τμήματα + title: Προσδιορίστε και επαληθεύστε έναν συμμετέχοντα + show: + back: Πίσω στα εκλογικά τμήματα + title: Αναμονή για εγγραφή για αυτοπροσώπως ψηφοφορία + update: + error: Παρουσιάστηκε σφάλμα κατά την εγγραφή της ψήφου. Παρακαλώ, προσπαθήστε ξανά. + success: + accepted: Η ψήφος καταχωρήθηκε με επιτυχία. + rejected: Η ψήφος δεν έγινε δεκτή από τον Πίνακα Ανακοινώσεων. Παρακαλούμε, επικοινωνήστε με το διαχειριστή του συστήματος. + verify_document: + census_present: Αυτός ο συμμετέχων περιλαμβάνεται στην απογραφή. + name: Όνομα + title: 'Ελέγξτε ότι τα ακόλουθα δεδομένα είναι σωστά:' + verify_document: Επαλήθευση εγγράφου + menu: + polling_officer_zone: Ζώνη Αξιωματούχου Εκλογών + polling_officers: + index: + polling_officer_role_description: Σας έχει ανατεθεί να ενεργήσετε ως Αξιωματούχος Εκλογικού Τμήματος (Πρόεδρος ή Διαχειριστής) σε μερικές από τις εκλογές σε αυτή την πλατφόρμα. + polling_station: + address: Διεύθυνση + count_votes: Καταμέτρηση ψήφων + election: Εκλογές + identify_person: Ταυτοποιήστε ένα άτομο + name: Όνομα + no_polling_stations: Δεν σας έχει ανατεθεί ακόμα κανένα Εκλογικό Τμήμα. + role: Ο ρόλος σας + show_closure: Προβολή κλεισίματος + title: Εκλογικά Τμήματα + voting: Ψηφοφορία + polling_officers: + actions: + confirm_destroy: Είστε σίγουροι; + destroy: Διαγραφή + title: Ενέργειες + roles: + manager: Διαχειριστής + president: Πρόεδρος + unassigned: Χωρίς ανάθεση + polling_station_closure_recount: + nota_option: Κενό/ Κανένα από τα παραπάνω + polling_officer_notes: 'Σημειώσεις Αξιωματούχου Εκλογών:' + polling_officer_notes_blank: Δεν υπάρχουν σημειώσεις + recount_summary: 'Σύνοψη επανακαταμέτρησης:' + signed: Υπογεγραμμένο + total_ballots: 'Συνολικά ψηφοδέλτια:' + total_blank_ballots: 'Συνολικά κενά ψηφοδέλτια:' + total_null_ballots: 'Συνολικά μηδενικά ψηφοδέλτια:' + total_valid_ballots: 'Σύνολο έγκυρα ψηφοδέλτια:' + polling_stations: + actions: + confirm_destroy: Είστε σίγουροι; + destroy: Διαγραφή + edit: Επεξεργασία + title: Ενέργειες + votings: + access_code_modal: + email: Αποστολή μέσω email στο %{email} + info: Χρειάζεστε έναν Κωδικό Πρόσβασης για να συμμετάσχετε. Αν δεν έχετε πάρει ένα μέσω ταχυδρομείου, μπορούμε να σας στείλουμε ένα νέο. + no_email: Δεν υπάρχει email + no_sms: Δεν υπάρχει τηλεφωνικός αριθμός + sms: Αποστολή με SMS στο %{sms} + title: Λάβετε Κωδικό Πρόσβασης + check_census: + check_status: Έλεγχος κατάστασης + description: Εδώ, έχετε τη δυνατότητα να ελέγξετε τα δεδομένα της απογραφής σας για να ξέρετε αν έχετε το δικαίωμα να συμμετάσχετε σε αυτήν την ψηφοφορία. Θα πρέπει να έχετε ήδη έναν κωδικό πρόσβασης, αλλά αν τον χάσετε, μπορείτε να τον ρωτήσετε ξανά, όταν τα δεδομένα σας είναι σωστά. + error: + info: 'Παρακαλώ δοκιμάστε ξανά. Αν νομίζετε ότι τα δεδομένα του συστήματος είναι εσφαλμένα, μπορείτε να το αναφέρετε εδώ: %{census_contact_information}.' + title: Τα δεδομένα που έχετε εισάγει δεν είναι στην απογραφή για αυτή την ψηφοφορία + form_title: 'Συμπληρώστε την παρακάτω φόρμα για να ελέγξετε τα δεδομένα απογραφής σας:' + invalid: Υπήρξε ένα πρόβλημα ελέγχου της απογραφής. + success: + access_link: μέσω email. + access_link_with_sms: μέσω SMS ή email. + info: Θα πρέπει να έχετε ήδη λάβει τον Κωδικό Πρόσβασης μέσω ταχυδρομείου. Σε περίπτωση που δεν τον έχετε, μπορείτε να τον ζητήσετε εδώ + title: Τα δεδομένα της απογραφής σας είναι σωστά! + title: Μπορώ να ψηφίσω; + check_fields: + date_of_birth: Ημερομηνία γέννησης + day: Ημέρα + day_placeholder: ΗΗ + document_number: Αριθμός εγγράφου + document_number_placeholder: Αριθμός ταυτότητας + document_type: Τύπος εγγράφου + month: Μήνας + month_placeholder: MM + postal_code: Τ.Κ. + postal_code_placeholder: Αριθμός Τ.Κ. + select: Επιλέξτε τον τύπο του εγγράφου + year: Έτος + year_placeholder: ΕΕΕΕ + count: + title: + one: "%{count} ψηφοφορία" + other: "%{count} ψηφοφορίες" + elections_log: + description: Το αρχείο καταγραφής εκλογών θα σας δείξει όλες τις σχετικές πληροφορίες για κάθε ψηφοφορία. Για παράδειγμα, η κατάσταση της διαδικασίας κλειδιού ή αν τα αποτελέσματα δημοσιεύθηκαν ήδη. Κάντε κλικ στις εκλογές που θέλετε τις πληροφορίες καταγραφής. + title: Αρχείο καταγραφής εκλογών + filters: + active: Ενεργό + all: Όλα + date: Ημερομηνία + finished: Ολοκληρώθηκε + search: Αναζήτηση + upcoming: Προσεχείς + index: + no_votings: Καμία ψήφος δεν ταιριάζει με τα κριτήρια αναζήτησής σας. + only_finished: Προς το παρόν, δεν υπάρχουν προγραμματισμένες εκλογές, αλλά εδώ μπορείτε να βρείτε τις ολοκληρωμένες εκλογές. + title: Ψηφοφορίες + login: + access_code: Κωδικός πρόσβασης + access_code_placeholder: Κωδικός πρόσβασης + ask_for_a_new_one: Ζητήστε ένα νέο. + dont_have_access_code: Δεν έχετε κωδικό πρόσβασης; + form_title: 'Συμπληρώστε την παρακάτω φόρμα για να αποκτήσετε πρόσβαση στην ψηφοφορία:' + start_voting: Αρχίστε να ψηφίζετε + title: Ταυτοποιήστε τον εαυτό μου με τα στοιχεία της απογραφής ψηφοφορίας + no_census_contact_information: Δεν υπάρχουν στοιχεία επικοινωνίας ακόμα. + orders: + label: 'Ταξινόμηση ψηφοφοριών κατά:' + random: Τυχαία + recent: Πιο πρόσφατες + send_access_code: + invalid: Παρουσιάστηκε πρόβλημα κατά την αποστολή του κωδικού πρόσβασης. + success: Ο Κωδικός Πρόσβασης σας στάλθηκε επιτυχώς. + votings_m: + badge_name: + finished: Ολοκληρωμένες + ongoing: Σε εξέλιξη + upcoming: Προσεχείς + unspecified: Δεν προσδιορίστηκε + voting_type: + hybrid: Υβριδικός + in_person: Αυτοπροσώπως + online: Ηλεκτρονικά + layouts: + decidim: + voting_navigation: + check_census: Μπορώ να ψηφίσω; + election_log: Αρχείο καταγραφής εκλογών + votings: + index: + promoted_votings: Επισημασμένες ψηφοφορίες + promoted_voting: + vote: Ψήφος diff --git a/decidim-elections/config/locales/en.yml b/decidim-elections/config/locales/en.yml new file mode 100644 index 00000000..80b9fb12 --- /dev/null +++ b/decidim-elections/config/locales/en.yml @@ -0,0 +1,1429 @@ +--- +en: + activemodel: + attributes: + answer: + description: Description + image: Image + proposals: Related proposals + title: Title + ballot_style: + code: Code + election: + description: Description + end_time: Voting ends at + start_time: Voting start at + title: Title + monitoring_committee_member: + email: Email + name: Name + polling_officer: + email: Email + name: Name + polling_station: + address: Address + location: Location + location_hints: Location hint + polling_station_managers: Managers + polling_station_president_id: President + title: Title + question: + max_selections: Maximum number of selections + min_selections: None of the above option + title: Title + trustees_participatory_space: + user_id: Participant + voting: + banner_image: Banner image + census_contact_information: Census contact information + description: Description + end_time: Voting ends + introductory_image: Introductory image + promoted: Promoted + scope_id: Scope + show_check_census: Show "check census" page + start_time: Voting begins + title: Title + voting_type: Voting type + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Needs to be reattached + ballot_result: + attributes: + base: + total_count_invalid: The total number of answers does not match the valid/blank/null breakdown. + election: + attributes: + attachment: + needs_to_be_reattached: Needs to be reattached + question_result: + attributes: + base: + blank_count_invalid: The total number of blank answers cannot be greater than the total of blank ballots. + trustee: + attributes: + name: + cant_be_changed: cannot be changed + public_key: + cant_be_changed: cannot be changed + voting: + attributes: + voting_type: + inclusion: "%{value} is not a valid voting type" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Polling officer already is a polling station president/manager. + voting: + different_organization: The voting must be in the same organizatoin as the user. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: The polling officer must be in the same voting as the polling station. + models: + decidim/elections/answer: + one: Answer + other: Answers + decidim/elections/election: + one: Election + other: Elections + decidim/elections/question: + one: Question + other: Questions + decidim/voting: + one: Voting + other: Votings + decidim/votings/census/dataset: + one: Dataset + other: Datasets + decidim/votings/census/datum: + one: Datum + other: Data + decidim/votings/polling_officer: + one: Polling officer + other: Polling officers + decidim/votings/polling_station: + one: Polling station + other: Polling stations + decidim/votings/voting: + one: Voting + other: Votings + decidim: + admin: + filters: + officers_assigned_eq: + label: Officers + values: + assigned: Assigned + unassigned: Not assigned + role_eq: + label: Role + values: + manager: Manager + president: President + unassigned: Unassigned + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Search %{collection} by name/email/nickname or polling station. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Search %{collection} by title, address or officer name/email/nickname. + signed_eq: + label: Signed + values: + 'false': Signed + 'true': Not signed + validated_eq: + label: Validated + values: + 'false': Not validated + 'true': Validated + voting_publications: + create: + error: There was a problem publishing this voting. + success: Voting successfully published. + destroy: + error: There was a problem unpublishing this voting. + success: Voting successfully unpublished. + components: + elections: + actions: + vote: Vote + name: Elections + settings: + global: + announcement: Announcement + step: + announcement: Announcement + elections: + actions: + confirm_destroy: Are you sure? + destroy: Destroy + edit: Edit + feedback: Voter feedback + import: Import proposals to answers + manage_answers: Manage answers + manage_questions: Manage questions + manage_steps: Manage steps + new_answer: New answer + new_election: New election + new_question: New question + new_trustee: New trustee + preview: Preview + publish: Publish + title: Actions + unpublish: Unpublish + admin: + answers: + create: + invalid: There was a problem creating this answer. + success: Answer successfully created. + destroy: + invalid: There was a problem deleting this answer. + success: Answer successfully deleted. + edit: + title: Edit answer + update: Update answer + index: + invalid_max_selections: You need %{missing_answers} more answer/s to match max selections. + title: Answers + new: + create: Create answer + title: New answer + not_selected: Not selected + select: + disable: Unselect answer + enable: Mark answer as selected + invalid: There was a problem selecting this answer. + success: Answer successfully selected. + selected: Selected + unselect: + invalid: There was a problem unselecting this answer. + success: Answer successfully unselected. + update: + invalid: There was a problem updating this answer. + success: Answer successfully updated. + elections: + create: + invalid: There was a problem creating this election. + success: Election successfully created. + destroy: + invalid: There was a problem deleting this election. + success: Election successfully deleted. + edit: + title: Edit election + update: Update election + form: + organization_time_zone: Check that the organization time zone is correct in the organization settings. The current configuration is %{time_zone} (%{time}). + index: + no_bulletin_board: There is no Bulletin Board server configured, which is needed to use this module. This task should be done by the System Administrator. + title: Elections + new: + create: Create election + title: New election + publish: + success: The election has been successfully published. + unpublish: + success: The election has been successfully unpublished. + update: + invalid: There was a problem updating this election. + success: Election successfully updated. + exports: + elections: Elections + feedback_form_answers: Feedback form answers + mailers: + trustee_mailer: + body: + help_html: |- +

    Hi %{user_name},


    +

    You have been added to act as a trustee in some elections that will take place in %{organization}.


    +

    A new section named "Trustee Zone" has been activated in your account. From there, you will perform tasks as needed. For now, please generate your identification keys.


    + subject: You have been added as a trustee to %{resource_name} + trustee_zone: Take me to the trustee zone + menu: + trustees: Trustees + models: + answer: + name: Answer + proposals_imports: + create: + invalid: There was a problem importing the proposals into answers. + success: "%{number} proposals successfully imported into answers." + new: + create: Import proposals to answers + no_components: There are no other proposal components in this participatory space to import the proposals into answers. + select_component: Please select a component + title: Import proposals + questions: + create: + election_started: The election has already started. + invalid: There was a problem creating this question. + success: Question successfully created. + destroy: + invalid: There was a problem deleting this question. + success: Question successfully deleted. + edit: + title: Edit question + update: Update question + index: + title: Questions + new: + create: Create question + title: New question + update: + invalid: There was a problem updating this question. + success: Question successfully updated. + steps: + create_election: + census: Census + errors: + census_codes_generated: Access codes for the census are not generated. + census_frozen: Access codes for the census are not exported. + census_uploaded: There is no census uploaded for this election. + component_published: The election component is not published. + fix_it_text: Fix it + max_selections: The questions do not have a correct value for amount of answers + minimum_answers: Questions must have at least two answers. + minimum_questions: The election must have at least one question. + published: The election is not published. + time_before: The start time is in less than %{hours} before the election starts. + trustees_number: The participatory space must have at least %{number} trustees with public key. + invalid: There was a problem setting up this election + no_trustees: There are no Trustees configured for this participatory space + not_used_trustee: "(not used)" + public_key: + 'false': does not have a public key + 'true': has a public key + requirements: + census_codes_generated: Access codes for the census are generated. + census_frozen: Access codes for the census are exported and census is frozen. + census_uploaded: Census is uploaded. + component_published: The election component is published. + max_selections: All the questions have a correct value for maximum of answers. + minimum_answers: Each question has at least 2 answers. + minimum_questions: The election has at least 1 question. + published: The election is published. + time_before: The setup is being done at least %{hours} before the election starts. + trustees_number: The participatory space has at least %{number} trustees with public key. + submit: Setup election + success: Election successfully sent to the Bulletin Board. + technical_configuration: + authority_name: "Authority name: %{value}" + bulletin_board_server: "Bulletin Board server: %{value}" + scheme_name: "Scheme name: %{value}" + title: View technical information + title: Setup election + trustees: Election Trustees + created: + invalid: There was a problem starting the key ceremony. + submit: Start the key ceremony + success: Start key ceremony request was successfully sent to the Bulletin Board. + title: Election created + trustees: Trustees + key_ceremony: + continue: Continue + title: Key ceremony + key_ceremony_ended: + errors: + time_before: The election is ready to start. You have to wait until %{hours} hours before the starting time (%{start_time}) to start the voting period. + invalid: There was a problem starting the voting period. + requirements: + time_before: The election will start soon. You can start the voting period manually, or it will be started automatically before the starting time, at %{start_time}. + submit: Start voting period + success: Start voting period request was successfully sent to the Bulletin Board. + title: Ready to start + processing: Processing... + results_published: + answer: Answer + not_selected: Not selected + question: Question + result: Result + selected: Selected + submit: Submit + title: Results published + tally_ended: + answer: Answer + not_selected: Not selected + question: Question + result: Result + selected: Selected + submit: Publish results + success: Publish results request was successfully sent to the Bulletin Board. + title: Calculated results + tally_started: + continue: Continue + invalid: There was a problem reporting the missing trustee. + mark_as_missing: Mark as missing + mark_as_missing_description: All the trustees should participate in this process, but if a trustee cannot take part in the process, you can mark it as missing. + success: Missing trustee report was successfully sent to the Bulletin Board. + tally_completion: The process will be completed when all the trustees are active or marked as missing. At least %{quorum} trustees are required to complete the process. + title: Tally process + undo_mark_as_missing: A trustee marked as missing by mistake will be able to participate before the completion of the process. They can proceed as usual and the missing mark will be ignored. + vote: + errors: + time_after: The election is still ongoing. You have to wait until the ending time (%{end_time}) to end the voting period. + invalid: There was a problem ending the voting period. + requirements: + time_after: The election has ended. You can end the voting period manually, or it will be ended automatically in a few minutes. + submit: End voting period + success: End voting period request was successfully sent to the Bulletin Board. + title: Vote period + vote_ended: + invalid: There was a problem starting the tally. + submit: Start tally + success: Start tally request was successfully sent to the Bulletin Board. + text: Vote has ended. You can start the tally now. + title: Vote period ended + vote_stats: + no_vote_statistics_yet: No vote statistics yet + title: Vote Statistics + voters: Voters + votes: Votes + trustees_participatory_spaces: + actions: + disable: Disable + enable: Consider + create: + exists: Trustee exists for this participatory space. + invalid: There was a problem creating a trustee. + success: Trustee successfully created. + delete: + invalid: There was a problem removing this trustee. + success: Trustee successfully removed. + form: + select_user: Select user + index: + title: Trustees + new: + create: Create Trustee + title: New Trustee + update: + invalid: There was a problem updating %{trustee} trustee. + success: Trustee %{trustee} successfully updated. + admin_log: + election: + create: "%{user_name} created the election %{resource_name} of %{space_name}" + delete: "%{user_name} deleted the election %{resource_name} of %{space_name}" + end_vote: "%{user_name} ended the voting period for the election %{resource_name} of %{space_name} on the Bulletin Board" + publish: "%{user_name} published the election %{resource_name} of %{space_name}" + publish_results: "%{user_name} published the results for the election %{resource_name} of %{space_name} on the Bulletin Board" + report_missing_trustee: "%{user_name} reported %{trustee_name} as a missing trustee during the tally for the election %{resource_name} of %{space_name} on the Bulletin Board" + setup: "%{user_name} created the election %{resource_name} of %{space_name} on the Bulletin Board" + start_key_ceremony: "%{user_name} started the key ceremony for the election %{resource_name} of %{space_name} on the Bulletin Board" + start_tally: "%{user_name} started the tally for the election %{resource_name} of %{space_name} on the Bulletin Board" + start_vote: "%{user_name} started the voting period for the election %{resource_name} of %{space_name} on the Bulletin Board" + unpublish: "%{user_name} unpublished the %{resource_name} of %{space_name} election" + update: "%{user_name} updated the election %{resource_name} of %{space_name}" + trustee: + create: "%{user_name} assigned the user %{trustee_user} as Trustee" + connection: + failed: + modal: + close: Close + communication_lost: Unfortunately, it looks like the communication with the voting server (Bulletin Board) is lost.
    It might be that the Internet connection is broken or that the destination server is too busy.
    You can try again later or contact support if this problem persist. + generic_error: Unfortunately, an unknown error has occurred. It is likely that your browser is not supported or that you are using the "incognito" or "private" mode which is not supported. + title: Something went wrong + election_m: + badge_name: + finished: Finished + ongoing: Active + upcoming: Upcoming + end_date: Ends + footer: + remaining_time: + one: "%{count} hour %{minutes} minutes remaining to vote." + other: "%{count} hours %{minutes} minutes remaining to vote." + zero: "%{minutes} minutes remaining to vote." + view: View + vote: Vote + label: + date: Dates + questions: Questions %{count} + start_date: Starts + unspecified: Not specified + elections: + count: + elections_count: + one: "%{count} election" + other: "%{count} elections" + election_log: + chained_hash: The chained Hash of this message + complete: Complete + creation_description: + complete: The election got created and is successfully set up on the Bulletin Board. + not_created: The election is not created yet. + creation_title: Election created + description: This is the election log where you can check the status of each step, e.g. when the election got created, if the tally process is completed, and when the election is closed. + download: Download + key_ceremony_description: + complete: The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys. + not_started: The key ceremony has not started yet. + started: The key ceremony has started but is not completed yet. + key_ceremony_title: Key Ceremony + not_available: Not yet available + not_created: Not created + not_ready: Not ready + not_started: Not started + published: Published + results_description: + not_published: The results are not published yet. + published: The results are published. + results_title: Results + started: Started + tally_description: + finished: The tally process is finished. + not_started: The tally process has not started yet. + started: The tally process has started. + tally_title: Tally process + title: Election Log + unpublished: Unpublished + verifiable_results: + checksum: 'File SHA256 checksum:' + description: + not_ready: The verifiable election file and SHA256 checksum are not available yet. As soon as the results are published, you will be able to verify this election. + ready: 'Here, you have the option to verify the election. First, you have to download the file and make sure it has not been corrupted. To do so, run the following command and check that the ouput matches the checksum:' + how_to_verify: 'Once you downloaded the file and made sure it is ok, you can proceed to run the universal verifier. Clone this repository and, from the root folder, run the following command:' + title: Verify Election results + verifiable_file: 'Verifiable election file:' + verify: Verify election + vote_description: + finished: The voting process is finished. + not_started: The voting process has not started yet. + started: The voting process has started. + vote_title: Voting process + filters: + active: Active + all: All + date: Date + finished: Finished + upcoming: Upcoming + preview: + available_answers: 'Available answers:' + description: 'These are the questions you will find in the voting process:' + title: Election questions + results: + description: 'These are the results of the voting, for each question:' + percentage: "%{count}%" + selected: Selected + title: Election results + votes: + one: "%{count} vote" + other: "%{count} votes" + zero: "%{count} votes" + show: + action_button: + change_vote: Change your vote + vote: Start voting + vote_again: Vote again + callout: + already_voted: You have already voted in this election. You can change your vote or verify it. + pending_vote: Your vote is being casted on the server. + vote_rejected: It was not possible to verify your vote. Please cast it again. + election_log: Election log + preview: Preview + verify: + already_voted: Already voted? + verify_here: Check your vote here. + will_verify: You will be able to verify your vote once the election is started. + voting_period_status: + finished: Voting began on %{start_time} and ended on %{end_time} + ongoing: 'Active voting until: %{end_time}' + upcoming: Voting begins on %{start_time} + feedback: + answer: + invalid: There was a problem submitting your feedback. + spam_detected: There was a problem answering the form. Maybe you have been too quick, can you try again? + success: Feedback successfully sent. + models: + answer: + fields: + proposals: Proposals + selected: Selected + title: Title + votes: Votes + election: + fields: + bb_status: Bulletin Board status + end_time: End at + start_time: Starts at + title: Title + verifiable_results_file_hash: File SHA256 checksum + verifiable_results_file_url: Verifiable election file + question: + fields: + answers: Answers + max_selections: Max. selections + title: Title + trustees_participatory_space: + fields: + considered: considered + email: Email + inactive: inactive + name: Name + notification: Notification sent at + public_key: Public Key + status: Status + orders: + label: Order elections by + older: Older + recent: Recent + trustee_zone: + elections: + backup_modal: + description: This election is being created in the Bulletin Board. Is is very important that every Trustee participating in it creates a backup copy of these keys and store them in a safe place. After that, the process continues. + download_election_keys: Download keys + title: Backup election keys for %{election} + key_ceremony_steps: + back: Back + description: This election is being created in the Bulletin Board. To complete this process, your participitation as a Trustee is needed. + keys: + create_election: Keys generation + key_ceremony: + joint_election_key: Joint Key generation + step_1: Keys publishing + list: + status: Status + task: Task + process_warning: Once the process is started, you should not exit this page until the process ends. It will take several minutes, as all Trustees should be connected to complete it. + start: Start + status: + completed: Completed + pending: Pending + processing: Processing + title: Create election keys for %{election} + restore_modal: + description: The Bulletin Board has information from you as a Trustee on this election. To continue the process, first upload the backup file generated during the previous session. + title: Restore election keys for %{election} + upload_election_keys: Upload election keys + tally_started_steps: + back: Back + description: The results for this election are being computed in the Bulletin Board. To complete this process, your participitation as a Trustee is needed. + keys: + end_tally: Tally ended + tally: + cast: Tally cast + share: Tally share + list: + status: Status + task: Task + process_warning: Once the process is started, you should not exit this page until the process ends. It will take several minutes, as all Trustees should be connected to complete it. + start: Start + status: + completed: Completed + pending: Pending + processing: Processing + title: Tally for %{election} + update: + error: The election status was not updated. + success: 'The election status is: %{status}.' + menu: + trustee_zone: Trustee zone + no_bulletin_board: + body: A configured Bulletin Board is required for this section. Contact the Administrator for more details. + title: Sorry, the Bulletin Board is not configured yet. + trustees: + show: + elections: + list: + action_required: + 'false': 'No' + name: Action required? + 'true': Perform action + bb_status: Status + election: Election + voting_period: Voting period + no_elections: Currently, you are not assigned to take any action as a trustee. You will receive a notification when it is time to act during different phases. + title: Elections + identification_keys: + cancel: Cancel + generate: Generate identification keys + generate_error: There was an error generating the identification keys. + generate_legend: You need to generate an identification pair of keys to participate on elections as a Trustee. + generate_legend_1: After pressing the button you should download the file with the generated identification keys. + generate_legend_2: Copy the downloaded file to a clean USB device + generate_legend_3: Ensure your computer does not have a copy of the file (e.g. check the Downloads and Desktop folders). + generate_legend_4: Make another copy of the file on a different external device and store it in a very safe place. + submit: Submit + submit_legend: After following all the steps explained above, complete the process sending the public identification key to the server. + submit_title: Submit the public identification key + title: Trustee identification keys + upload: Upload your identification keys + upload_error: + invalid_format: The uploaded file does not contain any identification key. + invalid_key: The identification keys in the uploaded file cannot be loaded. + invalid_public_key: The identification keys in the uploaded file does not match the public identification key stored. + upload_legend: The server has your public identification keys, but your browser still does not have it. You need to import the file with your identification keys to your computer from the backup you created after generating them. + not_supported_browser_description: It looks like you are using a web browser that cannot be used to act as a Trustee. Make sure you are using the most recent version of your browser, or try using any of the most popular browsers to be able to complete your Trustee tasks. + not_supported_browser_title: Upgrade browser to act as a Trustee + safari_warning_description: It looks like you are using Safari, which is not supported to act as a Trustee or to encrypt a Vote (this is due the memory restrictions that Apple impose in it). This might be solved in the future by a change of policy by Apple or future optimization of Decidim Elections. Please, use another browser meanwhile. + safari_warning_title: Safari browser detected + trustee_role_description: + with_keys: You have been assigned to act as a Trustee in some of the elections celebrated in this platform. + without_keys: You have been assigned to act as a Trustee. Please, generate and upload your identification keys. + update: + success: Your identification public key was successfully stored. + votes: + ballot_decision: + audit: Audit ballot + back: Start voting process again + ballot_hash: 'Your ballot identifier is:' + cast: Cast ballot to finish your vote + description_html: Here, you have the options to cast your ballot so that it is properly counted or, you can audit that your ballot was correctly encrypted. If you want to audit the vote, please read the instructions on how to proceed. + header: 'Ballot is encrypted: cast it or audit it' + casting: + header: Casting the vote... + text: Your ballot is being casted on the ballot box. + confirm: + answer_number: answer %{number} + confirm: Confirm + edit: edit + header: Confirm your vote + intro: Here is a summary of the vote you are about to cast.
    Please confirm your vote or edit your answers. + nota_option: Blank + confirmed: + back: Back to elections + experience: How was your experience? + feedback: Give us some feedback + header: Vote confirmed + lead: Your vote has been cast! + text: 'You can check that your vote has been successfully added to the ballot box with the following identifier: ' + verify_link: To check it, copy the identifier and paste it on the vote verification page + create: + error: There was a problem casting the vote. Please, try again. + encrypting: + header: Encrypting the vote... + text: Your ballot is being encrypted to ensure the secret of your vote. + failed: + header: Vote failed + lead: Your vote has not been casted! + text: Something went wrong, please try it again. + try_again: Try again + header: + ballot_decision: Cast + confirm: Confirm + election: Election + register: Register + vote_for: Vote for %{title} + messages: + invalid_token: Your session in the voting booth is not valid. Try to vote again. + not_allowed: You are not allowed to vote on this election at this moment. + modal: + close: Close + proposal_header: 'Proposals:' + new: + answer_choices: You can select up to %{choices} answers + more_information: More information + nota_option: Blank/ None of the above + preview_alert: This is a preview of the voting booth. + question_steps: Question %{current_step} of %{total_steps} + selections: "%{selected} of %{max_selections} selections" + onboarding_modal: + create_account: Create Account + description: Do you want to create a new account on the platform? You will be able to participate in the processes and be an active part of the organization. + no_account: No, thanks. + title: New to the platform? + update: + error: There was a problem updating the vote status. Please, vote again. + verify: + content: + heading: Verify your vote + info: This verifier checks that your vote, identified with an encrypted text string, has been cast correctly and is inside the ballot box. + error: + header: Vote not found! + info: The vote code was not found in the %{link} ballot box, try again. + form: + back: Back to the platform + submit: Check + vote_identifier: 'Identifier code:' + vote_identifier_help: This is the identifier that was given to you after you cast your vote (not the code to enter the voting booth). + header: + title: Verify your vote + success: + header: Vote located! + info: Your encrypted vote is in the %{link} ballot box. + voting_step: + back: Back + continue: Next + warnings: + empty_filters: There are no elections with this criteria. + no_elections: There is not any election scheduled. + no_scheduled_elections: Currently, there are no scheduled elections, but here you can find all the past elections listed. + events: + elections: + election_published: + email_intro: 'The %{resource_title} election is now active for %{participatory_space_title}. You can see it from this page:' + email_outro: You have received this notification because you are following %{participatory_space_title}. You can stop receiving notifications following the previous link. + email_subject: The %{resource_title} election is now active for %{participatory_space_title}. + notification_title: The %{resource_title} election is now active for %{participatory_space_title}. + trustees: + new_election: + email_intro: You got added as a trustee for the %{resource_title} election. + email_outro: You have received this notification because you have been added as trustee for the %{resource_title} election. + email_subject: You are a trustee for the %{resource_title} election. + notification_title: You have been selected as a Trustee in Election %{resource_title}. Please, do the key ceremony to set up the election. + new_trustee: + email_intro: An admin has added you as trustee for %{resource_name}. You should create your public key in your trustee zone + email_outro: You have received this notification because you have been added as trustee for %{resource_name}. + email_subject: You are a trustee for %{resource_name}. + notification_title: You have been added to act as a trustee in %{resource_name} for some elections that will take place in this platform.
    You will perform tasks as needed. For now, please generate your identification keys. + start_tally: + email_intro: The voting period for the %{resource_title} election has finished. Now, please, perform the tally of the election to publish the final results. + email_outro: You have received this notification because you are a trustee for the %{resource_title} election. + email_subject: The tally process for the %{resource_title} election has started. + notification_title: The voting period for the %{resource_title} election has finished. Now, please, perform the tally of the election to publish the final results. + votes: + accepted_votes: + email_intro: 'Your vote was accepted! Using your voting token: %{encrypted_vote_hash}, you can verify your vote here.' + email_outro: You have received this notification because you have voted for the %{resource_name} election. + email_subject: Your vote for %{resource_name} was accepted. + notification_title: 'Your vote was accepted. Verify your vote here using your vote token: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: You have been assigned as %{role} of the Polling Station %{polling_station_name} in %{resource_title}. You can manage the Polling Station from the dedicated Polling Officer Zone. + email_outro: You have received this notification because you have been assigned as %{role} of %{polling_station_name}. + email_subject: You are %{role} of the Polling Station %{polling_station_name}. + notification_title: You are %{role} of the Polling Station %{polling_station_name} in the voting %{resource_title}. + send_access_code: + instruction: 'Here is your Access Code that you asked for: %{access_code}. With this you will be able to participate in %{voting}.' + subject: Your Access Code to participate in %{voting} + help: + participatory_spaces: + votings: + contextual: "

    A voting is a space that allows you to ask a clear question to all the people who form an organization, make a call to participate in the voting, spark and order the debate for or against a response. When the voting date arrives, you can vote and publish the results of the votes.

    Examples: The votings can be about almost any aspect that affects an organization: some examples are changing the name or logo of the organization offering several alternatives, deciding Yes or No to become part of a larger organization, validating or rejecting a new strategic plan or the result of a working group, or defining whether the positions should remain a maximum of 1, 2 or 3 mandates.

    \n" + page: "

    A voting is a space that allows you to ask a clear question to all the people who form an organization, make a call to participate in the voting, spark and order the debate for or against a response. When the voting date arrives, you can vote and publish the results of the votes.

    Examples: The votings can be about almost any aspect that affects an organization: some examples are changing the name or logo of the organization offering several alternatives, deciding Yes or No to become part of a larger organization, validating or rejecting a new strategic plan or the result of a working group, or defining whether the positions should remain a maximum of 1, 2 or 3 mandates.

    \n" + title: What are votings? + menu: + votings: Votings + participatory_spaces: + related_elections: + see_all: See all elections + statistics: + elections_count: Elections + votings_count: Votings + votings: + admin: + ballot_styles: + create: + error: There was a problem creating this ballot style. + success: Ballot style successfully created. + destroy: + invalid: There was a problem deleting this ballot style. + success: Ballot style successfully deleted. + edit: + title: Edit ballot style + update: Update + form: + code_help: 'Hint: the code is the link between the census and a ballot style. When uploading the census data, every entry will be assigned a ballot style by matching the code.' + election: Election + questions: Questions for this ballot style + questions_help: 'Hint: select the questions from the election components to be presented to the voters assigned to this ballot style.' + index: + actions: + confirm_destroy: Are you sure? + destroy: Delete + edit: Edit + new: New ballot style + title: Actions + associated_census_data: Associated census entries + explanation_callout: A ballot style specifies what questions a voter will be presented in the booth. In a ballot style, you can choose what questions from this voting's election components belong to a ballot. The ballot style code is used to match a voter from the census with the ballot they will be presented in the booth. Do not create any ballot style if you always want to present all the questions. + title: Ballot Styles + new: + create: Create + title: Create ballot style + update: + invalid: There was a problem updating this ballot style. + success: Ballot style successfully updated. + content_blocks: + attachments_and_folders: + name: Voting attachments and folders + header: + name: Voting header + highlighted_votings: + max_results: Maximum amount of elements to show + html_block_1: + name: Voting html block 1 + html_block_2: + name: Voting html block 2 + html_block_3: + name: Voting html block 3 + main_data: + name: Title and description + metrics: + name: Voting metrics + polling_stations: + name: Voting polling stations + related_elections: + name: Voting elections + stats: + name: Voting statistics + timeline: + name: Voting timeline + index: + published: Published + unpublished: Unpublished + menu: + votings: Votings + votings_submenu: + attachment_collections: Folders + attachment_files: Files + attachments: Attachments + ballot_styles: Ballot Styles + census: Census + components: Components + info: About this voting + landing_page: Landing Page + monitoring_committee: Monitoring Committee + monitoring_committee_election_results: Validate Results + monitoring_committee_members: Members + monitoring_committee_polling_station_closures: Validate Certificates + monitoring_committee_verify_elections: Verify Elections + polling_officers: Polling Officers + polling_stations: Polling Stations + see_voting: See voting + models: + ballot_style: + fields: + code: Code + monitoring_committee_member: + fields: + email: Email + name: Name + polling_officer: + fields: + email: Email + name: Name + polling_station: Polling station (role) + polling_station: + fields: + address: Address + polling_station_managers: Managers + polling_station_president: President + title: Title + voting: + fields: + created_at: Created at + published: Published + title: Title + monitoring_committee_election_results: + actions: + title: Actions + view: View + index: + title: Choose an election you want to see the results for + results: + bulletin_board: Bulletin Board + election_totals: Election totals + polling_stations: Polling stations + result_types: + blank_answers: Blank answers + blank_ballots: Blank ballots + null_ballots: Null ballots + total_ballots: Total ballots + valid_ballots: Valid ballots + selected: Selected + title: Results for the election %{election_title} + totals: Totals + show: + change_election: Change election + publish_results: Publish results + publishing: Publishing results... + update: + invalid: There was a problem publishing the results. + rejected: The publication of the results was rejected by the Bulletin Board. Try again or contact the system administrator. + success: The results were successfully published. + monitoring_committee_members: + create: + invalid: There was a problem creating this monitoring committee member. + success: Monitoring committee member successfully created. + destroy: + invalid: There was a problem deleting this monitoring committee member. + success: Monitoring committee member successfully deleted. + form: + existing_user: Existing participant + non_user: Invite new participant + select_user: Search by name, email or nickname + user_type: Participant type + index: + title: Monitoring committee + new: + create: Create + title: Create monitoring committee member + monitoring_committee_polling_station_closures: + actions: + title: Actions + validate: Validate + view: View + closures: + change_election: Change election + signed: Signed? + title: Polling Stations for the election %{election_title} + validated: Validated? + edit: + change_polling_station: Back to Polling Stations + monitoring_committee_notes: Remarks + monitoring_committee_notes_placeholder: Report any incident here + title: Results for the election %{election_title} in the polling station %{polling_station_title} + elections: + title: Choose an election you want to validate + show: + change_polling_station: Back to Polling Stations + monitoring_committee_notes: Remarks from the Monitoring Committee + validate: + error: There was a problem validating the closure. + success: The closure has been validated correctly. + monitoring_committee_verify_elections: + index: + download: Download + how_to_checksum: 'To make sure the file you downloaded has not been corrupted or tampered with during the download process, run the following command in your console and check that the output matches the checksum reported above:' + how_to_download: To verify an election, download its verifiable file from the table above. + how_to_run_verifier: 'Once you downloaded the file and made sure it is ok, you can proceed to run the universal verifier. Clone this repository and, from the root folder, run the following command:' + how_to_title: How to verify the validity of an election + not_available: Not yet avaliable + title: Elections + polling_officers: + create: + invalid: There was a problem creating this polling officer. + success: Polling officer successfully created. + destroy: + invalid: There was a problem deleting this polling officer. + success: Polling officer successfully deleted. + form: + existing_user: Existing participant + non_user: Invite new participant + select_user: Search by name, email or nickname + user_type: Participant type + index: + role_manager: manager + role_president: president + title: Polling officers + new: + create: Create + title: Create polling officer + polling_officers_picker: + choose_polling_officers: Choose polling officers + no_polling_officers: No polling officers match your search criteria or there is not any polling officer. + polling_stations: + create: + invalid: There was a problem creating this polling station. + success: Polling station successfully created. + destroy: + invalid: There was a problem deleting this polling station. + success: Polling station successfully deleted. + edit: + title: Edit polling station + update: Update polling station + form: + address_help: 'Address: used by Geocoder to find the location' + location_help: 'Location: message directed to the voters implying the exact place of the polling station' + location_hints_help: 'Location hints: additional info. Example: the floor of the building where the polling station is located.' + polling_station_managers_help: 'Polling station managers: the officers that will act as polling station managers. Make sure the officers have already been created in Polling Officers and that they are not already assigned to another polling station' + polling_station_president_help: 'Polling station president: the officer that will act as polling station president. Make sure the officer has already been created in Polling Officers and that (s)he is not already assigned to another polling station' + select_president: Select a polling officer as president of the polling station + index: + title: Polling stations + new: + create: Create + title: Create polling station + update: + invalid: There was a problem updating this polling station. + success: Polling station successfully updated. + titles: + votings: Votings + votings: + actions: + confirm_destroy: Are you sure? + destroy: Destroy + new_voting: New Voting Space + create: + invalid: There was a problem creating this voting. + success: Voting successfully created. + edit: + add_election_component: You do not have any election configured for this voting. Please add it in the Components section. + assign_missing_officers: There are Polling Stations without President and/or Managers. Please assign them from the Polling stations section. + update: Update + form: + banner_image: Banner image + census_contact_information: Census contact information + census_contact_information_help: This contact information is for a participant who wants to report issues with the census. It can be an email address, a contact form on another site, a survey for visitors, etc. + introductory_image: Introductory image + promoted: Promoted + select_a_voting_type: Please select a voting type + show_check_census_help: Whether to show the "Can I vote?" link at the public votings menu. + slug: Slug + slug_help_html: 'URL slugs are used to generate the URLs that point to this voting. Only accepts letters, numbers and dashes, and must start with a letter. Example: %{url}' + title: Title + voting_type: + hybrid: Hybrid + in_person: In person + online: Online + voting_type_label: Voting type + new: + create: Create + title: New Voting + update: + invalid: There was a problem updating this voting. + success: Voting successfully updated. + admin_log: + ballot_style: + create: "%{user_name} created a ballot style with code %{ballot_style_code} in the space %{space_name}" + delete: "%{user_name} deleted the ballot style with code %{ballot_style_code} in the space %{space_name}" + update: "%{user_name} updated the ballot style with code %{ballot_style_code} in the space %{space_name}" + census: + create: "%{user_name} created the census for the space %{space_name}" + delete: "%{user_name} deleted the census for the space %{space_name}" + update: "%{user_name} updated the census for the space %{space_name}" + monitoring_committee_member: + create: "%{user_name} assigned the user %{monitoring_committee_member_user} as monitoring committee member in the space %{space_name}" + delete: "%{user_name} unassigned the user %{monitoring_committee_member_user} as monitoring committee member in the space %{space_name}" + polling_officer: + create: "%{user_name} assigned the user %{polling_officer_user} as polling officer in the space %{space_name}" + delete: "%{user_name} unassigned the user %{polling_officer_user} as polling officer in the space %{space_name}" + polling_station: + create: "%{user_name} created the polling station %{resource_name} in the space %{space_name}" + delete: "%{user_name} deleted the polling station %{resource_name} in the space %{space_name}" + update: "%{user_name} updated the polling station %{resource_name} in the space %{space_name}" + voting: + create: "%{user_name} created the %{resource_name} voting" + publish: "%{user_name} published the %{resource_name} voting" + unpublish: "%{user_name} unpublished the %{resource_name} voting" + census: + admin: + census: + create: + invalid: An error occurred uploading the census, please try again later. + invalid_csv_header: The CSV headers are missing or not correct - please read the instructions carefully. + creating_data: + info_message: "Please wait, processed %{processed_count} of %{raw_count} rows from %{file} file (this may take several minutes)." + delete: + button: Delete all census data + confirm: Deleting all the census data cannot be undone. Are you sure you want to continue? + destroy: + error: An error occurred deleting the census, please try again later. + success: Census data deleted. + export_access_codes: + button: Export voting Access Codes + callout: You can now proceed to export the access codes. This can only be done once. Once you launch the exportation, you will receive an email with the instructions to %{email} + confirm: You can only export the access codes once. Make sure you have access to the email account %{email}. + file_not_exist: This file does not exist. + launch_error: Problem launching the access codes export. + launch_success: Access codes export launched. Shortly you will receive an email to %{email}. + exporting_access_codes: + info_message: "Please wait, the export is being prepared, you will receive it shortly to %{email} (this may take several minutes)." + freeze: + callout: The census is frozen and cannot be modified. + help_html: | + The census data has been uploaded, the codes generated and exported successfully.
    + You are now ready to start the election.
    + Use the exported CSV with the individual codes to distribute it along your census using you own means or activate the "Can I vote" tab to let anyone retrieve this code using their own census data. + generate_access_codes: + button: Generate voting Access Codes + callout: You can now proceed to generate the access codes. Mind that after generating the access codes you will not be able to modify the census anymore. + confirm: If you continue, you will not be able to modify the census. + info_message_all: "All rows imported successfully from %{file} file (%{raw_count} of %{data_count})." + info_message_warn: Please check that no data is missing, because %{data_count} records were created and the uploaded file %{file} had %{raw_count} rows. + launch_error: Problem launching the access codes generation + launch_success: Codes generation launched. + start_over: Please delete the current census and start over again with a proper CSV file with valid rows. + generating_access_codes: + info_message: "Please wait, the voting access codes are being generated (this may take several minutes)..." + new: + file_help: + explanation: 'Guidance for file:' + message_1: Only CSV (.csv) files are allowed. + message_2: The separator between columns must be a semicolon (";"). + has_ballot_styles_message: You set up Ballot Styles. Please make sure that the "%{ballot_style_code_header}" field in the CSV corresponds to the desired Ballot Style's code. + info_message: "There is no census yet. Please use the form below to create it importing a CSV file." + missing_ballot_styles_message: 'There is no Ballot Style for this voting yet. If you wish to have conditional questions (i.e.: present the voter with different questions depending on, e.g., the district/region of residence), you need to set up the Ballot Styles before importing the census. If you want to present all voters with the same questions, you can proceed with the census import procedure.' + submit: Submit CSV + title: Create the census + show: + heading: Voting space census + upload_info: + csv_example_with_ballot_style: 'An example of the file with ballot styles:' + csv_example_without_ballot_style: 'An example of the file without ballot styles:' + csv_header_after: Do not include the last field ("%{ballot_style_code_header}") if you do not need ballot styles/conditional questions + csv_header_before: 'The census file must be a CSV file with the following header:' + document_types: + identification_number: Identification number + passport: Passport + export_mailer: + access_codes_export: + click_button: 'Click the next link to download the access codes data.
    The file will be available until %{date}.
    You will need 7-Zip (for Windows), Keka (for MacOS) or PeaZip (for Linux) to open it. Password: %{password}' + download: Download + subject: The export of the voting access codes for %{voting_title} is available + vote_flow: + already_voted_in_person: This participant has already voted in person and is not entitled to vote. + datum_not_found: The given data does not match any voter. + content_blocks: + highlighted_votings: + name: Highlighted votings + landing_page: + polling_stations: + heading: Polling stations + no_polling_stations: There are no polling stations yet. + monitoring_committee_members: + actions: + confirm_destroy: Are you sure? + destroy: Delete + new: New member + title: Actions + pages: + home: + highlighted_votings: + active_spaces: Active votings + see_all_spaces: See all votings + polling_officer_zone: + closures: + back_to_polling_stations: Back to polling stations + certify: + add_photos: Add photos + edit_photos: Edit photos + error: An error occurred attaching the certificate, please try again. + heading: Vote recount - Upload certificate + info_text: Please upload a picture of the Electoral Closure Certificate. + submit: Upload the certificate + success: Certificate uploaded successfully. + upload_photos: Upload a picture of the Electoral Closure Certificate + completed: + sub_heading: This recount has been certified and cannot longer be edited. + create: + error: An error occurred creating the closure, please try again later. + success: Closure successfully created. + destroy: + error: An error occurred removing the closure. + success: Closure successfully deleted. + edit: + confirm_start_over: This will remove the total number of votes and the notes attached. Are you sure? + heading: Vote recount - Answers recount + info_text: Please detail the total number of answers for each question. This must match the total number of answers introduced in the previous step (%{total} answers total). + modal_ballots_results_count_error: + blank: Expected total of blank votes is %{expected} but the sum of the blank questions is %{current}. + close_modal: Close + info_text: The total number of ballots does not match the total number of envelopes. Please review the total of ballots. + title: Total of ballots do not add up + total: Expected total is %{expected} but the sum of the valid, blank and null ballots is %{current}. + valid: Expected total of valid votes is %{expected} but the sum of the valid questions is %{current}. + save_recount: Save recount + start_over: If there is an error in the total number, you can delete everything and start over. + total_ballots: Total ballots + total_blank_ballots: Total blank ballots + total_null_ballots: Total null ballots + total_valid_ballots: Total valid ballots + new: + election: 'Election:' + heading: Vote recount + info_text: 'Please introduce the total number of ballots (envelopes) recounted in this Polling Station:' + modal_ballots_count_error: + btn_validate_total: Validate total recount of ballots + info_explanation_text: 'Please review the total number of ballots. If the total number is incorrect, you must provide an explanation for the Monitoring Committee:' + info_text: The total number of ballots (envelopes) introduced does not match the record of people that has voted in this Polling Station. + message_for_monitoring_committee: Message for the Monitoring Committee + review_recount: Review the recount + text_area_placeholder: Please type your message + title: Total records do not add up + total_ballots: 'Total of ballots:' + total_people: 'Total people:' + polling_station: 'Polling station:' + submit: Verify total number + total_ballots_count: Number of ballots + show: + edit_count_votes: Wrong numbers? You can still edit them. + heading: Vote recount + sub_heading: The recount needs to be closed by uploading a certificate. Once this is done, the recount will be sealed and can no longer be edited. + sign: + cancel: Cancel + check_box: I have reviewed this and is the same as the physical electoral closure certificate + confirm: Ok, continue + error: An error occurred, please try again. + heading: Vote recount - Sign closure + info_text: If you continue you can no longer modify any of the information, this action cannot be undone. + submit: Sign the closure + success: Closure signed successfully. + update: + error: An error occurred updating the closure results, please try again later. + success: Closure results successfully updated. + in_person_votes: + complete_voting: + available_answers: 'Available answers:' + census_verified: This participant has not voted in person yet. + census_verified_with_online_vote: This participant has already voted online. If they vote in person, the previous votes will be invalidated and this will be the definitive vote. + complete_voting: Complete voting + identify_another: Identify another participant + questions_title: 'They are entitled to vote in the following questions:' + questions_title_voted: 'This participant has already voted online and is entitled to vote in the following questions:' + voted: The participant has voted + create: + error: The vote was not registered. Please try again. + in_person_form: + census_not_present: This participant is not listed in the census. + census_not_present_description: They must go to the census complaint office or contact support. + date_of_birth: Date of birth + day: Day + day_placeholder: DD + document_number: Document number + document_number_placeholder: ID number + month: Month + month_placeholder: MM + select: Select the type of the document + title: 'Select the document type and enter the participant''s document number:' + validate_document: Validate document + year: Year + year_placeholder: YYYY + new: + back: Back to polling stations + title: Identify and verify a participant + show: + back: Back to polling stations + title: Waiting for the in person vote to be registered + update: + error: There was an error registering the vote. Please, try again. + success: + accepted: The vote was registered successfully. + rejected: The vote was not accepted by the Bulletin Board. Please, contact the system administrator. + verify_document: + census_present: This participant is listed in the census. + name: Name + title: 'Check that the following data is correct:' + verify_document: Verify document + menu: + polling_officer_zone: Polling Officer zone + polling_officers: + index: + polling_officer_role_description: You have been assigned to act as a Polling Station Officer (President or Manager) in some of the elections celebrated in this platform. + polling_station: + address: Address + count_votes: Count votes + election: Election + identify_person: Identify a person + name: Name + no_polling_stations: You are not assigned to any Polling Station yet. + role: Your role + show_closure: View closure + title: Polling Stations + voting: Voting + polling_officers: + actions: + confirm_destroy: Are you sure? + destroy: Delete + new: New polling officer + title: Actions + roles: + manager: Manager + president: President + unassigned: Unassigned + polling_station_closure_certificate: + current_certificate: 'Current certificate:' + polling_station_closure_recount: + nota_option: Blank/ None of the above + polling_officer_notes: 'Polling officer notes:' + polling_officer_notes_blank: There are no notes + recount_summary: 'Recount summary:' + signed: Signed + total_ballots: 'Total ballots:' + total_blank_ballots: 'Total blank ballots:' + total_null_ballots: 'Total null ballots:' + total_valid_ballots: 'Total valid ballots:' + polling_stations: + actions: + confirm_destroy: Are you sure? + destroy: Delete + edit: Edit + new: New polling station + title: Actions + votings: + access_code_modal: + email: Send by email to %{email} + info: You need an Access Code to participate. If you did not get one by postal mail, we can send you a new one. + no_email: No email available + no_sms: No phone number available + sms: Send by SMS to %{sms} + title: Get Access Code + check_census: + check_status: Check status + description: Here, you have the option to check your census data to know if you have the right to participate in this voting. You should have an access code already but if you lost it, you can ask for it again, when your data is correct. + error: + info: 'Please try again. If you think the data in the system is incorrect, you can report it here: %{census_contact_information}.' + title: The data you have entered are not in the census for this vote + form_title: 'Fill the following form to check your census data:' + invalid: There was a problem checking the census. + success: + access_link: via email. + access_link_with_sms: via SMS or email. + info: You should have received your Access Code by postal mail already. In case, you do not have it, you can request it here + title: Your census data is correct! + title: Can I vote? + check_fields: + date_of_birth: Date of birth + day: Day + day_placeholder: DD + document_number: Document number + document_number_placeholder: ID number + document_type: Document type + month: Month + month_placeholder: MM + postal_code: Postal code + postal_code_placeholder: Postal code number + select: Select the type of the document + year: Year + year_placeholder: YYYY + count: + title: + one: "%{count} voting" + other: "%{count} votings" + elections_log: + description: The election log will show you all relevant information about each voting. For example, the status of the key ceremony or tally or if results are published already. Click on the election you want the log information about. + title: Election log + filters: + active: Active + all: All + date: Date + finished: Finished + search: Search + upcoming: Upcoming + index: + no_votings: No voting matches your search criteria. + only_finished: Currently, there are no scheduled votings, but here you can find the finished votings listed. + title: Votings + login: + access_code: Access code + access_code_placeholder: Access code + ask_for_a_new_one: Ask for a new one. + dont_have_access_code: Do not have an access code? + form_title: 'Fill the following form to access the voting:' + start_voting: Start voting + title: Identify myself with my voting census data + no_census_contact_information: There is no contact information yet. + orders: + label: 'Sort votings by:' + random: Random + recent: Most recent + send_access_code: + invalid: There was a problem sending the Access Code. + success: Your Access Code got send successfully. + show: + title: About this voting + votings_m: + badge_name: + finished: Finished + ongoing: Ongoing + upcoming: Upcoming + unspecified: Not specified + voting_type: + hybrid: Hybrid + in_person: In person + online: Online + layouts: + decidim: + voting_navigation: + check_census: Can I vote? + election_log: Election log + votings: + index: + promoted_votings: Highlighted votings + promoted_voting: + vote: Vote diff --git a/decidim-elections/config/locales/eo-UY.yml b/decidim-elections/config/locales/eo-UY.yml new file mode 100644 index 00000000..75998140 --- /dev/null +++ b/decidim-elections/config/locales/eo-UY.yml @@ -0,0 +1 @@ +eo: diff --git a/decidim-elections/config/locales/eo.yml b/decidim-elections/config/locales/eo.yml new file mode 100644 index 00000000..75998140 --- /dev/null +++ b/decidim-elections/config/locales/eo.yml @@ -0,0 +1 @@ +eo: diff --git a/decidim-elections/config/locales/es-MX.yml b/decidim-elections/config/locales/es-MX.yml new file mode 100644 index 00000000..836786d5 --- /dev/null +++ b/decidim-elections/config/locales/es-MX.yml @@ -0,0 +1,1425 @@ +es-MX: + activemodel: + attributes: + answer: + description: Descripción + image: Imagen + proposals: Propuestas relacionadas + title: Título + ballot_style: + code: Código + election: + description: Descripción + end_time: La votación termina en + start_time: La votación comienza a las + title: Título + monitoring_committee_member: + email: Correo electrónico + name: Nombre + polling_officer: + email: Correo electrónico + name: Nombre + polling_station: + address: Dirección + location: Ubicación + location_hints: Detalles de ubicación + polling_station_managers: Gestoras + polling_station_president_id: Presidencia + title: Título + question: + max_selections: Número máximo de opciones + min_selections: Ninguna de las anteriores + title: Título + trustees_participatory_space: + user_id: Participante + voting: + banner_image: Imagen de cabecera + census_contact_information: Datos de contacto del censo + description: Descripción + end_time: La votación finaliza + introductory_image: Imagen de presentación + promoted: Destacada + scope_id: Ámbito + show_check_census: Mostrar la página de "comprobar censo" + start_time: La votación empieza + title: Título + voting_type: Tipo de votación + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Es necesario volver a adjuntar el archivo + ballot_result: + attributes: + base: + total_count_invalid: El número total de respuestas no coincide con el desglose válido/blanco/nulo. + election: + attributes: + attachment: + needs_to_be_reattached: Es necesario volver a adjuntar el archivo + question_result: + attributes: + base: + blank_count_invalid: El número total de respuestas en blanco no puede ser mayor que el total de papeletas en blanco. + trustee: + attributes: + name: + cant_be_changed: no se puede cambiar + public_key: + cant_be_changed: no se puede cambiar + voting: + attributes: + voting_type: + inclusion: "%{value} no es un tipo de votación válido" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: El gestor electoral ya es presidente/gestor del punto de votación. + voting: + different_organization: La votación debe estar en la misma organización que la participante. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: La oficial de votación debe estar en la misma votación que el punto de votación. + models: + decidim/elections/answer: + one: Respuesta + other: Respuestas + decidim/elections/election: + one: Elección + other: Elecciones + decidim/elections/question: + one: Pregunta + other: Preguntas + decidim/voting: + one: Votación + other: Votaciones + decidim/votings/census/dataset: + one: Conjunto de datos + other: Conjuntos de datos + decidim/votings/census/datum: + one: Dato + other: Datos + decidim/votings/polling_officer: + one: Gestor de mesa + other: Gestores de mesa + decidim/votings/polling_station: + one: Punto de votación + other: Puntos de votación + decidim/votings/voting: + one: Votación + other: Votaciones + decidim: + admin: + filters: + officers_assigned_eq: + label: Responsables + values: + assigned: Asignado + unassigned: No asignado + role_eq: + label: Rol + values: + manager: Gestor + president: Presidencia + unassigned: Sin asignar + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Buscar %{collection} por nombre/email/alias o punto de votación. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Buscar %{collection} por título, dirección o nombre del responsable/email/alias. + signed_eq: + label: Firmada + values: + 'false': Firmada + 'true': Sin firmar + validated_eq: + label: Validada + values: + 'false': Sin validar + 'true': Validada + voting_publications: + create: + error: Se ha producido un error al publicar esta votación. + success: Votación publicada correctamente. + destroy: + error: Ha habido un problema al despublicar esta votación. + success: Votación despublicada correctamente. + components: + elections: + actions: + vote: Votar + name: Elecciones + settings: + global: + announcement: Aviso + step: + announcement: Aviso + elections: + actions: + confirm_destroy: '¿Seguro que quieres eliminar?' + destroy: Eliminar + edit: Modificar + feedback: Retorno de la votante + import: Importar de propuestas a respuestas + manage_answers: Gestionar las respuestas + manage_questions: Gestionar las preguntas + manage_steps: Gestionar las fases + new_answer: Añadir respuesta + new_election: Añadir elección + new_question: Añadir pregunta + new_trustee: Añadir garante + preview: Vista previa + publish: Publicar + title: Acciones + unpublish: Despublicar + admin: + answers: + create: + invalid: Se ha producido un error al crear esta respuesta. + success: Respuesta creada con éxito. + destroy: + invalid: Se ha producido un error al eliminar esta respuesta. + success: Respuesta eliminada con éxito. + edit: + title: Editar las respuestas + update: Actualizar respuesta + index: + invalid_max_selections: Necesitas %{missing_answers} respuesta/s más para coincidir con la selección máxima. + title: Respuestas + new: + create: Crear respuesta + title: Nueva respuesta + not_selected: No seleccionada + select: + disable: Deseleccionar respuesta + enable: Marcar respuesta como seleccionada + invalid: Hubo un problema al seleccionar esta respuesta. + success: Respuesta seleccionada con éxito. + selected: Seleccionada + unselect: + invalid: Hubo un problema al deseleccionar esta respuesta. + success: Respuesta deseleccionada correctamente. + update: + invalid: Se ha producido un error al actualizar esta respuesta. + success: Respuesta actualizada correctamente. + elections: + create: + invalid: Se ha producido un error al crear esta elección. + success: La elección se ha creado correctamente. + destroy: + invalid: Se ha producido un error al eliminar la elección. + success: La elección se ha eliminado correctamente. + edit: + title: Editar la votación + update: Actualizar la votación + form: + organization_time_zone: Compruebe que la zona horaria es correcta en la configuración de la organización. La configuración actual es %{time_zone} (%{time}). + index: + no_bulletin_board: No hay ningún servidor de Bulletin Board configurado, el cual es necesario para utilizar este módulo. Esta tarea debe ser realizada por la persona administradora del sistema. + title: Votaciones + new: + create: Crear una votación + title: Nueva votación + publish: + success: La votación se ha publicado correctamente. + unpublish: + success: La votación se ha despublicado correctamente. + update: + invalid: Se ha producido un error al actualizar esta votación. + success: La votación se ha actualizado correctamente. + exports: + elections: Elecciones + feedback_form_answers: Retorno de las respuestas + mailers: + trustee_mailer: + body: + help_html: |- +

    Hola %{user_name},


    +

    Te han añadido para actuar como garante en algunes elecciones que tendran lugar en %{organization}.


    +

    Se ha creado en tu cuenta una nueva sessión llamada "Zona de garantes". Desde esta, podrá realitzar las tareas requeridas. De momento, por favor, genera tus claves de identificación.


    + subject: Se te ha añadido como garante de %{resource_name} + trustee_zone: Llévame al espacio de garantes + menu: + trustees: Garantes + models: + answer: + name: Respuesta + proposals_imports: + create: + invalid: Se ha producido un error al importar las propuestas como respuestas. + success: "%{number} propuestas importadas a respuestas correctamente." + new: + create: Importar de propuestas a respuestas + no_components: No hay otros componentes de la propuesta en este espacio participativo desde el que importar las propuestas en los proyectos. + select_component: Por favor selecciona un componente + title: Importar propuestas + questions: + create: + election_started: La elección ya ha comenzado. + invalid: Se ha producido un error al crear esta pregunta. + success: Se ha creado la pregunta correctamente. + destroy: + invalid: Se ha producido un error al eliminar esta pregunta. + success: Se ha eliminado la pregunta correctamente. + edit: + title: Editar la pregunta + update: Actualizar la pregunta + index: + title: Preguntas + new: + create: Crear una pregunta + title: Nueva pregunta + update: + invalid: Se ha producido un error al actualizar esta pregunta. + success: La pregunta se ha actualizado correctamente. + steps: + create_election: + census: Censo + errors: + census_codes_generated: Los códigos de acceso al censo no se han podido generar. + census_frozen: Los códigos de acceso al censo no se han podido exportar. + census_uploaded: No se ha subido ningún censo para esta elección. + component_published: La elección no está publicada. + fix_it_text: Arreglarlo + max_selections: Las preguntas no tienen un valor correcto para la cantidad de respuestas + minimum_answers: Las preguntas deben tener al menos dos respuestas. + minimum_questions: La elección debe tener al menos una pregunta. + published: La elección no está publicada. + time_before: Se ha configurado la hora de inicio en menos de %{hours} horas antes de que comience la elección. + trustees_number: El espacio participativo debe tener al menos %{number} garantes con clave pública. + invalid: Hubo un problema al configurar esta elección + no_trustees: No hay garantes configurados para este espacio participativo + not_used_trustee: "(no se usa)" + public_key: + 'false': no tiene una clave pública + 'true': tiene una clave pública + requirements: + census_codes_generated: Se han generado los códigos de acceso al censo. + census_frozen: Se han exportado los códgos para el censo y el censo ha quedado congelado. + census_uploaded: El censo está subido. + component_published: La elección está publicada. + max_selections: Todas las preguntas tienen un valor correcto para máximo de respuestas. + minimum_answers: Cada pregunta tiene al menos 2 respuestas. + minimum_questions: La elección tiene al menos 1 pregunta. + published: La elección está publicada. + time_before: La configuración se está realizando al menos %{hours} horas antes de que comience la elección. + trustees_number: El espacio participativo tiene al menos %{number} garantes con clave pública. + submit: Configurar elección + success: La elección se ha enviado con éxito al "Bulletin Board". + technical_configuration: + authority_name: "Nombre de la autoridad: %{value}" + bulletin_board_server: "Servidor del boletín: %{value}" + scheme_name: "Nombre del esquema: %{value}" + title: Ver la información técnica + title: Configurar elección + trustees: Garantes de la elección + created: + invalid: Hubo un problema al iniciar la ceremonia de claves. + submit: Iniciar la ceremonia de claves + success: La solicitud para iniciar la ceremonia de claves fue enviada con éxito al "Bulletin Board". + title: Elección creada + trustees: Garantes + key_ceremony: + continue: Continuar + title: Ceremonia de claves + key_ceremony_ended: + errors: + time_before: La elección está lista para comenzar. Tienes que esperar hasta %{hours} horas antes de la hora de inicio (%{start_time}) para comenzar el periodo de votación. + invalid: Hubo un problema al iniciar el período de votación. + requirements: + time_before: La elección comenzará pronto. Puede iniciar el periodo de votación manualmente, o se iniciará automáticamente antes de la hora de inicio, a las %{start_time}. + submit: Empezar período de votación + success: La solicitud de período de votación se ha enviado correctamente al "Bulletin Board". + title: Listo para empezar + processing: Procesando... + results_published: + answer: Respuesta + not_selected: No seleccionada + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Enviar + title: Resultados publicados + tally_ended: + answer: Respuesta + not_selected: No seleccionado + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Publicar resultados + success: La solicitud de publicación de resultados se envió correctamente al "Bulletin Board". + title: Resultados calculados + tally_started: + continue: Continuar + invalid: Hubo un problema al reportar la ausencia de la garante. + mark_as_missing: Marcar como ausente + mark_as_missing_description: Todos los garantes deberían participar en este proceso, pero si uno de ellos no puede tomar parte en él, puedes marcarlo como ausente. + success: La notificación de ausencia de la garante se ha enviado con éxito al "Bulletin Board". + tally_completion: El proceso estará completo cuando todas las personas garantes estén registradas como activas o reportadas como ausentes. Se requiere un quorum de %{quorum} personas garantes para completar el proceso. + title: Proceso de recuento + undo_mark_as_missing: Una persona garante reportada como ausente por error podrá participar hasta antes de la finalización del proceso. Puede realizar sus actividades normales y el reporte de ausencia será ignorado. + vote: + errors: + time_after: La elección aún está en curso. Tienes que esperar hasta la hora de finalización (%{end_time}) para terminar el periodo de votación. + invalid: Hubo un problema al terminar el período de votación. + requirements: + time_after: La elección ha terminado. Puedes terminar el período de votación manualmente, o terminará automáticamente en unos minutos. + submit: Finalizar período de votación + success: La solicitud para finalizar el período de votación se ha enviado correctamente al "Bulletin Board". + title: Periodo de votación + vote_ended: + invalid: Hubo un problema al iniciar el recuento. + submit: Comenzar recuento + success: La solicitud de inicio del recuento se envió correctamente al "Bulletin Board". + text: La votación ha terminado. Puedes iniciar el recuento ahora. + title: Periodo de votación finalizado + vote_stats: + no_vote_statistics_yet: Aún no hay estadísticas de voto + title: Estadísticas de voto + voters: Votantes + votes: Votos + trustees_participatory_spaces: + actions: + disable: Deshabilitar + enable: Considerar + create: + exists: Existe garante para este espacio participativo. + invalid: Se ha producido un error al crear una garante. + success: Garante creada correctamente. + delete: + invalid: Se ha producido un error al eliminar esta garanta. + success: Garante eliminada correctamente. + form: + select_user: Seleccionar usuario + index: + title: Garantes + new: + create: Crear garante + title: Nuevo garante + update: + invalid: Se ha producido un error al actualizar a %{trustee} como garante. + success: Garante %{trustee} actualizada correctamente. + admin_log: + election: + create: "%{user_name} creó la elección %{resource_name} de %{space_name}" + delete: "%{user_name} eliminó la elección %{resource_name} de %{space_name}" + end_vote: "%{user_name} terminó el período de votación para la elección %{resource_name} de %{space_name} en el Bulletin Board" + publish: "%{user_name} publicó la elección %{resource_name} de %{space_name}" + publish_results: "%{user_name} publicó los resultados para la elección %{resource_name} de %{space_name} en el Bulletin Board" + report_missing_trustee: "%{user_name} reportó a %{trustee_name} como garante ausente durante el recuento de la elección %{resource_name} de %{space_name} en el Bulletin Board" + setup: "%{user_name} creó la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_key_ceremony: "%{user_name} comenzó la ceremonia claves para la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_tally: "%{user_name} comenzó el recuento de la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_vote: "%{user_name} comenzó el período de votación para la elección %{resource_name} de %{space_name} en el Bulletin Board" + unpublish: "%{user_name} ha despublicado el %{resource_name} de la elección de %{space_name}" + update: "%{user_name} actualizó la elección %{resource_name} de %{space_name}" + trustee: + create: "%{user_name} asignó al usuario %{trustee_user} como garante" + connection: + failed: + modal: + close: Cerrar + communication_lost: Desafortunadamente, parece que la comunicación con el servidor de votación (Bulletin Board) se ha perdido.
    Puede ser que la conexión a Internet esté averiada o que el servidor de destino esté demasiado ocupado.
    Puedes intentarlo más tarde o ponerte en contacto con el servicio de asistencia si este problema persiste. + generic_error: Desafortunadamente, se ha producido un error desconocido. Es probable que tu navegador no esté soportado o que estés usando el modo "incógnito" o "privado" que no es compatible. + title: Algo salió mal + election_m: + badge_name: + finished: Finalizada + ongoing: Activa + upcoming: Próximas + end_date: Termina + footer: + remaining_time: + one: "%{count} hora %{minutes} minutos restantes para votar." + other: "%{count} horas %{minutes} minutos restantes para votar." + view: Ver + vote: Votar + label: + date: Fechas + questions: Preguntas %{count} + start_date: Empieza + unspecified: Sin especificar + elections: + count: + elections_count: + one: "%{count} votación" + other: "%{count} votaciones" + election_log: + chained_hash: El Hash encadenado de este mensaje + complete: Completar + creation_description: + complete: La elección se creó y se configuró con éxito en el Bulletin Board. + not_created: La elección no se ha creado todavía. + creation_title: Elección creada + description: Este es el registro de la elección, donde puedes comprobar el estado de cada paso, por ejemplo, cuándo se ha creado, si se ha completado el proceso de recuento y cuándo se ha cerrado la elección. + download: Descargar + key_ceremony_description: + complete: La ceremonia de claves se ha completado. Cada garante tiene claves válidas y ha descargado las claves de copia de seguridad necesarias. + not_started: La ceremonia de claves aún no ha comenzado. + started: La ceremonia de claves ha comenzado, pero aún no ha terminado. + key_ceremony_title: Ceremonia de claves + not_available: No disponible todavía + not_created: No creado + not_ready: No está listo + not_started: No iniciado + published: Publicado + results_description: + not_published: Los resultados aún no se han publicado. + published: Los resultados se han publicado. + results_title: Resultados + started: Iniciado + tally_description: + finished: El proceso de recuento ha terminado. + not_started: El proceso de recuento aún no ha comenzado. + started: El proceso de recuento ha comenzado. + tally_title: Proceso de recuento + title: Registro de la elección + unpublished: Despublicada + verifiable_results: + checksum: 'Suma de comprobación SHA256 del archivo:' + description: + not_ready: El archivo verificable de la elección y la suma de comprobación SHA256 aún no están disponibles. En cuanto se publiquen los resultados, podrás verificar esta elección. + ready: 'Aquí tienes la opción de verificar la elección. En primer lugar, tienes que descargar el archivo y asegurarte de que no se ha corrompido. Para ello, ejecuta el siguiente comando y comprueba que la salida coincide con la suma de comprobación:' + how_to_verify: 'Una vez que hayas descargado el archivo y te hayas asegurado de que está bien, puedes proceder a ejecutar el verificador universal. Clona este repositorio y, desde la carpeta raíz, ejecuta el siguiente comando:' + title: Verificar resultados de la elección + verifiable_file: 'Archivo verificacle de la elección:' + verify: Verificar elección + vote_description: + finished: El proceso de votación ha concluido. + not_started: El proceso de votación aún no ha comenzado. + started: El proceso de votación ha comenzado. + vote_title: Proceso de votación + filters: + active: Activas + all: Todas + date: Fecha + finished: Finalizadas + upcoming: Próximas + preview: + available_answers: 'Respuestas disponibles:' + description: 'Estas son las preguntas que encontrarás en el proceso de votación:' + title: Preguntas de la elección + results: + description: 'Estos son los resultados de la votación, para cada pregunta:' + percentage: "%{count}%" + selected: Seleccionado + title: Resultados de la elección + votes: + one: "%{count} voto" + other: "%{count} votos" + show: + action_button: + change_vote: Cambia tu voto + vote: Empezar a votar + vote_again: Votar de nuevo + callout: + already_voted: Ya has votado en esta elección. Puedes cambiar tu voto o verificarlo. + pending_vote: Se está emitiendo tu voto en el servidor. + vote_rejected: No ha sido posible verificar tu voto. Por favor, hazlo de nuevo. + election_log: Registro de la elección + preview: Previsualizar + verify: + already_voted: '¿Ya has votado?' + verify_here: Comprueba tu voto aquí. + will_verify: Podrás verificar tu voto una vez que comience la elección. + voting_period_status: + finished: La votación empezó el %{start_time} y terminó el %{end_time} + ongoing: 'Votación activa hasta: %{end_time}' + upcoming: La votación empieza el %{start_time} + feedback: + answer: + invalid: Hubo un problema al enviar tu feedback. + spam_detected: Hubo un problema respondiendo al formulario. Tal vez has sido demasiado rápido, ¿puedes intentarlo de nuevo? + success: Feedback enviado con éxito. + models: + answer: + fields: + proposals: Propuestas + selected: Seleccionada + title: Título + votes: Votos + election: + fields: + bb_status: Estado del tBulletin Board + end_time: Termina el + start_time: Empieza el + title: Título + verifiable_results_file_hash: Suma de comprobación SHA256 del archivo + verifiable_results_file_url: Archivo de verificación de la elección + question: + fields: + answers: Respuestas + max_selections: Número máximo de elementos a seleccionar + title: Título + trustees_participatory_space: + fields: + considered: considerado + email: Correo electrónico + inactive: inactivo + name: Nombre + notification: Notificación enviada el + public_key: Clave publica + status: Estado + orders: + label: Ordenar votaciones por + older: Más antigua + recent: Reciente + trustee_zone: + elections: + backup_modal: + description: Esta elección se está creando en el Bulletin Board. Es muy importante que cada garante que participe en ella cree una copia de seguridad de estas claves y las almacene en un lugar seguro. Después, el proceso continuará. + download_election_keys: Descargar claves + title: Copia de seguridad de claves para la elección %{election} + key_ceremony_steps: + back: Volver + description: Esta elección está siendo creada en el Bulletin Board. Para completar este proceso, se necesita tu participación como garante. + keys: + create_election: Generación de claves + key_ceremony: + joint_election_key: Generación de claves conjuntas + step_1: Publicación de claves + list: + status: Estado + task: Tarea + process_warning: Una vez iniciado el proceso, no debes salir de esta página hasta que el proceso termine. Tardará varios minutos, ya que todos los garantes deben estar conectados para completarlo. + start: Empezar + status: + completed: Completada + pending: Pendiente + processing: Procesando + title: Crear claves para la elección %{election} + restore_modal: + description: El Bulletin Board tiene tu información como garante de esta elección. Para continuar el proceso, primero sube el archivo de copia de seguridad generado durante la sesión anterior. + title: Restaurar claves para la elección %{election} + upload_election_keys: Subir claves de elección + tally_started_steps: + back: Atrás + description: Los resultados de esta elección están siendo calculados en el Bulletin Board. Para completar este proceso, se necesita tu participación como garante. + keys: + end_tally: Recuento finalizado + tally: + cast: Envío del recuento + share: Compartición del recuento + list: + status: Estado + task: Tarea + process_warning: Una vez iniciado el proceso, no debes salir de esta página hasta que el proceso termine. Tardará algunos minutos, ya que todas las garantes deben estar conectados para completarlo. + start: Empezar + status: + completed: Completado + pending: Pendiente + processing: Procesando + title: Recuento para %{election} + update: + error: El estado de la elección no se había actualizado. + success: 'El estado de la elección es: %{status}.' + menu: + trustee_zone: Zona del garante + no_bulletin_board: + body: Se requiere un Bulletin Board configurado para esta sección. Contacta con el Administrador para más detalles. + title: Lo sentimos, el Bulletin Board aún no está configurado. + trustees: + show: + elections: + list: + action_required: + 'false': 'No' + name: '¿Acción requerida?' + 'true': Realizar acción + bb_status: Estado + election: Elección + voting_period: Período de votación + no_elections: Actualmente no se te ha asignado ninguna acción como garante. Recibirás una notificación cuando sea el momento de actuar en las diferentes fases. + title: Elecciones + identification_keys: + cancel: Cancelar + generate: Generar claves de identificación + generate_error: Hubo un error al generar las claves de identificación. + generate_legend: Necesitas generar un par de claves de identificación para participar en las elecciones como garante. + generate_legend_1: Después de pulsar el botón debes descargar el archivo con las claves de identificación generadas. + generate_legend_2: Copia el archivo descargado a un dispositivo USB limpio + generate_legend_3: Asegúrate de que tu equipo no tenga una copia del archivo (por ejemplo, comprueba las carpetas de Descargas y de Escritorio). + generate_legend_4: Haz otra copia del archivo en un dispositivo externo diferente y guárdalo en un lugar muy seguro. + submit: Enviar + submit_legend: Después de seguir todos los pasos explicados anteriormente, completa el proceso enviando la clave de identificación pública al servidor. + submit_title: Enviar la clave pública de identificación + title: Claves de identificación del garante + upload: Sube tus claves de identificación + upload_error: + invalid_format: El archivo subido no contiene ninguna clave de identificación. + invalid_key: Las claves de identificación en el archivo subido no se pueden cargar. + invalid_public_key: Las claves de identificación en el archivo subido no coinciden con la clave pública de identificación almacenada. + upload_legend: En el servidor constan tus claves públicas de identificación, pero tu navegador todavía no las tiene. Necesitas importar el archivo con tus claves de identificación a tu ordenador desde la copia de seguridad que creaste después de generarlas. + not_supported_browser_description: Parece que estás usando un navegador que no puede ser utilizado para actuar como garante. Asegúrate de que estás usando la versión más reciente de tu navegador, o intenta utilizar cualquier otro de los navegadores más populares para poder completar tus tareas como garante. + not_supported_browser_title: Actualiza el navegador para actuar como garante + safari_warning_description: Parece que estás usando Safari, que no está soportado para actuar como garante o para cifrar un voto (esto se debe a las restricciones de memoria que le impone Apple). Esto podría resolverse en el futuro mediante un cambio de política por parte de Apple o la futura optimización de Decidim Elecciones. Mientras tanto, por favor, utiliza otro navegador. + safari_warning_title: Navegador Safari detectado + trustee_role_description: + with_keys: Has sido asignada como garante en algunas de las elecciones celebradas en esta plataforma. + without_keys: Has sido asignada para actuar como Garante. Por favor, genera y sube tus claves de identificación. + update: + success: Tu clave pública de identificación fue guardada con éxito. + votes: + ballot_decision: + audit: ( Auditar papeleta ) + back: Comenzar de nuevo el proceso de votación + ballot_hash: 'El indentificador de la papeleta es:' + cast: Deposita la papeleta para terminar tu voto + description_html: Aquí tienes las opciones de emitir tu boleta para que sea contada correctamente o puedes auditar que tu boleta ha sido cifrada correctamente. Si quieres auditar la votación, por favor, lee las instrucciones sobre cómo proceder. + header: 'La papeleta se ha cifrado: envíala o audítala' + casting: + header: Se está emitiendo el voto... + text: Tu papeleta se está depositando en la urna. + confirm: + answer_number: respuesta %{number} + confirm: Confirmar + edit: editar + header: Confirma tu voto + intro: Aquí tienes un resumen del voto que estás a punto de emitir.
    Por favor, confirma tu voto o edita tus respuestas. + nota_option: En blanco + confirmed: + back: Volver a las votaciones + experience: '¿Cómo valoras la experiencia?' + feedback: Danos tu opinión + header: Voto confirmado + lead: '¡Tu voto se ha emitido!' + text: 'Puedes comprobar que tu voto se ha añadido correctamente a la urna con el siguiente identificador: %{e_vote_poll_id}' + verify_link: Para comprobarlo, copia el identificador y pégalo en la página de verificación de voto + create: + error: Hubo un problema al emitir el voto. Por favor, inténtalo de nuevo. + encrypting: + header: Se está cifrando el voto... + text: Se está cifrando tu papeleta para garantizar el secreto de voto. + failed: + header: Voto fallido + lead: '¡Tu voto no ha sido emitido!' + text: Algo salió mal, por favor inténtalo de nuevo. + try_again: Inténtalo de nuevo + header: + ballot_decision: Emitir o auditar tu voto + confirm: Confirma tu voto + election: Elección + register: Regístrate + vote_for: Vota por %{title} + messages: + invalid_token: Tu sesión en la cabina de votación no es válida. Intenta votar de nuevo. + not_allowed: En este momento no puedes votar en esta votación. + modal: + close: Cerrar + proposal_header: 'Propuestas:' + new: + answer_choices: Puedes seleccionar hasta %{choices} respuestas + more_information: Más información + nota_option: En blanco / Ninguna de las anteriores + preview_alert: Esta es una vista previa de la cabina de votación. + question_steps: Pregunta %{current_step} de %{total_steps} + selections: "Seleccionada
    %{selected} de %{max_selections}" + onboarding_modal: + create_account: Crear Cuenta + description: '¿Quieres crear una nueva cuenta en la plataforma? Podrás participar en los procesos y ser parte activa de la organización.' + no_account: No, gracias. + title: '¿Eres nueva en la plataforma?' + update: + error: Ha habido un problema al actualizar el estado del voto. Vuelve a intentarlo. + verify: + content: + heading: Verifica tu voto + info: Este verificador comprueba que tu voto, identificado con una cadena de texto cifrada, ha sido emitido correctamente y está dentro de la urna. + error: + header: Voto no encontrado! + info: El código de voto no se ha encontrado en la urna de %{link}, inténtelo de nuevo. + form: + back: Volver a la plataforma + submit: Comprobar + vote_identifier: 'Código identificador:' + vote_identifier_help: Este es el identificador que se te dio después de emitir tu voto (no es el código para entrar la cabina de votación). + header: + title: Verifica tu voto + success: + header: '¡Voto localizado!' + info: Tu voto cifrado está en la urna de %{link}. + voting_step: + back: Atrás + continue: Siguiente + warnings: + empty_filters: No hay ninguna elección con este criterio. + no_elections: No hay ninguna elección programada. + no_scheduled_elections: Actualmente no hay elecciones programadas, pero puedes ver un listado de las anteriores. + events: + elections: + election_published: + email_intro: 'La votación %{resource_title} ya está activa en %{participatory_space_title}. Puedes verla desde esta página:' + email_outro: Has recibido esta notificación porque estás siguiendo %{participatory_space_title}. Puedes dejar de recibir notificaciones siguiendo el enlace anterior. + email_subject: La votación %{resource_title} en %{participatory_space_title} ya está activa. + notification_title: La votación %{resource_title} ya está activa en %{participatory_space_title}. + trustees: + new_election: + email_intro: Has sido añadido como garante para la elección de %{resource_title}. + email_outro: Has recibido esta notificación porque has sido añadido como garante para la elección %{resource_title}. + email_subject: Eres garante para la elección %{resource_title}. + notification_title: 'Has sido asignada para actuar como Garante en la Elección: %{resource_title}. Por favor, realiza la ceremonia de claves para poner en marcha la elección.' + new_trustee: + email_intro: Un administrador te ha añadido como garante para %{resource_name}. Debes crear tu clave pública en tu zona de garantes + email_outro: Has recibido esta notificación porque has sido añadido como garante para %{resource_name}. + email_subject: Eres garante de %{resource_name}. + notification_title: Te han añadido para actuar como garante en %{resource_name} para algunas elecciones que tendrán lugar en esta plataforma.
    Se te requerirá para llevar a cabo algunas tareas. De momento, por favor genera tus claves de identificación. + start_tally: + email_intro: El período de votación para la elección %{resource_title} ha finalizado. Ahora, por favor, realiza el recuento de las elecciones para publicar los resultados definitivos. + email_outro: Has recibido esta notificación porque has eres una de las garantes en la elección %{resource_title}. + email_subject: El proceso de recuento para las elecciones %{resource_title} ha comenzado. + notification_title: El período de votación para las elecciones %{resource_title} ha finalizado. Ahora, por favor, realiza el recuento de las elecciones para publicar el resultado final. + votes: + accepted_votes: + email_intro: '¡Tu voto ha sido aceptado! Utilizando tu token de voto: %{encrypted_vote_hash}, puedes verificar tu voto aquí.' + email_outro: Has recibido esta notificación porque has votado en la elección %{resource_name}. + email_subject: Tu voto para %{resource_name} ha sido aceptado. + notification_title: 'Tu voto ha sido aceptado. Verifica tu voto aquí usando tu token de voto: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Se te ha asignado como %{role} del punto de votación %{polling_station_name} en %{resource_title}. Puedes administrar el punto de votación desde el espacio Zona de gestores de mesa. + email_outro: Has recibido esta notificación porque has sido asignado como %{role} de %{polling_station_name}. + email_subject: Eres %{role} del punto de votación %{polling_station_name}. + notification_title: Eres %{role} del punto de votación %{polling_station_name} en la votación %{resource_title}. + send_access_code: + instruction: 'Ya tienes el código de acceso que has pedido: %{access_code}. Con esto ya podrás participar en %{voting}.' + subject: Tu código de acceso para participar en %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Una votación es un espacio que os permite hacer una pregunta clara al conjunto de miembros de una organización, hacer un llamamiento a participar en una votación, generar y ordenar el debate a favor o en contra de una respuesta. cunado llega la fecha de la votación, podéis votar y publicar el resultado.

    Las votaciones pueden ser prácticamente de cualquier aspecto que afecte a la organización. Algunos ejemplos serían: cambiar el nombre o el logotipo de la organización ofreciendo diversas alternativas, decidir si pasar a formar parte de una organización más grande o no, validar o desestimar un nuevo plan estratégico o el resultado de un gruipo de trabajo, o definir si los cargos deberían tener una duración máxima de uno, dos o tres mandatos.

    " + page: "

    Una votación es un espacio que os permite hacer una pregunta clara al conjunto de miembros de una organización, hacer un llamamiento a participar en una votación, generar y ordenar el debate a favor o en contra de una respuesta. cunado llega la fecha de la votación, podéis votar y publicar el resultado.

    Las votaciones pueden ser prácticamente de cualquier aspecto que afecte a la organización. Algunos ejemplos serían: cambiar el nombre o el logotipo de la organización ofreciendo diversas alternativas, decidir si pasar a formar parte de una organización más grande o no, validar o desestimar un nuevo plan estratégico o el resultado de un gruipo de trabajo, o definir si los cargos deberían tener una duración máxima de uno, dos o tres mandatos.

    " + title: '¿Qué son las votaciones?' + menu: + votings: Votaciones + participatory_spaces: + related_elections: + see_all: Ver todas las elecciones + statistics: + elections_count: Elecciones + votings_count: Votaciones + votings: + admin: + ballot_styles: + create: + error: Ha habido un problema al crear el estilo de papeleta. + success: Se ha creado correctamente el estilo de papeleta. + destroy: + invalid: Ha habido un problema al suprimir el estilo de papeleta. + success: Se ha eliminado correctamente el estilo de papeleta. + edit: + title: Editar el estilo de papeleta + update: Actualizar + form: + code_help: 'Pista: el código es el vínculo entre el censo y el estilo de papeleta. Al actualizar los datos del censo, a cada entrada se le asigna un estilo de papeleta que coincida con el código.' + election: Elección + questions: Preguntas para el estilo de papeleta + questions_help: 'Pista: selecciona las preguntas del componente de elecciones para presentarlas a las votantes asignadas a este estilo de papeleta.' + index: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + edit: Editar + new: Añadir estilo de papeleta + title: Acciones + associated_census_data: Entradas asociadas al censo + explanation_callout: Un estilo de papeleta especifica qué preguntas se presentarán a las votantes en la cabina. En un estilo de papeleta, puedes elegir qué preguntas del componente de elecciones pertenecen a esa papeleta. El código de estilo de la papeleta se utiliza para hacer coincidir a una votante del censo con la papeleta que se le debe mostrar en la cabina. No crees ningún estilo de papeleta si quieres que simpre se muesten todas las preguntas. + title: Estilos de papeleta + new: + create: Crear + title: Crear estilo de papeleta + update: + invalid: Ha habido un problema al actualizar el estilo de papeleta. + success: Se ha creado correctamente el estilo de papeleta. + content_blocks: + attachments_and_folders: + name: Archivos y carpetas de la votación + header: + name: Cabecera de la votación + highlighted_votings: + max_results: Cantidad máxima de elementos a mostrar + html_block_1: + name: Bloque html 1 de la votación + html_block_2: + name: Bloque html 2 de la votación + html_block_3: + name: Bloque html 3 de la votación + main_data: + name: Titulo y descripción + metrics: + name: Métricas de la votación + polling_stations: + name: Puntos de votación + related_elections: + name: Votaciones electorales + stats: + name: Estadísticas de la votación + timeline: + name: Calendario de la votación + index: + published: Publicada + unpublished: Despublicada + menu: + votings: Votaciones + votings_submenu: + attachment_collections: Carpetas + attachment_files: Archivos + attachments: Archivos adjuntos + ballot_styles: Estilos de papeleta + census: Censo + components: Componentes + info: Acerca de esta votación + landing_page: Página de inicio + monitoring_committee: Comité de seguimiento + monitoring_committee_election_results: Validar resultados + monitoring_committee_members: Miembros + monitoring_committee_polling_station_closures: Validar certificados + monitoring_committee_verify_elections: Verificar elecciones + polling_officers: Gestores de mesa + polling_stations: Puntos de votación + see_voting: Ver la votación + models: + ballot_style: + fields: + code: Código + monitoring_committee_member: + fields: + email: Correo electrónico + name: Nombre + polling_officer: + fields: + email: Correo electrónico + name: Nombre + polling_station: Punto de votación (rol) + polling_station: + fields: + address: Dirección + polling_station_managers: Administradores + polling_station_president: Presidente + title: Título + voting: + fields: + created_at: Creado el + published: Publicada + title: Título + monitoring_committee_election_results: + actions: + title: Acciones + view: Ver + index: + title: Elige una elección para ver los resultados + results: + bulletin_board: Bulletin Board + election_totals: Totales de la elección + polling_stations: Puntos de votación + result_types: + blank_answers: Respuestas en blanco + blank_ballots: Papeletas en blanco + null_ballots: Papeletas nulas + total_ballots: Total de papeletas + valid_ballots: Papeletas válidas + selected: Seleccionado + title: Resultados para la elección %{election_title} + totals: Totales + show: + change_election: Cambiar elección + publish_results: Publicar resultados + publishing: Publicando resultados... + update: + invalid: Se ha producido un error al publicar los resultados. + rejected: La publicación de los resultados fue rechazada por el Bulletin Board. Inténtalo de nuevo o ponte en contacto con el administrador del sistema. + success: Los resultados se han publicado correctamente. + monitoring_committee_members: + create: + invalid: Hubo un problema al crear esta miembro de la comisión de seguimiento. + success: Miembro del comisión de seguimiento creada con éxito. + destroy: + invalid: Hubo un problema al eliminar esta miembro de la comisión de seguimiento. + success: Miembro de la comisión de seguimiento eliminada con éxito. + form: + existing_user: Participante existente + non_user: Invitar nueva participante + select_user: Buscar por correo electrónico, nombre o alias + user_type: Tipo de participante + index: + title: Comité de seguimiento + new: + create: Crear + title: Crear miembro del comité de seguimiento + monitoring_committee_polling_station_closures: + actions: + title: Acciones + validate: Validar + view: Ver + closures: + change_election: Cambiar elección + signed: '¿Firmado?' + title: Puntos de votación para la elección %{election_title} + validated: '¿Validado?' + edit: + change_polling_station: Volver a los puntos de votación + monitoring_committee_notes: Observaciones + monitoring_committee_notes_placeholder: Reporta cualquier incidencia aquí + title: Resultados para la elección %{election_title} en el punto de votación %{polling_station_title} + elections: + title: Elige una elección para validar + show: + change_polling_station: Volver a los puntos de votación + monitoring_committee_notes: Observaciones del Comité de Seguimiento + validate: + error: Se ha producido un error al validar el cierre. + success: El cierre se ha validado correctamente. + monitoring_committee_verify_elections: + index: + download: Descargar + how_to_checksum: 'Para asegurarse de que el archivo descargado no ha sido dañado o manipulado durante el proceso de descarga, ejecuta el siguiente comando en tu consola y comprueba que la salida coincida con la suma de verificación reportada arriba:' + how_to_download: Para verificar una elección, descarga tu archivo verificable de la tabla de arriba. + how_to_run_verifier: 'Una vez que hayas descargado el archivo y te hayas asegurado de que está bien, puedes proceder a ejecutar el verificador universal. Clona este repositorio y, desde la carpeta raíz, ejecuta el siguiente comando:' + how_to_title: Cómo verificar la validez de una elección + not_available: No disponible todavía + title: Elecciones + polling_officers: + create: + invalid: Hubo un problema al crear esta oficial de votación. + success: Oficial de votación creada con éxito. + destroy: + invalid: Hubo un problema al eliminar esta oficial de votación. + success: Oficial de votación eliminada con éxito. + form: + existing_user: Participante existente + non_user: Invitar nueva participante + select_user: Buscar por correo electrónico, nombre o alias + user_type: Tipo de participante + index: + role_manager: administrador + role_president: presidente + title: Gestores de mesa + new: + create: Crear + title: Crear gestor de mesa + polling_officers_picker: + choose_polling_officers: Elegir gestores de mesa + no_polling_officers: Ninguna gestora de mesa coincide con tus criterios de búsqueda o no hay ninguna. + polling_stations: + create: + invalid: Hubo un problema al crear este punto de votación. + success: Punto de votación creado correctamente. + destroy: + invalid: Hubo un problema al eliminar este punto de votación. + success: Punto de votación eliminado correctamente. + edit: + title: Editar punto de votación + update: Actualizar punto de votación + form: + address_help: 'Dirección: usada por Geocoder para encontrar la ubicación' + location_help: 'Ubicación: mensaje dirigido a los votantes con el lugar exacto del punto de votación' + location_hints_help: 'Sugerencias de ubicación: información adicional. Ejemplo: el piso del edificio donde se encuentra el punto de votación.' + polling_station_managers_help: 'Administradores de mesa: los oficiales que actuarán como administradores de los puntos de votación. Asegúrate de que los oficiales ya han sido creados en Gestores de mesa y que no están ya asignados a otro punto de votación' + polling_station_president_help: 'Presidencia de mesa: oficiales que realizarán las funciones de presidencia en el punto de votación. Asegúrate de que están habilitadas como gestoras de mesa y que ya no están asignadas a otro punto de votación.' + select_president: Selecciona un gestor de mesa como presidente del punto de votación + index: + title: Puntos de votación + new: + create: Crear + title: Crear punto de votación + update: + invalid: Hubo un problema al actualizar este punto de votación. + success: Punto de votación actualizado correctamente. + titles: + votings: Votaciones + votings: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + new_voting: Nuevo espacio de votación + create: + invalid: Hubo un problema al crear esta votación. + success: Votación creada con éxito. + edit: + add_election_component: No tienes ninguna elección configurada para esta votación. Por favor añádela en la sección de componentes. + assign_missing_officers: Hay puntos de votación sin presidencia y/o administradoras. Asígnalas desde la sección de puntos de votación. + update: Actualizar + form: + banner_image: Imagen de cabecera + census_contact_information: Datos de contacto del censo + census_contact_information_help: Esta información de contacto es para una participante que quiere reportar problemas con el censo. Puede ser una dirección de correo electrónico, un formulario de contacto en otro sitio, una encuesta para visitantes, etc. + introductory_image: Imagen de presentación + promoted: Destacada + select_a_voting_type: Por favor, selecciona un tipo de votación + show_check_census_help: Especifica si mostrar el enlace "¿Puedo votar?" en el menú público de votaciones. + slug: Nombre corto de URL + slug_help_html: 'Los textos cortos de URL se utilizan para generar las URL que apuntan a esta votación. Sólo acepta letras, números y guiones, y debe comenzar con una letra. Ejemplo: %{url}' + title: Título + voting_type: + hybrid: Hibrida + in_person: Presencial + online: En línea + voting_type_label: Tipo de votación + new: + create: Crear + title: Nueva votación + update: + invalid: Hubo un problema al actualizar esta votación. + success: Votación actualizada correctamente. + admin_log: + ballot_style: + create: "%{user_name} creó un estilo de papeleta con código %{ballot_style_code} en el espacio %{space_name}" + delete: "%{user_name} eliminó el estilo de la papeleta con el código %{ballot_style_code} en el espacio %{space_name}" + update: "%{user_name} actualizó el estilo de la papeleta con el código %{ballot_style_code} en el espacio %{space_name}" + census: + create: "%{user_name} creó el censo para el espacio %{space_name}" + delete: "%{user_name} eliminó el censo del espacio %{space_name}" + update: "%{user_name} actualizó el censo del espacio %{space_name}" + monitoring_committee_member: + create: "%{user_name} asignó al usuario %{monitoring_committee_member_user} como miembro del comité de seguimiento en el espacio %{space_name}" + delete: "%{user_name} desasignó el usuario %{monitoring_committee_member_user} como miembro del comité de seguimiento en el espacio %{space_name}" + polling_officer: + create: "%{user_name} asignó al usuario %{polling_officer_user} como gestor de mesa en el espacio %{space_name}" + delete: "%{user_name} desasignó al usuario %{polling_officer_user} como gestor de mesa en el espacio %{space_name}" + polling_station: + create: "%{user_name} creó el punto de votación %{resource_name} en el espacio %{space_name}" + delete: "%{user_name} eliminó el punto de votación %{resource_name} en el espacio %{space_name}" + update: "%{user_name} actualizó el punto de votación %{resource_name} en el espacio %{space_name}" + voting: + create: "%{user_name} creó la votación %{resource_name}" + publish: "%{user_name} publicó la votación %{resource_name}" + unpublish: "%{user_name} despublicó la votación %{resource_name}" + census: + admin: + census: + create: + invalid: Se ha producido un error al subir el censo, por favor inténtalo de nuevo más tarde. + invalid_csv_header: Los encabezados CSV faltan o no son correctos - por favor lee las instrucciones cuidadosamente. + creating_data: + info_message: "Por favor espera, procesadas %{processed_count} de %{raw_count} filas del fichero %{file} (esto puede tardar algunos minutos)." + delete: + button: Borrar todos los datos del censo + confirm: Eliminar todo el censo no se puede deshacer. ¿Seguro que quieres continuar? + destroy: + error: Se ha producido un error al eliminar el censo, por favor inténtalo de nuevo más tarde. + success: Datos del censo eliminados. + export_access_codes: + button: Exportar códigos de acceso de la votación + callout: Ahora puedes exportar los códigos de acceso. Esto sólo se puede hacer una vez. Una vez que inicies la exportación, recibirás un correo electrónico con las instrucciones en %{email} + confirm: Solo puedes exportar los códigos de acceso una vez. Asegúrate de tener acceso a la cuenta de correo electrónico %{email}. + file_not_exist: Este archivo no existe. + launch_error: Problema al iniciar la exportación de códigos de acceso. + launch_success: Se ha iniciado la exportación de los códigos de acceso. En breve recibirás un correo en %{email}. + exporting_access_codes: + info_message: "Por favor, espera, se está preparando la exportación, la recibirás en breve en %{email} (esto puede tardar unos minutos)." + freeze: + callout: El censo está cerrado y no se puede modificar. + help_html: | + Los datos del censo Se han subido correctamente, los códigos se han generado y exportado con éxito.
    Ahora estás lista para comenzar la elección.
    + Utiliza el CSV exportado, que incluye los códigos individuales para distribuirlo al censo utilizando tus propios medios, o active la pestaña "¿Puedo votar?", para permitir que cualquiera recupere este código usando sus propios datos de censales. + generate_access_codes: + button: Generar códigos de acceso de la votación + callout: Ahora puedes generar los códigos de acceso. Ten en cuenta que después de generarlos ya no podrás modificar más el censo. + confirm: Si continúas, ya no podrás modificar el censo. + info_message_all: "Se han importado las filas correctamente del fichero %{file} (%{raw_count} de %{data_count})." + info_message_warn: Por favor, comprueba que no falten datos, ya que se han creado %{data_count} registros, pero el fichero que se ha subido (%{file}) tenía %{raw_count} filas. + launch_error: Ha habido un problema al generar los códigos de acceso + launch_success: Se ha iniciado la generación de códigos. + start_over: Por favor, elimine el censo actual y vuelva a empezar con un archivo CSV apropiado con filas válidas. + generating_access_codes: + info_message: "Espera, por favor<, los códigos de acceso a la votación se están generando (esto puede tardar unos minutos)..." + new: + file_help: + explanation: 'Instrucciones para el archivo:' + message_1: Sólo se permiten archivos CSV (.csv). + message_2: El separador entre columnas debe ser un punto y coma (";"). + has_ballot_styles_message: Has configurado los estilos de papeleta. Por favor, asegúrate de que el campo "%{ballot_style_code_header}" en el CSV corresponde al código de estilo de papeleta deseado. + info_message: "Aún no hay ningún censo. Por favor, utiliza el siguiente formulario para crearlo importando un archivo CSV." + missing_ballot_styles_message: 'No hay ningún estilo de papeleta para esta votación todavía. Si quieres tener preguntas condicionales (presentar al votante diferentes preguntas dependiendo de, p.ej. el distrito/región de residencia), debes configurar los estilos de papeleta antes de importar el censo. Si quieres presentar a todos los votantes las mismas preguntas, puedes continuar con el procedimiento de importación del censo.' + submit: Enviar CSV + title: Crear el censo + show: + heading: Censo del espacio de votación + upload_info: + csv_example_with_ballot_style: 'Un ejemplo del archivo con estilos de papeleta:' + csv_example_without_ballot_style: 'Un ejemplo del archivo sin estilos de papeleta:' + csv_header_after: No incluyas el último campo ("%{ballot_style_code_header}") si no necesitas preguntas condicionales/estilos de papeleta + csv_header_before: 'El censo debe ser un archivo CSV con la siguiente cabecera:' + document_types: + identification_number: Número de identificación + passport: Pasaporte + export_mailer: + access_codes_export: + click_button: 'Haz clic en el siguiente enlace para descargar tus datos.
    El archivo estará disponible hasta %{date}.
    Necesitarás 7-Zip (para Windows), Keka (para MacOS) o PeaZip (para Linux) para abrirlo. Contraseña: %{password}' + download: Descargar + subject: La exportación de los códigos de acceso de votación para %{voting_title} está disponible + vote_flow: + already_voted_in_person: Esta participante ya ha votado presencialmente y no tiene derecho a voto. + datum_not_found: Los datos introducidos no coinciden con ninguna votante. + content_blocks: + highlighted_votings: + name: Votaciones destacadas + landing_page: + polling_stations: + heading: Puntos de votación + no_polling_stations: Aún no hay puntos de votación. + monitoring_committee_members: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + new: Nuevo miembro + title: Acciones + pages: + home: + highlighted_votings: + active_spaces: Votaciones activas + see_all_spaces: Ver todas las votaciones + polling_officer_zone: + closures: + back_to_polling_stations: Volver a los puntos de votación + certify: + add_photos: Añadir fotos + edit_photos: Editar fotos + error: Se ha producido un error al adjuntar el certificado, por favor inténtalo de nuevo. + heading: Recuento de votos - Subir certificado + info_text: Por favor, sube una foto del certificado de cierre electoral. + submit: Subir el certificado + success: Certificado subido correctamente. + upload_photos: Sube una foto del Certificado de Cierre Electoral + completed: + sub_heading: Este recuento ha sido certificado y ya no puede ser editado. + create: + error: Se ha producido un error al crear el cierre, por favor inténtalo de nuevo más tarde. + success: Cierre creado correctamente. + destroy: + error: Ocurrió un error eliminando el cierre. + success: Cierre eliminado correctamente. + edit: + confirm_start_over: Esto eliminará el número total de votos y las notas adjuntas. ¿Seguro que lo quieres eliminar? + heading: Recuento de votos - recuento de respuestas + info_text: Por favor detalla el número total de respuestas para cada pregunta. Este debe coincidir con el número total de respuestas introducidas en el paso anterior (%{total} respuestas en total). + modal_ballots_results_count_error: + blank: El total esperado de votos en blanco es %{expected}, pero la suma de las preguntas en blanco es %{current}. + close_modal: Cerrar + info_text: El número total de papeletas no coincide con el número total de sobres. Por favor, revisa el total de papeletas. + title: El total de papeletas no coincide + total: El total esperado es %{expected}, pero la suma de las papeletas válidas, en blanco y nulas es %{current}. + valid: El total esperado de votos válidos es %{expected}, pero la suma de las preguntas válidas es %{current}. + save_recount: Guardar recuento + start_over: Si hay un error en el número total, puedes borrar todo y empezar de nuevo. + total_ballots: Total de papeletas + total_blank_ballots: Total de papeletas en blanco + total_null_ballots: Total de papeletas nulas + total_valid_ballots: Total de papeletas válidas + new: + election: 'Elección:' + heading: Recuento de votos + info_text: 'Por favor, introduce el número total de papeletas (sobres) recontadas en este punto de votación:' + modal_ballots_count_error: + btn_validate_total: Validar recuento total de papeletas + info_explanation_text: 'Por favor, revisa el número total de papeletas. Si el número total es incorrecto, debes proporcionar una explicación para el Comité de Seguimiento:' + info_text: El número total de papeletas (sobres) introducidas no coincide con el registro de personas que han votado en este punto de votación. + message_for_monitoring_committee: Mensaje para el Comité de Seguimiento + review_recount: Revisar el recuento + text_area_placeholder: Por favor, escribe tu mensaje + title: El total de registros no coincide + total_ballots: 'Total de papeletas:' + total_people: 'Total de personas:' + polling_station: 'Punto de votación:' + submit: Verificar número total + total_ballots_count: Número de papeletas + show: + edit_count_votes: '¿Número incorrecto? Aún puedes editarlos.' + heading: Recuento de votos + sub_heading: El recuento debe cerrarse mediante la carga de un certificado. Una vez hecho, el recuento se sellará y ya no se podrá editar más. + sign: + cancel: Cancelar + check_box: Lo he revisado y es idéntico al certificado físico de cierre electoral + confirm: Ok, continuar + error: Se ha producido un error, por favor inténtalo de nuevo. + heading: Recuento de votos - Firmar cierre + info_text: Si continúas ya no podrás modificar ninguna información, esta acción no se puede deshacer. + submit: Firmar el cierre + success: Cierre firmado con éxito. + update: + error: Se ha producido un error al actualizar los resultados del cierre. Inténtalo de nuevo más tarde. + success: Resultados de cierre actualizados correctamente. + in_person_votes: + complete_voting: + available_answers: 'Respuestas disponibles:' + census_verified: Esta participante todavía no ha votado en persona. + census_verified_with_online_vote: Esta participante ya votó en línea previamente. Si vota en persona, sus votos anteriores serán invalidados y este será su voto definitivo. + complete_voting: Completar votación + identify_another: Identificar a otra participante + questions_title: 'Tienen derecho a votar en las siguientes preguntas:' + questions_title_voted: 'Esta participante ya votó en línea y tiene derecho a votar en las siguientes preguntas:' + voted: La participante ha votado + create: + error: El voto no ha sido registrado. Por favor, inténtalo de nuevo. + in_person_form: + census_not_present: Esta participante no aparece en el censo. + census_not_present_description: Debe acudir a la oficina de reclamaciones del censo o al servicio de asistencia técnica. + date_of_birth: Fecha de nacimiento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + month: Mes + month_placeholder: MM + select: Selecciona el tipo de documento + title: 'Selecciona el tipo de documento e introduce el número de documento de la participante:' + validate_document: Validar documento + year: Año + year_placeholder: AAAA + new: + back: Volver a los puntos de votación + title: Identificar y verificar a una participante + show: + back: Volver a los puntos de votación + title: Esperando a que se registre el voto presencial + update: + error: Se ha producido un error al registrar el voto. Por favor, inténtalo de nuevo. + success: + accepted: El voto se ha registrado correctamente. + rejected: El Bulletin Board no ha aceptado el voto. Por favor, ponte en contacto con el administrador del sistema. + verify_document: + census_present: Esta participante aparece en el censo. + name: Nombre + title: 'Comprueba que los siguientes datos son correctos:' + verify_document: Verificar documento + menu: + polling_officer_zone: Zona del gestor de mesa + polling_officers: + index: + polling_officer_role_description: Has sido asignado para actuar como gestor de mesa (Presidente o Administrador) en algunas de las elecciones celebradas en esta plataforma. + polling_station: + address: Dirección + count_votes: Contar votos + election: Elección + identify_person: Identificar a una persona + name: Nombre + no_polling_stations: Todavía no estás asignado a ningún punto de votación. + role: Tu rol + show_closure: Ver cierre + title: Puntos de votación + voting: Votación + polling_officers: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + new: Añadir gestora de mesa + title: Acciones + roles: + manager: Administrador + president: Presidente + unassigned: No Asignado + polling_station_closure_certificate: + current_certificate: 'Certificado actual:' + polling_station_closure_recount: + nota_option: En blanco / Ninguna de las anteriores + polling_officer_notes: 'Notas del gestor de mesa:' + polling_officer_notes_blank: No hay notas + recount_summary: 'Resumen del recuento:' + signed: Firmado + total_ballots: 'Total de papeletas:' + total_blank_ballots: 'Total de papeletas en blanco:' + total_null_ballots: 'Total de papeletas nulas:' + total_valid_ballots: 'Total de papeletas válidas:' + polling_stations: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + edit: Editar + new: Añadir punto de votación + title: Acciones + votings: + access_code_modal: + email: Enviar por correo electrónico a %{email} + info: Necesitas un código de acceso para participar. Si no recibiste uno por correo postal, podemos enviarte uno nuevo. + no_email: Correo no disponible + no_sms: Número de teléfono no disponible + sms: Enviar por SMS a %{sms} + title: Obtener código de acceso + check_census: + check_status: Comprobar estado + description: Aquí, tienes la opción de comprobar tus datos del censo para saber si tienes derecho a participar en esta votación. Deberías tener ya un código de acceso, pero si lo has perdido, puedes solicitarlo de nuevo, si tus datos son correctos. + error: + info: 'Por favor, inténtalo de nuevo. Si crees que los datos del sistema son incorrectos, puedes reportarlo aquí: %{census_contact_information}.' + title: Los datos que ha introducido no están en el censo de esta votación + form_title: 'Rellena el siguiente formulario para comprobar tus datos del censo:' + invalid: Hubo un problema al comprobar el censo. + success: + access_link: por correo electrónico. + access_link_with_sms: vía SMS o correo electrónico. + info: Deberías haber recibido tu código de acceso por correo postal ya. En caso de que no lo tengas, puedes solicitarlo aquí + title: '¡Tus datos del censo son correctos!' + title: '¿Puedo votar?' + check_fields: + date_of_birth: Fecha de nacimiento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + document_type: Tipo de documento + month: Mes + month_placeholder: MM + postal_code: Código postal + postal_code_placeholder: Número de código postal + select: Selecciona el tipo de documento + year: Año + year_placeholder: AAAA + count: + title: + one: "%{count} votación" + other: "%{count} votaciones" + elections_log: + description: El registro de la elección te mostrará toda la información relevante sobre cada votación. Por ejemplo, el estado de la ceremonia de claves o el recuento o si los resultados ya están publicados. Haz clic en la elección sobre la cual quieres la información del registro. + title: Registro de la elección + filters: + active: Activas + all: Todas + date: Fecha + finished: Finalizadas + search: Buscar + upcoming: Próximas + index: + no_votings: Ninguna votación coincide con tus criterios de búsqueda. + only_finished: Actualmente no hay votaciones programadas, pero aquí puedes encontrar las votaciones terminadas en la lista. + title: Votaciones + login: + access_code: Código de acceso + access_code_placeholder: Código de acceso + ask_for_a_new_one: Solicitar uno nuevo. + dont_have_access_code: '¿No tienes un código de acceso?' + form_title: 'Rellena el siguiente formulario para acceder a la votación:' + start_voting: Empezar a votar + title: Identificarme con mis datos del censo + no_census_contact_information: Aún no hay ninguna información de contacto. + orders: + label: 'Ordenar votaciones por:' + random: Aleatorio + recent: Más recientes + send_access_code: + invalid: Hubo un problema al enviar el código de acceso. + success: Tu código de acceso se ha enviado correctamente. + show: + title: Acerca de esta votación + votings_m: + badge_name: + finished: Finalizada + ongoing: En curso + upcoming: Próximamente + unspecified: No especificado + voting_type: + hybrid: Hibrida + in_person: Presencial + online: En línea + layouts: + decidim: + voting_navigation: + check_census: '¿Puedo votar?' + election_log: Registro de la elección + votings: + index: + promoted_votings: Votaciones destacadas + promoted_voting: + vote: Votar diff --git a/decidim-elections/config/locales/es-PY.yml b/decidim-elections/config/locales/es-PY.yml new file mode 100644 index 00000000..5807f42e --- /dev/null +++ b/decidim-elections/config/locales/es-PY.yml @@ -0,0 +1,1425 @@ +es-PY: + activemodel: + attributes: + answer: + description: Descripción + image: Imagen + proposals: Propuestas relacionadas + title: Título + ballot_style: + code: Código + election: + description: Descripción + end_time: La votación acaba el + start_time: La votación empieza el + title: Título + monitoring_committee_member: + email: Correo electrónico + name: Nombre + polling_officer: + email: Correo electrónico + name: Nombre + polling_station: + address: Dirección + location: Ubicación + location_hints: Detalles de ubicación + polling_station_managers: Gestoras + polling_station_president_id: Presidencia + title: Título + question: + max_selections: Número máximo de opciones + min_selections: Ninguna de las anteriores + title: Título + trustees_participatory_space: + user_id: Participante + voting: + banner_image: Imagen de cabecera + census_contact_information: Datos de contacto del censo + description: Descripción + end_time: La votación finaliza + introductory_image: Imagen de presentación + promoted: Destacada + scope_id: Ámbito + show_check_census: Mostrar la página de "comprobar censo" + start_time: La votación empieza + title: Título + voting_type: Tipo de votación + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Es necesario volver a adjuntar el archivo + ballot_result: + attributes: + base: + total_count_invalid: El número total de respuestas no coincide con el desglose válido/blanco/nulo. + election: + attributes: + attachment: + needs_to_be_reattached: Es necesario volver a adjuntar el archivo + question_result: + attributes: + base: + blank_count_invalid: El número total de respuestas en blanco no puede ser mayor que el total de papeletas en blanco. + trustee: + attributes: + name: + cant_be_changed: no se puede cambiar + public_key: + cant_be_changed: no se puede cambiar + voting: + attributes: + voting_type: + inclusion: "%{value} no es un tipo de votación válido" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: El gestor electoral ya es presidente/gestor del punto de votación. + voting: + different_organization: La votación debe estar en la misma organización que la participante. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: La oficial de votación debe estar en la misma votación que el punto de votación. + models: + decidim/elections/answer: + one: Respuesta + other: Respuestas + decidim/elections/election: + one: Votación + other: Votaciones + decidim/elections/question: + one: Pregunta + other: Preguntas + decidim/voting: + one: Votación + other: Votaciones + decidim/votings/census/dataset: + one: Conjunto de datos + other: Conjuntos de datos + decidim/votings/census/datum: + one: Dato + other: Datos + decidim/votings/polling_officer: + one: Gestor de mesa + other: Gestores de mesa + decidim/votings/polling_station: + one: Punto de votación + other: Puntos de votación + decidim/votings/voting: + one: Votación + other: Votaciones + decidim: + admin: + filters: + officers_assigned_eq: + label: Responsables + values: + assigned: Asignado + unassigned: No asignado + role_eq: + label: Rol + values: + manager: Gestor + president: Presidencia + unassigned: Sin asignar + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Buscar %{collection} por nombre/email/alias o punto de votación. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Buscar %{collection} por título, dirección o nombre del responsable/email/alias. + signed_eq: + label: Firmada + values: + 'false': Firmada + 'true': Sin firmar + validated_eq: + label: Validada + values: + 'false': Sin validar + 'true': Validada + voting_publications: + create: + error: Se ha producido un error al publicar esta votación. + success: Votación publicada correctamente. + destroy: + error: Ha habido un problema al despublicar esta votación. + success: Votación despublicada correctamente. + components: + elections: + actions: + vote: Votar + name: Votaciones + settings: + global: + announcement: Aviso + step: + announcement: Aviso + elections: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Destruir + edit: Editar + feedback: Retorno de la votante + import: Importar de propuestas a respuestas + manage_answers: Gestionar las respuestas + manage_questions: Gestionar las preguntas + manage_steps: Gestionar las fases + new_answer: Añadir respuesta + new_election: Añadir elección + new_question: Añadir pregunta + new_trustee: Añadir garante + preview: Previsualizar + publish: Publicar + title: Acciones + unpublish: Despublicar + admin: + answers: + create: + invalid: Se ha producido un error al crear esta respuesta. + success: Respuesta creada con éxito. + destroy: + invalid: Se ha producido un error al eliminar esta respuesta. + success: Respuesta eliminada con éxito. + edit: + title: Editar las respuestas + update: Actualizar respuesta + index: + invalid_max_selections: Necesitas %{missing_answers} respuesta/s más para coincidir con la selección máxima. + title: Respuestas + new: + create: Crear respuesta + title: Nueva respuesta + not_selected: No seleccionada + select: + disable: Deseleccionar respuesta + enable: Marcar respuesta como seleccionada + invalid: Hubo un problema al seleccionar esta respuesta. + success: Respuesta seleccionada con éxito. + selected: Seleccionada + unselect: + invalid: Hubo un problema al deseleccionar esta respuesta. + success: Respuesta deseleccionada correctamente. + update: + invalid: Se ha producido un error al actualizar esta respuesta. + success: Respuesta actualizada correctamente. + elections: + create: + invalid: Se ha producido un error al crear esta elección. + success: La elección se ha creado correctamente. + destroy: + invalid: Se ha producido un error al eliminar la elección. + success: La elección se ha eliminado correctamente. + edit: + title: Editar la votación + update: Actualizar la votación + form: + organization_time_zone: Compruebe que la zona horaria es correcta en la configuración de la organización. La configuración actual es %{time_zone} (%{time}). + index: + no_bulletin_board: No hay ningún servidor de Bulletin Board configurado, el cual es necesario para utilizar este módulo. Esta tarea debe ser realizada por la persona administradora del sistema. + title: Votaciones + new: + create: Crear una votación + title: Nueva votación + publish: + success: La votación se ha publicado correctamente. + unpublish: + success: La votación se ha despublicado correctamente. + update: + invalid: Se ha producido un error al actualizar esta votación. + success: La votación se ha actualizado correctamente. + exports: + elections: Elecciones + feedback_form_answers: Retorno de las respuestas + mailers: + trustee_mailer: + body: + help_html: |- +

    Hola %{user_name},


    +

    Te han añadido para actuar como garante en algunes elecciones que tendran lugar en %{organization}.


    +

    Se ha creado en tu cuenta una nueva sessión llamada "Zona de garantes". Desde esta, podrá realitzar las tareas requeridas. De momento, por favor, genera tus claves de identificación.


    + subject: Se te ha añadido como garante de %{resource_name} + trustee_zone: Llévame al espacio de garantes + menu: + trustees: Garantes + models: + answer: + name: Respuesta + proposals_imports: + create: + invalid: Se ha producido un error al importar las propuestas como respuestas. + success: "%{number} propuestas importadas a respuestas correctamente." + new: + create: Importar de propuestas a respuestas + no_components: No hay otros componentes de la propuesta en este espacio participativo desde el que importar las propuestas en los proyectos. + select_component: Por favor selecciona un componente + title: Importar propuestas + questions: + create: + election_started: La elección ya ha comenzado. + invalid: Se ha producido un error al crear esta pregunta. + success: Se ha creado la pregunta correctamente. + destroy: + invalid: Se ha producido un error al eliminar esta pregunta. + success: Se ha eliminado la pregunta correctamente. + edit: + title: Editar la pregunta + update: Actualizar la pregunta + index: + title: Preguntas + new: + create: Crear una pregunta + title: Nueva pregunta + update: + invalid: Se ha producido un error al actualizar esta pregunta. + success: La pregunta se ha actualizado correctamente. + steps: + create_election: + census: Censo + errors: + census_codes_generated: Los códigos de acceso al censo no se han podido generar. + census_frozen: Los códigos de acceso al censo no se han podido exportar. + census_uploaded: No se ha subido ningún censo para esta elección. + component_published: La elección no está publicada. + fix_it_text: Arreglarlo + max_selections: Las preguntas no tienen un valor correcto para la cantidad de respuestas + minimum_answers: Las preguntas deben tener al menos dos respuestas. + minimum_questions: La elección debe tener al menos una pregunta. + published: La elección no está publicada. + time_before: Se ha configurado la hora de inicio en menos de %{hours} horas antes de que comience la elección. + trustees_number: El espacio participativo debe tener al menos %{number} garantes con clave pública. + invalid: Hubo un problema al configurar esta elección + no_trustees: No hay garantes configurados para este espacio participativo + not_used_trustee: "(no se usa)" + public_key: + 'false': no tiene una clave pública + 'true': tiene una clave pública + requirements: + census_codes_generated: Se han generado los códigos de acceso al censo. + census_frozen: Se han exportado los códgos para el censo y el censo ha quedado congelado. + census_uploaded: El censo está subido. + component_published: La elección está publicada. + max_selections: Todas las preguntas tienen un valor correcto para máximo de respuestas. + minimum_answers: Cada pregunta tiene al menos 2 respuestas. + minimum_questions: La elección tiene al menos 1 pregunta. + published: La elección está publicada. + time_before: La configuración se está realizando al menos %{hours} horas antes de que comience la elección. + trustees_number: El espacio participativo tiene al menos %{number} garantes con clave pública. + submit: Configurar elección + success: La elección se ha enviado con éxito al "Bulletin Board". + technical_configuration: + authority_name: "Nombre de la autoridad: %{value}" + bulletin_board_server: "Servidor del boletín: %{value}" + scheme_name: "Nombre del esquema: %{value}" + title: Ver la información técnica + title: Configurar elección + trustees: Garantes de la elección + created: + invalid: Hubo un problema al iniciar la ceremonia de claves. + submit: Iniciar la ceremonia de claves + success: La solicitud para iniciar la ceremonia de claves fue enviada con éxito al "Bulletin Board". + title: Elección creada + trustees: Garantes + key_ceremony: + continue: Continuar + title: Ceremonia de claves + key_ceremony_ended: + errors: + time_before: La elección está lista para comenzar. Tienes que esperar hasta %{hours} horas antes de la hora de inicio (%{start_time}) para comenzar el periodo de votación. + invalid: Hubo un problema al iniciar el período de votación. + requirements: + time_before: La elección comenzará pronto. Puede iniciar el periodo de votación manualmente, o se iniciará automáticamente antes de la hora de inicio, a las %{start_time}. + submit: Empezar período de votación + success: La solicitud de período de votación se ha enviado correctamente al "Bulletin Board". + title: Listo para empezar + processing: Procesando... + results_published: + answer: Respuesta + not_selected: No seleccionada + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Enviar + title: Resultados publicados + tally_ended: + answer: Respuesta + not_selected: No seleccionado + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Publicar resultados + success: La solicitud de publicación de resultados se envió correctamente al "Bulletin Board". + title: Resultados calculados + tally_started: + continue: Continuar + invalid: Hubo un problema al reportar la ausencia de la garante. + mark_as_missing: Marcar como ausente + mark_as_missing_description: Todos los garantes deberían participar en este proceso, pero si uno de ellos no puede tomar parte en él, puedes marcarlo como ausente. + success: La notificación de ausencia de la garante se ha enviado con éxito al "Bulletin Board". + tally_completion: El proceso se completará cuando todos los garantes estén activos o marcados como ausentes. Se requiere al menos %{quorum} garantes para completar el proceso. + title: Proceso de recuento + undo_mark_as_missing: Un garante marcado como ausente por error podrá participar antes de completar el proceso. Pueden proceder como de costumbre y la marca de ausente será ignorada. + vote: + errors: + time_after: La elección aún está en curso. Tienes que esperar hasta la hora de finalización (%{end_time}) para terminar el periodo de votación. + invalid: Hubo un problema al terminar el período de votación. + requirements: + time_after: La elección ha terminado. Puedes terminar el período de votación manualmente, o terminará automáticamente en unos minutos. + submit: Finalizar período de votación + success: La solicitud para finalizar el período de votación se ha enviado correctamente al "Bulletin Board". + title: Periodo de votación + vote_ended: + invalid: Hubo un problema al iniciar el recuento. + submit: Comenzar recuento + success: La solicitud de inicio del recuento se envió correctamente al "Bulletin Board". + text: La votación ha terminado. Puedes iniciar el recuento ahora. + title: Periodo de votación finalizado + vote_stats: + no_vote_statistics_yet: Aún no hay estadísticas de voto + title: Estadísticas de voto + voters: Votantes + votes: Votos + trustees_participatory_spaces: + actions: + disable: Deshabilitar + enable: Considerar + create: + exists: Existe garante para este espacio participativo. + invalid: Se ha producido un error al crear una garante. + success: Garante creada correctamente. + delete: + invalid: Se ha producido un error al eliminar esta garanta. + success: Garante eliminada correctamente. + form: + select_user: Seleccionar usuario + index: + title: Garantes + new: + create: Crear garante + title: Nuevo garante + update: + invalid: Se ha producido un error al actualizar a %{trustee} como garante. + success: Garante %{trustee} actualizada correctamente. + admin_log: + election: + create: "%{user_name} creó la elección %{resource_name} de %{space_name}" + delete: "%{user_name} eliminó la elección %{resource_name} de %{space_name}" + end_vote: "%{user_name} terminó el período de votación para la elección %{resource_name} de %{space_name} en el Bulletin Board" + publish: "%{user_name} publicó la elección %{resource_name} de %{space_name}" + publish_results: "%{user_name} publicó los resultados para la elección %{resource_name} de %{space_name} en el Bulletin Board" + report_missing_trustee: "%{user_name} reportó a %{trustee_name} como garante ausente durante el recuento de la elección %{resource_name} de %{space_name} en el Bulletin Board" + setup: "%{user_name} creó la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_key_ceremony: "%{user_name} comenzó la ceremonia claves para la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_tally: "%{user_name} comenzó el recuento de la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_vote: "%{user_name} comenzó el período de votación para la elección %{resource_name} de %{space_name} en el Bulletin Board" + unpublish: "%{user_name} ha despublicado el %{resource_name} de la elección de %{space_name}" + update: "%{user_name} actualizó la elección %{resource_name} de %{space_name}" + trustee: + create: "%{user_name} asignó al usuario %{trustee_user} como garante" + connection: + failed: + modal: + close: Cerrar + communication_lost: Desafortunadamente, parece que la comunicación con el servidor de votación (Bulletin Board) se ha perdido.
    Puede ser que la conexión a Internet esté averiada o que el servidor de destino esté demasiado ocupado.
    Puedes intentarlo más tarde o ponerte en contacto con el servicio de asistencia si este problema persiste. + generic_error: Desafortunadamente, se ha producido un error desconocido. Es probable que tu navegador no esté soportado o que estés usando el modo "incógnito" o "privado" que no es compatible. + title: Algo salió mal + election_m: + badge_name: + finished: Finalizada + ongoing: Activa + upcoming: Próximas + end_date: Termina + footer: + remaining_time: + one: "%{count} hora %{minutes} minutos restantes para votar." + other: "%{count} horas %{minutes} minutos restantes para votar." + view: Ver + vote: Votar + label: + date: Fechas + questions: Preguntas %{count} + start_date: Empieza + unspecified: Sin especificar + elections: + count: + elections_count: + one: "%{count} votación" + other: "%{count} votaciones" + election_log: + chained_hash: El Hash encadenado de este mensaje + complete: Completar + creation_description: + complete: La elección se creó y se configuró con éxito en el Bulletin Board. + not_created: La elección no se ha creado todavía. + creation_title: Elección creada + description: Este es el registro de la elección, donde puedes comprobar el estado de cada paso, por ejemplo, cuándo se ha creado, si se ha completado el proceso de recuento y cuándo se ha cerrado la elección. + download: Descargar + key_ceremony_description: + complete: La ceremonia de claves se ha completado. Cada garante tiene claves válidas y ha descargado las claves de copia de seguridad necesarias. + not_started: La ceremonia de claves aún no ha comenzado. + started: La ceremonia de claves ha comenzado, pero aún no ha terminado. + key_ceremony_title: Ceremonia de claves + not_available: No disponible todavía + not_created: No creado + not_ready: No está listo + not_started: No iniciado + published: Publicado + results_description: + not_published: Los resultados aún no se han publicado. + published: Los resultados se han publicado. + results_title: Resultados + started: Iniciado + tally_description: + finished: El proceso de recuento ha terminado. + not_started: El proceso de recuento aún no ha comenzado. + started: El proceso de recuento ha comenzado. + tally_title: Proceso de recuento + title: Registro de la elección + unpublished: Despublicada + verifiable_results: + checksum: 'Suma de comprobación SHA256 del archivo:' + description: + not_ready: El archivo verificable de la elección y la suma de comprobación SHA256 aún no están disponibles. En cuanto se publiquen los resultados, podrás verificar esta elección. + ready: 'Aquí tienes la opción de verificar la elección. En primer lugar, tienes que descargar el archivo y asegurarte de que no se ha corrompido. Para ello, ejecuta el siguiente comando y comprueba que la salida coincide con la suma de comprobación:' + how_to_verify: 'Una vez que hayas descargado el archivo y te hayas asegurado de que está bien, puedes proceder a ejecutar el verificador universal. Clona este repositorio y, desde la carpeta raíz, ejecuta el siguiente comando:' + title: Verificar resultados de la elección + verifiable_file: 'Archivo verificacle de la elección:' + verify: Verificar elección + vote_description: + finished: El proceso de votación ha concluido. + not_started: El proceso de votación aún no ha comenzado. + started: El proceso de votación ha comenzado. + vote_title: Proceso de votación + filters: + active: Activas + all: Todas + date: Fecha + finished: Finalizadas + upcoming: Próximas + preview: + available_answers: 'Respuestas disponibles:' + description: 'Estas son las preguntas que encontrarás en el proceso de votación:' + title: Preguntas de la elección + results: + description: 'Estos son los resultados de la votación, para cada pregunta:' + percentage: "%{count}%" + selected: Seleccionado + title: Resultados de la elección + votes: + one: "%{count} voto" + other: "%{count} votos" + show: + action_button: + change_vote: Cambia tu voto + vote: Empezar a votar + vote_again: Votar de nuevo + callout: + already_voted: Ya has votado en esta elección. Puedes cambiar tu voto o verificarlo. + pending_vote: Se está emitiendo tu voto en el servidor. + vote_rejected: No ha sido posible verificar tu voto. Por favor, hazlo de nuevo. + election_log: Registro de la elección + preview: Previsualizar + verify: + already_voted: '¿Ya has votado?' + verify_here: Comprueba tu voto aquí. + will_verify: Podrás verificar tu voto una vez que comience la elección. + voting_period_status: + finished: La votación empezó el %{start_time} y terminó el %{end_time} + ongoing: 'Votación activa hasta: %{end_time}' + upcoming: La votación empieza el %{start_time} + feedback: + answer: + invalid: Hubo un problema al enviar tu feedback. + spam_detected: Hubo un problema respondiendo al formulario. Tal vez has sido demasiado rápido, ¿puedes intentarlo de nuevo? + success: Feedback enviado con éxito. + models: + answer: + fields: + proposals: Propuestas + selected: Seleccionada + title: Título + votes: Votos + election: + fields: + bb_status: Estado del tBulletin Board + end_time: Termina el + start_time: Empieza el + title: Título + verifiable_results_file_hash: Suma de comprobación SHA256 del archivo + verifiable_results_file_url: Archivo de verificación de la elección + question: + fields: + answers: Respuestas + max_selections: Número máximo de elementos a seleccionar + title: Título + trustees_participatory_space: + fields: + considered: considerado + email: Correo electrónico + inactive: inactivo + name: Nombre + notification: Notificación enviada el + public_key: Clave publica + status: Estado + orders: + label: Ordenar votaciones por + older: Más antigua + recent: Reciente + trustee_zone: + elections: + backup_modal: + description: Esta elección se está creando en el Bulletin Board. Es muy importante que cada garante que participe en ella cree una copia de seguridad de estas claves y las almacene en un lugar seguro. Después, el proceso continuará. + download_election_keys: Descargar claves + title: Copia de seguridad de claves para la elección %{election} + key_ceremony_steps: + back: Volver + description: Esta elección está siendo creada en el Bulletin Board. Para completar este proceso, se necesita tu participación como garante. + keys: + create_election: Generación de claves + key_ceremony: + joint_election_key: Generación de claves conjuntas + step_1: Publicación de claves + list: + status: Estado + task: Tarea + process_warning: Una vez iniciado el proceso, no debes salir de esta página hasta que el proceso termine. Tardará varios minutos, ya que todos los garantes deben estar conectados para completarlo. + start: Empezar + status: + completed: Completada + pending: Pendiente + processing: Procesando + title: Crear claves para la elección %{election} + restore_modal: + description: El Bulletin Board tiene tu información como garante de esta elección. Para continuar el proceso, primero sube el archivo de copia de seguridad generado durante la sesión anterior. + title: Restaurar claves para la elección %{election} + upload_election_keys: Subir claves de elección + tally_started_steps: + back: Atrás + description: Los resultados de esta elección están siendo calculados en el Bulletin Board. Para completar este proceso, se necesita tu participación como garante. + keys: + end_tally: Recuento finalizado + tally: + cast: Envío del recuento + share: Compartición del recuento + list: + status: Estado + task: Tarea + process_warning: Una vez iniciado el proceso, no debes salir de esta página hasta que el proceso termine. Tardará algunos minutos, ya que todas las garantes deben estar conectados para completarlo. + start: Empezar + status: + completed: Completado + pending: Pendiente + processing: Procesando + title: Recuento para %{election} + update: + error: El estado de la elección no se había actualizado. + success: 'El estado de la elección es: %{status}.' + menu: + trustee_zone: Zona del garante + no_bulletin_board: + body: Se requiere un Bulletin Board configurado para esta sección. Contacta con el Administrador para más detalles. + title: Lo sentimos, el Bulletin Board aún no está configurado. + trustees: + show: + elections: + list: + action_required: + 'false': 'No' + name: '¿Acción requerida?' + 'true': Realizar acción + bb_status: Estado + election: Elección + voting_period: Período de votación + no_elections: Actualmente no se te ha asignado ninguna acción como garante. Recibirás una notificación cuando sea el momento de actuar en las diferentes fases. + title: Elecciones + identification_keys: + cancel: Cancelar + generate: Generar claves de identificación + generate_error: Hubo un error al generar las claves de identificación. + generate_legend: Necesitas generar un par de claves de identificación para participar en las elecciones como garante. + generate_legend_1: Después de pulsar el botón debes descargar el archivo con las claves de identificación generadas. + generate_legend_2: Copia el archivo descargado a un dispositivo USB limpio + generate_legend_3: Asegúrate de que tu equipo no tenga una copia del archivo (por ejemplo, comprueba las carpetas de Descargas y de Escritorio). + generate_legend_4: Haz otra copia del archivo en un dispositivo externo diferente y guárdalo en un lugar muy seguro. + submit: Enviar + submit_legend: Después de seguir todos los pasos explicados anteriormente, completa el proceso enviando la clave de identificación pública al servidor. + submit_title: Enviar la clave pública de identificación + title: Claves de identificación del garante + upload: Sube tus claves de identificación + upload_error: + invalid_format: El archivo subido no contiene ninguna clave de identificación. + invalid_key: Las claves de identificación en el archivo subido no se pueden cargar. + invalid_public_key: Las claves de identificación en el archivo subido no coinciden con la clave pública de identificación almacenada. + upload_legend: En el servidor constan tus claves públicas de identificación, pero tu navegador todavía no las tiene. Necesitas importar el archivo con tus claves de identificación a tu ordenador desde la copia de seguridad que creaste después de generarlas. + not_supported_browser_description: Parece que estás usando un navegador que no puede ser utilizado para actuar como garante. Asegúrate de que estás usando la versión más reciente de tu navegador, o intenta utilizar cualquier otro de los navegadores más populares para poder completar tus tareas como garante. + not_supported_browser_title: Actualiza el navegador para actuar como garante + safari_warning_description: Parece que estás usando Safari, que no está soportado para actuar como garante o para cifrar un voto (esto se debe a las restricciones de memoria que le impone Apple). Esto podría resolverse en el futuro mediante un cambio de política por parte de Apple o la futura optimización de Decidim Elecciones. Mientras tanto, por favor, utiliza otro navegador. + safari_warning_title: Navegador Safari detectado + trustee_role_description: + with_keys: Has sido asignada como garante en algunas de las elecciones celebradas en esta plataforma. + without_keys: Has sido asignada para actuar como Garante. Por favor, genera y sube tus claves de identificación. + update: + success: Tu clave pública de identificación fue guardada con éxito. + votes: + ballot_decision: + audit: ( Auditar papeleta ) + back: Comenzar de nuevo el proceso de votación + ballot_hash: 'El indentificador de la papeleta es:' + cast: Deposita la papeleta para terminar tu voto + description_html: Aquí tienes las opciones de emitir tu boleta para que sea contada correctamente o puedes auditar que tu boleta ha sido cifrada correctamente. Si quieres auditar la votación, por favor, lee las instrucciones sobre cómo proceder. + header: 'La papeleta se ha cifrado: envíala o audítala' + casting: + header: Se está emitiendo el voto... + text: Tu papeleta se está depositando en la urna. + confirm: + answer_number: respuesta %{number} + confirm: Confirmar + edit: editar + header: Confirma tu voto + intro: Aquí tienes un resumen del voto que estás a punto de emitir.
    Por favor, confirma tu voto o edita tus respuestas. + nota_option: En blanco + confirmed: + back: Volver a las votaciones + experience: '¿Cómo valoras la experiencia?' + feedback: Danos tu opinión + header: Voto confirmado + lead: '¡Tu voto se ha emitido!' + text: 'Puedes comprobar que tu voto se ha añadido correctamente a la urna con el siguiente identificador: %{e_vote_poll_id}' + verify_link: Para comprobarlo, copia el identificador y pégalo en la página de verificación de voto + create: + error: Hubo un problema al emitir el voto. Por favor, inténtalo de nuevo. + encrypting: + header: Se está cifrando el voto... + text: Se está cifrando tu papeleta para garantizar el secreto de voto. + failed: + header: Voto fallido + lead: '¡Tu voto no ha sido emitido!' + text: Algo salió mal, por favor inténtalo de nuevo. + try_again: Inténtalo de nuevo + header: + ballot_decision: Emitir o auditar tu voto + confirm: Confirma tu voto + election: Elección + register: Regístrate + vote_for: Vota por %{title} + messages: + invalid_token: Tu sesión en la cabina de votación no es válida. Intenta votar de nuevo. + not_allowed: En este momento no puedes votar en esta votación. + modal: + close: Cerrar + proposal_header: 'Propuestas:' + new: + answer_choices: Puedes seleccionar hasta %{choices} respuestas + more_information: Más información + nota_option: En blanco / Ninguna de las anteriores + preview_alert: Esta es una vista previa de la cabina de votación. + question_steps: Pregunta %{current_step} de %{total_steps} + selections: "Seleccionada
    %{selected} de %{max_selections}" + onboarding_modal: + create_account: Crear Cuenta + description: '¿Quieres crear una nueva cuenta en la plataforma? Podrás participar en los procesos y ser parte activa de la organización.' + no_account: No, gracias. + title: '¿Eres nueva en la plataforma?' + update: + error: Ha habido un problema al actualizar el estado del voto. Vuelve a intentarlo. + verify: + content: + heading: Verifica tu voto + info: Este verificador comprueba que tu voto, identificado con una cadena de texto cifrada, ha sido emitido correctamente y está dentro de la urna. + error: + header: Voto no encontrado! + info: El código de voto no se ha encontrado en la urna de %{link}, inténtelo de nuevo. + form: + back: Volver a la plataforma + submit: Comprobar + vote_identifier: 'Código identificador:' + vote_identifier_help: Este es el identificador que se te dio después de emitir tu voto (no es el código para entrar la cabina de votación). + header: + title: Verifica tu voto + success: + header: '¡Voto localizado!' + info: Tu voto cifrado está en la urna de %{link}. + voting_step: + back: Atrás + continue: Siguiente + warnings: + empty_filters: No hay ninguna elección con este criterio. + no_elections: No hay ninguna elección programada. + no_scheduled_elections: Actualmente no hay elecciones programadas, pero puedes ver un listado de las anteriores. + events: + elections: + election_published: + email_intro: 'La votación %{resource_title} ya está activa en %{participatory_space_title}. Puedes verla desde esta página:' + email_outro: Has recibido esta notificación porque estás siguiendo %{participatory_space_title}. Puedes dejar de recibir notificaciones siguiendo el enlace anterior. + email_subject: La votación %{resource_title} en %{participatory_space_title} ya está activa. + notification_title: La votación %{resource_title} ya está activa en %{participatory_space_title}. + trustees: + new_election: + email_intro: Has sido añadido como garante para la elección de %{resource_title}. + email_outro: Has recibido esta notificación porque has sido añadido como garante para la elección %{resource_title}. + email_subject: Eres garante para la elección %{resource_title}. + notification_title: 'Has sido asignada para actuar como Garante en la Elección: %{resource_title}. Por favor, realiza la ceremonia de claves para poner en marcha la elección.' + new_trustee: + email_intro: Un administrador te ha añadido como garante para %{resource_name}. Debes crear tu clave pública en tu zona de garantes + email_outro: Has recibido esta notificación porque has sido añadido como garante para %{resource_name}. + email_subject: Eres garante de %{resource_name}. + notification_title: Te han añadido para actuar como garante en %{resource_name} para algunas elecciones que tendrán lugar en esta plataforma.
    Se te requerirá para llevar a cabo algunas tareas. De momento, por favor genera tus claves de identificación. + start_tally: + email_intro: El período de votación para la elección %{resource_title} ha finalizado. Ahora, por favor, realiza el recuento de las elecciones para publicar los resultados definitivos. + email_outro: Has recibido esta notificación porque has eres una de las garantes en la elección %{resource_title}. + email_subject: El proceso de recuento para las elecciones %{resource_title} ha comenzado. + notification_title: El período de votación para las elecciones %{resource_title} ha finalizado. Ahora, por favor, realiza el recuento de las elecciones para publicar el resultado final. + votes: + accepted_votes: + email_intro: '¡Tu voto ha sido aceptado! Utilizando tu token de voto: %{encrypted_vote_hash}, puedes verificar tu voto aquí.' + email_outro: Has recibido esta notificación porque has votado en la elección %{resource_name}. + email_subject: Tu voto para %{resource_name} ha sido aceptado. + notification_title: 'Tu voto ha sido aceptado. Verifica tu voto aquí usando tu token de voto: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Se te ha asignado como %{role} del punto de votación %{polling_station_name} en %{resource_title}. Puedes administrar el punto de votación desde el espacio Zona de gestores de mesa. + email_outro: Has recibido esta notificación porque has sido asignado como %{role} de %{polling_station_name}. + email_subject: Eres %{role} del punto de votación %{polling_station_name}. + notification_title: Eres %{role} del punto de votación %{polling_station_name} en la votación %{resource_title}. + send_access_code: + instruction: 'Ya tienes el código de acceso que has pedido: %{access_code}. Con esto ya podrás participar en %{voting}.' + subject: Tu código de acceso para participar en %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Una votación es un espacio que os permite hacer una pregunta clara al conjunto de miembros de una organización, hacer un llamamiento a participar en una votación, generar y ordenar el debate a favor o en contra de una respuesta. cunado llega la fecha de la votación, podéis votar y publicar el resultado.

    Las votaciones pueden ser prácticamente de cualquier aspecto que afecte a la organización. Algunos ejemplos serían: cambiar el nombre o el logotipo de la organización ofreciendo diversas alternativas, decidir si pasar a formar parte de una organización más grande o no, validar o desestimar un nuevo plan estratégico o el resultado de un gruipo de trabajo, o definir si los cargos deberían tener una duración máxima de uno, dos o tres mandatos.

    " + page: "

    Una votación es un espacio que os permite hacer una pregunta clara al conjunto de miembros de una organización, hacer un llamamiento a participar en una votación, generar y ordenar el debate a favor o en contra de una respuesta. cunado llega la fecha de la votación, podéis votar y publicar el resultado.

    Las votaciones pueden ser prácticamente de cualquier aspecto que afecte a la organización. Algunos ejemplos serían: cambiar el nombre o el logotipo de la organización ofreciendo diversas alternativas, decidir si pasar a formar parte de una organización más grande o no, validar o desestimar un nuevo plan estratégico o el resultado de un gruipo de trabajo, o definir si los cargos deberían tener una duración máxima de uno, dos o tres mandatos.

    " + title: '¿Qué son las votaciones?' + menu: + votings: Votaciones + participatory_spaces: + related_elections: + see_all: Ver todas las elecciones + statistics: + elections_count: Elecciones + votings_count: Votaciones + votings: + admin: + ballot_styles: + create: + error: Ha habido un problema al crear el estilo de papeleta. + success: Se ha creado correctamente el estilo de papeleta. + destroy: + invalid: Ha habido un problema al suprimir el estilo de papeleta. + success: Se ha eliminado correctamente el estilo de papeleta. + edit: + title: Editar el estilo de papeleta + update: Actualizar + form: + code_help: 'Pista: el código es el vínculo entre el censo y el estilo de papeleta. Al actualizar los datos del censo, a cada entrada se le asigna un estilo de papeleta que coincida con el código.' + election: Elección + questions: Preguntas para el estilo de papeleta + questions_help: 'Pista: selecciona las preguntas del componente de elecciones para presentarlas a las votantes asignadas a este estilo de papeleta.' + index: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + edit: Editar + new: Añadir estilo de papeleta + title: Acciones + associated_census_data: Entradas asociadas al censo + explanation_callout: Un estilo de papeleta especifica qué preguntas se presentarán a las votantes en la cabina. En un estilo de papeleta, puedes elegir qué preguntas del componente de elecciones pertenecen a esa papeleta. El código de estilo de la papeleta se utiliza para hacer coincidir a una votante del censo con la papeleta que se le debe mostrar en la cabina. No crees ningún estilo de papeleta si quieres que simpre se muesten todas las preguntas. + title: Estilos de papeleta + new: + create: Crear + title: Crear estilo de papeleta + update: + invalid: Ha habido un problema al actualizar el estilo de papeleta. + success: Se ha creado correctamente el estilo de papeleta. + content_blocks: + attachments_and_folders: + name: Archivos y carpetas de la votación + header: + name: Cabecera de la votación + highlighted_votings: + max_results: Cantidad máxima de elementos a mostrar + html_block_1: + name: Bloque html 1 de la votación + html_block_2: + name: Bloque html 2 de la votación + html_block_3: + name: Bloque html 3 de la votación + main_data: + name: Titulo y descripción + metrics: + name: Métricas de la votación + polling_stations: + name: Puntos de votación + related_elections: + name: Elecciones + stats: + name: Estadísticas de la votación + timeline: + name: Calendario de la votación + index: + published: Publicada + unpublished: Despublicada + menu: + votings: Votaciones + votings_submenu: + attachment_collections: Carpetas + attachment_files: Archivos + attachments: Archivos adjuntos + ballot_styles: Estilos de papeleta + census: Censo + components: Componentes + info: Acerca de esta votación + landing_page: Página de inicio + monitoring_committee: Comité de seguimiento + monitoring_committee_election_results: Validar resultados + monitoring_committee_members: Miembros + monitoring_committee_polling_station_closures: Validar certificados + monitoring_committee_verify_elections: Verificar elecciones + polling_officers: Gestores de mesa + polling_stations: Puntos de votación + see_voting: Ver la votación + models: + ballot_style: + fields: + code: Código + monitoring_committee_member: + fields: + email: Correo electrónico + name: Nombre + polling_officer: + fields: + email: Correo electrónico + name: Nombre + polling_station: Punto de votación (rol) + polling_station: + fields: + address: Dirección + polling_station_managers: Administradores + polling_station_president: Presidente + title: Título + voting: + fields: + created_at: Creado el + published: Publicada + title: Título + monitoring_committee_election_results: + actions: + title: Acciones + view: Ver + index: + title: Elige una elección para ver los resultados + results: + bulletin_board: Bulletin Board + election_totals: Totales de la elección + polling_stations: Puntos de votación + result_types: + blank_answers: Respuestas en blanco + blank_ballots: Papeletas en blanco + null_ballots: Papeletas nulas + total_ballots: Total de papeletas + valid_ballots: Papeletas válidas + selected: Seleccionado + title: Resultados para la elección %{election_title} + totals: Totales + show: + change_election: Cambiar elección + publish_results: Publicar resultados + publishing: Publicando resultados... + update: + invalid: Se ha producido un error al publicar los resultados. + rejected: La publicación de los resultados fue rechazada por el Bulletin Board. Inténtalo de nuevo o ponte en contacto con el administrador del sistema. + success: Los resultados se han publicado correctamente. + monitoring_committee_members: + create: + invalid: Hubo un problema al crear esta miembro de la comisión de seguimiento. + success: Miembro del comisión de seguimiento creada con éxito. + destroy: + invalid: Hubo un problema al eliminar esta miembro de la comisión de seguimiento. + success: Miembro de la comisión de seguimiento eliminada con éxito. + form: + existing_user: Participante existente + non_user: Invitar nueva participante + select_user: Buscar por correo electrónico, nombre o alias + user_type: Tipo de participante + index: + title: Comité de seguimiento + new: + create: Crear + title: Crear miembro del comité de seguimiento + monitoring_committee_polling_station_closures: + actions: + title: Acciones + validate: Validar + view: Ver + closures: + change_election: Cambiar elección + signed: '¿Firmado?' + title: Puntos de votación para la elección %{election_title} + validated: '¿Validado?' + edit: + change_polling_station: Volver a los puntos de votación + monitoring_committee_notes: Observaciones + monitoring_committee_notes_placeholder: Reporta cualquier incidencia aquí + title: Resultados para la elección %{election_title} en el punto de votación %{polling_station_title} + elections: + title: Elige una elección para validar + show: + change_polling_station: Volver a los puntos de votación + monitoring_committee_notes: Observaciones del Comité de Seguimiento + validate: + error: Se ha producido un error al validar el cierre. + success: El cierre se ha validado correctamente. + monitoring_committee_verify_elections: + index: + download: Descargar + how_to_checksum: 'Para asegurarse de que el archivo descargado no ha sido dañado o manipulado durante el proceso de descarga, ejecuta el siguiente comando en tu consola y comprueba que la salida coincida con la suma de verificación reportada arriba:' + how_to_download: Para verificar una elección, descarga tu archivo verificable de la tabla de arriba. + how_to_run_verifier: 'Una vez que hayas descargado el archivo y te hayas asegurado de que está bien, puedes proceder a ejecutar el verificador universal. Clona este repositorio y, desde la carpeta raíz, ejecuta el siguiente comando:' + how_to_title: Cómo verificar la validez de una elección + not_available: No disponible todavía + title: Elecciones + polling_officers: + create: + invalid: Hubo un problema al crear esta oficial de votación. + success: Oficial de votación creada con éxito. + destroy: + invalid: Hubo un problema al eliminar esta oficial de votación. + success: Oficial de votación eliminada con éxito. + form: + existing_user: Participante existente + non_user: Invitar nueva participante + select_user: Buscar por correo electrónico, nombre o alias + user_type: Tipo de participante + index: + role_manager: administrador + role_president: presidente + title: Gestores de mesa + new: + create: Crear + title: Crear gestor de mesa + polling_officers_picker: + choose_polling_officers: Elegir gestores de mesa + no_polling_officers: Ninguna gestora de mesa coincide con tus criterios de búsqueda o no hay ninguna. + polling_stations: + create: + invalid: Hubo un problema al crear este punto de votación. + success: Punto de votación creado correctamente. + destroy: + invalid: Hubo un problema al eliminar este punto de votación. + success: Punto de votación eliminado correctamente. + edit: + title: Editar punto de votación + update: Actualizar punto de votación + form: + address_help: 'Dirección: usada por Geocoder para encontrar la ubicación' + location_help: 'Ubicación: mensaje dirigido a los votantes con el lugar exacto del punto de votación' + location_hints_help: 'Sugerencias de ubicación: información adicional. Ejemplo: el piso del edificio donde se encuentra el punto de votación.' + polling_station_managers_help: 'Administradores de mesa: los oficiales que actuarán como administradores de los puntos de votación. Asegúrate de que los oficiales ya han sido creados en Gestores de mesa y que no están ya asignados a otro punto de votación' + polling_station_president_help: 'Presidencia de mesa: oficiales que realizarán las funciones de presidencia en el punto de votación. Asegúrate de que están habilitadas como gestoras de mesa y que ya no están asignadas a otro punto de votación.' + select_president: Selecciona un gestor de mesa como presidente del punto de votación + index: + title: Puntos de votación + new: + create: Crear + title: Crear punto de votación + update: + invalid: Hubo un problema al actualizar este punto de votación. + success: Punto de votación actualizado correctamente. + titles: + votings: Votaciones + votings: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + new_voting: Nuevo espacio de votación + create: + invalid: Hubo un problema al crear esta votación. + success: Votación creada con éxito. + edit: + add_election_component: No tienes ninguna elección configurada para esta votación. Por favor añádela en la sección de componentes. + assign_missing_officers: Hay puntos de votación sin presidencia y/o administradoras. Asígnalas desde la sección de puntos de votación. + update: Actualizar + form: + banner_image: Imagen de cabecera + census_contact_information: Datos de contacto del censo + census_contact_information_help: Esta información de contacto es para una participante que quiere reportar problemas con el censo. Puede ser una dirección de correo electrónico, un formulario de contacto en otro sitio, una encuesta para visitantes, etc. + introductory_image: Imagen de presentación + promoted: Destacada + select_a_voting_type: Por favor, selecciona un tipo de votación + show_check_census_help: Especifica si mostrar el enlace "¿Puedo votar?" en el menú público de votaciones. + slug: Nombre corto de URL + slug_help_html: 'Los textos cortos de URL se utilizan para generar las URL que apuntan a esta votación. Sólo acepta letras, números y guiones, y debe comenzar con una letra. Ejemplo: %{url}' + title: Título + voting_type: + hybrid: Hibrida + in_person: Presencial + online: En línea + voting_type_label: Tipo de votación + new: + create: Crear + title: Nueva votación + update: + invalid: Hubo un problema al actualizar esta votación. + success: Votación actualizada correctamente. + admin_log: + ballot_style: + create: "%{user_name} creó un estilo de papeleta con código %{ballot_style_code} en el espacio %{space_name}" + delete: "%{user_name} eliminó el estilo de la papeleta con el código %{ballot_style_code} en el espacio %{space_name}" + update: "%{user_name} actualizó el estilo de la papeleta con el código %{ballot_style_code} en el espacio %{space_name}" + census: + create: "%{user_name} creó el censo para el espacio %{space_name}" + delete: "%{user_name} eliminó el censo del espacio %{space_name}" + update: "%{user_name} actualizó el censo del espacio %{space_name}" + monitoring_committee_member: + create: "%{user_name} asignó al usuario %{monitoring_committee_member_user} como miembro del comité de seguimiento en el espacio %{space_name}" + delete: "%{user_name} desasignó el usuario %{monitoring_committee_member_user} como miembro del comité de seguimiento en el espacio %{space_name}" + polling_officer: + create: "%{user_name} asignó al usuario %{polling_officer_user} como gestor de mesa en el espacio %{space_name}" + delete: "%{user_name} desasignó al usuario %{polling_officer_user} como gestor de mesa en el espacio %{space_name}" + polling_station: + create: "%{user_name} creó el punto de votación %{resource_name} en el espacio %{space_name}" + delete: "%{user_name} eliminó el punto de votación %{resource_name} en el espacio %{space_name}" + update: "%{user_name} actualizó el punto de votación %{resource_name} en el espacio %{space_name}" + voting: + create: "%{user_name} creó la votación %{resource_name}" + publish: "%{user_name} publicó la votación %{resource_name}" + unpublish: "%{user_name} despublicó la votación %{resource_name}" + census: + admin: + census: + create: + invalid: Se ha producido un error al subir el censo, por favor inténtalo de nuevo más tarde. + invalid_csv_header: Los encabezados CSV faltan o no son correctos - por favor lee las instrucciones cuidadosamente. + creating_data: + info_message: "Por favor espera, procesadas %{processed_count} de %{raw_count} filas del fichero %{file} (esto puede tardar algunos minutos)." + delete: + button: Borrar todos los datos del censo + confirm: Eliminar todo el censo no se puede deshacer. ¿Seguro que quieres continuar? + destroy: + error: Se ha producido un error al eliminar el censo, por favor inténtalo de nuevo más tarde. + success: Datos del censo eliminados. + export_access_codes: + button: Exportar códigos de acceso de la votación + callout: Ahora puedes exportar los códigos de acceso. Esto sólo se puede hacer una vez. Una vez que inicies la exportación, recibirás un correo electrónico con las instrucciones en %{email} + confirm: Solo puedes exportar los códigos de acceso una vez. Asegúrate de tener acceso a la cuenta de correo electrónico %{email}. + file_not_exist: Este archivo no existe. + launch_error: Problema al iniciar la exportación de códigos de acceso. + launch_success: Se ha iniciado la exportación de los códigos de acceso. En breve recibirás un correo en %{email}. + exporting_access_codes: + info_message: "Por favor, espera, se está preparando la exportación, la recibirás en breve en %{email} (esto puede tardar unos minutos)." + freeze: + callout: El censo está cerrado y no se puede modificar. + help_html: | + Los datos del censo Se han subido correctamente, los códigos se han generado y exportado con éxito.
    Ahora estás lista para comenzar la elección.
    + Utiliza el CSV exportado, que incluye los códigos individuales para distribuirlo al censo utilizando tus propios medios, o active la pestaña "¿Puedo votar?", para permitir que cualquiera recupere este código usando sus propios datos de censales. + generate_access_codes: + button: Generar códigos de acceso de la votación + callout: Ahora puedes generar los códigos de acceso. Ten en cuenta que después de generarlos ya no podrás modificar más el censo. + confirm: Si continúas, ya no podrás modificar el censo. + info_message_all: "Se han importado las filas correctamente del fichero %{file} (%{raw_count} de %{data_count})." + info_message_warn: Por favor, comprueba que no falten datos, ya que se han creado %{data_count} registros, pero el fichero que se ha subido (%{file}) tenía %{raw_count} filas. + launch_error: Ha habido un problema al generar los códigos de acceso + launch_success: Se ha iniciado la generación de códigos. + start_over: Por favor, elimine el censo actual y vuelva a empezar con un archivo CSV apropiado con filas válidas. + generating_access_codes: + info_message: "Espera, por favor<, los códigos de acceso a la votación se están generando (esto puede tardar unos minutos)..." + new: + file_help: + explanation: 'Instrucciones para el archivo:' + message_1: Sólo se permiten archivos CSV (.csv). + message_2: El separador entre columnas debe ser un punto y coma (";"). + has_ballot_styles_message: Has configurado los estilos de papeleta. Por favor, asegúrate de que el campo "%{ballot_style_code_header}" en el CSV corresponde al código de estilo de papeleta deseado. + info_message: "Aún no hay ningún censo. Por favor, utiliza el siguiente formulario para crearlo importando un archivo CSV." + missing_ballot_styles_message: 'No hay ningún estilo de papeleta para esta votación todavía. Si quieres tener preguntas condicionales (presentar al votante diferentes preguntas dependiendo de, p.ej. el distrito/región de residencia), debes configurar los estilos de papeleta antes de importar el censo. Si quieres presentar a todos los votantes las mismas preguntas, puedes continuar con el procedimiento de importación del censo.' + submit: Enviar CSV + title: Crear el censo + show: + heading: Censo del espacio de votación + upload_info: + csv_example_with_ballot_style: 'Un ejemplo del archivo con estilos de papeleta:' + csv_example_without_ballot_style: 'Un ejemplo del archivo sin estilos de papeleta:' + csv_header_after: No incluyas el último campo ("%{ballot_style_code_header}") si no necesitas preguntas condicionales/estilos de papeleta + csv_header_before: 'El censo debe ser un archivo CSV con la siguiente cabecera:' + document_types: + identification_number: Número de identificación + passport: Pasaporte + export_mailer: + access_codes_export: + click_button: 'Haz clic en el siguiente enlace para descargar tus datos.
    El archivo estará disponible hasta %{date}.
    Necesitarás 7-Zip (para Windows), Keka (para MacOS) o PeaZip (para Linux) para abrirlo. Contraseña: %{password}' + download: Descargar + subject: La exportación de los códigos de acceso de votación para %{voting_title} está disponible + vote_flow: + already_voted_in_person: Esta participante ya ha votado presencialmente y no tiene derecho a voto. + datum_not_found: Los datos introducidos no coinciden con ninguna votante. + content_blocks: + highlighted_votings: + name: Votaciones destacadas + landing_page: + polling_stations: + heading: Puntos de votación + no_polling_stations: Aún no hay puntos de votación. + monitoring_committee_members: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + new: Nuevo miembro + title: Acciones + pages: + home: + highlighted_votings: + active_spaces: Votaciones activas + see_all_spaces: Ver todas las votaciones + polling_officer_zone: + closures: + back_to_polling_stations: Volver a los puntos de votación + certify: + add_photos: Añadir fotos + edit_photos: Editar fotos + error: Se ha producido un error al adjuntar el certificado, por favor inténtalo de nuevo. + heading: Recuento de votos - Subir certificado + info_text: Por favor, sube una foto del certificado de cierre electoral. + submit: Subir el certificado + success: Certificado subido correctamente. + upload_photos: Sube una foto del Certificado de Cierre Electoral + completed: + sub_heading: Este recuento ha sido certificado y ya no puede ser editado. + create: + error: Se ha producido un error al crear el cierre, por favor inténtalo de nuevo más tarde. + success: Cierre creado correctamente. + destroy: + error: Ocurrió un error eliminando el cierre. + success: Cierre eliminado correctamente. + edit: + confirm_start_over: Esto eliminará el número total de votos y las notas adjuntas. ¿Seguro que lo quieres eliminar? + heading: Recuento de votos - recuento de respuestas + info_text: Por favor detalla el número total de respuestas para cada pregunta. Este debe coincidir con el número total de respuestas introducidas en el paso anterior (%{total} respuestas en total). + modal_ballots_results_count_error: + blank: El total esperado de votos en blanco es %{expected}, pero la suma de las preguntas en blanco es %{current}. + close_modal: Cerrar + info_text: El número total de papeletas no coincide con el número total de sobres. Por favor, revisa el total de papeletas. + title: El total de papeletas no coincide + total: El total esperado es %{expected}, pero la suma de las papeletas válidas, en blanco y nulas es %{current}. + valid: El total esperado de votos válidos es %{expected}, pero la suma de las preguntas válidas es %{current}. + save_recount: Guardar recuento + start_over: Si hay un error en el número total, puedes borrar todo y empezar de nuevo. + total_ballots: Total de papeletas + total_blank_ballots: Total de papeletas en blanco + total_null_ballots: Total de papeletas nulas + total_valid_ballots: Total de papeletas válidas + new: + election: 'Elección:' + heading: Recuento de votos + info_text: 'Por favor, introduce el número total de papeletas (sobres) recontadas en este punto de votación:' + modal_ballots_count_error: + btn_validate_total: Validar recuento total de papeletas + info_explanation_text: 'Por favor, revisa el número total de papeletas. Si el número total es incorrecto, debes proporcionar una explicación para el Comité de Seguimiento:' + info_text: El número total de papeletas (sobres) introducidas no coincide con el registro de personas que han votado en este punto de votación. + message_for_monitoring_committee: Mensaje para el Comité de Seguimiento + review_recount: Revisar el recuento + text_area_placeholder: Por favor, escribe tu mensaje + title: El total de registros no coincide + total_ballots: 'Total de papeletas:' + total_people: 'Total de personas:' + polling_station: 'Punto de votación:' + submit: Verificar número total + total_ballots_count: Número de papeletas + show: + edit_count_votes: '¿Número incorrecto? Aún puedes editarlos.' + heading: Recuento de votos + sub_heading: El recuento debe cerrarse mediante la carga de un certificado. Una vez hecho, el recuento se sellará y ya no se podrá editar más. + sign: + cancel: Cancelar + check_box: Lo he revisado y es idéntico al certificado físico de cierre electoral + confirm: Ok, continuar + error: Se ha producido un error, por favor inténtalo de nuevo. + heading: Recuento de votos - Firmar cierre + info_text: Si continúas ya no podrás modificar ninguna información, esta acción no se puede deshacer. + submit: Firmar el cierre + success: Cierre firmado con éxito. + update: + error: Se ha producido un error al actualizar los resultados del cierre. Inténtalo de nuevo más tarde. + success: Resultados de cierre actualizados correctamente. + in_person_votes: + complete_voting: + available_answers: 'Respuestas disponibles:' + census_verified: Esta participante no ha votado presencialmente aún. + census_verified_with_online_vote: Esta participante ya ha votado en línea. Si vota en persona, los votos anteriores serán invalidados y este será su voto definitivo. + complete_voting: Completar votación + identify_another: Identificar a otra participante + questions_title: 'Tienen derecho a votar en las siguientes preguntas:' + questions_title_voted: 'Esta participante ya ha votado en línea y tiene derecho a votar en las siguientes preguntas:' + voted: La participante ha votado + create: + error: El voto no ha sido registrado. Por favor, inténtalo de nuevo. + in_person_form: + census_not_present: Esta participante no aparece en el censo. + census_not_present_description: Debe acudir a la oficina de reclamaciones del censo o al servicio de asistencia técnica. + date_of_birth: Fecha de nacimiento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + month: Mes + month_placeholder: MM + select: Selecciona el tipo de documento + title: 'Selecciona el tipo de documento e introduce el número de documento de la participante:' + validate_document: Validar documento + year: Año + year_placeholder: AAAA + new: + back: Volver a los puntos de votación + title: Identificar y verificar a una participante + show: + back: Volver a los puntos de votación + title: Esperando a que se registre el voto presencial + update: + error: Se ha producido un error al registrar el voto. Por favor, inténtalo de nuevo. + success: + accepted: El voto se ha registrado correctamente. + rejected: El Bulletin Board no ha aceptado el voto. Por favor, ponte en contacto con el administrador del sistema. + verify_document: + census_present: Esta participante aparece en el censo. + name: Nombre + title: 'Comprueba que los siguientes datos son correctos:' + verify_document: Verificar documento + menu: + polling_officer_zone: Zona del gestor de mesa + polling_officers: + index: + polling_officer_role_description: Has sido asignado para actuar como gestor de mesa (Presidente o Administrador) en algunas de las elecciones celebradas en esta plataforma. + polling_station: + address: Dirección + count_votes: Contar votos + election: Elección + identify_person: Identificar a una persona + name: Nombre + no_polling_stations: Todavía no estás asignado a ningún punto de votación. + role: Tu rol + show_closure: Ver cierre + title: Puntos de votación + voting: Votación + polling_officers: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + new: Añadir gestora de mesa + title: Acciones + roles: + manager: Administrador + president: Presidente + unassigned: No Asignado + polling_station_closure_certificate: + current_certificate: 'Certificado actual:' + polling_station_closure_recount: + nota_option: En blanco / Ninguna de las anteriores + polling_officer_notes: 'Notas del gestor de mesa:' + polling_officer_notes_blank: No hay notas + recount_summary: 'Resumen del recuento:' + signed: Firmado + total_ballots: 'Total de papeletas:' + total_blank_ballots: 'Total de papeletas en blanco:' + total_null_ballots: 'Total de papeletas nulas:' + total_valid_ballots: 'Total de papeletas válidas:' + polling_stations: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + edit: Editar + new: Añadir punto de votación + title: Acciones + votings: + access_code_modal: + email: Enviar por correo electrónico a %{email} + info: Necesitas un código de acceso para participar. Si no recibiste uno por correo postal, podemos enviarte uno nuevo. + no_email: Correo no disponible + no_sms: Número de teléfono no disponible + sms: Enviar por SMS a %{sms} + title: Obtener código de acceso + check_census: + check_status: Comprobar estado + description: Aquí, tienes la opción de comprobar tus datos del censo para saber si tienes derecho a participar en esta votación. Deberías tener ya un código de acceso, pero si lo has perdido, puedes solicitarlo de nuevo, si tus datos son correctos. + error: + info: 'Por favor, inténtalo de nuevo. Si crees que los datos del sistema son incorrectos, puedes reportarlo aquí: %{census_contact_information}.' + title: Los datos que ha introducido no están en el censo de esta votación + form_title: 'Rellena el siguiente formulario para comprobar tus datos del censo:' + invalid: Hubo un problema al comprobar el censo. + success: + access_link: por correo electrónico. + access_link_with_sms: vía SMS o correo electrónico. + info: Deberías haber recibido tu código de acceso por correo postal ya. En caso de que no lo tengas, puedes solicitarlo aquí + title: '¡Tus datos del censo son correctos!' + title: '¿Puedo votar?' + check_fields: + date_of_birth: Fecha de nacimiento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + document_type: Tipo de documento + month: Mes + month_placeholder: MM + postal_code: Código postal + postal_code_placeholder: Número de código postal + select: Selecciona el tipo de documento + year: Año + year_placeholder: AAAA + count: + title: + one: "%{count} votación" + other: "%{count} votaciones" + elections_log: + description: El registro de la elección te mostrará toda la información relevante sobre cada votación. Por ejemplo, el estado de la ceremonia de claves o el recuento o si los resultados ya están publicados. Haz clic en la elección sobre la cual quieres la información del registro. + title: Registro de la elección + filters: + active: Activas + all: Todas + date: Fecha + finished: Finalizadas + search: Buscar + upcoming: Próximas + index: + no_votings: Ninguna votación coincide con tus criterios de búsqueda. + only_finished: Actualmente no hay votaciones programadas, pero aquí puedes encontrar las votaciones terminadas en la lista. + title: Votaciones + login: + access_code: Código de acceso + access_code_placeholder: Código de acceso + ask_for_a_new_one: Solicitar uno nuevo. + dont_have_access_code: '¿No tienes un código de acceso?' + form_title: 'Rellena el siguiente formulario para acceder a la votación:' + start_voting: Empezar a votar + title: Identificarme con mis datos del censo + no_census_contact_information: Aún no hay ninguna información de contacto. + orders: + label: 'Ordenar votaciones por:' + random: Aleatorio + recent: Más recientes + send_access_code: + invalid: Hubo un problema al enviar el código de acceso. + success: Tu código de acceso se ha enviado correctamente. + show: + title: Acerca de esta votación + votings_m: + badge_name: + finished: Finalizada + ongoing: En curso + upcoming: Próximamente + unspecified: No especificado + voting_type: + hybrid: Hibrida + in_person: Presencial + online: En línea + layouts: + decidim: + voting_navigation: + check_census: '¿Puedo votar?' + election_log: Registro de la elección + votings: + index: + promoted_votings: Votaciones destacadas + promoted_voting: + vote: Votar diff --git a/decidim-elections/config/locales/es.yml b/decidim-elections/config/locales/es.yml new file mode 100644 index 00000000..eb61e0bd --- /dev/null +++ b/decidim-elections/config/locales/es.yml @@ -0,0 +1,1425 @@ +es: + activemodel: + attributes: + answer: + description: Descripción + image: Imagen + proposals: Propuestas relacionadas + title: Título + ballot_style: + code: Código + election: + description: Descripción + end_time: La votación acaba el + start_time: La votación empieza el + title: Título + monitoring_committee_member: + email: Correo electrónico + name: Nombre + polling_officer: + email: Correo electrónico + name: Nombre + polling_station: + address: Dirección + location: Ubicación + location_hints: Detalles de ubicación + polling_station_managers: Gestoras + polling_station_president_id: Presidencia + title: Título + question: + max_selections: Número máximo de opciones + min_selections: Ninguna de las opciones anteriores + title: Título + trustees_participatory_space: + user_id: Participante + voting: + banner_image: Imagen de cabecera + census_contact_information: Datos de contacto del censo + description: Descripción + end_time: La votación finaliza + introductory_image: Imagen de presentación + promoted: Destacada + scope_id: Ámbito + show_check_census: Mostrar la página de "comprobar censo" + start_time: La votación empieza + title: Título + voting_type: Tipo de votación + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Es necesario volver a adjuntar el archivo + ballot_result: + attributes: + base: + total_count_invalid: El número total de respuestas no coincide con el desglose válido/blanco/nulo. + election: + attributes: + attachment: + needs_to_be_reattached: Es necesario volver a adjuntar el archivo + question_result: + attributes: + base: + blank_count_invalid: El número total de respuestas en blanco no puede ser mayor que el total de papeletas en blanco. + trustee: + attributes: + name: + cant_be_changed: no se puede cambiar + public_key: + cant_be_changed: no se puede cambiar + voting: + attributes: + voting_type: + inclusion: "%{value} no es un tipo de votación válido" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: El gestor electoral ya es presidente/gestor del punto de votación. + voting: + different_organization: La votación debe estar en la misma organización que la participante. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: La oficial de votación debe estar en la misma votación que el punto de votación. + models: + decidim/elections/answer: + one: Respuesta + other: Respuestas + decidim/elections/election: + one: Votación + other: Votaciones + decidim/elections/question: + one: Pregunta + other: Preguntas + decidim/voting: + one: Votación + other: Votaciones + decidim/votings/census/dataset: + one: Conjunto de datos + other: Conjuntos de datos + decidim/votings/census/datum: + one: Dato + other: Datos + decidim/votings/polling_officer: + one: Gestor de mesa + other: Gestores de mesa + decidim/votings/polling_station: + one: Punto de votación + other: Puntos de votación + decidim/votings/voting: + one: Votación + other: Votaciones + decidim: + admin: + filters: + officers_assigned_eq: + label: Responsables + values: + assigned: Asignado + unassigned: No asignado + role_eq: + label: Rol + values: + manager: Gestor + president: Presidencia + unassigned: Sin asignar + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Buscar %{collection} por nombre/email/alias o punto de votación. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Buscar %{collection} por título, dirección o nombre del responsable/email/alias. + signed_eq: + label: Firmada + values: + 'false': Firmada + 'true': Sin firmar + validated_eq: + label: Validada + values: + 'false': Sin validar + 'true': Validada + voting_publications: + create: + error: Se ha producido un error al publicar esta votación. + success: Votación publicada correctamente. + destroy: + error: Ha habido un problema al despublicar esta votación. + success: Votación despublicada correctamente. + components: + elections: + actions: + vote: Votar + name: Votaciones + settings: + global: + announcement: Aviso + step: + announcement: Aviso + elections: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + edit: Editar + feedback: Retorno de la votante + import: Importar propuestas como respuestas + manage_answers: Gestionar las respuestas + manage_questions: Gestionar las preguntas + manage_steps: Gestionar las fases + new_answer: Añadir respuesta + new_election: Añadir elección + new_question: Añadir pregunta + new_trustee: Añadir garante + preview: Previsualizar + publish: Publicar + title: Acciones + unpublish: Despublicar + admin: + answers: + create: + invalid: Se ha producido un error al crear esta respuesta. + success: Respuesta creada con éxito. + destroy: + invalid: Se ha producido un error al eliminar esta respuesta. + success: Respuesta eliminada con éxito. + edit: + title: Editar respuesta + update: Actualizar respuesta + index: + invalid_max_selections: Necesitas %{missing_answers} respuesta/s más para coincidir con la selección máxima. + title: Respuestas + new: + create: Crear respuesta + title: Nueva respuesta + not_selected: No seleccionada + select: + disable: Deseleccionar respuesta + enable: Marcar respuesta como seleccionada + invalid: Hubo un problema al seleccionar esta respuesta. + success: Respuesta seleccionada con éxito. + selected: Seleccionada + unselect: + invalid: Hubo un problema al deseleccionar esta respuesta. + success: Respuesta deseleccionada correctamente. + update: + invalid: Se ha producido un error al actualizar esta respuesta. + success: Respuesta actualizada correctamente. + elections: + create: + invalid: Se ha producido un error al crear esta elección. + success: La elección se ha creado correctamente. + destroy: + invalid: Se ha producido un error al eliminar la elección. + success: La elección se ha eliminado correctamente. + edit: + title: Editar la elección + update: Actualizar la elección + form: + organization_time_zone: Compruebe que la zona horaria es correcta en la configuración de la organización. La configuración actual es %{time_zone} (%{time}). + index: + no_bulletin_board: No hay ningún servidor de Bulletin Board configurado, el cual es necesario para utilizar este módulo. Esta tarea debe ser realizada por la persona administradora del sistema. + title: Elecciones + new: + create: Crear elección + title: Nueva elección + publish: + success: La votación se ha publicado correctamente. + unpublish: + success: La votación se ha despublicado correctamente. + update: + invalid: Se ha producido un error al actualizar esta votación. + success: La votación se ha actualizado correctamente. + exports: + elections: Elecciones + feedback_form_answers: Retorno de las respuestas + mailers: + trustee_mailer: + body: + help_html: |- +

    Hola %{user_name},


    +

    Te han añadido para actuar como garante en algunes elecciones que tendran lugar en %{organization}.


    +

    Se ha creado en tu cuenta una nueva sessión llamada "Zona de garantes". Desde esta, podrá realitzar las tareas requeridas. De momento, por favor, genera tus claves de identificación.


    + subject: Se te ha añadido como garante de %{resource_name} + trustee_zone: Llévame al espacio de garantes + menu: + trustees: Garantes + models: + answer: + name: Respuesta + proposals_imports: + create: + invalid: Se ha producido un error al importar las propuestas como respuestas. + success: "%{number} propuestas importadas a respuestas correctamente." + new: + create: Importar propuestas como respuestas + no_components: No hay otros componentes de propuestas en este espacio participativo desde los que importar propuestas como respuestas. + select_component: Por favor selecciona un componente + title: Importar propuestas + questions: + create: + election_started: La elección ya ha comenzado. + invalid: Se ha producido un error al crear esta pregunta. + success: Se ha creado la pregunta correctamente. + destroy: + invalid: Se ha producido un error al eliminar esta pregunta. + success: Se ha eliminado la pregunta correctamente. + edit: + title: Editar la pregunta + update: Actualizar la pregunta + index: + title: Preguntas + new: + create: Crear una pregunta + title: Nueva pregunta + update: + invalid: Se ha producido un error al actualizar esta pregunta. + success: La pregunta se ha actualizado correctamente. + steps: + create_election: + census: Censo + errors: + census_codes_generated: Los códigos de acceso al censo no se han podido generar. + census_frozen: Los códigos de acceso al censo no se han podido exportar. + census_uploaded: No se ha subido ningún censo para esta elección. + component_published: La elección no está publicada. + fix_it_text: Arreglarlo + max_selections: Las preguntas no tienen un valor correcto para la cantidad de respuestas + minimum_answers: Las preguntas deben tener al menos dos respuestas. + minimum_questions: La elección debe tener al menos una pregunta. + published: La elección no está publicada. + time_before: Se ha configurado la hora de inicio en menos de %{hours} horas antes de que comience la elección. + trustees_number: El espacio participativo debe tener al menos %{number} garantes con clave pública. + invalid: Hubo un problema al configurar esta elección + no_trustees: No hay garantes configurados para este espacio participativo + not_used_trustee: "(no se usa)" + public_key: + 'false': no tiene una clave pública + 'true': tiene una clave pública + requirements: + census_codes_generated: Se han generado los códigos de acceso al censo. + census_frozen: Se han exportado los códgos para el censo y el censo ha quedado congelado. + census_uploaded: El censo está subido. + component_published: La elección está publicada. + max_selections: Todas las preguntas tienen un valor correcto para máximo de respuestas. + minimum_answers: Cada pregunta tiene al menos 2 respuestas. + minimum_questions: La elección tiene al menos 1 pregunta. + published: La elección está publicada. + time_before: La configuración se está realizando al menos %{hours} horas antes de que comience la elección. + trustees_number: El espacio participativo tiene al menos %{number} garantes con clave pública. + submit: Configurar elección + success: La elección se ha enviado con éxito al "Bulletin Board". + technical_configuration: + authority_name: "Nombre de la autoridad: %{value}" + bulletin_board_server: "Servidor del boletín: %{value}" + scheme_name: "Nombre del esquema: %{value}" + title: Ver la información técnica + title: Configurar elección + trustees: Garantes de la elección + created: + invalid: Hubo un problema al iniciar la ceremonia de claves. + submit: Iniciar la ceremonia de claves + success: La solicitud para iniciar la ceremonia de claves fue enviada con éxito al "Bulletin Board". + title: Elección creada + trustees: Garantes + key_ceremony: + continue: Continuar + title: Ceremonia de claves + key_ceremony_ended: + errors: + time_before: La elección está lista para comenzar. Tienes que esperar hasta %{hours} horas antes de la hora de inicio (%{start_time}) para comenzar el periodo de votación. + invalid: Hubo un problema al iniciar el período de votación. + requirements: + time_before: La elección comenzará pronto. Puede iniciar el periodo de votación manualmente, o se iniciará automáticamente antes de la hora de inicio, a las %{start_time}. + submit: Empezar período de votación + success: La solicitud de período de votación se ha enviado correctamente al "Bulletin Board". + title: Listo para empezar + processing: Procesando... + results_published: + answer: Respuesta + not_selected: No seleccionada + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Enviar + title: Resultados publicados + tally_ended: + answer: Respuesta + not_selected: No seleccionado + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Publicar resultados + success: La solicitud de publicación de resultados se envió correctamente al "Bulletin Board". + title: Resultados calculados + tally_started: + continue: Continuar + invalid: Hubo un problema al reportar la ausencia de la garante. + mark_as_missing: Marcar como ausente + mark_as_missing_description: Todos los garantes deberían participar en este proceso, pero si uno de ellos no puede tomar parte en él, puedes marcarlo como ausente. + success: La notificación de ausencia de la garante se ha enviado con éxito al "Bulletin Board". + tally_completion: El proceso se completará cuando todos los garantes estén activos o marcados como ausentes. Se requiere al menos %{quorum} garantes para completar el proceso. + title: Proceso de recuento + undo_mark_as_missing: Un garante marcado como ausente por error podrá participar antes de completar el proceso. Pueden proceder como de costumbre y la marca de ausente será ignorada. + vote: + errors: + time_after: La elección aún está en curso. Tienes que esperar hasta la hora de finalización (%{end_time}) para terminar el periodo de votación. + invalid: Hubo un problema al terminar el período de votación. + requirements: + time_after: La elección ha terminado. Puedes terminar el período de votación manualmente, o terminará automáticamente en unos minutos. + submit: Finalizar período de votación + success: La solicitud para finalizar el período de votación se ha enviado correctamente al "Bulletin Board". + title: Periodo de votación + vote_ended: + invalid: Hubo un problema al iniciar el recuento. + submit: Comenzar recuento + success: La solicitud de inicio del recuento se envió correctamente al "Bulletin Board". + text: La votación ha terminado. Puedes iniciar el recuento ahora. + title: Periodo de votación finalizado + vote_stats: + no_vote_statistics_yet: Aún no hay estadísticas de voto + title: Estadísticas de voto + voters: Votantes + votes: Votos + trustees_participatory_spaces: + actions: + disable: Deshabilitar + enable: Considerar + create: + exists: Existe garante para este espacio participativo. + invalid: Se ha producido un error al crear una garante. + success: Garante creada correctamente. + delete: + invalid: Se ha producido un error al eliminar esta garanta. + success: Garante eliminada correctamente. + form: + select_user: Seleccionar usuario + index: + title: Garantes + new: + create: Crear garante + title: Nuevo garante + update: + invalid: Se ha producido un error al actualizar a %{trustee} como garante. + success: Garante %{trustee} actualizada correctamente. + admin_log: + election: + create: "%{user_name} creó la elección %{resource_name} de %{space_name}" + delete: "%{user_name} eliminó la elección %{resource_name} de %{space_name}" + end_vote: "%{user_name} terminó el período de votación para la elección %{resource_name} de %{space_name} en el Bulletin Board" + publish: "%{user_name} publicó la elección %{resource_name} de %{space_name}" + publish_results: "%{user_name} publicó los resultados para la elección %{resource_name} de %{space_name} en el Bulletin Board" + report_missing_trustee: "%{user_name} reportó a %{trustee_name} como garante ausente durante el recuento de la elección %{resource_name} de %{space_name} en el Bulletin Board" + setup: "%{user_name} creó la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_key_ceremony: "%{user_name} comenzó la ceremonia claves para la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_tally: "%{user_name} comenzó el recuento de la elección %{resource_name} de %{space_name} en el Bulletin Board" + start_vote: "%{user_name} comenzó el período de votación para la elección %{resource_name} de %{space_name} en el Bulletin Board" + unpublish: "%{user_name} ha despublicado el %{resource_name} de la elección de %{space_name}" + update: "%{user_name} actualizó la elección %{resource_name} de %{space_name}" + trustee: + create: "%{user_name} asignó al usuario %{trustee_user} como garante" + connection: + failed: + modal: + close: Cerrar + communication_lost: Desafortunadamente, parece que la comunicación con el servidor de votación (Bulletin Board) se ha perdido.
    Puede ser que la conexión a Internet esté averiada o que el servidor de destino esté demasiado ocupado.
    Puedes intentarlo más tarde o ponerte en contacto con el servicio de asistencia si este problema persiste. + generic_error: Desafortunadamente, se ha producido un error desconocido. Es probable que tu navegador no esté soportado o que estés usando el modo "incógnito" o "privado" que no es compatible. + title: Algo salió mal + election_m: + badge_name: + finished: Finalizada + ongoing: Activa + upcoming: Próximas + end_date: Termina + footer: + remaining_time: + one: "%{count} hora %{minutes} minutos restantes para votar." + other: "%{count} horas %{minutes} minutos restantes para votar." + view: Ver + vote: Votar + label: + date: Fechas + questions: Preguntas %{count} + start_date: Empieza + unspecified: Sin especificar + elections: + count: + elections_count: + one: "%{count} votación" + other: "%{count} votaciones" + election_log: + chained_hash: El Hash encadenado de este mensaje + complete: Completar + creation_description: + complete: La elección se creó y se configuró con éxito en el Bulletin Board. + not_created: La elección no se ha creado todavía. + creation_title: Elección creada + description: Este es el registro de la elección, donde puedes comprobar el estado de cada paso, por ejemplo, cuándo se ha creado, si se ha completado el proceso de recuento y cuándo se ha cerrado la elección. + download: Descargar + key_ceremony_description: + complete: La ceremonia de claves se ha completado. Cada garante tiene claves válidas y ha descargado las claves de copia de seguridad necesarias. + not_started: La ceremonia de claves aún no ha comenzado. + started: La ceremonia de claves ha comenzado, pero aún no ha terminado. + key_ceremony_title: Ceremonia de claves + not_available: No disponible todavía + not_created: No creado + not_ready: No está listo + not_started: No iniciado + published: Publicado + results_description: + not_published: Los resultados aún no se han publicado. + published: Los resultados se han publicado. + results_title: Resultados + started: Iniciado + tally_description: + finished: El proceso de recuento ha terminado. + not_started: El proceso de recuento aún no ha comenzado. + started: El proceso de recuento ha comenzado. + tally_title: Proceso de recuento + title: Registro de la elección + unpublished: Despublicada + verifiable_results: + checksum: 'Suma de comprobación SHA256 del archivo:' + description: + not_ready: El archivo verificable de la elección y la suma de comprobación SHA256 aún no están disponibles. En cuanto se publiquen los resultados, podrás verificar esta elección. + ready: 'Aquí tienes la opción de verificar la elección. En primer lugar, tienes que descargar el archivo y asegurarte de que no se ha corrompido. Para ello, ejecuta el siguiente comando y comprueba que la salida coincide con la suma de comprobación:' + how_to_verify: 'Una vez que hayas descargado el archivo y te hayas asegurado de que está bien, puedes proceder a ejecutar el verificador universal. Clona este repositorio y, desde la carpeta raíz, ejecuta el siguiente comando:' + title: Verificar resultados de la elección + verifiable_file: 'Archivo verificacle de la elección:' + verify: Verificar elección + vote_description: + finished: El proceso de votación ha concluido. + not_started: El proceso de votación aún no ha comenzado. + started: El proceso de votación ha comenzado. + vote_title: Proceso de votación + filters: + active: Activas + all: Todas + date: Fecha + finished: Finalizadas + upcoming: Próximas + preview: + available_answers: 'Respuestas disponibles:' + description: 'Estas son las preguntas que encontrarás en el proceso de votación:' + title: Preguntas de la elección + results: + description: 'Estos son los resultados de la votación, para cada pregunta:' + percentage: "%{count}%" + selected: Seleccionado + title: Resultados de la elección + votes: + one: "%{count} voto" + other: "%{count} votos" + show: + action_button: + change_vote: Cambia tu voto + vote: Empezar a votar + vote_again: Votar de nuevo + callout: + already_voted: Ya has votado en esta elección. Puedes cambiar tu voto o verificarlo. + pending_vote: Se está emitiendo tu voto en el servidor. + vote_rejected: No ha sido posible verificar tu voto. Por favor, hazlo de nuevo. + election_log: Registro de la elección + preview: Previsualizar + verify: + already_voted: '¿Ya has votado?' + verify_here: Comprueba tu voto aquí. + will_verify: Podrás verificar tu voto una vez que comience la elección. + voting_period_status: + finished: La votación empezó el %{start_time} y terminó el %{end_time} + ongoing: 'Votación activa hasta: %{end_time}' + upcoming: La votación empieza el %{start_time} + feedback: + answer: + invalid: Hubo un problema al enviar tu feedback. + spam_detected: Hubo un problema respondiendo al formulario. Tal vez has sido demasiado rápido, ¿puedes intentarlo de nuevo? + success: Feedback enviado con éxito. + models: + answer: + fields: + proposals: Propuestas + selected: Seleccionada + title: Título + votes: Votos + election: + fields: + bb_status: Estado del tBulletin Board + end_time: Termina el + start_time: Empieza el + title: Título + verifiable_results_file_hash: Suma de comprobación SHA256 del archivo + verifiable_results_file_url: Archivo de verificación de la elección + question: + fields: + answers: Respuestas + max_selections: Número máximo de elementos a seleccionar + title: Título + trustees_participatory_space: + fields: + considered: considerado + email: Correo electrónico + inactive: inactivo + name: Nombre + notification: Notificación enviada el + public_key: Clave publica + status: Estado + orders: + label: Ordenar votaciones por + older: Más antigua + recent: Reciente + trustee_zone: + elections: + backup_modal: + description: Esta elección se está creando en el Bulletin Board. Es muy importante que cada garante que participe en ella cree una copia de seguridad de estas claves y las almacene en un lugar seguro. Después, el proceso continuará. + download_election_keys: Descargar claves + title: Copia de seguridad de claves para la elección %{election} + key_ceremony_steps: + back: Volver + description: Esta elección está siendo creada en el Bulletin Board. Para completar este proceso, se necesita tu participación como garante. + keys: + create_election: Generación de claves + key_ceremony: + joint_election_key: Generación de claves conjuntas + step_1: Publicación de claves + list: + status: Estado + task: Tarea + process_warning: Una vez iniciado el proceso, no debes salir de esta página hasta que el proceso termine. Tardará varios minutos, ya que todos los garantes deben estar conectados para completarlo. + start: Empezar + status: + completed: Completada + pending: Pendiente + processing: Procesando + title: Crear claves para la elección %{election} + restore_modal: + description: El Bulletin Board tiene tu información como garante de esta elección. Para continuar el proceso, primero sube el archivo de copia de seguridad generado durante la sesión anterior. + title: Restaurar claves para la elección %{election} + upload_election_keys: Subir claves de elección + tally_started_steps: + back: Atrás + description: Los resultados de esta elección están siendo calculados en el Bulletin Board. Para completar este proceso, se necesita tu participación como garante. + keys: + end_tally: Recuento finalizado + tally: + cast: Envío del recuento + share: Compartición del recuento + list: + status: Estado + task: Tarea + process_warning: Una vez iniciado el proceso, no debes salir de esta página hasta que el proceso termine. Tardará algunos minutos, ya que todas las garantes deben estar conectados para completarlo. + start: Empezar + status: + completed: Completado + pending: Pendiente + processing: Procesando + title: Recuento para %{election} + update: + error: El estado de la elección no se había actualizado. + success: 'El estado de la elección es: %{status}.' + menu: + trustee_zone: Zona del garante + no_bulletin_board: + body: Se requiere un Bulletin Board configurado para esta sección. Contacta con el Administrador para más detalles. + title: Lo sentimos, el Bulletin Board aún no está configurado. + trustees: + show: + elections: + list: + action_required: + 'false': 'No' + name: '¿Acción requerida?' + 'true': Realizar acción + bb_status: Estado + election: Elección + voting_period: Período de votación + no_elections: Actualmente no se te ha asignado ninguna acción como garante. Recibirás una notificación cuando sea el momento de actuar en las diferentes fases. + title: Elecciones + identification_keys: + cancel: Cancelar + generate: Generar claves de identificación + generate_error: Hubo un error al generar las claves de identificación. + generate_legend: Necesitas generar un par de claves de identificación para participar en las elecciones como garante. + generate_legend_1: Después de pulsar el botón debes descargar el archivo con las claves de identificación generadas. + generate_legend_2: Copia el archivo descargado a un dispositivo USB limpio + generate_legend_3: Asegúrate de que tu equipo no tenga una copia del archivo (por ejemplo, comprueba las carpetas de Descargas y de Escritorio). + generate_legend_4: Haz otra copia del archivo en un dispositivo externo diferente y guárdalo en un lugar muy seguro. + submit: Enviar + submit_legend: Después de seguir todos los pasos explicados anteriormente, completa el proceso enviando la clave de identificación pública al servidor. + submit_title: Enviar la clave pública de identificación + title: Claves de identificación del garante + upload: Sube tus claves de identificación + upload_error: + invalid_format: El archivo subido no contiene ninguna clave de identificación. + invalid_key: Las claves de identificación en el archivo subido no se pueden cargar. + invalid_public_key: Las claves de identificación en el archivo subido no coinciden con la clave pública de identificación almacenada. + upload_legend: En el servidor constan tus claves públicas de identificación, pero tu navegador todavía no las tiene. Necesitas importar el archivo con tus claves de identificación a tu ordenador desde la copia de seguridad que creaste después de generarlas. + not_supported_browser_description: Parece que estás usando un navegador que no puede ser utilizado para actuar como garante. Asegúrate de que estás usando la versión más reciente de tu navegador, o intenta utilizar cualquier otro de los navegadores más populares para poder completar tus tareas como garante. + not_supported_browser_title: Actualiza el navegador para actuar como garante + safari_warning_description: Parece que estás usando Safari, que no está soportado para actuar como garante o para cifrar un voto (esto se debe a las restricciones de memoria que le impone Apple). Esto podría resolverse en el futuro mediante un cambio de política por parte de Apple o la futura optimización de Decidim Elecciones. Mientras tanto, por favor, utiliza otro navegador. + safari_warning_title: Navegador Safari detectado + trustee_role_description: + with_keys: Has sido asignada como garante en algunas de las elecciones celebradas en esta plataforma. + without_keys: Has sido asignada para actuar como Garante. Por favor, genera y sube tus claves de identificación. + update: + success: Tu clave pública de identificación fue guardada con éxito. + votes: + ballot_decision: + audit: ( Auditar papeleta ) + back: Comenzar de nuevo el proceso de votación + ballot_hash: 'El indentificador de la papeleta es:' + cast: Deposita la papeleta para terminar tu voto + description_html: Aquí tienes las opciones de emitir tu boleta para que sea contada correctamente o puedes auditar que tu boleta ha sido cifrada correctamente. Si quieres auditar la votación, por favor, lee las instrucciones sobre cómo proceder. + header: 'La papeleta se ha cifrado: envíala o audítala' + casting: + header: Se está emitiendo el voto... + text: Tu papeleta se está depositando en la urna. + confirm: + answer_number: respuesta %{number} + confirm: Confirmar + edit: editar + header: Confirma tu voto + intro: Aquí tienes un resumen del voto que estás a punto de emitir.
    Por favor, confirma tu voto o edita tus respuestas. + nota_option: En blanco + confirmed: + back: Volver a las votaciones + experience: '¿Cómo valoras la experiencia?' + feedback: Danos tu opinión + header: Voto confirmado + lead: '¡Tu voto se ha emitido!' + text: 'Puedes comprobar que tu voto se ha añadido correctamente a la urna con el siguiente identificador: ' + verify_link: Para comprobarlo, copia el identificador y pégalo en la página de verificación de voto + create: + error: Hubo un problema al emitir el voto. Por favor, inténtalo de nuevo. + encrypting: + header: Se está cifrando el voto... + text: Se está cifrando tu papeleta para garantizar el secreto de voto. + failed: + header: Voto fallido + lead: '¡Tu voto no ha sido emitido!' + text: Algo salió mal, por favor inténtalo de nuevo. + try_again: Inténtalo de nuevo + header: + ballot_decision: Emitir o auditar tu voto + confirm: Confirma tu voto + election: Elección + register: Regístrate + vote_for: Vota por %{title} + messages: + invalid_token: Tu sesión en la cabina de votación no es válida. Intenta votar de nuevo. + not_allowed: En este momento no puedes votar en esta votación. + modal: + close: Cerrar + proposal_header: 'Propuestas:' + new: + answer_choices: Puedes seleccionar hasta %{choices} respuestas + more_information: Más información + nota_option: En blanco / Ninguna de las anteriores + preview_alert: Esta es una vista previa de la cabina de votación. + question_steps: Pregunta %{current_step} de %{total_steps} + selections: "Seleccionada
    %{selected} de %{max_selections}" + onboarding_modal: + create_account: Crear Cuenta + description: '¿Quieres crear una nueva cuenta en la plataforma? Podrás participar en los procesos y ser parte activa de la organización.' + no_account: No, gracias. + title: '¿Eres nueva en la plataforma?' + update: + error: Ha habido un problema al actualizar el estado del voto. Vuelve a intentarlo. + verify: + content: + heading: Verifica tu voto + info: Este verificador comprueba que tu voto, identificado con una cadena de texto cifrada, ha sido emitido correctamente y está dentro de la urna. + error: + header: Voto no encontrado! + info: El código de voto no se ha encontrado en la urna de %{link}, inténtelo de nuevo. + form: + back: Volver a la plataforma + submit: Comprobar + vote_identifier: 'Código identificador:' + vote_identifier_help: Este es el identificador que se te dio después de emitir tu voto (no es el código para entrar la cabina de votación). + header: + title: Verifica tu voto + success: + header: '¡Voto localizado!' + info: Tu voto cifrado está en la urna de %{link}. + voting_step: + back: Atrás + continue: Siguiente + warnings: + empty_filters: No hay ninguna elección con este criterio. + no_elections: No hay ninguna elección programada. + no_scheduled_elections: Actualmente no hay elecciones programadas, pero puedes ver un listado de las anteriores. + events: + elections: + election_published: + email_intro: 'La votación %{resource_title} ya está activa en %{participatory_space_title}. Puedes verla desde esta página:' + email_outro: Has recibido esta notificación porque estás siguiendo %{participatory_space_title}. Puedes dejar de recibir notificaciones siguiendo el enlace anterior. + email_subject: La votación %{resource_title} en %{participatory_space_title} ya está activa. + notification_title: La votación %{resource_title} ya está activa en %{participatory_space_title}. + trustees: + new_election: + email_intro: Has sido añadido como garante para la elección de %{resource_title}. + email_outro: Has recibido esta notificación porque has sido añadido como garante para la elección %{resource_title}. + email_subject: Eres garante para la elección %{resource_title}. + notification_title: 'Has sido asignada para actuar como Garante en la Elección: %{resource_title}. Por favor, realiza la ceremonia de claves para poner en marcha la elección.' + new_trustee: + email_intro: Un administrador te ha añadido como garante para %{resource_name}. Debes crear tu clave pública en tu zona de garantes + email_outro: Has recibido esta notificación porque has sido añadido como garante para %{resource_name}. + email_subject: Eres garante de %{resource_name}. + notification_title: Te han añadido para actuar como garante en %{resource_name} para algunas elecciones que tendrán lugar en esta plataforma.
    Se te requerirá para llevar a cabo algunas tareas. De momento, por favor genera tus claves de identificación. + start_tally: + email_intro: El período de votación para la elección %{resource_title} ha finalizado. Ahora, por favor, realiza el recuento de las elecciones para publicar los resultados definitivos. + email_outro: Has recibido esta notificación porque has eres una de las garantes en la elección %{resource_title}. + email_subject: El proceso de recuento para las elecciones %{resource_title} ha comenzado. + notification_title: El período de votación para las elecciones %{resource_title} ha finalizado. Ahora, por favor, realiza el recuento de las elecciones para publicar el resultado final. + votes: + accepted_votes: + email_intro: '¡Tu voto ha sido aceptado! Utilizando tu token de voto: %{encrypted_vote_hash}, puedes verificar tu voto aquí.' + email_outro: Has recibido esta notificación porque has votado en la elección %{resource_name}. + email_subject: Tu voto para %{resource_name} ha sido aceptado. + notification_title: 'Tu voto ha sido aceptado. Verifica tu voto aquí usando tu token de voto: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Se te ha asignado como %{role} del punto de votación %{polling_station_name} en %{resource_title}. Puedes administrar el punto de votación desde el espacio Zona de gestores de mesa. + email_outro: Has recibido esta notificación porque has sido asignado como %{role} de %{polling_station_name}. + email_subject: Eres %{role} del punto de votación %{polling_station_name}. + notification_title: Eres %{role} del punto de votación %{polling_station_name} en la votación %{resource_title}. + send_access_code: + instruction: 'Ya tienes el código de acceso que has pedido: %{access_code}. Con esto ya podrás participar en %{voting}.' + subject: Tu código de acceso para participar en %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Una votación es un espacio que os permite hacer una pregunta clara al conjunto de miembros de una organización, hacer un llamamiento a participar en una votación, generar y ordenar el debate a favor o en contra de una respuesta. cunado llega la fecha de la votación, podéis votar y publicar el resultado.

    Las votaciones pueden ser prácticamente de cualquier aspecto que afecte a la organización. Algunos ejemplos serían: cambiar el nombre o el logotipo de la organización ofreciendo diversas alternativas, decidir si pasar a formar parte de una organización más grande o no, validar o desestimar un nuevo plan estratégico o el resultado de un gruipo de trabajo, o definir si los cargos deberían tener una duración máxima de uno, dos o tres mandatos.

    " + page: "

    Una votación es un espacio que os permite hacer una pregunta clara al conjunto de miembros de una organización, hacer un llamamiento a participar en una votación, generar y ordenar el debate a favor o en contra de una respuesta. cunado llega la fecha de la votación, podéis votar y publicar el resultado.

    Las votaciones pueden ser prácticamente de cualquier aspecto que afecte a la organización. Algunos ejemplos serían: cambiar el nombre o el logotipo de la organización ofreciendo diversas alternativas, decidir si pasar a formar parte de una organización más grande o no, validar o desestimar un nuevo plan estratégico o el resultado de un gruipo de trabajo, o definir si los cargos deberían tener una duración máxima de uno, dos o tres mandatos.

    " + title: '¿Qué son las votaciones?' + menu: + votings: Votaciones + participatory_spaces: + related_elections: + see_all: Ver todas las elecciones + statistics: + elections_count: Elecciones + votings_count: Votaciones + votings: + admin: + ballot_styles: + create: + error: Ha habido un problema al crear el estilo de papeleta. + success: Se ha creado correctamente el estilo de papeleta. + destroy: + invalid: Ha habido un problema al suprimir el estilo de papeleta. + success: Se ha eliminado correctamente el estilo de papeleta. + edit: + title: Editar el estilo de papeleta + update: Actualizar + form: + code_help: 'Pista: el código es el vínculo entre el censo y el estilo de papeleta. Al actualizar los datos del censo, a cada entrada se le asigna un estilo de papeleta que coincida con el código.' + election: Elección + questions: Preguntas para el estilo de papeleta + questions_help: 'Pista: selecciona las preguntas del componente de elecciones para presentarlas a las votantes asignadas a este estilo de papeleta.' + index: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + edit: Editar + new: Añadir estilo de papeleta + title: Acciones + associated_census_data: Entradas asociadas al censo + explanation_callout: Un estilo de papeleta especifica qué preguntas se presentarán a las votantes en la cabina. En un estilo de papeleta, puedes elegir qué preguntas del componente de elecciones pertenecen a esa papeleta. El código de estilo de la papeleta se utiliza para hacer coincidir a una votante del censo con la papeleta que se le debe mostrar en la cabina. No crees ningún estilo de papeleta si quieres que simpre se muesten todas las preguntas. + title: Estilos de papeleta + new: + create: Crear + title: Crear estilo de papeleta + update: + invalid: Ha habido un problema al actualizar el estilo de papeleta. + success: Se ha creado correctamente el estilo de papeleta. + content_blocks: + attachments_and_folders: + name: Archivos y carpetas de la votación + header: + name: Cabecera de la votación + highlighted_votings: + max_results: Cantidad máxima de elementos a mostrar + html_block_1: + name: Bloque html 1 de la votación + html_block_2: + name: Bloque html 2 de la votación + html_block_3: + name: Bloque html 3 de la votación + main_data: + name: Titulo y descripción + metrics: + name: Métricas de la votación + polling_stations: + name: Puntos de votación + related_elections: + name: Elecciones + stats: + name: Estadísticas de la votación + timeline: + name: Calendario de la votación + index: + published: Publicada + unpublished: Despublicada + menu: + votings: Votaciones + votings_submenu: + attachment_collections: Carpetas + attachment_files: Archivos + attachments: Archivos adjuntos + ballot_styles: Estilos de papeleta + census: Censo + components: Componentes + info: Acerca de esta votación + landing_page: Página de inicio + monitoring_committee: Comité de seguimiento + monitoring_committee_election_results: Validar resultados + monitoring_committee_members: Miembros + monitoring_committee_polling_station_closures: Validar certificados + monitoring_committee_verify_elections: Verificar elecciones + polling_officers: Gestores de mesa + polling_stations: Puntos de votación + see_voting: Ver la votación + models: + ballot_style: + fields: + code: Código + monitoring_committee_member: + fields: + email: Correo electrónico + name: Nombre + polling_officer: + fields: + email: Correo electrónico + name: Nombre + polling_station: Punto de votación (rol) + polling_station: + fields: + address: Dirección + polling_station_managers: Administradores + polling_station_president: Presidente + title: Título + voting: + fields: + created_at: Creado el + published: Publicada + title: Título + monitoring_committee_election_results: + actions: + title: Acciones + view: Ver + index: + title: Elige una elección para ver los resultados + results: + bulletin_board: Bulletin Board + election_totals: Totales de la elección + polling_stations: Puntos de votación + result_types: + blank_answers: Respuestas en blanco + blank_ballots: Papeletas en blanco + null_ballots: Papeletas nulas + total_ballots: Total de papeletas + valid_ballots: Papeletas válidas + selected: Seleccionado + title: Resultados para la elección %{election_title} + totals: Totales + show: + change_election: Cambiar elección + publish_results: Publicar resultados + publishing: Publicando resultados... + update: + invalid: Se ha producido un error al publicar los resultados. + rejected: La publicación de los resultados fue rechazada por el Bulletin Board. Inténtalo de nuevo o ponte en contacto con el administrador del sistema. + success: Los resultados se han publicado correctamente. + monitoring_committee_members: + create: + invalid: Hubo un problema al crear esta miembro de la comisión de seguimiento. + success: Miembro del comisión de seguimiento creada con éxito. + destroy: + invalid: Hubo un problema al eliminar esta miembro de la comisión de seguimiento. + success: Miembro de la comisión de seguimiento eliminada con éxito. + form: + existing_user: Participante existente + non_user: Invitar nueva participante + select_user: Buscar por correo electrónico, nombre o alias + user_type: Tipo de participante + index: + title: Comité de seguimiento + new: + create: Crear + title: Crear miembro del comité de seguimiento + monitoring_committee_polling_station_closures: + actions: + title: Acciones + validate: Validar + view: Ver + closures: + change_election: Cambiar elección + signed: '¿Firmado?' + title: Puntos de votación para la elección %{election_title} + validated: '¿Validado?' + edit: + change_polling_station: Volver a los puntos de votación + monitoring_committee_notes: Observaciones + monitoring_committee_notes_placeholder: Reporta cualquier incidencia aquí + title: Resultados para la elección %{election_title} en el punto de votación %{polling_station_title} + elections: + title: Elige una elección para validar + show: + change_polling_station: Volver a los puntos de votación + monitoring_committee_notes: Observaciones del Comité de Seguimiento + validate: + error: Se ha producido un error al validar el cierre. + success: El cierre se ha validado correctamente. + monitoring_committee_verify_elections: + index: + download: Descargar + how_to_checksum: 'Para asegurarse de que el archivo descargado no ha sido dañado o manipulado durante el proceso de descarga, ejecuta el siguiente comando en tu consola y comprueba que la salida coincida con la suma de verificación reportada arriba:' + how_to_download: Para verificar una elección, descarga tu archivo verificable de la tabla de arriba. + how_to_run_verifier: 'Una vez que hayas descargado el archivo y te hayas asegurado de que está bien, puedes proceder a ejecutar el verificador universal. Clona este repositorio y, desde la carpeta raíz, ejecuta el siguiente comando:' + how_to_title: Cómo verificar la validez de una elección + not_available: No disponible todavía + title: Elecciones + polling_officers: + create: + invalid: Hubo un problema al crear esta oficial de votación. + success: Oficial de votación creada con éxito. + destroy: + invalid: Hubo un problema al eliminar esta oficial de votación. + success: Oficial de votación eliminada con éxito. + form: + existing_user: Participante existente + non_user: Invitar nueva participante + select_user: Buscar por correo electrónico, nombre o alias + user_type: Tipo de participante + index: + role_manager: administrador + role_president: presidente + title: Gestores de mesa + new: + create: Crear + title: Crear gestor de mesa + polling_officers_picker: + choose_polling_officers: Elegir gestores de mesa + no_polling_officers: Ninguna gestora de mesa coincide con tus criterios de búsqueda o no hay ninguna. + polling_stations: + create: + invalid: Hubo un problema al crear este punto de votación. + success: Punto de votación creado correctamente. + destroy: + invalid: Hubo un problema al eliminar este punto de votación. + success: Punto de votación eliminado correctamente. + edit: + title: Editar punto de votación + update: Actualizar punto de votación + form: + address_help: 'Dirección: usada por Geocoder para encontrar la ubicación' + location_help: 'Ubicación: mensaje dirigido a los votantes con el lugar exacto del punto de votación' + location_hints_help: 'Sugerencias de ubicación: información adicional. Ejemplo: el piso del edificio donde se encuentra el punto de votación.' + polling_station_managers_help: 'Administradores de mesa: los oficiales que actuarán como administradores de los puntos de votación. Asegúrate de que los oficiales ya han sido creados en Gestores de mesa y que no están ya asignados a otro punto de votación' + polling_station_president_help: 'Presidencia de mesa: oficiales que realizarán las funciones de presidencia en el punto de votación. Asegúrate de que están habilitadas como gestoras de mesa y que ya no están asignadas a otro punto de votación.' + select_president: Selecciona un gestor de mesa como presidente del punto de votación + index: + title: Puntos de votación + new: + create: Crear + title: Crear punto de votación + update: + invalid: Hubo un problema al actualizar este punto de votación. + success: Punto de votación actualizado correctamente. + titles: + votings: Votaciones + votings: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Eliminar + new_voting: Nuevo espacio de votación + create: + invalid: Hubo un problema al crear esta votación. + success: Votación creada con éxito. + edit: + add_election_component: No tienes ninguna elección configurada para esta votación. Por favor añádela en la sección de componentes. + assign_missing_officers: Hay puntos de votación sin presidencia y/o administradoras. Asígnalas desde la sección de puntos de votación. + update: Actualizar + form: + banner_image: Imagen de cabecera + census_contact_information: Datos de contacto del censo + census_contact_information_help: Esta información de contacto es para una participante que quiere reportar problemas con el censo. Puede ser una dirección de correo electrónico, un formulario de contacto en otro sitio, una encuesta para visitantes, etc. + introductory_image: Imagen de presentación + promoted: Destacada + select_a_voting_type: Por favor, selecciona un tipo de votación + show_check_census_help: Especifica si mostrar el enlace "¿Puedo votar?" en el menú público de votaciones. + slug: Nombre corto de URL + slug_help_html: 'Los textos cortos de URL se utilizan para generar las URL que apuntan a esta votación. Sólo acepta letras, números y guiones, y debe comenzar con una letra. Ejemplo: %{url}' + title: Título + voting_type: + hybrid: Hibrida + in_person: Presencial + online: En línea + voting_type_label: Tipo de votación + new: + create: Crear + title: Nueva votación + update: + invalid: Hubo un problema al actualizar esta votación. + success: Votación actualizada correctamente. + admin_log: + ballot_style: + create: "%{user_name} creó un estilo de papeleta con código %{ballot_style_code} en el espacio %{space_name}" + delete: "%{user_name} eliminó el estilo de la papeleta con el código %{ballot_style_code} en el espacio %{space_name}" + update: "%{user_name} actualizó el estilo de la papeleta con el código %{ballot_style_code} en el espacio %{space_name}" + census: + create: "%{user_name} creó el censo para el espacio %{space_name}" + delete: "%{user_name} eliminó el censo del espacio %{space_name}" + update: "%{user_name} actualizó el censo del espacio %{space_name}" + monitoring_committee_member: + create: "%{user_name} asignó al usuario %{monitoring_committee_member_user} como miembro del comité de seguimiento en el espacio %{space_name}" + delete: "%{user_name} desasignó el usuario %{monitoring_committee_member_user} como miembro del comité de seguimiento en el espacio %{space_name}" + polling_officer: + create: "%{user_name} asignó al usuario %{polling_officer_user} como gestor de mesa en el espacio %{space_name}" + delete: "%{user_name} desasignó al usuario %{polling_officer_user} como gestor de mesa en el espacio %{space_name}" + polling_station: + create: "%{user_name} creó el punto de votación %{resource_name} en el espacio %{space_name}" + delete: "%{user_name} eliminó el punto de votación %{resource_name} en el espacio %{space_name}" + update: "%{user_name} actualizó el punto de votación %{resource_name} en el espacio %{space_name}" + voting: + create: "%{user_name} creó la votación %{resource_name}" + publish: "%{user_name} publicó la votación %{resource_name}" + unpublish: "%{user_name} despublicó la votación %{resource_name}" + census: + admin: + census: + create: + invalid: Se ha producido un error al subir el censo, por favor inténtalo de nuevo más tarde. + invalid_csv_header: Los encabezados CSV faltan o no son correctos - por favor lee las instrucciones cuidadosamente. + creating_data: + info_message: "Por favor espera, procesadas %{processed_count} de %{raw_count} filas del fichero %{file} (esto puede tardar algunos minutos)." + delete: + button: Borrar todos los datos del censo + confirm: Eliminar todo el censo no se puede deshacer. ¿Seguro que quieres continuar? + destroy: + error: Se ha producido un error al eliminar el censo, por favor inténtalo de nuevo más tarde. + success: Datos del censo eliminados. + export_access_codes: + button: Exportar códigos de acceso de la votación + callout: Ahora puedes exportar los códigos de acceso. Esto sólo se puede hacer una vez. Una vez que inicies la exportación, recibirás un correo electrónico con las instrucciones en %{email} + confirm: Solo puedes exportar los códigos de acceso una vez. Asegúrate de tener acceso a la cuenta de correo electrónico %{email}. + file_not_exist: Este archivo no existe. + launch_error: Problema al iniciar la exportación de códigos de acceso. + launch_success: Se ha iniciado la exportación de los códigos de acceso. En breve recibirás un correo en %{email}. + exporting_access_codes: + info_message: "Por favor, espera, se está preparando la exportación, la recibirás en breve en %{email} (esto puede tardar unos minutos)." + freeze: + callout: El censo está cerrado y no se puede modificar. + help_html: | + Los datos del censo Se han subido correctamente, los códigos se han generado y exportado con éxito.
    Ahora estás lista para comenzar la elección.
    + Utiliza el CSV exportado, que incluye los códigos individuales para distribuirlo al censo utilizando tus propios medios, o active la pestaña "¿Puedo votar?", para permitir que cualquiera recupere este código usando sus propios datos de censales. + generate_access_codes: + button: Generar códigos de acceso de la votación + callout: Ahora puedes generar los códigos de acceso. Ten en cuenta que después de generarlos ya no podrás modificar más el censo. + confirm: Si continúas, ya no podrás modificar el censo. + info_message_all: "Se han importado las filas correctamente del fichero %{file} (%{raw_count} de %{data_count})." + info_message_warn: Por favor, comprueba que no falten datos, ya que se han creado %{data_count} registros, pero el fichero que se ha subido (%{file}) tenía %{raw_count} filas. + launch_error: Ha habido un problema al generar los códigos de acceso + launch_success: Se ha iniciado la generación de códigos. + start_over: Por favor, elimine el censo actual y vuelva a empezar con un archivo CSV apropiado con filas válidas. + generating_access_codes: + info_message: "Espera, por favor<, los códigos de acceso a la votación se están generando (esto puede tardar unos minutos)..." + new: + file_help: + explanation: 'Instrucciones para el archivo:' + message_1: Sólo se permiten archivos CSV (.csv). + message_2: El separador entre columnas debe ser un punto y coma (";"). + has_ballot_styles_message: Has configurado los estilos de papeleta. Por favor, asegúrate de que el campo "%{ballot_style_code_header}" en el CSV corresponde al código de estilo de papeleta deseado. + info_message: "Aún no hay ningún censo. Por favor, utiliza el siguiente formulario para crearlo importando un archivo CSV." + missing_ballot_styles_message: 'No hay ningún estilo de papeleta para esta votación todavía. Si quieres tener preguntas condicionales (presentar al votante diferentes preguntas dependiendo de, p.ej. el distrito/región de residencia), debes configurar los estilos de papeleta antes de importar el censo. Si quieres presentar a todos los votantes las mismas preguntas, puedes continuar con el procedimiento de importación del censo.' + submit: Enviar CSV + title: Crear el censo + show: + heading: Censo del espacio de votación + upload_info: + csv_example_with_ballot_style: 'Un ejemplo del archivo con estilos de papeleta:' + csv_example_without_ballot_style: 'Un ejemplo del archivo sin estilos de papeleta:' + csv_header_after: No incluyas el último campo ("%{ballot_style_code_header}") si no necesitas preguntas condicionales/estilos de papeleta + csv_header_before: 'El censo debe ser un archivo CSV con la siguiente cabecera:' + document_types: + identification_number: Número de identificación + passport: Pasaporte + export_mailer: + access_codes_export: + click_button: 'Haz clic en el siguiente enlace para descargar tus datos.
    El archivo estará disponible hasta %{date}.
    Necesitarás 7-Zip (para Windows), Keka (para MacOS) o PeaZip (para Linux) para abrirlo. Contraseña: %{password}' + download: Descargar + subject: La exportación de los códigos de acceso de votación para %{voting_title} está disponible + vote_flow: + already_voted_in_person: Esta participante ya ha votado presencialmente y no tiene derecho a voto. + datum_not_found: Los datos introducidos no coinciden con ninguna votante. + content_blocks: + highlighted_votings: + name: Votaciones destacadas + landing_page: + polling_stations: + heading: Puntos de votación + no_polling_stations: Aún no hay puntos de votación. + monitoring_committee_members: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + new: Nuevo miembro + title: Acciones + pages: + home: + highlighted_votings: + active_spaces: Votaciones activas + see_all_spaces: Ver todas las votaciones + polling_officer_zone: + closures: + back_to_polling_stations: Volver a los puntos de votación + certify: + add_photos: Añadir fotos + edit_photos: Editar fotos + error: Se ha producido un error al adjuntar el certificado, por favor inténtalo de nuevo. + heading: Recuento de votos - Subir certificado + info_text: Por favor, sube una foto del certificado de cierre electoral. + submit: Subir el certificado + success: Certificado subido correctamente. + upload_photos: Sube una foto del Certificado de Cierre Electoral + completed: + sub_heading: Este recuento ha sido certificado y ya no puede ser editado. + create: + error: Se ha producido un error al crear el cierre, por favor inténtalo de nuevo más tarde. + success: Cierre creado correctamente. + destroy: + error: Ocurrió un error eliminando el cierre. + success: Cierre eliminado correctamente. + edit: + confirm_start_over: Esto eliminará el número total de votos y las notas adjuntas. ¿Seguro que lo quieres eliminar? + heading: Recuento de votos - recuento de respuestas + info_text: Por favor detalla el número total de respuestas para cada pregunta. Este debe coincidir con el número total de respuestas introducidas en el paso anterior (%{total} respuestas en total). + modal_ballots_results_count_error: + blank: El total esperado de votos en blanco es %{expected}, pero la suma de las preguntas en blanco es %{current}. + close_modal: Cerrar + info_text: El número total de papeletas no coincide con el número total de sobres. Por favor, revisa el total de papeletas. + title: El total de papeletas no coincide + total: El total esperado es %{expected}, pero la suma de las papeletas válidas, en blanco y nulas es %{current}. + valid: El total esperado de votos válidos es %{expected}, pero la suma de las preguntas válidas es %{current}. + save_recount: Guardar recuento + start_over: Si hay un error en el número total, puedes borrar todo y empezar de nuevo. + total_ballots: Total de papeletas + total_blank_ballots: Total de papeletas en blanco + total_null_ballots: Total de papeletas nulas + total_valid_ballots: Total de papeletas válidas + new: + election: 'Elección:' + heading: Recuento de votos + info_text: 'Por favor, introduce el número total de papeletas (sobres) recontadas en este punto de votación:' + modal_ballots_count_error: + btn_validate_total: Validar recuento total de papeletas + info_explanation_text: 'Por favor, revisa el número total de papeletas. Si el número total es incorrecto, debes proporcionar una explicación para el Comité de Seguimiento:' + info_text: El número total de papeletas (sobres) introducidas no coincide con el registro de personas que han votado en este punto de votación. + message_for_monitoring_committee: Mensaje para el Comité de Seguimiento + review_recount: Revisar el recuento + text_area_placeholder: Por favor, escribe tu mensaje + title: El total de registros no coincide + total_ballots: 'Total de papeletas:' + total_people: 'Total de personas:' + polling_station: 'Punto de votación:' + submit: Verificar número total + total_ballots_count: Número de papeletas + show: + edit_count_votes: '¿Número incorrecto? Aún puedes editarlos.' + heading: Recuento de votos + sub_heading: El recuento debe cerrarse mediante la carga de un certificado. Una vez hecho, el recuento se sellará y ya no se podrá editar más. + sign: + cancel: Cancelar + check_box: Lo he revisado y es idéntico al certificado físico de cierre electoral + confirm: Ok, continuar + error: Se ha producido un error, por favor inténtalo de nuevo. + heading: Recuento de votos - Firmar cierre + info_text: Si continúas ya no podrás modificar ninguna información, esta acción no se puede deshacer. + submit: Firmar el cierre + success: Cierre firmado con éxito. + update: + error: Se ha producido un error al actualizar los resultados del cierre. Inténtalo de nuevo más tarde. + success: Resultados de cierre actualizados correctamente. + in_person_votes: + complete_voting: + available_answers: 'Respuestas disponibles:' + census_verified: Esta participante no ha votado presencialmente aún. + census_verified_with_online_vote: Esta participante ya ha votado en línea. Si vota en persona, los votos anteriores serán invalidados y este será su voto definitivo. + complete_voting: Completar votación + identify_another: Identificar a otra participante + questions_title: 'Tienen derecho a votar en las siguientes preguntas:' + questions_title_voted: 'Esta participante ya ha votado en línea y tiene derecho a votar en las siguientes preguntas:' + voted: La participante ha votado + create: + error: El voto no ha sido registrado. Por favor, inténtalo de nuevo. + in_person_form: + census_not_present: Esta participante no aparece en el censo. + census_not_present_description: Deben acudir a la oficina de reclamaciones del censo o al servicio de asistencia técnica. + date_of_birth: Fecha de nacimiento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + month: Mes + month_placeholder: MM + select: Selecciona el tipo de documento + title: 'Selecciona el tipo de documento e introduce el número de documento de la participante:' + validate_document: Validar documento + year: Año + year_placeholder: AAAA + new: + back: Volver a los puntos de votación + title: Identificar y verificar a una participante + show: + back: Volver a los puntos de votación + title: Esperando a que se registre el voto presencial + update: + error: Se ha producido un error al registrar el voto. Por favor, inténtalo de nuevo. + success: + accepted: El voto se ha registrado correctamente. + rejected: El Bulletin Board no ha aceptado el voto. Por favor, ponte en contacto con el administrador del sistema. + verify_document: + census_present: Esta participante aparece en el censo. + name: Nombre + title: 'Comprueba que los siguientes datos son correctos:' + verify_document: Verificar documento + menu: + polling_officer_zone: Zona del gestor de mesa + polling_officers: + index: + polling_officer_role_description: Has sido asignado para actuar como gestor de mesa (Presidente o Administrador) en algunas de las elecciones celebradas en esta plataforma. + polling_station: + address: Dirección + count_votes: Contar votos + election: Elección + identify_person: Identificar a una persona + name: Nombre + no_polling_stations: Todavía no estás asignado a ningún punto de votación. + role: Tu rol + show_closure: Ver cierre + title: Puntos de votación + voting: Votación + polling_officers: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + new: Añadir gestora de mesa + title: Acciones + roles: + manager: Administrador + president: Presidente + unassigned: No Asignado + polling_station_closure_certificate: + current_certificate: 'Certificado actual:' + polling_station_closure_recount: + nota_option: En blanco / Ninguna de las anteriores + polling_officer_notes: 'Notas del gestor de mesa:' + polling_officer_notes_blank: No hay notas + recount_summary: 'Resumen del recuento:' + signed: Firmado + total_ballots: 'Total de papeletas:' + total_blank_ballots: 'Total de papeletas en blanco:' + total_null_ballots: 'Total de papeletas nulas:' + total_valid_ballots: 'Total de papeletas válidas:' + polling_stations: + actions: + confirm_destroy: '¿Estás segura?' + destroy: Borrar + edit: Editar + new: Añadir punto de votación + title: Acciones + votings: + access_code_modal: + email: Enviar por correo electrónico a %{email} + info: Necesitas un código de acceso para participar. Si no recibiste uno por correo postal, podemos enviarte uno nuevo. + no_email: Correo no disponible + no_sms: Número de teléfono no disponible + sms: Enviar por SMS a %{sms} + title: Obtener código de acceso + check_census: + check_status: Comprobar estado + description: Aquí, tienes la opción de comprobar tus datos del censo para saber si tienes derecho a participar en esta votación. Deberías tener ya un código de acceso, pero si lo has perdido, puedes solicitarlo de nuevo, si tus datos son correctos. + error: + info: 'Por favor, inténtalo de nuevo. Si crees que los datos del sistema son incorrectos, puedes reportarlo aquí: %{census_contact_information}.' + title: Los datos que ha introducido no están en el censo de esta votación + form_title: 'Rellena el siguiente formulario para comprobar tus datos del censo:' + invalid: Hubo un problema al comprobar el censo. + success: + access_link: por correo electrónico. + access_link_with_sms: vía SMS o correo electrónico. + info: Deberías haber recibido tu código de acceso por correo postal ya. En caso de que no lo tengas, puedes solicitarlo aquí + title: '¡Tus datos del censo son correctos!' + title: '¿Puedo votar?' + check_fields: + date_of_birth: Fecha de nacimiento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + document_type: Tipo de documento + month: Mes + month_placeholder: MM + postal_code: Código postal + postal_code_placeholder: Número de código postal + select: Selecciona el tipo de documento + year: Año + year_placeholder: AAAA + count: + title: + one: "%{count} votación" + other: "%{count} votaciones" + elections_log: + description: El registro de la elección te mostrará toda la información relevante sobre cada votación. Por ejemplo, el estado de la ceremonia de claves o el recuento o si los resultados ya están publicados. Haz clic en la elección sobre la cual quieres la información del registro. + title: Registro de la elección + filters: + active: Activas + all: Todas + date: Fecha + finished: Finalizadas + search: Buscar + upcoming: Próximas + index: + no_votings: Ninguna votación coincide con tus criterios de búsqueda. + only_finished: Actualmente no hay votaciones programadas, pero aquí puedes encontrar las votaciones terminadas en la lista. + title: Votaciones + login: + access_code: Código de acceso + access_code_placeholder: Código de acceso + ask_for_a_new_one: Solicitar uno nuevo. + dont_have_access_code: '¿No tienes un código de acceso?' + form_title: 'Rellena el siguiente formulario para acceder a la votación:' + start_voting: Empezar a votar + title: Identificarme con mis datos del censo + no_census_contact_information: Aún no hay ninguna información de contacto. + orders: + label: 'Ordenar votaciones por:' + random: Aleatorio + recent: Más recientes + send_access_code: + invalid: Hubo un problema al enviar el código de acceso. + success: Tu código de acceso se ha enviado correctamente. + show: + title: Acerca de esta votación + votings_m: + badge_name: + finished: Finalizada + ongoing: En curso + upcoming: Próximamente + unspecified: No especificado + voting_type: + hybrid: Hibrida + in_person: Presencial + online: En línea + layouts: + decidim: + voting_navigation: + check_census: '¿Puedo votar?' + election_log: Registro de la elección + votings: + index: + promoted_votings: Votaciones destacadas + promoted_voting: + vote: Votar diff --git a/decidim-elections/config/locales/et-EE.yml b/decidim-elections/config/locales/et-EE.yml new file mode 100644 index 00000000..e020c4ff --- /dev/null +++ b/decidim-elections/config/locales/et-EE.yml @@ -0,0 +1 @@ +et: diff --git a/decidim-elections/config/locales/et.yml b/decidim-elections/config/locales/et.yml new file mode 100644 index 00000000..e020c4ff --- /dev/null +++ b/decidim-elections/config/locales/et.yml @@ -0,0 +1 @@ +et: diff --git a/decidim-elections/config/locales/eu.yml b/decidim-elections/config/locales/eu.yml new file mode 100644 index 00000000..e5fc4c44 --- /dev/null +++ b/decidim-elections/config/locales/eu.yml @@ -0,0 +1,1423 @@ +eu: + activemodel: + attributes: + answer: + description: Deskribapena + image: Irudia + proposals: Lotutako proposamenak + title: Izenburua + ballot_style: + code: Kodea + election: + description: Deskribapena + end_time: Bozketaren amaiera-data + start_time: Bozketaren hasiera-data + title: Izenburua + monitoring_committee_member: + email: Helbide elektronikoa + name: Izena + polling_officer: + email: Helbide elektronikoa + name: Izena + polling_station: + address: Helbidea + location: Kokapena + location_hints: Kokapen-argibideak + polling_station_managers: KudeatzaileaK + polling_station_president_id: Presidentea + title: Izenburua + question: + max_selections: Gehieneko aukera kopurua + min_selections: Aurreko aukeretatik bat ere ez + title: Izenburua + trustees_participatory_space: + user_id: Parte-hartzailea + voting: + banner_image: Banner-irudia + census_contact_information: Erroldako kontaktoen informazioa + description: Deskribapena + end_time: Bozketaren amaiera-data + introductory_image: Aurkezpenaren irudia + promoted: Nabarmendua + scope_id: Esparrua + show_check_census: Erakutsi "Egiaztatu errolda" orria + start_time: Bozketaren hasiera-data + title: Izenburua + voting_type: Bozketa mota + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Berriro erantsi behar da + ballot_result: + attributes: + base: + total_count_invalid: Guztizko erantzun kopurua ez dator bat baliozko/zuri/baliogabe banaketarekin. + election: + attributes: + attachment: + needs_to_be_reattached: Berriro erantsi behar da + question_result: + attributes: + base: + blank_count_invalid: Erantzun zurien kopurua ezin da izan boto-paper zurien kopurua baino handiagoa. + trustee: + attributes: + name: + cant_be_changed: ezin da aldatu + public_key: + cant_be_changed: ezin da aldatu + voting: + attributes: + voting_type: + inclusion: "%{value} ez da bozketa mota baliozkoa" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Mahaiko kudeatzailea dagoeneko bozketa-puntuaren presidentea/kudeatzailea da. + voting: + different_organization: Bozketa parte-hartzailearen erakunde berean egon behar da. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Mahaiko kudeatzaileak bozketa-puntuaren bozketa berean egon behar du. + models: + decidim/elections/answer: + one: Erantzuna + other: Erantzunak + decidim/elections/election: + one: Bozketa + other: Bozketak + decidim/elections/question: + one: Galdera + other: Galderak + decidim/voting: + one: Bozketa + other: Bozketak + decidim/votings/census/dataset: + one: Data-multzoa + other: Data-multzoa + decidim/votings/census/datum: + one: Datua + other: Datua + decidim/votings/polling_officer: + one: Mahaiko kudeatzailea + other: Mahaiko kudeatzailea(k) + decidim/votings/polling_station: + one: Bozketa-gunea + other: Bozketa gunea(k) + decidim/votings/voting: + one: Bozketa + other: Bozketa(k) + decidim: + admin: + filters: + officers_assigned_eq: + label: Arduradunak + values: + assigned: Esleituta + unassigned: Ez esleituta + role_eq: + label: Rola + values: + manager: Kudeatzailea + president: Presidentea + unassigned: Esleitu gabe + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Bilatu %{collection} izena/posta elektronikoa/goitizena edo bozketa-puntuaren arabera. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Bilatu %{collection} izenburuaren, helbidearen edo arduradunaren izen/email/goitizenaren arabera. + signed_eq: + label: Sinatua + values: + 'false': Sinatua + 'true': Sinatu gabe + validated_eq: + label: Baliozkotuta + values: + 'false': Baliozkotu gabe + 'true': Baliozkotua + voting_publications: + create: + error: Arazo bat egon da bozketa hau argitaratzean. + success: Bozketa zuzen argitaratu da. + destroy: + error: Arazo bat sortu da bozketa honen argitalpena kentzean. + success: Bozketa honen argitalpena zuzen kendu da. + components: + elections: + actions: + vote: Eman botoa + name: Bozketak + settings: + global: + announcement: Iragarpena + step: + announcement: Iragarpena + elections: + actions: + confirm_destroy: Ziur zaude? + destroy: Suntsitu + edit: Editatu + feedback: Boto-emailearen feedback-a + import: Sartu proposamenak erantzun gisa + manage_answers: Kudeatu erantzunak + manage_questions: Kudeatu galderak + manage_steps: Kudeatu urratsak + new_answer: Beste erantzun bat + new_election: Beste aukera bat + new_question: Beste galdera bat + new_trustee: Beste bermatzaile bat + preview: Aurrebistaratu + publish: Argitaratu + title: Ekintzak + unpublish: Desargitaratu + admin: + answers: + create: + invalid: Arazo bat egon da erantzun hau sortzean. + success: Erantzuna ondo sortua. + destroy: + invalid: Arazo bat egon da erantzun hau ezabatzean. + success: Erantzuna ondo ezabatua. + edit: + title: Editatu erantzuna + update: Eguneratu erantzuna + index: + invalid_max_selections: '%{missing_answers} erantzun gehiago behar d(it)uzu gehieneko hautaketarekin bat etortzeko.' + title: Erantzunak + new: + create: Sortu erantzuna + title: Beste erantzun bat + not_selected: Ez hautatua + select: + disable: Desautatu erantzuna + enable: Markatu erantzuna hautatu gisa + invalid: Arazo bat egon da erantzun hau hautatzean. + success: Erantzuna ondo aukeratua. + selected: Hautatua + unselect: + invalid: Arazo bat egon da erantzun honen hautaketa kentzean. + success: Erantzunaren aukeraketa ondo kendua. + update: + invalid: Arazo bat egon da erantzun hau eguneratzean. + success: Erantzuna ondo eguneratua. + elections: + create: + invalid: Arazo bat egon da aukera hau sortzean. + success: Aukera ondo sortua. + destroy: + invalid: Arazo bat egon da aukera hau ezabatzean. + success: Aukera ondo ezabatua. + edit: + title: Editatu aukera + update: Eguneratu aukera + form: + organization_time_zone: Egiaztatu ordutegi-eremua egokia dela antolamenduaren konfigurazioan. Egungo konfigurazioa da %{time_zone} (%{time}). + index: + no_bulletin_board: Ez dago Iragarki Taula zerbitzari konfiguraturik, modulu hau erabiltzeko beharrezkoa dena. Ataza hori sistemaren administratzaileak egin behar du. + title: Aukerak + new: + create: Sortu aukera + title: Beste aukera bat + publish: + success: Aukera zuzen argitaratu da. + unpublish: + success: Aukera zuzen desargitaratu da. + update: + invalid: Arazo bat egon da aukera hau eguneratzean. + success: Aukera ondo eguneratua. + exports: + elections: Bozketak + feedback_form_answers: Erantzunen feedbacka + mailers: + trustee_mailer: + body: + help_html: |- +

    Kaixo, %{user_name}


    +

    Aukeratu zaituzte bermatzaile gisa aritzeko hemen: %{organization} egingo diren bozketa batzuetan.


    +

    "Bermatzaileen gunea" izeneko arlo berri bat aktibatu da zure kontuan. Hortik, eskatutako lanak egin ahal izango dituzu. Momentuz, mesedez, sortu identifikatzeko gakoak.


    + subject: Gehitu zaituzte %{resource_name} -ren bermatzaile gisa. + trustee_zone: Eraman bermatzaileen gunera + menu: + trustees: Bermatzaileak + models: + answer: + name: Erantzuna + proposals_imports: + create: + invalid: Arazo bat egon da proposamenak erantzun gisa inportatzean. + success: "%{number} proposamen erantzunetara zuzen inportatuta." + new: + create: Inportatu proposamenak erantzun gisa + no_components: Ez dago beste proposamen-osagairik espazio honetan. + select_component: Mesedez, hautatu osagai bat + title: Inportatu proposamenak + questions: + create: + election_started: Aukeraketa hasi da. + invalid: Arazo bat egon da galdera hau sortzean. + success: Galdera zuzen sortua. + destroy: + invalid: Arazo bat egon da galdera hau ezabatzean. + success: Galdera zuzen ezabatua. + edit: + title: Editatu galdera + update: Eguneratu galdera + index: + title: Galderak + new: + create: Sortu galdera bat + title: Beste galdera bat + update: + invalid: Arazo bat egon da galdera hau eguneratzean. + success: Galdera ondo eguneratua. + steps: + create_election: + census: Errolda + errors: + census_codes_generated: Erroldarako sarbide kodeak ez dira sortu. + census_frozen: Erroldarako sarbide kodeak ez dira esportatu. + census_uploaded: Ez da igo erroldarik aukeraketa honetarako. + component_published: aukera ez dago argitaratuta. + fix_it_text: Zuzendu + max_selections: Galderek ez dute balio zuzenik erantzun kopururako + minimum_answers: Galderek gutxienez eduki behar dituzte bi erantzun . + minimum_questions: aukerak gutxienez galdera bat eduki behar du. + published: aukera ez dago argitaratuta . + time_before: Hasiera-ordua %tik {hours} ordu baino gutxiagotan konfiguratu da, aukeraketa hasi aurretik. + trustees_number: espazio parte-hartzaileak gutxienez eduki behar du %{number} bermatzaile gako publikoarekin . + invalid: Arazo bat egon da aukera hau konfiguratzean + no_trustees: Ez dago bermatzaile konfiguraturik espazio parte-hartzaile honetarako + not_used_trustee: "(ez da erabiltzen)" + public_key: + 'false': ez du gako publikorik + 'true': badu gako publiko bat + requirements: + census_codes_generated: Sortu dira erroldarako sarbide kodeak. + census_frozen: Erroldarako sarbide kodeak esportatu dira eta errolda izoztu da. + census_uploaded: Errolda igota dago. + component_published: Aukeraketaren osagaia argitaratuta. + max_selections: Erantzun guztiek balio zuzena dute honetarako erantzun gehien. + minimum_answers: Galdera bakoitzak badu gutxienez 2 erantzun. + minimum_questions: Aukerak badu gutxienez galdera 1. + published: Aukera badago argitaratuta. + time_before: Konfigurazioa egiten ari da aukeraketa hasi baino gutxienez %{hours} ordu. + trustees_number: Espazio parte-hartzaileak badu gutxienez %{number} bermatzaile gako publikoarekin. + submit: Konfiguratu bozketa + success: 'Aukeraketa zuzen bidali da hona: Iragarki Taula.' + technical_configuration: + authority_name: "Agintaritzaren izena: %{value}" + bulletin_board_server: "Buletinaren serbidorea: %{value}" + scheme_name: "Eskemaren izena: %{value}" + title: Ikusi informazio teknikoa + title: Konfiguratu bozketa + trustees: Hautaketaren bermatzaileak + created: + invalid: Arazo bat egon da gakoen zeremonia hastean. + submit: Hasi gakoen zeremonia + success: 'Gakoen zeremonia hasteko eskaera zuzen bidali da hona: Iragarki taula.' + title: Bozketa sortuta + trustees: Bermatzaileak + key_ceremony: + continue: Jarraitu + title: Gakoen zeremonia + key_ceremony_ended: + errors: + time_before: Bozketa hasteko prest dago. (%{start_time}) hasierako ordua baino %{hours} ordu itxaron behar duzu bozketaldia hasteko. + invalid: Arazo bat egon da bozketaldia hastean. + requirements: + time_before: Bozketa laster hasiko da. Eskuz hasi ahal duzu bozketaldia, edo automatikoki hasiko da hasierako ordua izan baino lehen, ordu honetan %{start_time}. + submit: Hasi bozketaldia + success: 'Bozketaldiaren eskaera zuzen bidali da hona: Iragarki Taula.' + title: Hasteko prest + processing: Prozesatzen... + results_published: + answer: Erantzuna + not_selected: Ez hautatua + question: Galdera + result: Emaitza + selected: Hautatua + submit: Bidali + title: Argitaratutako emaitzak + tally_ended: + answer: Erantzuna + not_selected: Ez hautatua + question: Galdera + result: Emaitza + selected: Hautatua + submit: Argitaratu emaitzak + success: Emaitzak argitaratzeko eskaera zuzen bidali da Iragarki Taulara + title: Kalkulatutako emaitzak + tally_started: + continue: Jarraitu + invalid: Arazo bat egon da galdutako bermatzailea erreportatzean. + mark_as_missing: Marka desagertu da + mark_as_missing_description: Bermatzaile guztiek parte hartu beharko lukete prozesu honetan, baina horietako batek ezin badu parte hartu, desagertu gisa markatu dezakezu. + success: Desagertutako bermatzailearen txostena zuzen bidali da Iragarki-taulara. + tally_completion: Prozesua amaitu egingo da bermatzaileak aktibo daudenean edo desagertutzat jotzen direnean. Gutxienez % {quorum} bermatzaile behar dira prozesua amaitzeko. + title: Zenbatzeko prozesua + undo_mark_as_missing: Akats baten ondorioz desagertu gisa markatutako bermatzaile batek parte hartu ahal izango du prozesua amaitu aurretik. Ohi bezala joka dezakete, eta absentearen marka ez da kontuan hartuko. + vote: + errors: + time_after: Bozketa oraindik ez dago martxan. (%{end_time}) amaierako ordura arte itxaron behar duzu bozketaldia amaitzeko. + invalid: Arazo bat egon da bozketaldia amaitzean. + requirements: + time_after: Bozketa amaitu da. Bozketaldia eskuz amaitu ahal duzu, edo minutu batzuk barru automatikoki amaituko da. + submit: Amaitu bozketaldia + success: 'Bozketaldia amaitzeko eskaera zuzen bidali da hona: Iragarki Taula.' + title: Bozketa-aldia + vote_ended: + invalid: Arazo bat egon da zenbaketa hastean. + submit: Hasi zenbaketa + success: 'Zenbaketa hasteko eskaera zuzen bidali da hona: Iragarki Taula.' + text: Bozketa amaitu da. Orain has zaitezke zenbaketa egiten. + title: Bozketa-aldia amaituta + vote_stats: + no_vote_statistics_yet: Oraindik ez dago boto-estatistikarik + title: Boto-estatistikak + voters: Boto-emaileak + votes: Botoak + trustees_participatory_spaces: + actions: + disable: Desgaitu + enable: Kontuan hartu + create: + exists: Bada bermatzailea espazio parte-hartzaile honetarako. + invalid: Arazo bat egon da bermatzailea sortzean. + success: Bermatzailea zuzen sortua. + delete: + invalid: Arazo bat egon da bermatzaile hau kentzean. + success: Bermatzailea zuzen ezabatua. + form: + select_user: Hautatu erabiltzailea + index: + title: Bermatzaileak + new: + create: Sortu bermatzailea + title: Beste bermatzaile bat + update: + invalid: Arazo bat egon da %{trustee} bermatzailea eguneratzean. + success: '%{trustee} bermatzailea zuzen eguneratua.' + admin_log: + election: + create: "%{user_name} k %{space_name} ko %{resource_name} aukera sortu du" + delete: "%{user_name} k ezabatu du %{space_name} ko %{resource_name} aukera" + end_vote: "%{user_name} k amaitu du bozketaldia %{space_name} ko %{resource_name} bozketarako Iragarki Taulan" + publish: "%{user_name} k argitaratu du %{space_name} ko %{resource_name} hautaketa" + publish_results: "%{user_name} k argitaratu ditu %{resource_name} hautaketarako emaitzak %{space_name} Iragarki Taulan" + report_missing_trustee: "% {user_name} k jakinarazi zion % {trustee_name} ri desagertutako bermatzaile gisa % {space_name} aukeraren % {resource_name} zenbaketan Iragarki-taulan" + setup: "%{user_name} k sortu du %{space_name} ko %{resource_name} hautaketa Iragarki Taulan" + start_key_ceremony: "%{user_name} k hasi du kontaketa %{resource_name} hautaketarako Iragarki Taulan" + start_tally: "%{user_name} k hasi zuen kontaketa %{resource_name} aukerarako Iragarki Taulan" + start_vote: "%{user_name} k hasi du bozketaldia %{space_name} ko %{resource_name} hautaketarako Iragarki Taulan" + unpublish: "%{user_name} k despublikatu du %{space_name} ko %{resource_name} hautaketa" + update: "%{user_name} k eguneratu du %{space_name} ko %{resource_name} hautaketa" + trustee: + create: "%{user_name} k %{trustee_user} erabiltzailea esleitu du bermatzaile gisa" + connection: + failed: + modal: + close: Itxi + communication_lost: Zoritxarrez, badirudi (Iragarki-taula) boto-zerbitzariarekin komunikazioa galdu egin dela.
    Baliteke Interneterako konexioa hautsita egotea edo zerbitzaria lanpetuta egotea.
    Saiatu geroago, edo jarri kontaktuan, arazo honek irauten badu. + generic_error: Zoritxarrez, errore ezezagun bat izan da. Ziur aski, zure nabigatzaileak ez du pairatzen edo modu "ezkutuan" edo "pribatuan" erabiltzen ari zara, bateragarria ez izanik. + title: Zerbait gaizki joan da + election_m: + badge_name: + finished: Amaituta + ongoing: Aktibo + upcoming: Hurrengoak + end_date: Amaitu + footer: + view: Ikusi + vote: Eman botoa + label: + date: Datak + questions: '%{count} galdera' + start_date: Hasi + unspecified: Zehaztu gabe + elections: + count: + elections_count: + one: "%{count} bozketa" + other: "%{count} bozketa" + election_log: + chained_hash: Mezu honen Hash kateatua + complete: Osatu + creation_description: + complete: Aukeraketa sortu, eta zuzen konfiguratu da Iragarki Taulan. + not_created: Oraindik ez da sortu aukeraketa. + creation_title: Aukeraketa sortuta + description: Hau aukeraketaren erregistroa da, bertan urrats bakoitzaren egoera ikus dezakezu, adibidez, noiz sortu den, ea zenbaketa-prozesua osatu den eta noiz itxi den aukeraketa. + download: Deskargatu + key_ceremony_description: + complete: Zeremonien gakoa osatu da. Bermatzaile bakoitzak baditu gako baliodunak eta behar diren segurtasunezko kopiaren gakoak deskargatu ditu. + not_started: Oraindik ez da hasi gakoen zeremonia. + started: Gakoen zeremonia hasi da, baina oraindik ez da amaitu. + key_ceremony_title: Gakoen zeremonia + not_available: Oraindik ez dago erabilgarri + not_created: Sortu gabe + not_ready: Ez dago prest + not_started: Hasi gabe + published: Argitaratuta + results_description: + not_published: Oraindik ez dira argitaratu emaitzak. + published: Emaitzak argitaratu dira. + results_title: Emaitzak + started: Hasita + tally_description: + finished: Zenbatzeko prozesua amaitu da. + not_started: Oraindik ez da hasi zenbatzeko prozesua. + started: Zenbatzeko prozesua hasi da. + tally_title: Zenbatzeko prozesua + title: Aukeraketaren erregistroa + unpublished: Argitaratu gabea + verifiable_results: + checksum: 'Artxiboaren SHA256 egiaztapenaren batuketa:' + description: + not_ready: Hautapenaren artxibo egiaztagarria eta SHA256 egiaztatze-batura oraindik ez daude eskuragarri. Emaitzak argitaratu bezain laster, aukera hori egiaztatu ahal izango duzu. + ready: 'Hemen duzu aukera egiaztatzeko aukera. Lehenik eta behin, fitxategia deskargatu behar duzu eta ziurtatu ez dela hondatu. Horretarako, exekutatu ondorengo komandoa eta egiaztatu irteera egiaztatze-baturarekin bat datorrela:' + how_to_verify: 'Fitxategia deskargatu eta ondo dagoela ziurtatu ondoren, egiaztatzaile unibertsala exekuta dezakezu. Klona ezazu biltegi hau este repositorio eta, erro-karpetatik, exekuta ezazu ondoko komandoa:' + title: Egiaztatu aukeraketaren emaitzak + verifiable_file: 'Aukeraketaren artxibo egiaztagarria:' + verify: Egiaztatu aukeraketa + vote_description: + finished: Bozkatzeko prozesua amaitu da. + not_started: Oraindik ez da hasi bozkatzeko prozesua. + started: Bozkatzeko prozesua hasi da. + vote_title: Bozkatzeko prozesua + filters: + active: Aktibo + all: Guztiak + date: Data + finished: Amaituta + upcoming: Hurrengoak + preview: + available_answers: 'Aukerako erantzunak:' + description: 'Bozketa-prozesuan galdera hauek aurkituko dituzu:' + title: Aukeraketaren galderak + results: + description: 'Hauek dira bozketaren emaitzak, galdera bakoitzerako:' + percentage: "%{count}%" + selected: Hautatua + title: Hautaketaren emaitzak + votes: + one: "Boto %{count}" + other: "%{count} boto" + show: + action_button: + change_vote: Aldatu zure botoa + vote: Botoa ematen hasi + vote_again: Berriro eman botoa + callout: + already_voted: Jada botoa eman duzu aukera honetan. Zure botoa alda dezakezu edo egiaztatu. + pending_vote: Zure botoa ematen ari da zerbitzarian. + vote_rejected: Ezin izan da zure botoa egiaztatu. Mesedez, egizu berriro. + election_log: Aukeraketaren erregistroa + preview: Aurreikusi + verify: + already_voted: Bozkatu duzu? + verify_here: Egiaztatu zure botoa hemen. + will_verify: Zure botoa egiaztatu ahal izango duzu hautaketa hasten denean. + voting_period_status: + finished: Bozketa hasi zen %{start_time} eta amaitu zen %{end_time} + ongoing: 'Bozketa aktibo %{end_time} arte' + upcoming: Bozketa hasiko da %{start_time} + feedback: + answer: + invalid: Arazo bat egon da zure feedbacka bidaltzean. + spam_detected: Arazo bat izan da galdetegia erantzutean. Agian azkarregi joan zara, berriro egin dezakezu? + success: Feedbacka zuzen bidali da. + models: + answer: + fields: + proposals: Proposamenak + selected: Hautatua + title: Izenburua + votes: Botoak + election: + fields: + bb_status: Iragarki Taularen egoera + end_time: Bukaera-data + start_time: Hasiera-data + title: Izenburua + verifiable_results_file_hash: Artxiboaren SHA256 egiaztapenaren batuketa + verifiable_results_file_url: Aukeraketa egiaztatzeko artxiboa + question: + fields: + answers: Erantzunak + max_selections: Hautatzeko gehieneko elementu kopurua + title: Izenburua + trustees_participatory_space: + fields: + considered: kontuan hartuta + email: E-maila + inactive: inaktibo + name: Izena + notification: Jakinarazpena egun honetan bidali zen + public_key: Gako Publikoa + status: Egoera + orders: + label: Ordenatu bozketak honen arabera + older: Zaharrena + recent: Berriena + trustee_zone: + elections: + backup_modal: + description: Aukera hori Iragarki Taulan sortzen ari da. Oso garrantzitsua da bertan parte hartzen duen bermatzaile bakoitzak gako horien segurtasun-kopia bat sortzea eta leku seguruan gordetzea. Ondoren, prozesuak aurrera egingo du. + download_election_keys: Deskargatu gakoak + title: Gakoen segurtasun-kopia %{election} aukeraketarako + key_ceremony_steps: + back: Atzera + description: Aukeraketa hau Iragarki Taulan sortzen ari da. Prozesu hau osatzeko, zure parte-hartzea bermatzaile gisa behar da. + keys: + create_election: Gakoak sortzea + key_ceremony: + joint_election_key: Gakoak batera sortzea + step_1: Gakoak argitaratzea + list: + status: Egoera + task: Eginkizuna + process_warning: Prozesua hasi ondoren, ez duzu orrialde honetatik atera behar prozesua amaitu arte. Zenbait minutu beharko ditu, bermatzaile guztiek konektatuta egon behar baitute hura osatzeko. + start: Hasi + status: + completed: Osatuta + pending: Zain + processing: Prozesatzen + title: Sortu gakoak %{election} aukeraketarako + restore_modal: + description: Iragarki Taulak zure informazioa du aukera horren bermatzaile gisa. Prozesuarekin jarraitzeko, lehenengo igo aurreko saioan sortutako segurtasun-kopiaren fitxategia. + title: Berriztu gakoak %{election} aukerarako + upload_election_keys: Igo aukeraren gakoak + tally_started_steps: + back: Atzera + description: Aukera horren emaitzak Iragarki Taulan kalkulatzen ari dira. Prozesu hori osatzeko, bermatzaile gisa parte hartu behar duzu. + keys: + end_tally: Zenbaketa amaituta + tally: + cast: Zenbaketa bidaltzea + share: Zenbaketa partekatzea + list: + status: Egoera + task: Eginkizuna + process_warning: Prozesua hasi ondoren, ez irten orri honetatik prozesua amaitu arte. Minutu batzuk beharko dira, bermatzaile guztiek konektatuta egon behar baitute osatzeko. + start: Hasi + status: + completed: Osatuta + pending: Zain + processing: Prozesatzen + title: Zenbaketa %{election} aukerarako + update: + error: Aukeraketaren egoera ez zen eguneratu. + success: 'Aukeraketaren egoera hauxe da: %{status}.' + menu: + trustee_zone: Bermatzailearen gunea + no_bulletin_board: + body: Atal honetarako iragarki-taula konfiguratu bat behar da. Deitu administratzaileari xehetasun gehiago izateko. + title: Sentitzen dugu, Iragarki Taula oraindik ez dago konfiguratuta. + trustees: + show: + elections: + list: + action_required: + 'false': 'Ez' + name: Eskatutako ekintza? + 'true': Burutu ekintza + bb_status: Egoera + election: Aukera + voting_period: Bozketa-aldia + no_elections: Gaur egun, ez duzu esleituta ekintzarik bermatzaile gisa. Jakinarazpen bat jasoko duzu fase desberdinetan jarduteko unea denean. + title: Bozketak + identification_keys: + cancel: Utzi + generate: Sortu identifikatzeko gakoak + generate_error: Errore bat egon da identifikatzeko gakoak sortzean. + generate_legend: Bozketan bermatzaile gisa parte hartzeko, identifikatzeko gako pare bat sortu behar duzu. + generate_legend_1: Botoia sakatu ondoren sortutako identifikatzeko gakoak dituen artxiboa deskargatu behar duzu. + generate_legend_2: Kopiatu deskargatutako artxiboa USB gailu garbi batean + generate_legend_3: Ziurtatu zure ekipoak ez duela fitxategiaren kopiarik (adibidez, egiaztatu Deskargak eta Mahaigaina karpetak). + generate_legend_4: Egin beste kopia bat kanpoko beste gailu batean, eta gorde oso leku seguruan. + submit: Bidali + submit_legend: Aurretik azaldutako urrats guztiak jarraitu ondoren, osatu prozesua identifikazio publikoaren gakoa bidaliz Decidimen zerbitzarian. + submit_title: Bidali identifikatzeko gako publikoa + title: Bermatzailearen identifikazio-gakoak + upload: Igo zure identifikazio-gakoak + upload_error: + invalid_format: Igotako fitxategiak ez du identifikazio-gakorik. + invalid_key: Ezin dira kargatu identifikazio-gakoak igotako fitxategian. + invalid_public_key: Igotako fitxategiko identifikazio-gakoak ez datoz bat gordetako identifikazio-gako publikoarekin. + upload_legend: Serbidoreak zure identifikazio-gako publikoak ditu, baina zure nabigatzaileak oraindik ez ditu. Zure identifikazio-gakoak dituen fitxategia inportatu behar duzu zure ordenagailura, haiek sortu ondoren egin zenuen segurtasun-kopiatik. + not_supported_browser_description: Dirudienez, bermatzaile gisa erabili ezin den nabigatzaile bat erabiltzen ari zara. Ziurtatu nabigatzailearen bertsio berriena erabiltzen ari zarela, edo saiatu nabigatzaile ezagunenetako edozein erabiltzen, zure zereginak bermatzaile gisa bete ahal izateko. + not_supported_browser_title: Eguneratu nabigatzailea bermatzaile gisa aritzeko + safari_warning_description: Badirudi Safari erabiltzen ari zarela, zeina ez dagoen bermatuta bermatzaile gisa jarduteko edo boto bat zifratzeko (hori Applek ezartzen dizkion memoria-murrizketen ondorio da). Hori etorkizunean konpondu daiteke Applek politika aldatuz edo etorkizunean Decidim Hauteskundeak optimizatuz. Bitartean, mesedez, erabili beste nabigatzaile bat. + safari_warning_title: Safari nabigatzailea detektatua + trustee_role_description: + with_keys: Plataforma honetan egindako bozketa batzuen bermatzaile gisa izendatu zaituzte. + without_keys: Esleitu zaituzte bermatzaile gisa aritzeko. Mesedez, sortu eta igo zure identifikazio gakoak. + update: + success: Zure identifikazio-gako publikoa zuzen gorde da. + votes: + ballot_decision: + audit: ( Ikuskatu boto-papera ) + back: Hasi berriro bozkatzeko prozesua + ballot_hash: 'Boto-paperaren identifikatzailea hau da:' + cast: Eman boto-papera zure bozketa amaitzeko + description_html: 'Hemen dituzu zure botoa emateko aukerak, behar bezala konta dadin, edo zure botoa behar bezala zifratu dela ikuska dezakezu. Bozketa ikuskatu nahi baduzu, irakurri, mesedez, jarraibideak: jakiteko nola jokatu.' + header: 'Boto-papera zifratu da: bidali edo ikuskatu' + casting: + header: Botoa ematen ari da... + text: Zure bozketa-papera hautetsontzian jartzen ari da. + confirm: + answer_number: '%{number} erantzun' + confirm: Baieztatu + edit: editatu + header: Baieztatu botoa + intro: Hemen duzu emango duzun botoaren laburpena. < br> Mesedez, berretsi zure botoa edo editatu zure erantzunak. + nota_option: Hutsik + confirmed: + back: Itzuli bozketetara + experience: Nola baloratzen duzu esperientzia? + feedback: Emaguzu zure iritzia + header: Baieztatutako botoa + lead: Zure botoa eman da! + text: 'Egiaztatu ahal duzu zure botoa zuzen sartu dela hautesontzian honako identifikatzailearekin: %{e_vote_poll_id}' + verify_link: 'Egiaztatzeko, kopiatu identifikatzailea eta itsatsi hemen: página de verificación de voto' + create: + error: Arazo bat egon da botoa ematean. Mesedez, saiatu berriro. + encrypting: + header: Botoa zifratzen ari da... + text: Zure boto-papera zifratzen ari da boto sekretua bermatzeko. + failed: + header: Boto okerra + lead: Ez duzu eman botoa! + text: Zerbait txarto irten da, mesedez, saiatu berriro. + try_again: Saiatu berriro + header: + ballot_decision: Eman edo ikuskatu zure botoa + confirm: Baieztatu botoa + election: Hautaketa + register: Erregistroa + vote_for: Bozkatu %{title} (r) en alde + messages: + invalid_token: Bozketa-kabinan egindako saioa ez da baliozkoa. Saiatu berriro bozkatzen. + not_allowed: Une honetan ezin duzu botoa eman bozketa honetan. + modal: + close: Itxi + proposal_header: 'Proposamenak:' + new: + answer_choices: Gehienez %{choices} erantzun aukeratu ahal dituzu + more_information: Informazio gehiago + nota_option: Zuriz / Aurreko bat ere ez + preview_alert: Hau bozketa-kabinaren aurrebista da. + question_steps: '%{total_steps} etik %{current_step} galdera' + selections: "%{max_selections} etik
    %{selected} hautatuta" + onboarding_modal: + create_account: Sortu Kontua + description: Beste kontu bat sortu nahi duzu plataforman? Prozesuetan parte hartu ahal izango duzu eta erakundearen parte aktiboa izango zara. + no_account: Ez, eskerrik asko. + title: Berria plataforman? + update: + error: Arazo bat egon da botoaren egoera eguneratzean. Mesedez, saiatu berriro. + verify: + content: + heading: Egiaztatu zure botoa + info: Egiaztatzaile horrek egiaztatu behar du zure botoa, zifratutako testu-batekin identifikatua, zuzen eman dela eta hautestontziaren barruan dagoela. + error: + header: Botoa ez da aurkitu! + info: Botoaren kodea ez da aurkitu %{link} hautestontzian, saiatu berriro. + form: + back: Itzuli plataformara + submit: Egiaztatu + vote_identifier: 'Kode identifikatzailea:' + vote_identifier_help: Hau da eman zizuten identifikatzailea botoa eman ondoren (ez da bozkatzeko kutxan sartzeko kodea). + header: + title: Egiaztatu zure botoa + success: + header: Botoa aurkituta! + info: Zure boto zifratua %{link} hautestontzian dago. + voting_step: + back: Atzera + continue: Hurrengoa + warnings: + empty_filters: Ez dago irizpide hori betetzen duen hauteskunderik. + no_elections: Ez dago programatutako hauteskunderik. + no_scheduled_elections: Egun, ez dago programatutako hauteskunderik, baina hemen aurreko hauteskundeen zerrenda ikus dezakezu. + events: + elections: + election_published: + email_intro: '%{resource_title} bozketa badago aktibo hemen %{participatory_space_title}. Orrialde honetatik ikus dezakezu:' + email_outro: Jakinarazpen hau jaso duzu %{participatory_space_title} jarraitzen ari zarelako. Jakinarazpenak jasotzeari utzi ahal diozu aurreko estekan. + email_subject: '%{resource_title} bozketa hemen: %{participatory_space_title} badago aktibo.' + notification_title: '%{resource_title} bozketa aktibo dago hemen: %{participatory_space_title}.' + trustees: + new_election: + email_intro: Bazaude gehituta bermatzaile gisa %{resource_title} aukerarako. + email_outro: Jakinarazpen hau jaso duzu bermatzaile gisa gehitu zaituztelako %{resource_title} aukerarako. + email_subject: Fede-emailea zara %{resource_title} hauteskunde prozesuan. + notification_title: 'Esleitu zaituzte bermatzaile gisa aritzeko Aukera honetan: %{resource_title}. Mesedez, egin gakoen zeremonia hautaketa martxan jartzeko.' + new_trustee: + email_intro: 'Administratzaile batek gehitu zaitu bermatzaile gisa honetarako: %{resource_name}. Zure gako publikoa sortu behar duzu =<''%{trustee_zone_url}''>bermatzaileen zure gunean' + email_outro: Jakinarazpen hau jaso duzu bermatzaile gisa gehitu zaituztelako %{resource_name} aukerarako. + email_subject: '%{resource_name} ren bermatzailea zara.' + notification_title: "Gehitu zaituzte plataforma honetan egingo diren hauteskunde batzuetan %{baliabide_name} ko bermatzaile gisa jarduteko.\nLan batzuk egiteko eskatuko zaizu. Oraingoz, mesedez, sortu zure identifikazio-gakoak ." + start_tally: + email_intro: '%{resource_title} aukeraketarako bozketaldia amaitu egin da. Orain, mesedez, zenbatu aukeraketak behin betiko emaitzak argitaratzeko.' + email_outro: Jakinarazpen hau jaso duzu %{resource_title} aukeran bermatzailea zarelako. + email_subject: '%{resource_title} aukeraketarako zenbaketa-prozesua hasi egin da.' + notification_title: '%{resource_title} aukeraketarako bozketaldia amaitu egin da. Orain, mesedez, zenbatu aukeraketak behin betiko emaitzak argitaratzeko.' + votes: + accepted_votes: + email_intro: 'Zure botoa onartu egin da! Zure botoaren tokena erabiliz: %{encrypted_vote_hash}, zure botoa egiaztatu ahal duzuhemen.' + email_outro: Jakinarazpen hau jaso duzu %{resource_name} aukeran botoa eman duzulako. + email_subject: Zure botoa %{resource_name} aukerarako onartu egin da. + notification_title: 'Zure botoa onartu egin da. Egiaztatu zure botoa hemen zure botoaren tokena erabiliz: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: '%{polling_station_name} bozketa-guneko %{role} rol bezala esleitu zaizu hemen: %{resource_title}. Bozketa-gunea administratu ahal duzu espaziotik Mahaiko kudeatzaileen gunea.' + email_outro: Jakinarazpen hau jaso duzu %{polling_station_name} ko%{role} rol bezala esleitu zaituztelako. + email_subject: '%{polling_station_name} bozketa-guneko %{role} zara.' + notification_title: '%{polling_station_name} bozketa-guneko %{role} zara %{resource_title} bozketan.' + send_access_code: + instruction: 'Baduzu eskatu duzun sartzeko kodea: %{access_code}. Horrekin parte hartu ahal izango duzu hemen %{voting}.' + subject: 'Zure kodea sartzeko eta hemen parte hartzeko: %{voting}' + help: + participatory_spaces: + votings: + contextual: "

    bozketa espazio honek aukera ematen die elkarte bateko pertsona guztiei galdera argi bat egiteko, bozketan parte hartzeko deia egiteko, erantzun baten aldeko edo kontrako eztabaida sortzeko eta agintzeko. Bozketaren data iristen denean, bozkatu eta emaitzak argitaratu ditzakezu.

    Adibideak: Kontsultak erakunde bati eragiten dion ia edozein alderdiri buruzkoak izan daitezke: adibidez, erakundearen izena edo logotipoa aldatzeko aukeren artean erabakitzeko; erakunde handiago bateko kide izateari buruz galdetuta Bai edo Ez erantzuteko; Plan estrategiko berri bat edo lan-talde baten lana onartzeko edo ukatzeko; edo karguek gehienez 1, 2 edo 3 agintalditan egon behar duten erabakitzeko.

    " + page: "

    bozketa espazio honek aukera ematen die elkarte bateko pertsona guztiei galdera argi bat egiteko, bozketan parte hartzeko deia egiteko, erantzun baten aldeko edo kontrako eztabaida sortzeko eta agintzeko. Bozketaren data iristen denean, bozkatu eta emaitzak argitaratu ditzakezu.

    Adibideak: Kontsultak erakunde bati eragiten dion ia edozein alderdiri buruzkoak izan daitezke: adibidez, erakundearen izena edo logotipoa aldatzeko aukeren artean erabakitzeko; erakunde handiago bateko kide izateari buruz galdetuta Bai edo Ez erantzuteko; Plan estrategiko berri bat edo lan-talde baten lana onartzeko edo ukatzeko; edo karguek gehienez 1, 2 edo 3 agintalditan egon behar duten erabakitzeko.

    " + title: Zer dira bozketak? + menu: + votings: Bozketak + participatory_spaces: + related_elections: + see_all: Ikusi aukera guztiak + statistics: + elections_count: Bozketak + votings_count: Bozketak + votings: + admin: + ballot_styles: + create: + error: Arazo bat egon da boto-paper mota hau sortzean. + success: Boto-paper mota ondo sortua. + destroy: + invalid: Arazo bat egon da boto-paper hau ezabatzean. + success: Boto-paper mota ondo ezabatua. + edit: + title: Editatu boto-paper mota + update: Eguneratu + form: + code_help: 'Laguntza: kodea erroldaren eta boto-paper motaren arteko lotura da. Erroldako datuak eguneratzean, sarrera bakoitzari kodearekin bat datorren boto-paper mota esleitzen zaio.' + election: Aukera + questions: Galderak boto-paper mota honetarako + questions_help: 'Laguntza: hautatu bozketako osagaien galderak, boto-paper mota honetarako boto-emaileei aurkezteko.' + index: + actions: + confirm_destroy: Ziur zaude? + destroy: Ezabatu + edit: Editatu + new: Beste boto-paper mota bat + title: Ekintzak + associated_census_data: Erroldarekin erlazionatutako sarrerak + explanation_callout: Boto-paperaren mota batek zehazten du zein galdera aurkeztuko zaizkion boto-emaileari kabinan. Boto-paper mota batean, aukera dezakezu bozketa-osagaiaren zein galdera dagozkion boto-paper horri. Boto-paperaren estilo-kodea erabiltzen da bat etortzeko erroldako hautesle bat eta kabinan erakutsi behar zaion boto-papera. Beti galdera guztiak erakustea nahi baduzu, ez sortu inolako boto-paper motarik. + title: Boto-paper motak + new: + create: Sortu + title: Sortu boto-paper mota + update: + invalid: Arazo bat egon da boto-paper hau eguneratzean. + success: Boto-paper mota ondo eguneratua. + content_blocks: + attachments_and_folders: + name: Bozketako eranskinak eta karpetak + header: + name: Bozketaren goiburua + highlighted_votings: + max_results: Erakusteko gehieneko elementu kopurua + html_block_1: + name: Bozketaren html 1 blokea + html_block_2: + name: Bozketaren html 2 blokea + html_block_3: + name: Bozketaren html 3 blokea + main_data: + name: Izenburua eta deskribapena + metrics: + name: Bozketaren metrikak + polling_stations: + name: Bozketa-guneak + related_elections: + name: Bozketako aukerak + stats: + name: Bozketaren estatistikak + timeline: + name: Bozketaren egutegia + index: + published: Argitaratua + unpublished: Argitaratu gabea + menu: + votings: Bozketak + votings_submenu: + attachment_collections: Karpetak + attachment_files: Fitxategiak + attachments: Eranskinak + ballot_styles: Boto-paper motak + census: Errolda + components: Osagaiak + info: Bozketa honi buruz + landing_page: Hasierako orrialdea + monitoring_committee: Jarraipen-batzordea + monitoring_committee_election_results: Balioztatu emaitzak + monitoring_committee_members: Kideak + monitoring_committee_polling_station_closures: Balioztatu ziurtagiriak + monitoring_committee_verify_elections: Egiaztatu aukerak + polling_officers: Mahaiko kudeatzaileak + polling_stations: Bozketa-guneak + see_voting: Ikusi bozketa + models: + ballot_style: + fields: + code: Kodea + monitoring_committee_member: + fields: + email: E-maila + name: Izena + polling_officer: + fields: + email: E-maila + name: Izena + polling_station: Bozketa-gunea (rola) + polling_station: + fields: + address: Helbidea + polling_station_managers: Administratzaileak + polling_station_president: Presidentea + title: Izenburua + voting: + fields: + created_at: Sortze-data + published: Argitaratua + title: Izenburua + monitoring_committee_election_results: + actions: + title: Ekintzak + view: Ikusi + index: + title: Hautatu aukera bat emaitzak ikusteko + results: + bulletin_board: Iragarki Taula + election_totals: Aukeraketak, guztira + polling_stations: Bozketa-guneak + result_types: + blank_answers: Erantzunak zuriz + blank_ballots: Boto-paperak zuriz + null_ballots: Baliorik gabeko boto-papera + total_ballots: Boto-paperak, guztira + valid_ballots: Boto-paper baliodunak + selected: Hautatua + title: Emaitzak %{election_title}aukerarako + totals: Guztira + show: + change_election: Aldatu aukera + publish_results: Argitaratu emaitzak + publishing: Emaitzak argitaratzen... + update: + invalid: Arazo bat egon da emaitzak argitaratzean. + rejected: Iragarki Taulak baztertu egin du emaitzak argitaratzea. Saiatu berriro edo jarri harremanetan sistema-administratzailearekin. + success: Emaitzak zuzen argitaratu dira. + monitoring_committee_members: + create: + invalid: Arazo bat egon da jarraipen-batzordekide hau sortzean. + success: Jarraipen-batzordeko kidea zuzen sortua. + destroy: + invalid: Arazo bat egon da jarraipen-batzordekide hau ezabatzean. + success: Jarraipen-batzordeko kidea zuzen ezabatua. + form: + existing_user: Parte-hartzailea badago + non_user: Gonbidatu beste parte-hartzaile bat + select_user: Bilatu helbide elektroniko, izen edo ezizenaren arabera + user_type: Parte-hartzaile mota + index: + title: Jarraipen-batzordea + new: + create: Sortu + title: Sortu jarraipen-batzordeko kidea + monitoring_committee_polling_station_closures: + actions: + title: Ekintzak + validate: Baliozkotu + view: Ikusi + closures: + change_election: Aldatu aukera + signed: Sinatua? + title: Bozketa-guneak %{election_title} aukerarako + validated: Baliozkotuta? + edit: + change_polling_station: Itzuli bozketa-guneetara + monitoring_committee_notes: Oharrak + monitoring_committee_notes_placeholder: Eman edozein intzidentziaren berri hemen + title: Emaitzak %{election_title} aukerarako %{polling_station_title} bozketa-gunean + elections: + title: Hautatu aukera bat baliozkotzeko + show: + change_polling_station: Itzuli bozketa-guneetara + monitoring_committee_notes: Jarraipen-batzordearen oharrak + validate: + error: Arazo bat egon da itxiera balidatzean. + success: Itxiera zuzen baliozkotu da. + monitoring_committee_verify_elections: + index: + download: Deskargatu + how_to_checksum: 'Deskargatze-prozesuan deskargatutako fitxategia hondatu edo manipulatu ez dela ziurtatzeko, exekutatu komando hau zure kontsolan, eta egiaztatu irteera bat datorrela goian adierazitako egiaztatze-baturarekin:' + how_to_download: Aukera bat egiaztatzeko, deskargatu zure fitxategi egiaztagarria goiko taulatik. + how_to_run_verifier: 'Fitxategia deskargatu eta ondo dagoela ziurtatu ondoren, egiaztatzaile unibertsala exekuta dezakezu. Klona ezazu biltegi hau este repositorio eta, erro-karpetatik, exekuta ezazu ondoko komandoa:' + how_to_title: Nola egiaztatu aukera baten balioa + not_available: Oraindik ez dago erabilgarri + title: Bozketak + polling_officers: + create: + invalid: Arazo bat egon da mahaiko kudeatzailea sortzean. + success: Mahaiko kudeatzailea zuzen sortua. + destroy: + invalid: Arazo bat egon da mahaiko kudeatzailea ezabatzean. + success: Mahaiko kudeatzailea zuzen ezabatua. + form: + existing_user: Parte-hartzailea badago + non_user: Gonbidatu beste parte-hartzaile bat + select_user: Bilatu helbide elektroniko, izen edo ezizenaren arabera + user_type: Parte-hartzaile mota + index: + role_manager: administratzailea + role_president: presidentea + title: Mahaiko kudeatzaileak + new: + create: Sortu + title: Sortu mahaiko kudeatzailea + polling_officers_picker: + choose_polling_officers: Aukeratu mahaiko kudeatzailea + no_polling_officers: Ez dago zure bilaketa-irizpideekin bat datorren hauteslekurik edo ez dago hauteslekurik. + polling_stations: + create: + invalid: Arazo bat egon da bozketa-gune hau sortzean. + success: Bozketa-gunea zuzen sortua. + destroy: + invalid: Arazo bat egon da bozketa-gune hau ezabatzean. + success: Bozketa-gunea zuzen ezabatua. + edit: + title: Editatu bozketa-gunea + update: Eguneratu bozketa-gunea + form: + address_help: 'Helbidea: Geocoderrek erabilia kokapena aurkitzeko' + location_help: 'Kokapena: mezua boto-emaleei zuzendua bozketa-gune zehatza adieraziz' + location_hints_help: 'Kokapenerako iradokizunak: Informazio gehigarria. Adibidez: Bozketa-gunea dagoen eraikinaren solairua.' + polling_station_managers_help: 'Mahaiko administratzaileak: bozketa-guneko administratzaile gisa jardungo duten ofizialak. Ziurtatu ofizialak dagoeneko sortu direla Mahai-kudeatzaileetan eta ez daudela beste bozketa-gune bati esleituta' + polling_station_president_help: 'Bozketa-guneko presidentea: bozketa-guneko presidentearen funtzioak egingo dituen ofiziala. Ziurtatu ofiziala sortuta dagoela Mahaiko kudeatzaileetan eta ez duela esleituta beste bozketa-gunerik.' + select_president: Hautatu mahaiko kudeatzaile bat bozketa-guneko presidente gisa + index: + title: Bozketa-guneak + new: + create: Sortu + title: Sortu bozketa-gunea + update: + invalid: Arazo bat egon da bozketa-gune hau eguneratzean. + success: Bozketa-gunea zuzen eguneratua. + titles: + votings: Bozketak + votings: + actions: + confirm_destroy: Ziur zaude? + destroy: Ezabatu + new_voting: Beste bozketa-gune bat + create: + invalid: Arazo bat egon da bozketa hau sortzean. + success: Bozketa zuzen sortua. + edit: + add_election_component: Ez duzu bozketa honetarako hautaketarik konfiguratuta. Mesedez, gehitu osagaien atalean. + assign_missing_officers: Presidenterik eta/edo administratzailerik gabeko bozketa-guneak daude. Esleitu itzazu bozketa-guneen ataletik. + update: Eguneratu + form: + banner_image: Banner-irudia + census_contact_information: Erroldako kontaktoen informazioa + census_contact_information_help: Kontakturako informazio hau erroldarekin arazoak jakinarazi nahi dituen parte-hartzaile batentzat da. Helbide elektroniko bat izan daiteke, harremanetarako formulario bat beste leku batean, bisitarientzako inkesta bat, etab. + introductory_image: Aurkezpenaren irudia + promoted: Nabarmendua + select_a_voting_type: Mesedez, hautatu bozketa mota bat + show_check_census_help: Zehaztu botazioen menu publikoan "Bozka dezaket?" esteka erakutsi ala ez. + slug: Slug + slug_help_html: 'URLetako testu laburrak erabiltzen dira bozketa honi aurre egiteko URLak sortzeko. Letrak, zenbakiak eta gidoiak soilik onartzen ditu, eta letra batez hasi behar du. Adibidea: %{url}' + title: Izenburua + voting_type: + hybrid: Hibridoa + in_person: Aurrez aurre + online: Lineakoa + voting_type_label: Bozketa mota + new: + create: Sortu + title: Beste bozketa bat + update: + invalid: Arazo bat egon da bozketa hau eguneratzean. + success: Bozketa zuzen eguneratua. + admin_log: + ballot_style: + create: "%{user_name} k sortu du boto-paper bat kode honekin: %{ballot_style_code} espazio honetan: %{space_name}" + delete: "%{user_name} k ezabatu du boto-paper mota hau kode honekin: %{ballot_style_code} espazio honetan: %{space_name}" + update: "%{user_name} k eguneratu du boto-paper mota kode honekin: %{ballot_style_code} espazio honetan: %{space_name}" + census: + create: "%{user_name} k sortu du errolda espazio honetarako: %{space_name}" + delete: "%{user_name} k ezabatu du errolda espazio honetarako: %{space_name}" + update: "%{user_name} k eguneratu du errolda espazio honetarako: %{space_name}" + monitoring_committee_member: + create: "%{user_name} k esleitu du %{monitoring_committee_member_user} erabiltzailea jarraipen-batzordeko kide gisa espazio honetan: %{space_name}" + delete: "%{user_name} k desesleitu du %{monitoring_committee_member_user} erabiltzailea jarraipen-batzordeko kide gisa espazio honetan: %{space_name}" + polling_officer: + create: "%{user_name} k esleitu du %{polling_officer_user} erabiltzailea jarraipen-batzordeko kide gisa espazio honetan: %{space_name}" + delete: "%{user_name} k desesleitu du %{polling_officer_user} erabiltzailea jarraipen-batzordeko kide gisa espazio honetan: %{space_name}" + polling_station: + create: "%{user_name} k sortu du %{resource_name} bozketa-gunea %{space_name} espazioan" + delete: "%{user_name} k ezabatu du %{resource_name} bozketa-gunea %{space_name} espazioan" + update: "%{user_name} k eguneratu du %{resource_name} bozketa-gunea %{space_name} espazioan" + voting: + create: "%{user_name}-k sortu du %{resource_name} bozketa" + publish: "%{user_name}-k argitaratu du %{resource_name} bozketa" + unpublish: "%{user_name}-k desargitaratu du %{resource_name} bozketa" + census: + admin: + census: + create: + invalid: Arazo bai izan da errolda igotzean, mesedez, saiatu berriro geroago. + invalid_csv_header: CSV goiburuak galdu dira edo ez dira zuzenak - mesedez, irakurri arretaz jarraibideak. + creating_data: + info_message: "Mesedez itxaron, %{processed_count} prozesatuak %{raw_count} tik %{file} fitxeroko ilara)." + delete: + button: Ezabatu erroldako datu guztiak + confirm: Errolda osoa ezabatzea ezin da desegin. Ziur zaude jarraitu nahi duzula? + destroy: + error: Arazo bai izan da errolda ezabatzean, mesedez, saiatu berriro geroago. + success: Erroldako datua ezabatuta. + export_access_codes: + button: Esportatu bozketaren Sartzeko Kodeak + callout: Orain sartzeko kodeak esportatu ahal dituzu. Hori behin baino ezin da egin. Behin esportazioa hasita, posta elektroniko bat jasoko duzu, bertan jarraibideak %{email} + confirm: Sartzeko kodeak behin baino ezin dituzu esportatu. Ziurtatu %{email} posta elektronikoan sarbidea duzula. + file_not_exist: Fitxategi hau ez da existitzen. + launch_error: Arazoa sarbide-kodeak esportatzen hastean + launch_success: 'Sartzeko kodeen esportazioa hasi egin da. Laster posta elektroniko bat jasoko duzu hemen: %{email}.' + exporting_access_codes: + info_message: "Mesedez, itxaron, esportazioa prestatzen ari da, laster jasoko duzu hemen %{email})." + freeze: + callout: Errolda itxita dago eta ezin da aldatu. + help_html: | + Erroldako datuak igo egin dira, kodeak sortu eta esportatu, zuzen.
    + Orain prest zaude aukeraketa hasteko. + Erabili CSV esportatua, kode indibidualak dituena, zure erroldan zehar banatzeko, zure baliabideak erabiliz, edo aktibatu "Eman dezaket botoa?", kode hau edonork berreskuratzeko, bere erroldako datuak erabiliz. + generate_access_codes: + button: Sortu bozketaren Sartzeko Kodeak + callout: Sartzeko kodeak orain sortu ahal dituzu. Kontuan hartu sortu ondoren, errolda ezin izango duzula berriro aldatu. + confirm: Jarraitzen baduzu, ezin izango duzu errolda aldatu. + info_message_all: "ilarak zuzen inportatu dira %{data_count}) ko (%{raw_count} fitxategiaren %{file} ilara." + info_message_warn: Mesedez, egiaztatu daturik ez dela falta, %{data_count} erregistro sortu baitira, baina igo den (%{file}) fitxategiak %{raw_count} ilara zituen. + launch_error: Arazoa sarbide-kodeak sortzen hastean + launch_success: Kodeak sortzen hasi da. + start_over: Mesedez, ezabatu egungo errolda eta hasi berriro CSV fitxategi egoki batekin baliozko errenkadak. + generating_access_codes: + info_message: "Mesedez, itxaron, bozketan sartzeko kodeak sortzen ari dira (honek minutu batzuk beharko ditu)..." + new: + file_help: + explanation: 'Jarraibideak artxiborako:' + message_1: Soilik CSV (.csv) artxiboak onartzen dira. + message_2: Zutabeen arteko banatzailea puntu eta koma (";") izan behar da. + has_ballot_styles_message: Boto-paper motak konfiguratu dituzu. Mesedez, ziurtatu CSVren "%{ballot_style_code_header}" eremua eta nahi duzun boto-paper motaren kodea bat datozela. + info_message: "Oraindik ez dago erroldarik. Mesedez, erabili hurrengo galdetegia sortzeko CSV batetik inportatuz." + missing_ballot_styles_message: 'Oraindik ez dago boto-paperik bozketa honetarako. Galdera baldintzatuak egin nahi badituzu (adibidez, boto-emailea aurkeztu galdera ezberdinen arabera, adibidez, egoitza-barrutiaren/eskualdearen arabera), ezarri behar duzu con
    artxiboaren adibide bat boto-paper mota:' + csv_example_without_ballot_style: 'con artxiboaren adibide bat boto-paper mota:' + csv_header_after: Ez sartu ("%{ballot_style_code_header}") azken eremua, baldintza-galderarik/boto-paper motarik behar ez baduzu + csv_header_before: 'Errolda CSV artxiboa izan behar da honako goiburu hau duena:' + document_types: + identification_number: Identifikazio-zenbakia + passport: Pasaportea + export_mailer: + access_codes_export: + click_button: 'Egin klik hurrengo loturan zure datuak deskargatzeko.
    Artxiboa eskuragai egongo da data honetara arte %{date}.
    Irekitzeko beharko duzu
    7-Zip (Windowserako), Keka (para MacOS) o PeaZip (Linuxerako). Pasahitza: %{password}' + download: Deskargatu + subject: Bozketan sartzeko kodeen esportazioa %{voting_title} rako prest dago + vote_flow: + already_voted_in_person: Parte-hartzaile honek jada eman du botoa aurrez aurre eta ez du bozkatzeko eskubiderik. + datum_not_found: Sartutako datuak ez datoz bat boto-emaile batekin ere ez. + content_blocks: + highlighted_votings: + name: Bozketa nabarmenduak + landing_page: + polling_stations: + heading: Bozketa-guneak + no_polling_stations: Oraindik ez dago bozketa-gunerik. + monitoring_committee_members: + actions: + confirm_destroy: Ziur zaude? + destroy: Ezabatu + new: Beste kide bat + title: Ekintzak + pages: + home: + highlighted_votings: + active_spaces: Bozketa aktiboak + see_all_spaces: Ikusi bozketa guztiak + polling_officer_zone: + closures: + back_to_polling_stations: Itzuli bozketa-guneetara + certify: + add_photos: Erantsi argazkiak + edit_photos: Editatu argazkiak + error: Arazo bat izan da ziurtagiria eranstean, mesedez, saiatu berriro. + heading: Botoen zenbaketa - Igo ziurtagiria + info_text: Mesedez, igo bozketa-itxieraren ziurtagiriaren argazki bat. + submit: Igo ziurtagiria + success: Ziurtagiria zuzen igo da. + upload_photos: Igo Hauteskundearen Itxieraren Egiaztagiriaren agazkia + completed: + sub_heading: Zenbaketa hau egiaztatu egin da eta ezin da editatu. + create: + error: Arazo bat izan da itxiera sortzean, mesedez, saiatu berriro geroago. + success: Itxiera zuzen egin da. + destroy: + error: Errorea izan da itxiera ezabatzean. + success: Itxiera zuzen ezabatua. + edit: + confirm_start_over: Hau egiten baduzu ezabatu egingo dira boto guztiak eta erantsitako oharrak. Ziur zaude ezabatu nahi dituzula? + heading: Botoen zenbaketa - erantzunen zenbaketa + info_text: Mesedez, ezarri galdera bakoitzerako erantzun kopurua. Kopuru hau bat etorri behar da aurreko urratsean ezarritako guztizko erantzun kopuruarekin. (%{total} erantzun guztira). + modal_ballots_results_count_error: + blank: Espero zen boto zurien kopurua %{expected} da, baina erantzun zurien batuketa %{current} da. + close_modal: Itxi + info_text: Boto-paperen kopuru osoa ez dator bat gutun-azalen kopuru osoarekin. Mesedez, berrikusi boto-paper guztiak. + title: Boto-paperen kopuru osoa ez dator bat + total: Espero zen guztizkoa %{expected} da, baina boto-paper baliodun, zuri eta baliogabeen batuketa %{current} da. + valid: Espero zen boto baliodunen guztizkoa %{expected} da, baina erantzun baliodunen batuketa %{current} da. + save_recount: Gorde zenbaketa + start_over: Guztizkoan errorea badago, dena ezabatu eta berriro has zaitezke. + total_ballots: Boto-paperak, guztira + total_blank_ballots: Boto-paper zuriak, guztira + total_null_ballots: Boto-paper okerrak, guztira + total_valid_ballots: Boto-paper baliodunak, guztira + new: + election: 'Aukera:' + heading: Botoen zenbaketa + info_text: 'Mesedez, sartu bozketa-puntu honetan birzenbatutako boto-txartelen (gutun-azalen) kopuru osoa:' + modal_ballots_count_error: + btn_validate_total: Baliozkotu boto-paperen zenbaketa osoa + info_explanation_text: 'Mesedez, berrikusi zein boto-paper kopuru dagoen. Kopuru osoa zuzena ez bada, Jarraipen Batzordeari azalpenak eman behar dizkiozu:' + info_text: Aurkeztutako boto kopuru osoa (gutun-azalak) ez dator bat botoa eman dutenen erregistroarekin. + message_for_monitoring_committee: Mezua Jarraipen Batzordearentzat + review_recount: Berrikusi zenbaketa + text_area_placeholder: Mesedez, idatzi zure mezua + title: Erregistroen kopuru osoa ez dator bat + total_ballots: 'Boto-paperak, guztira:' + total_people: 'Pertsona kopurua, guztira:' + polling_station: 'Bozketa-gunea:' + submit: Egiaztatu guztizko zenbakia + total_ballots_count: Boto-paper kopurua + show: + edit_count_votes: Okerreko zenbakiak? Oraindik editatu ahal dituzu. + heading: Botoen zenbaketa + sub_heading: Zenbaketa ixteko egiaztagiria igo behar da. Behin eginez gero, zenbaketa zigilatu eta ezin izango da berriro editatu. + sign: + cancel: Utzi + check_box: Berrikusi dut eta bozketaren itxieraren ziurtagiri fisikoaren berdina da + confirm: Ados, jarraitu + error: Arazo bat izan da, mesedez, saiatu berriro. + heading: Botoen zenbaketa - Sinatu itxiera + info_text: Jarraitzen baduzu, ezin izango duzu informaziorik aldatu, ekintza hau ezin da desegin. + submit: Sinatu itxiera + success: Itxiera zuzen itxi da. + update: + error: Arazo bat izan da itxieraren emaitzak eguneratzean. Saiatu berriro geroago. + success: Itxieraren emaitzak zuzen eguneratu dira. + in_person_votes: + complete_voting: + available_answers: 'Erantzun eskuragarriak:' + census_verified: Parte-hartzaileak oraindik ez du aurrez aurre botoa eman. + census_verified_with_online_vote: Parte-hartzaile honek botoa eman du online. Aurrez aurre bozkatzen badu, aurreko botoak baliogabetuko dira eta hau izango da bere behin betiko botoa. + complete_voting: Osatu bozketa + identify_another: Identifikatu beste parte-hartzaile bat + questions_title: 'Botoa emateko eskubidea dute ondoko galdera hauetan:' + questions_title_voted: 'Parte-hartzaile honek dagoeneko lineako botoa eman du eta botoa emateko eskubidea du ondoko galdera hauetan:' + voted: Parte-hartzaileak botoa eman du + create: + error: Botoa ez da erregistratu. Mesedez, saiatu berriro. + in_person_form: + census_not_present: Parte-hartzaile hau ez da erroldan agertzen. + census_not_present_description: Erroldako erreklamazio-bulegora joan behar zara edo laguntza teknikoaren zerbitzura. + date_of_birth: Jaiotze-data + day: Eguna + day_placeholder: EE + document_number: Dokumentuaren zenbakia + document_number_placeholder: ID zenbakia + month: Hilabetea + month_placeholder: HH + select: Hautatu dokumentu mota + title: 'Hautatu dokumentu mota eta idatzi parte-hartzailearen agiriaren zenbakia:' + validate_document: Balidatu dokumentua + year: Urtea + year_placeholder: UUUU + new: + back: Itzuli bozketa-guneetara + title: Identifikatu eta egiaztatu parte-hartzaile bat + show: + back: Itzuli bozketa-guneetara + title: Aurrez aurreko botoa erregistratzearen zain + update: + error: Arazo bat egon da boto hau erregistratzean. Mesedez, saiatu berriro. + success: + accepted: Botoa zuzen erregistratu da. + rejected: Iragarki Taulak ez du onartu botoa. Mesedez, jarri harremanetan programaren administratzailearekin. + verify_document: + census_present: Parte-hartzaile hau erroldan agertzen da. + name: Izena + title: 'Egiaztatu ondoko datu hauek zuzenak direla:' + verify_document: Egiaztatu agiria + menu: + polling_officer_zone: Mahaiko kudeatzailearen gunea + polling_officers: + index: + polling_officer_role_description: Plataforma honetan egindako aukeraketa batzuetan mahaiko kudeatzaile gisa (Presidentea edo Administratzailea) jarduteko izendatu zaituzte. + polling_station: + address: Helbidea + count_votes: Zenbatu botoak + election: Aukera + identify_person: Identifikatu pertsona bat + name: Izena + no_polling_stations: Oraindik ez zaizu esleitu bozkatzeko lekurik. + role: Zure rola + show_closure: Ikusi itxiera + title: Bozketa-guneak + voting: Bozketa + polling_officers: + actions: + confirm_destroy: Ziur zaude? + destroy: Ezabatu + new: Beste bozketa-kudeatzaile bat + title: Ekintzak + roles: + manager: Administratzailea + president: Presidentea + unassigned: Esleitu gabe + polling_station_closure_certificate: + current_certificate: 'Egungo egiaztagiria:' + polling_station_closure_recount: + nota_option: Zuriz / Aurreko bat ere ez + polling_officer_notes: 'Mahaiko kudeatzailearen oharrak:' + polling_officer_notes_blank: Ez dago oharrik + recount_summary: 'Zenbaketaren laburpena:' + signed: Sinatua + total_ballots: 'Boto-paperak, guztira:' + total_blank_ballots: 'Boto-paper zuriak, guztira:' + total_null_ballots: 'Boto-paper okerrak, guztira:' + total_valid_ballots: 'Boto-paper baliodunak, guztira:' + polling_stations: + actions: + confirm_destroy: Ziur zaude? + destroy: Ezabatu + edit: Editatu + new: Beste bozketa-gune bat + title: Ekintzak + votings: + access_code_modal: + email: 'Bidali posta elektronikoz honi: %{email}' + info: Parte hartzeko sarbide-kodea behar duzu. Postaz bat jaso ez baduzu, beste bat bidali ahal dizugu. + no_email: Posta elektronikoa ez erabilgarri + no_sms: Telefono zenbakia ez erabilgarri + sms: Bidali SMSz honi %{sms} + title: Lortu sartzeko kodea + check_census: + check_status: Egiaztatu egoera + description: Hemen, zure erroldako datuak egiaztatzeko aukera duzu, bozketan parte hartzeko eskubidea duzun jakiteko. Sarbide-kode bat izan beharko zenuke, baina galdu baduzu, berriro eska dezakezu, zure datuak zuzenak badira. + error: + info: 'Mesedez, saiatu berriro. Sistemaren datuak okerrak direla uste baduzu, hemen eman ahal dituzu: %{census_contact_information}.' + title: Sartu dituzun datuak ez daude bozketa honen erroldan + form_title: 'Bete ondoko galdetegia zure erroldako datuak egiaztatzeko:' + invalid: Arazo bat egon da errolda egiaztatzean. + success: + access_link: emailez. + access_link_with_sms: sMSz edo posta elektronikoz. + info: Zure sarbide-kodea postaz jasota izan beharko zenuke. Ez baduzu, hemen eska dezakezu + title: Zure erroldako datuak zuzenak dira! + title: Botoa eman dezaket? + check_fields: + date_of_birth: Jaiotze-data + day: Eguna + day_placeholder: EE + document_number: Dokumentuaren zenbakia + document_number_placeholder: ID zenbakia + document_type: Dokumentu mota + month: Hilabetea + month_placeholder: HH + postal_code: Posta-kodea + postal_code_placeholder: Posta-kodearen zenbakia + select: Hautatu dokumentu mota + year: Urtea + year_placeholder: UUUU + count: + title: + one: "Boto %{count}" + other: "%{count} boto" + elections_log: + description: Aukeraren erregistroak bozketa bakoitzari buruzko informazio garrantzitsu guztia erakutsiko dizu. Adibidez, gako-zeremoniaren edo zenbaketaren egoera edo emaitzak argitaratuta dauden. Egin klik erregistroari buruzko informazioa nahi duzun aukeran. + title: Aukeraketaren erregistroa + filters: + active: Aktibo + all: Guztiak + date: Data + finished: Amaituta + search: Bilatu + upcoming: Hurrengoak + index: + no_votings: Ez dago bilaketa-irizpideekin bat datorren bozketarik. + only_finished: Orain ez dago bozketa programaturik, baina hemen zerrendan amaitutako bozketak ikus ditzakezu. + title: Bozketak + login: + access_code: Sarbide-kodea + access_code_placeholder: Sarbide-kodea + ask_for_a_new_one: Eskatu beste bat. + dont_have_access_code: Ez duzu sarbide-koderik? + form_title: 'Bete ezazu ondoko galdetegia bozketan sartzeko:' + start_voting: Botoa ematen hasi + title: Neure burua identifikatu erroldako nire datuekin + no_census_contact_information: Oraindik ez dago harremanetarako informaziorik. + orders: + label: 'Ordenatu bozketak honen arabera:' + random: Ausazkoa + recent: Azkenak + send_access_code: + invalid: Arazo bat egon da sarbide-kodea bidaltzean. + success: Zure sarbide-kodea zuzen bidali da. + show: + title: Bozketa honi buruz + votings_m: + badge_name: + finished: Amaituta + ongoing: Bidean + upcoming: Laster + unspecified: Zehaztu gabe + voting_type: + hybrid: Hibridoa + in_person: Aurrez aurre + online: Lineakoa + layouts: + decidim: + voting_navigation: + check_census: Botoa eman dezaket? + election_log: Aukeraketaren erregistroa + votings: + index: + promoted_votings: Bozketa nabarmenduak + promoted_voting: + vote: Eman botoa diff --git a/decidim-elections/config/locales/fa-IR.yml b/decidim-elections/config/locales/fa-IR.yml new file mode 100644 index 00000000..88215f82 --- /dev/null +++ b/decidim-elections/config/locales/fa-IR.yml @@ -0,0 +1 @@ +fa: diff --git a/decidim-elections/config/locales/fi-plain.yml b/decidim-elections/config/locales/fi-plain.yml new file mode 100644 index 00000000..684157da --- /dev/null +++ b/decidim-elections/config/locales/fi-plain.yml @@ -0,0 +1,1426 @@ +fi-pl: + activemodel: + attributes: + answer: + description: Kuvaus + image: Kuva + proposals: Liittyvät ehdotukset + title: Otsikko + ballot_style: + code: Koodi + election: + description: Kuvaus + end_time: Äänestys päättyy + start_time: Äänestys alkaa + title: Otsikko + monitoring_committee_member: + email: Sähköpostiosoite + name: Nimi + polling_officer: + email: Sähköpostiosoite + name: Nimi + polling_station: + address: Osoite + location: Sijainti + location_hints: Paikan tarkemmat tiedot + polling_station_managers: Virkailijat + polling_station_president_id: Esimies + title: Otsikko + question: + max_selections: Valintojen enimmäismäärä + min_selections: Ei mikään edellä mainituista + title: Otsikko + trustees_participatory_space: + user_id: Osallistuja + voting: + banner_image: Bannerikuva + census_contact_information: Henkilötietorekisterin vastuutahon yhteystiedot + description: Kuvaus + end_time: Äänestyksen päättymisaika + introductory_image: Esittelykuva + promoted: Korostettu + scope_id: Teema + show_check_census: Näytä "tarkasta henkilötietorekisterin tiedot" -sivu + start_time: Äänestyksen alkamisaika + title: Otsikko + voting_type: Äänestystapa + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Liitettävä uudelleen + ballot_result: + attributes: + base: + total_count_invalid: Vastausten kokonaismäärä ei vastaa hyväksytty/tyhjä -erittelyä. + election: + attributes: + attachment: + needs_to_be_reattached: Liitettävä uudestaan + question_result: + attributes: + base: + blank_count_invalid: Tyhjien vastausten kokonaismäärä ei voi olla suurempi kuin tyhjien äänten kokonaismäärä. + trustee: + attributes: + name: + cant_be_changed: tietoa ei voi muuttaa + public_key: + cant_be_changed: tietoa ei voi muuttaa + voting: + attributes: + voting_type: + inclusion: "%{value} ei ole sallittu äänestystyyppi" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Äänestysvirkailija on jo äänestyspaikan esimies tai hoitaja. + voting: + different_organization: Äänestyksen tulee tapahtua sen organisaation alla, johon käyttäjä on rekisteröitynyt. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Äänestysvirkailijan on oltava samassa äänestyksessä kuin äänestyspaikka. + models: + decidim/elections/answer: + one: Vastaus + other: Vastaukset + decidim/elections/election: + one: Vaali + other: Vaalit + decidim/elections/question: + one: Kysymys + other: Kysymykset + decidim/voting: + one: Äänestys + other: Äänestystä + decidim/votings/census/dataset: + one: Tietoaineisto + other: Tietoaineistot + decidim/votings/census/datum: + one: Tieto + other: Tiedot + decidim/votings/polling_officer: + one: Äänestysvirkailija + other: Äänestysvirkailijat + decidim/votings/polling_station: + one: Äänestyspaikka + other: Äänestyspaikat + decidim/votings/voting: + one: Äänestys + other: Äänestykset + decidim: + admin: + filters: + officers_assigned_eq: + label: Virkailijat + values: + assigned: Määritetty + unassigned: Ei määritetty + role_eq: + label: Rooli + values: + manager: Virkailija + president: Esimies + unassigned: Ei määritetty + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Hae %{collection} nimellä, sähköpostilla, nimimerkillä tai äänestyspaikan perusteella. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Hae %{collection} otsikolla, osoitteella tai virkailijan nimellä, sähköpostilla tai nimimerkillä. + signed_eq: + label: Allekirjoitettu + values: + 'false': Allekirjoitettu + 'true': Ei allekirjoitettu + validated_eq: + label: Vahvistettu + values: + 'false': Vahvistamaton + 'true': Vahvistettu + voting_publications: + create: + error: Äänestyksen julkaisu epäonnistui. + success: Äänestyksen julkaisu onnistui. + destroy: + error: Äänestyksen julkaisun lopettaminen epäonnistui. + success: Äänestyksen julkaisun lopettaminen onnistui. + components: + elections: + actions: + vote: Äänestä + name: Vaalit + settings: + global: + announcement: Ilmoitus + step: + announcement: Ilmoitus + elections: + actions: + confirm_destroy: Oletko varma? + destroy: Tuhoa + edit: Muokkaa + feedback: Äänestäjän palaute + import: Tuo ehdotuksia vastauksiin + manage_answers: Hallitse vastauksia + manage_questions: Hallinnoi kysymyksiä + manage_steps: Hallinnoi vaiheita + new_answer: Uusi vastaus + new_election: Uusi vaali + new_question: Uusi kysymys + new_trustee: Uusi luottamushenkilö + preview: Esikatsele + publish: Julkaise + title: Toiminnot + unpublish: Lopeta julkaisu + admin: + answers: + create: + invalid: Vastauksen luonti epäonnistui. + success: Vastauksen luonti onnistui. + destroy: + invalid: Vastauksen poisto epäonnistui. + success: Vastauksen poisto onnistui. + edit: + title: Muokkaa vastausta + update: Päivitä vastausta + index: + invalid_max_selections: Sinun on valittava vielä %{missing_answers} valinta/valintaa täyttääksesi maksimivalintojen ehdon. + title: Vastaukset + new: + create: Luo vastaus + title: Uusi vastaus + not_selected: Ei valittu + select: + disable: Poista vastausvalinta + enable: Merkitse vastaus valituksi + invalid: Vastauksen valinta epäonnistui. + success: Vastauksen valinta onnistui. + selected: Valittu + unselect: + invalid: Vastauksen valinnan poisto epäonnistui. + success: Vastauksen valinnan poisto onnistui. + update: + invalid: Vastauksen päivitys epäonnistui. + success: Vastauksen päivitys onnistui. + elections: + create: + invalid: Vaalin luonti epäonnistui. + success: Vaalin luonti onnistui. + destroy: + invalid: Vaalin poisto epäonnistui. + success: Vaalin poisto onnistui. + edit: + title: Muokkaa vaalia + update: Päivitä vaali + form: + organization_time_zone: Tarkista, että organisaation aikavyöhyke on oikea organisaation asetuksissa. Nykyinen aikavyöhyke on %{time_zone} (%{time}). + index: + no_bulletin_board: Järjestelmässä ei ole Bulletin Board -palvelinta määritettynä, mitä tarvitaan käyttääksesi tätä moduulia. Tämä tehtävä tulisi toteuttaa yhteistyössä järjestelmänvalvojan kanssa. + title: Vaalit + new: + create: Luo vaali + title: Uusi vaali + publish: + success: Vaalin julkaisu onnistui. + unpublish: + success: Vaalin julkaisu epäonnistui. + update: + invalid: Vaalin päivitys epäonnistui. + success: Vaalin päivitys onnistui. + exports: + elections: Vaalit + feedback_form_answers: Palautelomakkeen vastaukset + mailers: + trustee_mailer: + body: + help_html: |- +

    Hei %{user_name},


    +

    Sinut on lisätty luottamushenkilöksi joihinkin vaaleihin %{organization} -alustalla.


    +

    Uusi osio nimeltä "Luottamushenkilön alue" on nyt näkyvissä käyttäjätilisi valikossa. Voit suorittaa sitä kautta sinulle määrättyjä tehtäviä tarpeen mukaan. Pääset alkuun luomalla tunnistautumisavaimesi.


    + subject: Sinut on lisätty luottamushenkilöksi kohteeseen %{resource_name} + trustee_zone: Siirry luottamusmiesten osioon + menu: + trustees: Luottamushenkilöt + models: + answer: + name: Vastaus + proposals_imports: + create: + invalid: Ehdotusten tuonti vastauksiksi epäonnistui. + success: "%{number} ehdotusta tuotiin vastauksiksi." + new: + create: Tuo ehdotuksia vastauksiksi + no_components: Tässä osallistumistilassa ei ole muita ehdotuskomponentteja, joista voisit tuoda ehdotuksia vastauksiksi. + select_component: Valitse komponentti + title: Tuo ehdotuksia + questions: + create: + election_started: Tämä äänestys on jo aloitettu. + invalid: Kysymyksen luonti epäonnistui. + success: Kysymyksen luonti onnistui. + destroy: + invalid: Kysymyksen poisto epäonnistui. + success: Kysymyksen poisto onnistui. + edit: + title: Muokkaa kysymystä + update: Päivitä kysymys + index: + title: Kysymykset + new: + create: Luo kysymys + title: Uusi kysymys + update: + invalid: Kysymyksen päivitys epäonnistui. + success: Kysymyksen päivitys onnistui. + steps: + create_election: + census: Henkilötietorekisteri + errors: + census_codes_generated: Henkilötietorekisterin äänestyskoodeja ei ole luotu. + census_frozen: Henkilötietorekisterin äänestyskoodeja ei ole viety. + census_uploaded: Tälle äänestykselle ei ole luotu henkilötietorekisteriä. + component_published: Vaalikomponenttia ei ole julkaistu. + fix_it_text: Korjaa + max_selections: Kysymyksillä ei ole oikeaa arvoa vastausten määrälle + minimum_answers: Kysymyksillä on oltava vähintään kaksi vastausta. + minimum_questions: Vaalilla on oltava vähintään yksi kysymys. + published: Vaalia ei ole julkaistu. + time_before: Alkamisaika on alle %{hours} tuntia ennen vaalin alkua. + trustees_number: Osallistumistilassa on oltava vähintään %{number} luottamushenkilöä, joilla on julkinen avain. + invalid: Vaalin asetusten määrittäminen epäonnistui + no_trustees: Tähän osallistumistilaan ei ole määritetty luottamushenkilöitä + not_used_trustee: "(ei käytössä)" + public_key: + 'false': vaalilla ei ole julkista avainta + 'true': vaalilla on julkinen avain + requirements: + census_codes_generated: Henkilötietorekisterin äänestyskoodit on luotu. + census_frozen: Henkilötietorekisterin äänestyskoodit on viety ja henkilötietorekisteri on lukittu. + census_uploaded: Henkilötietorekisteri on ladattu palveluun. + component_published: Vaalikomponentti on julkaistu. + max_selections: Kaikilla kysymyksillä on oikea arvo asetukselle vastausten maksimimäärä. + minimum_answers: Jokaisella kysymyksellä on vähintään 2 vastausta. + minimum_questions: Vaalilla on vähintään 1 kysymys. + published: Vaalia ei ole julkaistu. + time_before: Vaalin määritys tulee tapahtua vähintään %{hours} tuntia ennen sen alkamista. + trustees_number: Osallistumistilassa on oltava vähintään %{number} luottamushenkilöä, joilla on julkinen avain. + submit: Määritä vaali + success: Vaalin lähetys sähköiselle ääniuurnalle onnistui. + technical_configuration: + authority_name: "Viranomaisen nimi: %{value}" + bulletin_board_server: "Sähköisen ääniuurnan palvelin: %{value}" + scheme_name: "Skeeman nimi: %{value}" + title: Näytä tekniset tiedot + title: Määritä vaali + trustees: Vaalin luottamushenkilöt + created: + invalid: Avainseremonian käynnistäminen epäonnistui. + submit: Aloita avainseremonia + success: Avainseremonian aloituspyyntö on lähetetty sähköiselle ääniuurnalle. + title: Vaali luotu + trustees: Luottamushenkilöt + key_ceremony: + continue: Jatka + title: Avainseremonia + key_ceremony_ended: + errors: + time_before: Vaali on valmis alkamaan. Vaalin äänestysaika voidaan käynnistää aikaisintaan %{hours} tuntia ennen alkamisaikaa (%{start_time}). + invalid: Äänestysajan käynnistäminen epäonnistui. + requirements: + time_before: 'Äänestys alkaa pian. Voit käynnistää äänestysajan manuaalisesti tai se käynnistetään automaattisesti ennen alkamisaikaa: %{start_time}.' + submit: Käynnistä äänestys + success: Pyyntösi aloittaa äänestysaika on lähetetty sähköiselle ääniuurnalle. + title: Valmis alkamaan + processing: Käsitellään... + results_published: + answer: Vastaus + not_selected: Ei valittu + question: Kysymys + result: Tulos + selected: Valittu + submit: Lähetä + title: Tulokset julkaistu + tally_ended: + answer: Vastaus + not_selected: Ei valittu + question: Kysymys + result: Tulos + selected: Valittu + submit: Julkaise tulokset + success: Pyyntösi julkaista äänestyksen tulokset on lähetetty sähköiselle ääniuurnalle. + title: Ääntenlaskun tulokset + tally_started: + continue: Jatka + invalid: Poissaolevan luottamushenkilön ilmoittaminen epäonnistui. + mark_as_missing: Merkitse poissaolevaksi + mark_as_missing_description: Kaikkien luottamushenkilöiden pitäisi osallistua tähän prosessiin, mutta jos luottamushenkilö ei voi osallistua prosessiin, voit merkitä kyseisen henkilön poissaolevaksi. + success: Poissaolevan luottamushenkilön merkitseminen sähköiselle ääniuurnalle onnistui. + tally_completion: Prosessi valmistuu, kun kaikki luottamushenkilöt ovat aktiivisia tai merkitty poissaoleviksi. Vähintään %{quorum} luottamushenkilöä tarvitaan prosessin loppuun saattamiseksi. + title: Ääntenlasku + undo_mark_as_missing: Poissaoleva luottamushenkilö voi osallistua prosessiin ennen sen päättymistä. He voivat jatkaa normaalisti ja heidän poissaolonsa jätetään huomioimatta. + vote: + errors: + time_after: Vaali on edelleen käynnissä. Sinun on odotettava päättymisaikaan saakka (%{end_time}) päättääksesi äänestyksen. + invalid: Äänestysajan päättäminen epäonnistui. + requirements: + time_after: Äänestys on päättynyt. Voit sulkea äänestysajan manuaalisesti tai se suljetaan automaattisesti muutaman minuutin päästä. + submit: Päätä äänestysaika + success: Pyyntösi päättää äänestysaika on lähetetty sähköiselle ääniuurnalle. + title: Äänestysaika + vote_ended: + invalid: Ääntenlaskennan käynnistäminen epäonnistui. + submit: Aloita ääntenlaskenta + success: Pyyntösi aloittaa ääntenlaskenta on lähetetty sähköiselle ääniuurnalle. + text: Äänestys on päättynyt. Voit nyt aloittaa ääntenlaskun. + title: Äänestysaika on päättynyt + vote_stats: + no_vote_statistics_yet: Ei vielä äänitilastoja + title: Äänten tilastot + voters: Äänestäjät + votes: Äänet + trustees_participatory_spaces: + actions: + disable: Poista käytöstä + enable: Harkitse + create: + exists: Tällä osallistumistilalla on luottamushenkilö. + invalid: Luottamushenkilön luonti epäonnistui. + success: Luottamushenkilön luonti onnistui. + delete: + invalid: Luottamushenkilön poisto epäonnistui. + success: Luottamushenkilön poisto onnistui. + form: + select_user: Valitse käyttäjä + index: + title: Luottamushenkilöt + new: + create: Luo luottamushenkilö + title: Uusi luottamushenkilö + update: + invalid: Luottamushenkilön %{trustee} päivitys epäonnistui. + success: Luottamushenkilön %{trustee} päivitys onnistui. + admin_log: + election: + create: "%{user_name} loi vaalin %{resource_name} osallistumistilassa %{space_name}" + delete: "%{user_name} poisti vaalin %{resource_name} osallistumistilassa %{space_name}" + end_vote: "%{user_name} päätti äänestysajan vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + publish: "%{user_name} julkaisi vaalin %{resource_name} osallistumistilassa %{space_name}" + publish_results: "%{user_name} julkaisi tulokset vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + report_missing_trustee: "%{user_name} ilmoitti henkilön %{trustee_name} puuttuvaksi luottamushenkilöksi äänestyksen %{resource_name} sähköisellä vaaliuurnalla osallistumistilassa %{space_name}" + setup: "%{user_name} loi vaalin %{resource_name} osallistumistilan %{space_name} sähköiselle ääniuurnalle" + start_key_ceremony: "%{user_name} aloitti avainseremonian vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + start_tally: "%{user_name} aloitti ääntenlaskennan vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + start_vote: "%{user_name} aloitti äänestysajan vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + unpublish: "%{user_name} lopetti vaalin %{resource_name} julkaisemisen osallistumistilassa %{space_name}" + update: "%{user_name} päivitti vaalia %{resource_name} osallistumistilassa %{space_name}" + trustee: + create: "%{user_name} määritti käyttäjän %{trustee_user} luottamushenkilöksi" + connection: + failed: + modal: + close: Sulje + communication_lost: Valitettavasti näyttää siltä, että viestien välitys sähköisen ääniuurnan kanssa ei onnistu.
    Internet-yhteydessäsi voi olla ongelma tai ääniuurnan palvelin on liian kiireinen.
    Voit yrittää myöhemmin uudestaan tai ottaa yhteyttä tekniseen tukeen, jos ongelma jatkuu. + generic_error: Valitettavasti on tapahtunut tuntematon virhe. On todennäköistä, että selaintasi ei tueta tai käytät selaimen "näkymättömyystilaa" tai "yksityisen selauksen tilaa", joita ei tueta. + title: Jokin meni vikaan + election_m: + badge_name: + finished: Valmis + ongoing: Aktiivinen + upcoming: Tuleva + end_date: Päättyy + footer: + remaining_time: + one: "%{count} tunti ja %{minutes} minuuttia äänestysaikaa jäljellä." + other: "%{count} tuntia ja %{minutes} minuuttia äänestysaikaa jäljellä." + view: Näytä + vote: Äänestä + label: + date: Päivämäärät + questions: Kysymys %{count} + start_date: Alkaa + unspecified: Ei määritelty + elections: + count: + elections_count: + one: "%{count} vaali" + other: "%{count} vaalia" + election_log: + chained_hash: Tämän viestin ketjutettu tiiviste + complete: Viimeistele + creation_description: + complete: Vaali luotiin ja perustettiin onnistuneesti sähköiseen ääniuurnaan. + not_created: Vaalia ei ole vielä luotu. + creation_title: Vaali luotu + description: Tämä on vaalin loki, josta voit tarkastaa jokaisen vaiheen tilan, esimerkiksi milloin vaali luotiin, onko ääntenlasku suoritettu ja milloin vaali suljettiin. + download: Lataa + key_ceremony_description: + complete: Avainseremonia on onnistunut. Jokaisella luottamushenkilöllä on kelvolliset avaimet ja he ovat ladanneet tarvittavat avainten varmuuskopiot. + not_started: Avainseremonia ei ole vielä alkanut. + started: Avainseremonia on alkanut, mutta se ei ole vielä päättynyt. + key_ceremony_title: Avainseremonia + not_available: Ei vielä saatavilla + not_created: Ei luotu + not_ready: Ei valmis + not_started: Ei aloitettu + published: Julkaistu + results_description: + not_published: Tuloksia ei ole vielä julkaistu. + published: Tulokset on julkaistu. + results_title: Tulokset + started: Aloitettu + tally_description: + finished: Ääntenlasku on suoritettu. + not_started: Ääntenlasku ei ole vielä alkanut. + started: Ääntenlasku on alkanut. + tally_title: Ääntenlasku + title: Vaalin loki + unpublished: Julkaisematon + verifiable_results: + checksum: 'Tiedoston SHA256 tarkistussumma:' + description: + not_ready: Vahvistettava vaalin tiedosto ja SHA256-tarkistesumma eivät ole vielä käytettävissä. Heti kun tulokset julkistetaan, voit tarkistaa nämä vaalit. + ready: 'Täällä voit vahvistaa vaalin. Ensin sinun täytyy ladata tiedosto ja tarkastaa, että se ei ole korruptoitunut. Tarkastaaksesi tiedoston, aja seuraava komento ja tarkasta, että komennon näyttämä lopputulos vastaa tarkistussummaa:' + how_to_verify: 'Kun olet ladannut tiedoston ja tarkastanut sen olevan eheä, voit ajaa yleisen tarkastuksen. Kloonaa tämä repositorio ja aja seuraava komento sen juurihakemistossa:' + title: Vahvista vaalin tulokset + verifiable_file: 'Vaalin tarkastettava tiedosto:' + verify: Vahvista vaali + vote_description: + finished: Äänestys on päättynyt. + not_started: Äänestys ei ole vielä alkanut. + started: Äänestys on alkanut. + vote_title: Äänestysprosessi + filters: + active: Aktiiviset + all: Kaikki + date: Päivämäärä + finished: Valmiit + upcoming: Tulevat + preview: + available_answers: 'Vastausvaihtoehdot:' + description: 'Nämä kysymykset esitetään äänestyksen yhteydessä:' + title: Vaaliin liittyvät kysymykset + results: + description: 'Tässä on äänestyksen tulokset kaikille kysymyksille:' + percentage: "%{count}%" + selected: Valittu + title: Vaalin tulokset + votes: + one: "%{count} ääni" + other: "%{count} ääntä" + show: + action_button: + change_vote: Muuta ääntäsi + vote: Aloita äänestys + vote_again: Äänestä uudelleen + callout: + already_voted: Olet jo äänestänyt näissä vaaleissa. Voit muuttaa ääntäsi tai tarkastaa sen. + pending_vote: Ääntäsi kirjataan palvelimelle. + vote_rejected: Ääntäsi ei voida tarkastaa. Ole hyvä ja lähetä äänesi uudestaan. + election_log: Vaalin loki + preview: Esikatsele + verify: + already_voted: Oletko jo äänestänyt? + verify_here: Tarkista äänesi täältä. + will_verify: Voit tarkistaa äänesi, kun vaalit ovat alkaneet. + voting_period_status: + finished: Äänestys alkoi %{start_time} ja päättyi %{end_time} + ongoing: 'Äänestysaika päättyy: %{end_time}' + upcoming: Äänestys alkaa %{start_time} + feedback: + answer: + invalid: Palautteen lähettäminen epäonnistui. + spam_detected: Lomakkeeseen vastaaminen epäonnistui. Saatoit toimia liian nopeasti. Yrittäisitkö uudestaan? + success: Palautteen lähettäminen onnistui. + models: + answer: + fields: + proposals: Ehdotukset + selected: Valittu + title: Otsikko + votes: Äänet + election: + fields: + bb_status: Äänestystaulun tila + end_time: Päättymisaika + start_time: Alkamisaika + title: Otsikko + verifiable_results_file_hash: Tiedoston SHA256-tarkistussumma + verifiable_results_file_url: Todennettavissa oleva vaalitiedosto + question: + fields: + answers: Vastaukset + max_selections: Valintojen enimmäismäärä + title: Otsikko + trustees_participatory_space: + fields: + considered: harkittu + email: Sähköposti + inactive: ei käytössä + name: Nimi + notification: Ilmoituksen lähetysaika + public_key: Julkinen avain + status: Tila + orders: + label: Järjestä vaalit + older: Vanhimmat + recent: Uusimmat + trustee_zone: + elections: + backup_modal: + description: Näitä vaaleja luodaan äänestystaululle. On erityisen tärkeää, että jokainen luottamushenkilö luo varmuuskopion näistä avaimista ja tallentaa sen turvalliseen paikkaan. Tämän jälkeen, prosessi jatkuu. + download_election_keys: Lataa avaimet + title: Varmuuskopioi vaaliavaimet kohteelle %{election} + key_ceremony_steps: + back: Takaisin + description: Näitä vaaleja luodaan parhaillaan äänestystaululle. Jotta tämä prosessi voidaan saattaa loppuun, sinun tulee osallistua luottamushenkilönä. + keys: + create_election: Avainten luonti + key_ceremony: + joint_election_key: Yhteisen avaimen luonti + step_1: Avainten julkaisu + list: + status: Tila + task: Tehtävä + process_warning: Kun prosessi on käynnistetty, sinun ei tule poistua tältä sivulta ennen kuin prosessi päättyy. Se kestää useita minuutteja, koska kaikkiin luottamushenkilöihin tulee olla yhteydessä, jotta prosessi voidaan päättää. + start: Aloita + status: + completed: Valmis + pending: Odottaa + processing: Käsitellään + title: Luo vaaliavaimet kohteelle %{election} + restore_modal: + description: Äänestystaululle on liitetty tieto sinusta näiden vaalien luottamushenkilönä. Jatkaaksesi tätä prosessia, lataa oma varmuuskopiotiedostosi, joka luotiin edellisen istuntosi yhteydessä. + title: Palauta vaaliavaimet kohteelle %{election} + upload_election_keys: Lataa vaaliavaimet + tally_started_steps: + back: Takaisin + description: Näiden vaalien tuloksia lasketaan parhaillaan äänestystaululla. Jotta tämä prosessi voidaan saattaa loppuun, sinun tulee osallistua luottamushenkilönä. + keys: + end_tally: Ääntenlaskennan päättyminen + tally: + cast: Ääntenlaskennan tuloksen valmistuminen + share: Ääntenlaskennan tulosten jakaminen + list: + status: Tila + task: Tehtävä + process_warning: Kun prosessi on käynnistetty, sinun ei tule poistua tältä sivulta ennen kuin prosessi päättyy. Tämä kestää useita minuutteja, koska kaikkien luottamushenkilöiden tulee olla yhteydessä samanaikaisesti, jotta prosessi voidaan suorittaa. + start: Aloitettu + status: + completed: Valmis + pending: Odottaa + processing: Käsitellään + title: Ääntenlaskenta vaaleille %{election} + update: + error: Vaalin tilaa ei päivitetty. + success: 'Vaalin tila on: %{status}.' + menu: + trustee_zone: Luottamushenkilön alue + no_bulletin_board: + body: Tätä osiota varten vaaditaan äänestystaulupalvelin. Ota yhteyttä järjestelmänvalvojaan saadaksesi lisätietoja tästä. + title: Pahoittelut, äänestystalulua ei ole vielä määritetty. + trustees: + show: + elections: + list: + action_required: + 'false': 'Ei' + name: Tarvitaanko toimenpiteitä? + 'true': Suorita toimenpide + bb_status: Tila + election: Vaali + voting_period: Äänestysaika + no_elections: Tällä hetkellä sinulle ei ole määritetty mitään vastuuhenkilön tehtäviä. Saat ilmoituksen, kun sinun odotetaan suorittavan tehtäviä eri vaiheiden aikana. + title: Vaalit + identification_keys: + cancel: Peruuta + generate: Luo tunnistusavaimet + generate_error: Tunnistusavainten luonti epäonnistui. + generate_legend: Sinun tulee luoda tunnistusavainten parit toimiaksesi äänestyksen luottamushenkilönä. + generate_legend_1: Painikkeen painamisen jälkeen sinun tulee ladata tunnistusavainten tiedosto omalle tietokoneellesi. + generate_legend_2: Kopioi ladattu tiedosto puhtaalle ja tyhjälle USB-tallennuslaitteelle + generate_legend_3: Tarkasta, että tietokoneellesi ei jää kopiota kyseisestä tiedostosta (tarkasta myös Lataukset-kansio sekä työpöytäsi). + generate_legend_4: Luo toinen kopio tiedostosta toiselle ulkoiselle laitteelle ja talleta se erittäin turvalliseen paikkaan. + submit: Lähetä + submit_legend: Kun olet toteuttanut kaikki edellä esitetyt vaiheet, viimeistele julkisten tunnistusvainten lähetys palvelimelle. + submit_title: Lähetä julkinen tunnistusavain + title: Luottamushenkilöiden tunnistusavaimet + upload: Lataa tunnistusavaimet + upload_error: + invalid_format: Ladattu tiedosto ei sisällä yhtään tunnistusavainta. + invalid_key: Ladatussa tiedostossa olevia tunnistusavaimia ei voida ladata. + invalid_public_key: Ladatun tiedoston tunnistusavaimet eivät vastaa tallennettua julkista tunnistusavainta. + upload_legend: Julkiset tunnistusavaimet ovat palvelimella, mutta eivät selaimessasi. Sinun täytyy tuoda tunnistusavainten tiedosto omalle tietokoneellesi varmuuskopiosta, jonka tallensit avaimia luotaessa. + not_supported_browser_description: Näyttää siltä, että käytät selainta, jota ei voi käyttää luottamushenkilönä toimimiseen. Tarkasta, että käytät selaimesi viimeisintä versiota tai yritä käyttää jotain suosituimmista selaimista luottamushenkilön tehtävien suorittamiseksi. + not_supported_browser_title: Päivitä selaimesi toimiaksesi luottamushenkilönä + safari_warning_description: Näyttää siltä, että käytät Safari-selainta, jota ei tueta luottamushenkilönä toimimiseen salataksesi äänen (tämä johtuu Applen asettamista muistinkäyttörajoituksista). Mahdollisesti tämä voidaan korjata tulevaisuudessa, mikäli Apple muuttaa käytäntöjään tai vaalit-tilaa optimoidaan tulevaisuudessa paremmin. Ole hyvä ja käytä toista selainta tällä hetkellä. + safari_warning_title: Safari-selain tunnistettu + trustee_role_description: + with_keys: Sinulle on annettu luottamushenkilön tehtävä joissain vaaleissa tällä alustalla. + without_keys: Sinulle on annettu luottamushenkilön tehtävä. Luo ja lataa tunnistautumisavaimesi. + update: + success: Julkisen tunnistusavaimesi tallentaminen onnistui. + votes: + ballot_decision: + audit: ( Tarkasta äänesi ) + back: Aloita äänestysprosessi uudestaan + ballot_hash: 'Äänestyslippusi tunniste on:' + cast: Lähetä äänestyslippusi viimeistelläksesi äänestyksen + description_html: Tältä sivulta voit antaa äänesi tai tarkastaa, että se on salattu oikealla tavalla. Jos haluat tarkastaa äänesi oikeellisuuden, tutustu ohjeisiin, kuinka tarkastat äänesi. + header: 'Äänesi on salattu: lähetä tai tarkasta se' + casting: + header: Kirjataan ääntä... + text: Ääntäsi lähetetään sähköiseen ääniuurnaan. + confirm: + answer_number: 'vastaus #%{number}' + confirm: Vahvista + edit: muokkaa + header: Vahvista äänesi + intro: Tässä on yhteeveto äänestäsi, jota olet jättämässä.
    Tarkasta, että kaikki on oikein tai muuta vastauksiasi. + nota_option: Tyhjä + confirmed: + back: Takaisin vaaleihin + experience: Minkälainen käyttökokemuksesi oli? + feedback: Jätä meille palautetta + header: Ääni vahvistettu + lead: Äänesi on annettu! + text: 'Voit tarkastaa, että äänesi on jätetty onnistuneesti äänestyslaatikkoon seuraavalla tunnisteella: %{e_vote_poll_id}' + verify_link: Tarkastaaksesi tämän, kopioi äänen tunniste äänten vahvistussivulle + create: + error: Äänen jättämisessä tapahtui virhe. Ole hyvä ja yritä uudelleen. + encrypting: + header: Salataan ääntä... + text: Äänesi salataan, jotta vaalisalaisuus voidaan taata. + failed: + header: Äänestys epäonnistui + lead: Ääntäsi ei ole annettu! + text: Jokin meni pieleen, yritä uudelleen. + try_again: Yritä uudestaan + header: + ballot_decision: Lähetä tai tarkasta äänesi + confirm: Vahvista äänesi + election: Vaali + register: Luo tunnus + vote_for: Äänestä - %{title} + messages: + invalid_token: Istuntosi äänestyskopissa ei ole kelvollinen. Yritä äänestää uudelleen. + not_allowed: Et voi äänestää tässä vaalissa tällä hetkellä. + modal: + close: Sulje + proposal_header: 'Ehdotukset:' + new: + answer_choices: Voit valita enintään %{choices} vastausta + more_information: Lisätietoa + nota_option: Tyhjä / Ei mitään näistä + preview_alert: Tämä on äänestyskopin esikatselunäkymä. + question_steps: Kysymys %{current_step} / %{total_steps} + selections: "Valittu
    \n%{selected} / %{max_selections}" + onboarding_modal: + create_account: Luo tili + description: Haluatko luoda oman käyttäjätilin alustalle? Voit osallistua prosesseihin ja alustalla esitettyjen asioiden kehittämiseen. + no_account: Ei kiitos. + title: Oletko uusi käyttäjä tässä palvelussa? + update: + error: Äänen tilan päivittäminen epäonnistui valitettavasti. Ole hyvä ja äänestä uudestaan. + verify: + content: + heading: Vahvista äänesi + info: Tämä tarkastustyökalu varmentaa, että salatulla merkkijonolla löytyvä äänesi on annettu oikein ja se on vaaliuurnassa. + error: + header: Ääntä ei löytynyt! + info: Äänestystunnusta ei löytynyt vaaliuurnasta %{link}, yritä uudelleen. + form: + back: Takaisin alustalle + submit: Tarkista + vote_identifier: 'Äänen tunniste:' + vote_identifier_help: Tämä tunniste annettiin sinulle äänen jättämisen jälkeen (se ei ole koodi, jota käytit päästäksesi äänestyskoppiin). + header: + title: Vahvista äänesi + success: + header: Äänesi löytyi! + info: Salattu äänesi on vaaliuurnassa %{link}. + voting_step: + back: Takaisin + continue: Seuraava + warnings: + empty_filters: Vaaleja ei löytynyt annetuilla hakuehdoilla. + no_elections: Tällä hetkellä yhtään vaalia ei ole aikataulutettu. + no_scheduled_elections: Tällä hetkellä yhtään vaalia ei ole aikataulutettu, mutta löydät täältä kaikki aikaisemmat vaalit. + events: + elections: + election_published: + email_intro: 'Vaali %{resource_title} on nyt aktiivisena osallistumistilassa %{participatory_space_title}. Voit tutustua siihen tältä sivulta:' + email_outro: Tämä ilmoitus on lähetetty sinulle, koska seuraat kohdetta %{participatory_space_title}. Voit lopettaa ilmoitusten vastaanottamisen edellä esitetyn linkin kautta. + email_subject: Vaali %{resource_title} on nyt aktiivisena osallistumistilassa %{participatory_space_title}. + notification_title: Vaali %{resource_title} on nyt aktiivisena osallistumistilassa %{participatory_space_title}. + trustees: + new_election: + email_intro: Sinut on lisätty luottamushenkilöksi vaaliin %{resource_title}. + email_outro: Tämä ilmoitus on lähetetty sinulle, koska sinulle on myönnettu luottamushenkilön tehtävä kohteessa %{resource_title}. + email_subject: Olet luottamushenkilönä vaalissa %{resource_title}. + notification_title: Sinulle on annettu luottamushenkilön tehtävä vaalissa %{resource_title}. Suorita avainseremonia aloittaaksesi vaalin. + new_trustee: + email_intro: Hallinnointikäyttäjä on myöntänyt sinulle luottamushenkilön tehtävän kohteessa %{resource_name}. Sinun tulee luoda julkinen tunnistusavain omalla luottamushenkilöiden alueellasi + email_outro: Tämä ilmoitus on lähetetty sinulle, koska sinulle on myönnettu luottamushenkilön tehtävä kohteessa %{resource_name}. + email_subject: Olet luottamushenkilö kohteessa %{resource_name}. + notification_title: Sinulle on annettu luottamushenkilön tehtävä kohteen %{resource_name} joissain vaaleissa, jotka suoritetaan tällä alustalla.
    Voit suorittaa tehtävät sitä mukaa, kun vaali etenee. Aloita luomalla tunnistautumisavaimesi. + start_tally: + email_intro: Vaalin %{resource_title} äänestysaika on päättynyt. Suorita nyt ääntenlaskenta kyseiselle vaalille julkaistaksesi tulokset. + email_outro: Tämä ilmoitus on lähetetty sinulle, koska olet vastuuhenkilönä vaalissa %{resource_title}. + email_subject: Ääntenlaskenta vaalille %{resource_title} on alkanut. + notification_title: Vaalin %{resource_title} äänestysaika on päättynyt. Suorita nyt ääntenlaskenta kyseiselle vaalille julkaistaksesi tulokset. + votes: + accepted_votes: + email_intro: 'Äänesi hyväksyttiin! Voit tarkastaa äänesi täältä käyttäen äänestystunnustasi: %{encrypted_vote_hash}.' + email_outro: Tämä ilmoitus on lähetetty sinulle, koska äänestit vaaleissa %{resource_name}. + email_subject: Äänesi vaaleihin %{resource_name} hyväksyttiin. + notification_title: 'Äänesi hyväksyttiin. Voit tarkastaa äänesi täältä käyttäen äänestystunnustasi: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Sinulle on määritetty rooli %{role} äänestyspaikalla %{polling_station_name} osiossa %{resource_title}. Voit hallinnoida äänestyspaikkaa äänestysvirkailijoille määritetyssä osiossa. + email_outro: Tämä ilmoitus on lähetetty sinulle, koska sinulle on myönnettu rooli %{role} äänestyspaikalle %{polling_station_name}. + email_subject: Roolisi äänestyspaikalla %{polling_station_name} on %{role}. + notification_title: Roolisi äänestyspaikan %{polling_station_name} äänestyksessä %{resource_title} on %{role}. + send_access_code: + instruction: 'Tässä on tunnistautumiskoodisi, jota pyysit: %{access_code}. Koodin avulla voit osallistua äänestykseen %{voting}.' + subject: Tunnistautumiskoodisi osallistuaksesi äänestykseen %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Äänestys on osallistumistila, jossa voit kysyä selkeitä kysymyksiä kaikilta organisaation ihmisiltä, päättää äänestykseen osallistumisesta sekä kannustaa ja järjestää keskusteluita tietyn lopputuloksen puolesta tai sitä vastaan. Kun äänestysaika alkaa, voit äänestää ja julkaista äänestystuloksen.

    Esimerkkejä: äänestyksiä voidaan järjestää lähes mistä tahansa organisaatioon vaikuttavasta asiasta, kuten nimen tai logon muuttamisesta, päättämisestä laajempaan organisaatioon liittymisestä, strategisen suunnitelman tai työryhmän päätöksen hyväksymisestä tai hylkäämisestä, tai tarvittavien mandaattien määrän päättämisestä.

    \n" + page: "

    Äänestys on osallistumistila, jossa voit kysyä selkeitä kysymyksiä kaikilta organisaation ihmisiltä, päättää äänestykseen osallistumisesta sekä kannustaa ja järjestää keskusteluita tietyn lopputuloksen puolesta tai sitä vastaan. Kun äänestysaika alkaa, voit äänestää ja julkaista äänestystuloksen.

    Esimerkkejä: äänestyksiä voidaan järjestää lähes mistä tahansa organisaatioon vaikuttavasta asiasta, kuten nimen tai logon muuttamisesta, päättämisestä laajempaan organisaatioon liittymisestä, strategisen suunnitelman tai työryhmän päätöksen hyväksymisestä tai hylkäämisestä, tai tarvittavien mandaattien määrän päättämisestä.

    \n" + title: Mitä ovat äänestykset? + menu: + votings: Äänestykset + participatory_spaces: + related_elections: + see_all: Näytä kaikki vaalit + statistics: + elections_count: Vaalia + votings_count: Äänestystä + votings: + admin: + ballot_styles: + create: + error: Äänestystyylin luonti epäonnistui. + success: Äänestystyylin luonti onnistui. + destroy: + invalid: Äänestystyylin poisto epäonnistui. + success: Äänestystyylin poisto onnistui. + edit: + title: Muokkaa äänestystyyliä + update: Päivitä + form: + code_help: 'Vihje: koodi yhdistää henkilötietorekisterin äänestystyyliin. Kun henkilötietorekisterin tiedot ladataan järjestelmään, jokaiselle tietueelle luodaan äänestystyyli koodin perusteella.' + election: Vaali + questions: Äänestystyylin kysymykset + questions_help: 'Vihje: valitse tähän äänestystyyliin määritetyille äänestäjille kysymykset vaalikomponenteista.' + index: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + edit: Muokkaa + new: Luo äänestystyyli + title: Toiminnot + associated_census_data: Liitetyt henkilötietorekisterin tiedot + explanation_callout: Äänestystyyli määrittelee, mitä kysymyksiä äänestäjälle esitetään äänestyskopissa. Voit valita äänestystyylille kysymykset äänestyslappuun tämän äänestyksen vaalikomponenteista. Äänestystyylin koodeja käytetään henkilötietorekisterissä olevien henkilöiden yhdistämiseksi äänestyslappuihin. Älä luo äänestystyylejä, jos haluat esittää kaikki kysymykset kaikille äänestäjille. + title: Äänestystyylit + new: + create: Luo + title: Luo äänestystyyli + update: + invalid: Äänestystyylin päivitys epäonnistui. + success: Äänestystyylin päivitys onnistui. + content_blocks: + attachments_and_folders: + name: Äänestyksen liitteet ja kansiot + header: + name: Äänestyksen otsikko + highlighted_votings: + max_results: Näytettävien elementtien enimmäismäärä + html_block_1: + name: Äänestyksen HTML-lohko 1 + html_block_2: + name: Äänestyksen HTML-lohko 2 + html_block_3: + name: Äänestyksen HTML-lohko 3 + main_data: + name: Otsikko ja kuvaus + metrics: + name: Äänestyksen tilastomittarit + polling_stations: + name: Äänestyspaikat + related_elections: + name: Äänestyksen vaalit + stats: + name: Äänestyksen tilastot + timeline: + name: Äänestyksen aikajana + index: + published: Julkaistu + unpublished: Julkaisematon + menu: + votings: Äänestykset + votings_submenu: + attachment_collections: Kansiot + attachment_files: Tiedostot + attachments: Liitteet + ballot_styles: Äänestystyylit + census: Henkilötietorekisteri + components: Komponentit + info: Tietoa tästä äänestyksestä + landing_page: Laskeutumissivu + monitoring_committee: Tarkkailukomitea + monitoring_committee_election_results: Vahvista tulokset + monitoring_committee_members: Jäsenet + monitoring_committee_polling_station_closures: Vahvista varmenteet + monitoring_committee_verify_elections: Vahvista vaali + polling_officers: Äänestysvirkailijat + polling_stations: Äänestyspaikat + see_voting: Näytä äänestys + models: + ballot_style: + fields: + code: Koodi + monitoring_committee_member: + fields: + email: Sähköposti + name: Nimi + polling_officer: + fields: + email: Sähköposti + name: Nimi + polling_station: Äänestyspaikka (rooli) + polling_station: + fields: + address: Osoite + polling_station_managers: Virkailijat + polling_station_president: Esimies + title: Otsikko + voting: + fields: + created_at: Luonnin ajankohta + published: Julkaistu + title: Otsikko + monitoring_committee_election_results: + actions: + title: Toiminnot + view: Näytä + index: + title: Valitse vaali, jonka tuloksia haluat tarkastella + results: + bulletin_board: Sähköinen ääniuurna + election_totals: Vaali yhteensä + polling_stations: Äänestyspaikat + result_types: + blank_answers: Tyhjät vastaukset + blank_ballots: Tyhjät äänet + null_ballots: Hylätyt äänet + total_ballots: Äänten kokonaismäärä + valid_ballots: Kelvolliset äänet + selected: Valittu + title: Vaalin %{election_title} tulokset + totals: Yhteensä + show: + change_election: Muuta vaalia + publish_results: Julkaise tulokset + publishing: Tuloksia julkaistaan... + update: + invalid: Tulosten julkaiseminen epäonnistui. + rejected: Tulosten julkaiseminen evättiin sähköisessä ääniuurnassa. Yritä uudestaan tai ota yhteyttä järjestelmänvalvojaan. + success: Tulosten julkaiseminen onnistui. + monitoring_committee_members: + create: + invalid: Tarkkailukomitean jäsenen luonti epäonnistui. + success: Tarkkailukomitean jäsenen luonti onnistui. + destroy: + invalid: Tarkkailukomitean jäsenen poistaminen epäonnistui. + success: Tarkkailukomitean jäsenen poistaminen onnistui. + form: + existing_user: Olemassa oleva osallistuja + non_user: Kutsu uusi osallistuja + select_user: Etsi nimellä, sähköpostilla tai nimimerkillä + user_type: Osallistujan tyyppi + index: + title: Tarkkailukomitea + new: + create: Luo + title: Luo tarkkailukomitean jäsen + monitoring_committee_polling_station_closures: + actions: + title: Toiminnot + validate: Vahvista + view: Näytä + closures: + change_election: Muuta vaalia + signed: Allekirjoitettu? + title: Vaalin %{election_title} äänestyspaikat + validated: Vahvistettu? + edit: + change_polling_station: Takaisin äänestyspaikkoihin + monitoring_committee_notes: Huomautukset + monitoring_committee_notes_placeholder: Ilmoita kaikista huomionarvoisista tapahtumista täällä + title: Vaalin %{election_title} tulokset äänestyspaikalla %{polling_station_title} + elections: + title: Valitse vaali, jonka haluat vahvistaa + show: + change_polling_station: Takaisin äänestyspaikkoihin + monitoring_committee_notes: Huomiot tarkkailukomitealle + validate: + error: Äänestyksen päättämisen vahvistaminen epäonnistui. + success: Äänestyksen päättämisen vahvistaminen onnistui. + monitoring_committee_verify_elections: + index: + download: Lataa + how_to_checksum: 'Varmistaaksesi, että lataamasi tiedosto ei ole korruptoitunut tai muuttunut lataamisen aikana, aja seuraava komento konsolissasi ja tarkasta, että komennon antama tarkistussumma vastaa yllä esitettyä tarkistussummaa:' + how_to_download: Vahvistaaksesi vaalin, lataa sen vahvistettava tiedosto yllä olevasta taulukosta. + how_to_run_verifier: 'Kun olet ladannut tiedoston ja tarkastanut sen olevan eheä, voit ajaa yleisen tarkastuksen. Kloonaa tämä repositorio ja aja seuraava komento sen juurihakemistossa:' + how_to_title: Kuinka vaalin oikeellisuus vahvistetaan + not_available: Ei vielä saatavilla + title: Vaalit + polling_officers: + create: + invalid: Äänestysvirkailijan luonti epäonnistui. + success: Äänestysvirkailijan luonti onnistui. + destroy: + invalid: Äänestysvirkailijan poistaminen epäonnistui. + success: Äänestysvirkailijan poistaminen onnistui. + form: + existing_user: Olemassa oleva osallistuja + non_user: Kutsu uusi osallistuja + select_user: Etsi nimellä, sähköpostilla tai nimimerkillä + user_type: Osallistujan tyyppi + index: + role_manager: virkailija + role_president: esimies + title: Äänestysvirkailijat + new: + create: Luo + title: Luo äänestysvirkailija + polling_officers_picker: + choose_polling_officers: Valitse äänestysvirkailijat + no_polling_officers: Yksikään äänestysvirkailija ei vastaa hakuehtojasi tai yhtään äänestysvirkailijaa ei vielä ole määritetty. + polling_stations: + create: + invalid: Äänestyspaikan luonti epäonnistui. + success: Äänestyspaikan luonti onnistui. + destroy: + invalid: Äänestyspaikan poistaminen epäonnistui. + success: Äänestyspaikan poistaminen onnistui. + edit: + title: Muokkaa äänestyspaikkaa + update: Päivitä äänestyspaikkaa + form: + address_help: 'Osoite: käytetään geokoodauksessa sijainnin löytämiseen' + location_help: 'Sijaintitieto: äänestäjille näytettävä viesti, jolla selvennetään äänestyspaikan tarkkaa sijaintia' + location_hints_help: 'Sijainnin lisätiedot: sijaintia tarkentava lisätieto. Esimerkiksi: äänestyspaikan kerros rakennuksessa.' + polling_station_managers_help: 'Äänestyspaikan virkailijat: ne virkailijat, jotka hallinnoivat äänestyspaikkaa. Tarkasta, että virkailijat on jo luotu Äänestysvirkailijat-osiossa ja heitä ei ole määrätty mihinkään muuhun äänestyspaikkaan' + polling_station_president_help: 'Äänestyspaikan esimies: se virkailija, joka toimii äänestyspaikan esimiehenä. Tarkasta, että virkailija on jo luotu Äänestysvirkailijat-osiossa ja häntä ei ole määrätty mihinkään muuhun äänestyspaikkaan.' + select_president: Valitse äänestysvirkailija äänestyspaikan esimieheksi + index: + title: Äänestyspaikat + new: + create: Luo + title: Luo äänestyspaikka + update: + invalid: Äänestyspaikan luonti epäonnistui. + success: Äänestyspaikan päivitys onnistui. + titles: + votings: Äänestykset + votings: + actions: + confirm_destroy: Oletko varma? + destroy: Tuhoa + new_voting: Uusi äänestystila + create: + invalid: Äänestyksen luonti epäonnistui. + success: Äänestyksen luonti onnistui. + edit: + add_election_component: Tälle äänestykselle ei ole määritetty vaalia. Lisää vaali Komponentit-osiosta. + assign_missing_officers: Joillekin äänestyspaikoille ei ole määrätty esimiehiä ja/tai virkailijoita. Määritä virkailijat Äänestyspaikat-osiosta. + update: Päivitä + form: + banner_image: Bannerikuva + census_contact_information: Henkilötietorekisterin vastuutahon yhteystiedot + census_contact_information_help: Nämä yhteystiedot ovat tarkoitettu osallistujille, jotka haluavat ilmoittaa virheistä henkilötietorekisterissä. Yhteystieto voi olla sähköpostiosoite, yhteydenottolomake toisella sivustolla, avoin verkkokysely, tms. + introductory_image: Esittelykuva + promoted: Korostettu + select_a_voting_type: Valitse äänestystapa + show_check_census_help: Määrittää, näytetäänkö "Voinko äänestää?" -linkki julkisten äänestysten sivuilla. + slug: Tunniste + slug_help_html: 'URL-tunnisteita käytetään luomaan URL-osoitteita, jotka viittaavat tähän äänestykseen. Hyväksyy vain kirjaimet, numerot ja viivat. Kirjaimen on oltava ensimmäinen merkki tunnisteessa. Esimerkki: %{url}' + title: Otsikko + voting_type: + hybrid: Yhdistetty äänestystapa + in_person: Fyysinen äänestys + online: Verkkoäänestys + voting_type_label: Äänestystapa + new: + create: Luo + title: Uusi äänestys + update: + invalid: Äänestyksen päivitys epäonnistui. + success: Äänestyksen päivitys onnistui. + admin_log: + ballot_style: + create: "%{user_name} loi äänestystyylin koodilla %{ballot_style_code} osallistumistilassa %{space_name}" + delete: "%{user_name} poisti äänestystyylin koodilla %{ballot_style_code} osallistumistilasta %{space_name}" + update: "%{user_name} päivitti äänestystyyliä koodilla %{ballot_style_code} osallistumistilassa %{space_name}" + census: + create: "%{user_name} loi henkilötietorekisterin osallistumistilalle %{space_name}" + delete: "%{user_name} poisti henkilötietorekisterin osallistumistilasta %{space_name}" + update: "%{user_name} päivitti henkilötietorekisteriä osallistumistilassa %{space_name}" + monitoring_committee_member: + create: "%{user_name} määritti käyttäjän %{monitoring_committee_member_user} tarkkailukomitean jäseneksi osallistumistilassa %{space_name}" + delete: "%{user_name} poisti käyttäjän %{monitoring_committee_member_user} tarkkailukomitean jäsenyyden osallistumistilassa %{space_name}" + polling_officer: + create: "%{user_name} määritti käyttäjän %{polling_officer_user} äänestysvirkailijaksi osallistumistilassa %{space_name}" + delete: "%{user_name} poisti käyttäjän %{polling_officer_user} äänestysvirkailijan roolin osallistumistilasta %{space_name}" + polling_station: + create: "%{user_name} loi äänestyspaikan %{resource_name} osallistumistilaan %{space_name}" + delete: "%{user_name} poisti äänestyspaikan %{resource_name} osallistumistilasta %{space_name}" + update: "%{user_name} päivitti äänestyspaikkaa %{resource_name} osallistumistilassa %{space_name}" + voting: + create: "%{user_name} loi äänestyksen %{resource_name}" + publish: "%{user_name} julkaisi äänestyksen %{resource_name}" + unpublish: "%{user_name} lopetti äänestyksen %{resource_name} julkaisun" + census: + admin: + census: + create: + invalid: Henkilötietorekisterin lataaminen epäonnistui, yritä uudestaan myöhemmin. + invalid_csv_header: CSV-otsakkeet puuttuvat puuttuvat tai ovat virheellisiä - lue ohjeet huolellisesti. + creating_data: + info_message: "Odota hetki, käsitelty %{processed_count} / %{raw_count} riviä tiedostosta %{file} (tämä voi kestää useita minuutteja)." + delete: + button: Poista kaikki henkilötietorekisterin tiedot + confirm: Väestörekisterin tietojen poistamista ei voi peruuttaa jälkikäteen. Haluatko varmasti jatkaa? + destroy: + error: Henkilötietorekisterin poistaminen epäonnistui, yritä uudestaan myöhemmin. + success: Henkilötietorekisterin tiedot poistettu. + export_access_codes: + button: Vie äänestyksen tunnistautumiskoodit + callout: Voit nyt viedä tunnistautumiskoodit järjestelmästä. Tämä voidaan tehdä vain kerran. Kun aloitat viennin, sinulle lähetetään tarvittavat ohjeet sähköpostiin %{email} + confirm: Voit viedä tunnistautumiskoodit vain kerran. Varmista, että pääset käsiksi sähköpostitiliin %{email}. + file_not_exist: Tiedostoa ei ole olemassa. + launch_error: Tunnistautumiskoodien viennin aloittaminen epäonnistui. + launch_success: Tunnistautumiskoodien vienti aloitettu. Saat sähköpostia hetken kuluttua sähköpostiosoitteeseen %{email}. + exporting_access_codes: + info_message: "Odota hetki, vientiä valmistellaan, saat hetken kuluttua viestin sähköpostiisi osoitteeseen %{email} (tämä voi kestää useita minuutteja)." + freeze: + callout: Henkilötietorekisteri on jäädytetty, minkä takia sitä ei voi muokata. + help_html: | + Henkilötietorekisterin tiedot on ladattu, äänestyskoodien luonti ja vienti on onnistunut.
    + Voit nyt aloittaa äänestyksen.
    + Käytä vietyä CSV-tiedostoa ja toimita siinä näkyvät äänestyskoodit henkilötietorekisterin henkilöille haluamallasi tavalla tai ota käyttöön "Voinko äänestää" -välilehti mahdollistaaksesi osallistujille heidän omien koodiensa tilaaminen henkilötietorekisteriin merkittyjen tietojen perusteella. + generate_access_codes: + button: Luo äänestyksen tunnistautumiskoodit + callout: Voit nyt jatkaa tunnistautumiskoodien luontiin. Huomioithan, että tunnistautumiskoodien luonnin jälkeen, et voi enää muokata henkilötietorekisteriä. + confirm: Jos jatkat, et voi enää muokata henkilötietorekisteriä. + info_message_all: "Tietojen tuonti onnistui tiedostosta %{file} (yhteensä tuotu %{raw_count} / %{data_count} riviä)." + info_message_warn: Tarkasta, että mitään tietoja ei puutu, koska %{data_count} tietuetta luotiin ja ladatussa tiedostossa %{file} on %{raw_count} riviä. + launch_error: Tunnistautumiskoodien luonnin aloittaminen epäonnistui + launch_success: Koodien luonti aloitettu. + start_over: Poista nykyinen henkilötietorekisteri ja aloita alusta oikeamuotoisella CSV-tiedostolla, joka sisältää ohjeiden mukaan muotointuja rivejä. + generating_access_codes: + info_message: "Odota hetki, äänestyskoodeja luodaan (tämä voi kestää useita minuutteja)..." + new: + file_help: + explanation: 'Tiedoston ohjeistus:' + message_1: Vain CSV (.csv) tiedostot ovat sallittuja. + message_2: Erotin sarakkeiden välillä on oltava puolipiste (";"). + has_ballot_styles_message: Olet määrittänyt äänestystyylejä. Pidä huolta siitä, että "%{ballot_style_code_header}" -kenttä CSV-tiedostossa vastaa rivin äänestystyylin koodia. + info_message: "Henkilötietorekisteriä ei ole vielä olemassa. Käytä alla olevaa lomaketta luodaksesi rekisterin tuomalla tiedot CSV-tiedostosta." + missing_ballot_styles_message: 'Tälle äänestykselle ei ole määritetty yhtään äänestystyyliä. Jos haluat lisätä ehdollisia kysymyksiä (ts. esittää äänestäjille eri kysymyksiä esimerkiksi riippuen heidän asuinalueestaan), sinun on määritettävä äänestystyylit ennen henkilötietorekisterin lataamista järjestelmään. Jos haluat esittää kaikille äänestäjille samat kysymykset, voit jatkaa henkilötietorekisterin lataamiseen.' + submit: Lähetä CSV + title: Luo henkilötietorekisteri + show: + heading: Äänestystilan henkilötietorekisteri + upload_info: + csv_example_with_ballot_style: 'Esimerkki tiedostosta, jossa on äänestystyylejä:' + csv_example_without_ballot_style: 'Esimerkki tiedostosta, jossa ei ole äänestystyylejä:' + csv_header_after: Älä lisää viimeistä saraketta ("%{ballot_style_code_header}"), jos et tarvitse äänestystyylejä tai ehdollisia kysymyksiä + csv_header_before: 'Henkilötietorekisterin tiedot tulee antaa CSV-tiedostossa seuraavien otsakekenttien kanssa:' + document_types: + identification_number: Henkilötunnus + passport: Passi + export_mailer: + access_codes_export: + click_button: 'Voit ladata tunnistautumiskoodit seuraavan linkin kautta.
    Tiedosto on ladattavissa %{date} saakka.
    Tarvitset 7-Zip-ohjelman (Windowsille), Keka-ohjelman (macOS-käyttöjärjestelmille) tai PeaZip-ohjelman (Linux-käyttöjärjestelmille) avataksesi tiedoston. Salasana tiedoston avaamista varten: %{password}' + download: Lataa + subject: Äänestyksen tunnistautumiskoodien vientitiedosto äänestykselle %{voting_title} on nyt ladattavissa + vote_flow: + already_voted_in_person: Tämä osallistuja on jo äänestänyt fyysisessä tilaisuudessa eikä hänellä ole oikeutta äänestää verkossa. + datum_not_found: Annetut tiedot eivät vastaa yhtään äänestäjää. + content_blocks: + highlighted_votings: + name: Korostetut äänestykset + landing_page: + polling_stations: + heading: Äänestyspaikat + no_polling_stations: Äänestyspaikkoja ei vielä ole. + monitoring_committee_members: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + new: Uusi jäsen + title: Toiminnot + pages: + home: + highlighted_votings: + active_spaces: Aktiiviset äänestykset + see_all_spaces: Näytä kaikki äänestykset + polling_officer_zone: + closures: + back_to_polling_stations: Takaisin äänestyspaikkoihin + certify: + add_photos: Lisää kuvia + edit_photos: Muokkaa kuvia + error: Varmenteen liittäminen epäonnistui, yritä uudestaan. + heading: Äänten uudelleenlaskenta - Lataa varmenne + info_text: Lataa kuva äänestyksen päättämisen varmenteesta. + submit: Lataa varmenne + success: Varmenteen lataaminen onnistui. + upload_photos: Lataa kuva äänestyksen päättämisen todistuksesta + completed: + sub_heading: Uudelleenlaskenta on vahvistettu ja sitä ei voi enää muokata. + create: + error: Äänestyksen päättämisen luonti epäonnistui, yritä uudestaan myöhemmin. + success: Äänestyksen päättämisen luonti onnistui. + destroy: + error: Äänestyksen päättämisen poistamisessa tapahtui virhe. + success: Äänestyksen päättämisen poisto onnistui. + edit: + confirm_start_over: Tämä poistaa äänten kokonaismäärän ja kaikki liitetyt muistiinpanot. Haluatko varmasti jatkaa? + heading: Äänten uudelleenlaskenta - Vastausten uudelleenlaskenta + info_text: Määritä jokaisen kysymyksen vastausten kokonaismäärä. Tämän tulee vastata edellisessä vaiheessa määritettyä vastausten kokonaismäärää (yhteensä %{total} vastausta). + modal_ballots_results_count_error: + blank: Oletettu tyhjien äänten määrä on %{expected}, mutta tyhjien vastausten yhteismäärä on %{current}. + close_modal: Sulje + info_text: Äänten kokonaismäärä ei vastaa kirjeiden kokonaismäärää. Tarkasta äänten kokonaismäärä. + title: Äänten kokonaismäärä ei täsmää + total: Oletettu äänten määrä on %{expected}, mutta hyväksyttyjen ja tyhjien äänten yhteismäärä on %{current}. + valid: Oletettu hyväksyttyjen äänten määrä on %{expected}, mutta hyväksyttyjen vastausten yhteismäärä on %{current}. + save_recount: Tallenna uudelleenlaskenta + start_over: Jos kokonaismäärässä on virhe, voit poistaa kaikki tiedot ja aloittaa alusta. + total_ballots: Äänten kokonaismäärä + total_blank_ballots: Tyhjien äänten kokonaismäärä + total_null_ballots: Hylättyjen äänten kokonaismäärä + total_valid_ballots: Kelvollisten äänten kokonaismäärä + new: + election: 'Vaali:' + heading: Äänten uudelleenlaskenta + info_text: 'Syötä tällä äänestyspaikalla uudelleenlaskettavien äänten (kirjeiden) kokonaismäärä:' + modal_ballots_count_error: + btn_validate_total: Tarkasta äänten uudelleenlaskenta + info_explanation_text: 'Tarkasta äänten kokonaismäärä. Jos kokonaismäärä ei ole oikea, sinun on annettava selvitys tarkkailukomitealle:' + info_text: Syötettyjen äänten (kirjeiden) kokonaismäärä ei täsmää ihmisten määrään, jotka ovat äänestäneet tällä äänestyspaikalla. + message_for_monitoring_committee: Viesti tarkkailukomitealle + review_recount: Tarkasta uudelleenlaskenta + text_area_placeholder: Kirjoita viestisi + title: Tietueiden kokonaismäärä ei täsmää + total_ballots: 'Äänten kokonaismäärä:' + total_people: 'Henkilöt yhteensä:' + polling_station: 'Äänestyspaikka:' + submit: Vahvista kokonaismäärä + total_ballots_count: Äänten kokonaismäärä + show: + edit_count_votes: Syötitkö väärät numerot? Voit edelleen muokata niitä. + heading: Äänten uudelleenlaskenta + sub_heading: Äänten uudelleenlaskenta tulee sulkea lataamalla todistus ääntenlaskennan päättämisestä. Tämän jälkeen uudelleenlaskenta sinetöidään ja sitä ei voi enää muokata. + sign: + cancel: Peruuta + check_box: Olen tarkastanut tämän ja se on sama kuin fyysisen äänestyksen päättämisen todistus + confirm: Ok, jatka + error: Tapahtui jokin virhe. Yritä uudestaan. + heading: Äänten uudelleenlaskenta - Allekirjoita äänestyksen päättäminen + info_text: Jos jatkat, et voi enää muokata mitään tietoja. Tätä toimintoa ei voi peruuttaa. + submit: Allekirjoita äänestyksen päättäminen + success: Äänestyksen päättämisen allekirjoittaminen onnistui. + update: + error: Äänestyksen päättämisen tulosten päivittäminen epäonnistui, yritä uudestaan myöhemmin. + success: Äänestyksen päättämisne tulosten päivittäminen onnistui. + in_person_votes: + complete_voting: + available_answers: 'Mahdolliset vastaukset:' + census_verified: Tämä osallistuja ei ole vielä äänestänyt fyysisesti. + census_verified_with_online_vote: Tämä osallistuja on jo äänestänyt verkossa. Jos hän äänestää fyysisesti, edelliset äänet mitätöidään ja ainoastaan fyysinen ääni lasketaan. + complete_voting: Viimeistele äänestys + identify_another: Tunnista toinen osallistuja + questions_title: 'Osallistujilla on oikeus äänestää seuraavista kysymyksistä:' + questions_title_voted: 'Osallistuja on jo äänestänyt verkossa ja hänellä on oikeus äänestää seuraavista kysymyksistä:' + voted: Osallistuja on äänestänyt + create: + error: Ääntä ei tallennettu. Ole hyvä ja yritä uudelleen. + in_person_form: + census_not_present: Tätä osallistujaa ei ole henkilötietorekisterissä. + census_not_present_description: Hänen on mentävä reklamoimaan henkilötietorekisteriä hallinnoivaan toimistoon tai otettava yhteyttä tukeen. + date_of_birth: Syntymäaika + day: Päivä + day_placeholder: PV + document_number: Asiakirjan numero + document_number_placeholder: Henkilöllisyystodistuksen numero + month: Kuukausi + month_placeholder: KK + select: Valitse asiakirjan tyyppi + title: 'Valitse asiakirjan tyyppi ja syötä osallistujan asiakirjan numero:' + validate_document: Vahvista asiakirja + year: Vuosi + year_placeholder: VVVV + new: + back: Takaisin äänestyspaikkoihin + title: Tunnista ja vahvista osallistuja + show: + back: Takaisin äänestyspaikkoihin + title: Odottaa fyysisessä tilaisuudessa annetun äänen tallennusta + update: + error: Äänen tallennuksessa tapahtui virhe. Ole hyvä ja yritä uudelleen. + success: + accepted: Äänen tallennus onnistui. + rejected: Ääntä ei hyväksytty sähköisessä ääniuurnassa. Ota yhteyttä järjestelmänvalvojaan. + verify_document: + census_present: Tätä osallistujaa ei ole henkilötietorekisterissä. + name: Nimi + title: 'Tarkista, että seuraavat tiedot ovat oikein:' + verify_document: Vahvista asiakirja + menu: + polling_officer_zone: Äänestysvirkailijoiden alue + polling_officers: + index: + polling_officer_role_description: Sinulle on annettu tehtäväksi toimia äänestyspaikan virkailijana (esimiehenä tai muuna virkailijana) tällä alustalla järjestettävissä vaaleissa. + polling_station: + address: Osoite + count_votes: Laske äänet + election: Vaali + identify_person: Tunnista henkilö + name: Nimi + no_polling_stations: Sinua ei ole vielä liitetty millekään äänestyspaikalle. + role: Roolisi + show_closure: Näytä äänestyksen päättäminen + title: Äänestyspaikat + voting: Äänestys + polling_officers: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + new: Uusi äänestysvirkailija + title: Toiminnot + roles: + manager: Virkailija + president: Esimies + unassigned: Ei määritetty + polling_station_closure_certificate: + current_certificate: 'Nykyinen todistus:' + polling_station_closure_recount: + nota_option: Tyhjä / Ei mitään näistä + polling_officer_notes: 'Äänestysvirkailijan muistiinpanot:' + polling_officer_notes_blank: Muistiinpanoja ei ole + recount_summary: 'Uudelleenlaskennan yhteenveto:' + signed: Allekirjoitettu + total_ballots: 'Äänten kokonaismäärä:' + total_blank_ballots: 'Tyhjien äänten kokonaismäärä:' + total_null_ballots: 'Hylättyjen äänten kokonaismäärä:' + total_valid_ballots: 'Kelvollisten äänten kokonaismäärä:' + polling_stations: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + edit: Muokkaa + new: Uusi äänestyspaikka + title: Toiminnot + votings: + access_code_modal: + email: Lähetä sähköpostitse osoitteeseen %{email} + info: Tarvitset tunnistautumiskoodin osallistuaksesi. Jos et saanut sitä paperipostissa, voimme lähettää sinulle uuden koodin. + no_email: Sähköpostiosoitetta ei ole käytettävissä + no_sms: Puhelinnumeroa ei ole käytettävissä + sms: Lähetä tekstiviestinä numeroon %{sms} + title: Hanki tunnistautumiskoodi + check_census: + check_status: Tarkista tila + description: Täällä voit tarkastaa omat tietosi henkilötietorekisterissä selvittääksesi, onko sinulla oikeus osallistua tähän äänestykseen. Sinulla pitäisi jo olla tunnistautumiskoodi, mutta jos hävitit sen, voit pyytää sitä uudestaan, jos tietosi on syötetty oikein. + error: + info: 'Yritä uudestaan. Jos epäilet, että järjestelmässä olevat tiedot eivät pidä paikkaansa, voit ilmoittaa siitä täällä: %{census_contact_information}.' + title: Syöttämäsi tiedot eivät ole tämän äänestyksen henkilötietorekisterissä + form_title: 'Täytä seuraava lomake tarkistaaksesi tietosi henkilötietorekisterissä:' + invalid: Henkilötietorekisterin tietojen tarkastaminen epäonnistui. + success: + access_link: sähköpostitse. + access_link_with_sms: tekstiviestitse tai sähköpostitse. + info: Sinun olisi pitänyt saada tunnistautumiskoodisi paperipostissa. Jos sinulla ei ole koodia, voit pyytää sitä täältä + title: Tietosi henkilötietorekisterissä ovat oikein! + title: Voinko äänestää? + check_fields: + date_of_birth: Syntymäaika + day: Päivä + day_placeholder: PV + document_number: Asiakirjan numero + document_number_placeholder: Henkilöllisyystodistuksen numero + document_type: Asiakirjan tyyppi + month: Kuukausi + month_placeholder: KK + postal_code: Postinumero + postal_code_placeholder: Postinumero + select: Valitse asiakirjan tyyppi + year: Vuosi + year_placeholder: VVVV + count: + title: + one: "%{count} äänestys" + other: "%{count} äänestystä" + elections_log: + description: Vaalin loki näyttää jokaiseen äänestykseen liittyvän olennaisen tiedon. Esimerkiksi, avainseremonian tai ääntenlaskennan tila sekä tiedon tulosten julkistamisesta. Klikkaa sitä vaalia, jonka lokia haluat tarkastella. + title: Vaalin loki + filters: + active: Aktiiviset + all: Kaikki + date: Päivämäärä + finished: Päättyneet + search: Hae + upcoming: Tulevat + index: + no_votings: Hakuehdoillasi ei löytynyt yhtään äänestystä. + only_finished: Tällä hetkellä ei ole yhtään ajastettua äänestystä, mutta täältä näet kaikki päättyneet äänestykset. + title: Äänestykset + login: + access_code: Tunnistautumiskoodi + access_code_placeholder: Tunnistautumiskoodi + ask_for_a_new_one: Pyydä uutta. + dont_have_access_code: Eikö sinulla ole tunnistautumiskoodia? + form_title: 'Täytä seuraava lomake aloittaaksesi äänestyksen:' + start_voting: Aloita äänestys + title: Tunnistan itseni henkilötietorekisterin tietojen avulla + no_census_contact_information: Yhteystietoja ei ole vielä. + orders: + label: 'Järjestä äänestykset:' + random: Satunnainen + recent: Viimeisimmät + send_access_code: + invalid: Tunnistautumiskoodin lähetys epäonnistui. + success: Tunnistautumiskoodisi lähetys onnistui. + show: + title: Tietoa tästä äänestyksestä + votings_m: + badge_name: + finished: Päättynyt + ongoing: Käynnissä + upcoming: Tulossa + unspecified: Ei määritetty + voting_type: + hybrid: Yhdistetty äänestystapa + in_person: Fyysinen äänestys + online: Verkkoäänestys + layouts: + decidim: + voting_navigation: + check_census: Voinko äänestää? + election_log: Vaalin loki + votings: + index: + promoted_votings: Korostetut äänestykset + promoted_voting: + vote: Äänestä diff --git a/decidim-elections/config/locales/fi.yml b/decidim-elections/config/locales/fi.yml new file mode 100644 index 00000000..23bf04ea --- /dev/null +++ b/decidim-elections/config/locales/fi.yml @@ -0,0 +1,1426 @@ +fi: + activemodel: + attributes: + answer: + description: Kuvaus + image: Kuva + proposals: Liittyvät ehdotukset + title: Otsikko + ballot_style: + code: Koodi + election: + description: Kuvaus + end_time: Äänestys päättyy + start_time: Äänestys alkaa + title: Otsikko + monitoring_committee_member: + email: Sähköpostiosoite + name: Nimi + polling_officer: + email: Sähköpostiosoite + name: Nimi + polling_station: + address: Osoite + location: Sijainti + location_hints: Paikan tarkemmat tiedot + polling_station_managers: Virkailijat + polling_station_president_id: Esimies + title: Otsikko + question: + max_selections: Valintojen enimmäismäärä + min_selections: Ei mikään edellä mainituista + title: Otsikko + trustees_participatory_space: + user_id: Osallistuja + voting: + banner_image: Bannerikuva + census_contact_information: Henkilötietorekisterin vastuutahon yhteystiedot + description: Kuvaus + end_time: Äänestyksen päättymisaika + introductory_image: Esittelykuva + promoted: Korostettu + scope_id: Teema + show_check_census: Näytä "tarkasta henkilötietorekisterin tiedot" -sivu + start_time: Äänestyksen alkamisaika + title: Otsikko + voting_type: Äänestystapa + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Liitettävä uudelleen + ballot_result: + attributes: + base: + total_count_invalid: Vastausten kokonaismäärä ei vastaa hyväksytty/tyhjä -erittelyä. + election: + attributes: + attachment: + needs_to_be_reattached: Liitettävä uudestaan + question_result: + attributes: + base: + blank_count_invalid: Tyhjien vastausten kokonaismäärä ei voi olla suurempi kuin tyhjien äänten kokonaismäärä. + trustee: + attributes: + name: + cant_be_changed: tietoa ei voi muuttaa + public_key: + cant_be_changed: tietoa ei voi muuttaa + voting: + attributes: + voting_type: + inclusion: "%{value} ei ole sallittu äänestystyyppi" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Äänestysvirkailija on jo äänestyspaikan esimies tai hoitaja. + voting: + different_organization: Äänestyksen tulee tapahtua sen organisaation alla, johon käyttäjä on rekisteröitynyt. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Äänestysvirkailijan on oltava samassa äänestyksessä kuin äänestyspaikka. + models: + decidim/elections/answer: + one: Vastaus + other: Vastaukset + decidim/elections/election: + one: Vaali + other: Vaalit + decidim/elections/question: + one: Kysymys + other: Kysymykset + decidim/voting: + one: Äänestys + other: Äänestystä + decidim/votings/census/dataset: + one: Tietoaineisto + other: Tietoaineistot + decidim/votings/census/datum: + one: Tieto + other: Tiedot + decidim/votings/polling_officer: + one: Äänestysvirkailija + other: Äänestysvirkailijat + decidim/votings/polling_station: + one: Äänestyspaikka + other: Äänestyspaikat + decidim/votings/voting: + one: Äänestys + other: Äänestykset + decidim: + admin: + filters: + officers_assigned_eq: + label: Virkailijat + values: + assigned: Määritetty + unassigned: Ei määritetty + role_eq: + label: Rooli + values: + manager: Virkailija + president: Esimies + unassigned: Ei määritetty + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Hae %{collection} nimellä, sähköpostilla, nimimerkillä tai äänestyspaikan perusteella. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Hae %{collection} otsikolla, osoitteella tai virkailijan nimellä, sähköpostilla tai nimimerkillä. + signed_eq: + label: Allekirjoitettu + values: + 'false': Allekirjoitettu + 'true': Ei allekirjoitettu + validated_eq: + label: Vahvistettu + values: + 'false': Vahvistamaton + 'true': Vahvistettu + voting_publications: + create: + error: Äänestyksen julkaisu epäonnistui. + success: Äänestyksen julkaisu onnistui. + destroy: + error: Äänestyksen julkaisun lopettaminen epäonnistui. + success: Äänestyksen julkaisun lopettaminen onnistui. + components: + elections: + actions: + vote: Äänestä + name: Vaalit + settings: + global: + announcement: Ilmoitus + step: + announcement: Ilmoitus + elections: + actions: + confirm_destroy: Oletko varma? + destroy: Tuhoa + edit: Muokkaa + feedback: Äänestäjän palaute + import: Tuo ehdotuksia vastauksiin + manage_answers: Hallitse vastauksia + manage_questions: Hallinnoi kysymyksiä + manage_steps: Hallinnoi vaiheita + new_answer: Uusi vastaus + new_election: Uusi vaali + new_question: Uusi kysymys + new_trustee: Uusi luottamushenkilö + preview: Esikatsele + publish: Julkaise + title: Toiminnot + unpublish: Lopeta julkaisu + admin: + answers: + create: + invalid: Vastauksen luonti epäonnistui. + success: Vastauksen luonti onnistui. + destroy: + invalid: Vastauksen poisto epäonnistui. + success: Vastauksen poisto onnistui. + edit: + title: Muokkaa vastausta + update: Päivitä vastausta + index: + invalid_max_selections: Sinun on valittava vielä %{missing_answers} valinta/valintaa täyttääksesi maksimivalintojen ehdon. + title: Vastaukset + new: + create: Luo vastaus + title: Uusi vastaus + not_selected: Ei valittu + select: + disable: Poista vastausvalinta + enable: Merkitse vastaus valituksi + invalid: Vastauksen valinta epäonnistui. + success: Vastauksen valinta onnistui. + selected: Valittu + unselect: + invalid: Vastauksen valinnan poisto epäonnistui. + success: Vastauksen valinnan poisto onnistui. + update: + invalid: Vastauksen päivitys epäonnistui. + success: Vastauksen päivitys onnistui. + elections: + create: + invalid: Vaalin luonti epäonnistui. + success: Vaalin luonti onnistui. + destroy: + invalid: Vaalin poisto epäonnistui. + success: Vaalin poisto onnistui. + edit: + title: Muokkaa vaalia + update: Päivitä vaali + form: + organization_time_zone: Tarkista, että organisaation aikavyöhyke on oikea organisaation asetuksissa. Nykyinen aikavyöhyke on %{time_zone} (%{time}). + index: + no_bulletin_board: Järjestelmään ei ole määritetty sähköisen ääniuurnan palvelinta, joka tarvitaan käyttääksesi tätä moduulia. Tämä tehtävä tulisi toteuttaa yhteistyössä järjestelmänvalvojan kanssa. + title: Vaalit + new: + create: Luo vaali + title: Uusi vaali + publish: + success: Vaalin julkaisu onnistui. + unpublish: + success: Vaalin julkaisun lopettaminen epäonnistui. + update: + invalid: Vaalin päivitys epäonnistui. + success: Vaalin päivitys onnistui. + exports: + elections: Vaalit + feedback_form_answers: Palautelomakkeen vastaukset + mailers: + trustee_mailer: + body: + help_html: |- +

    Hei %{user_name},


    +

    Sinut on lisätty luottamushenkilöksi joihinkin vaaleihin %{organization} -alustalla.


    +

    Uusi osio nimeltä "Luottamushenkilön alue" on nyt näkyvissä käyttäjätilisi valikossa. Voit suorittaa sitä kautta sinulle määrättyjä tehtäviä tarpeen mukaan. Pääset alkuun luomalla tunnistautumisavaimesi.


    + subject: Sinut on lisätty luottamushenkilöksi kohteeseen %{resource_name} + trustee_zone: Siirry luottamusmiesten osioon + menu: + trustees: Luottamushenkilöt + models: + answer: + name: Vastaus + proposals_imports: + create: + invalid: Ehdotusten tuonti vastauksiksi epäonnistui. + success: "%{number} ehdotusta tuotiin vastauksiksi." + new: + create: Tuo ehdotuksia vastauksiksi + no_components: Tässä osallistumistilassa ei ole muita ehdotuskomponentteja, joista voisit tuoda ehdotuksia vastauksiksi. + select_component: Valitse komponentti + title: Tuo ehdotuksia + questions: + create: + election_started: Tämä äänestys on jo aloitettu. + invalid: Kysymyksen luonti epäonnistui. + success: Kysymyksen luonti onnistui. + destroy: + invalid: Kysymyksen poisto epäonnistui. + success: Kysymyksen poisto onnistui. + edit: + title: Muokkaa kysymystä + update: Päivitä kysymys + index: + title: Kysymykset + new: + create: Luo kysymys + title: Uusi kysymys + update: + invalid: Kysymyksen päivitys epäonnistui. + success: Kysymyksen päivitys onnistui. + steps: + create_election: + census: Henkilötietorekisteri + errors: + census_codes_generated: Henkilötietorekisterin äänestyskoodeja ei ole luotu. + census_frozen: Henkilötietorekisterin äänestyskoodeja ei ole viety. + census_uploaded: Tälle äänestykselle ei ole luotu henkilötietorekisteriä. + component_published: Vaalikomponenttia ei ole julkaistu. + fix_it_text: Korjaa + max_selections: Kysymyksillä ei ole oikeaa arvoa vastausten määrälle + minimum_answers: Kysymyksillä on oltava vähintään kaksi vastausta. + minimum_questions: Vaalilla on oltava vähintään yksi kysymys. + published: Vaalia ei ole julkaistu. + time_before: Alkamisaika on alle %{hours} tuntia ennen vaalin alkua. + trustees_number: Osallistumistilassa on oltava vähintään %{number} luottamushenkilöä, joilla on julkinen avain. + invalid: Vaalin asetusten määrittäminen epäonnistui + no_trustees: Tähän osallistumistilaan ei ole määritetty luottamushenkilöitä + not_used_trustee: "(ei käytössä)" + public_key: + 'false': vaalilla ei ole julkista avainta + 'true': vaalilla on julkinen avain + requirements: + census_codes_generated: Henkilötietorekisterin äänestyskoodit on luotu. + census_frozen: Henkilötietorekisterin äänestyskoodit on viety ja henkilötietorekisteri on lukittu. + census_uploaded: Henkilötietorekisteri on ladattu palveluun. + component_published: Vaalikomponentti on julkaistu. + max_selections: Kaikilla kysymyksillä on oikea arvo asetukselle vastausten maksimimäärä. + minimum_answers: Jokaisella kysymyksellä on vähintään 2 vastausta. + minimum_questions: Vaalilla on vähintään 1 kysymys. + published: Vaalia ei ole julkaistu. + time_before: Vaalin määritys tulee tapahtua vähintään %{hours} tuntia ennen sen alkamista. + trustees_number: Osallistumistilassa on oltava vähintään %{number} luottamushenkilöä, joilla on julkinen avain. + submit: Määritä vaali + success: Vaalin lähetys sähköiselle ääniuurnalle onnistui. + technical_configuration: + authority_name: "Viranomaisen nimi: %{value}" + bulletin_board_server: "Sähköisen ääniuurnan palvelin: %{value}" + scheme_name: "Skeeman nimi: %{value}" + title: Näytä tekniset tiedot + title: Määritä vaali + trustees: Vaalin luottamushenkilöt + created: + invalid: Avainseremonian käynnistäminen epäonnistui. + submit: Aloita avainseremonia + success: Avainseremonian aloituspyyntö on lähetetty sähköiselle ääniuurnalle. + title: Vaali luotu + trustees: Luottamushenkilöt + key_ceremony: + continue: Jatka + title: Avainseremonia + key_ceremony_ended: + errors: + time_before: Vaali on valmis alkamaan. Vaalin äänestysaika voidaan käynnistää aikaisintaan %{hours} tuntia ennen alkamisaikaa (%{start_time}). + invalid: Äänestysajan käynnistäminen epäonnistui. + requirements: + time_before: 'Äänestys alkaa pian. Voit käynnistää äänestysajan manuaalisesti tai se käynnistetään automaattisesti ennen alkamisaikaa: %{start_time}.' + submit: Käynnistä äänestys + success: Pyyntösi aloittaa äänestysaika on lähetetty sähköiselle ääniuurnalle. + title: Valmis alkamaan + processing: Käsitellään... + results_published: + answer: Vastaus + not_selected: Ei valittu + question: Kysymys + result: Tulos + selected: Valittu + submit: Lähetä + title: Tulokset julkaistu + tally_ended: + answer: Vastaus + not_selected: Ei valittu + question: Kysymys + result: Tulos + selected: Valittu + submit: Julkaise tulokset + success: Pyyntösi julkaista äänestyksen tulokset on lähetetty sähköiselle ääniuurnalle. + title: Ääntenlaskun tulokset + tally_started: + continue: Jatka + invalid: Poissaolevan luottamushenkilön ilmoittaminen epäonnistui. + mark_as_missing: Merkitse poissaolevaksi + mark_as_missing_description: Kaikkien luottamushenkilöiden pitäisi osallistua tähän prosessiin, mutta jos luottamushenkilö ei voi osallistua prosessiin, voit merkitä kyseisen henkilön poissaolevaksi. + success: Poissaolevan luottamushenkilön merkitseminen sähköiselle ääniuurnalle onnistui. + tally_completion: Prosessi valmistuu, kun kaikki luottamushenkilöt ovat aktiivisia tai merkitty poissaoleviksi. Vähintään %{quorum} luottamushenkilöä tarvitaan prosessin loppuun saattamiseksi. + title: Ääntenlasku + undo_mark_as_missing: Poissaoleva luottamushenkilö voi osallistua prosessiin ennen sen päättymistä. He voivat jatkaa normaalisti ja heidän poissaolonsa jätetään huomioimatta. + vote: + errors: + time_after: Vaali on edelleen käynnissä. Sinun on odotettava päättymisaikaan saakka (%{end_time}) päättääksesi äänestyksen. + invalid: Äänestysajan päättäminen epäonnistui. + requirements: + time_after: Äänestys on päättynyt. Voit sulkea äänestysajan manuaalisesti tai se suljetaan automaattisesti muutaman minuutin päästä. + submit: Päätä äänestysaika + success: Pyyntösi päättää äänestysaika on lähetetty sähköiselle ääniuurnalle. + title: Äänestysaika + vote_ended: + invalid: Ääntenlaskennan käynnistäminen epäonnistui. + submit: Aloita ääntenlaskenta + success: Pyyntösi aloittaa ääntenlaskenta on lähetetty sähköiselle ääniuurnalle. + text: Äänestys on päättynyt. Voit nyt aloittaa ääntenlaskun. + title: Äänestysaika on päättynyt + vote_stats: + no_vote_statistics_yet: Ei vielä äänitilastoja + title: Äänten tilastot + voters: Äänestäjät + votes: Äänet + trustees_participatory_spaces: + actions: + disable: Poista käytöstä + enable: Harkitse + create: + exists: Tällä osallistumistilalla on luottamushenkilö. + invalid: Luottamushenkilön luonti epäonnistui. + success: Luottamushenkilön luonti onnistui. + delete: + invalid: Luottamushenkilön poisto epäonnistui. + success: Luottamushenkilön poisto onnistui. + form: + select_user: Valitse käyttäjä + index: + title: Luottamushenkilöt + new: + create: Luo luottamushenkilö + title: Uusi luottamushenkilö + update: + invalid: Luottamushenkilön %{trustee} päivitys epäonnistui. + success: Luottamushenkilön %{trustee} päivitys onnistui. + admin_log: + election: + create: "%{user_name} loi vaalin %{resource_name} osallistumistilassa %{space_name}" + delete: "%{user_name} poisti vaalin %{resource_name} osallistumistilassa %{space_name}" + end_vote: "%{user_name} päätti äänestysajan vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + publish: "%{user_name} julkaisi vaalin %{resource_name} osallistumistilassa %{space_name}" + publish_results: "%{user_name} julkaisi tulokset vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + report_missing_trustee: "%{user_name} ilmoitti henkilön %{trustee_name} puuttuvaksi luottamushenkilöksi äänestyksen %{resource_name} sähköisellä vaaliuurnalla osallistumistilassa %{space_name}" + setup: "%{user_name} loi vaalin %{resource_name} osallistumistilan %{space_name} sähköiselle ääniuurnalle" + start_key_ceremony: "%{user_name} aloitti avainseremonian vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + start_tally: "%{user_name} aloitti ääntenlaskennan vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + start_vote: "%{user_name} aloitti äänestysajan vaalille %{resource_name} osallistumistilan %{space_name} sähköisellä ääniuurnalla" + unpublish: "%{user_name} lopetti vaalin %{resource_name} julkaisun osallistumistilassa %{space_name}" + update: "%{user_name} päivitti vaalia %{resource_name} osallistumistilassa %{space_name}" + trustee: + create: "%{user_name} määritti käyttäjän %{trustee_user} luottamushenkilöksi" + connection: + failed: + modal: + close: Sulje + communication_lost: Valitettavasti näyttää siltä, että viestien välitys sähköisen ääniuurnan kanssa ei onnistu.
    Internet-yhteydessäsi voi olla ongelma tai ääniuurnan palvelin on liian kiireinen.
    Voit yrittää myöhemmin uudestaan tai ottaa yhteyttä tekniseen tukeen, jos ongelma jatkuu. + generic_error: Valitettavasti on tapahtunut tuntematon virhe. On todennäköistä, että selaintasi ei tueta tai käytät selaimen "näkymättömyystilaa" tai "yksityisen selauksen tilaa", joita ei tueta. + title: Jokin meni vikaan + election_m: + badge_name: + finished: Valmis + ongoing: Aktiivinen + upcoming: Tuleva + end_date: Päättyy + footer: + remaining_time: + one: "%{count} tunti ja %{minutes} minuuttia äänestysaikaa jäljellä." + other: "%{count} tuntia ja %{minutes} minuuttia äänestysaikaa jäljellä." + view: Näytä + vote: Äänestä + label: + date: Päivämäärät + questions: Kysymys %{count} + start_date: Alkaa + unspecified: Ei määritelty + elections: + count: + elections_count: + one: "%{count} vaali" + other: "%{count} vaalia" + election_log: + chained_hash: Tämän viestin ketjutettu tiiviste + complete: Viimeistele + creation_description: + complete: Vaali luotiin ja perustettiin onnistuneesti sähköiseen ääniuurnaan. + not_created: Vaalia ei ole vielä luotu. + creation_title: Vaali luotu + description: Tämä on vaalin loki, josta voit tarkastaa jokaisen vaiheen tilan, esimerkiksi milloin vaali luotiin, onko ääntenlasku suoritettu ja milloin vaali suljettiin. + download: Lataa + key_ceremony_description: + complete: Avainseremonia on onnistunut. Jokaisella luottamushenkilöllä on kelvolliset avaimet ja he ovat ladanneet tarvittavat avainten varmuuskopiot. + not_started: Avainseremonia ei ole vielä alkanut. + started: Avainseremonia on alkanut, mutta se ei ole vielä päättynyt. + key_ceremony_title: Avainseremonia + not_available: Ei vielä saatavilla + not_created: Ei luotu + not_ready: Ei valmis + not_started: Ei aloitettu + published: Julkaistu + results_description: + not_published: Tuloksia ei ole vielä julkaistu. + published: Tulokset on julkaistu. + results_title: Tulokset + started: Aloitettu + tally_description: + finished: Ääntenlasku on suoritettu. + not_started: Ääntenlasku ei ole vielä alkanut. + started: Ääntenlasku on alkanut. + tally_title: Ääntenlasku + title: Vaalin loki + unpublished: Julkaisematon + verifiable_results: + checksum: 'Tiedoston SHA256 tarkistussumma:' + description: + not_ready: Vahvistettava vaalin tiedosto ja SHA256-tarkistesumma eivät ole vielä käytettävissä. Heti kun tulokset julkistetaan, voit tarkistaa nämä vaalit. + ready: 'Täällä voit vahvistaa vaalin. Ensin sinun täytyy ladata tiedosto ja tarkastaa, että se ei ole korruptoitunut. Tarkastaaksesi tiedoston, aja seuraava komento ja tarkasta, että komennon näyttämä lopputulos vastaa tarkistussummaa:' + how_to_verify: 'Kun olet ladannut tiedoston ja tarkastanut sen olevan eheä, voit ajaa yleisen tarkastuksen. Kloonaa tämä repositorio ja aja seuraava komento sen juurihakemistossa:' + title: Vahvista vaalin tulokset + verifiable_file: 'Vaalin tarkastettava tiedosto:' + verify: Vahvista vaali + vote_description: + finished: Äänestys on päättynyt. + not_started: Äänestys ei ole vielä alkanut. + started: Äänestys on alkanut. + vote_title: Äänestysprosessi + filters: + active: Aktiiviset + all: Kaikki + date: Päivämäärä + finished: Valmiit + upcoming: Tulevat + preview: + available_answers: 'Vastausvaihtoehdot:' + description: 'Nämä kysymykset esitetään äänestyksen yhteydessä:' + title: Vaaliin liittyvät kysymykset + results: + description: 'Tässä on äänestyksen tulokset kaikille kysymyksille:' + percentage: "%{count}%" + selected: Valittu + title: Vaalin tulokset + votes: + one: "%{count} ääni" + other: "%{count} ääntä" + show: + action_button: + change_vote: Muuta ääntäsi + vote: Aloita äänestys + vote_again: Äänestä uudelleen + callout: + already_voted: Olet jo äänestänyt näissä vaaleissa. Voit muuttaa ääntäsi tai tarkastaa sen. + pending_vote: Ääntäsi kirjataan palvelimelle. + vote_rejected: Ääntäsi ei voida tarkastaa. Ole hyvä ja lähetä äänesi uudestaan. + election_log: Vaalin loki + preview: Esikatsele + verify: + already_voted: Oletko jo äänestänyt? + verify_here: Tarkista äänesi täältä. + will_verify: Voit tarkistaa äänesi, kun vaalit ovat alkaneet. + voting_period_status: + finished: Äänestys alkoi %{start_time} ja päättyi %{end_time} + ongoing: 'Äänestysaika päättyy: %{end_time}' + upcoming: Äänestys alkaa %{start_time} + feedback: + answer: + invalid: Palautteen lähettäminen epäonnistui. + spam_detected: Lomakkeeseen vastaaminen epäonnistui. Saatoit toimia liian nopeasti. Yrittäisitkö uudestaan? + success: Palautteen lähettäminen onnistui. + models: + answer: + fields: + proposals: Ehdotukset + selected: Valittu + title: Otsikko + votes: Äänet + election: + fields: + bb_status: Sähköisen ääniuurnan tila + end_time: Päättymisaika + start_time: Alkamisaika + title: Otsikko + verifiable_results_file_hash: Tiedoston SHA256-tarkistussumma + verifiable_results_file_url: Todennettavissa oleva vaalitiedosto + question: + fields: + answers: Vastaukset + max_selections: Valintojen enimmäismäärä + title: Otsikko + trustees_participatory_space: + fields: + considered: harkittu + email: Sähköposti + inactive: ei käytössä + name: Nimi + notification: Ilmoituksen lähetysaika + public_key: Julkinen avain + status: Tila + orders: + label: Järjestä vaalit + older: Vanhimmat + recent: Uusimmat + trustee_zone: + elections: + backup_modal: + description: Näitä vaaleja luodaan sähköiselle ääniuurnalle. On erityisen tärkeää, että jokainen luottamushenkilö luo varmuuskopion näistä avaimista ja tallentaa sen turvalliseen paikkaan. Tämän jälkeen, prosessi jatkuu. + download_election_keys: Lataa avaimet + title: Varmuuskopioi vaaliavaimet kohteelle %{election} + key_ceremony_steps: + back: Takaisin + description: Näitä vaaleja luodaan parhaillaan sähköiselle ääniuurnalle. Jotta tämä prosessi voidaan saattaa loppuun, sinun tulee osallistua luottamushenkilönä. + keys: + create_election: Avainten luonti + key_ceremony: + joint_election_key: Yhteisen avaimen luonti + step_1: Avainten julkaisu + list: + status: Tila + task: Tehtävä + process_warning: Kun prosessi on käynnistetty, sinun ei tule poistua tältä sivulta ennen kuin prosessi päättyy. Se kestää useita minuutteja, koska kaikkiin luottamushenkilöihin tulee olla yhteydessä, jotta prosessi voidaan päättää. + start: Aloita + status: + completed: Valmis + pending: Odottaa + processing: Käsitellään + title: Luo vaaliavaimet kohteelle %{election} + restore_modal: + description: Sähköiseen ääniuurnaan on liitetty tieto sinusta näiden vaalien luottamushenkilönä. Jatkaaksesi tätä prosessia, lataa oma varmuuskopiotiedostosi, joka luotiin edellisen istuntosi yhteydessä. + title: Palauta vaaliavaimet kohteelle %{election} + upload_election_keys: Lataa vaaliavaimet + tally_started_steps: + back: Takaisin + description: Näiden vaalien tuloksia lasketaan parhaillaan sähköiseen ääniuurnaan. Jotta tämä prosessi voidaan saattaa loppuun, sinun tulee osallistua luottamushenkilönä. + keys: + end_tally: Ääntenlaskennan päättyminen + tally: + cast: Ääntenlaskennan tuloksen valmistuminen + share: Ääntenlaskennan tulosten jakaminen + list: + status: Tila + task: Tehtävä + process_warning: Kun prosessi on käynnistetty, sinun ei tule poistua tältä sivulta ennen kuin prosessi päättyy. Tämä kestää useita minuutteja, koska kaikkien luottamushenkilöiden tulee olla yhteydessä samanaikaisesti, jotta prosessi voidaan suorittaa. + start: Aloitettu + status: + completed: Valmis + pending: Odottaa + processing: Käsitellään + title: Ääntenlaskenta vaaleille %{election} + update: + error: Vaalin tilaa ei päivitetty. + success: 'Vaalin tila on: %{status}.' + menu: + trustee_zone: Luottamushenkilön alue + no_bulletin_board: + body: Tätä osiota varten vaaditaan sähköisen ääniuurnan palvelin. Ota yhteyttä järjestelmänvalvojaan saadaksesi lisätietoja tästä. + title: Pahoittelut, sähköistä ääniuurnaa ei ole vielä määritetty. + trustees: + show: + elections: + list: + action_required: + 'false': 'Ei' + name: Tarvitaanko toimenpiteitä? + 'true': Suorita toimenpide + bb_status: Tila + election: Vaali + voting_period: Äänestysaika + no_elections: Tällä hetkellä sinulle ei ole määritetty mitään vastuuhenkilön tehtäviä. Saat ilmoituksen, kun sinun odotetaan suorittavan tehtäviä eri vaiheiden aikana. + title: Vaalit + identification_keys: + cancel: Peruuta + generate: Luo tunnistusavaimet + generate_error: Tunnistusavainten luonti epäonnistui. + generate_legend: Sinun tulee luoda tunnistusavainten parit toimiaksesi äänestyksen luottamushenkilönä. + generate_legend_1: Painikkeen painamisen jälkeen sinun tulee ladata tunnistusavainten tiedosto omalle tietokoneellesi. + generate_legend_2: Kopioi ladattu tiedosto puhtaalle ja tyhjälle USB-tallennuslaitteelle + generate_legend_3: Tarkasta, että tietokoneellesi ei jää kopiota kyseisestä tiedostosta (tarkasta myös Lataukset-kansio sekä työpöytäsi). + generate_legend_4: Luo toinen kopio tiedostosta toiselle ulkoiselle laitteelle ja talleta se erittäin turvalliseen paikkaan. + submit: Lähetä + submit_legend: Kun olet toteuttanut kaikki edellä esitetyt vaiheet, viimeistele julkisten tunnistusvainten lähetys palvelimelle. + submit_title: Lähetä julkinen tunnistusavain + title: Luottamushenkilöiden tunnistusavaimet + upload: Lataa tunnistusavaimet + upload_error: + invalid_format: Ladattu tiedosto ei sisällä yhtään tunnistusavainta. + invalid_key: Ladatussa tiedostossa olevia tunnistusavaimia ei voida ladata. + invalid_public_key: Ladatun tiedoston tunnistusavaimet eivät vastaa tallennettua julkista tunnistusavainta. + upload_legend: Julkiset tunnistusavaimet ovat palvelimella, mutta eivät selaimessasi. Sinun täytyy tuoda tunnistusavainten tiedosto omalle tietokoneellesi varmuuskopiosta, jonka tallensit avaimia luotaessa. + not_supported_browser_description: Näyttää siltä, että käytät selainta, jota ei voi käyttää luottamushenkilönä toimimiseen. Tarkasta, että käytät selaimesi viimeisintä versiota tai yritä käyttää jotain suosituimmista selaimista luottamushenkilön tehtävien suorittamiseksi. + not_supported_browser_title: Päivitä selaimesi toimiaksesi luottamushenkilönä + safari_warning_description: Näyttää siltä, että käytät Safari-selainta, jota ei tueta luottamushenkilönä toimimiseen salataksesi äänen (tämä johtuu Applen asettamista muistinkäyttörajoituksista). Mahdollisesti tämä voidaan korjata tulevaisuudessa, mikäli Apple muuttaa käytäntöjään tai vaalit-tilaa optimoidaan tulevaisuudessa paremmin. Ole hyvä ja käytä toista selainta tällä hetkellä. + safari_warning_title: Safari-selain tunnistettu + trustee_role_description: + with_keys: Sinulle on annettu luottamushenkilön tehtävä joissain vaaleissa tällä alustalla. + without_keys: Sinulle on annettu luottamushenkilön tehtävä. Luo ja lataa tunnistautumisavaimesi. + update: + success: Julkisen tunnistusavaimesi tallentaminen onnistui. + votes: + ballot_decision: + audit: Tarkasta äänesi + back: Aloita äänestysprosessi uudestaan + ballot_hash: 'Äänestyslippusi tunniste on:' + cast: Lähetä äänestyslippusi viimeistelläksesi äänestyksen + description_html: Tältä sivulta voit antaa äänesi tai tarkastaa, että se on salattu oikealla tavalla. Jos haluat tarkastaa äänesi oikeellisuuden, tutustu ohjeisiin, kuinka tarkastat äänesi. + header: 'Äänesi on salattu: lähetä tai tarkasta se' + casting: + header: Kirjataan ääntä... + text: Ääntäsi lähetetään sähköiseen ääniuurnaan. + confirm: + answer_number: 'vastaus #%{number}' + confirm: Vahvista + edit: muokkaa + header: Vahvista äänesi + intro: Tässä on yhteeveto äänestäsi, jota olet jättämässä.
    Tarkasta, että kaikki on oikein tai muuta vastauksiasi. + nota_option: Tyhjä + confirmed: + back: Takaisin vaaleihin + experience: Minkälainen käyttökokemuksesi oli? + feedback: Jätä meille palautetta + header: Ääni vahvistettu + lead: Äänesi on annettu! + text: 'Voit tarkastaa, että äänesi on jätetty onnistuneesti äänestyslaatikkoon seuraavalla tunnisteella: ' + verify_link: Tarkastaaksesi tämän, kopioi äänen tunniste äänten vahvistussivulle + create: + error: Äänen jättämisessä tapahtui virhe. Ole hyvä ja yritä uudelleen. + encrypting: + header: Salataan ääntä... + text: Äänesi salataan, jotta vaalisalaisuus voidaan taata. + failed: + header: Äänestys epäonnistui + lead: Ääntäsi ei ole annettu! + text: Jokin meni pieleen, yritä uudelleen. + try_again: Yritä uudestaan + header: + ballot_decision: Jätä äänesi + confirm: Vahvista + election: Vaali + register: Luo tunnus + vote_for: Äänestä - %{title} + messages: + invalid_token: Istuntosi äänestyskopissa ei ole kelvollinen. Yritä äänestää uudelleen. + not_allowed: Et voi äänestää tässä vaalissa tällä hetkellä. + modal: + close: Sulje + proposal_header: 'Ehdotukset:' + new: + answer_choices: Voit valita enintään %{choices} vastausta + more_information: Lisätietoa + nota_option: Tyhjä / Ei mitään näistä + preview_alert: Tämä on äänestyskopin esikatselunäkymä. + question_steps: Kysymys %{current_step} / %{total_steps} + selections: "Valittu %{selected} / %{max_selections}" + onboarding_modal: + create_account: Luo tili + description: Haluatko luoda oman käyttäjätilin alustalle? Voit osallistua prosesseihin ja alustalla esitettyjen asioiden kehittämiseen. + no_account: Ei kiitos. + title: Oletko uusi käyttäjä tässä palvelussa? + update: + error: Äänen tilan päivittäminen epäonnistui valitettavasti. Ole hyvä ja äänestä uudestaan. + verify: + content: + heading: Vahvista äänesi + info: Tämä tarkastustyökalu varmentaa, että salatulla merkkijonolla löytyvä äänesi on annettu oikein ja se on vaaliuurnassa. + error: + header: Ääntä ei löytynyt! + info: Äänestystunnusta ei löytynyt vaaliuurnasta %{link}, yritä uudelleen. + form: + back: Takaisin alustalle + submit: Tarkista + vote_identifier: 'Äänen tunniste:' + vote_identifier_help: Tämä tunniste annettiin sinulle äänen jättämisen jälkeen (se ei ole koodi, jota käytit päästäksesi äänestyskoppiin). + header: + title: Vahvista äänesi + success: + header: Äänesi löytyi! + info: Salattu äänesi on vaaliuurnassa %{link}. + voting_step: + back: Takaisin + continue: Seuraava + warnings: + empty_filters: Vaaleja ei löytynyt annetuilla hakuehdoilla. + no_elections: Tällä hetkellä yhtään vaalia ei ole aikataulutettu. + no_scheduled_elections: Tällä hetkellä yhtään vaalia ei ole aikataulutettu, mutta löydät täältä kaikki aikaisemmat vaalit. + events: + elections: + election_published: + email_intro: 'Vaali %{resource_title} on nyt aktiivisena osallistumistilassa %{participatory_space_title}. Voit tutustua siihen tältä sivulta:' + email_outro: Tämä ilmoitus on lähetetty sinulle, koska seuraat kohdetta %{participatory_space_title}. Voit lopettaa ilmoitusten vastaanottamisen edellä esitetyn linkin kautta. + email_subject: Vaali %{resource_title} on nyt aktiivisena osallistumistilassa %{participatory_space_title}. + notification_title: Vaali %{resource_title} on nyt aktiivisena osallistumistilassa %{participatory_space_title}. + trustees: + new_election: + email_intro: Sinut on lisätty luottamushenkilöksi vaaliin %{resource_title}. + email_outro: Tämä ilmoitus on lähetetty sinulle, koska sinulle on myönnettu luottamushenkilön tehtävä kohteessa %{resource_title}. + email_subject: Olet luottamushenkilönä vaalissa %{resource_title}. + notification_title: Sinulle on annettu luottamushenkilön tehtävä vaalissa %{resource_title}. Suorita avainseremonia aloittaaksesi vaalin. + new_trustee: + email_intro: Hallintakäyttäjä on myöntänyt sinulle luottamushenkilön tehtävän kohteessa %{resource_name}. Sinun tulee luoda julkinen tunnistusavain omalla luottamushenkilöiden alueellasi + email_outro: Tämä ilmoitus on lähetetty sinulle, koska sinulle on myönnettu luottamushenkilön tehtävä kohteessa %{resource_name}. + email_subject: Olet luottamushenkilö kohteessa %{resource_name}. + notification_title: Sinulle on annettu luottamushenkilön tehtävä kohteen %{resource_name} joissain vaaleissa, jotka suoritetaan tällä alustalla.
    Voit suorittaa tehtävät sitä mukaa, kun vaali etenee. Aloita luomalla tunnistautumisavaimesi. + start_tally: + email_intro: Vaalin %{resource_title} äänestysaika on päättynyt. Suorita nyt ääntenlaskenta kyseiselle vaalille julkaistaksesi tulokset. + email_outro: Tämä ilmoitus on lähetetty sinulle, koska olet vastuuhenkilönä vaalissa %{resource_title}. + email_subject: Ääntenlaskenta vaalille %{resource_title} on alkanut. + notification_title: Vaalin %{resource_title} äänestysaika on päättynyt. Suorita nyt ääntenlaskenta kyseiselle vaalille julkaistaksesi tulokset. + votes: + accepted_votes: + email_intro: 'Äänesi hyväksyttiin! Voit tarkastaa äänesi täältä käyttäen äänestystunnustasi: %{encrypted_vote_hash}.' + email_outro: Tämä ilmoitus on lähetetty sinulle, koska äänestit vaaleissa %{resource_name}. + email_subject: Äänesi vaaleihin %{resource_name} hyväksyttiin. + notification_title: 'Äänesi hyväksyttiin. Voit tarkastaa äänesi täältä käyttäen äänestystunnustasi: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Sinulle on määritetty rooli %{role} äänestyspaikalla %{polling_station_name} osiossa %{resource_title}. Voit hallinnoida äänestyspaikkaa äänestysvirkailijoille määritetyssä osiossa. + email_outro: Tämä ilmoitus on lähetetty sinulle, koska sinulle on myönnettu rooli %{role} äänestyspaikalle %{polling_station_name}. + email_subject: Roolisi äänestyspaikalla %{polling_station_name} on %{role}. + notification_title: Roolisi äänestyspaikan %{polling_station_name} äänestyksessä %{resource_title} on %{role}. + send_access_code: + instruction: 'Tässä on tunnistautumiskoodisi, jota pyysit: %{access_code}. Koodin avulla voit osallistua äänestykseen %{voting}.' + subject: Tunnistautumiskoodisi osallistuaksesi äänestykseen %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Äänestys on osallistumistila, jossa voit kysyä selkeitä kysymyksiä kaikilta organisaation ihmisiltä, päättää äänestykseen osallistumisesta sekä kannustaa ja järjestää keskusteluita tietyn lopputuloksen puolesta tai sitä vastaan. Kun äänestysaika alkaa, voit äänestää ja julkaista äänestystuloksen.

    Esimerkkejä: äänestyksiä voidaan järjestää lähes mistä tahansa organisaatioon vaikuttavasta asiasta, kuten nimen tai logon muuttamisesta, päättämisestä laajempaan organisaatioon liittymisestä, strategisen suunnitelman tai työryhmän päätöksen hyväksymisestä tai hylkäämisestä, tai tarvittavien mandaattien määrän päättämisestä.

    \n" + page: "

    Äänestys on osallistumistila, jossa voit kysyä selkeitä kysymyksiä kaikilta organisaation ihmisiltä, päättää äänestykseen osallistumisesta sekä kannustaa ja järjestää keskusteluita tietyn lopputuloksen puolesta tai sitä vastaan. Kun äänestysaika alkaa, voit äänestää ja julkaista äänestystuloksen.

    Esimerkkejä: äänestyksiä voidaan järjestää lähes mistä tahansa organisaatioon vaikuttavasta asiasta, kuten nimen tai logon muuttamisesta, päättämisestä laajempaan organisaatioon liittymisestä, strategisen suunnitelman tai työryhmän päätöksen hyväksymisestä tai hylkäämisestä, tai tarvittavien mandaattien määrän päättämisestä.

    \n" + title: Mitä ovat äänestykset? + menu: + votings: Äänestykset + participatory_spaces: + related_elections: + see_all: Näytä kaikki vaalit + statistics: + elections_count: Vaalia + votings_count: Äänestystä + votings: + admin: + ballot_styles: + create: + error: Äänestystyylin luonti epäonnistui. + success: Äänestystyylin luonti onnistui. + destroy: + invalid: Äänestystyylin poisto epäonnistui. + success: Äänestystyylin poisto onnistui. + edit: + title: Muokkaa äänestystyyliä + update: Päivitä + form: + code_help: 'Vihje: koodi yhdistää henkilötietorekisterin äänestystyyliin. Kun henkilötietorekisterin tiedot ladataan järjestelmään, jokaiselle tietueelle luodaan äänestystyyli koodin perusteella.' + election: Vaali + questions: Äänestystyylin kysymykset + questions_help: 'Vihje: valitse tähän äänestystyyliin määritetyille äänestäjille kysymykset vaalikomponenteista.' + index: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + edit: Muokkaa + new: Luo äänestystyyli + title: Toiminnot + associated_census_data: Liitetyt henkilötietorekisterin tiedot + explanation_callout: Äänestystyyli määrittelee, mitä kysymyksiä äänestäjälle esitetään äänestyskopissa. Voit valita äänestystyylille kysymykset äänestyslappuun tämän äänestyksen vaalikomponenteista. Äänestystyylin koodeja käytetään henkilötietorekisterissä olevien henkilöiden yhdistämiseksi äänestyslappuihin. Älä luo äänestystyylejä, jos haluat esittää kaikki kysymykset kaikille äänestäjille. + title: Äänestystyylit + new: + create: Luo + title: Luo äänestystyyli + update: + invalid: Äänestystyylin päivitys epäonnistui. + success: Äänestystyylin päivitys onnistui. + content_blocks: + attachments_and_folders: + name: Äänestyksen liitteet ja kansiot + header: + name: Äänestyksen otsikko + highlighted_votings: + max_results: Näytettävien elementtien enimmäismäärä + html_block_1: + name: Äänestyksen HTML-lohko 1 + html_block_2: + name: Äänestyksen HTML-lohko 2 + html_block_3: + name: Äänestyksen HTML-lohko 3 + main_data: + name: Otsikko ja kuvaus + metrics: + name: Äänestyksen tilastomittarit + polling_stations: + name: Äänestyspaikat + related_elections: + name: Äänestyksen vaalit + stats: + name: Äänestyksen tilastot + timeline: + name: Äänestyksen aikajana + index: + published: Julkaistu + unpublished: Julkaisematon + menu: + votings: Äänestykset + votings_submenu: + attachment_collections: Kansiot + attachment_files: Tiedostot + attachments: Liitteet + ballot_styles: Äänestystyylit + census: Henkilötietorekisteri + components: Komponentit + info: Tietoa tästä äänestyksestä + landing_page: Laskeutumissivu + monitoring_committee: Tarkkailukomitea + monitoring_committee_election_results: Vahvista tulokset + monitoring_committee_members: Jäsenet + monitoring_committee_polling_station_closures: Vahvista varmenteet + monitoring_committee_verify_elections: Vahvista vaali + polling_officers: Äänestysvirkailijat + polling_stations: Äänestyspaikat + see_voting: Näytä äänestys + models: + ballot_style: + fields: + code: Koodi + monitoring_committee_member: + fields: + email: Sähköposti + name: Nimi + polling_officer: + fields: + email: Sähköposti + name: Nimi + polling_station: Äänestyspaikka (rooli) + polling_station: + fields: + address: Osoite + polling_station_managers: Virkailijat + polling_station_president: Esimies + title: Otsikko + voting: + fields: + created_at: Luonnin ajankohta + published: Julkaistu + title: Otsikko + monitoring_committee_election_results: + actions: + title: Toiminnot + view: Näytä + index: + title: Valitse vaali, jonka tuloksia haluat tarkastella + results: + bulletin_board: Sähköinen ääniuurna + election_totals: Vaali yhteensä + polling_stations: Äänestyspaikat + result_types: + blank_answers: Tyhjät vastaukset + blank_ballots: Tyhjät äänet + null_ballots: Hylätyt äänet + total_ballots: Äänten kokonaismäärä + valid_ballots: Kelvolliset äänet + selected: Valittu + title: Vaalin %{election_title} tulokset + totals: Yhteensä + show: + change_election: Muuta vaalia + publish_results: Julkaise tulokset + publishing: Tuloksia julkaistaan... + update: + invalid: Tulosten julkaiseminen epäonnistui. + rejected: Tulosten julkaiseminen evättiin sähköisessä ääniuurnassa. Yritä uudestaan tai ota yhteyttä järjestelmänvalvojaan. + success: Tulosten julkaiseminen onnistui. + monitoring_committee_members: + create: + invalid: Tarkkailukomitean jäsenen luonti epäonnistui. + success: Tarkkailukomitean jäsenen luonti onnistui. + destroy: + invalid: Tarkkailukomitean jäsenen poistaminen epäonnistui. + success: Tarkkailukomitean jäsenen poistaminen onnistui. + form: + existing_user: Olemassa oleva osallistuja + non_user: Kutsu uusi osallistuja + select_user: Etsi nimellä, sähköpostilla tai nimimerkillä + user_type: Osallistujan tyyppi + index: + title: Tarkkailukomitea + new: + create: Luo + title: Luo tarkkailukomitean jäsen + monitoring_committee_polling_station_closures: + actions: + title: Toiminnot + validate: Vahvista + view: Näytä + closures: + change_election: Muuta vaalia + signed: Allekirjoitettu? + title: Vaalin %{election_title} äänestyspaikat + validated: Vahvistettu? + edit: + change_polling_station: Takaisin äänestyspaikkoihin + monitoring_committee_notes: Huomautukset + monitoring_committee_notes_placeholder: Ilmoita kaikista huomionarvoisista tapahtumista täällä + title: Vaalin %{election_title} tulokset äänestyspaikalla %{polling_station_title} + elections: + title: Valitse vaali, jonka haluat vahvistaa + show: + change_polling_station: Takaisin äänestyspaikkoihin + monitoring_committee_notes: Huomiot tarkkailukomitealle + validate: + error: Äänestyksen päättämisen vahvistaminen epäonnistui. + success: Äänestyksen päättämisen vahvistaminen onnistui. + monitoring_committee_verify_elections: + index: + download: Lataa + how_to_checksum: 'Varmistaaksesi, että lataamasi tiedosto ei ole korruptoitunut tai muuttunut lataamisen aikana, aja seuraava komento konsolissasi ja tarkasta, että komennon antama tarkistussumma vastaa yllä esitettyä tarkistussummaa:' + how_to_download: Vahvistaaksesi vaalin, lataa sen vahvistettava tiedosto yllä olevasta taulukosta. + how_to_run_verifier: 'Kun olet ladannut tiedoston ja tarkastanut sen olevan eheä, voit ajaa yleisen tarkastuksen. Kloonaa tämä repositorio ja aja seuraava komento sen juurihakemistossa:' + how_to_title: Kuinka vaalin oikeellisuus vahvistetaan + not_available: Ei vielä saatavilla + title: Vaalit + polling_officers: + create: + invalid: Äänestysvirkailijan luonti epäonnistui. + success: Äänestysvirkailijan luonti onnistui. + destroy: + invalid: Äänestysvirkailijan poistaminen epäonnistui. + success: Äänestysvirkailijan poistaminen onnistui. + form: + existing_user: Olemassa oleva osallistuja + non_user: Kutsu uusi osallistuja + select_user: Etsi nimellä, sähköpostilla tai nimimerkillä + user_type: Osallistujan tyyppi + index: + role_manager: virkailija + role_president: esimies + title: Äänestysvirkailijat + new: + create: Luo + title: Luo äänestysvirkailija + polling_officers_picker: + choose_polling_officers: Valitse äänestysvirkailijat + no_polling_officers: Yksikään äänestysvirkailija ei vastaa hakuehtojasi tai yhtään äänestysvirkailijaa ei vielä ole määritetty. + polling_stations: + create: + invalid: Äänestyspaikan luonti epäonnistui. + success: Äänestyspaikan luonti onnistui. + destroy: + invalid: Äänestyspaikan poistaminen epäonnistui. + success: Äänestyspaikan poistaminen onnistui. + edit: + title: Muokkaa äänestyspaikkaa + update: Päivitä äänestyspaikkaa + form: + address_help: 'Osoite: käytetään geokoodauksessa sijainnin löytämiseen' + location_help: 'Sijaintitieto: äänestäjille näytettävä viesti, jolla selvennetään äänestyspaikan tarkkaa sijaintia' + location_hints_help: 'Sijainnin lisätiedot: sijaintia tarkentava lisätieto. Esimerkiksi: äänestyspaikan kerros rakennuksessa.' + polling_station_managers_help: 'Äänestyspaikan virkailijat: ne virkailijat, jotka hallinnoivat äänestyspaikkaa. Tarkasta, että virkailijat on jo luotu Äänestysvirkailijat-osiossa ja heitä ei ole määrätty mihinkään muuhun äänestyspaikkaan' + polling_station_president_help: 'Äänestyspaikan esimies: se virkailija, joka toimii äänestyspaikan esimiehenä. Tarkasta, että virkailija on jo luotu Äänestysvirkailijat-osiossa ja häntä ei ole määrätty mihinkään muuhun äänestyspaikkaan.' + select_president: Valitse äänestysvirkailija äänestyspaikan esimieheksi + index: + title: Äänestyspaikat + new: + create: Luo + title: Luo äänestyspaikka + update: + invalid: Äänestyspaikan luonti epäonnistui. + success: Äänestyspaikan päivitys onnistui. + titles: + votings: Äänestykset + votings: + actions: + confirm_destroy: Oletko varma? + destroy: Tuhoa + new_voting: Uusi äänestystila + create: + invalid: Äänestyksen luonti epäonnistui. + success: Äänestyksen luonti onnistui. + edit: + add_election_component: Tälle äänestykselle ei ole määritetty vaalia. Lisää vaali Komponentit-osiosta. + assign_missing_officers: Joillekin äänestyspaikoille ei ole määrätty esimiehiä ja/tai virkailijoita. Määritä virkailijat Äänestyspaikat-osiosta. + update: Päivitä + form: + banner_image: Bannerikuva + census_contact_information: Henkilötietorekisterin vastuutahon yhteystiedot + census_contact_information_help: Nämä yhteystiedot ovat tarkoitettu osallistujille, jotka haluavat ilmoittaa virheistä henkilötietorekisterissä. Yhteystieto voi olla sähköpostiosoite, yhteydenottolomake toisella sivustolla, avoin verkkokysely, tms. + introductory_image: Esittelykuva + promoted: Korostettu + select_a_voting_type: Valitse äänestystapa + show_check_census_help: Määrittää, näytetäänkö "Voinko äänestää?" -linkki julkisten äänestysten sivuilla. + slug: Tunniste + slug_help_html: 'URL-tunnisteita käytetään luomaan URL-osoitteita, jotka viittaavat tähän äänestykseen. Hyväksyy vain kirjaimet, numerot ja viivat. Kirjaimen on oltava ensimmäinen merkki tunnisteessa. Esimerkki: %{url}' + title: Otsikko + voting_type: + hybrid: Yhdistetty äänestystapa + in_person: Fyysinen äänestys + online: Verkkoäänestys + voting_type_label: Äänestystapa + new: + create: Luo + title: Uusi äänestys + update: + invalid: Äänestyksen päivitys epäonnistui. + success: Äänestyksen päivitys onnistui. + admin_log: + ballot_style: + create: "%{user_name} loi äänestystyylin koodilla %{ballot_style_code} osallistumistilassa %{space_name}" + delete: "%{user_name} poisti äänestystyylin koodilla %{ballot_style_code} osallistumistilasta %{space_name}" + update: "%{user_name} päivitti äänestystyyliä koodilla %{ballot_style_code} osallistumistilassa %{space_name}" + census: + create: "%{user_name} loi henkilötietorekisterin osallistumistilalle %{space_name}" + delete: "%{user_name} poisti henkilötietorekisterin osallistumistilasta %{space_name}" + update: "%{user_name} päivitti henkilötietorekisteriä osallistumistilassa %{space_name}" + monitoring_committee_member: + create: "%{user_name} määritti käyttäjän %{monitoring_committee_member_user} tarkkailukomitean jäseneksi osallistumistilassa %{space_name}" + delete: "%{user_name} poisti käyttäjän %{monitoring_committee_member_user} tarkkailukomitean jäsenyyden osallistumistilassa %{space_name}" + polling_officer: + create: "%{user_name} määritti käyttäjän %{polling_officer_user} äänestysvirkailijaksi osallistumistilassa %{space_name}" + delete: "%{user_name} poisti käyttäjän %{polling_officer_user} äänestysvirkailijan roolin osallistumistilasta %{space_name}" + polling_station: + create: "%{user_name} loi äänestyspaikan %{resource_name} osallistumistilaan %{space_name}" + delete: "%{user_name} poisti äänestyspaikan %{resource_name} osallistumistilasta %{space_name}" + update: "%{user_name} päivitti äänestyspaikkaa %{resource_name} osallistumistilassa %{space_name}" + voting: + create: "%{user_name} loi äänestyksen %{resource_name}" + publish: "%{user_name} julkaisi äänestyksen %{resource_name}" + unpublish: "%{user_name} lopetti äänestyksen %{resource_name} julkaisun" + census: + admin: + census: + create: + invalid: Henkilötietorekisterin lataaminen epäonnistui, yritä uudestaan myöhemmin. + invalid_csv_header: CSV-otsakkeet puuttuvat puuttuvat tai ovat virheellisiä - lue ohjeet huolellisesti. + creating_data: + info_message: "Odota hetki, käsitelty %{processed_count} / %{raw_count} riviä tiedostosta %{file} (tämä voi kestää useita minuutteja)." + delete: + button: Poista kaikki henkilötietorekisterin tiedot + confirm: Väestörekisterin tietojen poistamista ei voi peruuttaa jälkikäteen. Haluatko varmasti jatkaa? + destroy: + error: Henkilötietorekisterin poistaminen epäonnistui, yritä uudestaan myöhemmin. + success: Henkilötietorekisterin tiedot poistettu. + export_access_codes: + button: Vie äänestyksen tunnistautumiskoodit + callout: Voit nyt viedä tunnistautumiskoodit järjestelmästä. Tämä voidaan tehdä vain kerran. Kun aloitat viennin, sinulle lähetetään tarvittavat ohjeet sähköpostiin %{email} + confirm: Voit viedä tunnistautumiskoodit vain kerran. Varmista, että pääset käsiksi sähköpostitiliin %{email}. + file_not_exist: Tiedostoa ei ole olemassa. + launch_error: Tunnistautumiskoodien viennin aloittaminen epäonnistui. + launch_success: Tunnistautumiskoodien vienti aloitettu. Saat sähköpostia hetken kuluttua sähköpostiosoitteeseen %{email}. + exporting_access_codes: + info_message: "Odota hetki, vientiä valmistellaan, saat hetken kuluttua viestin sähköpostiisi osoitteeseen %{email} (tämä voi kestää useita minuutteja)." + freeze: + callout: Henkilötietorekisteri on jäädytetty, minkä takia sitä ei voi muokata. + help_html: | + Henkilötietorekisterin tiedot on ladattu, äänestyskoodien luonti ja vienti on onnistunut.
    + Voit nyt aloittaa äänestyksen.
    + Käytä vietyä CSV-tiedostoa ja toimita siinä näkyvät äänestyskoodit henkilötietorekisterin henkilöille haluamallasi tavalla tai ota käyttöön "Voinko äänestää" -välilehti mahdollistaaksesi osallistujille heidän omien koodiensa tilaaminen henkilötietorekisteriin merkittyjen tietojen perusteella. + generate_access_codes: + button: Luo äänestyksen tunnistautumiskoodit + callout: Voit nyt jatkaa tunnistautumiskoodien luontiin. Huomioithan, että tunnistautumiskoodien luonnin jälkeen, et voi enää muokata henkilötietorekisteriä. + confirm: Jos jatkat, et voi enää muokata henkilötietorekisteriä. + info_message_all: "Tietojen tuonti onnistui tiedostosta %{file} (yhteensä tuotu %{raw_count} / %{data_count} riviä)." + info_message_warn: Tarkasta, että mitään tietoja ei puutu, koska %{data_count} tietuetta luotiin ja ladatussa tiedostossa %{file} on %{raw_count} riviä. + launch_error: Tunnistautumiskoodien luonnin aloittaminen epäonnistui + launch_success: Koodien luonti aloitettu. + start_over: Poista nykyinen henkilötietorekisteri ja aloita alusta oikeamuotoisella CSV-tiedostolla, joka sisältää ohjeiden mukaan muotointuja rivejä. + generating_access_codes: + info_message: "Odota hetki, äänestyskoodeja luodaan (tämä voi kestää useita minuutteja)..." + new: + file_help: + explanation: 'Tiedoston ohjeistus:' + message_1: Vain CSV (.csv) tiedostot ovat sallittuja. + message_2: Erotin sarakkeiden välillä on oltava puolipiste (";"). + has_ballot_styles_message: Olet määrittänyt äänestystyylejä. Pidä huolta siitä, että "%{ballot_style_code_header}" -kenttä CSV-tiedostossa vastaa rivin äänestystyylin koodia. + info_message: "Henkilötietorekisteriä ei ole vielä olemassa. Käytä alla olevaa lomaketta luodaksesi rekisterin tuomalla tiedot CSV-tiedostosta." + missing_ballot_styles_message: 'Tälle äänestykselle ei ole määritetty yhtään äänestystyyliä. Jos haluat lisätä ehdollisia kysymyksiä (ts. esittää äänestäjille eri kysymyksiä esimerkiksi riippuen heidän asuinalueestaan), sinun on määritettävä äänestystyylit ennen henkilötietorekisterin lataamista järjestelmään. Jos haluat esittää kaikille äänestäjille samat kysymykset, voit jatkaa henkilötietorekisterin lataamiseen.' + submit: Lähetä CSV + title: Luo henkilötietorekisteri + show: + heading: Äänestystilan henkilötietorekisteri + upload_info: + csv_example_with_ballot_style: 'Esimerkki tiedostosta, jossa on äänestystyylejä:' + csv_example_without_ballot_style: 'Esimerkki tiedostosta, jossa ei ole äänestystyylejä:' + csv_header_after: Älä lisää viimeistä saraketta ("%{ballot_style_code_header}"), jos et tarvitse äänestystyylejä tai ehdollisia kysymyksiä + csv_header_before: 'Henkilötietorekisterin tiedot tulee antaa CSV-tiedostossa seuraavien otsakekenttien kanssa:' + document_types: + identification_number: Henkilötunnus + passport: Passi + export_mailer: + access_codes_export: + click_button: 'Voit ladata tunnistautumiskoodit seuraavan linkin kautta.
    Tiedosto on ladattavissa %{date} saakka.
    Tarvitset 7-Zip-ohjelman (Windowsille), Keka-ohjelman (macOS-käyttöjärjestelmille) tai PeaZip-ohjelman (Linux-käyttöjärjestelmille) avataksesi tiedoston. Salasana tiedoston avaamista varten: %{password}' + download: Lataa + subject: Äänestyksen tunnistautumiskoodien vientitiedosto äänestykselle %{voting_title} on nyt ladattavissa + vote_flow: + already_voted_in_person: Tämä osallistuja on jo äänestänyt fyysisessä tilaisuudessa eikä hänellä ole oikeutta äänestää verkossa. + datum_not_found: Annetut tiedot eivät vastaa yhtään äänestäjää. + content_blocks: + highlighted_votings: + name: Korostetut äänestykset + landing_page: + polling_stations: + heading: Äänestyspaikat + no_polling_stations: Äänestyspaikkoja ei vielä ole. + monitoring_committee_members: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + new: Uusi jäsen + title: Toiminnot + pages: + home: + highlighted_votings: + active_spaces: Aktiiviset äänestykset + see_all_spaces: Näytä kaikki äänestykset + polling_officer_zone: + closures: + back_to_polling_stations: Takaisin äänestyspaikkoihin + certify: + add_photos: Lisää kuvia + edit_photos: Muokkaa kuvia + error: Varmenteen liittäminen epäonnistui, yritä uudestaan. + heading: Äänten uudelleenlaskenta - Lataa varmenne + info_text: Lataa kuva äänestyksen päättämisen varmenteesta. + submit: Lataa varmenne + success: Varmenteen lataaminen onnistui. + upload_photos: Lataa kuva äänestyksen päättämisen todistuksesta + completed: + sub_heading: Uudelleenlaskenta on vahvistettu ja sitä ei voi enää muokata. + create: + error: Äänestyksen päättämisen luonti epäonnistui, yritä uudestaan myöhemmin. + success: Äänestyksen päättämisen luonti onnistui. + destroy: + error: Äänestyksen päättämisen poistamisessa tapahtui virhe. + success: Äänestyksen päättämisen poisto onnistui. + edit: + confirm_start_over: Tämä poistaa äänten kokonaismäärän ja kaikki liitetyt muistiinpanot. Haluatko varmasti jatkaa? + heading: Äänten uudelleenlaskenta - Vastausten uudelleenlaskenta + info_text: Määritä jokaisen kysymyksen vastausten kokonaismäärä. Tämän tulee vastata edellisessä vaiheessa määritettyä vastausten kokonaismäärää (yhteensä %{total} vastausta). + modal_ballots_results_count_error: + blank: Oletettu tyhjien äänten määrä on %{expected}, mutta tyhjien vastausten yhteismäärä on %{current}. + close_modal: Sulje + info_text: Äänten kokonaismäärä ei vastaa kirjeiden kokonaismäärää. Tarkasta äänten kokonaismäärä. + title: Äänten kokonaismäärä ei täsmää + total: Oletettu äänten määrä on %{expected}, mutta hyväksyttyjen ja tyhjien äänten yhteismäärä on %{current}. + valid: Oletettu hyväksyttyjen äänten määrä on %{expected}, mutta hyväksyttyjen vastausten yhteismäärä on %{current}. + save_recount: Tallenna uudelleenlaskenta + start_over: Jos kokonaismäärässä on virhe, voit poistaa kaikki tiedot ja aloittaa alusta. + total_ballots: Äänten kokonaismäärä + total_blank_ballots: Tyhjien äänten kokonaismäärä + total_null_ballots: Hylättyjen äänten kokonaismäärä + total_valid_ballots: Kelvollisten äänten kokonaismäärä + new: + election: 'Vaali:' + heading: Äänten uudelleenlaskenta + info_text: 'Syötä tällä äänestyspaikalla uudelleenlaskettavien äänten (kirjeiden) kokonaismäärä:' + modal_ballots_count_error: + btn_validate_total: Tarkasta äänten uudelleenlaskenta + info_explanation_text: 'Tarkasta äänten kokonaismäärä. Jos kokonaismäärä ei ole oikea, sinun on annettava selvitys tarkkailukomitealle:' + info_text: Syötettyjen äänten (kirjeiden) kokonaismäärä ei täsmää ihmisten määrään, jotka ovat äänestäneet tällä äänestyspaikalla. + message_for_monitoring_committee: Viesti tarkkailukomitealle + review_recount: Tarkasta uudelleenlaskenta + text_area_placeholder: Kirjoita viestisi + title: Tietueiden kokonaismäärä ei täsmää + total_ballots: 'Äänten kokonaismäärä:' + total_people: 'Henkilöt yhteensä:' + polling_station: 'Äänestyspaikka:' + submit: Vahvista kokonaismäärä + total_ballots_count: Äänten kokonaismäärä + show: + edit_count_votes: Syötitkö väärät numerot? Voit edelleen muokata niitä. + heading: Äänten uudelleenlaskenta + sub_heading: Äänten uudelleenlaskenta tulee sulkea lataamalla todistus ääntenlaskennan päättämisestä. Tämän jälkeen uudelleenlaskenta sinetöidään ja sitä ei voi enää muokata. + sign: + cancel: Peruuta + check_box: Olen tarkastanut tämän ja se on sama kuin fyysisen äänestyksen päättämisen todistus + confirm: Ok, jatka + error: Tapahtui jokin virhe. Yritä uudestaan. + heading: Äänten uudelleenlaskenta - Allekirjoita äänestyksen päättäminen + info_text: Jos jatkat, et voi enää muokata mitään tietoja. Tätä toimintoa ei voi peruuttaa. + submit: Allekirjoita äänestyksen päättäminen + success: Äänestyksen päättämisen allekirjoittaminen onnistui. + update: + error: Äänestyksen päättämisen tulosten päivittäminen epäonnistui, yritä uudestaan myöhemmin. + success: Äänestyksen päättämisne tulosten päivittäminen onnistui. + in_person_votes: + complete_voting: + available_answers: 'Mahdolliset vastaukset:' + census_verified: Tämä osallistuja ei ole vielä äänestänyt fyysisesti. + census_verified_with_online_vote: Tämä osallistuja on jo äänestänyt verkossa. Jos hän äänestää fyysisesti, edelliset äänet mitätöidään ja ainoastaan fyysinen ääni lasketaan. + complete_voting: Viimeistele äänestys + identify_another: Tunnista toinen osallistuja + questions_title: 'Osallistujilla on oikeus äänestää seuraavista kysymyksistä:' + questions_title_voted: 'Osallistuja on jo äänestänyt verkossa ja hänellä on oikeus äänestää seuraavista kysymyksistä:' + voted: Osallistuja on äänestänyt + create: + error: Ääntä ei tallennettu. Ole hyvä ja yritä uudelleen. + in_person_form: + census_not_present: Tätä osallistujaa ei ole henkilötietorekisterissä. + census_not_present_description: Osallistujan on mentävä reklamoimaan henkilötietorekisteriä hallinnoivaan toimistoon tai otettava yhteyttä tukeen. + date_of_birth: Syntymäaika + day: Päivä + day_placeholder: PV + document_number: Asiakirjan numero + document_number_placeholder: Henkilöllisyystodistuksen numero + month: Kuukausi + month_placeholder: KK + select: Valitse asiakirjan tyyppi + title: 'Valitse asiakirjan tyyppi ja syötä osallistujan asiakirjan numero:' + validate_document: Vahvista asiakirja + year: Vuosi + year_placeholder: VVVV + new: + back: Takaisin äänestyspaikkoihin + title: Tunnista ja vahvista osallistuja + show: + back: Takaisin äänestyspaikkoihin + title: Odottaa fyysisessä tilaisuudessa annetun äänen tallennusta + update: + error: Äänen tallennuksessa tapahtui virhe. Ole hyvä ja yritä uudelleen. + success: + accepted: Äänen tallennus onnistui. + rejected: Ääntä ei hyväksytty sähköisessä ääniuurnassa. Ota yhteyttä järjestelmänvalvojaan. + verify_document: + census_present: Tätä osallistujaa ei ole henkilötietorekisterissä. + name: Nimi + title: 'Tarkista, että seuraavat tiedot ovat oikein:' + verify_document: Vahvista asiakirja + menu: + polling_officer_zone: Äänestysvirkailijoiden alue + polling_officers: + index: + polling_officer_role_description: Sinulle on annettu tehtäväksi toimia äänestyspaikan virkailijana (esimiehenä tai muuna virkailijana) tällä alustalla järjestettävissä vaaleissa. + polling_station: + address: Osoite + count_votes: Laske äänet + election: Vaali + identify_person: Tunnista henkilö + name: Nimi + no_polling_stations: Sinua ei ole vielä liitetty millekään äänestyspaikalle. + role: Roolisi + show_closure: Näytä äänestyksen päättäminen + title: Äänestyspaikat + voting: Äänestys + polling_officers: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + new: Uusi äänestysvirkailija + title: Toiminnot + roles: + manager: Virkailija + president: Esimies + unassigned: Ei määritetty + polling_station_closure_certificate: + current_certificate: 'Nykyinen todistus:' + polling_station_closure_recount: + nota_option: Tyhjä / Ei mitään näistä + polling_officer_notes: 'Äänestysvirkailijan muistiinpanot:' + polling_officer_notes_blank: Muistiinpanoja ei ole + recount_summary: 'Uudelleenlaskennan yhteenveto:' + signed: Allekirjoitettu + total_ballots: 'Äänten kokonaismäärä:' + total_blank_ballots: 'Tyhjien äänten kokonaismäärä:' + total_null_ballots: 'Hylättyjen äänten kokonaismäärä:' + total_valid_ballots: 'Kelvollisten äänten kokonaismäärä:' + polling_stations: + actions: + confirm_destroy: Oletko varma? + destroy: Poista + edit: Muokkaa + new: Uusi äänestyspaikka + title: Toiminnot + votings: + access_code_modal: + email: Lähetä sähköpostitse osoitteeseen %{email} + info: Tarvitset tunnistautumiskoodin osallistuaksesi. Jos et saanut sitä paperipostissa, voimme lähettää sinulle uuden koodin. + no_email: Sähköpostiosoitetta ei ole käytettävissä + no_sms: Puhelinnumeroa ei ole käytettävissä + sms: Lähetä tekstiviestinä numeroon %{sms} + title: Hanki tunnistautumiskoodi + check_census: + check_status: Tarkista tila + description: Täällä voit tarkastaa omat tietosi henkilötietorekisterissä selvittääksesi, onko sinulla oikeus osallistua tähän äänestykseen. Sinulla pitäisi jo olla tunnistautumiskoodi, mutta jos hävitit sen, voit pyytää sitä uudestaan, jos tietosi on syötetty oikein. + error: + info: 'Yritä uudestaan. Jos epäilet, että järjestelmässä olevat tiedot eivät pidä paikkaansa, voit ilmoittaa siitä täällä: %{census_contact_information}.' + title: Syöttämäsi tiedot eivät ole tämän äänestyksen henkilötietorekisterissä + form_title: 'Täytä seuraava lomake tarkistaaksesi tietosi henkilötietorekisterissä:' + invalid: Henkilötietorekisterin tietojen tarkastaminen epäonnistui. + success: + access_link: sähköpostitse. + access_link_with_sms: tekstiviestitse tai sähköpostitse. + info: Sinun olisi pitänyt saada tunnistautumiskoodisi paperipostissa. Jos sinulla ei ole koodia, voit pyytää sitä täältä + title: Tietosi henkilötietorekisterissä ovat oikein! + title: Voinko äänestää? + check_fields: + date_of_birth: Syntymäaika + day: Päivä + day_placeholder: PV + document_number: Asiakirjan numero + document_number_placeholder: Henkilöllisyystodistuksen numero + document_type: Asiakirjan tyyppi + month: Kuukausi + month_placeholder: KK + postal_code: Postinumero + postal_code_placeholder: Postinumero + select: Valitse asiakirjan tyyppi + year: Vuosi + year_placeholder: VVVV + count: + title: + one: "%{count} äänestys" + other: "%{count} äänestystä" + elections_log: + description: Vaalin loki näyttää jokaiseen äänestykseen liittyvän olennaisen tiedon. Esimerkiksi, avainseremonian tai ääntenlaskennan tila sekä tiedon tulosten julkistamisesta. Klikkaa sitä vaalia, jonka lokia haluat tarkastella. + title: Vaalin loki + filters: + active: Aktiiviset + all: Kaikki + date: Päivämäärä + finished: Päättyneet + search: Hae + upcoming: Tulevat + index: + no_votings: Hakuehdoillasi ei löytynyt yhtään äänestystä. + only_finished: Tällä hetkellä ei ole yhtään ajastettua äänestystä, mutta täältä näet kaikki päättyneet äänestykset. + title: Äänestykset + login: + access_code: Tunnistautumiskoodi + access_code_placeholder: Tunnistautumiskoodi + ask_for_a_new_one: Pyydä uutta. + dont_have_access_code: Eikö sinulla ole tunnistautumiskoodia? + form_title: 'Täytä seuraava lomake aloittaaksesi äänestyksen:' + start_voting: Aloita äänestys + title: Tunnistan itseni henkilötietorekisterin tietojen avulla + no_census_contact_information: Yhteystietoja ei ole vielä. + orders: + label: 'Järjestä äänestykset:' + random: Satunnainen + recent: Viimeisimmät + send_access_code: + invalid: Tunnistautumiskoodin lähetys epäonnistui. + success: Tunnistautumiskoodisi lähetys onnistui. + show: + title: Tietoa tästä äänestyksestä + votings_m: + badge_name: + finished: Päättynyt + ongoing: Käynnissä + upcoming: Tulossa + unspecified: Ei määritetty + voting_type: + hybrid: Yhdistetty äänestystapa + in_person: Fyysinen äänestys + online: Verkkoäänestys + layouts: + decidim: + voting_navigation: + check_census: Voinko äänestää? + election_log: Vaalin loki + votings: + index: + promoted_votings: Korostetut äänestykset + promoted_voting: + vote: Äänestä diff --git a/decidim-elections/config/locales/fr-CA.yml b/decidim-elections/config/locales/fr-CA.yml new file mode 100644 index 00000000..4afdd1dc --- /dev/null +++ b/decidim-elections/config/locales/fr-CA.yml @@ -0,0 +1,1426 @@ +fr-CA: + activemodel: + attributes: + answer: + description: Description + image: Image + proposals: Propositions associées + title: Titre + ballot_style: + code: Code + election: + description: Description + end_time: Le vote se termine à + start_time: Le vote commence à + title: Titre + monitoring_committee_member: + email: Email + name: Nom + polling_officer: + email: Email + name: Nom + polling_station: + address: Adresse + location: Lieu + location_hints: Indication de lieu + polling_station_managers: Gestionnaires + polling_station_president_id: Président + title: Titre + question: + max_selections: Nombre maximum de sélections + min_selections: Aucune des options ci-dessus + title: Titre + trustees_participatory_space: + user_id: Participant + voting: + banner_image: Image de bannière + census_contact_information: Informations de contact du recensement + description: Description + end_time: Le vote se termine + introductory_image: Image d'introduction + promoted: Mis en avant + scope_id: Secteur + show_check_census: Afficher la page "Vérifier le recensement" + start_time: Le vote commence le + title: Titre + voting_type: Type de vote + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Doit être téléchargé de nouveau + ballot_result: + attributes: + base: + total_count_invalid: Le nombre total de réponses ne correspond pas à la répartition valide/blanc/nul. + election: + attributes: + attachment: + needs_to_be_reattached: Doit être téléchargé de nouveau + question_result: + attributes: + base: + blank_count_invalid: Le nombre total de réponses vides ne peut pas être supérieur au nombre total de bulletins blancs. + trustee: + attributes: + name: + cant_be_changed: ne peut pas être modifié + public_key: + cant_be_changed: ne peut pas être modifié + voting: + attributes: + voting_type: + inclusion: "%{value} n'est pas un type de vote valide" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Le responsable de scrutin est déjà président/responsable d'un bureau de vote. + voting: + different_organization: L'élection doit être dans la même organisation que l'utilisateur. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Le responsable de scrutin doit être dans la même élection que le bureau de vote. + models: + decidim/elections/answer: + one: Réponse + other: Réponses + decidim/elections/election: + one: Élection + other: Élections + decidim/elections/question: + one: Question + other: Questions + decidim/voting: + one: Scrutin + other: Scrutins + decidim/votings/census/dataset: + one: Jeu de données + other: Jeux de données + decidim/votings/census/datum: + one: Donnée + other: Données + decidim/votings/polling_officer: + one: Responsable du scrutin + other: Responsables du scrutin + decidim/votings/polling_station: + one: Bureau de vote + other: Bureaux de vote + decidim/votings/voting: + one: Vote + other: Votes + decidim: + admin: + filters: + officers_assigned_eq: + label: Officier + values: + assigned: Assigné + unassigned: Non affecté + role_eq: + label: Rôle + values: + manager: Responsable + president: Président + unassigned: Non assigné(e) + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Rechercher %{collection} par nom/email/pseudo ou par bureau de vote. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Recherchez %{collection} par titre, adresse ou nom d'officier/email/pseudo de l'officier. + signed_eq: + label: Signé + values: + 'false': Signé + 'true': Non signé + validated_eq: + label: Validé + values: + 'false': Non validé + 'true': Validé + voting_publications: + create: + error: Une erreur s'est produite lors de la publication de ce vote. + success: Vote publié avec succès. + destroy: + error: Il y a eu un problème lors de la dépublication de ce vote. + success: Vote dépublié avec succès. + components: + elections: + actions: + vote: Voter + name: Élections + settings: + global: + announcement: Annonce + step: + announcement: Annonce + elections: + actions: + confirm_destroy: Êtes-vous certain ? + destroy: Détruire + edit: Modifier + feedback: Feedbacks des votants + import: Importer des propositions dans les réponses + manage_answers: Gérer les réponses + manage_questions: Gérer les questions + manage_steps: Gérer les étapes + new_answer: Nouvelle réponse + new_election: Nouvelle élection + new_question: Nouvelle question + new_trustee: Nouveau garant + preview: Aperçu + publish: Publier + title: Actions + unpublish: Dépublier + admin: + answers: + create: + invalid: Il y a eu un problème lors de la création de cette réponse. + success: Réponse créée avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de cette réponse. + success: Réponse supprimée avec succès. + edit: + title: Modifier la réponse + update: Mettre à jour la réponse + index: + invalid_max_selections: Vous avez besoin de %{missing_answers} réponse(s) de plus pour correspondre aux sélections maximales. + title: Réponses + new: + create: Créer une réponse + title: Nouvelle réponse + not_selected: Non sélectionnée + select: + disable: Désélectionner la réponse + enable: Marquer la réponse comme sélectionnée + invalid: Un problème est survenu lors de la sélection de cette réponse. + success: Réponse sélectionnée avec succès. + selected: Sélectionnée + unselect: + invalid: Une erreur est survenue lors de la désélection de cette réponse. + success: Réponse désélectionnée avec succès. + update: + invalid: Un problème est survenu lors de la mise à jour de cette réponse. + success: Réponse mise à jour avec succès. + elections: + create: + invalid: Il y a eu un problème lors de la création de cette élection. + success: Élection créée avec succès. + destroy: + invalid: Une erreur s'est produite lors de la suppression de cette élection. + success: Élection supprimée avec succès. + edit: + title: Modifier l'élection + update: Mettre à jour l'élection + form: + organization_time_zone: Vérifiez dans les paramètres de l'organisation que le fuseau horaire de l'organisation est correct. La configuration actuelle est %{time_zone} (%{time}). + index: + no_bulletin_board: Il n'y a pas de serveur Bulletin Board configuré, ce qui est nécessaire pour utiliser ce module. Cette tâche doit être effectuée par l'administrateur système. + title: Élections + new: + create: Créer une élection + title: Nouvelle élection + publish: + success: L'élection a été publiée avec succès. + unpublish: + success: L'élection a été dépubliée avec succès. + update: + invalid: Il y a eu un problème lors de la mise à jour de cette élection. + success: Élection mise à jour avec succès. + exports: + elections: Élections + feedback_form_answers: Feedbacks des répondants + mailers: + trustee_mailer: + body: + help_html: |- +

    Bonjour %{user_name},


    +

    Vous avez été invité pour agir en tant que garant d'une élection sur %{organization}.


    +

    Une nouvelle section nommée "Espace de garant" a été activée sur votre compte. Depuis cette section, vous pourrez agir en tant que garant. Pour l'instant, nous vous prions de générer vos clés d'identification.


    + subject: Vous avez été ajouté(e) comme garant pour %{resource_name} + trustee_zone: Emmenez-moi dans la zone des garants + menu: + trustees: Garants + models: + answer: + name: Réponse + proposals_imports: + create: + invalid: Un problème est survenu lors de l'import des propositions en réponses. + success: "%{number} propositions importées en réponses." + new: + create: Importer des propositions dans les réponses + no_components: Il n'y a pas d'autres composantes de proposition dans cet espace participatif pour importer les propositions dans des réponses. + select_component: Veuillez sélectionner un composant + title: Importer des propositions + questions: + create: + election_started: L'élection a déjà commencé. + invalid: Il y a eu un problème lors de la création de cette question. + success: Question créée avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de cette question. + success: Question supprimée avec succès. + edit: + title: Modifier la question + update: Mettre à jour la question + index: + title: Questions + new: + create: Créer une question + title: Nouvelle question + update: + invalid: Une erreur s'est produite lors de la mise à jour de cette question. + success: Question mise à jour avec succès. + steps: + create_election: + census: Recensement + errors: + census_codes_generated: Les codes d'accès pour le recensement ne sont pas générés. + census_frozen: Les codes d'accès pour le recensement ne sont pas exportés. + census_uploaded: Il n'y a pas de recensement téléchargé pour cette élection. + component_published: La fonctionnalité élection n'est pas publiée. + fix_it_text: Corriger + max_selections: Les questions n'ont pas de valeur correcte pour le nombre de réponses + minimum_answers: Les questions doivent avoir au moins deux réponses. + minimum_questions: L'élection doit avoir au moins une question. + published: L'élection n'est pas publiée. + time_before: L'heure de début se situemoins de %{hours} heures avant le commencement de l'élection. + trustees_number: L'espace participatif doit avoir au moins %{number} garants avec une clé publique. + invalid: Une erreur est survenue lors de la création de l'élection + no_trustees: Il n'y a pas de garants configurés pour cet espace participatif + not_used_trustee: "(non utilisé)" + public_key: + 'false': n'a pas de clé publique + 'true': a une clé publique + requirements: + census_codes_generated: Les codes d'accès pour le recensement sont générés. + census_frozen: Les codes d'accès pour le recensement sont exportés et le recensement est gelé. + census_uploaded: Le recensement a été téléchargé. + component_published: La fonctionnalité élection est publiée. + max_selections: Toutes les questions ont une valeur correcte pour le maximum de réponses. + minimum_answers: Chaque question a au moins 2 réponses. + minimum_questions: L'élection a au moins 1 question. + published: L'élection est publiée. + time_before: La configuration est réalisée au moins %{hours} heures avant le début de l'élection. + trustees_number: L'espace participatif doit avoir au moins %{number} garants avec une clé publique. + submit: Configurer l'élection + success: L' élection a été envoyée avec succès au Panneau d'Affichage. + technical_configuration: + authority_name: "Nom de l'autorité : %{value}" + bulletin_board_server: "Serveur du Panneau d'affichage: %{value}" + scheme_name: "Nom du schema : %{value}" + title: Voir les informations techniques + title: Configurer l'élection + trustees: Garants de l'élection + created: + invalid: Il y a eu un problème lors du démarrage de la cérémonie des clés. + submit: Démarrer la cérémonie des clés + success: La demande de lancement de cérémonie des clés a bien été envoyée au Panneau d'Affichage. + title: Élection créée + trustees: Garants + key_ceremony: + continue: Continuer + title: Cérémonie des clés + key_ceremony_ended: + errors: + time_before: L'élection est prête à commencer. Vous devez attendre %{hours} heures avant le lancement (%{start_time}) pour démarrer la période de vote. + invalid: Il y a eu un problème au démarrage de la période de vote. + requirements: + time_before: L'élection va bientôt commencer. Vous pouvez démarrer la période de vote manuellement, ou elle sera démarrée automatiquement avant l'heure de départ, à %{start_time}. + submit: Commencer la période de vote + success: La demande de lancement de la période de vote a bien été envoyée au Panneau d'Affichage. + title: Prêt à commencer + processing: Traitement en cours... + results_published: + answer: Répondre + not_selected: Non sélectionné + question: Question + result: Résultat + selected: Sélectionné + submit: Envoyer + title: Résultats publiés + tally_ended: + answer: Répondre + not_selected: Non sélectionné + question: Question + result: Réalisation + selected: Sélectionné + submit: Publier les résultats + success: La demande de publication des résultats a bien été envoyée au Panneau d'Affichage. + title: Résultat du calcul + tally_started: + continue: Continuer + invalid: Une erreur est survenue lors du signalement du garant absent. + mark_as_missing: Marquer comme absent + mark_as_missing_description: Tous les garants doivent participer à ce processus, toutefois, si l'un deux ne peut pas y participer, vous pouvez le marquer comme manquant. + success: Le garant manquant a été envoyé avec succès au Panneau d'Affichage. + tally_completion: Le processus sera terminé lorsque tout les garants seront marqués comme actif ou absent. Au moins %{quorum} garants sont requis pour compléter le processus. + title: Dépouillement + undo_mark_as_missing: Un garant marqué comme absent par erreur sera capable de participer avant l'achèvement du processus. Ils peuvent procéder comme d'habitude et le signalement d'absence sera ignoré. + vote: + errors: + time_after: L'élection est toujours en cours. Vous devez attendre (%{end_time}) pour terminer la période de vote. + invalid: Un problème est survenu lors de la clôture de la période de vote. + requirements: + time_after: L'élection est terminée. Vous pouvez terminer la période de vote manuellement, ou elle se terminera automatiquement dans quelques minutes. + submit: Fin de la période de vote + success: La demande de fin de la période de vote a été envoyée avec succès au Panneau d'Affichage. + title: Période de vote + vote_ended: + invalid: Une erreur s'est produite au lancement du dépouillement. + submit: Démarrer le dépouillement + success: La demande de lancement du dépouillement a bien été envoyée au Panneau d'Affichage. + text: Le vote est terminé. Vous pouvez commencer le comptage. + title: Période de vote terminée + vote_stats: + no_vote_statistics_yet: Pas encore de statistiques sur le vote + title: Statistiques du vote + voters: Votants + votes: Votes + trustees_participatory_spaces: + actions: + disable: Désactiver + enable: Autoriser + create: + exists: Ce garant existe déjà dans cet espace participatif. + invalid: Une erreur s'est produite lors de la création du garant. + success: Le garant a bien été créé. + delete: + invalid: Il y a eu un problème lors de la suppression de ce garant. + success: Le garant a bien été supprimé. + form: + select_user: Sélectionner un utilisateur + index: + title: Garants + new: + create: Créer un garant + title: Nouveau garant + update: + invalid: Une erreur s'est produite lors de la mise à jour du garant %{trustee}. + success: Le garant %{trustee} a été mis à jour avec succès. + admin_log: + election: + create: "%{user_name} a créé l'élection %{resource_name} dans %{space_name}" + delete: "%{user_name} a supprimé l'élection %{resource_name} dans %{space_name}" + end_vote: "%{user_name} a terminé la période de vote pour l'élection %{resource_name} de l'espace %{space_name} sur le Tableau des Bulletins" + publish: "%{user_name} a publié l'élection %{resource_name} de %{space_name}" + publish_results: "%{user_name} a publié les résultats de l'élection %{resource_name} de %{space_name} sur le Tableau d'Affichage" + report_missing_trustee: "%{user_name} a signalé %{trustee_name} en tant qu'accesseur manquant pendant le dépouillement de l'élection %{resource_name} de %{space_name} sur le Tableau d'affichage" + setup: "%{user_name} a créé l'élection %{resource_name} de %{space_name} sur le Tableau d'affichage" + start_key_ceremony: "%{user_name} a commencé la cérémonie des clés pour l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + start_tally: "%{user_name} a commencé le dépouillement pour l'élection %{resource_name} de %{space_name} sur le Tableau d'affichage" + start_vote: "%{user_name} a commencé la période de vote pour l'élection %{resource_name} de %{space_name} sur le Tableau d'affichage" + unpublish: "%{user_name} a dépublié le %{resource_name} de l'élection %{space_name}" + update: "%{user_name} a mis à jour l'élection %{resource_name} de %{space_name}" + trustee: + create: "%{user_name} a nommé l'utilisateur %{trustee_user} en tant que accesseur" + connection: + failed: + modal: + close: Fermer + communication_lost: Malheureusement, il semble que la communication avec le serveur de vote (Bulletin Board) est perdue.
    Il se peut que la connexion Internet soit interrompue ou que le serveur de destination soit trop occupé.
    Vous pouvez réessayer plus tard ou contacter le support si ce problème persiste. + generic_error: Malheureusement, une erreur inconnue s'est produite. Il est probable que votre navigateur n'est pas pris en charge ou que vous utilisez le mode "incognito" ou "privé" qui n'est pas pris en charge. + title: Un problème est survenu + election_m: + badge_name: + finished: Terminées + ongoing: Active + upcoming: À venir + end_date: Se termine le + footer: + remaining_time: + one: "Il reste %{count} heure %{minutes} minutes pour voter." + other: "Il reste %{count} heures %{minutes} minutes pour voter." + view: Voir + vote: Voter + label: + date: Dates + questions: '%{count} questions' + start_date: Début + unspecified: Non spécifié + elections: + count: + elections_count: + one: "%{count} élection" + other: "%{count} élections" + election_log: + chained_hash: La chaîne de hash de ce message + complete: Terminée + creation_description: + complete: L'élection a bien été créée et ajoutée sur le Tableau d'affichage. + not_created: L'élection n'est pas encore créée. + creation_title: Élection créée + description: Ceci est le journal des élections, où vous pouvez vérifier le statut de chaque étape, par exemple lorsque l'élection a été créée, si le processus de dépouillement est terminé, et la date de clôture de l'élection. + download: Télécharger + key_ceremony_description: + complete: La cérémonie de génération de clés est terminée. Chaque garant a des clés valides et a téléchargé les clés de sauvegarde nécessaires. + not_started: La génération des clés n'a pas encore commencée. + started: La cérémonie de génération des clés a commencé mais n'est pas encore terminée. + key_ceremony_title: Cérémonie de génération des clés + not_available: Pas encore disponible + not_created: N'est pas créée + not_ready: Pas encore prêt + not_started: Non démarré + published: Publié + results_description: + not_published: Les résultats ne sont pas encore publiés. + published: Les résultats sont publiés. + results_title: Résultats + started: Débutée + tally_description: + finished: Le dépouillement est terminé. + not_started: Le dépouillement n'a pas encore commencé. + started: Le dépouillement a commencé. + tally_title: Dépouillement + title: Journal de l'élection + unpublished: Non publié + verifiable_results: + checksum: 'Somme de contrôle SHA256:' + description: + not_ready: Le fichier de vérification électoral et la somme de contrôle SHA256 ne sont pas encore disponibles. Dès que les résultats seront publiés, vous serez en mesure de vérifier cette élection. + ready: 'Ici, vous avez la possibilité de vérifier l''élection. D''abord, vous devez télécharger le fichier et vous assurer qu''il n''a pas été corrompu. Pour ce faire, exécutez la commande suivante et vérifiez que la sortie correspond à la somme de contrôle :' + how_to_verify: 'Une fois le fichier téléchargé et vérifié qu''il est correct, vous pouvez exécuter le vérificateur universel. Clonez ce dépôt et, depuis le dossier racine, exécutez la commande suivante :' + title: Vérifier les résultats de l'élection + verifiable_file: 'Fichier de vérification d''élection:' + verify: Vérifier l'élection + vote_description: + finished: Le vote est terminé. + not_started: Le vote n'a pas encore commencé. + started: Le vote a commencé. + vote_title: Processus de vote + filters: + active: Actif + all: Toutes + date: Date + finished: Terminées + upcoming: À venir + preview: + available_answers: 'Réponses disponibles:' + description: 'Voici les questions que vous trouverez dans le processus de vote:' + title: Questions de l'élection + results: + description: 'Ce sont les résultats du vote, pour chaque question:' + percentage: "%{count}%" + selected: Sélectionné + title: Résultats de l'élection + votes: + one: "%{count} vote" + other: "%{count} votes" + show: + action_button: + change_vote: Changer votre vote + vote: Commencer à voter + vote_again: Voter à nouveau + callout: + already_voted: Vous avez déjà voté pour cette élection. Vous pouvez modifier votre vote ou le vérifier. + pending_vote: Votre vote est en cours de validation le serveur. + vote_rejected: Il n'a pas été possible de vérifier votre vote. Veuillez recommencer. + election_log: Journal de l'élection + preview: Aperçu + verify: + already_voted: Déjà voté? + verify_here: Vérifiez votre vote ici. + will_verify: Vous pourrez vérifier votre vote une fois l'élection commencée. + voting_period_status: + finished: Le vote a commencé le %{start_time} et s'est terminé le %{end_time} + ongoing: 'Vote actif jusqu''au : %{end_time}' + upcoming: Le vote commence le %{start_time} + feedback: + answer: + invalid: Un problème est survenu lors de l'envoi de votre commentaire. + spam_detected: Une erreur est survenue lorsque vous avez répondu au questionnaire. Pouvez-vous réessayer ? + success: Votre avis a bien été envoyé. + models: + answer: + fields: + proposals: Propositions + selected: Sélectionné + title: Titre + votes: Votes + election: + fields: + bb_status: Statut du Tableau d'affichage + end_time: Prend fin à + start_time: Commence à + title: Titre + verifiable_results_file_hash: Somme de contrôle SHA256 du fichier + verifiable_results_file_url: Fichier d'élection vérifiable + question: + fields: + answers: Réponses + max_selections: Nombre maximum de sélections + title: Titre + trustees_participatory_space: + fields: + considered: autorisé + email: Email + inactive: inactif + name: Nom + notification: Notification envoyée à + public_key: Clé publique + status: Statut + orders: + label: Trier les élections par + older: Les plus anciens + recent: Les plus récents + trustee_zone: + elections: + backup_modal: + description: Cette élection est en cours de création dans le Tableau d'Affichage. Il est très important que chaque Mandataire qui y participe fasse une sauvegarde de ces clés et les stocke dans un endroit sûr. Ensuite, le processus se poursuit. + download_election_keys: Télécharger les clés + title: Sauvegarder les clés d'élection pour %{election} + key_ceremony_steps: + back: Retour + description: Cette élection est en cours de création dans le Tableau d'Affichage. Pour compléter ce processus, votre participation en tant que Mandataire est nécessaire. + keys: + create_election: Génération des clés + key_ceremony: + joint_election_key: Génération de la clé conjointe + step_1: Publication des clés + list: + status: Statut + task: Tâche + process_warning: Une fois le processus démarré, vous ne devriez pas quitter cette page avant la fin du processus. Cela prendra plusieurs minutes, car tous les Mandataires doivent être connectés pour le compléter. + start: Démarrer + status: + completed: Terminé + pending: En attente + processing: Traitement en cours + title: Créer des clés d'élection pour %{election} + restore_modal: + description: Le Tableau d'Affichage a des informations pour vous en temps que Mandataire de cette élection. Pour continuer le processus, commencer par télécharger le fichier de sauvegarde généré pendant la session précédente. + title: Restaurer les clés d'élection pour %{election} + upload_election_keys: Télécharger les clés d'élection + tally_started_steps: + back: Retour + description: Les résultats de cette élection sont calculés dans le Bulletin Board et pour compléter ce processus, votre participation en tant que garant est nécessaire. + keys: + end_tally: Dépouillement terminé + tally: + cast: Valider le dépouillement + share: Partager le décompte + list: + status: Statut + task: Tâche + process_warning: Une fois le processus démarré, vous ne devriez pas quitter cette page avant la fin du processus. Cela prendra plusieurs minutes, car tous les garants doivent être connectés pour le compléter. + start: Démarrer + status: + completed: Terminé + pending: En attente + processing: Traitement en cours + title: Dépouillement pour %{election} + update: + error: Le statut de l'élection n'a pas été mis à jour. + success: 'Le statut de l''élection est : %{status}.' + menu: + trustee_zone: Espace de garant + no_bulletin_board: + body: Un tableau d'affichage configuré est requis pour cette section. Contactez l'administrateur pour plus de détails. + title: Désolé, le tableau d'affichage n'est pas encore configuré. + trustees: + show: + elections: + list: + action_required: + 'false': 'Non' + name: Action requise ? + 'true': Effectuer l'action + bb_status: Statut + election: Élection + voting_period: Période de vote + no_elections: Actuellement, vous n'avez pas été assigné pour prendre des mesures en tant que garant. Vous recevrez une notification quand il sera temps d'agir pendant les différentes phases. + title: Élections + identification_keys: + cancel: Annuler + generate: Générer les clés d'identification + generate_error: Une erreur s'est produite lors de la génération des clés d'identification. + generate_legend: Vous devez générer une paire de clés d'identification pour participer aux élections en tant que garant. + generate_legend_1: Après avoir cliqué sur le bouton, le téléchargement du fichier contenant les clés d'identification générées va commencer. + generate_legend_2: Copiez le fichier téléchargé sur un périphérique USB fiable + generate_legend_3: Assurez-vous que votre ordinateur ne possède pas de copie du fichier (par exemple, vérifiez les dossiers Téléchargements et Bureau). + generate_legend_4: Faites une autre copie du fichier sur un appareil externe et conservez-le dans un endroit très sûr. + submit: Envoyer + submit_legend: Après avoir suivi toutes les étapes expliquées ci-dessus, complétez le processus d'envoi de la clé d'identification publique au serveur. + submit_title: Soumettre la clé d'identification publique + title: Clés d'identification du garant + upload: Téléchargez vos clés d'identification + upload_error: + invalid_format: Le fichier téléchargé ne contient pas de clé d'identification. + invalid_key: Impossible de charger les clés d'identification à partir du fichier téléchargé. + invalid_public_key: Les clés d'identification dans le fichier téléchargé ne correspondent pas à la clé d'identification publique stockée. + upload_legend: Le serveur dispose de vos clés d'identification publiques, mais pas votre navigateur. Vous devez importer le fichier contenant vos clés d'identification sur votre ordinateur à partir de la sauvegarde que vous avez créée après les avoir générées. + not_supported_browser_description: Il semblerait que votre navigateur web ne peut pas être utilisé pour agir en tant qu'administrateur. Assurez-vous d'utiliser la dernière version de votre navigateur ou essayez d'utiliser l'un des navigateurs les plus populaires afin de réaliser vos tâches. + not_supported_browser_title: Mettre à niveau le navigateur pour agir en tant que garant + safari_warning_description: Il semblerait que vous utilisez Safari, qui n'est pas pris en charge pour agir en tant que fiduciaire ou pour chiffrer un vote (cela est dû aux restrictions de mémoire qu'Apple y impose). Cela pourrait être résolu à l'avenir par un changement de politique par Apple ou par l'optimisation future des élections Decidim. S'il vous plaît, utilisez un autre navigateur entre-temps. + safari_warning_title: Navigateur Safari détecté + trustee_role_description: + with_keys: Vous avez été assigné à agir en tant que garant lors de certaines élections mises en place sur cette plateforme. + without_keys: Vous avez été assigné à agir en tant que garant. Veuillez générer et télécharger vos clés d’identification. + update: + success: Votre clé d'identification publique a bien été stockée. + votes: + ballot_decision: + audit: (Auditer le bulletin de vote) + back: Recommencer le processus de vote + ballot_hash: 'L''identifiant de votre bulletin est:' + cast: Remplir le bulletin pour terminer votre vote + description_html: Ici, vous avez les options de valider votre bulletin pour qu'il soit correctement compté ou, vous pouvez vérifier que votre bulletin a été correctement chiffré. Si vous voulez auditer le vote, veuillez lire les instructions sur comment procéder. + header: 'Le bulletin est chiffré: validez-le ou auditez-le' + casting: + header: Vote en cours... + text: Votre bulletin a bien été pris en compte. + confirm: + answer_number: répondre à %{number} + confirm: Valider + edit: éditer + header: Confirmer votre vote + intro: Voici un résumé du vote que vous êtes sur le point de prononcer.
    Veuillez confirmer votre vote ou modifier vos réponses. + nota_option: Vide + confirmed: + back: Retour aux élections + experience: Comment a été votre expérience? + feedback: Donnez-nous votre avis + header: Vote confirmé + lead: Votre vote a été comptabilisé ! + text: 'Vous pouvez vérifier que votre vote a bien été ajouté à la zone des urnes avec l''identifiant suivant : %{e_vote_poll_id}' + verify_link: Pour vérifier que votre vote a bien été pris en compte, copiez l'identifiant et collez-le sur la page de vérification de vote + create: + error: Un problème s'est produit lors de la validation du vote. Veuillez réessayer. + encrypting: + header: Chiffrement du vote... + text: Votre bulletin est en cours de chiffrement afin de garantir le secret de votre vote. + failed: + header: Le vote a échoué + lead: Votre vote n'a pas été exprimé! + text: Une erreur est survenue, veuillez réessayer. + try_again: Réessayez + header: + ballot_decision: Validez ou auditez votre vote + confirm: Confirmer votre vote + election: Élection + register: S'inscrire + vote_for: Voter pour %{title} + messages: + invalid_token: Votre session dans l'isoloir n'est pas valide. Essayez de voter à nouveau. + not_allowed: Vous n'êtes pas autorisé à voter sur cette élection pour le moment. + modal: + close: Fermer + proposal_header: 'Propositions:' + new: + answer_choices: Vous pouvez sélectionner jusqu'à %{choices} réponses + more_information: Plus d'informations + nota_option: Vide / Aucune des options ci-dessus + preview_alert: Ceci est un aperçu de la cabine de vote. + question_steps: Question %{current_step} sur %{total_steps} + selections: "%{selected} sur %{max_selections}
    sélections" + onboarding_modal: + create_account: Créer un compte + description: Voulez-vous créer un compte sur la plateforme ? Vous pourrez participer aux concertations et prendre une part active dans l'organisation. + no_account: Non, merci. + title: Nouveau sur la plateforme ? + update: + error: Une erreur s'est produite lors de la mise à jour du statut du vote. Tentez de voter à nouveau. + verify: + content: + heading: Vérifiez votre vote + info: Ce vérificateur s'assure que votre vote, identifié avec une chaîne de texte chiffrée, a été diffusé correctement et se trouve bien dans l'urne. + error: + header: Vote introuvable ! + info: Le code de vote n'a pas été trouvé dans %{link} l'urne, essayez à nouveau. + form: + back: Retour à la plateforme + submit: Vérifier + vote_identifier: 'Code d''identification :' + vote_identifier_help: C'est l'identifiant qui vous a été donné après que vous avez voté (pas le code pour entrer dans l'isoloir). + header: + title: Vérifiez votre vote + success: + header: Vote trouvé ! + info: Votre vote chiffré se trouve bien dans l'urne %{link} . + voting_step: + back: Précédent + continue: Suivant + warnings: + empty_filters: Il n'y a pas d'élections correspondant à ces critères. + no_elections: Il n'y a aucune élection programmée. + no_scheduled_elections: À l'heure actuelle, il n'y a pas d'élections programmées, mais vous trouverez ici toutes les élections passées. + events: + elections: + election_published: + email_intro: 'L''élection %{resource_title} est maintenant active pour %{participatory_space_title}. Vous pouvez la voir sur cette page :' + email_outro: Vous avez reçu cette notification parce que vous suivez %{participatory_space_title}. Vous pouvez arrêter de recevoir des notifications en suivant le lien précédent. + email_subject: L'élection %{resource_title} est maintenant active pour %{participatory_space_title}. + notification_title: L'élection de %{resource_title} est maintenant active pour %{participatory_space_title}. + trustees: + new_election: + email_intro: Vous avez été ajouté en tant que garant pour l'élection %{resource_title}. + email_outro: Vous avez reçu cette notification parce que vous avez été ajouté(e) comme garant de l'élection "%{resource_title}". Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: Vous avez été ajouté en tant que garant pour l'élection %{resource_title}. + notification_title: Vous avez été sélectionné en tant que garant de l'élection %{resource_title}. Veuillez entamer la cérémonie des clés pour mettre en place l'élection. + new_trustee: + email_intro: Un administrateur vous a ajouté en tant que garant de %{resource_name}. Vous devriez créer votre clé publique dans votre espace de garant + email_outro: Vous avez reçu cette notification parce que vous avez été ajouté(s) en tant que garant pour %{resource_name}. Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: Vous avez été ajouté en tant que garant pour l'élection %{resource_name}. + notification_title: Vous avez été ajouté en tant que garant de %{resource_name} pour des élections qui auront lieu sur cette plateform.
    Pour l'instant, veuillez générer vos clés d'identification. + start_tally: + email_intro: La période de vote pour l'élection %{resource_title} est terminée. Maintenant, veuillez effectuer le décompte de l'élection pour publier les résultats finaux. + email_outro: Vous avez reçu cette notification parce que vous êtes un garant de l'élection %{resource_title}. + email_subject: Le processus de décompte pour l'élection %{resource_title} a commencé. + notification_title: La période de vote pour l'élection %{resource_title} est terminée. Maintenant, veuillez effectuer le décompte de l'élection pour publier les résultats finaux. + votes: + accepted_votes: + email_intro: 'Votre vote a été accepté ! En utilisant votre jeton de vote : %{encrypted_vote_hash}, vous pouvez vérifier votre vote ici.' + email_outro: "Vous avez reçu cette notification parce que vous avez voté pour l'élection %{resource_name}.\nSi vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”." + email_subject: Votre vote pour %{resource_name} a été accepté. + notification_title: 'Votre vote a été accepté. Vérifiez votre vote ici en utilisant votre jeton de vote : %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Vous avez été assigné en tant que %{role} du Bureau de vote %{polling_station_name} sur %{resource_title}. Vous pouvez gérer le Bureau de vote à partir de la zones des responsable du scrutin. + email_outro: Vous avez reçu cette notification parce que vous avez été assigné au rôle de %{role} sur %{polling_station_name}. Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: Vous êtes %{role} du Bureau de vote %{polling_station_name}. + notification_title: Vous êtes %{role} du Bureau de vote %{polling_station_name} sur le vote %{resource_title}. + send_access_code: + instruction: 'Voici le code d''accès que vous avez demandé : %{access_code}, avec lequel vous pourrez participer à %{voting}.' + subject: Votre code d'accès pour participer à %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Une élection est un espace qui vous permet de poser une question claire à toutes les personnes formant une organisation, d'appeler à participer à l'élection, à susciter et organiser le débat pour ou contre une réponse. Lorsque la date de l'élection arrive, vous pouvez voter et publier les résultats des votes.

    Exemples : Les élections peuvent porter sur presque n'importe quel aspect qui affecte une organisation : changer le nom, le logo en offrant plusieurs alternatives, décider ou non de d'intégrer une organisation plus grande, valider ou rejeter un nouveau plan stratégique ou le résultat d'un groupe de travail, choisir la limitation à 1,2 ou 3 mandatures.

    \n" + page: "

    Une élection est un espace qui vous permet de poser une question claire à toutes les personnes formant une organisation, d'appeler à participer à l'élection, à susciter et organiser le débat pour ou contre une réponse. Lorsque la date de l'élection arrive, vous pouvez voter et publier les résultats des votes.

    Exemples : Les élections peuvent porter sur presque n'importe quel aspect qui affecte une organisation : changer le nom, le logo en offrant plusieurs alternatives, décider ou non de d'intégrer une organisation plus grande, valider ou rejeter un nouveau plan stratégique ou le résultat d'un groupe de travail, choisir la limitation à 1,2 ou 3 mandatures.

    \n" + title: Que sont les élections ? + menu: + votings: Élections + participatory_spaces: + related_elections: + see_all: Voir toutes les élections + statistics: + elections_count: Élections + votings_count: Votes + votings: + admin: + ballot_styles: + create: + error: Il y a eu un problème lors de la création de ce style de bulletin. + success: Style de bulletin créé avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de ce style de bulletin. + success: Style de bulletin supprimé avec succès. + edit: + title: Modifier le style de bulletin + update: Mettre à jour + form: + code_help: 'Astuce : le code est le lien entre l''élection et un bulletin de vote. Lors du téléchargement des données de recensement, chaque entrée se verra assignée un style de vote en faisant la correspondance avec le code.' + election: Élection + questions: Questions pour ce style de bulletin + questions_help: 'Astuce : sélectionnez les questions des fonctionalités Élections à présenter aux électeurs affectés à ce type de vote.' + index: + actions: + confirm_destroy: Êtes-vous sûr·e ? + destroy: Supprimer + edit: Modifier + new: Nouveau style de bulletin + title: Actions + associated_census_data: Entrées de recensement associées + explanation_callout: Un style de bulletin spécifie quelles questions seront présentées aux électeurs dans l'isoloir. Dans un style de bulletin, vous pouvez choisir quelles questions de la fonctionnalité Élection de ce vote appartiennent à un bulletin de vote. Le code de style de bulletin est utilisé pour faire correspondre un électeur du recensement avec le bulletin de vote qui lui sera présenté dans l'isoloir. Ne créez pas de style de vote si vous voulez présenter toutes les questions systématiquement. + title: Styles de bulletin + new: + create: Créer + title: Créer un style de bulletin + update: + invalid: Un problème est survenu lors de la mise à jour de ce style de scrutin. + success: Mise à jour réussie du style de scrutin. + content_blocks: + attachments_and_folders: + name: Pièces jointes et dossiers du vote + header: + name: En-tête de vote + highlighted_votings: + max_results: Nombre maximum d'éléments à afficher + html_block_1: + name: Bloc HTML 1 du Vote + html_block_2: + name: Bloc HTML 2 du Vote + html_block_3: + name: Bloc HTML 3 du Vote + main_data: + name: Titre et description + metrics: + name: Métriques de vote + polling_stations: + name: Bureaux de vote + related_elections: + name: Élections + stats: + name: Statistiques du vote + timeline: + name: Calendrier du vote + index: + published: Publié + unpublished: Non publié + menu: + votings: Élections + votings_submenu: + attachment_collections: Dossiers + attachment_files: Fichiers + attachments: Pièces jointes + ballot_styles: Styles de bulletin + census: Recensement + components: Fonctionnalités + info: À propos de ce vote + landing_page: Page d’accueil + monitoring_committee: Comité de suivi + monitoring_committee_election_results: Valider les résultats + monitoring_committee_members: Membres + monitoring_committee_polling_station_closures: Valider les certificats + monitoring_committee_verify_elections: Vérifier les élections + polling_officers: Responsables du scrutin + polling_stations: Bureaux de vote + see_voting: Voir le vote + models: + ballot_style: + fields: + code: Code + monitoring_committee_member: + fields: + email: Email + name: Nom + polling_officer: + fields: + email: Email + name: Nom + polling_station: Bureau de vote (rôle) + polling_station: + fields: + address: Adresse + polling_station_managers: Directeurs + polling_station_president: Président + title: Titre + voting: + fields: + created_at: Créée le + published: Publié + title: Titre + monitoring_committee_election_results: + actions: + title: Actions + view: Afficher + index: + title: Choisissez une élection pour laquelle vous souhaitez voir les résultats + results: + bulletin_board: Panneau d'affichage + election_totals: Total des élections + polling_stations: Bureaux de vote + result_types: + blank_answers: Réponses vides + blank_ballots: Votes blancs + null_ballots: Votes nuls + total_ballots: Total des votes + valid_ballots: Votes valides + selected: Sélectionné + title: Résultats pour l'élection %{election_title} + totals: Totaux + show: + change_election: Changer l'élection + publish_results: Publier les résultats + publishing: Publication en cours ... + update: + invalid: Un problème est survenu lors de la publication des résultats. + rejected: La publication des résultats a été rejetée par le Panneau d'affichage. Réessayez ou contactez l'administrateur système. + success: Les résultats ont été publiés avec succès. + monitoring_committee_members: + create: + invalid: Un problème est survenu lors de la création de ce membre du comité de suivi. + success: Membre du comité de suivi créé avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de ce membre de comité de suivi. + success: Membre de comité de suivi supprimé avec succès. + form: + existing_user: Utilisateur existant + non_user: Inviter un nouveau participant + select_user: Recherche par nom, email ou pseudo + user_type: Type de participant + index: + title: Comité de suivi + new: + create: Créer + title: Créer un membre du comité de suivi + monitoring_committee_polling_station_closures: + actions: + title: Actions + validate: Valider + view: Afficher + closures: + change_election: Changer l'élection + signed: Signé? + title: Bureaux de vote pour l'élection %{election_title} + validated: Validé? + edit: + change_polling_station: Retourner aux bureaux de vote + monitoring_committee_notes: Remarques + monitoring_committee_notes_placeholder: Signaler tout incident ici + title: Résultats pour l'élection %{election_title} dans le bureau de vote %{polling_station_title} + elections: + title: Choisissez une élection que vous souhaitez valider + show: + change_polling_station: Retour aux bureaux de vote + monitoring_committee_notes: Remarques du comité de suivi + validate: + error: Un problème est survenu lors de la validation de la fermeture. + success: La fermeture a été validée correctement. + monitoring_committee_verify_elections: + index: + download: Télécharger + how_to_checksum: 'Pour vous assurer que le fichier que vous avez téléchargé n''a pas été corrompu ou altéré pendant le processus de téléchargement, exécutez la commande suivante dans votre console et vérifiez que la sortie correspond à la somme de contrôle indiquée ci-dessus :' + how_to_download: Pour vérifier une élection, téléchargez son fichier vérifiable à partir du tableau ci-dessus. + how_to_run_verifier: 'Une fois le fichier téléchargé et vérifié, vous pouvez exécuter le vérificateur universel. Clonez ce dépôt et, depuis le dossier racine, exécutez la commande suivante :' + how_to_title: Comment vérifier la validité d'une élection + not_available: Pas encore disponible + title: Élections + polling_officers: + create: + invalid: Une erreur est survenue lors de la création de cet accesseur. + success: L'accesseur a été créé avec succès. + destroy: + invalid: Une erreur est survenue lors de la suppression de cet accesseur. + success: Accesseur supprimé avec succès. + form: + existing_user: Participant existant + non_user: Inviter un nouveau participant + select_user: Recherche par nom, email ou pseudo + user_type: Type du participant + index: + role_manager: directeur + role_president: président + title: Responsables du scrutin + new: + create: Créer + title: Créer un responsable du scrutin + polling_officers_picker: + choose_polling_officers: Choisir les responsables du scrutin + no_polling_officers: Aucun responsable du scrutin ne correspond à vos critères de recherche ou il n'y a pas de responsable du scrutin. + polling_stations: + create: + invalid: Une erreur est survenue lors de la création de ce bureau de vote. + success: Le bureau de vote a été créée avec succès. + destroy: + invalid: Une erreur est survenue lors de la suppression de ce bureau de vote. + success: Le bureau de vote a bien été supprimé. + edit: + title: Modifier le bureau de vote + update: Mettre à jour le bureau de vote + form: + address_help: 'Adresse: utilisée par l''outil de géocadage pour trouver l''emplacement' + location_help: 'Lieu : message adressé aux électeurs informant du lieu exact du bureau de vote' + location_hints_help: 'Indices de localisation : informations complémentaires. Par exemple : l''étage du bâtiment où se trouve le bureau de vote.' + polling_station_managers_help: 'Directeur du scrutin : les personnes qui seront gestionnaires des bureaux de vote. Assurez-vous que les responsables du scrutin ont déjà été créés dans les bureaux de vote et qu''ils ne sont pas déjà affectés à un autre bureau de vote' + polling_station_president_help: "Président du bureau de vote : Agent qui agit en tant que président du bureau de vote. \nAssurez-vous que l'agent soit créé dans \"Bureau de vote\" et qu'il ne soit pas déjà assigné à un autre bureau de vote" + select_president: Sélectionnez un responsable de scrutin en tant que président du bureau de vote + index: + title: Bureaux de vote + new: + create: Créer + title: Créer un bureau de vote + update: + invalid: Une erreur est survenue lors de la mise à jour de ce bureau de vote. + success: Le bureau de vote a bien été mis à jour. + titles: + votings: Élections + votings: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + new_voting: Nouvel espace de vote + create: + invalid: Une erreur s'est produite lors de la création de cette élection. + success: Élection créée avec succès. + edit: + add_election_component: Vous n'avez aucune élection configurée pour ce vote. Veuillez en créer une dans la section Fonctionnalités. + assign_missing_officers: Il reste des bureaux de votes sans président et/ou gérant. Veuillez lier ces derniers à partir de la section Bureaux de votes. + update: Mettre à jour + form: + banner_image: Image de la bannière + census_contact_information: Coordonnées du recensement + census_contact_information_help: Cette information de contact est destinée à un participant qui veut signaler des problèmes avec le recensement. Il peut s'agir d'une adresse e-mail, d'un formulaire de contact sur un autre site, d'une enquête pour les visiteurs, etc. + introductory_image: Image d'introduction + promoted: Mis en avant + select_a_voting_type: Veuillez sélectionner un type de vote + show_check_census_help: Si vous voulez afficher le lien "Puis-je voter?" dans le menu des votes publics. + slug: Slug + slug_help_html: 'Les identifiants d''URL sont utilisés pour générer les URL qui pointent vers ce vote. N''accepte que des lettres, des chiffres et des tirets et doit commencer par une lettre. Exemple : %{url}' + title: Titre + voting_type: + hybrid: Hybride + in_person: En présentiel + online: En ligne + voting_type_label: Type de vote + new: + create: Créer + title: Nouvelle élection + update: + invalid: Une erreur s'est produite lors de la mise à jour de ce vote. + success: Vote mis à jour avec succès. + admin_log: + ballot_style: + create: "%{user_name} a créé un style de bulletin avec le code %{ballot_style_code} dans l'espace %{space_name}" + delete: "%{user_name} a supprimé un style de bulletin avec le code %{ballot_style_code} dans l'espace %{space_name}" + update: "%{user_name} a mis à jour un style de bulletin avec le code %{ballot_style_code} dans l'espace %{space_name}" + census: + create: "%{user_name} a créé le recensement pour l'espace %{space_name}" + delete: "%{user_name} a supprimé le recensement pour l'espace %{space_name}" + update: "%{user_name} a mis à jour le recensement pour l'espace %{space_name}" + monitoring_committee_member: + create: "%{user_name} a assigné l'utilisateur %{monitoring_committee_member_user} en tant que membre du comité de suivi dans l'espace %{space_name}" + delete: "%{user_name} a supprimé l'utilisateur %{monitoring_committee_member_user} en tant que membre du comité de suivi dans l'espace %{space_name}" + polling_officer: + create: "%{user_name} a assigné l'utilisateur %{polling_officer_user} en tant que responsable de scrutin dans l'espace %{space_name}" + delete: "%{user_name} a supprimé l'utilisateur %{polling_officer_user} en tant que responsable de scrutin dans l'espace %{space_name}" + polling_station: + create: "%{user_name} a créé le bureau de vote %{resource_name} dans l'espace %{space_name}" + delete: "%{user_name} a supprimé le bureau de vote %{resource_name} dans l'espace %{space_name}" + update: "%{user_name} a mis à jour le bureau de vote %{resource_name} dans l'espace %{space_name}" + voting: + create: "%{user_name} a créé l'élection %{resource_name}" + publish: "%{user_name} a publié l'élection %{resource_name}" + unpublish: "%{user_name} a annulé la publication de l'élection %{resource_name}" + census: + admin: + census: + create: + invalid: Une erreur s'est produite lors de la mise à jour du recensement, veuillez réessayer plus tard. + invalid_csv_header: Les en-têtes CSV sont manquants ou incorrects - veuillez lire attentivement les instructions. + creating_data: + info_message: "Veuillez patienter, %{processed_count} traités sur %{raw_count} lignes à partir du fichier %{file} (cela peut prendre plusieurs minutes)." + delete: + button: Supprimer toutes les données du recensement + confirm: La suppression de tout le recensement ne peut pas être annulée. Êtes-vous sûr de vouloir continuer? + destroy: + error: Une erreur s'est produite lors de la suppression du recensement, veuillez réessayer plus tard. + success: Données d'élections supprimées. + export_access_codes: + button: Exporter les codes d'accès aux votes + callout: Vous pouvez maintenant exporter les codes d'accès. Cela ne peut être fait qu'une seule fois. Une fois que vous avez lancé l'exportation, vous recevrez un e-mail avec les instructions sur %{email} + confirm: Vous ne pouvez exporter les codes d'accès qu'une seule fois. Assurez-vous d'avoir accès au compte de messagerie %{email}. + file_not_exist: Ce fichier n'existe pas. + launch_error: Problème lors du lancement de l'export des codes d'accès. + launch_success: L'export des codes d'accès a été lancée. Vous recevrez sous peu un e-mail à %{email}. + exporting_access_codes: + info_message: "Veuillez patienter, l'export est en cours de préparation, vous le recevrez sous peu à %{email} (cela peut prendre plusieurs minutes)." + freeze: + callout: Le recensement est gelé et ne peut pas être modifié. + help_html: | + Les données du recensement ont été téléchargées, les codes générés et exportés avec succès.
    + Vous êtes maintenant prêt à commencer l'élection.
    + Utilisez le CSV exporté avec les codes individuels pour le distribuer le long de votre recensement en utilisant vos propres moyens ou activez l'onglet "Puis-je voter" pour permettre à quiconque de récupérer ce code en utilisant ses propres données de recensement. + generate_access_codes: + button: Générer des codes d'accès au vote + callout: Vous pouvez maintenant procéder à la génération des codes d'accès. N'oubliez pas qu'après la génération des codes d'accès, vous ne pourrez plus modifier le recensement. + confirm: Si vous continuez, vous ne pourrez plus modifier le recensement. + info_message_all: "Toutes les lignes ont été importées avec succès depuis le fichier %{file} (%{raw_count} sur %{data_count})." + info_message_warn: Veuillez vérifier qu'aucune donnée n'est manquante, car %{data_count} enregistrements ont été créés et le fichier téléchargé %{file} a %{raw_count} lignes. + launch_error: Problème lors du lancement de la génération des codes d'accès + launch_success: Génération de codes lancée. + start_over: Veuillez supprimer le recensement actuel et recommencer avec un fichier CSV correct avec des lignes valides. + generating_access_codes: + info_message: "Veuillez patienter, les codes d'accès au vote sont en cours de génération (cela peut prendre plusieurs minutes)..." + new: + file_help: + explanation: 'Instructions concernant le fichier :' + message_1: Seuls les fichiers CSV (.csv) sont autorisés. + message_2: Le séparateur de colonnes doit être un point-virgule (";"). + has_ballot_styles_message: Vous avez mis en place des styles de bulletin. Assurez-vous que le champ "%{ballot_style_code_header}" dans le CSV correspond au code du style de bulletin désiré. + info_message: "Il n'y a pas encore de recensement. Veuillez utiliser le formulaire ci-dessous pour le créer en important un fichier CSV." + missing_ballot_styles_message: 'Il n''y a pas encore de style de bulletin pour ce vote. Si vous souhaitez avoir des questions conditionnelles (ex : présenter à l’électeur des questions différentes en fonction de son quartier/ville de résidence), vous devez configurer les styles de bulletin avant d''importer le recensement. Si vous voulez présenter les mêmes questions à tous les électeurs, vous pouvez passer à la procédure d''import du recensement.' + submit: Valider le fichier csv + title: Créer le recensement + show: + heading: Recensement de l'espace de vote + upload_info: + csv_example_with_ballot_style: 'Un exemple du fichier avec styles de bulletin :' + csv_example_without_ballot_style: 'Un exemple du fichier sans styles de bulletin :' + csv_header_after: Ne pas inclure le dernier champ ("%{ballot_style_code_header}") si vous n'avez pas besoin de styles de bulletin/questions conditionnelles + csv_header_before: 'Le fichier de recensement doit être un fichier CSV avec l''en-tête suivante :' + document_types: + identification_number: Numéro d'identification + passport: Passeport + export_mailer: + access_codes_export: + click_button: 'Cliquez sur le lien suivant pour télécharger les données des codes d''accès.
    Le fichier sera disponible jusqu''au %{date}.
    Vous aurez besoin de 7-Zip (pour Windows), Keka (pour MacOS) ou PeaZip (pour Linux) pour l''ouvrir. Mot de passe : %{password}' + download: Télécharger + subject: L'export des codes d'accès au vote pour %{voting_title} est disponible + vote_flow: + already_voted_in_person: Ce participant a déjà voté en personne et n'a plus le droit de voter. + datum_not_found: Les données fournies ne correspondent à aucun électeur. + content_blocks: + highlighted_votings: + name: Élections remarquables + landing_page: + polling_stations: + heading: Bureaux de vote + no_polling_stations: Il n'y a pas encore de bureau de vote. + monitoring_committee_members: + actions: + confirm_destroy: Êtes-vous sûr? + destroy: Supprimer + new: Nouveau membre + title: Actions + pages: + home: + highlighted_votings: + active_spaces: Votations en cours + see_all_spaces: Voir toutes les votations + polling_officer_zone: + closures: + back_to_polling_stations: Retourner aux bureaux de vote + certify: + add_photos: Ajouter des photos + edit_photos: Modifier les photos + error: Une erreur est survenue lors de la mise en place du certificat, veuillez réessayer. + heading: Recomptage des votes - Télécharger un certificat + info_text: Veuillez télécharger une photo du certificat de fermeture électorale. + submit: Télécharger le certificat + success: Le certificat à été téléchargé avec succès. + upload_photos: Télécharger une photo du certificat de clôture électoral + completed: + sub_heading: Ce recomptage a été certifié et ne peut plus être modifié. + create: + error: Une erreur s'est produite lors de la création de la fermeture, veuillez réessayer plus tard. + success: Fermeture créée avec succès. + destroy: + error: Une erreur s'est produite lors de la suppression de la clôture. + success: Clôture supprimée avec succès. + edit: + confirm_start_over: Cela supprimera le nombre total de votes et les notes jointes. Êtes-vous sûr(e) ? + heading: Recomptage des votes - Comptage des réponses + info_text: Veuillez détailler le nombre total de réponses pour chaque question. Ce nombre doit correspondre au total de réponses fourni lors de l'étape précédente (%{total} réponses totales). + modal_ballots_results_count_error: + blank: Le total attendu des votes blancs est %{expected} mais la somme des questions vides est %{current}. + close_modal: Fermer + info_text: Le nombre total de bulletins ne correspond pas au nombre total d'enveloppes. Veuillez vérifier le nombre total de bulletins. + title: Le total des bulletins ne correspond pas + total: Le total attendu est %{expected} mais la somme des bulletins valides, blancs et nuls est %{current}. + valid: Le total attendu des votes blancs est %{expected} mais la somme des questions vides est %{current}. + save_recount: Enregistrer le comptage + start_over: S'il y a une erreur dans le nombre total, vous pouvez tout supprimer et recommencer. + total_ballots: Total des votes + total_blank_ballots: Nombre total de bulletins blanc + total_null_ballots: Nombre total de bulletins nuls + total_valid_ballots: Nombre total de bulletins valides + new: + election: 'Élection:' + heading: Recomptage des votes + info_text: 'Veuillez introduire le nombre total de bulletins de vote (enveloppes) recomptés dans ce Bureau de vote:' + modal_ballots_count_error: + btn_validate_total: Valider le nombre total de bulletins + info_explanation_text: 'Veuillez examiner le nombre total de bulletins de vote. Si le nombre total est incorrect, vous devez fournir une explication pour le comité de suivi :' + info_text: Le nombre total de bulletins de vote ne correspond pas au nombre de personnes ayant voté dans ce bureau de vote. + message_for_monitoring_committee: Message pour le comité de suivi + review_recount: Revoir le comptage + text_area_placeholder: Veuillez saisir votre message + title: Le total des enregistrements ne correspond pas + total_ballots: 'Nombre total de bulletins :' + total_people: 'Nombre total de personnes :' + polling_station: 'Bureau de vote :' + submit: Vérifier le nombre total + total_ballots_count: Nombre de bulletins + show: + edit_count_votes: Mauvais nombres ? Vous pouvez toujours les modifier. + heading: Recomptage des votes + sub_heading: Le recomptage doit être fermé en téléchargeant un certificat. Une fois cela fait, le recomptage sera scellé et ne pourra plus être modifié. + sign: + cancel: Annuler + check_box: Après vérification, ce certificat est identique au certificat physique de clôture des élections + confirm: Ok, continuer + error: Une erreur est survenue, veuillez réessayer. + heading: Recomptage des voix - Signer la fermeture + info_text: Cette action est irréversible. Si vous continuez, vous ne pourrez plus modifier les informations. + submit: Signer la fermeture + success: Fermeture signée avec succès. + update: + error: Une erreur s'est produite lors de la mise à jour des résultats de fermeture, veuillez réessayer plus tard. + success: Résultats de fermeture mis à jour avec succès. + in_person_votes: + complete_voting: + available_answers: 'Réponses disponibles:' + census_verified: Ce participant n'a pas encore voté en personne. + census_verified_with_online_vote: Ce participant a déjà voté en ligne. S'il vote en personne, les votes précédents seront invalidés et ce sera le vote définitif. + complete_voting: Vote terminé + identify_another: Identifier un autre participant + questions_title: 'Ils/elles ont le droit de voter sur les questions suivantes :' + questions_title_voted: 'Le participant a déjà voté en ligne et a le droit de voter dans les questions suivantes :' + voted: Le participant a voté + create: + error: Le vote n'a pas été enregistré. Veuillez réessayer. + in_person_form: + census_not_present: Ce participant n'est pas répertorié dans le recensement. + census_not_present_description: Elle doit se rendre au bureau des plaintes du recensement ou contacter le support. + date_of_birth: Date de naissance + day: Jour + day_placeholder: JJ + document_number: Numéro de document + document_number_placeholder: Numéro de carte d'identité + month: Mois + month_placeholder: MM + select: Sélectionnez le type de document + title: 'Sélectionnez le type de document et entrez le numéro de document du participant :' + validate_document: Valider le document + year: Année + year_placeholder: AAAA + new: + back: Retourner aux bureaux de vote + title: Identifier et vérifier un participant + show: + back: Retourner aux bureaux de vote + title: En attente de l'enregistrement du vote en personne + update: + error: Une erreur est survenue lors de l'enregistrement du vote. Veuillez réessayer. + success: + accepted: Le vote a été enregistré avec succès. + rejected: Le vote n'a pas été accepté par le Tableau d'Affichage. Veuillez contacter l'administrateur du système. + verify_document: + census_present: Ce participant n'est pas répertorié dans le recensement. + name: Nom + title: 'Vérifiez que les données suivantes sont correctes:' + verify_document: Document requis + menu: + polling_officer_zone: Zone des responsables du scrutin + polling_officers: + index: + polling_officer_role_description: Vous avez été assigné à agir en tant qu’officier du Bureau de vote (Président ou Gestionnaire) sur certaines des élections ayant lieu sur cette plateforme. + polling_station: + address: Adresse + count_votes: Nombre de votes + election: Élection + identify_person: Identifier une personne + name: Nom + no_polling_stations: Vous n'êtes pas encore assigné à un Bureau de vote. + role: Votre rôle + show_closure: Voir la fermeture + title: Bureaux de vote + voting: Élection + polling_officers: + actions: + confirm_destroy: Êtes-vous sûr? + destroy: Supprimer + new: Nouvel accesseur + title: Actions + roles: + manager: Directeur + president: Président + unassigned: Non assigné + polling_station_closure_certificate: + current_certificate: 'Certificat actuel :' + polling_station_closure_recount: + nota_option: Vide / Aucune des options ci-dessus + polling_officer_notes: 'Notes de l''accesseur :' + polling_officer_notes_blank: Il n'y a aucune note + recount_summary: 'Bilan du recomptage:' + signed: Signé + total_ballots: 'Nombre total de bulletins :' + total_blank_ballots: 'Nombre total de bulletins blanc :' + total_null_ballots: 'Nombre total de bulletins nuls :' + total_valid_ballots: 'Nombre total de bulletins valides :' + polling_stations: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + edit: Modifier + new: Nouveau bureau de vote + title: Actions + votings: + access_code_modal: + email: Envoyer par e-mail à %{email} + info: Un code d'accès est nécessaire pour participer. Si vous ne l'avez pas reçu par courrier, un nouveau code peut vous être envoyé. + no_email: Aucun email disponible + no_sms: Aucun numéro de téléphone disponible + sms: Envoyer par SMS à %{sms} + title: Obtenir un code d’accès + check_census: + check_status: Vérifiez le statut + description: Ici, vous avez la possibilité de vérifier vos données de recensement pour savoir si vous avez le droit de participer à ce vote. Vous devriez déjà avoir un code d'accès, mais si vous l'avez perdu, vous pouvez le demander à nouveau, lorsque vos données sont correctes. + error: + info: 'Veuillez réessayer. Si vous pensez que les données du système sont incorrectes, vous pouvez les signaler ici : %{census_contact_information}.' + title: Les données que vous avez saisies ne sont pas dans le recensement pour ce vote + form_title: 'Remplissez le formulaire suivant pour vérifier vos données de recensement:' + invalid: Un problème est survenu lors de la vérification du recensement. + success: + access_link: par email. + access_link_with_sms: par SMS ou e-mail. + info: Vous devriez avoir reçu votre code d'accès par courrier postal. Si vous ne l'avez pas reçu, vous pouvez le demander ici + title: Vos données de recensement sont correctes! + title: Puis-je voter ? + check_fields: + date_of_birth: Date de naissance + day: Jour + day_placeholder: JJ + document_number: Numéro de document + document_number_placeholder: Numéro de carte d'identité + document_type: Type de document + month: Mois + month_placeholder: MM + postal_code: Code postal + postal_code_placeholder: Numéro de code postal + select: Sélectionnez le type de document + year: Année + year_placeholder: AAAA + count: + title: + one: "%{count} élection" + other: "%{count} élections" + elections_log: + description: Le journal des élections vous montrera toutes les informations pertinentes sur chaque vote. Par exemple, le statut de la cérémonie de génération des clées ou du dépouillement ou si les résultats sont déjà publiés. Cliquez sur l'élection à propos de laquelle vous voulez des informations. + title: Journal de l'élection + filters: + active: Actifs + all: Tous + date: Date + finished: Terminées + search: Rechercher + upcoming: À venir + index: + no_votings: Aucun vote ne correspond à votre recherche. + only_finished: À l'heure actuelle, il n'y a pas de votes planifiés, mais vous trouverez ici une liste de tous les votes passées. + title: Votations + login: + access_code: Code d'accès + access_code_placeholder: Code d'accès + ask_for_a_new_one: Demander un nouveau. + dont_have_access_code: Vous n'avez pas de code d'accès? + form_title: 'Remplissez le formulaire suivant pour accéder au vote :' + start_voting: Commencer à voter + title: Je m'identifie avec mes données de recensement de vote + no_census_contact_information: Il n'y a pas encore d'informations de contact. + orders: + label: 'Trier les votations par:' + random: Aléatoire + recent: Les plus récentes + send_access_code: + invalid: Un problème est survenu lors de l'envoi du code d'accès. + success: Votre code d'accès a été envoyé avec succès. + show: + title: À propos de ce vote + votings_m: + badge_name: + finished: Passées + ongoing: En cours + upcoming: À venir + unspecified: Non précisées + voting_type: + hybrid: Hybride + in_person: En présentiel + online: En ligne + layouts: + decidim: + voting_navigation: + check_census: Puis-je voter ? + election_log: Journal de l'élection + votings: + index: + promoted_votings: Votations remarquables + promoted_voting: + vote: Voter diff --git a/decidim-elections/config/locales/fr-LU.yml b/decidim-elections/config/locales/fr-LU.yml new file mode 100644 index 00000000..e73fcdab --- /dev/null +++ b/decidim-elections/config/locales/fr-LU.yml @@ -0,0 +1,890 @@ +fr-LU: + activemodel: + attributes: + answer: + description: Description + image: Image + proposals: Propositions associées + title: Titre + election: + description: Description + end_time: Le vote se termine à + start_time: Le vote commence à + title: Titre + question: + description: Description + max_selections: Nombre maximum de sélections + min_selections: Aucune des options ci-dessus + title: Titre + voting: + end_time: Le vote se termine + start_time: Le vote commence le + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Doit être téléchargé de nouveau + election: + attributes: + attachment: + needs_to_be_reattached: Doit être téléchargé de nouveau + trustee: + attributes: + name: + cant_be_changed: ne peut pas être modifié + public_key: + cant_be_changed: ne peut être modifiée + activerecord: + models: + decidim/elections/answer: + one: Réponse + other: Réponses + decidim/elections/election: + one: Élection + other: Élections + decidim/elections/question: + one: Question + other: Questions + decidim/votings/census/dataset: + one: Jeu de données + other: Jeux de données + decidim/votings/voting: + one: Vote + other: Votes + decidim: + components: + elections: + actions: + vote: Voter + name: Élections + settings: + global: + announcement: Annonce + step: + announcement: Annonce + elections: + actions: + confirm_destroy: Êtes-vous certain ? + destroy: Détruire + edit: Modifier + feedback: Feedbacks des votants + import: Importer des propositions dans les réponses + manage_answers: Gérer les réponses + manage_questions: Gérer les questions + manage_steps: Gérer les étapes + new: Nouveau %{name} + preview: Aperçu + publish: Publier + title: Actions + unpublish: Dépublier + admin: + answers: + create: + invalid: Il y a eu un problème lors de la création de cette réponse + success: Réponse créée avec succès + destroy: + invalid: Un problème est survenu lors de la suppression de cette réponse + success: Réponse supprimée avec succès + edit: + title: Modifier la réponse + update: Mettre à jour la réponse + index: + invalid_max_selections: Vous avez besoin de %{missing_answers} réponse(s) de plus pour correspondre aux sélections maximales + title: Réponses + new: + create: Créer une réponse + title: Nouvelle réponse + not_selected: Non sélectionnée + select: + disable: Désélectionner la réponse + enable: Marquer la réponse comme sélectionnée + invalid: Un problème est survenu lors de la sélection de cette réponse + success: Réponse sélectionnée avec succès + selected: Sélectionnée + unselect: + invalid: Une erreur est survenue lors de la désélection de cette réponse + success: Réponse désélectionnée avec succès + update: + invalid: Un problème est survenu lors de la mise à jour de cette réponse + success: Réponse mise à jour avec succès + elections: + create: + invalid: Il y a eu un problème lors de la création de cette élection + success: Élection créée avec succès + destroy: + invalid: Il y a eu un problème lors de la suppression de cette élection + success: Élection supprimée avec succès + edit: + title: Modifier l'élection + update: Mettre à jour l'élection + index: + no_bulletin_board: Il n'y a pas de serveur Bulletin Board configuré, ce qui est nécessaire pour utiliser ce module. Cette tâche doit être effectuée par l'administrateur système. + title: Élections + new: + create: Créer une élection + title: Nouvelle élection + publish: + success: L'élection a été publiée avec succès. + unpublish: + success: L'élection a été dépubliée avec succès. + update: + invalid: Il y a eu un problème lors de la mise à jour de cette élection + success: Élection mise à jour avec succès + exports: + elections: Élections + feedback_form_answers: Feedbacks des répondants + menu: + trustees: Garants + models: + answer: + name: Réponse + election: + name: Élection + question: + name: Question + trustee: + name: Garant + proposals_imports: + create: + invalid: Un problème est survenu lors de l'importation des propositions en réponses + success: "%{number} propositions importées avec succès dans les réponses" + new: + create: Importer des propositions dans les réponses + no_components: Il n'y a pas d'autres fonctionnalités Propositions dans cet espace participatif pour importer des propositions. + select_component: Veuillez sélectionner une fonctionnalité + title: Importer des propositions + questions: + create: + invalid: Il y a eu un problème lors de la création de cette question + success: Question créée avec succès + destroy: + invalid: Un problème est survenu lors de la suppression de cette question + success: Question supprimée avec succès + edit: + title: Modifier la question + update: Mettre à jour la question + index: + title: Questions + new: + create: Créer une question + title: Nouvelle question + update: + invalid: Une erreur s'est produite lors de la mise à jour de cette question + success: Question mise à jour avec succès + steps: + create_election: + errors: + max_selections: Les questions n'ont pas de valeur correcte pour le nombre de réponses + minimum_answers: Les questions doivent avoir au moins deux réponses. + minimum_questions: L'élection doit avoir au moins une question. + published: L'élection n'est pas publiée. + time_before: L'heure de début se situemoins de 3 heures avant le commencement de l'élection. + trustees_number: L'espace participatif doit avoir au moins %{number} garants avec une clé publique. + invalid: Une erreur est survenue lors de la création de l'élection + no_trustees: Il n'y a pas de garants configurés pour cet espace participatif + not_used_trustee: "(non utilisé)" + public_key: + 'false': n'a pas de clé publique + 'true': a une clé publique + requirements: + max_selections: Toutes les questions ont une valeur correcte pour le maximum de réponses. + minimum_answers: Chaque question a au moins 2 réponses. + minimum_questions: L'élection a au moins 1 question. + published: L'élection est publiée. + time_before: La configuration est réalisée au moins %{hours} heures avant le début de l'élection. + trustees_number: L'espace participatif doit avoir au moins %{number} garants avec une clé publique. + submit: Configurer l'élection + success: Élection envoyée avec succès au Tableau de Bord + title: Configurer l'élection + trustees: Garants de l'élection + created: + invalid: Il y a eu un problème lors du démarrage de la cérémonie des clés + submit: Démarrer la cérémonie des clés + title: Élection créée + trustees: Garants + key_ceremony: + title: Cérémonie des clés + trustees: Garants + key_ceremony_ended: + errors: + time_before: L'élection est prête à commencer. Vous devez attendre %{hours} heures avant le lancement (%{start_time}) pour démarrer la période de vote. + invalid: Il y a eu un problème au démarrage de la période de vote + requirements: + time_before: L'élection va bientôt commencer. Vous pouvez démarrer la période de vote manuellement, ou elle sera démarrée automatiquement avant l'heure de départ, à %{start_time}. + submit: Commencer la période de vote + title: Prêt à commencer + processing: Traitement en cours... + results_published: + answer: Répondre + not_selected: Non sélectionné + question: Question + result: Résultat + selected: Sélectionné + submit: Envoyer + title: Résultats publiés + tally: + trustees: Garants + tally_ended: + answer: Répondre + not_selected: Non sélectionné + question: Question + result: Réalisation + selected: Sélectionné + submit: Publier les résultats + title: Résultat du calcul + vote: + errors: + time_after: L'élection est toujours en cours. Vous devez attendre (%{end_time}) pour terminer la période de vote. + invalid: Un problème est survenu lors de la clôture de la période de vote + requirements: + time_after: L'élection est terminée. Vous pouvez terminer la période de vote manuellement, ou elle se terminera automatiquement dans quelques minutes. + submit: Fin de la période de vote + success: La demande de fin de la période de vote a été envoyée avec succès au Panneau d'Affichage + title: Période de vote + vote_ended: + text: Le vote est terminé. Vous pouvez commencer le comptage. + title: Période de vote terminée + vote_stats: + no_vote_statistics_yet: Pas encore de statistiques sur le vote + title: Statistiques du vote + voters: Votants + votes: Votes + trustees_participatory_spaces: + actions: + disable: Désactiver + enable: Autoriser + create: + exists: Ce garant existe déjà dans cet espace participatif + invalid: Une erreur s'est produite lors de la création du garant + success: Le garant a bien été créé + delete: + invalid: Il y a eu un problème lors de la suppression de ce garant + success: Le garant a bien été supprimé + form: + select_user: Sélectionner un utilisateur + index: + title: Garants + new: + create: Créer un garant + title: Nouveau garant + update: + invalid: Une erreur s'est produite lors de la mise à jour du garant %{trustee} + success: Le garant %{trustee} a été mis à jour avec succès + admin_log: + election: + publish: "%{user_name} a publié l'élection %{resource_name}" + publish_results: "%{user_name} a publié les résultats de l'élection %{resource_name} sur le Tableau d'affichage" + unpublish: "%{user_name} a dépublié l'élection %{resource_name}" + election_m: + badge_name: + finished: Terminées + ongoing: Active + upcoming: À venir + end_date: Se termine le + footer: + remaining_time: + one: "Il reste %{count} heure %{minutes} minutes pour voter." + other: "Il reste %{count} heures %{minutes} minutes pour voter." + view: Voir + vote: Voter + label: + date: Dates + questions: '%{count} questions' + start_date: Début + unspecified: Non spécifié + elections: + count: + elections_count: + one: "%{count} élection" + other: "%{count} élections" + election_log: + chained_hash: La chaîne de hash de ce message + complete: Terminée + creation_description: + complete: L'élection a bien été créée et ajoutée sur le Tableau d'affichage. + not_created: L'élection n'est pas encore créée. + creation_title: Élection créée + description: Ceci est le journal des élections, où vous pouvez vérifier le statut de chaque étape, par exemple lorsque l'élection a été créée, si le processus de dépouillement est terminé, et la date de clôture de l'élection. + download: Télécharger + key_ceremony_description: + complete: La cérémonie de génération de clés est terminée. Chaque garant a des clés valides et a téléchargé les clés de sauvegarde nécessaires. + not_started: La génération des clés n'a pas encore commencée. + started: La cérémonie de génération des clés a commencé mais n'est pas encore terminée. + key_ceremony_title: Cérémonie de génération des clés + not_available: Pas encore disponible + not_created: N'est pas créée + not_published: Non publié + not_ready: Pas encore prêt + not_started: Non démarré + published: Publié + results_description: + not_published: Les résultats ne sont pas encore publiés. + published: Les résultats sont publiés. + results_title: Résultats + started: Débutée + tally_description: + finished: Le dépouillement est terminé. + not_started: Le dépouillement n'a pas encore commencé. + started: Le dépouillement a commencé. + tally_title: Dépouillement + title: Journal de l'élection + verifiable_results: + checksum: 'Somme de contrôle SHA256:' + description: + not_ready: Le fichier de vérification électoral et la somme de contrôle SHA256 ne sont pas encore disponibles. Dès que les résultats seront publiés, vous serez en mesure de vérifier cette élection. + ready: 'Ici, vous avez la possibilité de vérifier l''élection. D''abord, vous devez télécharger le fichier et vous assurer qu''il n''a pas été corrompu. Pour ce faire, exécutez la commande suivante et vérifiez que la sortie correspond à la somme de contrôle :' + how_to_verify: 'Une fois le fichier téléchargé et vérifié qu''il est correct, vous pouvez exécuter le vérificateur universel. Clonez ce dépôt et, depuis le dossier racine, exécutez la commande suivante :' + title: Vérifier les résultats de l'élection + verifiable_file: 'Fichier de vérification d''élection:' + verify: Vérifier l'élection + vote_description: + finished: Le vote est terminé. + not_started: Le vote n'a pas encore commencé. + started: Le vote a commencé. + vote_title: Processus de vote + filters: + active: Actif + all: Toutes + finished: Terminées + search: Rechercher + state: Statut + upcoming: À venir + filters_small_view: + close_modal: Fermer la fenêtre de dialogue + filter: Filtrer + filter_by: Filtrer par + unfold: Voir plus + preview: + available_answers: 'Réponses disponibles:' + description: 'Voici les questions que vous trouverez dans le processus de vote:' + title: Questions de l'élection + results: + description: 'Ce sont les résultats du vote, pour chaque question:' + selected: Sélectionné + title: Résultats de l'élection + votes: + one: "%{count} vote" + other: "%{count} votes" + show: + action_button: + change_vote: Changer votre vote + vote: Commencer à voter + vote_again: Voter à nouveau + back: Élections disponibles + callout: + already_voted: Vous avez déjà voté pour cette élection. Vous pouvez modifier votre vote ou le vérifier. + vote_rejected: Il n'a pas été possible de vérifier votre vote. Veuillez recommencer. + election_log: Journal de l'élection + preview: Aperçu + verify: + already_voted: Déjà voté? + verify_here: Vérifiez votre vote ici. + will_verify: Vous pourrez vérifier votre vote une fois l'élection commencée. + voting_period_status: + finished: Le vote a commencé le %{start_time} et s'est terminé le %{end_time} + ongoing: 'Vote actif jusqu''au : %{end_time}' + upcoming: Le vote commence le %{start_time} + feedback: + answer: + invalid: Un problème est survenu lors de l'envoi de votre commentaire. + spam_detected: Une erreur s'est produite lors de la complétion du formulaire. Peut-être êtes-vous allés trop vite, pouvez-vous réessayer? + success: Votre avis a bien été envoyé. + models: + answer: + fields: + proposals: Propositions + selected: Sélectionné + title: Titre + votes: Votes + election: + fields: + end_time: Prend fin à + start_time: Commence à + title: Titre + question: + fields: + answers: Réponses + max_selections: Nombre maximum de sélections + title: Titre + trustees_participatory_space: + fields: + considered: autorisé + email: Email + inactive: inactif + name: Nom + notification: Notification envoyée à + public_key: Clé publique + status: Statut + orders: + label: Trier les élections par + older: Les plus anciens + recent: Les plus récents + trustee_zone: + elections: + backup_modal: + description: Cette élection est en cours de création dans le Tableau d'Affichage. Il est très important que chaque Mandataire qui y participe fasse une sauvegarde de ces clés et les stocke dans un endroit sûr. Ensuite, le processus se poursuit. + download_election_keys: Télécharger les clés + download_icon: Icône indiquant une action de téléchargement + title: Sauvegarder les clés d'élection pour %{election} + key_ceremony_steps: + back: Retour + description: Cette élection est en cours de création dans le Tableau d'Affichage. Pour compléter ce processus, votre participation en tant que Mandataire est nécessaire. + keys: + create_election: Génération des clés + key_ceremony: + joint_election_key: Génération de la clé conjointe + step_1: Publication des clés + list: + status: Statut + task: Tâche + process_warning: Une fois le processus démarré, vous ne devriez pas quitter cette page avant la fin du processus. Cela prendra plusieurs minutes, car tous les Mandataires doivent être connectés pour le compléter. + start: Démarrer + start_icon: Icône indiquant un bouton de démarrage pour démarrer la génération des clés d'élection + status: + completed: Terminé + pending: En attente + processing: Traitement en cours + title: Créer des clés d'élection pour %{election} + restore_modal: + description: Le Tableau d'Affichage a des informations pour vous en temps que Mandataire de cette élection. Pour continuer le processus, commencer par télécharger le fichier de sauvegarde généré pendant la session précédente. + title: Restaurer les clés d'élection pour %{election} + upload_election_keys: Télécharger les clés d'élection + upload_icon: Icône indiquant une action de téléchargement + tally_steps: + back: Retour + description: Les résultats de cette élection sont calculés dans le Bulletin Board et pour compléter ce processus, votre participation en tant que garant est nécessaire. + list: + status: Statut + task: Tâche + process_warning: Une fois le processus démarré, vous ne devriez pas quitter cette page avant la fin du processus. Cela prendra plusieurs minutes, car tous les garants doivent être connectés pour le compléter. + start: Démarrer + start_icon: Icône indiquant un bouton de démarrage pour démarrer la génération des clés d'élection + status: + completed: Terminé + pending: En attente + processing: Traitement en cours + update: + error: Le statut de l'élection n'a pas été mis à jour. + success: 'Le statut de l''élection est : %{status}' + menu: + trustee_zone: Espace de garant + trustees: + show: + elections: + list: + action_required: + 'false': 'Non' + name: Action requise ? + 'true': Effectuer l'action + bb_status: Statut + election: Élection + voting_period: Période de vote + no_elections: Il n'y a pas d'élections dans lesquelles vous êtes garant. + title: Élections + identification_keys: + cancel: Annuler + generate: Générer les clés d'identification + generate_error: Une erreur s'est produite lors de la génération des clés d'identification. + generate_legend: Vous devez générer une paire de clés d'identification pour participer aux élections en tant que garant. + generate_legend_1: Après avoir cliqué sur le bouton, le téléchargement du fichier contenant les clés d'identification générées va commencer. + generate_legend_2: Copiez le fichier téléchargé sur un périphérique USB fiable + generate_legend_3: Assurez-vous que votre ordinateur ne possède pas de copie du fichier (par exemple, vérifiez les dossiers Téléchargements et Bureau). + generate_legend_4: Faites une autre copie du fichier sur un appareil externe et conservez-le dans un endroit très sûr. + submit: Envoyer + submit_legend: Après avoir suivi toutes les étapes expliquées ci-dessus, complétez le processus d'envoi de la clé d'identification publique au serveur Decidim. + submit_title: Soumettre la clé d'identification publique + title: Clés d'identification du garant + upload: Téléchargez vos clés d'identification + upload_error: + invalid_format: Le fichier téléchargé ne contient pas de clé d'identification. + invalid_key: Impossible de charger les clés d'identification à partir du fichier téléchargé. + invalid_public_key: Les clés d'identification dans le fichier téléchargé ne correspondent pas à la clé d'identification publique stockée par Decidim. + upload_legend: Decidim a vos clés d'identification publiques, mais votre navigateur ne l'a toujours pas. Vous devez importer le fichier avec vos clés d'identification sur votre ordinateur à partir de la sauvegarde que vous avez créée après leur génération. + not_supported_browser_description: On dirait que vous utilisez un navigateur Web qui ne peut pas être utilisé pour agir en tant que garant. Assurez-vous d'utiliser la version la plus récente de votre navigateur, ou essayez d'utiliser l'un des navigateurs les plus populaires pour accomplir vos tâches de garant. + not_supported_browser_title: Mettre à niveau le navigateur pour agir en tant que garant + trustee_role_description: Vous avez été assigné à agir en tant que garant lors de certaines des élections mises en place sur cette plateforme. + update: + success: Votre clé d'identification publique a bien été stockée. + votes: + ballot_decision: + audit: "(Auditer le bulletin de vote)" + back: Recommencer le processus de vote + ballot_hash: 'L''identifiant de votre bulletin est:' + cast: Valider votre vote + description: Ici, vous pouvez valider votre vote de sorte qu'il soit correctement compté ou alternativement vous pouvez vérifier que votre bulletin a bien été chiffré. Pour des raisons de sécurité, l’audit de votre bulletin révélera son contenu. Si vous souhaitez valider votre vote, vous devrez donc recommencer le processus de vote. + confirm: + answer: Répondre + answer_number: répondre à %{number} + confirm: Valider + edit: éditer + header: Confirmer votre vote + intro: Voici un résumé du vote que vous êtes sur le point d'envoyer.
    Veuillez confirmer votre vote ou modifier vos réponses. + nota_option: Vide + question: Question %{count} + confirmed: + back: Retour aux élections + experience: Comment a été votre expérience? + feedback: Donnez-nous votre avis + header: Vote confirmé + lead: Votre vote a été comptabilisé ! + text: 'Vous pouvez vérifier que votre vote a bien été ajouté aux urnes avec l''identifiant suivant : %{e_vote_poll_id}' + create: + error: Un problème s'est produit lors de la validation du vote. Veuillez réessayer. + failed: + header: Le vote a échoué + lead: Votre vote n'a pas été exprimé! + text: Une erreur est survenue, veuillez réessayer. + header: + ballot_decision: Validez ou auditez votre vote + confirm: Confirmer votre vote + confirmed: Vote confirmé + messages: + invalid_token: Votre session dans l'isoloir n'est pas valide. Essayez de voter à nouveau. + not_allowed: Vous n'êtes pas autorisé à voter sur cette élection pour le moment. + modal: + close: Fermer + proposal_header: 'Propositions:' + new: + answer_choices: Vous pouvez sélectionner jusqu'à %{choices} réponses + more_information: Plus d'informations + nota_option: Vide / Aucune des options ci-dessus + preview_alert: Ceci est un aperçu de la cabine de vote. + question_steps: Question %{current_step} sur %{total_steps} + selections: "%{selected} sur %{max_selections}
    sélections" + onboarding_modal: + close: Fermer la fenêtre de dialogue + create_account: Créer un compte + description: Voulez-vous créer un compte sur Decidim ? Vous pourrez participer à toutes les concertations et prendre une part active dans l'organisation. + no_account: Non, merci. + title: Nouveau sur Decidim ? + verify: + content: + heading: Vérifiez votre vote + error: + header: Vote introuvable ! + voting_step: + back: Retour + continue: Suivant + warnings: + no_elections_warning: Aucune élection ne correspond à vos critères de recherche ou aucune élection n'est programmée. + no_scheduled_elections_warning: À l'heure actuelle, il n'y a pas d'élections programmées, mais vous trouverez ici toutes les élections passées. + events: + elections: + election_published: + email_intro: 'L''élection %{resource_title} est maintenant active pour %{participatory_space_title}. Vous pouvez la voir sur cette page :' + email_outro: Vous avez reçu cette notification parce que vous suivez %{participatory_space_title}. Vous pouvez arrêter de recevoir des notifications en suivant le lien précédent. + email_subject: L'élection %{resource_title} est maintenant active pour %{participatory_space_title}. + notification_title: L'élection de %{resource_title} est maintenant active pour %{participatory_space_title}. + trustees: + new_election: + email_intro: Vous avez été ajouté en tant que garant pour l'élection %{resource_title}. + email_outro: Vous avez reçu cette notification parce que vous avez été ajouté comme garant de l'élection "%{resource_title}". + email_subject: Vous avez été ajouté en tant que garant pour l'élection %{resource_title}. + notification_title: Vous êtes garant de l'élection %{resource_title}. + new_trustee: + email_intro: Un administrateur vous a ajouté en tant que garant de %{resource_name}. Vous devriez créer votre clé publique dans votre espace de garant + email_outro: Vous avez reçu cette notification parce que vous avez été ajouté comme garant de l'élection "%{resource_name}". + email_subject: Vous avez été ajouté en tant que garant pour l'élection %{resource_name}. + notification_title: Vous êtes garant de %{resource_name}. + help: + participatory_spaces: + votings: + contextual: "

    Une élection est un espace qui vous permet de poser une question claire à toutes les personnes formant une organisation, d'appeler à participer à l'élection, à susciter et organiser le débat pour ou contre une réponse. Lorsque la date de l'élection arrive, vous pouvez voter et publier les résultats des votes.

    Exemples : Les élections peuvent porter sur presque n'importe quel aspect qui affecte une organisation : changer le nom, le logo en offrant plusieurs alternatives, décider ou non de d'intégrer une organisation plus grande, valider ou rejeter un nouveau plan stratégique ou le résultat d'un groupe de travail, choisir la limitation à 1,2 ou 3 mandatures.

    \n" + page: "

    Une élection est un espace qui vous permet de poser une question claire à toutes les personnes formant une organisation, d'appeler à participer à l'élection, à susciter et organiser le débat pour ou contre une réponse. Lorsque la date de l'élection arrive, vous pouvez voter et publier les résultats des votes.

    Exemples : Les élections peuvent porter sur presque n'importe quel aspect qui affecte une organisation : changer le nom, le logo en offrant plusieurs alternatives, décider ou non de d'intégrer une organisation plus grande, valider ou rejeter un nouveau plan stratégique ou le résultat d'un groupe de travail, choisir la limitation à 1,2 ou 3 mandatures.

    \n" + title: Que sont les élections ? + menu: + votings: Élections + votings: + admin: + content_blocks: + highlighted_votings: + max_results: Nombre maximum d'éléments à afficher + index: + not_published: Non publié + published: Publié + menu: + votings: Élections + votings_submenu: + attachment_collections: Dossiers + attachment_files: Fichiers + attachments: Pièces jointes + census: Recensement + info: Information + monitoring_committee_members: Membres + polling_stations: Bureaux de vote + models: + polling_station: + fields: + address: Adresse + title: Titre + name: Bureau de vote + voting: + fields: + created_at: Créée le + promoted: Mise en avant + published: Publié + title: Titre + polling_stations: + create: + invalid: Une erreur est survenue lors de la création de ce bureau de vote + success: Le bureau de vote a été créée avec succès + destroy: + invalid: Une erreur est survenue lors de la suppression de ce bureau de vote + success: Le bureau de vote a bien été supprimé + edit: + title: Modifier le bureau de vote + update: Mettre à jour le bureau de vote + form: + address_help: 'L''adresse entrée est utilisée par le géocodage pour trouver l''emplacement et afficher la réunion sur la carte. Format accepté : [rue] [code postal] [ville]' + location_help: 'Lieu : message adressé aux électeurs informant du lieu exact du bureau de vote' + location_hints_help: 'Indices de localisation : informations complémentaires. Par exemple : l''étage du bâtiment où se trouve le bureau de vote.' + index: + title: Bureaux de vote + new: + create: Créer + title: Créer un bureau de vote + update: + invalid: Une erreur est survenue lors de la mise à jour de ce bureau de vote + success: Le bureau de vote a bien été mis à jour + titles: + votings: Élections + votings: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + new_voting: Nouvel espace de vote + publish: Publier + unpublish: Dépublier + create: + invalid: Une erreur s'est produite lors de la création de cette élection + success: Élection créée avec succès + edit: + assign_missing_officers: Il reste des bureaux de votes sans président et/ou accesseurs. Veuillez lier ces derniers à partir de la section Bureaux de votes + update: Mettre à jour + form: + slug_help: 'Les identifiants d''URL sont utilisés pour générer les URL qui renvoient vers cette élection. Ils ne contiennent que des lettres, des chiffres et des tirets et doivent commencer par une lettre. Exemple : %{url}' + title: Titre + new: + create: Créer + title: Nouvelle élection + publish: + success: Élection publiée avec succès + unpublish: + success: Élection publiée avec succès + update: + invalid: Une erreur s'est produite lors de la mise à jour de cette élection + success: Élection mise à jour avec succès + admin_log: + voting: + create: "%{user_name} a créé l'élection %{resource_name}" + publish: "%{user_name} a publié l'élection %{resource_name}" + unpublish: "%{user_name} a annulé la publication de l'élection %{resource_name}" + census: + admin: + census: + creating_data: + info_message: "Veuillez patienter, %{processed_count} traités sur %{raw_count} lignes à partir du fichier %{file}." + delete: + button: Supprimer toutes les données du recensement + confirm: La suppression du recensement est irréversible. Êtes-vous sûr de vouloir continuer ? + destroy: + success: Données de recensement supprimées + generate_access_codes: + button: Générer des codes d'accès au vote + confirm: Si vous continuez, vous ne pourrez plus modifier le recensement. + info_message_all: "Toutes les lignes ont été importées avec succès depuis le fichier %{file} (%{raw_count} sur %{data_count})." + info_message_warn: Veuillez vérifier qu'aucune donnée n'est manquante, car %{data_count} enregistrements ont été créés et le fichier téléchargé %{file} a %{raw_count} lignes. + new: + file_help: + explanation: 'Instructions concernant le fichier :' + message_1: Seuls les fichiers CSV (.csv) sont autorisés. + message_2: Le séparateur de colonnes doit être un point-virgule (";"). + info_message: "Il n'y a pas encore de recensement. Veuillez utiliser le formulaire ci-dessous pour le créer en important un fichier CSV." + submit: Valider le fichier csv + title: Créer le recensement + show: + heading: Recensement de l'espace de vote + document_types: + passport: Passeport + vote_flow: + already_voted_in_person: Ce participant a déjà voté en personne et n'a plus le droit de voter. + datum_not_found: Les données données ne correspondent à aucun électeur. + content_blocks: + highlighted_votings: + name: Élections remarquables + landing_page: + description: + show_less: Voir moins + show_more: Lire la suite + metrics: + heading: Statistiques + polling_stations: + heading: Bureaux de vote + no_polling_stations: Il n'y a pas encore de bureau de vote. + timeline: + heading: Chronologie + pages: + home: + highlighted_votings: + active_votings: Votations en cours + see_all_votings: Voir toutes les votations + votings_button_title: Lien vers la page Votations où toutes les votations sont affichées + polling_officer_zone: + closures: + show: + heading: Recomptage des votes + sign: + cancel: Annuler + check_box: J'ai passé en revue ce certificat, il est identique à celui de fermeture des élections physiques + close_modal: Clore + confirm: Ok, continuer + error: Une erreur est survenue, veuillez réessayer. + heading: Recomptage des voix - Signer la fermeture + info_text: Si vous continuez, vous ne pouvez plus modifier aucune des informations, cette action ne peut pas être annulée. + submit: Signer la fermeture + success: Fermeture signée avec succès. + title: Cette action ne peut pas être annulée + in_person_votes: + complete_voting: + available_answers: 'Réponses disponibles:' + census_verified: Le participant n'a pas encore voté. + complete_voting: Vote terminé + identify_another: Identifier un autre participant + questions_title: 'Elle a le droit de voter sur les questions suivantes:' + questions_title_voted: 'Le participant a déjà voté en ligne et a le droit de voter dans les questions suivantes :' + voted: Le participant a voté + create: + error: Le vote n'a pas été enregistré. Veuillez réessayer. + in_person_form: + census_not_present: Ce participant n'est pas répertorié dans le recensement. + census_not_present_description: Elle doit se rendre au bureau des plaintes du recensement ou contacter le support. + date_of_birth: Date de naissance + day: Jour + day_placeholder: JJ + document_number: Numéro de document + document_number_placeholder: Numéro de carte d'identité + month: Mois + month_placeholder: MM + select: Sélectionnez le type de document + title: 'Sélectionnez le type de document et entrez le numéro de document du participant :' + validate_document: Valider le document + year: Année + year_placeholder: AAAA + new: + back: Retourner aux bureaux de vote + title: Identifier et vérifier un participant + show: + back: Retourner aux bureaux de vote + title: En attente de l'enregistrement du vote en personne + update: + error: Une erreur est survenue lors de l'enregistrement du vote. Veuillez réessayer. + success: + accepted: Le vote a été enregistré avec succès. + rejected: Le vote n'a pas été accepté par le Tableau d'Affichage. Veuillez contacter l'administrateur du système. + verify_document: + census_present: Ce participant n'est pas répertorié dans le recensement. + name: Nom + title: 'Vérifiez que les données suivantes sont correctes:' + verify_document: Document requis + polling_station_closure_recount: + nota_option: Vide / Aucune des options ci-dessus + polling_officer_notes: 'Notes de l''accesseur :' + polling_officer_notes_blank: Il n'y a aucune note + recount_summary: 'Bilan du recomptage:' + signed: Signé + total_ballots: 'Nombre total de bulletins :' + total_blank_ballots: 'Nombre total de bulletins blanc :' + total_null_ballots: 'Nombre total de bulletins nuls :' + total_valid_ballots: 'Nombre total de bulletins valides :' + polling_stations: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + edit: Modifier + new: Nouveau + title: Actions + votings: + check_census: + check_status: Vérifiez le statut + form_title: 'Remplissez le formulaire suivant pour vérifier vos données de recensement:' + check_fields: + date_of_birth: Date de naissance + day: Jour + day_placeholder: JJ + document_number: Numéro de document + document_number_placeholder: Numéro de carte d'identité + month: Mois + month_placeholder: MM + postal_code: Code postal + postal_code_placeholder: Numéro de code postal + select: Sélectionnez le type de document + year: Année + year_placeholder: AAAA + count: + title: + one: "%{count} élection" + other: "%{count} élections" + elections_log: + bb_status: État actuel de l'élection + description: Le journal des élections vous montrera toutes les informations pertinentes sur chaque vote. Par exemple, le statut de la cérémonie de génération des clées ou du dépouillement ou si les résultats sont déjà publiés. Cliquez sur l'élection à propos de laquelle vous voulez des informations. + election_log: Journal de l'élection + title: Journal de l'élection + filters: + active: Actifs + all: Tous + finished: Terminées + search: Rechercher + state: Statut + upcoming: À venir + filters_small_view: + close_modal: Fermer la fenêtre de dialogue + filter: Filtrer + filter_by: Filtrer par + unfold: Dérouler + index: + no_votings: Aucun vote ne correspond à votre recherche. + only_finished: À l'heure actuelle, il n'y a pas de votes planifiés, mais vous trouverez ici une liste de tous les votes passées. + title: Votations + login: + access_code: Code d'accès + access_code_placeholder: Code d'accès + dont_have_access_code: Vous n'avez pas de code d'accès? + form_title: 'Remplissez le formulaire suivant pour accéder au vote :' + start_voting: Commencer à voter + step: Identification + title: Je m'identifie avec mes données de recensement de vote + orders: + label: 'Trier les votations par:' + random: Aléatoire + recent: Les plus récentes + show: + dates: Dates + votings_m: + badge_name: + finished: Passées + ongoing: En cours + upcoming: À venir + footer_button_text: + participate: Participer + view: Voir + vote: Voter + unspecified: Non précisées + voting_type: + hybrid: Hybride + in_person: En présentiel + online: En ligne + voting_types_label: Type de vote + layouts: + decidim: + election_votes_header: + exit: Quitter + voting_navigation: + election_log: Journal de l'élection + voting_menu_item: Le vote + votings: + index: + promoted_votings: Votations remarquables + promoted_voting: + more_info: Plus d'informations + vote: Voter diff --git a/decidim-elections/config/locales/fr.yml b/decidim-elections/config/locales/fr.yml new file mode 100644 index 00000000..a38e7850 --- /dev/null +++ b/decidim-elections/config/locales/fr.yml @@ -0,0 +1,1426 @@ +fr: + activemodel: + attributes: + answer: + description: Description + image: Image + proposals: Propositions associées + title: Titre + ballot_style: + code: Code + election: + description: Description + end_time: Le vote se termine à + start_time: Le vote commence à + title: Titre + monitoring_committee_member: + email: Email + name: Nom + polling_officer: + email: Email + name: Nom + polling_station: + address: Adresse + location: Lieu + location_hints: Indication de lieu + polling_station_managers: Gestionnaires + polling_station_president_id: Président + title: Titre + question: + max_selections: Nombre maximum de sélections + min_selections: Aucune des options ci-dessus + title: Titre + trustees_participatory_space: + user_id: Participant + voting: + banner_image: Image de bannière + census_contact_information: Informations de contact du recensement + description: Description + end_time: Le vote prend fin le + introductory_image: Image d'introduction + promoted: Mis en avant + scope_id: Secteur + show_check_census: Afficher la page "Vérifier le recensement" + start_time: Le vote commence le + title: Titre + voting_type: Type de vote + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Doit être téléchargé de nouveau + ballot_result: + attributes: + base: + total_count_invalid: Le nombre total de réponses ne correspond pas à la répartition valide/blanc/nul. + election: + attributes: + attachment: + needs_to_be_reattached: Doit être téléchargé de nouveau + question_result: + attributes: + base: + blank_count_invalid: Le nombre total de réponses vides ne peut pas être supérieur au nombre total de bulletins blancs. + trustee: + attributes: + name: + cant_be_changed: ne peut pas être modifié + public_key: + cant_be_changed: ne peut pas être modifié + voting: + attributes: + voting_type: + inclusion: "%{value} n'est pas un type de vote valide" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Le responsable de scrutin est déjà président/responsable d'un bureau de vote. + voting: + different_organization: L'élection doit être dans la même organisation que l'utilisateur. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Le responsable de scrutin doit être dans la même élection que le bureau de vote. + models: + decidim/elections/answer: + one: Réponse + other: Réponses + decidim/elections/election: + one: Élection + other: Élections + decidim/elections/question: + one: Question + other: Questions + decidim/voting: + one: Scrutin + other: Scrutins + decidim/votings/census/dataset: + one: Jeu de données + other: Jeux de données + decidim/votings/census/datum: + one: Donnée + other: Données + decidim/votings/polling_officer: + one: Responsable du scrutin + other: Responsables du scrutin + decidim/votings/polling_station: + one: Bureau de vote + other: Bureaux de vote + decidim/votings/voting: + one: Vote + other: Votes + decidim: + admin: + filters: + officers_assigned_eq: + label: Responsable + values: + assigned: Assigné + unassigned: Non assigné + role_eq: + label: Rôle + values: + manager: Responsable + president: Président + unassigned: Non assigné + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Rechercher %{collection} par nom/email/pseudo ou par bureau de vote. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Recherchez %{collection} par titre, adresse ou nom d'officier/email/pseudo de l'officier. + signed_eq: + label: Signature + values: + 'false': Signé + 'true': Non signé + validated_eq: + label: Validé + values: + 'false': Non validé + 'true': Validé + voting_publications: + create: + error: Une erreur s'est produite lors de la publication de ce vote. + success: Vote publié avec succès. + destroy: + error: Il y a eu un problème lors de la dépublication de ce vote. + success: Vote dépublié avec succès. + components: + elections: + actions: + vote: Voter + name: Élections + settings: + global: + announcement: Annonce + step: + announcement: Annonce + elections: + actions: + confirm_destroy: Êtes-vous certain ? + destroy: Détruire + edit: Modifier + feedback: Feedbacks des votants + import: Importer des propositions dans les réponses + manage_answers: Gérer les réponses + manage_questions: Gérer les questions + manage_steps: Gérer les étapes + new_answer: Nouvelle réponse + new_election: Nouvelle élection + new_question: Nouvelle question + new_trustee: Nouveau garant + preview: Aperçu + publish: Publier + title: Actions + unpublish: Dépublier + admin: + answers: + create: + invalid: Il y a eu un problème lors de la création de cette réponse. + success: Réponse créée avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de cette réponse. + success: Réponse supprimée avec succès. + edit: + title: Modifier la réponse + update: Mettre à jour la réponse + index: + invalid_max_selections: Vous avez besoin de %{missing_answers} réponse(s) de plus pour correspondre aux sélections maximales. + title: Réponses + new: + create: Créer une réponse + title: Nouvelle réponse + not_selected: Non sélectionnée + select: + disable: Désélectionner la réponse + enable: Marquer la réponse comme sélectionnée + invalid: Un problème est survenu lors de la sélection de cette réponse. + success: Réponse sélectionnée avec succès. + selected: Sélectionnée + unselect: + invalid: Une erreur est survenue lors de la désélection de cette réponse. + success: Réponse désélectionnée avec succès. + update: + invalid: Un problème est survenu lors de la mise à jour de cette réponse. + success: Réponse mise à jour avec succès. + elections: + create: + invalid: Il y a eu un problème lors de la création de cette élection. + success: Élection créée avec succès. + destroy: + invalid: Une erreur s'est produite lors de la suppression de cette élection. + success: Élection supprimée avec succès. + edit: + title: Modifier l'élection + update: Mettre à jour l'élection + form: + organization_time_zone: Vérifiez dans les paramètres de l'organisation que le fuseau horaire de l'organisation est correct. La configuration actuelle est %{time_zone} (%{time}). + index: + no_bulletin_board: Il n'y a pas de serveur Bulletin Board configuré, ce qui est nécessaire pour utiliser ce module. Cette tâche doit être effectuée par l'administrateur système. + title: Élections + new: + create: Créer une élection + title: Nouvelle élection + publish: + success: L'élection a été publiée avec succès. + unpublish: + success: L'élection a été dépubliée avec succès. + update: + invalid: Il y a eu un problème lors de la mise à jour de cette élection. + success: Élection mise à jour avec succès. + exports: + elections: Élections + feedback_form_answers: Feedbacks des répondants + mailers: + trustee_mailer: + body: + help_html: |- +

    Bonjour %{user_name},


    +

    Vous avez été invité pour agir en tant que garant d'une élection sur %{organization}.


    +

    Une nouvelle section nommée "Espace de garant" a été activée sur votre compte. Depuis cette section, vous pourrez agir en tant que garant. Pour l'instant, nous vous prions de générer vos clés d'identification.


    + subject: Vous avez été ajouté(e) comme garant pour %{resource_name} + trustee_zone: Emmenez-moi dans la zone des garants + menu: + trustees: Garants + models: + answer: + name: Réponse + proposals_imports: + create: + invalid: Un problème est survenu lors de l'import des propositions en réponses. + success: "%{number} propositions importées en réponses." + new: + create: Importer des propositions dans les réponses + no_components: Il n'y a pas d'autres fonctionnalités Propositions dans cet espace participatif pour importer des propositions. + select_component: Veuillez sélectionner une fonctionnalité + title: Importer des propositions + questions: + create: + election_started: L'élection a déjà commencé. + invalid: Il y a eu un problème lors de la création de cette question. + success: Question créée avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de cette question. + success: Question supprimée avec succès. + edit: + title: Modifier la question + update: Mettre à jour la question + index: + title: Questions + new: + create: Créer une question + title: Nouvelle question + update: + invalid: Une erreur s'est produite lors de la mise à jour de cette question. + success: Question mise à jour avec succès. + steps: + create_election: + census: Recensement + errors: + census_codes_generated: Les codes d'accès pour le recensement ne sont pas générés. + census_frozen: Les codes d'accès pour le recensement ne sont pas exportés. + census_uploaded: Il n'y a pas de recensement téléchargé pour cette élection. + component_published: La fonctionnalité élection n'est pas publiée. + fix_it_text: Corriger + max_selections: Les questions n'ont pas de valeur correcte pour le nombre de réponses + minimum_answers: Les questions doivent avoir au moins deux réponses. + minimum_questions: L'élection doit avoir au moins une question. + published: L'élection n'est pas publiée. + time_before: L'heure de début se situemoins de %{hours} avant le début de l'élection. + trustees_number: L'espace participatif doit avoir au moins %{number} garants avec une clé publique. + invalid: Une erreur est survenue lors de la création de l'élection + no_trustees: Il n'y a pas de garants configurés pour cet espace participatif + not_used_trustee: "(non utilisé)" + public_key: + 'false': n'a pas de clé publique + 'true': a une clé publique + requirements: + census_codes_generated: Les codes d'accès pour le recensement sont générés. + census_frozen: Les codes d'accès pour le recensement sont exportés et le recensement est gelé. + census_uploaded: Le recensement a été téléchargé. + component_published: La fonctionnalité élection est publiée. + max_selections: Toutes les questions ont une valeur correcte pour le maximum de réponses. + minimum_answers: Chaque question a au moins 2 réponses. + minimum_questions: L'élection a au moins 1 question. + published: L'élection est publiée. + time_before: La configuration est réalisée au moins %{hours} avant le début de l'élection. + trustees_number: L'espace participatif doit avoir au moins %{number} garants avec une clé publique. + submit: Configurer l'élection + success: L' élection a été envoyée avec succès au Panneau d'Affichage. + technical_configuration: + authority_name: "Nom de l'autorité : %{value}" + bulletin_board_server: "Serveur du Panneau d'affichage: %{value}" + scheme_name: "Nom du schema : %{value}" + title: Voir les informations techniques + title: Configurer l'élection + trustees: Garants de l'élection + created: + invalid: Il y a eu un problème lors du démarrage de la cérémonie des clés. + submit: Démarrer la cérémonie des clés + success: La demande de lancement de cérémonie des clés a bien été envoyée au Panneau d'Affichage. + title: Élection créée + trustees: Garants + key_ceremony: + continue: Continuer + title: Cérémonie des clés + key_ceremony_ended: + errors: + time_before: L'élection est prête à commencer. Vous devez attendre %{hours} heures avant le lancement (%{start_time}) pour démarrer la période de vote. + invalid: Il y a eu un problème au démarrage de la période de vote. + requirements: + time_before: L'élection va bientôt commencer. Vous pouvez démarrer la période de vote manuellement, ou elle sera démarrée automatiquement avant l'heure de départ, à %{start_time}. + submit: Commencer la période de vote + success: La demande de lancement de la période de vote a bien été envoyée au Panneau d'Affichage. + title: Prêt à commencer + processing: Traitement en cours... + results_published: + answer: Répondre + not_selected: Non sélectionné + question: Question + result: Résultat + selected: Sélectionné + submit: Envoyer + title: Résultats publiés + tally_ended: + answer: Répondre + not_selected: Non sélectionné + question: Question + result: Résultat + selected: Sélectionné + submit: Publier les résultats + success: La demande de publication des résultats a bien été envoyée au Panneau d'Affichage. + title: Résultats du calcul + tally_started: + continue: Continuer + invalid: Une erreur est survenue lors du signalement du garant absent. + mark_as_missing: Marquer comme absent + mark_as_missing_description: Tous les garants doivent participer à ce processus, toutefois, si l'un deux ne peut pas y participer, vous pouvez le marquer comme manquant. + success: Le garant manquant a été envoyé avec succès au Panneau d'Affichage. + tally_completion: Le processus sera terminé lorsque tout les garants seront marqués comme actifs ou absents. Au moins %{quorum} garants sont requis pour compléter le processus. + title: Dépouillement + undo_mark_as_missing: Un garant marqué comme absent par erreur sera capable de participer avant l'achèvement du processus. Ils peuvent procéder comme d'habitude et le signalement d'absence sera ignoré. + vote: + errors: + time_after: L'élection est toujours en cours. Vous devez attendre (%{end_time}) pour terminer la période de vote. + invalid: Un problème est survenu lors de la clôture de la période de vote. + requirements: + time_after: L'élection est terminée. Vous pouvez terminer la période de vote manuellement, ou elle se terminera automatiquement dans quelques minutes. + submit: Fin de la période de vote + success: La demande de fin de la période de vote a été envoyée avec succès au Panneau d'Affichage. + title: Période de vote + vote_ended: + invalid: Une erreur s'est produite au lancement du dépouillement. + submit: Démarrer le dépouillement + success: La demande de lancement du dépouillement a bien été envoyée au Panneau d'Affichage. + text: Le vote est terminé. Vous pouvez commencer le comptage. + title: Période de vote terminée + vote_stats: + no_vote_statistics_yet: Pas encore de statistiques sur le vote + title: Statistiques du vote + voters: Votants + votes: Votes + trustees_participatory_spaces: + actions: + disable: Désactiver + enable: Autoriser + create: + exists: Ce garant existe déjà dans cet espace participatif. + invalid: Une erreur s'est produite lors de la création du garant. + success: Le garant a bien été créé. + delete: + invalid: Il y a eu un problème lors de la suppression de ce garant. + success: Le garant a bien été supprimé. + form: + select_user: Sélectionner un utilisateur + index: + title: Garants + new: + create: Créer un garant + title: Nouveau garant + update: + invalid: Une erreur s'est produite lors de la mise à jour du garant %{trustee}. + success: Le garant %{trustee} a été mis à jour avec succès. + admin_log: + election: + create: "%{user_name} a créé l'élection %{resource_name} dans %{space_name}" + delete: "%{user_name} a supprimé l'élection %{resource_name} dans %{space_name}" + end_vote: "%{user_name} a terminé la période de vote pour l'élection %{resource_name} de l'espace %{space_name} sur le Panneau d'affichage" + publish: "%{user_name} a publié l'élection %{resource_name} de %{space_name}" + publish_results: "%{user_name} a publié les résultats de l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + report_missing_trustee: "%{user_name} a signalé %{trustee_name} en tant que garant absent pendant le dépouillement de l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + setup: "%{user_name} a créé l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + start_key_ceremony: "%{user_name} a commencé la cérémonie des clés pour l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + start_tally: "%{user_name} a commencé le dépouillement pour l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + start_vote: "%{user_name} a commencé la période de vote pour l'élection %{resource_name} de %{space_name} sur le Panneau d'affichage" + unpublish: "%{user_name} a dépublié le %{resource_name} de l'élection %{space_name}" + update: "%{user_name} a mis à jour l'élection %{resource_name} de %{space_name}" + trustee: + create: "%{user_name} a nommé l'utilisateur %{trustee_user} en tant que Garant" + connection: + failed: + modal: + close: Fermer + communication_lost: Malheureusement, il semble que la communication avec le serveur de vote (Bulletin Board) est perdue.
    Il se peut que la connexion Internet soit interrompue ou que le serveur de destination soit trop occupé.
    Vous pouvez réessayer plus tard ou contacter le support si ce problème persiste. + generic_error: Malheureusement, une erreur inconnue s'est produite. Il est probable que votre navigateur n'est pas pris en charge ou que vous utilisez le mode "incognito" ou "privé" qui n'est pas pris en charge. + title: Un problème est survenu + election_m: + badge_name: + finished: Terminées + ongoing: Active + upcoming: À venir + end_date: Se termine le + footer: + remaining_time: + one: "Il reste %{count} heure %{minutes} minutes pour voter." + other: "Il reste %{count} heures %{minutes} minutes pour voter." + view: Voir + vote: Voter + label: + date: Dates + questions: '%{count} questions' + start_date: Début + unspecified: Non spécifié + elections: + count: + elections_count: + one: "%{count} élection" + other: "%{count} élections" + election_log: + chained_hash: La chaîne de hash de ce message + complete: Terminée + creation_description: + complete: L'élection a bien été créée et ajoutée au Panneau d'affichage. + not_created: L'élection n'est pas encore créée. + creation_title: Élection créée + description: Ceci est le journal des élections, où vous pouvez vérifier le statut de chaque étape, par exemple lorsque l'élection a été créée, si le processus de dépouillement est terminé, et la date de clôture de l'élection. + download: Télécharger + key_ceremony_description: + complete: La cérémonie de génération de clés est terminée. Chaque garant a des clés valides et a téléchargé les clés de sauvegarde nécessaires. + not_started: La génération des clés n'a pas encore commencé. + started: La cérémonie de génération des clés a commencé mais n'est pas encore terminée. + key_ceremony_title: Cérémonie de génération des clés + not_available: N'est pas encore disponible + not_created: N'est pas créée + not_ready: Pas encore prêt + not_started: Non démarré + published: Publié + results_description: + not_published: Les résultats ne sont pas encore publiés. + published: Les résultats sont publiés. + results_title: Résultats + started: Démarré + tally_description: + finished: Le dépouillement est terminé. + not_started: Le dépouillement n'a pas encore commencé. + started: Le dépouillement a commencé. + tally_title: Dépouillement + title: Journal de l'élection + unpublished: Non publié + verifiable_results: + checksum: 'Fichier de somme de contrôle SHA256:' + description: + not_ready: Le fichier de vérification électoral et la somme de contrôle SHA256 ne sont pas encore disponibles. Dès que les résultats seront publiés, vous serez en mesure de vérifier cette élection. + ready: 'Ici, vous avez la possibilité de vérifier l''élection. D''abord, vous devez télécharger le fichier et vous assurer qu''il n''a pas été corrompu. Pour ce faire, exécutez la commande suivante et vérifiez que la sortie correspond à la somme de contrôle :' + how_to_verify: 'Une fois le fichier téléchargé et vérifié qu''il est correct, vous pouvez exécuter le vérificateur universel. Clonez ce dépôt et, depuis le dossier racine, exécutez la commande suivante :' + title: Vérifier les résultats de l'élection + verifiable_file: 'Fichier de vérification d''élection:' + verify: Vérifier l'élection + vote_description: + finished: Le vote est terminé. + not_started: Le vote n'a pas encore commencé. + started: Le vote a commencé. + vote_title: Processus de vote + filters: + active: Actif + all: Toutes + date: Date + finished: Terminées + upcoming: À venir + preview: + available_answers: 'Réponses disponibles :' + description: 'Voici les questions que vous trouverez dans le processus de vote :' + title: Questions de l'élection + results: + description: 'Ce sont les résultats du vote, pour chaque question :' + percentage: "%{count}%" + selected: Sélectionné + title: Résultats de l'élection + votes: + one: "%{count} vote" + other: "%{count} votes" + show: + action_button: + change_vote: Changer votre vote + vote: Commencer à voter + vote_again: Voter à nouveau + callout: + already_voted: Vous avez déjà voté pour cette élection. Vous pouvez modifier votre vote ou le vérifier. + pending_vote: Votre vote est en cours de validation sur le serveur. + vote_rejected: Il n'a pas été possible de vérifier votre vote. Veuillez recommencer. + election_log: Logs de l'élection + preview: Aperçu + verify: + already_voted: Déjà voté? + verify_here: Vérifiez votre vote ici. + will_verify: Vous pourrez vérifier votre vote une fois l'élection commencée. + voting_period_status: + finished: Le vote a commencé le %{start_time} et s'est terminé le %{end_time} + ongoing: 'Vote actif jusqu''au : %{end_time}' + upcoming: Le vote commence le %{start_time} + feedback: + answer: + invalid: Un problème est survenu lors de l'envoi de votre retour utilisateur. + spam_detected: Une erreur est survenue lorsque vous avez répondu au questionnaire. Pouvez-vous réessayer ? + success: Votre avis a bien été envoyé. + models: + answer: + fields: + proposals: Propositions + selected: Sélectionné + title: Titre + votes: Votes + election: + fields: + bb_status: Statut du Panneau d'affichage + end_time: Prend fin à + start_time: Commence à + title: Titre + verifiable_results_file_hash: Somme de contrôle SHA256 du fichier + verifiable_results_file_url: Fichier d'élection vérifiable + question: + fields: + answers: Réponses + max_selections: Nombre maximum de sélections + title: Titre + trustees_participatory_space: + fields: + considered: autorisé + email: Email + inactive: inactif + name: Nom + notification: Notification envoyée à + public_key: Clé publique + status: Statut + orders: + label: Trier les élections par + older: Les plus anciens + recent: Les plus récents + trustee_zone: + elections: + backup_modal: + description: Cette élection est en cours de création dans le Panneau d'affichage. Il est très important que chaque garant qui y participe fasse une sauvegarde de ces clés et les stocke dans un endroit sûr. Ensuite, le processus se poursuit. + download_election_keys: Télécharger les clés + title: Sauvegarder les clés d'élection pour %{election} + key_ceremony_steps: + back: Retour + description: Cette élection est en cours de création dans le Tableau d'Affichage. Pour compléter ce processus, votre participation en tant que Mandataire est nécessaire. + keys: + create_election: Génération des clés + key_ceremony: + joint_election_key: Génération de la clé conjointe + step_1: Publication des clés + list: + status: Statut + task: Tâche + process_warning: Une fois le processus démarré, vous ne devriez pas quitter cette page avant la fin du processus. Cela prendra plusieurs minutes, car tous les garants doivent être connectés pour le compléter. + start: Démarrer + status: + completed: Terminé + pending: En attente + processing: Traitement en cours + title: Créer des clés d'élection pour %{election} + restore_modal: + description: Le Panneau d'affichage a des informations pour vous en temps que garant de cette élection. Pour continuer le processus, commencer par télécharger le fichier de sauvegarde généré pendant la session précédente. + title: Restaurer les clés d'élection pour %{election} + upload_election_keys: Télécharger les clés d'élection + tally_started_steps: + back: Retour + description: Les résultats de cette élection sont calculés dans le Panneau d'affichage et pour compléter ce processus, votre participation en tant que garant est nécessaire. + keys: + end_tally: Dépouillement terminé + tally: + cast: Valider le dépouillement + share: Partager le dépouillement + list: + status: Statut + task: Tâche + process_warning: Une fois le processus démarré, vous ne devriez pas quitter cette page avant la fin du processus. Cela prendra plusieurs minutes, car tous les garants doivent être connectés pour le compléter. + start: Démarrer + status: + completed: Terminé + pending: En attente + processing: Traitement en cours + title: Dépouillement pour %{election} + update: + error: Le statut de l'élection n'a pas été mis à jour. + success: 'Le statut de l''élection est : %{status}.' + menu: + trustee_zone: Espace de garant + no_bulletin_board: + body: Un Panneau d'affichage configuré est requis pour cette section. Contactez l'administrateur pour plus de détails. + title: Désolé, le Panneau d'affichage n'est pas encore configuré. + trustees: + show: + elections: + list: + action_required: + 'false': 'Non' + name: Action requise ? + 'true': Effectuer l'action + bb_status: Statut + election: Élection + voting_period: Période de vote + no_elections: Actuellement, vous n'avez pas été assigné pour prendre des mesures en tant que garant. Vous recevrez une notification quand il sera temps d'agir pendant les différentes phases. + title: Élections + identification_keys: + cancel: Annuler + generate: Générer les clés d'identification + generate_error: Une erreur s'est produite lors de la génération des clés d'identification. + generate_legend: Vous devez générer une paire de clés d'identification pour participer aux élections en tant que garant. + generate_legend_1: Après avoir cliqué sur le bouton, le téléchargement du fichier contenant les clés d'identification générées va commencer. + generate_legend_2: Copiez le fichier téléchargé sur un périphérique USB fiable + generate_legend_3: Assurez-vous que votre ordinateur ne possède pas de copie du fichier (par exemple, vérifiez les dossiers Téléchargements et Bureau). + generate_legend_4: Faites une autre copie du fichier sur un appareil externe et conservez-le dans un endroit très sûr. + submit: Envoyer + submit_legend: Après avoir suivi toutes les étapes expliquées ci-dessus, complétez le processus d'envoi de la clé d'identification publique au serveur. + submit_title: Soumettre la clé d'identification publique + title: Clés d'identification du garant + upload: Téléchargez vos clés d'identification + upload_error: + invalid_format: Le fichier téléchargé ne contient pas de clé d'identification. + invalid_key: Impossible de charger les clés d'identification à partir du fichier téléchargé. + invalid_public_key: Les clés d'identification dans le fichier téléchargé ne correspondent pas à la clé d'identification publique stockée. + upload_legend: Le serveur dispose de vos clés d'identification publiques, mais pas votre navigateur. Vous devez importer le fichier contenant vos clés d'identification sur votre ordinateur à partir de la sauvegarde que vous avez créée après les avoir générées. + not_supported_browser_description: Il semblerait que votre navigateur web ne peut pas être utilisé pour agir en tant qu'administrateur. Assurez-vous d'utiliser la dernière version de votre navigateur ou essayez d'utiliser l'un des navigateurs les plus populaires afin de réaliser vos tâches. + not_supported_browser_title: Mettre à niveau le navigateur pour agir en tant que garant + safari_warning_description: Il semblerait que vous utilisez Safari, qui n'est pas pris en charge pour agir en tant que fiduciaire ou pour chiffrer un vote (cela est dû aux restrictions de mémoire qu'Apple y impose). Cela pourrait être résolu à l'avenir par un changement de politique par Apple ou par l'optimisation future des élections Decidim. S'il vous plaît, utilisez un autre navigateur entre-temps. + safari_warning_title: Navigateur Safari détecté + trustee_role_description: + with_keys: Vous avez été assigné à agir en tant que garant lors de certaines élections mises en place sur cette plateforme. + without_keys: Vous avez été assigné à agir en tant que garant. Veuillez générer et télécharger vos clés d’identification. + update: + success: Votre clé d'identification publique a bien été stockée. + votes: + ballot_decision: + audit: Auditer le bulletin de vote + back: Recommencer le processus de vote + ballot_hash: 'L''identifiant de votre bulletin est :' + cast: Remplir le bulletin pour terminer votre vote + description_html: Ici, vous avez les options de valider votre bulletin pour qu'il soit correctement compté ou, vous pouvez vérifier que votre bulletin a été correctement chiffré. Si vous voulez auditer le vote, veuillez lire les instructions sur comment procéder. + header: 'Le bulletin est chiffré : validez-le ou auditez-le' + casting: + header: Vote en cours... + text: Votre bulletin a bien été pris en compte. + confirm: + answer_number: répondre à %{number} + confirm: Valider + edit: éditer + header: Confirmer votre vote + intro: Voici un résumé du vote que vous êtes sur le point d'envoyer.
    Veuillez confirmer votre vote ou modifier vos réponses. + nota_option: Vide + confirmed: + back: Retour aux élections + experience: Comment a été votre expérience? + feedback: Donnez-nous votre avis + header: Vote confirmé + lead: Votre vote a été comptabilisé ! + text: 'Vous pouvez vérifier que votre vote a bien été ajouté aux urnes avec l''identifiant suivant : ' + verify_link: Pour vérifier que votre vote a bien été pris en compte, copiez l'identifiant et collez-le sur la page de vérification de vote + create: + error: Un problème s'est produit lors de la validation du vote. Veuillez réessayer. + encrypting: + header: Chiffrement du vote... + text: Votre bulletin est en cours de chiffrement afin de garantir le secret de votre vote. + failed: + header: Le vote a échoué + lead: Votre vote n'a pas été exprimé ! + text: Une erreur est survenue, veuillez réessayer. + try_again: Réessayez + header: + ballot_decision: Voter + confirm: Confirmer votre vote + election: Élection + register: S'inscrire + vote_for: Voter pour %{title} + messages: + invalid_token: Votre session dans l'isoloir n'est pas valide. Essayez de voter à nouveau. + not_allowed: Vous n'êtes pas autorisé à voter sur cette élection pour le moment. + modal: + close: Fermer + proposal_header: 'Propositions:' + new: + answer_choices: Vous pouvez sélectionner jusqu'à %{choices} réponses + more_information: Plus d'informations + nota_option: Vide / Aucune des options ci-dessus + preview_alert: Ceci est un aperçu de la cabine de vote. + question_steps: Question %{current_step} sur %{total_steps} + selections: "%{selected} sur %{max_selections} sélections" + onboarding_modal: + create_account: S'inscrire + description: Voulez-vous créer un compte sur la plateforme ? Vous pourrez participer aux concertations et prendre une part active dans l'organisation. + no_account: Non, merci. + title: Nouveau sur la plateforme ? + update: + error: Une erreur s'est produite lors de la mise à jour du statut du vote. Tentez de voter à nouveau. + verify: + content: + heading: Vérifiez votre vote + info: Ce vérificateur s'assure que votre vote, identifié avec une chaîne de texte chiffrée, a été diffusé correctement et se trouve bien dans l'urne. + error: + header: Vote introuvable ! + info: Le code de vote n'a pas été trouvé dans %{link} l'urne, essayez à nouveau. + form: + back: Retour à la plateforme + submit: Vérifier + vote_identifier: 'Code d''identification :' + vote_identifier_help: C'est l'identifiant qui vous a été donné après que vous avez voté (pas le code pour entrer dans l'isoloir). + header: + title: Vérifiez votre vote + success: + header: Vote trouvé ! + info: Votre vote chiffré se trouve bien dans l'urne %{link} . + voting_step: + back: Retour + continue: Suivant + warnings: + empty_filters: Il n'y a pas d'élections correspondant à ces critères. + no_elections: Il n'y a aucune élection programmée. + no_scheduled_elections: À l'heure actuelle, il n'y a pas d'élections programmées, mais vous trouverez ici toutes les élections passées. + events: + elections: + election_published: + email_intro: 'L''élection %{resource_title} est maintenant active pour %{participatory_space_title}. Vous pouvez la voir sur cette page :' + email_outro: Vous avez reçu cette notification parce que vous suivez %{participatory_space_title}. Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: L'élection %{resource_title} est maintenant active pour %{participatory_space_title}. + notification_title: L'élection de %{resource_title} est maintenant active pour %{participatory_space_title}. + trustees: + new_election: + email_intro: Vous avez été ajouté en tant que garant pour l'élection %{resource_title}. + email_outro: Vous avez reçu cette notification parce que vous avez été ajouté(e) comme garant de l'élection "%{resource_title}". Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: Vous avez été ajouté en tant que garant pour l'élection %{resource_title}. + notification_title: Vous avez été sélectionné en tant que garant de l'élection %{resource_title}. Veuillez entamer la cérémonie des clés pour mettre en place l'élection. + new_trustee: + email_intro: Un administrateur vous a ajouté en tant que garant de %{resource_name}. Vous devriez créer votre clé publique dans votre espace de garant + email_outro: Vous avez reçu cette notification parce que vous avez été ajouté(s) en tant que garant pour %{resource_name}. Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: Vous avez été ajouté en tant que garant pour l'élection %{resource_name}. + notification_title: Vous avez été ajouté en tant que garant de %{resource_name} pour des élections qui auront lieu sur cette plateform.
    Pour l'instant, veuillez générer vos clés d'identification. + start_tally: + email_intro: La période de vote pour l'élection %{resource_title} est terminée. Maintenant, veuillez effectuer le décompte de l'élection pour publier les résultats finaux. + email_outro: Vous avez reçu cette notification parce que vous êtes un garant de l'élection %{resource_title}. + email_subject: Le processus de décompte pour l'élection %{resource_title} a commencé. + notification_title: La période de vote pour l'élection %{resource_title} est terminée. Maintenant, veuillez effectuer le décompte de l'élection pour publier les résultats finaux. + votes: + accepted_votes: + email_intro: 'Votre vote a été accepté ! En utilisant votre jeton de vote : %{encrypted_vote_hash}, vous pouvez vérifier votre vote ici.' + email_outro: "Vous avez reçu cette notification parce que vous avez voté pour l'élection %{resource_name}.\nSi vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”." + email_subject: Votre vote pour %{resource_name} a été accepté. + notification_title: 'Votre vote a été accepté. Vérifiez votre vote ici en utilisant votre jeton de vote : %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Vous avez été assigné en tant que %{role} du Bureau de vote %{polling_station_name} sur %{resource_title}. Vous pouvez gérer le Bureau de vote à partir de la zones des responsable du scrutin. + email_outro: Vous avez reçu cette notification parce que vous avez été assigné au rôle de %{role} sur %{polling_station_name}. Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”. + email_subject: Vous êtes %{role} du Bureau de vote %{polling_station_name}. + notification_title: Vous êtes %{role} du Bureau de vote %{polling_station_name} sur le vote %{resource_title}. + send_access_code: + instruction: 'Voici le code d''accès que vous avez demandé : %{access_code}, avec lequel vous pourrez participer à %{voting}.' + subject: Votre code d'accès pour participer à %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Une élection est un espace qui vous permet de poser une question claire à toutes les personnes formant une organisation, d'appeler à participer à l'élection, à susciter et organiser le débat pour ou contre une réponse. Lorsque la date de l'élection arrive, vous pouvez voter et publier les résultats des votes.

    Exemples : Les élections peuvent porter sur presque n'importe quel aspect qui affecte une organisation : changer le nom, le logo en offrant plusieurs alternatives, décider ou non de d'intégrer une organisation plus grande, valider ou rejeter un nouveau plan stratégique ou le résultat d'un groupe de travail, choisir la limitation à 1,2 ou 3 mandatures.

    \n" + page: "

    Une élection est un espace qui vous permet de poser une question claire à toutes les personnes formant une organisation, d'appeler à participer à l'élection, à susciter et organiser le débat pour ou contre une réponse. Lorsque la date de l'élection arrive, vous pouvez voter et publier les résultats des votes.

    Exemples : Les élections peuvent porter sur presque n'importe quel aspect qui affecte une organisation : changer le nom, le logo en offrant plusieurs alternatives, décider ou non de d'intégrer une organisation plus grande, valider ou rejeter un nouveau plan stratégique ou le résultat d'un groupe de travail, choisir la limitation à 1,2 ou 3 mandatures.

    \n" + title: Que sont les élections ? + menu: + votings: Élections + participatory_spaces: + related_elections: + see_all: Voir toutes les élections + statistics: + elections_count: Élections + votings_count: Votes + votings: + admin: + ballot_styles: + create: + error: Il y a eu un problème lors de la création de ce style de bulletin. + success: Style de bulletin créé avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de ce style de bulletin. + success: Style de bulletin supprimé avec succès. + edit: + title: Modifier le style de bulletin + update: Mettre à jour + form: + code_help: 'Astuce : le code est le lien entre l''élection et un bulletin de vote. Lors du téléchargement des données de recensement, chaque entrée se verra assignée un style de vote en faisant la correspondance avec le code.' + election: Élection + questions: Questions pour ce style de bulletin + questions_help: 'Astuce : sélectionnez les questions des fonctionalités Élections à présenter aux électeurs affectés à ce type de vote.' + index: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + edit: Modifier + new: Nouveau style de bulletin + title: Actions + associated_census_data: Entrées de recensement associées + explanation_callout: Un style de bulletin spécifie quelles questions seront présentées aux électeurs dans l'isoloir. Dans un style de bulletin, vous pouvez choisir quelles questions de la fonctionnalité Élection de ce vote appartiennent à un bulletin de vote. Le code de style de bulletin est utilisé pour faire correspondre un électeur du recensement avec le bulletin de vote qui lui sera présenté dans l'isoloir. Ne créez pas de style de vote si vous voulez présenter toutes les questions systématiquement. + title: Styles de bulletin + new: + create: Créer + title: Créer un style de bulletin + update: + invalid: Un problème est survenu lors de la mise à jour de ce style de scrutin. + success: Mise à jour réussie du style de scrutin. + content_blocks: + attachments_and_folders: + name: Pièces jointes et dossiers du vote + header: + name: En-tête de vote + highlighted_votings: + max_results: Nombre maximum d'éléments à afficher + html_block_1: + name: Bloc HTML 1 du Vote + html_block_2: + name: Bloc HTML 2 du Vote + html_block_3: + name: Bloc HTML 3 du Vote + main_data: + name: Titre et description + metrics: + name: Métriques de vote + polling_stations: + name: Bureaux de vote + related_elections: + name: Élections + stats: + name: Statistiques du vote + timeline: + name: Calendrier du vote + index: + published: Publié + unpublished: Non publié + menu: + votings: Élections + votings_submenu: + attachment_collections: Dossiers + attachment_files: Fichiers + attachments: Pièces jointes + ballot_styles: Styles de bulletin + census: Recensement + components: Fonctionnalités + info: À propos de ce vote + landing_page: Page d’accueil + monitoring_committee: Comité de suivi + monitoring_committee_election_results: Valider les résultats + monitoring_committee_members: Membres + monitoring_committee_polling_station_closures: Valider les certificats + monitoring_committee_verify_elections: Vérifier les élections + polling_officers: Responsables du scrutin + polling_stations: Bureaux de vote + see_voting: Voir le vote + models: + ballot_style: + fields: + code: Code + monitoring_committee_member: + fields: + email: Email + name: Nom + polling_officer: + fields: + email: Email + name: Nom + polling_station: Bureau de vote (rôle) + polling_station: + fields: + address: Adresse + polling_station_managers: Directeurs + polling_station_president: Président + title: Titre + voting: + fields: + created_at: Créée le + published: Publié + title: Titre + monitoring_committee_election_results: + actions: + title: Actions + view: Afficher + index: + title: Choisissez une élection pour laquelle vous souhaitez voir les résultats + results: + bulletin_board: Panneau d'affichage + election_totals: Total des élections + polling_stations: Bureaux de vote + result_types: + blank_answers: Réponses vides + blank_ballots: Votes blancs + null_ballots: Votes nuls + total_ballots: Total des votes + valid_ballots: Votes valides + selected: Sélectionné + title: Résultats pour l'élection %{election_title} + totals: Totaux + show: + change_election: Changer l'élection + publish_results: Publier les résultats + publishing: Publication en cours ... + update: + invalid: Un problème est survenu lors de la publication des résultats. + rejected: La publication des résultats a été rejetée par le Panneau d'affichage. Réessayez ou contactez l'administrateur système. + success: Les résultats ont été publiés avec succès. + monitoring_committee_members: + create: + invalid: Un problème est survenu lors de la création de ce membre du comité de suivi. + success: Membre du comité de suivi créé avec succès. + destroy: + invalid: Un problème est survenu lors de la suppression de ce membre de comité de suivi. + success: Membre de comité de suivi supprimé avec succès. + form: + existing_user: Utilisateur existant + non_user: Inviter un nouveau participant + select_user: Recherche par nom, email ou pseudo + user_type: Type de participant + index: + title: Comité de suivi + new: + create: Créer + title: Créer un membre du comité de suivi + monitoring_committee_polling_station_closures: + actions: + title: Actions + validate: Valider + view: Afficher + closures: + change_election: Changer l'élection + signed: Signé? + title: Bureaux de vote pour l'élection %{election_title} + validated: Validé? + edit: + change_polling_station: Retourner aux bureaux de vote + monitoring_committee_notes: Remarques + monitoring_committee_notes_placeholder: Signaler tout incident ici + title: Résultats pour l'élection %{election_title} dans le bureau de vote %{polling_station_title} + elections: + title: Choisissez une élection que vous souhaitez valider + show: + change_polling_station: Retour aux bureaux de vote + monitoring_committee_notes: Remarques du comité de suivi + validate: + error: Un problème est survenu lors de la validation de la fermeture. + success: La fermeture a été validée correctement. + monitoring_committee_verify_elections: + index: + download: Télécharger + how_to_checksum: 'Pour vous assurer que le fichier que vous avez téléchargé n''a pas été corrompu ou altéré pendant le processus de téléchargement, exécutez la commande suivante dans votre console et vérifiez que la sortie correspond à la somme de contrôle indiquée ci-dessus :' + how_to_download: Pour vérifier une élection, téléchargez son fichier vérifiable à partir du tableau ci-dessus. + how_to_run_verifier: 'Une fois le fichier téléchargé et vérifié, vous pouvez exécuter le vérificateur universel. Clonez ce dépôt et, depuis le dossier racine, exécutez la commande suivante :' + how_to_title: Comment vérifier la validité d'une élection + not_available: Pas encore disponible + title: Élections + polling_officers: + create: + invalid: Une erreur est survenue lors de la création de cet accesseur. + success: L'accesseur a été créé avec succès. + destroy: + invalid: Une erreur est survenue lors de la suppression de cet accesseur. + success: Accesseur supprimé avec succès. + form: + existing_user: Participant existant + non_user: Inviter un nouveau participant + select_user: Recherche par nom, email ou pseudo + user_type: Type du participant + index: + role_manager: directeur + role_president: président + title: Responsables du scrutin + new: + create: Créer + title: Créer un responsable du scrutin + polling_officers_picker: + choose_polling_officers: Choisir les responsables du scrutin + no_polling_officers: Aucun responsable du scrutin ne correspond à vos critères de recherche ou il n'y a pas de responsable du scrutin. + polling_stations: + create: + invalid: Une erreur est survenue lors de la création de ce bureau de vote. + success: Le bureau de vote a été créée avec succès. + destroy: + invalid: Une erreur est survenue lors de la suppression de ce bureau de vote. + success: Le bureau de vote a bien été supprimé. + edit: + title: Modifier le bureau de vote + update: Mettre à jour le bureau de vote + form: + address_help: 'L''adresse entrée est utilisée par le géocodage pour trouver l''emplacement et afficher la réunion sur la carte. Format accepté : [rue] [code postal] [ville]' + location_help: 'Lieu : message adressé aux électeurs informant du lieu exact du bureau de vote' + location_hints_help: 'Indices de localisation : informations complémentaires. Par exemple : l''étage du bâtiment où se trouve le bureau de vote.' + polling_station_managers_help: 'Directeur du scrutin : les personnes qui seront gestionnaires des bureaux de vote. Assurez-vous que les responsables du scrutin ont déjà été créés dans les bureaux de vote et qu''ils ne sont pas déjà affectés à un autre bureau de vote' + polling_station_president_help: "Président du bureau de vote : Agent qui agit en tant que président du bureau de vote. \nAssurez-vous que l'agent soit créé dans \"Bureau de vote\" et qu'il ne soit pas déjà assigné à un autre bureau de vote" + select_president: Sélectionnez un responsable de scrutin en tant que président du bureau de vote + index: + title: Bureaux de vote + new: + create: Créer + title: Créer un bureau de vote + update: + invalid: Une erreur est survenue lors de la mise à jour de ce bureau de vote. + success: Le bureau de vote a bien été mis à jour. + titles: + votings: Élections + votings: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + new_voting: Nouvel espace de vote + create: + invalid: Une erreur s'est produite lors de la création de cette élection. + success: Élection créée avec succès. + edit: + add_election_component: Vous n'avez aucune élection configurée pour ce vote. Veuillez en créer une dans la section Fonctionnalités. + assign_missing_officers: Il reste des bureaux de votes sans président et/ou gérant. Veuillez lier ces derniers à partir de la section Bureaux de votes. + update: Mettre à jour + form: + banner_image: Image de la bannière + census_contact_information: Coordonnées du recensement + census_contact_information_help: Cette information de contact est destinée à un participant qui veut signaler des problèmes avec le recensement. Il peut s'agir d'une adresse e-mail, d'un formulaire de contact sur un autre site, d'une enquête pour les visiteurs, etc. + introductory_image: Image d'introduction + promoted: Mis en avant + select_a_voting_type: Veuillez sélectionner un type de vote + show_check_census_help: Si vous voulez afficher le lien "Puis-je voter?" dans le menu des votes publics. + slug: Slug + slug_help_html: 'Les identifiants d''URL sont utilisés pour générer les URL qui pointent vers ce vote. N''accepte que des lettres, des chiffres et des tirets et doit commencer par une lettre. Exemple : %{url}' + title: Titre + voting_type: + hybrid: Hybride + in_person: En présentiel + online: En ligne + voting_type_label: Type de vote + new: + create: Créer + title: Nouveau vote + update: + invalid: Une erreur s'est produite lors de la mise à jour de ce vote. + success: Vote mis à jour avec succès. + admin_log: + ballot_style: + create: "%{user_name} a créé un style de bulletin avec le code %{ballot_style_code} dans l'espace %{space_name}" + delete: "%{user_name} a supprimé un style de bulletin avec le code %{ballot_style_code} dans l'espace %{space_name}" + update: "%{user_name} a mis à jour un style de bulletin avec le code %{ballot_style_code} dans l'espace %{space_name}" + census: + create: "%{user_name} a créé le recensement pour l'espace %{space_name}" + delete: "%{user_name} a supprimé le recensement pour l'espace %{space_name}" + update: "%{user_name} a mis à jour le recensement pour l'espace %{space_name}" + monitoring_committee_member: + create: "%{user_name} a assigné l'utilisateur %{monitoring_committee_member_user} en tant que membre du comité de suivi dans l'espace %{space_name}" + delete: "%{user_name} a supprimé l'utilisateur %{monitoring_committee_member_user} en tant que membre du comité de suivi dans l'espace %{space_name}" + polling_officer: + create: "%{user_name} a assigné l'utilisateur %{polling_officer_user} en tant que responsable de scrutin dans l'espace %{space_name}" + delete: "%{user_name} a supprimé l'utilisateur %{polling_officer_user} en tant que responsable de scrutin dans l'espace %{space_name}" + polling_station: + create: "%{user_name} a créé le bureau de vote %{resource_name} dans l'espace %{space_name}" + delete: "%{user_name} a supprimé le bureau de vote %{resource_name} dans l'espace %{space_name}" + update: "%{user_name} a mis à jour le bureau de vote %{resource_name} dans l'espace %{space_name}" + voting: + create: "%{user_name} a créé le vote %{resource_name}" + publish: "%{user_name} a publié le vote %{resource_name}" + unpublish: "%{user_name} a annulé la publication du vote %{resource_name}" + census: + admin: + census: + create: + invalid: Une erreur s'est produite lors de la mise à jour du recensement, veuillez réessayer plus tard. + invalid_csv_header: Les en-têtes CSV sont manquants ou incorrects - veuillez lire attentivement les instructions. + creating_data: + info_message: "Veuillez patienter, %{processed_count} traités sur %{raw_count} lignes à partir du fichier %{file} (cela peut prendre plusieurs minutes)." + delete: + button: Supprimer toutes les données du recensement + confirm: La suppression de tout le recensement ne peut pas être annulée. Êtes-vous sûr de vouloir continuer? + destroy: + error: Une erreur s'est produite lors de la suppression du recensement, veuillez réessayer plus tard. + success: Données d'élections supprimées. + export_access_codes: + button: Exporter les codes d'accès aux votes + callout: Vous pouvez maintenant exporter les codes d'accès. Cela ne peut être fait qu'une seule fois. Une fois que vous avez lancé l'exportation, vous recevrez un e-mail avec les instructions sur %{email} + confirm: Vous ne pouvez exporter les codes d'accès qu'une seule fois. Assurez-vous d'avoir accès au compte de messagerie %{email}. + file_not_exist: Ce fichier n'existe pas. + launch_error: Problème lors du lancement de l'export des codes d'accès. + launch_success: L'export des codes d'accès a été lancée. Vous recevrez sous peu un e-mail à %{email}. + exporting_access_codes: + info_message: "Veuillez patienter, l'export est en cours de préparation, vous le recevrez sous peu à %{email} (cela peut prendre plusieurs minutes)." + freeze: + callout: Le recensement est gelé et ne peut pas être modifié. + help_html: | + Les données du recensement ont été téléchargées, les codes générés et exportés avec succès.
    + Vous êtes maintenant prêt à commencer l'élection.
    + Utilisez le CSV exporté avec les codes individuels pour le distribuer le long de votre recensement en utilisant vos propres moyens ou activez l'onglet "Puis-je voter" pour permettre à quiconque de récupérer ce code en utilisant ses propres données de recensement. + generate_access_codes: + button: Générer des codes d'accès au vote + callout: Vous pouvez maintenant procéder à la génération des codes d'accès. N'oubliez pas qu'après la génération des codes d'accès, vous ne pourrez plus modifier le recensement. + confirm: Si vous continuez, vous ne pourrez plus modifier le recensement. + info_message_all: "Toutes les lignes ont été importées avec succès depuis le fichier %{file} (%{raw_count} sur %{data_count})." + info_message_warn: Veuillez vérifier qu'aucune donnée n'est manquante, car %{data_count} enregistrements ont été créés et le fichier téléchargé %{file} a %{raw_count} lignes. + launch_error: Problème lors du lancement de la génération des codes d'accès + launch_success: Génération de codes lancée. + start_over: Veuillez supprimer le recensement actuel et recommencer avec un fichier CSV correct avec des lignes valides. + generating_access_codes: + info_message: "Veuillez patienter, les codes d'accès au vote sont en cours de génération (cela peut prendre plusieurs minutes)..." + new: + file_help: + explanation: 'Instructions concernant le fichier :' + message_1: Seuls les fichiers CSV (.csv) sont autorisés. + message_2: Le séparateur de colonnes doit être un point-virgule (";"). + has_ballot_styles_message: Vous avez mis en place des styles de bulletin. Assurez-vous que le champ "%{ballot_style_code_header}" dans le CSV correspond au code du style de bulletin désiré. + info_message: "Il n'y a pas encore de recensement. Veuillez utiliser le formulaire ci-dessous pour le créer en important un fichier CSV." + missing_ballot_styles_message: 'Il n''y a pas encore de style de bulletin pour ce vote. Si vous souhaitez avoir des questions conditionnelles (ex : présenter à l’électeur des questions différentes en fonction de son quartier/ville de résidence), vous devez configurer les styles de bulletin avant d''importer le recensement. Si vous voulez présenter les mêmes questions à tous les électeurs, vous pouvez passer à la procédure d''import du recensement.' + submit: Valider le fichier csv + title: Créer le recensement + show: + heading: Recensement de l'espace de vote + upload_info: + csv_example_with_ballot_style: 'Un exemple du fichier avec styles de bulletin :' + csv_example_without_ballot_style: 'Un exemple du fichier sans styles de bulletin :' + csv_header_after: Ne pas inclure le dernier champ ("%{ballot_style_code_header}") si vous n'avez pas besoin de styles de bulletin/questions conditionnelles + csv_header_before: 'Le fichier de recensement doit être un fichier CSV avec l''en-tête suivante :' + document_types: + identification_number: Numéro d'identification + passport: Passeport + export_mailer: + access_codes_export: + click_button: 'Cliquez sur le lien suivant pour télécharger les données des codes d''accès.
    Le fichier sera disponible jusqu''au %{date}.
    Vous aurez besoin de 7-Zip (pour Windows), Keka (pour MacOS) ou PeaZip (pour Linux) pour l''ouvrir. Mot de passe : %{password}' + download: Télécharger + subject: L'export des codes d'accès au vote pour %{voting_title} est disponible + vote_flow: + already_voted_in_person: Ce participant a déjà voté en personne et n'a plus le droit de voter. + datum_not_found: Les données fournies ne correspondent à aucun électeur. + content_blocks: + highlighted_votings: + name: Élections mises en avant + landing_page: + polling_stations: + heading: Bureaux de vote + no_polling_stations: Il n'y a pas encore de bureau de vote. + monitoring_committee_members: + actions: + confirm_destroy: Êtes-vous sûr? + destroy: Supprimer + new: Nouveau membre + title: Actions + pages: + home: + highlighted_votings: + active_spaces: Votations en cours + see_all_spaces: Voir toutes les votations + polling_officer_zone: + closures: + back_to_polling_stations: Retourner aux bureaux de vote + certify: + add_photos: Ajouter des photos + edit_photos: Modifier les photos + error: Une erreur est survenue lors de la mise en place du certificat, veuillez réessayer. + heading: Recomptage des votes - Télécharger un certificat + info_text: Veuillez télécharger une photo du certificat de fermeture électorale. + submit: Télécharger le certificat + success: Le certificat à été téléchargé avec succès. + upload_photos: Télécharger une photo du certificat de clôture électoral + completed: + sub_heading: Ce recomptage a été certifié et ne peut plus être modifié. + create: + error: Une erreur s'est produite lors de la création de la fermeture, veuillez réessayer plus tard. + success: Fermeture créée avec succès. + destroy: + error: Une erreur s'est produite lors de la suppression de la clôture. + success: Clôture supprimée avec succès. + edit: + confirm_start_over: Cela supprimera le nombre total de votes et les notes jointes. Êtes-vous sûr(e) ? + heading: Recomptage des votes - Comptage des réponses + info_text: Veuillez détailler le nombre total de réponses pour chaque question. Ce nombre doit correspondre au total de réponses fourni lors de l'étape précédente (%{total} réponses totales). + modal_ballots_results_count_error: + blank: Le total attendu des votes blancs est %{expected} mais la somme des questions vides est %{current}. + close_modal: Fermer + info_text: Le nombre total de bulletins ne correspond pas au nombre total d'enveloppes. Veuillez vérifier le nombre total de bulletins. + title: Le total des bulletins ne correspond pas + total: Le total attendu est %{expected} mais la somme des bulletins valides, blancs et nuls est %{current}. + valid: Le total attendu des votes blancs est %{expected} mais la somme des questions vides est %{current}. + save_recount: Enregistrer le comptage + start_over: S'il y a une erreur dans le nombre total, vous pouvez tout supprimer et recommencer. + total_ballots: Total des votes + total_blank_ballots: Nombre total de bulletins blanc + total_null_ballots: Nombre total de bulletins nuls + total_valid_ballots: Nombre total de bulletins valides + new: + election: 'Élection:' + heading: Recomptage des votes + info_text: 'Veuillez introduire le nombre total de bulletins de vote (enveloppes) recomptés dans ce Bureau de vote:' + modal_ballots_count_error: + btn_validate_total: Valider le nombre total de bulletins + info_explanation_text: 'Veuillez examiner le nombre total de bulletins de vote. Si le nombre total est incorrect, vous devez fournir une explication pour le comité de suivi :' + info_text: Le nombre total de bulletins de vote ne correspond pas au nombre de personnes ayant voté dans ce bureau de vote. + message_for_monitoring_committee: Message pour le comité de suivi + review_recount: Revoir le comptage + text_area_placeholder: Veuillez saisir votre message + title: Le total des enregistrements ne correspond pas + total_ballots: 'Nombre total de bulletins :' + total_people: 'Nombre total de personnes :' + polling_station: 'Bureau de vote :' + submit: Vérifier le nombre total + total_ballots_count: Nombre de bulletins + show: + edit_count_votes: Mauvais nombres ? Vous pouvez toujours les modifier. + heading: Recomptage des votes + sub_heading: Le recomptage doit être fermé en téléchargeant un certificat. Une fois cela fait, le recomptage sera scellé et ne pourra plus être modifié. + sign: + cancel: Annuler + check_box: Après vérification, ce certificat est identique au certificat physique de clôture des élections + confirm: Ok, continuer + error: Une erreur est survenue, veuillez réessayer. + heading: Recomptage des voix - Signer la fermeture + info_text: Cette action est irréversible. Si vous continuez, vous ne pourrez plus modifier les informations. + submit: Signer la fermeture + success: Fermeture signée avec succès. + update: + error: Une erreur s'est produite lors de la mise à jour des résultats de fermeture, veuillez réessayer plus tard. + success: Résultats de fermeture mis à jour avec succès. + in_person_votes: + complete_voting: + available_answers: 'Réponses disponibles:' + census_verified: Ce participant n'a pas encore voté en personne. + census_verified_with_online_vote: Ce participant a déjà voté en ligne. S'il vote en personne, les votes précédents seront invalidés et ce sera le vote définitif. + complete_voting: Vote terminé + identify_another: Identifier un autre participant + questions_title: 'Ils/elles ont le droit de voter sur les questions suivantes :' + questions_title_voted: 'Le participant a déjà voté en ligne et a le droit de voter dans les questions suivantes :' + voted: Le participant a voté + create: + error: Le vote n'a pas été enregistré. Veuillez réessayer. + in_person_form: + census_not_present: Ce participant n'est pas répertorié dans le recensement. + census_not_present_description: Elle doit se rendre au bureau des plaintes du recensement ou contacter le support. + date_of_birth: Date de naissance + day: Jour + day_placeholder: JJ + document_number: Numéro de document + document_number_placeholder: Numéro de carte d'identité + month: Mois + month_placeholder: MM + select: Sélectionnez le type de document + title: 'Sélectionnez le type de document et entrez le numéro de document du participant :' + validate_document: Valider le document + year: Année + year_placeholder: AAAA + new: + back: Retourner aux bureaux de vote + title: Identifier et vérifier un participant + show: + back: Retourner aux bureaux de vote + title: En attente de l'enregistrement du vote en personne + update: + error: Une erreur est survenue lors de l'enregistrement du vote. Veuillez réessayer. + success: + accepted: Le vote a été enregistré avec succès. + rejected: Le vote n'a pas été accepté par le Tableau d'Affichage. Veuillez contacter l'administrateur du système. + verify_document: + census_present: Ce participant n'est pas répertorié dans le recensement. + name: Nom + title: 'Vérifiez que les données suivantes sont correctes:' + verify_document: Vérifier le document + menu: + polling_officer_zone: Zone des responsables du scrutin + polling_officers: + index: + polling_officer_role_description: Vous avez été assigné à agir en tant qu’officier du Bureau de vote (Président ou Gestionnaire) sur certaines des élections ayant lieu sur cette plateforme. + polling_station: + address: Adresse + count_votes: Nombre de votes + election: Élection + identify_person: Identifier une personne + name: Nom + no_polling_stations: Vous n'êtes pas encore assigné à un Bureau de vote. + role: Votre rôle + show_closure: Voir la fermeture + title: Bureaux de vote + voting: Élection + polling_officers: + actions: + confirm_destroy: Êtes-vous sûr? + destroy: Supprimer + new: Nouvel accesseur + title: Actions + roles: + manager: Directeur + president: Président + unassigned: Non assigné + polling_station_closure_certificate: + current_certificate: 'Certificat actuel :' + polling_station_closure_recount: + nota_option: Vide / Aucune des options ci-dessus + polling_officer_notes: 'Notes de l''accesseur :' + polling_officer_notes_blank: Il n'y a aucune note + recount_summary: 'Bilan du recomptage:' + signed: Signé + total_ballots: 'Nombre total de bulletins :' + total_blank_ballots: 'Nombre total de bulletins blanc :' + total_null_ballots: 'Nombre total de bulletins nuls :' + total_valid_ballots: 'Nombre total de bulletins valides :' + polling_stations: + actions: + confirm_destroy: Êtes-vous sûr(e) ? + destroy: Supprimer + edit: Modifier + new: Nouveau bureau de vote + title: Actions + votings: + access_code_modal: + email: Envoyer par e-mail à %{email} + info: Un code d'accès est nécessaire pour participer. Si vous ne l'avez pas reçu par courrier, un nouveau code peut vous être envoyé. + no_email: Aucun email disponible + no_sms: Aucun numéro de téléphone disponible + sms: Envoyer par SMS à %{sms} + title: Obtenir un code d’accès + check_census: + check_status: Vérifiez le statut + description: Ici, vous avez la possibilité de vérifier vos données de recensement pour savoir si vous avez le droit de participer à ce vote. Vous devriez déjà avoir un code d'accès, mais si vous l'avez perdu, vous pouvez le demander à nouveau, lorsque vos données sont correctes. + error: + info: 'Veuillez réessayer. Si vous pensez que les données du système sont incorrectes, vous pouvez les signaler ici : %{census_contact_information}.' + title: Les données que vous avez saisies ne sont pas dans le recensement pour ce vote + form_title: 'Remplissez le formulaire suivant pour vérifier vos données de recensement:' + invalid: Un problème est survenu lors de la vérification du recensement. + success: + access_link: par email. + access_link_with_sms: par SMS ou e-mail. + info: Vous devriez avoir reçu votre code d'accès par courrier postal. Si vous ne l'avez pas reçu, vous pouvez le demander ici + title: Vos données de recensement sont correctes! + title: Puis-je voter ? + check_fields: + date_of_birth: Date de naissance + day: Jour + day_placeholder: JJ + document_number: Numéro de document + document_number_placeholder: Numéro de carte d'identité + document_type: Type de document + month: Mois + month_placeholder: MM + postal_code: Code postal + postal_code_placeholder: Numéro de code postal + select: Sélectionnez le type de document + year: Année + year_placeholder: AAAA + count: + title: + one: "%{count} élection" + other: "%{count} élections" + elections_log: + description: Le journal des élections vous montrera toutes les informations pertinentes sur chaque vote. Par exemple, le statut de la cérémonie de génération des clés ou du dépouillement ou si les résultats sont déjà publiés. Cliquez sur l'élection à propos de laquelle vous voulez des informations. + title: Journal de l'élection + filters: + active: Actifs + all: Tous + date: Date + finished: Terminées + search: Rechercher + upcoming: À venir + index: + no_votings: Aucun vote ne correspond à votre recherche. + only_finished: À l'heure actuelle, il n'y a pas de votes planifiés, mais vous trouverez ici une liste de tous les votes passées. + title: Votations + login: + access_code: Code d'accès + access_code_placeholder: Code d'accès + ask_for_a_new_one: Demander un nouveau. + dont_have_access_code: Vous n'avez pas de code d'accès? + form_title: 'Remplissez le formulaire suivant pour accéder au vote :' + start_voting: Commencer à voter + title: Je m'identifie avec mes données de recensement de vote + no_census_contact_information: Il n'y a pas encore d'informations de contact. + orders: + label: 'Trier les votations par:' + random: Aléatoire + recent: Les plus récentes + send_access_code: + invalid: Un problème est survenu lors de l'envoi du code d'accès. + success: Votre code d'accès a été envoyé avec succès. + show: + title: À propos de ce vote + votings_m: + badge_name: + finished: Passées + ongoing: En cours + upcoming: À venir + unspecified: Non précisées + voting_type: + hybrid: Hybride + in_person: En présentiel + online: En ligne + layouts: + decidim: + voting_navigation: + check_census: Puis-je voter ? + election_log: Journal de l'élection + votings: + index: + promoted_votings: Votations mises en avant + promoted_voting: + vote: Voter diff --git a/decidim-elections/config/locales/ga-IE.yml b/decidim-elections/config/locales/ga-IE.yml new file mode 100644 index 00000000..179118c8 --- /dev/null +++ b/decidim-elections/config/locales/ga-IE.yml @@ -0,0 +1,322 @@ +ga: + activemodel: + attributes: + answer: + image: Íomhá + title: Teideal + election: + title: Teideal + question: + title: Teideal + activerecord: + models: + decidim/elections/question: + one: Ceist + two: Ceisteanna + few: Ceisteanna + many: Ceisteanna + other: Ceisteanna + decidim: + admin: + filters: + role_eq: + label: Ról + values: + manager: Bainisteoir + signed_eq: + label: Sínithe + values: + 'false': Sínithe + components: + elections: + actions: + vote: Vótáil + elections: + actions: + destroy: Scrios + edit: Eagar + preview: Réamhamharc + publish: Foilsigh + title: Gníomhartha + admin: + answers: + index: + title: Freagraí + menu: + trustees: Iontaobhaithe + models: + answer: + name: Freagra + questions: + index: + title: Ceisteanna + steps: + created: + trustees: Iontaobhaithe + results_published: + answer: Freagra + question: Ceist + result: Toradh + submit: Deimhnigh + tally_ended: + answer: Freagra + question: Ceist + result: Toradh + vote_stats: + voters: Vótálaithe + votes: Vótaí + trustees_participatory_spaces: + actions: + disable: Dhíchumasaigh + index: + title: Iontaobhaithe + election_m: + badge_name: + finished: Críochnaithe + ongoing: Gníomhach + upcoming: Le teacht + end_date: Deireadh + footer: + view: Amharc + vote: Vótáil + label: + date: Dátaí + start_date: Ag Tosú + elections: + election_log: + download: Íoslódáil + results_title: Torthaí + started: Tosaithe + filters: + active: Gníomhach + all: Uile + finished: Críochnaithe + upcoming: Le teacht + show: + action_button: + change_vote: Athraigh do vóta + vote: Tosaigh ag vótáil + vote_again: Vótáil arís + preview: Réamhamharc + verify: + already_voted: Ar vótáil tú cheana? + models: + answer: + fields: + proposals: Moltaí + title: Teideal + votes: Vótaí + election: + fields: + end_time: Deireadh ag + title: Teideal + question: + fields: + answers: Freagraí + title: Teideal + trustees_participatory_space: + fields: + email: Seoladh ríomhphoist + name: Ainm + status: Stádas + orders: + older: Níos Sine + recent: Is Déanaí + trustee_zone: + elections: + key_ceremony_steps: + back: Ar Ais + list: + status: Stádas + task: Tasc + start: Tosaigh + status: + pending: Ar feitheamh + trustees: + show: + elections: + list: + action_required: + 'false': 'Níl' + bb_status: Stádas + identification_keys: + cancel: Cealaigh + submit: Deimhnigh + votes: + confirm: + confirm: Deimhnigh + edit: eagar + nota_option: Bán + modal: + close: Dún + verify: + form: + submit: Seiceáil + voting_step: + back: Ar Ais + continue: I ndiaidh + menu: + votings: Vótála + statistics: + votings_count: Vótála + votings: + admin: + ballot_styles: + edit: + update: Nuashonraigh + index: + actions: + destroy: Scrios + edit: Eagar + title: Gníomhartha + new: + create: Cruthaigh + menu: + votings: Vótála + votings_submenu: + attachment_collections: Fillteáin + attachment_files: Comhaid + attachments: Ceangaltáin + census: Móráireamh + monitoring_committee_members: Baill + models: + ballot_style: + fields: + code: Cód + monitoring_committee_member: + fields: + email: Seoladh ríomhphoist + name: Ainm + polling_officer: + fields: + email: Seoladh ríomhphoist + name: Ainm + polling_station: + fields: + address: Seoladh + title: Teideal + voting: + fields: + title: Teideal + monitoring_committee_election_results: + actions: + title: Gníomhartha + view: Amharc + results: + totals: Iomlána + monitoring_committee_members: + new: + create: Cruthaigh + monitoring_committee_polling_station_closures: + actions: + title: Gníomhartha + view: Amharc + closures: + signed: Sínithe? + edit: + monitoring_committee_notes: Ráitis + monitoring_committee_verify_elections: + index: + download: Íoslódáil + polling_officers: + index: + role_manager: bainisteoir + new: + create: Cruthaigh + polling_stations: + new: + create: Cruthaigh + titles: + votings: Vótála + votings: + actions: + destroy: Scrios + edit: + update: Nuashonraigh + form: + title: Teideal + voting_type: + hybrid: Hibrid + online: Ar líne + new: + create: Cruthaigh + census: + export_mailer: + access_codes_export: + download: Íoslódáil + monitoring_committee_members: + actions: + destroy: Scrios + title: Gníomhartha + polling_officer_zone: + closures: + edit: + modal_ballots_results_count_error: + close_modal: Dún + sign: + cancel: Cealaigh + in_person_votes: + in_person_form: + day: Lá + day_placeholder: LL + month: Mí + month_placeholder: MM + year: Bliain + year_placeholder: BBBB + verify_document: + name: Ainm + polling_officers: + index: + polling_station: + address: Seoladh + name: Ainm + role: Do ról + voting: Vótáil + polling_officers: + actions: + destroy: Scrios + title: Gníomhartha + roles: + manager: Bainisteoir + polling_station_closure_recount: + signed: Sínithe + polling_stations: + actions: + destroy: Scrios + edit: Eagar + title: Gníomhartha + votings: + check_fields: + day: Lá + day_placeholder: LL + month: Mí + month_placeholder: MM + postal_code: Cód poist + postal_code_placeholder: Cód poist + year: Bliain + year_placeholder: BBBB + filters: + active: Gníomhach + all: Uile + finished: Críochnaithe + search: Cuardaigh + upcoming: Le teacht + index: + title: Vótála + login: + start_voting: Tosaigh ag vótáil + orders: + random: Randamach + votings_m: + badge_name: + finished: Críochnaithe + ongoing: Leanúnach + upcoming: Le teacht + voting_type: + hybrid: Hibrid + online: Ar líne + layouts: + decidim: + votings: + promoted_voting: + vote: Vótáil diff --git a/decidim-elections/config/locales/gl.yml b/decidim-elections/config/locales/gl.yml new file mode 100644 index 00000000..adf6d019 --- /dev/null +++ b/decidim-elections/config/locales/gl.yml @@ -0,0 +1,545 @@ +gl: + activemodel: + attributes: + answer: + description: Descrición + image: Imaxe + proposals: Propostas relacionadas + title: Título + election: + description: Descrición + end_time: A votación remata ás + start_time: A votación comeza ás + title: Título + question: + title: Título + activerecord: + models: + decidim/elections/answer: + one: Resposta + other: Respostas + decidim/elections/question: + one: Pregunta + other: Preguntas + decidim/votings/census/dataset: + one: Conxunto de datos + other: Conxuntos de datos + decidim/votings/census/datum: + one: Referencias + other: Datos + decidim/votings/voting: + one: Votación + other: Votacións + decidim: + admin: + filters: + signed_eq: + label: Asinado + values: + 'false': Asinado + 'true': Sen asinar + validated_eq: + label: Validado + components: + elections: + actions: + vote: Votar + elections: + actions: + confirm_destroy: Estás certo/a? + edit: Editar + manage_answers: Xestionar respostas + manage_questions: Xestionar preguntas + manage_steps: Xestionar pasos + publish: Publicar + title: Accións + admin: + answers: + edit: + title: Editar resposta + update: Actualizar resposta + index: + title: Respostas + new: + create: Crear resposta + title: Nova resposta + not_selected: Non seleccionada + selected: Seleccionada + elections: + index: + title: Eleccións + new: + create: Crear elección + title: Nova elección + exports: + elections: Eleccións + menu: + trustees: Comisarios + models: + answer: + name: Resposta + questions: + edit: + title: Editar pregunta + update: Actualizar pregunta + index: + title: Preguntas + new: + create: Crear pregunta + title: Nova pregunta + steps: + create_election: + errors: + minimum_answers: As preguntas teñen de ter polo menos dúas respostas. + not_used_trustee: "(non usada)" + key_ceremony: + continue: Continuar + results_published: + answer: Resposta + not_selected: Non seleccionado + question: Pregunta + result: Resultado + selected: Seleccionado + submit: Enviar + tally_ended: + answer: Resposta + not_selected: Non seleccionado + question: Pregunta + result: Resultado + selected: Seleccionado + title: Resultados calculados + tally_started: + mark_as_missing: Marcar coma ausente + tally_completion: O proceso completarase cando todos os comisarios estean activos ou marcados coma ausentes. Son necesarios polo menos %{quorum} comisarios para completar o proceso. + undo_mark_as_missing: Un comisario sinalado como ausente poderá participar antes de se completar o proceso. Poderá proceder como é usual e a marca de ausencia será ignorada. + vote_ended: + text: Rematou a votación. Podes comezar o reconto agora. + admin_log: + election: + create: "%{user_name} creou a elección %{resource_name} para %{space_name}" + delete: "%{user_name} eliminou a elección %{resource_name} para %{space_name}" + end_vote: "%{user_name} terminou o período de votación para a elección %{resource_name} de %{space_name} no Bulletin Board" + publish: "%{user_name} publicou a elección %{resource_name} para %{space_name}" + publish_results: "%{user_name} publicou os resultados da elección %{resource_name} para %{space_name} no Bulletin Board" + setup: "%{user_name} creou a elección %{resource_name} para %{space_name} no Bulletin Board" + trustee: + create: "%{user_name} atribuíu ao usuario %{trustee_user} como comisario" + election_m: + badge_name: + finished: Finalizada + ongoing: Activa + upcoming: Vindeiras + footer: + remaining_time: + one: "%{count} hora %{minutes} minutos para votar." + other: "%{count} horas %{minutes} minutos para votar." + view: Ver + vote: Votar + label: + date: Datas + questions: Preguntas %{count} + unspecified: Sen especificar + elections: + election_log: + complete: Completar + download: Descargar + not_available: Aínda non dispoñíbel + not_created: Sen crear + not_started: Sen iniciar + published: Publicado + results_description: + not_published: Aínda non se publicaron os resultados. + published: Os resultados foron publicados. + results_title: Resultados + started: Iniciada + vote_title: Proceso de votación + filters: + active: Activa + all: Todas + date: Data + finished: Finalizada + upcoming: Vindeiras + preview: + available_answers: 'Respostas dispoñibles:' + description: 'Estas son as preguntas que atoparás non proceso de votación:' + results: + description: 'Estes son os resultados da votación, para cada pregunta:' + percentage: "%{count}%" + selected: Seleccionado + show: + action_button: + change_vote: Troca o teu voto + vote: Comezar votación + vote_again: Votar de novo + callout: + already_voted: Xa votaches nesta elección. Podes trocar o teu voto ou verificalo. + vote_rejected: Non foi posíbel verificar o teu voto. Por favor, faino de novo. + election_log: Rexistro da elección + verify: + already_voted: Xa votaches? + verify_here: Verifica o teu voto aquí. + will_verify: Poderás verificar o teu voto unha vez comece a elección. + feedback: + answer: + invalid: Produciuse un problema ao enviar os teu comentario. + models: + answer: + fields: + selected: Seleccionado + title: Título + votes: Votos + election: + fields: + title: Título + verifiable_results_file_hash: Suma de verificación SHA256 do ficheiro + question: + fields: + answers: Respostas + title: Título + trustees_participatory_space: + fields: + inactive: inactivo + trustee_zone: + elections: + key_ceremony_steps: + back: Volver + list: + status: Estado + task: Tarefa + start: Comezar + status: + pending: Pendente + processing: Procesando + trustees: + show: + elections: + list: + action_required: + 'false': 'Non' + name: Acción requirida? + bb_status: Estado + identification_keys: + submit: Enviar + votes: + ballot_decision: + audit: ( Auditar papeleta ) + back: Comezar o proceso de voto de novo + ballot_hash: 'O identificador da túa papeleta é:' + confirmed: + experience: Como valoras a túa experiencia? + feedback: Dános a túa opinión + header: Voto confirmado + lead: O teu voto foi emitido! + encrypting: + header: Cifrando o voto... + text: Cifrando a papeleta para asegurar que o teu voto é segredo. + failed: + try_again: Tentar de novo + header: + ballot_decision: Enviar ou auditar o teu voto + confirm: Confirma o teu voto + messages: + invalid_token: A túa sesión na cabina de votación non é válida. Tenta votar de novo. + modal: + close: Pechar + onboarding_modal: + create_account: Crear conta + description: Queres crear unha conta nova na plataforma? Poderás participar nos procesos e ser unha parte activa da organización. + no_account: Non, grazas. + title: Es novo na plataforma? + update: + error: Produciuse un problema ao actualizar o estado do voto. Por favor, téntao de novo. + verify: + form: + back: Volver á plataforma + events: + elections: + votes: + accepted_votes: + email_intro: 'O teu voto foi aceptado! Empregando o teu token de votación: %{encrypted_vote_hash}, podes verificar o teu voto aquí.' + email_subject: O teu voto por %{resource_name} foi aceptado. + notification_title: 'O teu voto foi aceptado, verifícao aquí empregando o teu token de votación: %{encrypted_vote_hash}' + votings: + send_access_code: + instruction: 'Este é o teu código de acceso que solicitaches: %{access_code}. Permitirache participar en %{voting}.' + subject: O teu código de acceso para participar en %{voting} + statistics: + elections_count: Eleccións + votings_count: Votacións + votings: + admin: + ballot_styles: + edit: + title: Editar estilo da papeleta + update: Actualizar + form: + election: Elección + questions: Preguntas para este estilo de papeleta + index: + actions: + confirm_destroy: Estás certo/a? + destroy: Eliminar + edit: Editar + title: Accións + title: Estilos de papeletas + new: + create: Crear + title: Crear un estilo de papeleta + menu: + votings_submenu: + ballot_styles: Estilos de papeletas + census: Censo + components: Compoñentes + monitoring_committee: Comité de seguimento + monitoring_committee_election_results: Validar resultados + monitoring_committee_members: Membros + monitoring_committee_polling_station_closures: Validar certificados + monitoring_committee_verify_elections: Verificar eleccións + models: + ballot_style: + fields: + code: Código + monitoring_committee_election_results: + actions: + title: Accións + view: Ver + index: + title: Escolle a elección da que desexes ver os resultados + results: + bulletin_board: Boletín de Información + election_totals: Totais da elección + polling_stations: Puntos de votación + result_types: + blank_answers: Respostas en branco + blank_ballots: Papeletas en branco + null_ballots: Papeletas nulas + total_ballots: Total de papeletas + valid_ballots: Votos válidos + selected: Seleccionado + title: Resultados da elección %{election_title} + totals: Totais + show: + change_election: Trocar de elección + publish_results: Publicar resultados + publishing: Publicando resultados... + update: + rejected: A publicación dos resultados foi rexeitada polo Boletín de Información. Téntao de novo ou contacta co administrador do sistema. + monitoring_committee_polling_station_closures: + actions: + title: Accións + validate: Validar + view: Ver + closures: + change_election: Trocar de elección + signed: Asinado? + title: Puntos de votación da elección %{election_title} + validated: Validado? + edit: + change_polling_station: Volver aos puntos de votación + monitoring_committee_notes: Observacións + title: Os resultados da elección %{election_title} no punto de votación %{polling_station_title} + elections: + title: Escolle unha elección que desexes validar + show: + change_polling_station: Volver aos puntos de votación + monitoring_committee_notes: Obervacións do Comité de seguimento + monitoring_committee_verify_elections: + index: + download: Descargar + how_to_checksum: 'Para asegurarte que o ficheiro que descargaches non foi manipulado ou corrompido durante a descarga, executa a seguinte orde na túa consola e verifica que a saída coincide coa suma de verificación informada arriba:' + how_to_download: Para verificar a elección, descarga o seu ficheiro verificábel da táboa de arriba. + how_to_run_verifier: 'Unha vez descargades o ficheiro e verifiques que é correcto, podes executar o verficador universal. Clona este repositorio e, dende o cartafol raiz, executa a seguinte orde:' + how_to_title: Como verificar a validez dunha elección + not_available: Aínda non dispoñíbel + title: Eleccións + votings: + form: + select_a_voting_type: Por favor, selecciona un tipo de votación + voting_type: + hybrid: Híbrido + in_person: En persoa + online: En liña + census: + admin: + census: + delete: + button: Eliminar todos os datos do censo + export_access_codes: + button: Exportar códigos de acceso da votación + callout: Agora xa podes exportar os códigos de acceso. Só é posíbel facelo unha vez. Así que inicies a exportación, recibirás un correo electrónico coas instrucións en %{email} + confirm: Só podes exportar os códigos de acceso unha vez. Asegúrate de ter acceso á conta de correo %{email}. + freeze: + callout: O censo está conxelado e non pode ser modificado. + generate_access_codes: + button: Xerar códigos de acceso para a votación + callout: Agora xa podes xerar os códigos de acceso. Lembra que unha vez xerados, xa non poderás modificar o censo. + confirm: Se continuas, non poderás modificar o censo. + info_message_all: "Todas as filas foron importadas con éxito dende o ficheiro %{file} (%{raw_count} de %{data_count})." + info_message_warn: Por favor, verifica que non se perderan datos, xa que se teñen creado %{data_count} rexistros e o ficheiro subido %{file} ten %{raw_count} filas. + launch_error: Produciuse un problema ao iniciar a xeración dos códigos de acceso + launch_success: Xeración dos códigos iniciada. + new: + file_help: + explanation: 'Instrucións para o ficheiro:' + message_1: Só están permitidos os ficheiros CSV (.csv). + message_2: O separador entre columnas ten de ser o punto e coma (";"). + info_message: "Aínda non hai censo. Por favor, usa o formulario de abaixo para crear un importando un ficheiro CSV." + title: Crear o censo + show: + heading: Espazo de votación do censo + document_types: + passport: Pasaporte + export_mailer: + access_codes_export: + click_button: 'Fai clic na seguinte ligazón para descargar os datos dos códigos de acceso.
    O ficheiro estará dispoñíbel até o %{date}.
    É preciso que teñas 7-Zip (para Windows), Keka (para macOS) ou PeaZip (para Linux). Contrasinal: %{password}' + download: Descargar + subject: A exportación dos códigos de acceso á votación de %{voting_title} xa están dispoñibles + content_blocks: + landing_page: + polling_stations: + heading: Puntos de votación + no_polling_stations: Aínda non hai puntos de votación. + polling_officer_zone: + closures: + back_to_polling_stations: Volver aos puntos de votación + certify: + error: Produciuse un erro ao anexar o certificado, téntao de novo. + heading: Reconto de votos - Subir certificado + success: Certificado subido con éxito. + create: + error: Produciuse un erro ao crear o peche, téntao de novo. + success: Peche creado con éxito. + edit: + heading: Reconto de votos - reconto de respostas + modal_ballots_results_count_error: + close_modal: Pechar + title: O total de papeletas non coincide + save_recount: Gardar reconto + total_ballots: Total de papeletas + total_blank_ballots: Total papeletas en branco + total_null_ballots: Total papeletas nulas + total_valid_ballots: Total papeletas válidas + new: + election: 'Elección:' + heading: Reconto de votos + info_text: 'Por favor, insire o número total de papeletas (sobres) recontados neste punto de votación:' + modal_ballots_count_error: + btn_validate_total: Validar o reconto total de papeletas + message_for_monitoring_committee: Mensaxe para o comité de seguimento + review_recount: Revisar o reconto + text_area_placeholder: Por favor, escribe a túa mensaxe + title: O total de rexistros non coincide + total_ballots: 'Total de papeletas:' + total_people: 'Total de persoas:' + polling_station: 'Punto de votación:' + submit: Verificar o número total + total_ballots_count: Número de papeletas + show: + heading: Reconto de votos + sign: + cancel: Cancelar + error: Produciuse un erro, téntao de novo. + update: + error: Produciuse un erro ao actualizar os resultados do peche, téntao de novo. + success: Resultados do peche actualizados con éxito. + in_person_votes: + complete_voting: + available_answers: 'Respostas dispoñibles:' + complete_voting: Completar votación + in_person_form: + census_not_present: Este participante non está no censo. + census_not_present_description: Ten de ir á oficina de reclamacións do censo ou contactar coa asistencia técnica. + date_of_birth: Data de nacemento + day: Día + day_placeholder: DD + document_number: Número de documento + document_number_placeholder: Número de ID + month: Mes + month_placeholder: MM + select: Seleccionar o tipo de documento + title: 'Selecciona o tipo de documento e insire o número de documento do participante:' + validate_document: Validar documento + year: Ano + year_placeholder: YYYY + new: + back: Volver aos puntos de votación + title: Identificar e verificar un participante + show: + back: Volver aos puntos de votación + update: + error: Produciuse un erro rexistrando o voto. Por favor, téntao de novo. + success: + accepted: O voto rexistrouse con éxito. + verify_document: + census_present: Este participante está no censo. + name: Nome + title: 'Verifica que os seguintes datos son correctos:' + verify_document: Verificar documento + polling_officers: + index: + polling_officer_role_description: Fuches asignado para actuar como xestor de mesa (Presidente ou Administrador) nalgunhas das eleccións celebradas nesta plataforma. + polling_station: + address: Enderezo + count_votes: Contar votos + election: Elección + identify_person: Identificar a unha persoa + name: Nome + no_polling_stations: Aínda non fuches asignado/a a ningún punto de votación. + role: O teu rol + show_closure: Ver peche + title: Puntos de votación + voting: Votacións + polling_station_closure_recount: + polling_officer_notes: 'Notas do xestor de mesa:' + polling_officer_notes_blank: Non hai notas + recount_summary: 'Resumo do reconto:' + total_ballots: 'Total de papeletas:' + total_blank_ballots: 'Total papeletas en branco:' + total_null_ballots: 'Total papeletas nulas:' + total_valid_ballots: 'Total papeletas válidas:' + votings: + access_code_modal: + email: Enviar a través de correo electrónico a %{email} + no_email: Correo electrónico non dispoñíbel + no_sms: Número de teléfono non dispoñíbel + sms: Enviar a través de SMS a %{sms} + title: Obter un código de acceso + check_census: + check_status: Verificar censo + description: Tes a opción de verificar os datos do censo para saber se tes dereito a participar na votación. Xa terías de ter un código de acceso, porén, se o perdiches podes pedilo de novo se os teus datos son correctos. + form_title: 'Enche o seguinte formulario para verificar os teus datos do censo:' + invalid: Produciuse un problema ao verificar o censo. + success: + access_link_with_sms: a través de SMS ou correo electrónico. + title: Os datos do censo son correctos! + title: Podo votar? + check_fields: + date_of_birth: Data de nacemento + day: Día + day_placeholder: DD + document_number: Número do documento + document_number_placeholder: Número de ID + month: Mes + month_placeholder: MM + postal_code: Código postal + postal_code_placeholder: Número do código postal + select: Seleccionar o tipo de documento + year: Ano + year_placeholder: YYYY + elections_log: + description: O rexistro da elección amosarache toda a información importante sobre cada votación. Por exemplo, o estado da cerimonia de chaves, o reconto ou se os resultados xa están publicados. Fai clic na elección da que desexas a información do rexistro. + title: Rexistro da elección + filters: + date: Data + login: + access_code: Código de acceso + access_code_placeholder: Código de acceso + ask_for_a_new_one: Solicitar un novo. + form_title: 'Enche o seguinte formulario para acceder á votación:' + start_voting: Comezar votación + title: Identificarme cos meus datos do censo da votación + layouts: + decidim: + voting_navigation: + check_census: Podo votar? + election_log: Rexistro da elección diff --git a/decidim-elections/config/locales/gn-PY.yml b/decidim-elections/config/locales/gn-PY.yml new file mode 100644 index 00000000..bd442b0a --- /dev/null +++ b/decidim-elections/config/locales/gn-PY.yml @@ -0,0 +1 @@ +gn: diff --git a/decidim-elections/config/locales/hr-HR.yml b/decidim-elections/config/locales/hr-HR.yml new file mode 100644 index 00000000..f67f33c7 --- /dev/null +++ b/decidim-elections/config/locales/hr-HR.yml @@ -0,0 +1 @@ +hr: diff --git a/decidim-elections/config/locales/hr.yml b/decidim-elections/config/locales/hr.yml new file mode 100644 index 00000000..f67f33c7 --- /dev/null +++ b/decidim-elections/config/locales/hr.yml @@ -0,0 +1 @@ +hr: diff --git a/decidim-elections/config/locales/hu.yml b/decidim-elections/config/locales/hu.yml new file mode 100644 index 00000000..a0e104c8 --- /dev/null +++ b/decidim-elections/config/locales/hu.yml @@ -0,0 +1,228 @@ +hu: + activemodel: + attributes: + answer: + description: Leírás + image: Kép + proposals: Kapcsolódó javaslatok + title: Cím + election: + description: Leírás + end_time: Szavazás véget ér + start_time: Szavazás indul + title: Cím + question: + max_selections: Kiválasztások maximális száma + min_selections: Egyik opció sem a fentiek közül + title: Cím + voting: + banner_image: Banner kép + description: Leírás + end_time: Szavazás vége + promoted: Támogatott + scope_id: Hatókör + start_time: Szavazás kezdete + title: Cím + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Ismételt csatolásra van szükség + election: + attributes: + attachment: + needs_to_be_reattached: Ismételt csatolásra van szükség + activerecord: + models: + decidim/elections/answer: + one: Válasz + other: Válaszok + decidim/elections/election: + one: Választás + other: Választások + decidim/elections/question: + one: Kérdés + other: Kérdések + decidim/votings/census/dataset: + one: Adatkészlet + other: Adatkészletek + decidim/votings/census/datum: + one: Dátum + other: Adatok + decidim/votings/polling_officer: + one: Szavazóbiztos + other: Szavazóbiztosok + decidim/votings/polling_station: + one: Szavazóhely + other: Szavazóhelyek + decidim/votings/voting: + one: Szavazás + other: Szavazások + decidim: + admin: + filters: + officers_assigned_eq: + label: Biztosok + values: + assigned: Hozzárendelt + unassigned: Nem hozzárendelt + role_eq: + label: Szerepkör + values: + manager: Ügyvezető + president: Elnök + unassigned: Nem hozzárendelt + signed_eq: + label: Aláírt + values: + 'false': Aláírt + validated_eq: + label: Érvényesített + components: + elections: + actions: + vote: Szavazás + name: Választások + settings: + global: + announcement: Közlemény + step: + announcement: Közlemény + elections: + actions: + confirm_destroy: Biztos vagy benne? + destroy: Töröl + edit: Szerkeszt + feedback: Szavazó visszajelzése + import: Válaszjavaslatok importálása + manage_answers: Válaszok kezelése + manage_questions: Kérdések kezelése + preview: Előnézet + publish: Közzétesz + title: Műveletek + unpublish: Közzététel visszavonása + admin: + answers: + edit: + title: Válasz szerkesztése + update: Válasz frissítése + index: + title: Válaszok + new: + create: Válasz létrehozása + title: Új válasz + not_selected: Nincs kiválasztva + selected: Kiválasztva + elections: + edit: + update: Választás frissítése + index: + title: Választások + new: + create: Választás létrehozása + title: Új választás + publish: + success: A választás sikeresen publikálásra került. + unpublish: + success: A választás publikálását sikeresen visszavonta. + exports: + elections: Választások + menu: + trustees: Felügyelők + models: + answer: + name: Válasz + proposals_imports: + new: + create: Válaszjavaslatok importálása + select_component: Kérjük, válassz egy komponenst + title: Javaslatok importálása + questions: + edit: + title: Kérdés szerkesztése + update: Kérdés frissítése + index: + title: Kérdések + new: + create: Kérdés létrehozása + title: Új kérdés + steps: + create_election: + trustees: Választási megbízottak + created: + trustees: Felügyelők + key_ceremony: + continue: Folytatás + key_ceremony_ended: + title: Készen áll a kezdésre + tally_started: + continue: Folytatás + mark_as_missing: Hiányzóként megjelölt + tally_completion: A folyamat akkor fejeződik be, ha az összes megbízott aktív vagy hiányzónak van jelölve. A folyamat befejezéséhez legalább %{quorum} megbízottra van szükség. + undo_mark_as_missing: A tévedésből hiányzónak megjelölt megbízott az eljárás befejezése előtt részt vehet. A szokásos módon folytathatja a folyamatot, és a hiányzó jelölést figyelmen kívül hagyják. + elections: + filters: + date: Dátum + show: + action_button: + change_vote: Szavazatának megváltoztatása + vote: Szavazás kezdete + vote_again: Újraszavaz + callout: + already_voted: Már szavaztál ezen a választáson. Megváltoztathatod szavazatod, vagy ellenőrizheted. + vote_rejected: Nem volt lehetséges szavazatod ellenőrzése. Kérjük, dobd be újra. + verify: + already_voted: Már szavaztál? + verify_here: Itt ellenőrizheted szavazatod. + will_verify: A választás megkezdését követően ellenőrizheted szavazatod. + trustee_zone: + trustees: + show: + identification_keys: + submit_legend: A fent ismertetett lépések végrehajtása után, a nyilvános azonosító kulcs szervernek való elküldésével fejezi be a folyamatot. + votes: + modal: + proposal_header: 'Javaslatok:' + onboarding_modal: + description: Szeretnél új fiókot létrehozni a platformon? Képes leszel a folyamatokban részt venni, és aktív részese lehetsz a szervezetnek. + title: Új a platformon? + verify: + form: + back: Vissza a platformra + voting_step: + continue: Következő + events: + elections: + election_published: + email_intro: 'A %{resource_title} című választás már aktív a %{participatory_space_title}-ben. Megtekinthető erről az oldalról:' + email_outro: Azért kapta ezt az értesítést, mert a %{participatory_space_title}-t követi. Az értesítések küldését leállíthatja az előző linkkel. + email_subject: 'A %{resource_title} választás aktív itt: %{participatory_space_title}.' + notification_title: 'A %{resource_title} választás aktív itt: %{participatory_space_title}.' + votings: + admin: + menu: + votings_submenu: + monitoring_committee: Ellenőrző Bizottság + votings: + form: + census_contact_information_help: Ez a kapcsolatfelvételi információ azoknak a résztvevőknek szól, akik a népszámlálással kapcsolatos problémákat szeretnének jelenteni. Ez lehet e-mail cím, kapcsolatfelvételi űrlap egy másik oldalon, kérdőív a látogatók számára, stb. + polling_officer_zone: + closures: + sign: + confirm: Ok, folytassuk + polling_officers: + index: + polling_officer_role_description: Szavazóhelyiségi Tisztnek jelöltek (Elnökként vagy Ügyvezetőként) azért, hogy ezen platformon megtartott választásokon intézkedj. + polling_station: + no_polling_stations: Még nem vagy kijelölve egyetlen szavazóhelyiséghez sem. + role: A te szereped + voting: Szavazás + votings: + check_census: + success: + access_link: e-mailen keresztül. + filters: + date: Dátum + login: + start_voting: Szavazás kezdete diff --git a/decidim-elections/config/locales/id-ID.yml b/decidim-elections/config/locales/id-ID.yml new file mode 100644 index 00000000..8446cbad --- /dev/null +++ b/decidim-elections/config/locales/id-ID.yml @@ -0,0 +1 @@ +id: diff --git a/decidim-elections/config/locales/is-IS.yml b/decidim-elections/config/locales/is-IS.yml new file mode 100644 index 00000000..1fd0783d --- /dev/null +++ b/decidim-elections/config/locales/is-IS.yml @@ -0,0 +1 @@ +is-IS: diff --git a/decidim-elections/config/locales/is.yml b/decidim-elections/config/locales/is.yml new file mode 100644 index 00000000..337c106d --- /dev/null +++ b/decidim-elections/config/locales/is.yml @@ -0,0 +1 @@ +is: diff --git a/decidim-elections/config/locales/it.yml b/decidim-elections/config/locales/it.yml new file mode 100644 index 00000000..03bd0467 --- /dev/null +++ b/decidim-elections/config/locales/it.yml @@ -0,0 +1,1043 @@ +it: + activemodel: + attributes: + answer: + description: Descrizione + image: Immagine + proposals: Proposte correlate + title: Titolo + election: + description: Descrizione + end_time: La votazione termina il + start_time: La votazione inizia il + title: Titolo + question: + max_selections: Numero massimo di selezioni + min_selections: Nessuna delle precedenti opzioni + title: Titolo + voting: + end_time: La votazione termina il + start_time: La votazione inizia il + voting_type: Tipo di votazione + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Necessita di essere riallegato + election: + attributes: + attachment: + needs_to_be_reattached: Necessita di essere riallegato + activerecord: + models: + decidim/elections/answer: + one: Risposta + other: Risposte + decidim/elections/election: + one: Elezione + other: Elezioni + decidim/elections/question: + one: Domanda + other: Domande + decidim/votings/census/dataset: + one: Set di dati + other: Set di dati + decidim/votings/census/datum: + one: Dato + other: Dati + decidim/votings/polling_officer: + one: Scrutatore/trice + other: Scrutatore/trice + decidim/votings/polling_station: + one: Seggio elettorale + other: Seggio elettorale + decidim/votings/voting: + one: Votazione + other: Votazioni + decidim: + admin: + filters: + officers_assigned_eq: + label: Responsabili + values: + assigned: Assegnato + unassigned: Non assegnato + role_eq: + label: Ruolo + values: + manager: Responsabile + president: Presidente/Presidentessa + unassigned: Non assegnato + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Cerca %{collection} per nome/email/nickname o seggio elettorale. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Cerca %{collection} per titolo, indirizzo o nome responsabile/email/nickname. + signed_eq: + label: Firmato + values: + 'false': Firmato + 'true': Non firmato + validated_eq: + label: Convalidato + components: + elections: + actions: + vote: Vota + name: Elezioni + settings: + global: + announcement: Annuncio + step: + announcement: Annuncio + elections: + actions: + confirm_destroy: Sei sicuro? + destroy: Elimina + edit: Modifica + feedback: Feedback del votante + import: Importa proposte in risposte + manage_answers: Gestisci le risposte + manage_questions: Gestisci i quesiti + manage_steps: Gestisci passaggi + preview: Anteprima + publish: Pubblica + title: Azioni + unpublish: Ritira + admin: + answers: + edit: + title: Modifica risposta + update: Aggiorna risposta + index: + title: Risposte + new: + create: Crea risposta + title: Nuova risposta + not_selected: Non selezionato + select: + disable: Deseleziona la risposta + enable: Segna la risposta come selezionata + selected: Selezionato + elections: + edit: + title: Modifica elezione + update: Aggiorna elezione + index: + no_bulletin_board: Non è configurato alcun Bulletin Board server che sia necessario per usare questo modulo. Questa attività dovrebbe essere eseguita dall'amministratore di sistema. + title: Elezioni + new: + create: Crea elezione + title: Nuova elezione + publish: + success: L'elezione è stata pubblicata con successo. + unpublish: + success: L'elezione è stata ritirata con successo. + exports: + elections: Elezioni + feedback_form_answers: Risposte al modulo di feedback + menu: + trustees: Garanti + models: + answer: + name: Risposta + proposals_imports: + new: + create: Importa proposte a cui rispondere + no_components: Non vi sono altre componenti di proposte in questo spazio partecipativo per importare le proposte in risposte. + select_component: Si prega di selezionare un componente + title: Importa proposte + questions: + edit: + title: Modifica quesito + update: Aggiorna quesito + index: + title: Quesiti + new: + create: Crea quesito + title: Nuovo quesito + steps: + create_election: + errors: + max_selections: Le domande non hanno un valore corretto per la quantità di risposte + minimum_answers: Le domande devono avere almeno due risposte. + minimum_questions: Le elezioni devono avere almeno un quesito. + published: L'elezione non è pubblicata. + trustees_number: Lo spazio partecipativo deve avere almeno %{number} fiduciari con chiave pubblica. + invalid: Si è verificato un errore durante la creazione di questa elezione + no_trustees: Non ci sono Fiduciari configurati per questo spazio partecipativo + not_used_trustee: "(non utilizzato)" + public_key: + 'false': non ha una chiave pubblica + 'true': ha una chiave pubblica + requirements: + max_selections: Tutti i quesiti hanno un valore corretto per massimo di risposte. + minimum_answers: Ogni quesito ha almeno 2 risposte. + minimum_questions: L'elezione ha almeno 1 quesito. + published: L'elezione è pubblicata. + time_before: La configurazione viene eseguita almeno %{hours} ore prima dell'inizio delle elezioni. + trustees_number: Lo spazio partecipativo ha almeno %{number} fiduciari con chiave pubblica. + submit: Imposta elezione + title: Imposta elezione + trustees: Fiduciari Delle Elezioni + created: + submit: Iniziare la cerimonia della chiave + title: Elezione creata + trustees: Garanti + key_ceremony: + continue: Continua + title: Cerimonia chiave + key_ceremony_ended: + errors: + time_before: La votazione sta per cominciare. Devi aspettare fino a %{hours} ore prima dell'ora di inizio (%{start_time}) per poter votare. + requirements: + time_before: L'elezione inizierà presto. È possibile iniziare il periodo di votazione manualmente, o sarà avviato automaticamente prima dell'ora di inizio, a %{start_time}. + submit: Inizio del periodo di votazione + title: Pronti via + processing: Processando... + results_published: + answer: Risposta + not_selected: Non selezionato + question: Domanda + result: Risultato + selected: Selezionato + submit: Invia + title: Risultati pubblicati + tally_ended: + answer: Risposta + not_selected: Non selezionato + question: Domanda + result: Risultato + selected: Selezionato + submit: Pubblica i risultati + title: Risultati calcolati + vote: + errors: + time_after: Le elezioni sono ancora in corso. Devi aspettare fino all´ora (%{end_time}) per terminare il periodo di votazione. + requirements: + time_after: Le elezioni sono terminate. Puoi terminare manualmente il periodo di votazione, oppure si concluderà automaticamente tra qualche minuto. + submit: Fine del periodo di votazione + title: Periodo di votazione + vote_ended: + submit: Inizia conteggio + text: Il voto è terminato. È possibile iniziare il conteggio ora. + title: Periodo di votazione terminato + vote_stats: + no_vote_statistics_yet: Ancora nessuna statistica di voto + title: Statistiche di voto + voters: Votanti + votes: Voti + trustees_participatory_spaces: + actions: + disable: Disattiva + enable: Considerare + form: + select_user: Selezionare utente + index: + title: Garanti + new: + create: Crea garante + title: Nuovo Garante + admin_log: + election: + create: "%{user_name} ha creato l'elezione %{resource_name} di %{space_name}" + delete: "%{user_name} ha eliminato l'elezione %{resource_name} di %{space_name}" + end_vote: "%{user_name} ha terminato il periodo di votazione per l'elezione %{resource_name} di %{space_name} sul Bollettino" + publish: "%{user_name} ha pubblicato l'elezione %{resource_name} di %{space_name}" + publish_results: "%{user_name} ha pubblicato i risultati per l'elezione %{resource_name} di %{space_name} sul Bollettino" + setup: "%{user_name} ha creato l'elezione %{resource_name} di %{space_name} sul Bollettino" + start_key_ceremony: "%{user_name} ha iniziato la cerimonia chiave per l'elezione %{resource_name} di %{space_name} sul Bollettino" + start_tally: "%{user_name} ha iniziato lo spoglio per l'elezione %{resource_name} di %{space_name} sul Bollettino" + start_vote: "%{user_name} ha iniziato il periodo di votazione per l'elezione %{resource_name} di %{space_name} sul Bollettino" + unpublish: "%{user_name} ha annullato la %{resource_name} di %{space_name} elezione" + update: "%{user_name} ha aggiornato l'elezione %{resource_name} di %{space_name}" + trustee: + create: "%{user_name} ha assegnato l'utente %{trustee_user} come fiduciario" + election_m: + badge_name: + finished: Completata + ongoing: Attiva + upcoming: Imminente + end_date: Fine + footer: + remaining_time: + one: "%{count} ora %{minutes} minuti rimanenti per votare." + other: "%{count} ore %{minutes} minuti rimanenti per votare." + view: Visualizza + vote: Vota + label: + date: Date + questions: Domande %{count} + start_date: Inizio + unspecified: Non specificato + elections: + count: + elections_count: + one: "%{count} elezione" + other: "%{count} elezioni" + election_log: + chained_hash: Hash incatenato di questo messaggio + complete: Completa + creation_description: + complete: Le elezioni sono state create e sono state registrate con successo sul Bollettin Board. + not_created: Le elezioni non sono ancora state create. + creation_title: Elezioni create + description: Questo è il registro elettorale in cui è possibile controllare lo stato di ogni passaggio, ad es. quando le elezioni sono state create, se il processo di conteggio è stato completato, e quando le elezioni sono state chiuse. + download: Download + key_ceremony_description: + complete: La cerimonia delle chiavi è completata. Ogni garante ha chiavi valide e ha scaricato le chiavi di backup necessarie. + not_started: La cerimonia delle chiavi non è ancora iniziata. + started: La cerimonia delle chiavi è iniziata ma non è ancora completata. + key_ceremony_title: Cerimonia della Chiave + not_available: Non ancora disponibili + not_created: Non creato + not_ready: Non pronte + not_started: Non iniziate + published: Pubblicate + results_description: + not_published: I risultati non sono ancora stati pubblicati. + published: I risultati sono stati pubblicati. + results_title: Risultati + started: Iniziate + tally_description: + finished: Il processo di conteggio è terminato. + not_started: Il processo di conteggio non è ancora iniziato. + started: Il processo di conteggio è cominciato. + tally_title: Conteggio + title: Registro delle elezioni + unpublished: Non pubblicato + verifiable_results: + checksum: 'Somma di controllo del file SHA256:' + description: + ready: 'Qui, si ha la possibilità di verificare l''elezione. In primo luogo, è necessario scaricare il file e assicurarsi che non è sia danneggiato. Per farlo, eseguire il seguente comando e verificare che il risultato corrisponda alla somma di controllo:' + how_to_verify: 'Una volta scaricato il file e assicuratosi che sia ok, è possibile procedere con l''esecuzione del verificatore universale. Clona questo repository e, dalla cartella root, esegui il seguente comando:' + title: Verifica i risultati delle elezioni + verifiable_file: 'File elettorale verificabile:' + verify: Verifica l'elezione + vote_description: + finished: Il processo di votazione è terminato. + not_started: Il processo di votazione non è ancora iniziato. + started: Il processo di votazione è iniziato. + vote_title: Processo di votazione + filters: + active: Attiva + all: Tutti + finished: Completata + upcoming: Imminente + preview: + available_answers: 'Risposte disponibili:' + description: 'Queste sono le domande che troverete nel processo di votazione:' + title: Domande elettorali + results: + description: 'Questi sono i risultati della votazione, per ogni domanda:' + percentage: "%{count}%" + selected: Selezionato + title: Risultati delle elezioni + votes: + one: "%{count} voto" + other: "%{count} voti" + show: + action_button: + change_vote: Cambia il tuo voto + vote: Inizia a votare + vote_again: Vota di nuovo + callout: + already_voted: Hai già espresso il tuo voto in questa elezione. Puoi cambiare il tuo voto o verificarlo. + pending_vote: Il tuo voto è stato espresso sul server. + vote_rejected: Non è stato possibile verificare il tuo voto. Per favore esprimi il tuo voto di nuovo. + election_log: Registro delle elezioni + preview: Anteprima + verify: + already_voted: Hai già votato? + verify_here: Verifica il tuo voto qui. + will_verify: Sarete in grado di verificare il vostro voto una volta che le votazioni saranno iniziate. + voting_period_status: + finished: La votazione è iniziata il %{start_time} ed è terminata il %{end_time} + ongoing: 'Voto attivo fino a: %{end_time}' + upcoming: La votazione inizia il %{start_time} + feedback: + answer: + invalid: Si è verificato un errore durante l'invio del tuo feedback. + success: Feedback inviato con successo. + models: + answer: + fields: + proposals: Proposte + selected: Selezionato + title: Titolo + votes: Voti + election: + fields: + bb_status: Stato scheda bollettino + end_time: Finisce il + start_time: Inizia il + title: Titolo + verifiable_results_file_hash: Codici di controllo del file SHA256 + verifiable_results_file_url: File elettorale verificabile + question: + fields: + answers: Risposte + max_selections: Max. selezioni + title: Titolo + trustees_participatory_space: + fields: + considered: considerato + email: Email + inactive: inattivo + name: Nome + notification: Notifica inviata a + public_key: Chiave Pubblica + status: Stato + orders: + label: Ordina le elezioni per + older: Meno recente + recent: Recenti + trustee_zone: + elections: + backup_modal: + description: Questa elezione è in corso di creazione nel Bollettino. È molto importante che ogni fiduciario che partecipa crei una copia di backup di queste chiavi e le memorizzi in un luogo sicuro. Dopo di che, il processo continuerà. + download_election_keys: Scarica le chiavi + title: Backup delle chiavi elettorali per %{election} + key_ceremony_steps: + back: Indietro + description: Questa elezione è in fase di creazione nel Bollettino. Per completare questo processo, è necessaria la vostra partecipazione come fiduciario. + keys: + create_election: Generazione chiavi + key_ceremony: + joint_election_key: Generazione chiave congiunta + step_1: Pubblicazione delle chiavi + list: + status: Stato + task: Attività + process_warning: Una volta avviato il processo, non si dovrebbe uscire da questa pagina fino alla fine dello stesso. Ci vorranno diversi minuti, poiché tutti i Garanti dovrebbero essere collegati per completarlo. + start: Avvia + status: + completed: Completata + pending: In attesa + processing: Elaborazione in corso + title: Creare chiavi elettorali per %{election} + restore_modal: + description: Il Bulletin Board ha ricevuto informazioni da parte tua in quanto Garante di queste elezioni. Per continuare il processo, caricare il file di backup generato durante la sessione precedente. + title: Ripristina le chiavi elettorali per %{election} + upload_election_keys: Carica chiavi elettorali + tally_started_steps: + description: I risultati di queste elezioni sono in fase di elaborazione nel Bulletin Board. Per completare questo processo, è necessaria la tua partecipazione come Garante. + keys: + end_tally: Conteggio terminato + tally: + cast: Invio del conteggio + share: Condivisione del conteggio + title: Conteggio di %{election} + menu: + trustee_zone: Zona Garanti + no_bulletin_board: + body: Per questa sezione è necessario un Bulletin Board configurato. Contattare l'Amministratore per maggiori dettagli. + title: Siamo spiacenti, il Bollettin Board non è ancora configurato. + trustees: + show: + elections: + list: + action_required: + 'false': 'No' + name: Azione necessaria? + 'true': Esegui azione + bb_status: Stato + election: Elezione + voting_period: Periodo di votazione + title: Elezioni + identification_keys: + cancel: Annulla + generate: Genera chiavi di identificazione + generate_error: Si è verificato un errore durante la generazione delle chiavi di identificazione. + generate_legend: Devi generare un paio di chiavi di identificazione per partecipare alle elezioni come Garante. + generate_legend_1: Dopo aver premuto il pulsante dovresti scaricare il file con le chiavi di identificazione generate. + generate_legend_2: Copia il file scaricato su un dispositivo USB pulito + generate_legend_4: Fai un'altra copia del file su un altro dispositivo esterno e memorizzarlo in un luogo molto sicuro. + submit: Invia + submit_title: Invia la chiave di identificazione pubblica + title: Codici di identificazione del Garante + upload: Carica i tuoi codici di identificazione + not_supported_browser_title: Aggiorna il browser per fungere da Garante + update: + success: I tuoi codici di identificazione pubblici sono stati salvati con successo. + votes: + ballot_decision: + audit: ( Voto di controllo ) + back: Inizia nuovamente il processo di voto + ballot_hash: 'Il tuo identificativo elettorale è:' + header: 'Il voto è criptato: confermalo o controllalo' + casting: + header: Salvo voto... + text: Il tuo voto viene deposto nell'urna. + confirm: + answer_number: rispondi %{number} + confirm: Conferma + edit: modifica + header: Conferma il tuo voto + intro: Ecco un riassunto del voto che stai per lanciare.
    Per favore conferma il tuo voto o modifica le tue risposte. + nota_option: Vuoto + confirmed: + back: Torna alle elezioni + experience: Com'è stata la tua esperienza? + feedback: Lasciaci un feedback + header: Voto confermato + lead: Il tuo voto è stato inviato! + text: 'Puoi controllare che il tuo voto sia stato aggiunto con successo alla scheda elettorale con il seguente identificatore: %{e_vote_poll_id}' + verify_link: Per controllarlo, copia l'identificatore e incollalo nella pagina di verifica del voto + create: + error: Si è verificato un problema durante l'invio del voto. Per favore, riprova. + encrypting: + header: Cifrando il voto... + text: Il tuo voto viene crittografato per garantirne la segretezza. + failed: + header: Votazione fallita + lead: Il tuo voto non è stato deposto! + text: Qualcosa è andato storto. Riprova di nuovo. + try_again: Riprova + header: + ballot_decision: Invia o controlla il tuo voto + confirm: Conferma il tuo voto + messages: + invalid_token: La tua sessione elettorale non è valida. Prova a votare di nuovo. + not_allowed: In questo momento non ti è permesso di votare a queste elezioni. + modal: + close: Chiuso + proposal_header: 'Proposte:' + new: + answer_choices: Puoi selezionare fino a %{choices} risposte + more_information: Altre informazioni + nota_option: Vuoto/ Nessuno dei precedenti + preview_alert: Questa è un'anteprima della cabina di voto. + question_steps: Domanda %{current_step} di %{total_steps} + selections: "%{selected} di %{max_selections}
    selezioni" + onboarding_modal: + create_account: Crea Account + no_account: No, grazie. + update: + error: Si è verificato un errore durante l'aggiornamento del voto. Si prega di votare di nuovo. + verify: + content: + heading: Verifica il tuo voto + info: Questo verificatore verifica che il tuo voto, identificato con una stringa di testo crittografata, sia stato inviato correttamente e che sia all'interno della scheda elettorale. + error: + header: Voto non trovato! + info: Il codice di voto non è stato trovato nella scheda elettorale %{link}, riprova. + form: + submit: Controlla + vote_identifier: 'Codice identificativo:' + header: + title: Verifica il tuo voto + success: + header: Voto trovato! + voting_step: + back: Indietro + continue: Successivo + events: + elections: + election_published: + email_intro: 'L''elezione %{resource_title} è ora attiva per %{participatory_space_title}. Puoi vederla da questa pagina:' + email_outro: Hai ricevuto questa notifica perché stai seguendo %{participatory_space_title}. È possibile interrompere la ricezione di notifiche seguendo il collegamento precedente. + email_subject: L'elezione di %{resource_title} è ora attiva per %{participatory_space_title}. + notification_title: L'elezione %{resource_title} è ora attiva per %{participatory_space_title}. + trustees: + new_election: + email_intro: Sei stato aggiunto come Garante per le elezioni %{resource_title}. + new_trustee: + email_intro: Un amministratore ti ha aggiunto come Garante di %{resource_name}. Dovresti creare la tua chiave pubblica nella tua zona garanti + email_subject: Sei Garante di %{resource_name}. + votes: + accepted_votes: + email_intro: 'Il tuo voto è stato accettato! Usando il tuo token di voto: %{encrypted_vote_hash}, puoi verificare il tuo voto qui.' + email_subject: Il tuo voto per %{resource_name} è stato accettato. + notification_title: 'Il tuo voto è stato accettato. Verifica il tuo voto qui usando il tuo token di voto: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Sei stata assegnata come %{role} del Seggio Elettorale %{polling_station_name} in %{resource_title}. È possibile gestire il Seggio Elettorale dalla zona dedicata Zona Scrutatrice. + email_outro: Hai ricevuto questa notifica perché ti è stato assegnato il ruolo di %{role} del %{polling_station_name}. + email_subject: Sei %{role} del Seggio Elettorale %{polling_station_name}. + notification_title: Sei %{role} del Seggio Elettorale %{polling_station_name} nella votazione %{resource_title}. + send_access_code: + instruction: 'Ecco il tuo Codice di Accesso che hai chiesto: %{access_code}. Con questo potrai partecipare a %{voting}.' + subject: Il tuo Codice di Accesso per partecipare a %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Una votazione è uno spazio nel quale puoi sottoporre un quesito puntuale a tutti i membri di un'organizzazione, lanciare un invito a partecipare alla votazione stessa, dare vita a un dibattito pro o contro una determinata risposta. Alla data della consultazione potrai esprimere il tuo voto e pubblicare i risultati della votazione.

    Esempi: L'argomento di una votazione può riguardare pressoché ogni aspetto legato a un'organizzazione: ad esempio, modificarne il logo scegliendo tra diverse opzioni, approvare o respingere la proposta di confluire in un'organizzazione più ampia, convalidare o respingere un nuovo piano strategico o il risultato di un gruppo di lavoro oppure stabilire un limite di 1, 2 o 3 mandati per le diverse funzioni in seno all'organizzazione.

    \n" + page: "

    Una votazione è uno spazio nel quale puoi sottoporre un quesito puntuale a tutti i membri di un'organizzazione, lanciare un invito a partecipare alla votazione stessa, dare vita a un dibattito pro o contro una determinata risposta. Alla data della votazione potrai esprimere il tuo voto e pubblicare i risultati della votazione.

    Esempi: L'argomento di una votazione può riguardare pressoché ogni aspetto legato a un'organizzazione: ad esempio, modificarne il logo scegliendo tra diverse opzioni, approvare o respingere la proposta di confluire in un'organizzazione più ampia, convalidare o respingere un nuovo piano strategico o il risultato di un gruppo di lavoro oppure stabilire un limite di 1, 2 o 3 mandati per le diverse funzioni in seno all'organizzazione.

    \n" + title: Cosa sono le votazioni? + menu: + votings: Votazioni + statistics: + elections_count: Elezioni + votings_count: Votazioni + votings: + admin: + ballot_styles: + edit: + title: Modifica stile di voto + update: Aggiorna + form: + election: Elezione + questions: Domande per questo stile di voto + index: + actions: + confirm_destroy: Sei sicura? + destroy: Elimina + edit: Modifica + title: Azioni + associated_census_data: Voci di voto associate + title: Stile di voto + new: + create: Crea + title: Crea stile di voto + content_blocks: + highlighted_votings: + max_results: Quantità massima di elementi da mostrare + index: + published: Pubblicato + unpublished: Non pubblicato + menu: + votings: Votazioni + votings_submenu: + attachment_collections: Cartelle + attachment_files: Files + attachments: Allegati + ballot_styles: Stili di voto + census: Censimento + components: Componenti + landing_page: Pagina iniziale + monitoring_committee: Comitato di Sorveglianza + monitoring_committee_election_results: Convalida Risultati + monitoring_committee_members: Membri + monitoring_committee_polling_station_closures: Convalida Certificati + monitoring_committee_verify_elections: Verifica Elezioni + polling_officers: Scrutatrici/tori + polling_stations: Seggi elettorali + models: + ballot_style: + fields: + code: Codice + monitoring_committee_member: + fields: + email: Email + name: Nome + polling_officer: + fields: + email: Email + name: Nome + polling_station: Seggio elettorale (ruolo) + polling_station: + fields: + address: Indirizzo + polling_station_managers: Gestori + polling_station_president: Presidentessa/Presidente + title: Titolo + voting: + fields: + created_at: Creato il + published: Pubblicato + title: Titolo + monitoring_committee_election_results: + actions: + title: Azioni + view: Visualizza + index: + title: Scegli un elezione di cui vuoi vedere i risultati + results: + bulletin_board: Bacheca + election_totals: Totale delle elezioni + polling_stations: Seggi elettorali + result_types: + blank_answers: Risposte vuote + blank_ballots: Votazioni in bianco + null_ballots: Votazioni nulle + total_ballots: Voti totali + valid_ballots: Voti validi + selected: Selezionato + title: Risultati per l'elezione %{election_title} + totals: Totali + show: + change_election: Cambia elezione + publish_results: Pubblica i risultati + publishing: Pubblicazione risultati... + update: + rejected: La pubblicazione dei risultati in bacheca è stata rifiutata. Riprova o contatta l'amministratore di sistema. + monitoring_committee_members: + form: + existing_user: Partecipante esistente + non_user: Invita un nuovo partecipante + select_user: Cerca per nome, email o nickname + user_type: Tipo di partecipante + index: + title: Comitato di sorveglianza + new: + create: Crea + title: Crea membro del comitato di sorveglianza + monitoring_committee_polling_station_closures: + actions: + title: Azioni + validate: Convalida + view: Visualizza + closures: + change_election: Cambia elezione + signed: Firmato? + title: Seggi elettorali per le elezioni %{election_title} + validated: Convalidato? + edit: + change_polling_station: Torna ai Seggi Elettorali + monitoring_committee_notes: Commenti + title: Risultati dell'elezione %{election_title} nel seggio elettorale %{polling_station_title} + elections: + title: Scegli un'elezione che vuoi convalidare + show: + change_polling_station: Torna ai Seggi Elettorali + monitoring_committee_notes: Osservazioni del comitato di sorveglianza + monitoring_committee_verify_elections: + index: + download: Scarica + how_to_checksum: 'Per assicurarsi che il file scaricato non sia stato danneggiato o manomesso durante il processo di download, eseguire il seguente comando nella console e controllare che il risultato corrisponda alla somma di controllo riportata sopra:' + how_to_download: Per verificare un'elezione, scarica il suo file verificabile dalla tabella qui sopra. + how_to_run_verifier: 'Una volta scaricato il file e assicuratosi che sia ok, è possibile procedere con l''esecuzione del verificatore universale. Clona questo repository e, dalla cartella root, esegui il seguente comando:' + how_to_title: Come verificare la validità di un'elezione + not_available: Non ancora disponibile + title: Elezioni + polling_officers: + form: + existing_user: Partecipante esistente + non_user: Invita un nuovo partecipante + select_user: Cerca per nome, email o pseudonimo + user_type: Tipologia di utente + index: + role_manager: gestori + role_president: presidente/presidentessa + title: Scrutatore/trice + new: + create: Crea + title: Titolo + polling_officers_picker: + choose_polling_officers: Crea responsabile sondaggio + polling_stations: + edit: + title: Modifica la stazione di sondaggio + update: Aggiorna la stazione di sondaggio + form: + address_help: 'Indirizzo: usato da Geocoder per trovare la posizione' + location_help: 'Posizione: messaggio diretto agli elettori che implica il luogo esatto del seggi elettorali' + location_hints_help: 'Suggerimenti di posizione: informazioni aggiuntive. Esempio: il piano dell''edificio in cui si trova la stazione elettorale.' + polling_station_managers_help: 'Dirigenti dei sondaggi: i dirigenti che fungeranno da direttori dei seggi elettorali. Assicurarsi che i dirigenti siano già stati creati negli uffici Elettorali e che non siano già assegnati ad un altro seggi elettorali' + select_president: Selezionare un funzionario dell'ufficio come presidente del seggio elettorale + index: + title: Seggio elettorale + new: + create: Crea + title: Crea un seggio per la votazione + titles: + votings: Votazioni + votings: + actions: + confirm_destroy: Sei sicuro? + destroy: Elimina + new_voting: Nuovo Spazio Di Votazione + edit: + update: Aggiorna + form: + select_a_voting_type: Seleziona un tipo di votazione + title: Titolo + voting_type: + hybrid: Ibrido + in_person: Di persona + online: Online + new: + create: Crea + title: Nuova Votazione + admin_log: + ballot_style: + create: "%{user_name} ha creato uno stile di voto con il codice %{ballot_style_code} nello spazio %{space_name}" + delete: "%{user_name} ha eliminato lo stile di voto con il codice %{ballot_style_code} nello spazio %{space_name}" + update: "%{user_name} ha aggiornato lo stile dello scrutinio con il codice %{ballot_style_code} nello spazio %{space_name}" + census: + create: "%{user_name} ha creato il censimento per lo spazio %{space_name}" + delete: "%{user_name} ha eliminato il censimento per lo spazio %{space_name}" + update: "%{user_name} ha aggiornato il censimento per lo spazio %{space_name}" + monitoring_committee_member: + create: "%{user_name} ha assegnato l'utente %{monitoring_committee_member_user} come membro del comitato di monitoraggio nello spazio %{space_name}" + delete: "%{user_name} non ha assegnato l'utente %{monitoring_committee_member_user} come membro del comitato di monitoraggio nello spazio %{space_name}" + polling_officer: + create: "%{user_name} ha assegnato l'utente %{polling_officer_user} come funzionario di sondaggio nello spazio %{space_name}" + delete: "%{user_name} non ha assegnato l'utente %{polling_officer_user} come funzionario di sondaggio nello spazio %{space_name}" + polling_station: + create: "%{user_name} ha creato il seggio elettorale %{resource_name} nello spazio %{space_name}" + delete: "%{user_name} ha eliminato il seggio elettorale %{resource_name} nello spazio %{space_name}" + update: "%{user_name} ha aggiornato il seggio elettorale %{resource_name} nello spazio %{space_name}" + voting: + create: "%{user_name} ha creato la votazione %{resource_name}" + publish: "%{user_name} ha pubblicato la votazione %{resource_name}" + unpublish: "%{user_name} ha rimosso la votazione %{resource_name}" + census: + admin: + census: + create: + invalid: Si è verificato un errore durante il caricamento del censimento, riprova più tardi. + delete: + button: Elimina tutti i dati del censimento + destroy: + error: Si è verificato un errore durante l'eliminazione del censimento, riprova più tardi. + export_access_codes: + button: Esporta codici di accesso alle votazioni + callout: Ora puoi procedere all'esportazione dei codici di accesso. Questo può essere fatto solo una volta. Una volta avviata l'esportazione, riceverai un'email con le istruzioni all'indirizzo %{email} + confirm: Puoi esportare i codici di accesso solo una volta. Assicurati di avere accesso all'indirizzo %{email}. + freeze: + callout: Il censimento è congelato e non può essere modificato. + generate_access_codes: + button: Genera codici di accesso alla votazione + callout: Ora puoi procedere a generare i codici di accesso. Ricordati che dopo aver generato i codici di accesso non sarai più in grado di modificare il censimento. + confirm: Se continui, non sarai in grado di modificare il censimento. + info_message_all: "Tutte le righe importate con successo dal file %{file} (%{raw_count} di %{data_count})." + info_message_warn: Si prega di controllare che non manchino dati, poiché %{data_count} record sono stati creati e il file caricato %{file} aveva %{raw_count} righe. + launch_error: Problema nel lanciare la generazione di codici di accesso + launch_success: Generazione di codici lanciata. + new: + file_help: + explanation: 'Guida per il file:' + message_1: Sono ammessi solo file CSV (.csv). + message_2: Il separatore tra le colonne deve essere un punto e virgola (";"). + has_ballot_styles_message: Hai impostato la modalità di voto. Assicurati che il campo "%{ballot_style_code_header}" nel CSV corrisponda al codice desiderato della modalità di voto. + info_message: "Non c'è ancora nessun censimento. Si prega di utilizzare il modulo sottostante per crearlo importando un file CSV." + missing_ballot_styles_message: 'Non c''è ancora una modalità di voto per questa votazione. Se desideri avere domande condizionate (i.e.: mostrare agli elettori domande diverse, in base al quartiere/regione di residenza), è necessario impostare lo stile Modalità di voto prima di importare il censimento. Se si desidera mostrare a tutti gli elettori le stesse domande, è possibile procedere con la procedura di importazione del censimento.' + title: Crea il censimento + show: + heading: Censimento dello spazio vuoto + upload_info: + csv_example_with_ballot_style: 'Un esempio del file con mdalità di voto:' + csv_example_without_ballot_style: 'Un esempio del file senza modalità di voto:' + csv_header_after: Non includere l'ultimo campo ("%{ballot_style_code_header}") se non hai bisogno di modalità di voto/domande condizionali + csv_header_before: 'Il file del censimento deve essere un file CSV con la seguente intestazione:' + document_types: + passport: Passaporto + export_mailer: + access_codes_export: + click_button: 'Clicca sul link successivo per scaricare i dati dei codici di accesso.
    Il file sarà disponibile fino a %{date}.
    Avrai bisogno di 7-Zip (per Windows), Keka (per macOS) o PeaZip (per Linux) per aprirlo. Password: %{password}' + download: Scarica + subject: L'esportazione dei codici di accesso della votazeion per %{voting_title} è disponibile + vote_flow: + already_voted_in_person: Questo partecipante ha già votato di persona e non ha diritto di voto. + content_blocks: + highlighted_votings: + name: Votazioni evidenziate + landing_page: + polling_stations: + heading: Seggi elettorali + no_polling_stations: Non ci sono ancora seggi elettorali. + monitoring_committee_members: + actions: + confirm_destroy: Sei sicura? + destroy: Elimina + new: Nuovo membro + title: Azioni + polling_officer_zone: + closures: + back_to_polling_stations: Torna ai seggi elettorali + certify: + error: Si è verificato un errore nell'allegare il certificato, per favore riprova. + heading: Resoconto del voto - Certificato di caricamento + info_text: Carica una foto del certificato di chiusura elettorale. + submit: Carica il certificato + success: Certificato caricato con successo. + create: + error: Si è verificato un errore durante la creazione della chiusura, riprova più tardi. + success: Chiusura creata con successo. + edit: + heading: Resoconto dei voti - Risposte + modal_ballots_results_count_error: + close_modal: Chiudi + title: Il totale delle urne non si sommano + save_recount: Salva riconteggio + total_ballots: Votazioni totali + total_blank_ballots: Totale delle schede vuote + total_null_ballots: Totale delle schede annullate + total_valid_ballots: Votazioni totali valide + new: + election: 'Elezioni:' + heading: Riconteggio voti + info_text: 'Si prega di introdurre il numero totale di schede (buste) contate in questa stazione di sondaggio:' + modal_ballots_count_error: + btn_validate_total: Convalida il totale delle votazioni + message_for_monitoring_committee: Messaggio per il comitato di sorveglianza + review_recount: Rivedi il conto + text_area_placeholder: Digita il tuo messaggio + title: I record totali non si aggiungono + total_ballots: 'Totale delle urne:' + total_people: 'Persone totali:' + polling_station: 'Seggio elettorale:' + submit: Verifica numero totale + total_ballots_count: Numero di schede + show: + heading: Resoconto del voto + sign: + cancel: Annulla + confirm: Ok, continua + error: Si è verificato un errore, riprova. + heading: Resoconto dei voti - Chiusura dei segni + submit: Firma la chiusura + success: Chiusura firmata con successo. + update: + error: Si è verificato un errore durante l'aggiornamento dei risultati della chiusura, riprova più tardi. + success: Risultati di chiusura aggiornati con successo. + in_person_votes: + complete_voting: + available_answers: 'Risposte disponibili:' + complete_voting: Completa la votazione + identify_another: Identificare un altro utente + voted: L'utente ha votato + create: + error: La votazione non è stata registrata. Per favore riprova. + in_person_form: + census_not_present: Questo partecipante non è elencato nel censimento. + census_not_present_description: Deve andare all'ufficio reclami del censimento o contattare l'assistenza. + date_of_birth: Data di nascita + day: Giorno + day_placeholder: DD + document_number: Numero del documento + document_number_placeholder: Numero identificativo + month: Mese + month_placeholder: MM + select: Seleziona il tipo di documento + title: 'Selezionare il tipo di documento e inserire il numero del documento del partecipante:' + validate_document: Convalida documento + year: Anno + year_placeholder: YYYY + new: + back: Torna ai seggi elettorali + title: Identificare e verificare un partecipante + show: + back: Torna ai seggi elettorali + title: In attesa che il voto in persona sia registrato + update: + error: Si è verificato un errore durante la registrazione della votazione. Riprova. + success: + accepted: La votazione è stata registrata con successo. + rejected: La votazione non è stata accettata dal Bulletin Board. Si prega di contattare l'amministratore di sistema. + verify_document: + census_present: Questo partecipante è elencato nel censimento. + name: Nome + title: 'Verificare che i seguenti dati siano corretti:' + verify_document: Verifica documento + menu: + polling_officer_zone: Zona di sondaggio ufficiale + polling_officers: + index: + polling_officer_role_description: Sei stato designato funzionario del seggio elettorale (Presidente o Gestore) in alcune elezioni svolte su questa piattaforma. + polling_station: + address: '&Indirizzo' + count_votes: Conteggio voti + election: Elezioni + identify_person: Identificare una persona + name: Nome + no_polling_stations: Non sei ancora assegnato a nessun seggio elettorale. + role: Il tuo ruolo + show_closure: Visualizza chiusura + title: Seggi elettorali + voting: Votazione + polling_officers: + actions: + confirm_destroy: Sei sicuro? + destroy: Cancella + title: Azioni + roles: + manager: Gestore + president: Presidente/Presidentessa + unassigned: Non Assegnato + polling_station_closure_recount: + nota_option: Vuoto/ Nessuno dei precedenti + polling_officer_notes: 'Osservazioni del funzionario incaricato del sondaggio:' + polling_officer_notes_blank: Non ci sono note + recount_summary: 'Riepilogo riepilogo:' + signed: Firmato + total_ballots: 'Votazioni totali:' + total_blank_ballots: 'Totale delle schede vuote:' + total_null_ballots: 'Totale delle schede annullate:' + total_valid_ballots: 'Votazioni totali valide:' + polling_stations: + actions: + confirm_destroy: Sei sicuro? + destroy: Cancella + edit: Modifica + title: Azioni + votings: + access_code_modal: + email: Invia per email a %{email} + no_email: Nessuna email disponibile + no_sms: Nessun numero di telefono disponibile + sms: Invia tramite SMS a %{sms} + title: Ottieni Codice Accesso + check_census: + check_status: Verifica stato + description: Qui, hai la possibilità di controllare i tuoi dati di censimento per sapere se hai il diritto di partecipare a questa votazione. Dovresti avere già un codice di accesso, ma se lo hai perso, puoi chiederlo di nuovo, quando i tuoi dati sono corretti. + form_title: 'Compila il seguente modulo per controllare i dati del censimento:' + invalid: Si è verificato un errore nel controllare il censimento. + success: + access_link_with_sms: via SMS o e-mail. + title: I tuoi dati di censimento sono corretti! + title: Posso votare? + check_fields: + date_of_birth: Data di nascita + day: Giorno + day_placeholder: DD + document_number: Numero del documento + document_number_placeholder: Numero ID + month: Mese + month_placeholder: MM + postal_code: Codice postale + postal_code_placeholder: Cap + select: Seleziona il tipo di documento + year: Anno + year_placeholder: YYYY + count: + title: + one: "%{count} votazioni" + other: "%{count} votazioni" + elections_log: + description: Il registro elettorale vi mostrerà tutte le informazioni pertinenti su ogni voto. Per esempio, lo stato della cerimonia chiave o tally o se i risultati sono già stati pubblicati. Fare clic sull'elezione in cui si desidera le informazioni di registro. + title: Registro elettorale + filters: + active: Attivo + all: Tutti + finished: Finito + search: Ricerca + upcoming: Prossimi + index: + no_votings: Nessun voto corrisponde ai criteri di ricerca. + only_finished: Al momento non sono previste elezioni, ma qui puoi trovare un elenco di tutte quelle passate. + title: Votazioni + login: + access_code: Codice di accesso + access_code_placeholder: Codice Accesso + ask_for_a_new_one: Chiedi uno nuovo. + form_title: 'Compila il seguente modulo per accedere alla votazione:' + start_voting: Inizia a votare + title: Identificarmi con i miei dati di censimento di voto + orders: + label: 'Ordina le votazioni per:' + random: Casuale + recent: Più recente + votings_m: + badge_name: + finished: Completata + ongoing: In corso + upcoming: In arrivo + unspecified: Non specificato + voting_type: + hybrid: Ibrido + in_person: Di persona + online: Online + layouts: + decidim: + voting_navigation: + check_census: Posso votare? + election_log: Registro elettorale + votings: + index: + promoted_votings: Votazioni in evidenza + promoted_voting: + vote: Vota diff --git a/decidim-elections/config/locales/ja.yml b/decidim-elections/config/locales/ja.yml new file mode 100644 index 00000000..8f3d34db --- /dev/null +++ b/decidim-elections/config/locales/ja.yml @@ -0,0 +1,1412 @@ +ja: + activemodel: + attributes: + answer: + description: 説明 + image: 画像 + proposals: 関連する提案 + title: タイトル + ballot_style: + code: コード + election: + description: 説明 + end_time: 投票が終了します: + start_time: 投票開始 + title: タイトル + monitoring_committee_member: + email: Eメールアドレス + name: 名前 + polling_officer: + email: Eメールアドレス + name: 名前 + polling_station: + address: 住所 + location: 場所 + location_hints: 位置情報のヒント + polling_station_managers: マネージャー + polling_station_president_id: 投票責任者 + title: タイトル + question: + max_selections: 選択範囲の最大数 + min_selections: 上記のどれでもない + title: タイトル + trustees_participatory_space: + user_id: 参加者 + voting: + banner_image: バナー画像 + census_contact_information: センサスの連絡先情報 + description: 説明 + end_time: 投票終了 + introductory_image: 導入画像 + promoted: プロモート + scope_id: スコープ + show_check_census: '「センサスの確認」ページを表示' + start_time: 投票開始 + title: タイトル + voting_type: 投票タイプ + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: 再接続が必要 + ballot_result: + attributes: + base: + total_count_invalid: 回答の合計数は、有効票/白票/空 のブレークダウンと一致しません。 + election: + attributes: + attachment: + needs_to_be_reattached: 再接続が必要 + question_result: + attributes: + base: + blank_count_invalid: 白票の回答の合計数は、白票の投票の合計数より大きくすることはできません。 + trustee: + attributes: + name: + cant_be_changed: 変更できません + public_key: + cant_be_changed: 変更できません + voting: + attributes: + voting_type: + inclusion: "%{value} は有効な投票タイプではありません" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: 投票委員はすでに投票所の責任者/マネージャーです。 + voting: + different_organization: 投票は、ユーザーと同じ組織内で行う必要があります。 + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: 投票委員は、投票所と同じ投票区内にいる必要があります。 + models: + decidim/elections/answer: + other: 回答 + decidim/elections/election: + other: 選挙 + decidim/elections/question: + other: 質問 + decidim/voting: + other: 投票 + decidim/votings/census/dataset: + other: データセット + decidim/votings/census/datum: + other: データ + decidim/votings/polling_officer: + other: 投票委員 + decidim/votings/polling_station: + other: 投票所 + decidim/votings/voting: + other: 投票 + decidim: + admin: + filters: + officers_assigned_eq: + label: 委員 + values: + assigned: 割り当て済み + unassigned: 未割り当て + role_eq: + label: ロール + values: + manager: マネージャー + president: 投票責任者 + unassigned: 未割り当て + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: '%{collection} を名前/メールアドレス/ニックネーム/投票所で検索します。' + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : '%{collection} をタイトル、住所、委員名/メールアドレス/ニックネームで検索します。' + signed_eq: + label: 署名済み + values: + 'false': 署名済み + 'true': 未署名 + validated_eq: + label: 検証済み + values: + 'false': 未検証 + 'true': 検証済み + voting_publications: + create: + error: この投票の公開中に問題が発生しました。 + success: 投票を公開しました。 + destroy: + error: 投票を非公開にする際に問題が発生しました。 + success: 投票を非公開にしました。 + components: + elections: + actions: + vote: 投票 + name: 選挙 + settings: + global: + announcement: お知らせ + step: + announcement: お知らせ + elections: + actions: + confirm_destroy: よろしいですか? + destroy: 削除 + edit: 編集 + feedback: 投票者からのフィードバック + import: 提案を回答にインポート + manage_answers: 回答の管理 + manage_questions: 質問の管理 + manage_steps: ステップの管理 + new_answer: 新しい回答 + new_election: 新しい選挙 + new_question: 新しい質問 + new_trustee: 新しい受託者 + preview: プレビュー + publish: 公開 + title: アクション + unpublish: 公開しない + admin: + answers: + create: + invalid: 回答を作成する際に問題がありました。 + success: 回答を作成しました。 + destroy: + invalid: 回答を削除する際に問題が発生しました。 + success: 回答を削除しました。 + edit: + title: 回答を編集 + update: 回答を更新 + index: + invalid_max_selections: 最大選択数に合わせるには、%{missing_answers} 個以上の回答が必要です。 + title: 回答 + new: + create: 回答を作成 + title: 新しい回答 + not_selected: 未選択 + select: + disable: 回答の選択を解除 + enable: 回答を選択済みとしてマーク + invalid: 回答を選択する際に問題が発生しました。 + success: 回答を選択しました。 + selected: 選択済み + unselect: + invalid: 回答の選択を解除する際に問題が発生しました。 + success: 選択を解除しました。 + update: + invalid: 回答を更新する際に問題が発生しました。 + success: 回答を更新しました。 + elections: + create: + invalid: 選挙を作る際に問題がありました。 + success: 選挙を作成しました。 + destroy: + invalid: 選挙を削除する際に問題が発生しました。 + success: 選挙を削除しました。 + edit: + title: 選挙を編集 + update: 選挙を更新 + form: + organization_time_zone: 組織のタイムゾーンが正しいことを確認してください。現在の設定は %{time_zone} (%{time}) です。 + index: + no_bulletin_board: BBS が設定されていませんが、このモジュールを使用するためには必要です。このタスクはシステム管理者が行う必要があります。 + title: 選挙 + new: + create: 選挙を作成 + title: 新しい選挙 + publish: + success: 選挙を公開しました。 + unpublish: + success: 選挙を非公開にしました。 + update: + invalid: 選挙を更新する際に問題が発生しました。 + success: 選挙を更新しました。 + exports: + elections: 選挙 + feedback_form_answers: フィードバックフォームの回答 + mailers: + trustee_mailer: + body: + help_html: |- +

    こんにちは、%{user_name} さん、


    +

    %{organization} で行われるいくつかの選挙において、あなたが受託者として追加されました。


    +

    あなたのアカウントに「受託者ゾーン」という新しいセクションが追加されました。そこから、必要に応じて任務を遂行してください。まずは、あなたの識別キーを生成してください。


    + subject: あなたは %{resource_name} に受託者として追加されました + trustee_zone: 私を受託者ゾーンに連れて行ってください + menu: + trustees: 受託者 + models: + answer: + name: 回答 + proposals_imports: + create: + invalid: 提案を回答にインポートする際に問題が発生しました。 + success: "%{number} 件の提案を回答にインポートしました。" + new: + create: 提案を回答にインポート + no_components: この参加スペースには、回答に提案をインポートするための他の提案コンポーネントはありません。 + select_component: コンポーネントを選択してください + title: 提案のインポート + questions: + create: + election_started: 選挙はすでに始まっています。 + invalid: 質問を作成中に問題が発生しました。 + success: 質問を作成しました。 + destroy: + invalid: 質問を削除する際に問題が発生しました。 + success: 質問を削除しました。 + edit: + title: 質問を編集 + update: 質問を更新 + index: + title: 質問 + new: + create: 質問を作成 + title: 新しい質問 + update: + invalid: 質問の更新中に問題が発生しました。 + success: 質問を更新しました。 + steps: + create_election: + census: センサス + errors: + census_codes_generated: センサスのアクセスコードは生成されませんでした。 + census_frozen: センサスのアクセスコードはエクスポートされませんでした。 + census_uploaded: この選挙にアップロードされたセンサスはありません。 + component_published: 選挙コンポーネントは 未公開です。 + fix_it_text: 修理する + max_selections: 質問が 回答の数に対して正しくありません + minimum_answers: 質問には 少なくとも2つの回答が必要です + minimum_questions: 選挙には 少なくとも1つの質問が必要です。 + published: 選挙はが 公開されていません 。 + time_before: 開始時刻は選挙が始まる %{hours} 時間以内 になります。 + trustees_number: 参加スペースには 少なくとも %{number} 人の公開鍵を持つ受託者が必要です。 + invalid: この選挙の設定中に問題が発生しました + no_trustees: この参加スペースに設定された受託者はいません + not_used_trustee: "(未使用)" + public_key: + 'false': 公開鍵がありません + 'true': 公開鍵があります + requirements: + census_codes_generated: センサスのアクセスコードが生成されます。 + census_frozen: センサスのアクセスコードはエクスポートされ、センサスは凍結されます。 + census_uploaded: センサスがアップロードされました。 + component_published: 選挙コンポーネントは 公開済みです. + max_selections: すべての質問は正しい 回答の最大値 を持っています。 + minimum_answers: 各質問には 少なくとも2つの回答を持ちます。 + minimum_questions: 選挙には 少なくとも1つの質問があります。 + published: 選挙を 公開しました 。 + time_before: 選挙開始の 少なくとも %{hours} 時間前 までに準備を完了させます。 + trustees_number: 参加スペースには 少なくとも %{number} 人の公開鍵を持つ受託者がいます。 + submit: 選挙を設定 + success: 選挙を掲示板に送信しました。 + technical_configuration: + authority_name: "オーソリティ名: %{value}" + bulletin_board_server: "掲示板サーバー: %{value}" + scheme_name: "スキーム名: %{value}" + title: 技術情報を表示 + title: 選挙を設定する + trustees: 選挙受託者 + created: + invalid: キーセレモニーを開始する際に問題が発生しました。 + submit: キーセレモニーを開始 + success: キーセレモニーの開始リクエストを掲示板に送信しました。 + title: 選挙の作成 + trustees: 受託者 + key_ceremony: + continue: 続ける + title: キーセレモニー + key_ceremony_ended: + errors: + time_before: 選挙を開始する準備ができました。投票期間を開始するには、 開始時刻 ( %{start_time}) の%{hours} 時間前まで待つ必要があります。 + invalid: 投票期間の開始に問題がありました。 + requirements: + time_before: 選挙はまもなく開始されます。手動で投票期間を開始するか、開始時刻の %{start_time} より前に自動的に開始されます。 + submit: 投票期間を開始 + success: 投票期間の開始リクエストを掲示板に送信しました。 + title: 開始準備完了 + processing: 処理しています... + results_published: + answer: 回答 + not_selected: 未選択 + question: 質問 + result: 結果 + selected: 選択済み + submit: 送信 + title: 結果を公開しました + tally_ended: + answer: 回答 + not_selected: 未選択 + question: 質問 + result: 結果 + selected: 選択済み + submit: 結果を公開 + success: 公開結果リクエストを掲示板に送信しました。 + title: 計算結果 + tally_started: + continue: 続ける + invalid: 不在の受託者を報告する際に問題が発生しました。 + mark_as_missing: missing としてマーク + mark_as_missing_description: 全ての受託者はこのプロセスに参加する必要がありますが、受託者がプロセスに参加できない場合は、不在としてマークすることができます。 + success: 不在の受託者の報告は正常に掲示板に送信されました。 + tally_completion: すべての受託者がアクティブまたは missing とマークされた場合、プロセスは完了します。少なくとも %{quorum} 人の受託者がプロセスを完了する必要があります。 + title: 集計処理 + undo_mark_as_missing: 受託者は、プロセスが完了する前に、間違ってmissingとマークされていることができます。 その場合、通常通りに進行することができ、missingのマークは無視されます。 + vote: + errors: + time_after: 選挙はまだ進行中です。投票期間を終了するには、終了時間(%{end_time})まで待つ必要があります。 + invalid: 投票期間の終了に問題がありました。 + requirements: + time_after: 選挙は終了しました。投票期間を手動で終了するか、数分で自動的に終了します。 + submit: 投票を終了する + success: 投票期間終了リクエストは掲示板に正常に送信されました。 + title: 投票期間 + vote_ended: + invalid: 集計を開始する際に問題が発生しました。 + submit: 集計を開始 + success: 集計開始リクエストは掲示板に正常に送信されました。 + text: 投票が終了しました。今すぐ集計を開始できます。 + title: 投票期間が終了しました + vote_stats: + no_vote_statistics_yet: まだ投票統計がありません + title: 投票統計 + voters: 投票者 + votes: 投票 + trustees_participatory_spaces: + actions: + disable: 無効 + enable: 検討する + create: + exists: 受託者はこの参加スペースに存在します。 + invalid: 受託者の作成中に問題が発生しました。 + success: 受託者の作成に成功しました。 + delete: + invalid: 受託者を削除する際に問題が発生しました。 + success: 受託者を削除しました。 + form: + select_user: ユーザーを選択 + index: + title: 受託者 + new: + create: 受託者を作成 + title: 新しい受託者 + update: + invalid: 受託者 %{trustee} の更新中に問題が発生しました。 + success: 受託者 %{trustee} が正常に更新されました。 + admin_log: + election: + create: "%{user_name} が %{space_name} の選挙 %{resource_name} を作成しました" + delete: "%{user_name} が %{space_name} の選挙 %{resource_name} を削除しました" + end_vote: "%{user_name} が掲示板の %{space_name} の選挙 %{resource_name} の投票期間を終了させました" + publish: "%{user_name} が %{space_name} の選挙 %{resource_name} を公開しました" + publish_results: "%{user_name} が %{space_name} の選挙 %{resource_name} の結果を掲載しました" + report_missing_trustee: "%{user_name} は、%{space_name} の選挙 %{resource_name} の集計で不足している受託者として %{trustee_name} を報告しました" + setup: "%{user_name} が掲示板に %{space_name} の選挙%{resource_name} を作成しました" + start_key_ceremony: "%{user_name} が掲示板で %{space_name} の選挙%{resource_name} のキーセレモニーを開始しました" + start_tally: "%{user_name} が %{space_name} の選挙%{resource_name} の集計を開始しました。" + start_vote: "%{user_name} が掲示板で %{space_name} の 選挙%{resource_name} の投票期間を開始しました" + unpublish: "%{user_name} が %{space_name} の 選挙 %{resource_name} を非公開にしました" + update: "%{user_name} が %{space_name} の選挙 %{resource_name} を更新しました" + trustee: + create: "%{user_name} がユーザー %{trustee_user} を受託者に割り当てました" + connection: + failed: + modal: + close: 閉じる + communication_lost: 投票サーバー (掲示板) との通信が切れているようです。
    インターネット接続が切れているか、接続先のサーバーが混雑している可能性があります。
    この問題が続くようでしたら、時間をおいて再度お試しいただくか、サポートまでお問い合わせください。 + generic_error: 不明なエラーが発生しました。 お使いのブラウザがサポートされていないか、サポートされていない「シークレット」または「プライベート」モードを使用している可能性があります。 + title: 問題が発生しました + election_m: + badge_name: + finished: 完了 + ongoing: 有効 + upcoming: 今後の予定 + end_date: 終了 + footer: + remaining_time: + other: "投票期間は残り %{count} 時間 %{minutes} 分 です。" + view: 表示 + vote: 投票 + label: + date: 日付 + questions: 質問 %{count} + start_date: 開始 + unspecified: 指定されていません + elections: + count: + elections_count: + other: "%{count} 選挙" + election_log: + chained_hash: このメッセージの連鎖的ハッシュ + complete: 完了 + creation_description: + complete: 選挙は作成され、掲示板に設定されています。 + not_created: 選挙はまだ作成されていません。 + creation_title: 選挙が作成されました + description: これは各ステップの状態、例えば選挙が行われた時、集計が完了した時、選曲が終わった時などを確認できる選挙ログです。 + download: ダウンロード + key_ceremony_description: + complete: キーセレモニーが完了しました。すべての受託者は有効なキーを持っており、必要なバックアップキーをダウンロードしました。 + not_started: キーセレモニーはまだ始まっていません。 + started: キーセレモニーは開始されましたが、まだ完了していません。 + key_ceremony_title: キーセレモニー + not_available: まだ利用できません + not_created: 作成されていません + not_ready: 準備中 + not_started: 開始前 + published: 公開済み + results_description: + not_published: 結果はまだ公開されていません。 + published: 結果が公開されます。 + results_title: 結果 + started: 開始 + tally_description: + finished: 集計処理は終了します。 + not_started: 集計処理はまだ始まっていません。 + started: 集計処理が始まりました。 + tally_title: 集計処理 + title: 選挙ログ + unpublished: 未公開 + verifiable_results: + checksum: 'ファイルのSHA256 チェックサム:' + description: + not_ready: 検証可能な選挙ファイルと SHA256 チェックサムはまだ利用できません。 結果が公表されるとすぐに、あなたはこの選挙を確認することができます。 + ready: 'ここでは、選挙を検証するためのオプションが用意されています。まず、ファイルをダウンロードし、破損していないことを確認する必要があります。そのためには、次のコマンドを実行し、outputがチェックサムと一致することを確認します。' + how_to_verify: 'いったんファイルをダウンロードし問題のないことが確認できたら、ユニバーサルベリファイアの実行に進めます。 このリポジトリ をクローンして、ルートのフォルダにて次のコマンドを実行します:' + title: 選挙結果を検証 + verifiable_file: '検証可能な選挙ファイル:' + verify: 選挙を検証 + vote_description: + finished: 投票プロセスは終了しました。 + not_started: 投票プロセスはまだ開始されていません。 + started: 投票プロセスが開始されました。 + vote_title: 投票プロセス + filters: + active: 有効 + all: 全て + date: 日付 + finished: 完了 + upcoming: 今後の予定 + preview: + available_answers: '利用可能な回答:' + description: 'これらは投票プロセスでの質問内容です。' + title: 選挙の質問 + results: + description: 'これらは、各質問に対する投票結果です:' + percentage: "%{count}%" + selected: 選択済 + title: 選挙結果 + votes: + other: "%{count} 票" + show: + action_button: + change_vote: 投票の変更 + vote: 投票の開始 + vote_again: 再投票 + callout: + already_voted: すでにこの選挙に投票しました。投票の変更と確認ができます。 + pending_vote: あなたの投票はサーバー上で行われています。 + vote_rejected: 投票を確認できませんでした。もう一度お試しください。 + election_log: 選挙ログ + preview: プレビュー + verify: + already_voted: すでに投票しましたか? + verify_here: 投票を確認してください。 + will_verify: 選挙が開始されると、投票を確認することができます。 + voting_period_status: + finished: '%{start_time} に投票が開始され、 %{end_time} に終了しました' + ongoing: '有効な投票まで: %{end_time}' + upcoming: '%{start_time} に投票が終了します' + feedback: + answer: + invalid: フィードバックの送信中に問題が発生しました。 + spam_detected: フォームに回答する際に問題が発生しました。もう一度やり直していただけますか? + success: フィードバックを送信しました + models: + answer: + fields: + proposals: 提案 + selected: 選択済み + title: タイトル + votes: 投票 + election: + fields: + bb_status: 掲示板の状態 + end_time: 終了時刻 + start_time: 開始 + title: タイトル + verifiable_results_file_hash: ファイルのSHA256 チェックサム + verifiable_results_file_url: 検証可能な選挙ファイル + question: + fields: + answers: 回答 + max_selections: 最大選挙数 + title: タイトル + trustees_participatory_space: + fields: + considered: 考慮された + email: Eメールアドレス + inactive: 非アクティブ + name: 名前 + notification: 通知送信日時 + public_key: 公開鍵 + status: ステータス: + orders: + label: 選挙の順序 + older: 古い + recent: 新しい + trustee_zone: + elections: + backup_modal: + description: この選挙は、掲示板で作成されています。 それに参加しているすべての受託者がこれらのキーのバックアップコピーを作成し、安全な場所に保存することが非常に重要です。 その後、処理が続行されます。 + download_election_keys: ダウンロードキー + title: '%{election} のバックアップ選挙キー' + key_ceremony_steps: + back: 戻る + description: この選挙は、掲示板に作成されています。このプロセスを完了するには、受託者としての参加が必要です。 + keys: + create_election: キーの生成 + key_ceremony: + joint_election_key: キーの生成に参加する + step_1: キーの発行 + list: + status: ステータス + task: タスク + process_warning: プロセスが開始された後は、プロセスが終了するまでこのページを終了しないでください。 すべての受託者が連結され完了されるまで、数分かかることがあります。 + start: 開始 + status: + completed: 完了 + pending: 保留中 + processing: 処理中 + title: '%{election} の選挙用キーを作成' + restore_modal: + description: 掲示板には、今回の選挙の受託者としての情報が掲載されます。 処理を続行するには、最初に前回のセッションで生成されたバックアップファイルをアップロードします。 + title: '%{election} の選挙用キーを復元' + upload_election_keys: 選挙用キーをアップロード + tally_started_steps: + back: 戻る + description: この選挙の結果は、掲示板上で計算されます。この処理を完了するには、受託者として参加すること必要があります。 + keys: + end_tally: 終了しました + tally: + cast: 集計キャスト + share: 集計を共有 + list: + status: ステータス + task: タスク + process_warning: 処理が開始された後は、処理が終了するまでこのページを閉じないでください。 すべての受託者が接続し完了するまで、しばらくかかることがあります。 + start: 開始 + status: + completed: 完了 + pending: 保留 + processing: 処理中 + title: '%{election} の合計' + update: + error: 選挙ステータスは更新されませんでした。 + success: '選挙ステータスは %{status} です。' + menu: + trustee_zone: Trusteeゾーン + no_bulletin_board: + body: このセクションでは、設定された掲示板が必要です。詳細については管理者にお問い合わせください。 + title: 申し訳ありませんが、掲示板はまだ設定されていません。 + trustees: + show: + elections: + list: + action_required: + 'false': 'いいえ' + name: 操作が必要ですか? + 'true': アクションを実行 + bb_status: ステータス + election: 選挙 + voting_period: 投票期間 + no_elections: 現在、あなたは受託者として行動を取るようにアサインされていません。 別フェーズで活動するタイミングになると通知が届きます。 + title: 選挙 + identification_keys: + cancel: キャンセル + generate: 識別キーを生成 + generate_error: 識別キーの生成中にエラーが発生しました。 + generate_legend: あなたは、受託者として選挙に参加するために識別キーのペアを生成する必要があります。 + generate_legend_1: ボタンを押すと、生成された識別キーを含むファイルがダウンロードされます。 + generate_legend_2: ダウンロードしたファイルをクリーンなUSBデバイスにコピーします + generate_legend_3: 'コンピュータにファイルのコピーがないことを確認してください (例: ダウンロードフォルダとデスクトップフォルダを確認してください)。' + generate_legend_4: 異なる外部デバイスにファイルの別のコピーを作成し、非常に安全な場所に保存します。 + submit: 送信 + submit_legend: 上記のすべての手順に従えば、公開識別キーをサーバーに送信するプロセスは完了になります。 + submit_title: 公開識別キーを送信 + title: 受託者の識別キー + upload: 識別キーをアップロード + upload_error: + invalid_format: アップロードされたファイルには識別キーが含まれていません。 + invalid_key: アップロードされたファイルの識別キーが読み込めません。 + invalid_public_key: アップロードされたファイルの識別キーが公開識別キーと一致しません。 + upload_legend: サーバーには公開識別キーがありますが、ブラウザにはまだ公開識別キーがありません。 生成後に作成したバックアップから、識別キーのあるファイルをコンピュータにインポートする必要があります。 + not_supported_browser_description: お使いのWebブラウザでは受託者の業務を行うことができません。ブラウザの最新バージョンを使用していることを確認するか、もっと一般的なブラウザのいずれかを使用して、受託者タスクを完了できるようにしてください。 + not_supported_browser_title: ブラウザをアップグレードして受託者として行動する + safari_warning_description: Safariを使用しているようですが、Safariは受託者としての役割や投票の暗号化には対応していません(Appleが課すメモリ制限によるものです)。 Appleによるポリシーの変更やDecidim投票モジュールの将来の最適化によって、将来的にはこれが解決するかもしれません。それまでの間、別のブラウザを使用してください。 + safari_warning_title: Safariブラウザが検出されました + trustee_role_description: + with_keys: あなたは、このプラットフォームで行われる選挙のいくつかで受託者として行動するようにアサインされています。 + without_keys: あなたは受託者として行動するようアサインされています。識別キーを生成してアップロードしてください。 + update: + success: 公開識別キーを保存しました。 + votes: + ballot_decision: + audit: (監査投票) + back: 投票プロセスを再度開始 + ballot_hash: '投票用紙の識別子:' + cast: 投票を完了するには投票を行ってください + description_html: 投票用紙が正しく集計されるようにキャストするオプションがあります。または、あなたの投票用紙が正しく暗号化されたかを監査することもできます。投票を監査したい場合は、進め方についての指示をお読みください。 + header: '投票は暗号化されています:キャストまたは監査されます' + casting: + header: 投票中… + text: あなたの投票は投票箱に投じられています。 + confirm: + answer_number: 回答 %{number} + confirm: 確定 + edit: 編集 + header: 投票を確定 + intro: あなたが投じようとしている投票の概要です。
    投票を確定するか、回答を編集してください。 + nota_option: 空白 + confirmed: + back: 選挙に戻る + experience: ご利用いただき、どう思われますか? + feedback: フィードバックを送る + header: 投票が確定されました + lead: あなたの投票が行われました! + text: '投票が投票箱に追加されたことを、次の識別子で確認できます: %{e_vote_poll_id} ' + verify_link: 確認するには、識別子をコピーして 投票検証ページ に貼り付けます + create: + error: 投票を行う際に問題が発生しました。もう一度やり直してください。 + encrypting: + header: 投票を暗号化しています... + text: 投票の秘密を確保するために、投票は暗号化されています。 + failed: + header: 投票に失敗しました + lead: 投票は行われませんでした! + text: 問題が発生しました。もう一度やり直してください。 + try_again: 再実行 + header: + ballot_decision: 投票を行うか監査する + confirm: 投票を確定 + election: 選挙 + register: 登録 + vote_for: '%{title} に投票' + messages: + invalid_token: 投票ブースでのセッションが無効です。もう一度投票してください。 + not_allowed: 現在、この選挙に投票することはできません。 + modal: + close: 閉じる + proposal_header: '提案:' + new: + answer_choices: 最大 %{choices} 個の回答を選択できます + more_information: 詳しい情報 + nota_option: 空白、もしくは上記のどれでもない + preview_alert: これは投票ブースのプレビューです。 + question_steps: '%{current_step} / %{total_steps} の質問' + selections: "%{selected} / %{max_selections}
    の選択" + onboarding_modal: + create_account: アカウントの作成 + description: プラットフォームに新しいアカウントを作成しますか? プロセスに参加し、組織の積極的な一員になることができます。 + no_account: いいえ結構です。 + title: プラットフォームは初めてですか? + update: + error: 投票ステータスの更新中に問題が発生しました。もう一度投票してください。 + verify: + content: + heading: 投票を確認 + info: この検証器は、暗号化されたテキスト文字列で識別された投票が正しく変換され、投票箱内にあることを確認します。 + error: + header: 投票が見つかりません! + info: 投票コードが %{link} 投票箱に見つかりませんでした。もう一度やり直してください。 + form: + back: プラットフォームに戻る + submit: チェック + vote_identifier: '識別コード:' + vote_identifier_help: これは投票後に与えられた識別子です (投票ブースに入るためのコードではありません)。 + header: + title: 投票を確認 + success: + header: 投票しました! + info: 暗号化された投票は、投票箱 %{link} に入っています。 + voting_step: + back: 戻る + continue: 次へ + warnings: + empty_filters: この条件の選挙はありません。 + no_elections: 選挙が予定されていません。 + no_scheduled_elections: 現在、予定された選挙はありませんが、過去のすべての選挙を検索できます。 + events: + elections: + election_published: + email_intro: '%{resource_title} 選挙は %{participatory_space_title}に対して有効になりました。このページから見ることができます:' + email_outro: '%{participatory_space_title}をフォローしているため、この通知を受け取りました。前のリンクに続く通知の受信を停止することができます。' + email_subject: '%{resource_title} の選挙は %{participatory_space_title} のために有効になりました。' + notification_title: %{resource_title} の選挙が %{participatory_space_title} に有効になりました。 + trustees: + new_election: + email_intro: '%{resource_title} 選挙の受託者として追加されました。' + email_outro: '%{resource_title} 選挙の受託者に追加されたため、この通知を受け取りました。' + email_subject: あなたは %{resource_title} 選挙の受託者です。 + notification_title: 選挙 %{resource_title}の受託者に選ばれました。選挙を準備するためのキーセレモニーを行ってください。 + new_trustee: + email_intro: 管理者が %{resource_name} の受託者としてあなたを追加しました。受託者ゾーンで 公開鍵を作成する必要があります。 + email_outro: '%{resource_name} の受託者に追加されたため、この通知を受信しました。' + email_subject: あなたは %{resource_name} の受託者です。 + notification_title: '%{resource_name} におけるいくつかの選挙で、貴方が受託者として追加されました。
    このプラットフォームで行われる選挙において、必要に応じて任務を遂行していただきます。まずはじめに、あなたの識別キーを生成してください。' + start_tally: + email_intro: '%{resource_title} 選挙の投票期間が終了しました。最終結果を公開するために、選挙の合計を行ってください。' + email_outro: あなたは %{resource_title} 選挙の受託者であるため、この通知を受け取りました。 + email_subject: '%{resource_title} 選挙の集計プロセスが開始されました。' + notification_title: '%{resource_title} 選挙の投票期間が終了しました。最終結果を公開するために、選挙の合計を行ってください。' + votes: + accepted_votes: + email_intro: '投票が承認されました!投票トークン: %{encrypted_vote_hash} を使用して、投票を ここ で確認できます。' + email_outro: '%{resource_name} 選挙に投票したため、この通知を受け取りました。' + email_subject: '%{resource_name} への投票が承認されました。' + notification_title: 'あなたの投票が承認されました。 ここであなたの投票トークン: %{encrypted_vote_hash} を使って投票を確認してください。' + votings: + polling_officers: + polling_station_assigned: + email_intro: "あなたは %{resource_title}の投票において、投票所 %{polling_station_name} の%{role} がアサインされました。\nあなたは投票委員ゾーンで投票所を管理できます." + email_outro: '%{polling_station_name} の%{role} として割り当てられているため、この通知を受け取りました。' + email_subject: あなたは投票所%{polling_station_name} の %{role} です。 + notification_title: あなたは %{resource_title}の投票において、投票所 %{polling_station_name} の%{role} です。 + send_access_code: + instruction: 'あなたのアクセスコードこちらです: %{access_code}。これで %{voting} に参加できます。' + subject: '%{voting} に参加するためのアクセスコードです。' + help: + participatory_spaces: + votings: + contextual: "

    投票は、組織を形成するすべての人に明確な質問を投げかけ、投票への参加を呼びかけ、回答の賛否の議論に火をつけ、注文をつけることができるスペースです。投票日が来たら、投票を行い、その結果を公開することができます。

    例: 投票は組織に影響するどのようなことにも利用できます。例えば、いくつかの案から組織の名前やロゴを選んで変更したり、より大きな組織の一部になるかどうかをイエスかノーかで決定したり、新しい戦略計画やワーキンググループの結果の検証や拒否したり、ポジションが最大で 1、2、3 の委任のままであるべきかどうかを決定したりできます。

    \n" + page: "

    投票は、組織を形成するすべての人に明確な質問を投げかけ、投票への参加を呼びかけ、回答の賛否の議論に火をつけ、注文をつけることができるスペースです。投票日が来たら、投票を行い、その結果を公開することができます。

    例: 投票は組織に影響するどのようなことにも利用できます。例えば、いくつかの案から組織の名前やロゴを選んで変更したり、より大きな組織の一部になるかどうかをイエスかノーかで決定したり、新しい戦略計画やワーキンググループの結果の検証や拒否したり、ポジションが最大で 1、2、3 の委任のままであるべきかどうかを決定したりできます。

    \n" + title: 投票とは何ですか? + menu: + votings: 投票 + participatory_spaces: + related_elections: + see_all: すべての選挙を見る + statistics: + elections_count: 選挙 + votings_count: 投票 + votings: + admin: + ballot_styles: + create: + error: この投票スタイルの作成に問題がありました。 + success: 投票スタイルを作成しました。 + destroy: + invalid: この投票スタイルを削除する際に問題が発生しました。 + success: 投票スタイルを削除しました。 + edit: + title: 投票スタイルを編集 + update: 更新 + form: + code_help: 'ヒント: コードは、センサスと投票スタイルとのリンクです。 センサスデータをアップロードする場合、コードに一致する投票スタイルが各エントリに割り当てられます。' + election: 選挙 + questions: この投票スタイルに関する質問 + questions_help: 'ヒント: この投票スタイルに割り当てられた投票者用の選挙コンポーネントから質問を選択します。' + index: + actions: + confirm_destroy: 本当に削除してよろしいですか? + destroy: 削除 + edit: 編集 + new: 新しい投票スタイル + title: アクション + associated_census_data: 関連センサス項目 + explanation_callout: 投票者にどのような質問がブースで提示されるかは、投票スタイルで指定します。 投票スタイルでは、投票の構成要素からどの質問が投票に属するかを選択できます。 投票コードは、センサスの投票者とブースで提示される投票用紙を一致させるために使用されます。 すべての質問を常に提示する場合は、投票スタイルを作成しないでください。 + title: 投票スタイル + new: + create: 作成 + title: 投票スタイルを作成 + update: + invalid: この投票スタイルの更新中に問題が発生しました。 + success: 投票スタイルを更新しました。 + content_blocks: + attachments_and_folders: + name: 投票の添付ファイルとフォルダ + header: + name: 投票ヘッダー + highlighted_votings: + max_results: 表示する要素の最大量 + html_block_1: + name: 投票htmlブロック1 + html_block_2: + name: 投票htmlブロック2 + html_block_3: + name: 投票htmlブロック3 + main_data: + name: タイトルと説明 + metrics: + name: 投票メトリック + polling_stations: + name: 投票所 + related_elections: + name: 投票する選挙 + stats: + name: 投票の統計 + timeline: + name: 投票のタイムライン + index: + published: 公開済み + unpublished: 未公開 + menu: + votings: 投票 + votings_submenu: + attachment_collections: フォルダ + attachment_files: ファイル + attachments: 添付ファイル + ballot_styles: 投票スタイル + census: センサス + components: コンポーネント + info: この投票について + landing_page: ランディングページ + monitoring_committee: 監視委員会 + monitoring_committee_election_results: 結果を検証する + monitoring_committee_members: メンバー + monitoring_committee_polling_station_closures: 証明書の検証 + monitoring_committee_verify_elections: 選挙の検証 + polling_officers: 投票委員 + polling_stations: 投票所 + see_voting: 投票を見る + models: + ballot_style: + fields: + code: コード + monitoring_committee_member: + fields: + email: Eメールアドレス + name: 名前 + polling_officer: + fields: + email: Eメールアドレス + name: 名前 + polling_station: 投票所 (role) + polling_station: + fields: + address: 住所 + polling_station_managers: マネージャー + polling_station_president: 投票責任者 + title: タイトル + voting: + fields: + created_at: 作成日時 + published: 公開済み + title: タイトル + monitoring_committee_election_results: + actions: + title: アクション + view: 表示 + index: + title: 結果を表示したい選挙を選択してください + results: + bulletin_board: 掲示板 + election_totals: 総選挙数 + polling_stations: 投票所 + result_types: + blank_answers: 空白の回答 + blank_ballots: 空白の投票数 + null_ballots: 空の投票数 + total_ballots: 総投票数 + valid_ballots: 有効な投票数 + selected: 選択済 + title: %{election_title} 選挙の結果 + totals: 合計 + show: + change_election: 選択を変更 + publish_results: 結果を公開 + publishing: 結果を公開中... + update: + invalid: 結果の公表中に問題が発生しました。 + rejected: 結果の公開が掲示板によって拒否されました。もう一度試すか、システム管理者に連絡してください。 + success: 結果を公開しました。 + monitoring_committee_members: + create: + invalid: 監視委員会のメンバーの作成中に問題が発生しました。 + success: 監視委員会メンバーを作成しました。 + destroy: + invalid: 監視委員会メンバーの削除中に問題が発生しました。 + success: 監視委員会のメンバーを削除しました。 + form: + existing_user: 既存の参加者 + non_user: 新しい参加者を招待 + select_user: 名前、メールアドレス、ニックネームで検索 + user_type: 参加者の種類 + index: + title: 監視委員会 + new: + create: 作成 + title: 監視委員会メンバーを作成 + monitoring_committee_polling_station_closures: + actions: + title: アクション + validate: 検証 + view: 表示 + closures: + change_election: 選挙を変更 + signed: 署名済み? + title: %{election_title}選挙の投票所 + validated: 検証済み? + edit: + change_polling_station: 投票所に戻る + monitoring_committee_notes: 備考 + monitoring_committee_notes_placeholder: すべての事件をここで報告してください + title: 投票所 %{polling_station_title} で行われた 選挙 %{election_title} の結果 + elections: + title: 承認したい選挙を選択してください + show: + change_polling_station: 投票所に戻る + monitoring_committee_notes: 監視委員会からの発言 + validate: + error: 閉鎖のチェック中に問題が発生しました。 + success: 閉鎖が正しく検証されました。 + monitoring_committee_verify_elections: + index: + download: ダウンロード + how_to_checksum: 'ダウンロードプロセス中にダウンロードしたファイルが破損していないか、改ざんされていないかを確認するため、コンソールで次のコマンドを実行し、上記の報告されたチェックサムと出力が一致することを確認します。' + how_to_download: 選挙を検証するため、上記の表から検証可能なファイルをダウンロードしてください。 + how_to_run_verifier: 'いったんファイルをダウンロードし問題のないことが確認できたら、ユニバーサルベリファイアの実行に進めます。 このリポジトリ をクローンして、ルートのフォルダにて次のコマンドを実行します:' + how_to_title: 選挙の有効性を検証する方法 + not_available: まだ利用できません + title: 選挙 + polling_officers: + create: + invalid: この投票委員の作成中に問題が発生しました。 + success: 投票委員を作成しました。 + destroy: + invalid: 投票委員の削除中に問題が発生しました。 + success: 投票委員を削除しました。 + form: + existing_user: 既存の参加者 + non_user: 新しい参加者を招待 + select_user: 名前、メールアドレス、ニックネームで検索 + user_type: 参加者の種類 + index: + role_manager: マネージャー + role_president: 投票管理者 + title: 投票委員 + new: + create: 作成 + title: 投票委員を作成 + polling_officers_picker: + choose_polling_officers: 投票委員を選択してください + no_polling_officers: 検索条件に一致する投票委員がいないか、または投票委員が存在しません。 + polling_stations: + create: + invalid: 投票所を作成する際に問題が発生しました。 + success: 投票所を作成しました。 + destroy: + invalid: 投票所を削除する際に問題が発生しました。 + success: 投票所を削除しました。 + edit: + title: 投票所を編集 + update: 投票所を更新 + form: + address_help: '住所: ジオコーダーが場所を見つけるために使用' + location_help: '場所: 投票所の正確な場所を知らせるための投票者向けメッセージ' + location_hints_help: '場所のヒント: 追加情報。例:投票所がある建物のフロア。' + polling_station_managers_help: '投票所管理者: 投票所の管理を行う担当者。担当者がすでに投票委員として作成されていること、および他の投票所に割り当てられていないことを確認してください' + polling_station_president_help: '投票所の代表者:投票所の代表を務める委員。 委員がすでに投票所に配置されていること、および他の投票所に割り当てられていないことを確認してください' + select_president: 投票所の投票責任者として投票委員を選択してください + index: + title: 投票所 + new: + create: 作成 + title: 投票所の作成 + update: + invalid: この投票所の更新中に問題が発生しました。 + success: 投票所を更新しました。 + titles: + votings: 投票 + votings: + actions: + confirm_destroy: 本当に削除してよろしいですか? + destroy: 削除 + new_voting: 新しい投票スペース + create: + invalid: 投票の作成中に問題が発生しました。 + success: 投票を作成しました。 + edit: + add_election_component: この投票用に設定された選挙がありません。コンポーネントセクションに追加してください。 + assign_missing_officers: 投票所には、責任者やマネージャーがいない場合があります。投票所セクションからそれらを割り当ててください。 + update: 更新 + form: + banner_image: バナー画像 + census_contact_information: センサスの連絡先情報 + census_contact_information_help: この連絡先情報は、センサスについて問題を報告したい参加者のためのものです。 電子メールアドレス、別のサイトの連絡先フォーム、訪問者のための調査などが利用できます。 + introductory_image: 導入画像 + promoted: プロモート + select_a_voting_type: 投票方式を選択してください + show_check_census_help: 公開されている投票メニューに「投票できますか?」リンクを表示するかどうか。 + slug: スラグ + slug_help_html: 'URLスラグは、この投票を指すURLを生成するために使用されます。 英字、数字、ハイフンのみを受け付け、英字で始める必要があります。例: %{url}' + title: タイトル + voting_type: + hybrid: ハイブリッド + in_person: 対面 + online: オンライン + voting_type_label: 投票タイプ + new: + create: 作成 + title: 新しい投票 + update: + invalid: 投票の更新中に問題が発生しました。 + success: 投票が更新されました。 + admin_log: + ballot_style: + create: "%{user_name} がスペース %{space_name} にコード %{ballot_style_code} の投票スタイルを作成しました" + delete: "%{user_name} がスペース %{space_name} の コード %{ballot_style_code} の投票スタイルを削除しました" + update: "%{user_name} は、スペース %{space_name} のコード %{ballot_style_code} の投票スタイルを更新しました" + census: + create: "%{user_name} がスペース %{space_name} のセンサスを作成しました" + delete: "%{user_name} がスペース %{space_name} のセンサスを削除しました" + update: "%{user_name} がスペース %{space_name} のセンサスを更新しました" + monitoring_committee_member: + create: "%{user_name} がユーザー %{monitoring_committee_member_user} をスペース %{space_name} の監視委員に指名しました" + delete: "%{user_name} は、 %{monitoring_committee_member_user} をスペース %{space_name} の監視委員の割り当てから解除しました" + polling_officer: + create: "%{user_name} がユーザー %{polling_officer_user} をスペース %{space_name} の投票委員に割り当てました" + delete: "%{user_name} は、 %{polling_officer_user} をスペース %{space_name} の投票委員の割り当てから解除しました" + polling_station: + create: "%{user_name} がスペース %{resource_name} で投票所 %{space_name} を作成しました" + delete: "%{user_name} がスペース %{resource_name} の投票所 %{space_name} を削除しました" + update: "%{user_name} がスペース %{resource_name} の投票所 %{space_name} を更新しました" + voting: + create: "%{user_name} が %{resource_name} の投票を作成しました" + publish: "%{user_name} が %{resource_name} の投票を公開しました" + unpublish: "%{user_name} が %{resource_name} の投票を非公開にしました" + census: + admin: + census: + create: + invalid: センサスのアップロード中にエラーが発生しました。後でもう一度お試しください。 + invalid_csv_header: CSVヘッダーが見つからないか正しくありません。手順をよくお読みください。 + creating_data: + info_message: "しばらくお待ち下さい, ファイル %{file} の %{raw_count} 行中 %{processed_count} 行目を処理しています (数分かかる場合もあります)." + delete: + button: センサスデータをすべて削除する + confirm: センサスデータをすべて削除します。元に戻すことはできません。続行してもよろしいですか? + destroy: + error: センサスの削除中にエラーが発生しました。後でもう一度お試しください。 + success: センサスデータを削除しました。 + export_access_codes: + button: 投票アクセスコードをエクスポート + callout: これで、アクセスコードのエクスポートに進むことができます。これは一度だけ行うことができます。 エクスポートを開始すると、指示が記載されたメールが %{email} に届きます。 + confirm: アクセスコードは一度だけエクスポートできます。メールアカウント %{email} へのアクセス権があることを確認してください. + file_not_exist: このファイルは存在しません。 + launch_error: アクセスコードのエクスポートを起動する際に問題が発生しました。 + launch_success: アクセスコードのエクスポートが開始されました。まもなく %{email} にメールが届きます。 + exporting_access_codes: + info_message: "しばらくお待ち下さい, エクスポートを準備しており, 間もなく %{email} に送信されます (数分かかる場合があります)" + freeze: + callout: センサスは凍結しており、変更することはできません。 + help_html: | + センサスデータのアップロード、コードの生成、およびエクスポートが正常に行われました。
    これで選挙を開始する準備が整いました。
    + エクスポートされた個人のコード付きの CSV を使用して、独自の方法でセンサスに沿って配布するか、「投票できる」タブを有効にして、誰でも自分のセンサスデータでこのコードを取得できるようにします。 + generate_access_codes: + button: 投票アクセスコードを生成する + callout: アクセスコードの生成に進めることができるようになりました。 アクセスコードを生成すると、センサスを変更することができなくなります。 + confirm: 続行すると、センサスを変更することはできません。 + info_message_all: "すべての行をインポートできました: ファイル %{file} (%{raw_count} / %{data_count})." + info_message_warn: '%{data_count} レコードが作成されましたが、アップロードされたファイル %{file} は %{raw_count} 行だったため、データが失われていないことを確認してください。' + launch_error: アクセスコードの生成中に問題が発生しました + launch_success: コードの生成が開始されました + start_over: 現在のセンサスを削除し、有効な行からなる適切なCSVファイルでやり直してください。 + generating_access_codes: + info_message: "しばらくお待ちください, 投票アクセスコードを生成しています (数分かかることがあります)。" + new: + file_help: + explanation: 'ファイルのガイダンス:' + message_1: CSV (.csv) ファイルのみが許可されます。 + message_2: カラムの区切り文字はセミコロン(";") でなければなりません。 + has_ballot_styles_message: 投票スタイルを設定しました。CSVの「%{ballot_style_code_header}」フィールドが、希望する投票スタイルのコードに対応していることを確認してください。 + info_message: "センサスはまだありません。 以下のフォームを使用してCSVファイルをインポートしてください。" + missing_ballot_styles_message: 'この投票の投票スタイルがありません。もし条件付きの質問がある場合 (例: 居住地域などにより投票者に異なる質問を提示する場合), 投票スタイルをセンサスをインポートする 前に 設定する必要があります。すべての投票者に対して同じ質問を定時したい場合は、センサスのインポート手続きを行ってください。' + submit: CSVを送信 + title: センサスを作成 + show: + heading: センサスに投票する + upload_info: + csv_example_with_ballot_style: '投票スタイルのあるファイルの例:' + csv_example_without_ballot_style: '投票スタイルのないファイルの例:' + csv_header_after: 投票スタイル/条件付き質問が必要ない場合は、最後のフィールド ("%{ballot_style_code_header}") を含めないでください + csv_header_before: 'センサスファイルは次のヘッダを持つCSVファイルでなければなりません:' + document_types: + identification_number: 識別番号 + passport: パスポート + export_mailer: + access_codes_export: + click_button: '次のリンクをクリックするとあなたのアクセスコードがダウンロードできます。
    このファイルは %{date} まで利用できます。
    ファイルを開くには 7-Zip (Windowsユーザー向け), Keka (MacOSユーザー向け) または PeaZip (Linuxユーザー向け)等が必要です。パスワード:%{password}' + download: ダウンロード + subject: '%{voting_title} の投票アクセスコードのエクスポートが可能です' + vote_flow: + already_voted_in_person: この参加者はすでに対面で投票を行っており、投票権がありません。 + datum_not_found: 指定されたデータが投票者と一致しません。 + content_blocks: + highlighted_votings: + name: 注目の投票 + landing_page: + polling_stations: + heading: 投票所 + no_polling_stations: 投票所がまだありません。 + monitoring_committee_members: + actions: + confirm_destroy: 本当に削除してよろしいですか? + destroy: 削除 + new: 新規メンバー + title: アクション + pages: + home: + highlighted_votings: + active_spaces: 有効な投票 + see_all_spaces: すべての投票を表示 + polling_officer_zone: + closures: + back_to_polling_stations: 投票所に戻る + certify: + add_photos: 写真を追加 + edit_photos: 写真を編集 + error: 証明書の添付中にエラーが発生しました。もう一度やり直してください。 + heading: 投票の再集計 - 証明書をアップロード + info_text: 選挙閉鎖証明の写真をアップロードしてください + submit: 証明書をアップロードする + success: 証明書をアップロードしました。 + upload_photos: 選挙閉鎖証明書の写真をアップロードする + completed: + sub_heading: この再集計は認証されており、編集できません。 + create: + error: 閉鎖の作成中にエラーが発生しました。後でもう一度お試しください。 + success: 閉鎖を作成しました。 + destroy: + error: 閉鎖の削除中にエラーが発生しました。 + success: 閉鎖を削除しました。 + edit: + confirm_start_over: 合計票数と添付されたメモを削除します。よろしいですか? + heading: 投票の再集計 - 回答の再集計 + info_text: 各質問の回答総数を詳しく説明してください。 これは、前のステップで導入された回答の合計数と一致する必要があります(%{total} 回答合計)。 + modal_ballots_results_count_error: + blank: 空白票の合計は %{expected} ですが、空白の質問の合計は %{current} です。 + close_modal: 閉じる + info_text: 投票数が封筒の合計数と一致しません。投票数の合計を確認してください。 + title: 投票数の合計が足りません + total: 期待される合計は %{expected} ですが、有効票と空白票と無効票の合計は %{current} です。 + valid: 有効な投票数の合計は %{expected} ですが、有効な質問の合計は %{current} です。 + save_recount: 再集計を保存 + start_over: 合計数にエラーがある場合は、すべてを削除してやり直すことができます。 + total_ballots: 総投票数 + total_blank_ballots: 白票の総数 + total_null_ballots: 空の投票の総数 + total_valid_ballots: 有効票の総数 + new: + election: '選挙:' + heading: 投票再集計 + info_text: 'こちらの投票所で再集計された総投票数(封筒)を入力してください:' + modal_ballots_count_error: + btn_validate_total: 投票の再集計を検証します。 + info_explanation_text: '投票の合計数を確認してください。合計数が正しくない場合は、監視委員会に説明する必要があります。' + info_text: 投票所で投票した人数の記録と、投票の(封筒の)総数が一致しません。 + message_for_monitoring_committee: 監視委員会へのメッセージ + review_recount: 再集計を確認する + text_area_placeholder: メッセージを入力してください + title: 合計レコードが追加されていません + total_ballots: '総投票数:' + total_people: '総人数:' + polling_station: '投票所:' + submit: 合計数を確認する + total_ballots_count: 投票数 + show: + edit_count_votes: 間違った数字ですか? まだ編集できます。 + heading: 投票再集計 + sub_heading: 証明書をアップロードして、再集計を終わらせる必要があります。これを行うと、再集計は確定され、それ以降は編集ができなくなります。 + sign: + cancel: キャンセル + check_box: 私はこれを確認し、物理的な選挙閉鎖証明書と同じであることを確認しました。 + confirm: はい、続行します + error: エラーが発生しました。もう一度やり直してください。 + heading: 投票の再集計 - 閉鎖を署名 + info_text: 続行すると、情報を変更できなくなります。このアクションは取り消せません。 + submit: 閉鎖に署名する + success: 閉鎖の署名に成功しました。 + update: + error: 閉鎖結果の更新中にエラーが発生しました。後でもう一度お試しください。 + success: 閉鎖結果が正常に更新されました。 + in_person_votes: + complete_voting: + available_answers: '利用可能な回答:' + census_verified: この参加者はまだ対面投票されていません。 + census_verified_with_online_vote: この参加者はすでにオンラインで投票しています。参加者が直接投票した場合、以前の投票は無効になり、こちらが最終投票となります。 + complete_voting: 投票を完了 + identify_another: 他の参加者を特定する + questions_title: '彼らは次の質問に投票する権利があります:' + questions_title_voted: 'この参加者はすでにオンラインに投票しており、次の質問に投票する権利があります:' + voted: 参加者が投票しました + create: + error: 投票は登録されませんでした。もう一度やり直してください。 + in_person_form: + census_not_present: この参加者はセンサスに記載されていません。 + census_not_present_description: 彼女はセンサスの苦情窓口に行くか、サポートに連絡しなければなりません。 + date_of_birth: 生年月日 + day: 日 + day_placeholder: DD + document_number: ドキュメント番号 + document_number_placeholder: ID 番号 + month: 月 + month_placeholder: MM + select: ドキュメントの種類を選択します + title: 'ドキュメントの種類を選択し、参加者のドキュメント番号を入力します:' + validate_document: ドキュメントを検証する + year: 年 + year_placeholder: YYYY + new: + back: 投票所に戻る + title: 参加者を特定して確認する + show: + back: 投票所に戻る + title: 個人の投票が登録されるのを待っています + update: + error: 投票の登録中にエラーが発生しました。もう一度やり直してください。 + success: + accepted: 投票は正常に登録されました。 + rejected: 投票は掲示板に受理されませんでした。システム管理者にご連絡ください。 + verify_document: + census_present: この参加者はセンサスにリストされています。 + name: 名前 + title: '以下のデータが正しいことを確認してください:' + verify_document: ドキュメントを検証する + menu: + polling_officer_zone: 投票委員ゾーン + polling_officers: + index: + polling_officer_role_description: あなたは、このプラットフォームで行われるいくつかの選挙で、投票所の責任者(またはマネージャー) として行動するように割り当てられています。 + polling_station: + address: 住所 + count_votes: 投票数の集計 + election: 選挙 + identify_person: 個人を特定する + name: 名前 + no_polling_stations: まだどの投票所にも割り当てられていません。 + role: 役割 + show_closure: 閉鎖情報を表示 + title: 投票所 + voting: 投票 + polling_officers: + actions: + confirm_destroy: 本当に削除してよろしいですか? + destroy: 削除 + new: 新しい投票委員 + title: アクション + roles: + manager: マネージャー + president: 投票責任者 + unassigned: 未割り当て + polling_station_closure_certificate: + current_certificate: '現在の証明書:' + polling_station_closure_recount: + nota_option: 空欄 / 上記のどれでもない + polling_officer_notes: '投票委員のメモ:' + polling_officer_notes_blank: メモはありません + recount_summary: '再集計の概要:' + signed: 署名済み + total_ballots: '総投票数:' + total_blank_ballots: '白票の投票数の合計:' + total_null_ballots: '空の投票数の合計:' + total_valid_ballots: '有効な投票数の合計:' + polling_stations: + actions: + confirm_destroy: 本当に削除してよろしいですか? + destroy: 削除 + edit: 編集 + new: 新しい投票所 + title: アクション + votings: + access_code_modal: + email: '%{email} にメールで送信' + info: 参加するにはアクセスコードが必要です。郵便で受け取っていない場合は、新しいものをお送りします。 + no_email: メールアドレスがありません + no_sms: 電話番号がありません + sms: SMSで %{sms} に送信 + title: アクセスコードを取得 + check_census: + check_status: ステータスの確認 + description: ここでは、あなたがこの投票に参加する権利があるかどうかを知るためにあなたのセンサスデータをチェックするオプションがあります。 すでにアクセスコードを持っているはずですが、紛失した場合は、データが正しいときにもう一度それを求めることができます。 + error: + info: 'もう一度やり直してください。システム内のデータが正しくないと思われる場合は、こちらからご報告ください: %{census_contact_information}.' + title: 入力したデータはこの投票のためのセンサスにありません + form_title: 'あなたのセンサスデータを確認するには、次のフォームに記入してください:' + invalid: センサスの確認中に問題がありました。 + success: + access_link: メール経由 + access_link_with_sms: SMSまたは電子メールで + info: すでに郵便でアクセスコードを受け取っているはずです。お持ちでない場合、こちらからリクエストできます。 + title: あなたのセンサスデータは正しいです! + title: 投票できますか? + check_fields: + date_of_birth: 生年月日 + day: 日 + day_placeholder: DD + document_number: ドキュメント番号 + document_number_placeholder: ID 番号 + document_type: ドキュメントの種類 + month: 月 + month_placeholder: MM + postal_code: 郵便番号 + postal_code_placeholder: 郵便番号 + select: ドキュメントの種類の選択 + year: 年 + year_placeholder: YYYY + count: + title: + other: "%{count} 票" + elections_log: + description: 選挙ログには、各投票に関するすべての関連情報が表示されます。 例えば、キーセレモニーや集計の状態、または結果がすでに公開されているがどうかなどです。 ログ情報を表示する選挙をクリックしてください。 + title: 選挙ログ + filters: + active: 有効 + all: 全て + date: 日付 + finished: 完了 + search: 検索 + upcoming: 今後の予定 + index: + no_votings: 検索条件に一致する投票はありません。 + only_finished: 現在、予定された投票はありませんが、ここでは記載されている完了した投票を探すことができます。 + title: 投票 + login: + access_code: アクセスコード + access_code_placeholder: アクセスコード: + ask_for_a_new_one: 新しいものを要求します。 + dont_have_access_code: アクセスコードがありませんか? + form_title: '投票にアクセスするには、次のフォームに入力してください:' + start_voting: 投票の開始 + title: 自分の投票センサスのデータで自分を特定する + no_census_contact_information: 連絡先はまだありません。 + orders: + label: '投票の並べ替え:' + random: ランダム + recent: 新着順 + send_access_code: + invalid: アクセスコードの送信中に問題が発生しました。 + success: アクセスコードが正常に送信されました。 + show: + title: この投票について + votings_m: + badge_name: + finished: 完了 + ongoing: 進行中 + upcoming: 今後の予定 + unspecified: 指定されていません + voting_type: + hybrid: ハイブリッド + in_person: 対面 + online: オンライン + layouts: + decidim: + voting_navigation: + check_census: 投票できますか? + election_log: 選挙ログ + votings: + index: + promoted_votings: 強調された投票 + promoted_voting: + vote: 投票 diff --git a/decidim-elections/config/locales/ka-GE.yml b/decidim-elections/config/locales/ka-GE.yml new file mode 100644 index 00000000..57a95cb0 --- /dev/null +++ b/decidim-elections/config/locales/ka-GE.yml @@ -0,0 +1 @@ +ka: diff --git a/decidim-elections/config/locales/kaa.yml b/decidim-elections/config/locales/kaa.yml new file mode 100644 index 00000000..455cb565 --- /dev/null +++ b/decidim-elections/config/locales/kaa.yml @@ -0,0 +1 @@ +kaa: diff --git a/decidim-elections/config/locales/ko-KR.yml b/decidim-elections/config/locales/ko-KR.yml new file mode 100644 index 00000000..8a7b3b86 --- /dev/null +++ b/decidim-elections/config/locales/ko-KR.yml @@ -0,0 +1 @@ +ko: diff --git a/decidim-elections/config/locales/ko.yml b/decidim-elections/config/locales/ko.yml new file mode 100644 index 00000000..8a7b3b86 --- /dev/null +++ b/decidim-elections/config/locales/ko.yml @@ -0,0 +1 @@ +ko: diff --git a/decidim-elections/config/locales/lb-LU.yml b/decidim-elections/config/locales/lb-LU.yml new file mode 100644 index 00000000..d67b296c --- /dev/null +++ b/decidim-elections/config/locales/lb-LU.yml @@ -0,0 +1,25 @@ +lb: + decidim: + elections: + elections: + show: + callout: + already_voted: Sie haben bei dieser Wahl bereits abgestimmt. Sie können Ihre Stimme ändern oder sie verifizieren. + vote_rejected: Es war nicht möglich, Ihre Stimme zu verifizieren. Bitte geben Sie ihre Stimme erneut ab. + verify: + already_voted: Bereits abgestimmt? + verify_here: Stimme hier verifizieren. + will_verify: Sie können ihre Stimme nach dem Start der Wahl verifizieren. + votings: + admin: + menu: + votings_submenu: + monitoring_committee: Überwachungskomitee + polling_officer_zone: + polling_officers: + index: + polling_officer_role_description: Ihnen wurde eine Rolle als Wahlhelfer (Präsident oder Manager) in Wahlen dieser Plattform zugewiesen. + polling_station: + no_polling_stations: Sie sind noch zu keinem Wahllokal zugewiesen. + role: Ihre Rolle + voting: Abstimmung diff --git a/decidim-elections/config/locales/lb.yml b/decidim-elections/config/locales/lb.yml new file mode 100644 index 00000000..d67b296c --- /dev/null +++ b/decidim-elections/config/locales/lb.yml @@ -0,0 +1,25 @@ +lb: + decidim: + elections: + elections: + show: + callout: + already_voted: Sie haben bei dieser Wahl bereits abgestimmt. Sie können Ihre Stimme ändern oder sie verifizieren. + vote_rejected: Es war nicht möglich, Ihre Stimme zu verifizieren. Bitte geben Sie ihre Stimme erneut ab. + verify: + already_voted: Bereits abgestimmt? + verify_here: Stimme hier verifizieren. + will_verify: Sie können ihre Stimme nach dem Start der Wahl verifizieren. + votings: + admin: + menu: + votings_submenu: + monitoring_committee: Überwachungskomitee + polling_officer_zone: + polling_officers: + index: + polling_officer_role_description: Ihnen wurde eine Rolle als Wahlhelfer (Präsident oder Manager) in Wahlen dieser Plattform zugewiesen. + polling_station: + no_polling_stations: Sie sind noch zu keinem Wahllokal zugewiesen. + role: Ihre Rolle + voting: Abstimmung diff --git a/decidim-elections/config/locales/lo-LA.yml b/decidim-elections/config/locales/lo-LA.yml new file mode 100644 index 00000000..27a02bfe --- /dev/null +++ b/decidim-elections/config/locales/lo-LA.yml @@ -0,0 +1 @@ +lo: diff --git a/decidim-elections/config/locales/lt-LT.yml b/decidim-elections/config/locales/lt-LT.yml new file mode 100644 index 00000000..6c5cb837 --- /dev/null +++ b/decidim-elections/config/locales/lt-LT.yml @@ -0,0 +1 @@ +lt: diff --git a/decidim-elections/config/locales/lt.yml b/decidim-elections/config/locales/lt.yml new file mode 100644 index 00000000..392171a2 --- /dev/null +++ b/decidim-elections/config/locales/lt.yml @@ -0,0 +1,1374 @@ +lt: + activemodel: + attributes: + answer: + description: Aprašymas + image: Paveikslėlis + proposals: Susiję pasiūlymai + title: Pavadinimas + ballot_style: + code: Kodas + election: + description: Aprašymas + end_time: Balsavimas baigiasi + start_time: Balsavimas prasideda + title: Pavadinimas + monitoring_committee_member: + email: El. paštas + name: Pavadinimas + polling_officer: + email: El. paštas + name: Pavadinimas + polling_station: + address: Adresas + location: Vieta + location_hints: Vietos užuomina + polling_station_managers: Vadybininkai + polling_station_president_id: Pirmininkas + title: Pavadinimas + question: + max_selections: Didžiausias pasirenkimų skaičius + min_selections: Nei vienas iš pasiūlytų variantų + title: Pavadinimas + trustees_participatory_space: + user_id: Dalyvis + voting: + banner_image: Reklamjuostės paveikslėlis + census_contact_information: Surašytojų kontaktinė informacija + description: Aprašymas + end_time: Balsavimas baigiasi + introductory_image: Įvadinis paveikslėlis + promoted: Paremta + scope_id: Sritis + show_check_census: Rodyti "patikrinti surašymą" puslapį + start_time: Balsavimas prasideda + title: Pavadinimas + voting_type: Balsavimo tipas + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Reikia pridėti iš naujo + election: + attributes: + attachment: + needs_to_be_reattached: Reikia pridėti iš naujo + trustee: + attributes: + name: + cant_be_changed: negali būti pakeistas + public_key: + cant_be_changed: negali būti pakeistas + voting: + attributes: + voting_type: + inclusion: "%{value} nėra tinkamas balsavimo tipas" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: Balsavimo prižiūrėtojas jau yra balsavimo punkto prezidentas/vadybininkas. + voting: + different_organization: Balsavimas turi būti toje pačioje organizacijoje kaip naudotojas. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: Balsavimo prižiūrėtojas turi būti tame pačiame balsavime kaip balsavimo punktas. + models: + decidim/elections/answer: + one: Atsakymas + few: Atsakymai + many: Atsakymai + other: Atsakymai + decidim/elections/election: + one: Rinkimai + few: Rinkimai + many: Rinkimai + other: Rinkimai + decidim/elections/question: + one: Klausimas + few: Klausimai + many: Klausimai + other: Klausimai + decidim/voting: + one: Balsavimas + few: Balsavimai + many: Balsavimai + other: Balsavimai + decidim/votings/census/dataset: + one: Duomenų rinkinys + few: Duomenų rinkiniai + many: Duomenų rinkiniai + other: Duomenų rinkiniai + decidim/votings/census/datum: + one: Duomuo + few: Duomenys + many: Duomenys + other: Duomenys + decidim/votings/polling_officer: + one: Balsavimo prižiūrėtojas + few: Balsavimo prižiūrėtojai + many: Balsavimo prižiūrėtojai + other: Balsavimo prižiūrėtojai + decidim/votings/polling_station: + one: Balsavimo punktas + few: Balsavimo punktas + many: Balsavimo punktas + other: Balsavimo punktas + decidim/votings/voting: + one: Balsavimas + few: Balsavimai + many: Balsavimai + other: Balsavimai + decidim: + admin: + filters: + officers_assigned_eq: + label: Prižiūrėtojai + values: + assigned: Priskirta + unassigned: Nepriskirtas + role_eq: + label: Rolė + values: + manager: Vadybininkas + president: Prezidentas + unassigned: Nepaskirtas + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Ieškoti %{collection} vardu/el. paštu arba balsavimo punktu. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Ieškoti %{collection} pavadinimu, adresu arba prižiūrėtojo vardu/el. paštu. + signed_eq: + label: Pasirašyta + values: + 'false': Pasirašyta + 'true': Nepasirašyta + validated_eq: + label: Patvirtinta + values: + 'false': Nepatvirtinta + 'true': Patvirtinta + components: + elections: + actions: + vote: Balsuoti + name: Rinkimai + settings: + global: + announcement: Pranešimas + step: + announcement: Pranešimas + elections: + actions: + confirm_destroy: Ar esate tikri? + destroy: Sunaikinti + edit: Redaguoti + feedback: Balsuotojų atsiliepimai + import: Importuoti pasiūlymus į atsakymus + manage_answers: Tvarkyti atsakymus + manage_questions: Tvarkyti klausimus + manage_steps: Tvarkyti žingsnius + preview: Peržiūra + publish: Publikuoti + title: Veiksmai + unpublish: Depublikuoti + admin: + answers: + create: + invalid: Kuriant šią atsakymą iškilo problema. + success: Atsakymas sukurtas. + destroy: + invalid: Šalinant šį atsakymą iškilo problema. + success: Atsakymas pašalintas. + edit: + title: Redaguoti atsakymą + update: Atnaujinti atsakymą + index: + invalid_max_selections: Jums reikia dar %{missing_answers} atsakymų, kad atitikti pasirinkimą. + title: Atsakymai + new: + create: Sukurti atsakymą + title: Naujas atsakymas + not_selected: Nepasrinkta + select: + disable: Nebepasirinkti atsakymo + enable: Pažymėti atsakymą kaip pasirinktą + invalid: Pasirenkant šį atsakymą iškilo problema. + success: Atsakymas sėkmingai pasirinktas. + selected: Pasirinkta + unselect: + invalid: Nebepasirinktant šio atsakymo iškilo problema. + success: Atsakymas sėkmingai nebepasirinktas. + update: + invalid: Atnaujinant šį atsakymą iškilo problema. + success: Atsakymas atnaujintas. + elections: + create: + invalid: Kuriant šiuos rinkimus iškilo problema. + success: Rinkimai sukurti. + destroy: + invalid: Šalinant šiuos rinkimus iškilo problema. + success: Rinkimai pašalinti. + edit: + title: Redaguoti rinkimus + update: Atnaujinti rinkimus + form: + organization_time_zone: 'Patikrinkite ar organizacijos laiko juosta teisinga organizacijos nustatymuose. Dabartinė konfigūracija: %{time_zone} (%{time}).' + index: + no_bulletin_board: Nėra šiam moduliui reikalingo sukonfigūruoto Skelbimų Lentos serverio. Šis veiksmas turėtų būti atliktas sistemos administratoriaus. + title: Rinkimai + new: + create: Sukurti rinkimus + title: Nauji rinkimai + publish: + success: Rinkimai paskelbti. + unpublish: + success: Rinkimų skelbimas panaikintas. + update: + invalid: Atnaujinant šiuos rinkimus iškilo problema. + success: Rinkimai atnaujinti sėkmingai. + exports: + elections: Rinkimai + feedback_form_answers: Atsiliepimų formos atsakymai + mailers: + trustee_mailer: + subject: Buvote pridėtas kaip %{resource_name} patikėtinis + trustee_zone: Eiti į patikėtinių zoną + menu: + trustees: Patikėtiniai + models: + answer: + name: Atsakymas + proposals_imports: + create: + invalid: Importuojant pasiūlymus į atsakymus iškilo problema. + success: "%{number} pasiūlymai sėkmingai importuoti į atsakymus." + new: + create: Importuoti pasiūlymus į atsakymus + no_components: Šioje dalyvaujamojoje erdvėje nėra kitų pasiūlymų komponentų, pagal kuriuos pasiūlymai būtų įtraukiami į atsakymus. + select_component: Pasirinkite komponentą + title: Importuoti pasiūlymus + questions: + create: + election_started: Rinkimai jau prasidėjo. + invalid: Kuriant šį klausimą iškilo problema. + success: Klausimas sukurtas. + destroy: + invalid: Šalinant šį klausimą iškilo problema. + success: Klausimas pašalintas. + edit: + title: Redaguoti klausimą + update: Atnaujinti klausimą + index: + title: Klausimai + new: + create: Sukurti klausimą + title: Naujas klausimas + update: + invalid: Atnaujinant šį klausimą iškilo problema. + success: Klausimas atnaujintas. + steps: + create_election: + census: Surašymas + errors: + census_codes_generated: Rinkimų kodai surašymui nėra sugeneruoti. + census_frozen: Rinkimų kodai surašymui nėra eksportuoti. + census_uploaded: Šiems rinkimams nėra surašymo. + component_published: Rinkimų komponentas nėra publikuotas. + max_selections: Klausimai neturi teisingos vertės atsakymų kiekiui + minimum_answers: Klausimai turi turėti bent du atsakymus. + minimum_questions: Rinkimai turi turėti bent vieną klausimą. + published: Rinkimai nėra publikuoti. + time_before: Pradžios laikas už mažiau nei %{hours} valandų. + trustees_number: Dalyvaujomi erdvė turi turėti bent %{number} patikėtinių su viešų raktu. + invalid: Konfigūruojant šiuos rinkimus iškilo problema + no_trustees: Šiai dalyvaujamajai erdvei nėra sukonfigūruotų Patikėtinių + not_used_trustee: "(nenaudojama)" + public_key: + 'false': neturi viešo rakto + 'true': turi viešą raktą + requirements: + census_codes_generated: Rinkimų kodai surašymui sugeneruoti. + census_frozen: Rinkimų kodai surašymui eksportuoti ir surašymas sustabdytas. + census_uploaded: Surašymas įkeliamas. + component_published: Rinkimų komponentas paskelbtas. + max_selections: Visi klausimai turi teisingą kiekį maksimalių klausimų. + minimum_answers: Kiekvienas klausimas turi bent du atsakymus. + minimum_questions: Rinkimai turi bent vieną klausimą. + published: Rinkimai publikuoti. + time_before: Konfigūracija atliekama bent %{hours} valandas prieš rinkimų pradžią. + trustees_number: Dalyvaujomi erdvė turi bent %{number} patikėtinių su viešų raktu. + submit: Konfigūruoti rinkimus + success: Rinkimai sėkmingai nusiųsti į Skelbimų Lentą. + title: Konfigūruoti rinkimus + trustees: Rinkimų patikėtiniai + created: + invalid: Pradedant raktų ceremoniją iškilo problema. + submit: Pradėti raktų ceremoniją + success: Raktų ceremonijos pradžios užklausa išsiųsta į Skelbimų Lentą. + title: Rinkimai sukurti + trustees: Patikėtiniai + key_ceremony: + continue: Tęsti + title: Raktų ceremonija + key_ceremony_ended: + errors: + time_before: Rinkimai paruošti pradžiai. Reikia palaukti %{hours} valandas prieš pradžios laiką %{start_time} tam, kad pradėti balsavimą. + invalid: Pradedant balsavimo periodą iškilo problema. + requirements: + time_before: Rinkimai prasidės neužilgo. Galite pradėti balsavimo perioda rankiniu būdu arba jie prasidės automatiškai %{start_time}. + submit: Pradėti balsavimo periodą + success: Balsavimo pradžios periodo užklausa buvo sėkmingai išsiųsta Skelbimų Lentai. + title: Paruošta + processing: Apdorojama... + results_published: + answer: Atsakymas + not_selected: Nepasrinkta + question: Klausimas + result: Rezultatas + selected: Pasirinkta + submit: Pateikti + title: Rezultatai publikuoti + tally_ended: + answer: Atsakymas + not_selected: Nepasrinkta + question: Klausimas + result: Rezultatas + selected: Pasirinkta + submit: Skelbti rezultatus + success: Balsavimo rezultatų publikavimo užklausa buvo sėkmingai išsiųsta Skelbimų Lentai. + title: Suskaičiuoti rezultatai + tally_started: + continue: Tęsti + invalid: Raportuojant trūkstantį patikėtinį iškilo problema. + mark_as_missing: Pažymėti neesančiu + mark_as_missing_description: Visi patikėtiniai turėtų dalyvauti procese, bet jei patikėtinis to padaryti negali, galite jį pažymėti kaip trūkstantį. + success: Trūkstančio patikėtinio ataskaita išsiųsta į Skelbimų Lentą. + tally_completion: Šis procesas bus įvykdytas kai visi patikėtiniai bus aktyvūs arba pažymėti kaip trūkstantys. Reikia bent %{quorum} patikėtinių, kad įvykdyti šį procesą. + title: Perskaičiavimo procesas + undo_mark_as_missing: Patikėtinis netyčia pažymėtas kaip trūkstantis gali dalyvauti prieš pasibaigiant procesui. Jis gali atlikti savo užduotis kaip įprasta. + vote: + errors: + time_after: Rinkimai vis dar vyksta. Turite palaukti iki %{end_time}, kad baigti balsavimo periodą. + invalid: Baigiant balsavimo periodą iškilo problema. + requirements: + time_after: Rinkimai baigėse. Galite uždaryti balsavimą rankiniu būdu arba jis bus uždarytas automatiškai. + submit: Uždaryti balsavimo periodą + success: Balsavimo uždarymo periodo užklausa buvo sėkmingai išsiųsta Skelbimų Lentai. + title: Balsavimo periodas + vote_ended: + invalid: Pradedant perskaičiviamą iškilo problema. + submit: Pradėti perskaičiavimą + success: Perskaičiviamo pradžios užklausa buvo sėkmingai išsiųsta Skelbimų Lentai. + text: Balsavimas baigėsi. Galite pradėti skaičiavimą. + title: Balsavimo periodas baigėsi + vote_stats: + no_vote_statistics_yet: Dar nėra balsavimo statistikos + title: Balsavimo statistika + voters: Balsuotojai + votes: Balsai + trustees_participatory_spaces: + actions: + disable: Išjungti + enable: Atsižvelgti + create: + exists: Patikėtinis dalyvaujamojoje erdvėje yra. + invalid: Kuriant patikėtinį iškilo problema. + success: Patikėtinis sėkmingai sukurtas. + delete: + invalid: Šalinant patikėtinį iškilo problema. + success: Patikėtinis sėkmingai pašalintas. + form: + select_user: Pasirinkti naudotoją + index: + title: Patikėtiniai + new: + create: Sukurti Patikėtinį + title: Naujas Patikėtinis + update: + invalid: Atnaujinant%{trustee} patikėtinį iškilo problema. + success: Patikėtinis %{trustee} sėkmingai atnaujintas. + admin_log: + election: + create: "%{user_name} sukūrė rinkimus %{resource_name} erdvėje %{space_name}" + delete: "%{user_name} ištrynė rinkimus %{resource_name} erdvėje %{space_name}" + end_vote: "%{user_name} baigė balsavimo periodą %{resource_name} rinkimams %{space_name} erdvėje Skelbimų Lentoje" + publish: "%{user_name} paskelbė rinkimus %{resource_name} erdvėje%{space_name}" + publish_results: "%{user_name} paskelbė rinkimų %{resource_name} erdėje %{space_name} rezultatus Skelbimų Lentoje" + report_missing_trustee: "%{user_name} pranešė %{trustee_name} kaip trūkstantį patikėtinį rinkimų %{resource_name} esančių %{space_name} erdvėje skaičiavimui Skelbimų Lentoje" + setup: "%{user_name} sukūrė rinkimus %{resource_name} erdvėje %{space_name} Skelbimų Lentoje" + start_key_ceremony: "%{user_name} pradėjo raktų ceremoniją rinkimams %{resource_name} erdvėje %{space_name} Skelbimų Lentoje" + start_tally: "%{user_name} pradėjo skaičiavimą rinkimams %{resource_name} erdvėje %{space_name} Skelbimų Lentoje" + start_vote: "%{user_name} pradėjo balsavimo periodą rinkimams %{resource_name} erdvėje%{space_name} Skelbimų Lentoje" + unpublish: "%{user_name} depublikavo %{resource_name} rinkimus %{space_name} erdvėje" + update: "%{user_name} atnaujino %{resource_name} rinkimus %{space_name} erdvėje" + trustee: + create: "%{user_name} priskyrė naudotoją %{trustee_user} kaip patikėtinį" + connection: + failed: + modal: + close: Uždaryti + communication_lost: Deja, bet panašu, kad ryšys su balsavimo serveriu (Skelbimų Lenta) - prarastas.
    Gali būti, kad taip yra dėl interneto ryšio arba serverio apkrovos.
    Pabandykite vėliau arba susisiekite su serverių prižiūrėtojais. + generic_error: Iškilo nežinoma klaida. Gali būti, kad taip yra todėl, kad Jūsų naršyklė nepalaikoma arba naudojate "incognito" nustatymus. + title: Įvyko klaida + election_m: + badge_name: + finished: Užbaigta + ongoing: Aktyvūs + upcoming: Artėjantys + end_date: Baigiasi + footer: + remaining_time: + one: "Iki balsavimo liko %{count} valandos %{minutes} minutės." + few: "Iki balsavimo liko %{count} valandos %{minutes} minutės." + many: "Iki balsavimo liko %{count} valandos %{minutes} minutės." + other: "Iki balsavimo liko %{count} valandos %{minutes} minutės." + view: Peržiūrėti + vote: Balsuoti + label: + date: Datos + questions: Klausimai %{count} + start_date: Pradžia + unspecified: Nepatikslinta + elections: + count: + elections_count: + one: "%{count} rinkimai" + few: "%{count} rinkimai" + many: "%{count} rinkimai" + other: "%{count} rinkimai" + election_log: + chained_hash: Žinutės Hash grandinė + complete: Baigti + creation_description: + complete: Rinkimai sukurti ir sėkmingai sukonfiguruoti Skelbimų Lentoje. + not_created: Rinkimai dar nesukurti. + creation_title: Rinkimai sukurti + description: Čia yra rinkimų ataskaita, kurije galite peržiūrėti kiekvieno žingsnio būseną. + download: Atsisiųsti + key_ceremony_description: + complete: Raktų ceremonija baigta. Visi patikėtiniai turi tinkamus raktus ir parsisiuntė reikalingus atsarginius raktus. + not_started: Raktų ceremonija dar neprasidėjo. + started: Raktų ceremonija prasidėjo bet dar nesibaigė. + key_ceremony_title: Raktų Ceremonija + not_available: Dar neprieinama + not_created: Nesukurta + not_ready: Neparuošta + not_started: Nepradėta + published: Paskelbta + results_description: + not_published: Rezultatai dar nepaskelbti. + published: Rezultatai paskelbti. + results_title: Rezultatai + started: Prasidėjo + tally_description: + finished: Skaičiavimas baigėsi. + not_started: Skaičiavimas dar neprasidėjo. + started: Skaičiavimas prasidėjo. + tally_title: Skaičiavimo procesas + title: Rinkimų ataskaita + verifiable_results: + checksum: 'SHA256 suma:' + description: + not_ready: Paikrinama rinkim7 rinkmena ir SHA256 suma dar nėra prieinami. Kai tik rezultatai bus publikuoti galėsite patikrinti šiuos rinkimus. + ready: 'Čia turite galimybę patikrinti rinkimus. Pirmiausia parsisiųskite rinkmeną ir įsitikinkite, kad ji pilna. Tam, kad tai padaryti naudokite šią komandą ir patikrinkite ar išeitis sutampa su suma:' + how_to_verify: 'Parsisiuntus rinkimeną galite pradėti naudoti universalų tikrintoją. Klonuokite šią repozitoriją ir iš šakninio aplanko paleiskite šią komandą:' + title: Tikrinti rinkimų rezultatus + verifiable_file: 'Tikrinama rinkimų rinkmena:' + verify: Tikrinti rinkimus + vote_description: + finished: Balsavimo procesas baigėsi. + not_started: Balsavimo procesas dar neprasidėjo. + started: Balsavimo procesas prasidėjo. + vote_title: Balsavimo procesas + filters: + active: Aktyvūs + all: Visi + date: Data + finished: Užbaigta + upcoming: Artėjantys + preview: + available_answers: 'Galimi atsakymai:' + description: 'Balsavimo procese rasite šiuos klausimus:' + title: Rinkimų klausimai + results: + description: 'Štai rinkimų rezultatai kiekvienam klausimui:' + percentage: "%{count}%" + selected: Pasirinkta + title: Rinkimų rezultatai + votes: + one: "%{count} balsas" + few: "%{count} balsai" + many: "%{count} balsai" + other: "%{count} balsai" + show: + action_button: + change_vote: Pakeisti balsą + vote: Pradėti balsavimą + vote_again: Balsuoti dar kartą + callout: + already_voted: Šiuose rinkimuose jau balsavote. Galite pakeisti arba patikrinti savo balsą. + pending_vote: Jūsų balsas įkeliamas į serverį. + vote_rejected: Nepavyko patikrinti Jūsų balso. Prašau išsiųskite jį dar kartą. + election_log: Rinkimų ataskaita + preview: Peržiūra + verify: + already_voted: Jau balsuota? + verify_here: Peržiūrėkite savo balsą čia. + will_verify: Galėsite patikrinti savo balsą kai rinkimai prasidės. + voting_period_status: + finished: Balsavimas prasidėjo %{start_time} ir baigėsi %{end_time} + ongoing: 'Balsavimas aktyvus iki: %{end_time}' + upcoming: Balsavimas prasideda %{start_time} + feedback: + answer: + invalid: Siunčiant atsiliepimą iškilo problema. + spam_detected: Atsakant į šią formą iškilo problema. Galbūt paskubėjote? Ar galite pabandyti dar kartą? + success: Atsiliepimas sėkmingai išsiųstas. + models: + answer: + fields: + proposals: Pasiūlymai + selected: Pasirinkta + title: Pavadinimas + votes: Balsai + election: + fields: + bb_status: Skelbimų Lentos statusas + end_time: Baigiasi + start_time: Prasideda + title: Pavadinimas + verifiable_results_file_hash: SHA256 suma + verifiable_results_file_url: Tikrinama rinkimų rinkmena + question: + fields: + answers: Atsakymai + max_selections: Didžiausias pasirenkamų atsakymų skaičius + title: Pavadinimas + trustees_participatory_space: + fields: + considered: atsižvelgta + email: El. paštas + inactive: neaktyvus + name: Pavadinimas + notification: Pranešimas išsiųstas + public_key: Viešasis raktas + status: Būsena + orders: + label: Rikiuoti rinkimus pagal + older: Ankstesni + recent: Paskutiniai + trustee_zone: + elections: + backup_modal: + description: Šie rinkimai sukūriami Skelbimų Lentoje. Labai svarbu, kad kiekvienas dalyvaujantis patikėtinis susikurtų atsarginę raktų kopiją ir juos laikytų saugioje vietoje. Po to, procesas tęsis. + download_election_keys: Parsisiųsti raktus + title: Atsarginiai rinkimų raktai %{election} + key_ceremony_steps: + back: Atgal + description: Šie rinkimai kūriami Skelbimų Lentoje. Norėdami baigti šį procesą, Jūsų dalyvavimas kaip patikėtinio - būtinas. + keys: + create_election: Raktų generavimas + key_ceremony: + joint_election_key: Bendras raktų generavimas + step_1: Raktų skelbimas + list: + status: Būsena + task: Užduotis + process_warning: Kai procesas prasidės neturėtumėt išeiti iš puslapio iki proceso pabaigos. Tai gali užtrukti keletą minučių, kol visi patikėtiniai prisijungs ir įvykdys. + start: Pradėti + status: + completed: Baigta + pending: Laukiama + processing: Apdorojama + title: Sukurti rinkimų raktus %{election} + restore_modal: + description: Skelbimų Lenta turi informacijų iš Jūsų, kaip patikėtinio, apie rinkimus. Norėdami tęsti procesą pirmiausiai įkelkite atsargos rinkmeną sugeneruotą praeitos sesijos metu. + title: Atkurti %{election} rinkimų raktus + upload_election_keys: Įkelti rinkimų raktus + tally_started_steps: + back: Atgal + description: Šių rinkimų rezultatai skaičiuojami Skelbimų lentoje. Norint užbaigti šį procesą, būtinas jūsų, kaip Patikėtinio, dalyvavimas. + keys: + end_tally: Balsų skaičiavimas baigtas + tally: + cast: Duoti balsai + share: Balsų dalis + list: + status: Būsena + task: Užduotis + process_warning: Kai procesas prasidės neturėtumėt išeiti iš puslapio iki proceso pabaigos. Tai gali užtrukti keletą minučių, kol visi patikėtiniai prisijungs ir įvykdys. + start: Pradėti + status: + completed: Baigta + pending: Laukiama + processing: Apdorojama + title: Balsai %{election} + update: + error: Rinkimų būsena neatnaujinta. + success: 'Rinkimų būsena yra: %{status}.' + menu: + trustee_zone: Patikėtinių zona + no_bulletin_board: + body: Sukonfiguruota Skelbimų Lenta yra reikalinga šiai daliai. Susisiekite su administratoriumi dėl daugiau informacijos. + title: Atleiskite, Skelbimų Lenta dar nesukonfiguruota. + trustees: + show: + elections: + list: + action_required: + 'false': 'Ne' + name: Reikalingas veiksmas? + 'true': Atlikti veiksmą + bb_status: Būsena + election: Rinkimai + voting_period: Balsavimo periodas + title: Rinkimai + identification_keys: + cancel: Atšaukti + generate: Generuoti atpažinimo raktus + generate_error: Generuojant atpažinimo raktus iškilo problema. + generate_legend: Turite sugeneruoti porą atpažinimo raktų norėdami dalyvauti rinkimuose kaip patikėtinis. + generate_legend_1: Paspaudus mygtuką turėtumėt atsisiųsti rinkmeną su sugeneruotais atpažinimo raktais. + generate_legend_2: Nukopijuokite rinkmeną į švarų USB raktą + generate_legend_3: Įsitikinkite, kad kompiuteryje nėra rinkmenos kopijos. + generate_legend_4: Padarykite kitą rinkmenos kopiją ir laikykite ją labai saugioje vietoje. + submit: Pateikti + submit_legend: Įgyvendinus visus anksčiau paaiškintus žingsnius, baikite procesą išsiųsdami viešo atpažinimo raktą į serverį. + submit_title: Įkelti viešo atpažinimo raktą + title: Patikėtinių atpažinimo raktai + upload: Įkelkite atpažinimo raktus + upload_error: + invalid_format: Įkelta rinkmena neturi atpažinimo rakto. + invalid_key: Atpažinimo raktai negali būti įkelti. + invalid_public_key: Atpažinimo raktai įkeltoje rinkmenoje nesutampa su viešu apažinimo raktu. + upload_legend: Serveris turi atpažinimo raktus, tačiau naršyklė jų neturi. Turite importuoti atpažinimo raktų rinkimeną į kompiuterį iš atsarginės saugyklos kurią sukūrėte sugeneravę raktus. + not_supported_browser_description: Regis naudojate patikėtinio rolei netinkamą naršyklę. Įsitikinkite, kad naršyklė atnaujinta arba pabandykite naudoti kitas naršykles. + not_supported_browser_title: Atnaujinkite naršyklę + update: + success: Jūsų viešas atpažinimo raktas sėkmingai išsaugotas. + votes: + ballot_decision: + audit: Audituoti biuletenį + back: Vėl pradėti balsavimo procesą + ballot_hash: 'Biuletenio identifikacinis kodas:' + cast: Užpildykite biuletenį norėdami baigti balsavimą + header: 'Biuletenis užšifruotas: galite jį įskaityti arba audituoti' + casting: + header: Biuletenis įskaitomas... + text: Biuletenis yra užskaitomas balsadėžėje. + confirm: + answer_number: '%{number} atsakymas' + confirm: Patvirtinti + edit: redaguoti + header: Patvirtinkite savo balsą + intro: Toliau pateikiama balsavimo, kurį rengiatės atlikti, santrauka.
    Patvirtinkite savo balsą arba pakeiskite savo atsakymus. + nota_option: Tuščia + confirmed: + back: Atgal į rinkimus + experience: Kokia buvo Jūsų patirtis? + feedback: Pasidalinkite savo nuomone + header: Balsas patvirtintas + lead: Jūsų balsas įskaičiuotas! + text: 'Galite patikrinti, ar jūsų balsas pateko į balsadėžę, pasinaudodami šiuo identifikatoriumi: %{e_vote_poll_id}' + verify_link: Norėdami patikrinti, kopijuokite identifikatorių ir jį įklijuokite balsavimo patikros puslapyje + create: + error: Užskaitant balsą iškilo problema. Prašau pabandykite iš naujo. + encrypting: + header: Biuletenis šifruojamas... + text: Biuletenis šifruojamas siekiant užtikrinti balso slaptumą. + failed: + header: Balsavimas nepavyko + lead: Jūsų balsas nebuvo įskaičiuotas! + text: Įvyko klaida. Bandykite dar kartą. + try_again: Bandykite dar kartą + header: + ballot_decision: Balsuokite arba audituokite savo balsą + confirm: Patvirtinkite savo balsą + election: Rinkimai + register: Registruotis + vote_for: Balsuoti dėl %{title} + messages: + invalid_token: Jūsų sesija balsavimo kabinoje nėra validi. Pabandykite balsuoti iš naujo. + not_allowed: Dabar balsuoti šiuose rinkimuose negalite. + modal: + close: Uždaryti + proposal_header: 'Pasiūlymai:' + new: + answer_choices: Galite pasirinkti ne daugiau kaip %{choices} atsakymų + more_information: Daugiau informacijos + nota_option: Nei vienas iš pasiūlytų variantų + preview_alert: Tai – balsavimo kabinos peržiūra. + question_steps: '%{current_step} klausimas iš %{total_steps}' + selections: "%{selected} iš %{max_selections}
    pasirinkčių" + onboarding_modal: + create_account: Sukurti paskyrą + description: Ar norite susikurti naują paskyrą šioje platformoje? Galėsite dalyvauti procesuose ir būti aktyvi(-us) organizacijos narė(-ys). + no_account: Ne, ačiū. + title: Pirmą kartą platformoje? + update: + error: Atnaujinant balso būseną iškilo problema. Prašau pabandykite iš naujo. + verify: + content: + heading: Patikrinkite savo balsą + info: Tikrintojas patikrina ar Jūsų balsas identifikuojamas su užšifruotu kodu buvo užskaitytas. + error: + header: Balsas nerastas! + info: Balso kodas nebuvo rastas %{link} balsadėžėje. Pabandykite iš naujo. + form: + back: Atgal į platformą + submit: Patikrinti + vote_identifier: 'Atpažinimo kodas:' + vote_identifier_help: Šis identifikatorius Jums buvo duotas po balsavimo. + header: + title: Patikrinkite savo balsą + success: + header: Balsas rastas! + info: Jūsų užkoduotas balsas yra %{link} balsadėžėje. + voting_step: + back: Atgal + continue: Kita + events: + elections: + election_published: + email_intro: '%{participatory_space_title} %{resource_title} rinkimai dabar aktyvūs. Juos galite pamatyti šiame puslapyje:' + email_outro: Šį pranešimą gavote dėl to, kad sekate %{participatory_space_title}. Jeigu nebenorite gauti pranešimų, spustelėkite ankstesnę nuorodą. + email_subject: '%{participatory_space_title} %{resource_title} rinkimai dabar aktyvūs.' + notification_title: '%{participatory_space_title} %{resource_title} rinkimai dabar aktyvūs.' + trustees: + new_election: + email_intro: Buvote pridėtas kaip patikėtinis į %{resource_title} rinkimus. + email_outro: Gavote šį pranešimą nes buvote pridėtas kaip patikėtinis %{resource_title} rinkimams. + new_trustee: + email_intro: Administratorius pridėjo Jus kaip %{resource_name} patikėtinį. Galite sukurti viešą raktą patikėtinių zonoje + email_outro: Gavote šį pranešimą nes buvote pridėtas kaip patikėtinis %{resource_name}. + email_subject: Esate %{resource_name} patikėtinis. + votes: + accepted_votes: + email_intro: 'Jūsų balsas priimtas. Naudodami balsavimo žetoną: %{encrypted_vote_hash} galite patikrinti savo balsą čia.' + email_outro: Gavote šį pranešimą nes balsavote %{resource_name} rinkimuose. + email_subject: Jūsų balsas %{resource_name} buvo priimtas. + notification_title: 'Jūsų balsas buvo priimtas. Patikrinkite savo balsą čia naudodami balsavimo žetoną %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Buvote priskirtas kaip %{resource_title} Balsavimo Punkto %{role}. Galite valdyti Balsavimo Punktą iš dedikuotos zonos. + email_outro: Gavote šį pranešimą nes buvote pridėtas kaip %{polling_station_name} %{role}. + email_subject: Jūs esate %{polling_station_name} Balsavimo Punkto %{role}. + notification_title: Jūs esate %{resource_title} balsavimo, %{polling_station_name} punkto %{role}. + send_access_code: + instruction: 'Štai Jūsų prieigos kodas: %{access_code}. Su juo galite dalyvauti %{voting}.' + subject: Jūsų prieigos kodas dalyvavimui %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Balsavimai yra erdvė, kurioje galite užduoti aiškų klausimą visiems organizacijos žmonėms, pakviesti dalyvauti balsavime, pradėti ir valdyti diskusiją už arba prieš atsakymų variantus. Atėjus konsultacijos pradžios datai, galite balsuoti ir paskelbti balsavimo rezultatus.

    Pavyzdžiai: Balsavimuose gali vykti dėl beveik visų aspektų, turinčių poveikio organizacijai: galima balsuoti dėl pavadinimo ar logotipo, nuspręsti, ar tapti didesnės organizacijos dalimi, patvirtinti arba atmesti naują strateginį planą arba darbo grupės rezultatus, ir t. t.

    \n" + page: "

    Balsavimai yra erdvė, kurioje galite užduoti aiškų klausimą visiems organizacijos žmonėms, pakviesti dalyvauti balsavime, pradėti ir valdyti diskusiją už arba prieš atsakymų variantus. Atėjus konsultacijos pradžios datai, galite balsuoti ir paskelbti balsavimo rezultatus.

    Pavyzdžiai: Balsavimuose gali vykti dėl beveik visų aspektų, turinčių poveikio organizacijai: galima balsuoti dėl pavadinimo ar logotipo, nuspręsti, ar tapti didesnės organizacijos dalimi, patvirtinti arba atmesti naują strateginį planą arba darbo grupės rezultatus, ir t. t.

    \n" + title: Kas yra balsavimai? + menu: + votings: Balsavimai + participatory_spaces: + related_elections: + see_all: Žiūrėti visus rinkimus + statistics: + elections_count: Rinkimai + votings_count: Balsavimai + votings: + admin: + ballot_styles: + create: + error: Kuriant šį biuletenio tipą iškilo problema. + success: Biuletenio tipas sukurtas sėkmingai. + destroy: + invalid: Ištrinant šį biuletenio tipą iškilo problema. + success: Biuletenio tipas ištrintas. + edit: + title: Redaguoti biuletenio tipą + update: Atnaujinti + form: + code_help: 'Užuomina: kodas yra sujungėjas tarp surašymo ir biuletenio tipo. Kai įkeliate surašymo duomenis, kiekvienai įvesčiai priskiriamas biuletenio stilius ir atitinkamas kodas.' + election: Rinkimai + questions: Klausimai biuletenio tipui + questions_help: 'Užuomina: pasirinkite klausimus iš rinkimų komponentų, kurie bus pristatyti balsuotojams priskirtiems šiam biuletenio tipui.' + index: + actions: + confirm_destroy: Ar esate tikri? + destroy: Ištrinti + edit: Redaguoti + title: Veiksmai + associated_census_data: Susiję surašymo duomenys + explanation_callout: Biuletenio tipas numato kokie klausimai bus užduoti balsuotojui. Biuletenio tipe, galite pasirinkti kokie klausimai iš balsavimo komponento priklauso biuletenio tipui. Biuletenio tipo kodas naudojamas sujungti balsuotoją iš surašymo su biuleteniu, kuris jiems pateikiamas balsavimo kabinoje. Nekurkite biuletenio tipų jei norite, kad visi gautų visus klausimus. + title: Biuletenio tipas + new: + create: Sukurti + title: Sukurti biuletenio tipą + update: + invalid: Atnaujinant šį biuletenio tipą iškilo problema. + success: Biuletenio tipas atnaujintas sėkmingai. + content_blocks: + attachments_and_folders: + name: Balsavimo prisegimai ir aplankai + header: + name: Balsavimo antraštė + highlighted_votings: + max_results: Didžiausias rodomų elementų skaičius + html_block_1: + name: Balsavimo html blokas 1 + html_block_2: + name: Balsavimo html blokas 2 + html_block_3: + name: Balsavimo html blokas 3 + main_data: + name: Pavadinimas ir aprašymas + metrics: + name: Balsavimo rodikliai + polling_stations: + name: Balsavimo punktai + related_elections: + name: Balsavimo rinkimai + stats: + name: Balsavimo statistika + timeline: + name: Balsavimo laiko juosta + index: + published: Paskelbta + menu: + votings: Balsavimai + votings_submenu: + attachment_collections: Aplankai + attachment_files: Dokumentai + attachments: Priedai + ballot_styles: Biuletenio tipai + census: Surašymas + components: Komponentai + landing_page: Pagrindinis puslapis + monitoring_committee: Priežiūros komitetas + monitoring_committee_election_results: Patvirtinti rezultatus + monitoring_committee_members: Nariai + monitoring_committee_polling_station_closures: Validuoti sertifikatus + monitoring_committee_verify_elections: Tikrinti rinkimus + polling_officers: Balsavimo prižiūrėtojai + polling_stations: Balsavimo punktai + models: + ballot_style: + fields: + code: Kodas + monitoring_committee_member: + fields: + email: El. paštas + name: Pavadinimas + polling_officer: + fields: + email: El. paštas + name: Pavadinimas + polling_station: Balsavimo punktas (rolė) + polling_station: + fields: + address: Adresas + polling_station_managers: Vadybininkai + polling_station_president: Prezidentas + title: Pavadinimas + voting: + fields: + created_at: Sukurta + published: Paskelbta + title: Pavadinimas + monitoring_committee_election_results: + actions: + title: Veiksmai + view: Peržiūrėti + index: + title: Pasirinkite rinkimus iš kurių norite matyti rezultatus + results: + bulletin_board: Skelbimų lenta + election_totals: Rinkimų bendri rezultatai + polling_stations: Balsavimo punktai + result_types: + blank_answers: Tušti atsakymai + blank_ballots: Tušti biuleteniai + null_ballots: Nuliniai biuleteniai + total_ballots: Visi biuleteniai + valid_ballots: Tinkami biuleteniai + selected: Pasirinkta + title: %{election_title} rezultatai + totals: Iš viso + show: + change_election: Keisti rinkimus + publish_results: Skelbti rezultatus + publishing: Rezultatai skelbiami... + update: + invalid: Skelbiant rezultatus iškilo problema. + rejected: Skelbimų Lenta atmetė rezultatų publikavimą. Pabandykite dar kartą arba susisiekite su sistemos administratoriumi. + success: Rinkimų rezultatai paskelbti. + monitoring_committee_members: + create: + invalid: Kuriant šį priežiūros komiteto narį iškilo problema. + success: Priežiūros komiteto narys sukurtas sėkmingai. + destroy: + invalid: Trinant šį priežiūros komiteto narį iškilo problema. + success: Priežiūros komiteto narys ištrintas sėkmingai. + form: + existing_user: Egzistuojantis dalyvis + non_user: Pakviesti naują dalyvį + select_user: Ieškoti pagal el. pašto adresą, vardą arba naudotojo vardą + user_type: Dalyvio tipas + index: + title: Priežiūros komitetas + new: + create: Sukurti + title: Sukurti priežiūros komiteto narį + monitoring_committee_polling_station_closures: + actions: + title: Veiksmai + validate: Patvirtinti + view: Peržiūrėti + closures: + change_election: Keisti rinkimus + signed: Pasirašyta? + title: %{election_title} balsavimo punktai + validated: Patvirtinta? + edit: + change_polling_station: Atgal į balsavimo punktus + monitoring_committee_notes: Pastabos + monitoring_committee_notes_placeholder: Pateikite informaciją apie incidentus čia + title: %{polling_station_title} balsavimo punkto rezultatai %{election_title} rinkimuose + elections: + title: Pasirinkite rinkimus, kuriuos norite patvirtinti + show: + change_polling_station: Atgal į balsavimo punktus + monitoring_committee_notes: Pastabos iš priežiūros komiteto + validate: + error: Patvirtinant užbaigimą iškilo problema. + success: Užbaigimas patvirtintas teisingai. + monitoring_committee_verify_elections: + index: + download: Atsisiųsti + how_to_checksum: 'Norėdami įsitikinti, kad atsisiųsta rinkmena - tvarkinga, paleiskite šią komandą konsolėje ir patikrinkite ar išeitis sutampa su šia patikrinimo suma:' + how_to_download: Norėdami patvirtinti rinkimus, atsisiųskite patvirtinimo failą iš viršuje esančios lentelės. + how_to_run_verifier: 'Parsisiuntus rinkimeną galite pradėti naudoti universalų tikrintoją. Klonuokite šią repozitoriją ir iš šakninio aplanko paleiskite šią komandą:' + how_to_title: Kaip patikrinti rinkimų galiojimą + not_available: Dar neprieinama + title: Rinkimai + polling_officers: + create: + invalid: Kilo problema sukuriant šį balsavimo prižiūrėtoją. + success: Balsavimo prižiūrėtojas sukurtas sėkmingai. + destroy: + invalid: Kilo problema pašalinant šį balsavimo prižiūrėtoją. + success: Balsavimo prižiūrėtojas sėkmingai pašalintas. + form: + existing_user: Egzistuojantis dalyvis + non_user: Pakviesti naują dalyvį + select_user: Ieškoti pagal vardą, el. pašto adresą arba naudotojo vardą + user_type: Dalyvio tipas + index: + role_manager: vadybininkas + role_president: prezidentas + title: Balsavimo prižiūrėtojai + new: + create: Sukurti + title: Sukurti balsavimo prižiūrėtoją + polling_officers_picker: + choose_polling_officers: Pasirinkti balsavimo prižiūrėtojus + no_polling_officers: Jūsų paieškos kriterijus atitinkančių balsavimo prižiūrėtojų nerasta arba nėra balsavimo prižiūrėtojų. + polling_stations: + create: + invalid: Sukuriant šį balsavimo punktą iškilo problema. + success: Balsavimo punktas sėkmingai sukurtas. + destroy: + invalid: Kilo problema pašalinant šį balsavimo punktą. + success: Balsavimo punktas sėkmingai pašalintas. + edit: + title: Atnaujinti balsavimo punktą + update: Atnaujinti balsavimo punktą + form: + address_help: 'Adresas: jį naudoja „Geocoder“, kad rastų vietą' + location_help: 'Vieta: žinutė skirta balsuotojams nurodanti tikslią balsavimo punkto vietą' + location_hints_help: 'Vietos nuorodos: papildoma informacija.' + polling_station_managers_help: 'Balsavimo punkto vadybininkai: įsitikinkite, kad jie buvo sukurti ir nepriskirti kitam balsavimo punktui' + polling_station_president_help: 'Balsavimo punkto prezidentas: įsitikinkite, kad jis buvo pridėtas ir nebuvo priskirtas kitam balsavimo punktui' + select_president: Pasirinkite balsavimo punkto vadybininką kaip balsavimo punkto prezidentą + index: + title: Balsavimo punktai + new: + create: Sukurti + title: Sukurti balsavimo punktą + update: + invalid: Atnaujinant balsavimo punktą iškilo problema. + success: Balsavimo punktas atnaujintas sėkmingai. + titles: + votings: Balsavimai + votings: + actions: + confirm_destroy: Ar esate tikri? + destroy: Sunaikinti + new_voting: Nauja balsavimo erdvė + create: + invalid: Kuriant šį balsavimą iškilo problema. + success: Balsavimas sukurtas sėkmingai. + edit: + add_election_component: Šiam balsavimui nėra sukonfigūruotų rinkimų. Pridėkite juos per Komponentų sekciją. + assign_missing_officers: Kai kurie balsavimo punktai neturi prezidento ir/arba vadybininkų. Prašome juos priskirti. + update: Atnaujinti + form: + banner_image: Reklamjuostės paveikslėlis + census_contact_information: Surašytojų kontaktinė informacija + census_contact_information_help: Ši kontaktinė informacija skirta dalyviams norintiems pranešti apie problemas. Tai gali būti el. pašto adresas, kontaktinė forma kitame puslapyje ir t. t. + introductory_image: Įvadinis paveikslėlis + promoted: Paremta + select_a_voting_type: Pasirinkite balsavimo tipą + show_check_census_help: Ar rodyti "Ar galiu balsuoti?" nuorodą viešų balsavimų meniu. + slug: Nuorodos dalis + title: Pavadinimas + voting_type: + hybrid: Hibridinis + in_person: Fizinis + online: Virtualus + voting_type_label: Balsavimo tipas + new: + create: Sukurti + title: Naujas balsavimas + update: + invalid: Atnaujinant šį balsavimą iškilo problema. + success: Balsavimas sėkmingai atnaujintas. + admin_log: + ballot_style: + create: "%{user_name} sukūrė biuletenio tipą su kodu %{ballot_style_code} erdvėje %{space_name}" + delete: "%{user_name} ištrynė biuletenio tipą su kodu %{ballot_style_code} erdvėje %{space_name}" + update: "%{user_name} atnaujino biuletenio tipą su kodu %{ballot_style_code} erdvėje %{space_name}" + census: + create: "%{user_name} sukūrė surašymą %{space_name} erdvėje" + delete: "%{user_name} ištrynė surašymą erdvėje %{space_name}" + update: "%{user_name} atnaujino surašymą erdvėje %{space_name}" + monitoring_committee_member: + create: "%{user_name} priskyrė naudotoją %{monitoring_committee_member_user} kaip priežiūros komiteto narį erdvėje %{space_name}" + delete: "%{user_name} atskyrė naudotoją %{monitoring_committee_member_user} kaip priežiūros komiteto narį nuo erdvės %{space_name}" + polling_officer: + create: "%{user_name} priskyrė naudotoją %{polling_officer_user} kaip balsavimo prižiūrėtoją erdvėje %{space_name}" + delete: "%{user_name} atskyrė naudotoją %{polling_officer_user} kaip balsavimo prižiūrėtoją nuo erdvės %{space_name}" + polling_station: + create: "%{user_name} sukūrė balsavimo punktą %{resource_name} erdvėje %{space_name}" + delete: "%{user_name} pašalino balsavimo punktą %{resource_name} erdvėje %{space_name}" + update: "%{user_name} atnaujino balsavimo punktą %{resource_name} erdvėje %{space_name}" + voting: + create: "%{user_name} sukūrė %{resource_name} balsavimą" + publish: "%{user_name} paskelbė %{resource_name} balsavimą" + unpublish: "%{user_name} panaikino %{resource_name} balsavimą" + census: + admin: + census: + create: + invalid: Kuriant surašymą kilo problema. Pabandykite dar kartą vėliau. + invalid_csv_header: Trūksta CSV antraščių, arba jos nėra teisingos - prašome atidžiai perskaityti instrukcijas. + creating_data: + info_message: "Prašome palaukti, aprodota %{processed_count} iš %{raw_count} eilučių iš %{file} failo (tai gali užtrukti kelias minutes)." + delete: + button: Pašalinti visus surašymo duomenis + confirm: Pašalinus visus surašymo duomenis, veiksmo nebus galima anuliuoti. Ar tikrai norite tęsti? + destroy: + error: Pašalinant surašymą kilo problema. Pabandykite dar kartą vėliau. + success: Surašymo duomenys pašalinti. + export_access_codes: + button: Eksportuoti balsavimo prieigos kodus + callout: Galite toliau tęsti prieigos kodų eksportą. Tai gali būti atlikta tik vieną kartą. Kai pradėsite eksportavimą, el. pašto dėžutėje rasite laišką su instrukcijomis į %{email} + confirm: Galite eskportuoti prieigos kodą vieną kartą. Užtikrinkite, kad turite prieigą prie el. pašto %{email}. + file_not_exist: Failas neegzistuoja. + launch_error: Pradedant prieigos kodų eksportą kilo problema. + launch_success: Prieigos kodų eksportas prasidėjo. Neužilgo gausite el. laišką į %{email}. + exporting_access_codes: + info_message: "Prašome palaukti, eskportas ruošiamas ir neužilgo gausite kodus į %{email}." + freeze: + callout: Surašymas užšaldytas ir negali būti pakeistas. + help_html: | + Surašymo duomenys buvo įkelti - kodai sugeneruoti ir sėkmingai eksportuoti.
    Esate pasiruošę pradėti rinkimus
    Naudokite eksportuota CSV su individualiais kodais, kad juos išdalinti surašytiems asmenims arba aktyvuokite "Ar galiu balsuoti" skiltį, kad bet kas galėtų surasti savo kodą. + generate_access_codes: + button: Generuoti balsavimo prieigos kodus + callout: Dabar galite sugeneruoti prieigos kodus. Atsižvelkite į tai, kad sugeneravus prieigos kodus nebegalėsite pakeisti surašymo. + confirm: Jei tęsite, nebegalėsite pakeisti surašymo. + info_message_all: "Visos eilutės importuotos sėkmingai iš %{file} failo (%{raw_count} nuo %{data_count})." + info_message_warn: Prašome patikrinti ar netrūksta duomenų, nes %{data_count} įrašai buvo sukurti ir įkeltas failas file %{file} turėjo %{raw_count} eilučių. + launch_error: Pradedant prieigos kodų generavimą kilo problema + launch_success: Kodų generavimas pradėtas. + start_over: Prašome pašalinti turimą surašymą ir pradėkite iš naujo su deramu CSV failu ir teisingomis eilutėmis. + generating_access_codes: + info_message: "Prašome palaukti, balsavimo prieigos kodai yra generuojami (tai gali užtrukti kelias minutes)..." + new: + file_help: + explanation: 'Failo gairės:' + message_1: Leidžiami tik CSV (.csv) failai. + message_2: Stulpeliai privalo būti išskiriami kabliataškiu (";"). + has_ballot_styles_message: Jūs sukuriate balsavimo biuletenio tipus. Prašome įsitikinti, kad "%{ballot_style_code_header}" laukelis esantis CSV faile sutampa su pageidaujamo biuletenio tipo kodu. + info_message: "Kol kas nėra sukurto surašymo. Kad jį sukurti, prašome naudoti žemiau nurodyta CSV failo importo formą." + missing_ballot_styles_message: 'Šiam balsavimui nėra biuletenio stiliaus. Jei norėtumėt pridėti sąlyginį klausimą (t.y. pateikti skirtingus klausimus skirtingiems balsuotojams pvz. priklausomai nuo jų gyvenamosios vietos), turėtumėt numatyti biuletenių stiliųpriešimportuojant surašymą. Jei norėtumėt visiems balsuotojams adresuoti tą patį klausimą, galite importuoti surašymą.' + submit: Pateikti CSV + title: Sukurkite surašymą + show: + heading: Balsavimo erdvės surašymas + upload_info: + csv_example_with_ballot_style: 'Pavyzdinis failas su balsavimo biuletenio tipais:' + csv_example_without_ballot_style: 'Pavyzdinis failas be balsavimo biuletenio tipų:' + csv_header_after: Neįtraukite paskutinio lauko (%{ballot_style_code_header}) jei Jums nereikia bliutelenio stilių ir sąlyginių klausimų + csv_header_before: 'Surašymo failas privalo būti CSV su šia antrašte:' + document_types: + passport: Pasas + export_mailer: + access_codes_export: + click_button: 'Spustelėkite ant sekančios nuorodos, kad atsisiųsti prieigos kodų duomenis.
    Failas bus prieinamas iki %{date}.
    Jums reikės 7-Zip (Windows), Keka (MacOS) arba PeaZip (Linux) kad jį atverti. Slaptažodis: %{password}' + download: Atsisiųsti + subject: '%{voting_title} balsavimo prieigos kodai gali būti eksportuojami' + vote_flow: + already_voted_in_person: Šis dalyvis jau balsavo ir negali balsuoti dar kartą. + datum_not_found: Šie duomenys nesutampa su jokiu balsuotoju. + content_blocks: + highlighted_votings: + name: Paryškinti balsavimai + landing_page: + polling_stations: + heading: Balsavimo punktai + no_polling_stations: Balsavimo punktų dar nėra. + monitoring_committee_members: + actions: + confirm_destroy: Ar esate tikri? + destroy: Ištrinti + new: Naujas narys + title: Veiksmai + pages: + home: + highlighted_votings: + active_spaces: Aktyvūs balsavimai + see_all_spaces: Žiūrėti visus balsavimus + polling_officer_zone: + closures: + back_to_polling_stations: Atgal į balsavimo punktus + certify: + error: Prisegant sertifikatą įvyko klaida. Pabandykite dar kartą. + heading: Balsų perksaičiavimas - įkelkite sertifikatą + info_text: Įkelkite rinkiminės baigties sertifikato nuotrauką. + submit: Įkelkite sertifikatą + success: Sertifikatas įkeltas sėkmingai. + create: + error: Kuriant uždarymą kilo problema. Pabandykite dar kartą vėliau. + success: Uždarymas sėkmingai sukurtas. + edit: + heading: Balsų perskaičiviamas - atsakymų perskaičiviamas + modal_ballots_results_count_error: + close_modal: Uždaryti + info_text: Biuletenių skaičius nesutampa su vokų skaičium. Patikrinkite biuletenių skaičių. + title: Visas biuletenių skaičius + save_recount: Išsaugoti perskaičiavimą + total_ballots: Visi biuleteniai + total_blank_ballots: Visi tušti biuleteniai + total_null_ballots: Visi nuliniai biuleteniai + total_valid_ballots: Visi validūs biuleteniai + new: + election: 'Rinkimai:' + heading: Balsų perskaičiviamas + info_text: 'Pridėkite visų biuletenių (vokų) skaičių perskaičiuotą balsavimo punkte:' + modal_ballots_count_error: + btn_validate_total: Patvirtinti biuletenių perskaičiavimą + info_explanation_text: 'Prašau peržiūrėkite visą biuletenių skaičių. Jei suminis skaičius neteisingas, prašau pateikite paaiškinimą priežiūros komitetui:' + info_text: Įvestas visas biuletenių skaičius (vokai) neatitinka balsavusių balsavimo punkte skaičiaus. + message_for_monitoring_committee: Žinutė priežiūros komitetui + review_recount: Peržiūrėti perskaičiavimą + text_area_placeholder: Įveskite žinutę + title: Skaičiai nesutampa + total_ballots: 'Viso balsavimo biuletenių:' + total_people: 'Viso žmonių:' + polling_station: 'Balsavimo punktas:' + submit: Patvirtinkite bendrą skaičių + total_ballots_count: Balsavimo biuletenių kiekis + show: + heading: Balsų perskaičiviamas + sign: + cancel: Atšaukti + check_box: Peržiūrėjau, ir tai yra tas pats, kaip ir fizinis rinkimų užbaigimo sertifikatas + confirm: Gerai, testi + error: Įvyko klaida, pamėginkite dar kartą. + heading: Balsų perskaičiavimas - Pasirašykite užbaigimą + info_text: Jeigu tęsite, nebegalėsite koreguoti jokios informacijos, šio veiksmo anuliuoti negalima. + submit: Pasirašykite užbaigimą + success: Užbaigimas pasirašytas sėkmingai. + update: + error: Atliekant užbaigimo atnaujinimą kilo klaida, pabandykite dar kartą vėliau. + success: Užbaigimo rezultatai sėkmingai atnaujinti. + in_person_votes: + complete_voting: + available_answers: 'Galimi atsakymai:' + census_verified: Šis dalyvis iki šiol fiziškai nebalsavo. + census_verified_with_online_vote: Šis dalyvis jau balsavo internetu. Jeigu dalyvis nuspręs balsuoti fiziškai, prieš tai pateiktas balsas bus anuliuotas ir šis taps galutiniu balsu. + complete_voting: Užbaigti balsavimą + identify_another: Identifikuoti kitą dalyvį + questions_title: 'Jie turi teisę balsuoti šiais klausimais:' + questions_title_voted: 'Šis dalyvis jau balsavo internetu ir turi galimybė balsuoti šiais klausimais:' + voted: Dalyvis prabalsavo + create: + error: Balsavimas nebuvo užregistruotas. Prašome bandyti dar kartą. + in_person_form: + census_not_present: Šis dalyvis nėra įskaičiuotas į surašymą. + census_not_present_description: Jie privalo kreiptis į surašymo skundų biurą arba susisiekti su klientų pagalba. + date_of_birth: Gimimo data + day: Diena + day_placeholder: DD + document_number: Dokumento numeris + document_number_placeholder: Asmens tapatybės dokumento numeris + month: Mėnuo + month_placeholder: MM + select: Pasirinkite dokumento tipą + title: 'Pasirinkite dokumento tipą ir įveskite dalyvio dokumento numerį:' + validate_document: Patvirtinti dokumentą + year: Metai + year_placeholder: MMMM + new: + back: Atgal į balsavimo punktus + title: Identifikuoti ir patvirtinti dalyvį + show: + back: Atgal į balsavimo punktus + title: Laukiama, kol bus užregistruotas fizinis balsavimas + update: + error: Užskaitant balsą iškilo problema. Prašome pabandykite iš naujo. + success: + accepted: Balsavimas užregistruotas sėkmingai. + rejected: Skelbimų lenta balsavimo nepriėmė. Prašome susisiekti su sistemos administratoriumi. + verify_document: + census_present: Šis dalyvis įskaičiuotas surašyme. + name: Vardas + title: 'Patikrinkite ar šie duomenys teisingi:' + verify_document: Patvirtinti dokumentą + menu: + polling_officer_zone: Balsavimo vadybininkų zona + polling_officers: + index: + polling_officer_role_description: Buvote priskirtas kaip balsavimo punkto vadybininkas ar prezidentas. + polling_station: + address: Adresas + count_votes: Skaičiuoti balsus + election: Rinkimai + identify_person: Atpažinti asmenį + name: Vardas + no_polling_stations: Nesate priskirtas prie balsavimo punkto. + role: Jūsų rolė + show_closure: Peržiūrėti uždarymą + title: Balsavimo punktai + voting: Balsavimas + polling_officers: + actions: + confirm_destroy: Ar esate tikri? + destroy: Ištrinti + title: Veiksmai + roles: + manager: Vadybininkas + president: Prezidentas + unassigned: Nepaskirtas + polling_station_closure_recount: + nota_option: Nei vienas iš pasiūlytų variantų + polling_officer_notes: 'Balsavimo vadybininkų užrašai:' + polling_officer_notes_blank: Užrašų nėra + recount_summary: 'Perskaičiavimo reziumė:' + signed: Pasirašyta + total_ballots: 'Visi biuleteniai:' + total_blank_ballots: 'Visi tušti biuleteniai:' + total_null_ballots: 'Visi nuliniai biuleteniai:' + total_valid_ballots: 'Visi validūs biuleteniai:' + polling_stations: + actions: + confirm_destroy: Ar esate tikri? + destroy: Ištrinti + edit: Redaguoti + title: Veiksmai + votings: + access_code_modal: + email: Siųsti el. paštu į %{email} + info: Norėdami dalyvauti turite turėti prieigos kodą. Jei jo negavote paštu, galime atsiųsti kitą. + no_email: Nėra galimo el. pašto + no_sms: Nėra galimo tel. nr + sms: Siųsti SMS žinute į %{sms} + title: Gauti prieigos kodą + check_census: + check_status: Patikrinti būseną + description: Čia galite patikrinti surašymo duomenis ir patikrinti ar galite dalyvauti. Turėtumėt turėti prieigos kodą, tačiau jei jį pametėt galėsite paprašyti kito kai duomenys bus teisingi. + error: + info: 'Pabandykite iš naujo. Jei manote, kad duomenys sistemoje neteisingi, apie tai galite pranešti: %{census_contact_information}.' + title: Duomenys, kuriuos įvedėte nėra šio balsavimo surašyme + form_title: 'Užpildykite šią formą, kad patikrinti savo surašymo duomenis:' + invalid: Tikrinant surašymo duomenis iškilo problema. + success: + access_link: el. paštu. + access_link_with_sms: SMS ar el. paštu. + info: Turėjote gauti prieigos kodą per paštą. Jei negavote, išsiųskite užklausą čia + title: Jūsų surašymo duomenys teisingi! + title: Ar galiu balsuoti? + check_fields: + date_of_birth: Gimimo data + day: Diena + day_placeholder: DD + document_number: Dokumento numeris + document_number_placeholder: Asmens tapatybės dokumento numeris + document_type: Dokumento tipas + month: Mėnuo + month_placeholder: MM + postal_code: Pašto kodas + postal_code_placeholder: Pašto kodo numeris + select: Pasirinkite dokumento tipą + year: Metai + year_placeholder: MMMM + count: + title: + one: "%{count} balsavimas" + few: "%{count} balsavimai" + many: "%{count} balsavimai" + other: "%{count} balsavimai" + elections_log: + description: Rinkimų ataskaitoje bus pateikta visa aktuali informacija apie kiekvieną balsavimą. Pavyzdžiui pagrindinės ceremonijos būsena, taškai arba jeigu rezultatai jau paskelbti. Spustelėkite ant rinkimų, apie kuriuos norite gauti informaciją. + title: Rinkimų ataskaita + filters: + active: Aktyvūs + all: Visi + date: Data + finished: Užbaigta + search: Ieškoti + upcoming: Artėjantys + index: + no_votings: Jūsų paieškos kriterijus atitinkančių balsavimų nerasta. + only_finished: Dabar suplanuotų balsavimų nėra, tačiau čia galite rasti visų įvykusių balsavimų sąrašą. + title: Balsavimai + login: + access_code: Prieigos kodas + access_code_placeholder: Prieigos kodas + ask_for_a_new_one: Prašyti naujo. + dont_have_access_code: Neturite prieigos kodo? + form_title: 'Norėdami prisijungti prie balsavimo, užpildykite šią formą:' + start_voting: Pradėti balsavimą + title: Identifikuoti save pagal mano balsavimo surašymo duomenis + no_census_contact_information: Kol kas nėra kontaktinės informacijos. + orders: + label: 'Rūšiuoti balsus pagal:' + random: Atsitiktinai + recent: Naujausia + send_access_code: + invalid: Iškilo problema išsiunčiant Prieigos Kodą. + success: Jūsų Prieigos Kodas buvo išsiųstas sėkmingai. + show: + title: Apie šį balsavimą + votings_m: + badge_name: + finished: Užbaigta + ongoing: Vykstantys + upcoming: Artėjantys + unspecified: Nepatikslinta + voting_type: + hybrid: Hibridinis + in_person: Fizinis + online: Virtualus + layouts: + decidim: + voting_navigation: + check_census: Ar galiu balsuoti? + election_log: Rinkimų ataskaita + votings: + index: + promoted_votings: Paryškinti balsavimai + promoted_voting: + vote: Balsuoti diff --git a/decidim-elections/config/locales/lv.yml b/decidim-elections/config/locales/lv.yml new file mode 100644 index 00000000..d7dc6274 --- /dev/null +++ b/decidim-elections/config/locales/lv.yml @@ -0,0 +1,104 @@ +lv: + activemodel: + attributes: + answer: + description: Apraksts + image: Attēls + proposals: Saistītie priekšlikumi + title: Nosaukums + election: + description: Apraksts + end_time: Balsošana beidzas plkst. + start_time: Balsošanas sākas plkst. + title: Nosaukums + question: + max_selections: Maksimālais izvēļu skaits + title: Nosaukums + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Jāpievieno no jauna + activerecord: + models: + decidim/elections/answer: + zero: Atbildes + one: Atbilde + other: Atbildes + decidim/elections/election: + zero: Vēlēšanas + one: Vēlēšanas + other: Vēlēšanas + decidim/elections/question: + zero: Jautājumi + one: Jautājums + other: Jautājumi + decidim: + components: + elections: + name: Vēlēšanas + settings: + global: + announcement: Paziņojums + step: + announcement: Paziņojums + elections: + actions: + confirm_destroy: Vai esat pārliecināts? + destroy: Dzēst + edit: Labot + import: Importēt priekšlikumus atbildēm + preview: Priekšskatīt + title: Darbības + admin: + answers: + edit: + title: Rediģēt atbildi + update: Atjaunināt atbildi + index: + title: Atbildes + new: + create: Izveidot atbildi + title: Jauna atbilde + elections: + edit: + title: Rediģēt vēlēšanas + update: Atjaunināt vēlēšanas + index: + title: Vēlēšanas + new: + create: Izveidot vēlēšanas + title: Jaunas vēlēšanas + models: + answer: + name: Atbilde + proposals_imports: + new: + create: Importēt priekšlikumus atbildēm + no_components: Šajā līdzdalības telpā nav citu priekšlikumu komponentu, lai priekšlikumus importētu atbildēs. + select_component: Lūdzu, izvēlieties komponentu + questions: + edit: + title: Rediģēt jautājumu + update: Atjaunināt jautājumu + index: + title: Jautājumi + new: + create: Izveidot jautājumu + title: Jauns jautājums + models: + answer: + fields: + proposals: Priekšlikumi + title: Nosaukums + election: + fields: + end_time: Beidzas plkst. + start_time: Sākas plkst. + title: Nosaukums + question: + fields: + answers: Atbildes + max_selections: Maks. izvēles + title: Nosaukums diff --git a/decidim-elections/config/locales/mt-MT.yml b/decidim-elections/config/locales/mt-MT.yml new file mode 100644 index 00000000..f7aabc71 --- /dev/null +++ b/decidim-elections/config/locales/mt-MT.yml @@ -0,0 +1 @@ +mt: diff --git a/decidim-elections/config/locales/mt.yml b/decidim-elections/config/locales/mt.yml new file mode 100644 index 00000000..f7aabc71 --- /dev/null +++ b/decidim-elections/config/locales/mt.yml @@ -0,0 +1 @@ +mt: diff --git a/decidim-elections/config/locales/nl.yml b/decidim-elections/config/locales/nl.yml new file mode 100644 index 00000000..17780f8a --- /dev/null +++ b/decidim-elections/config/locales/nl.yml @@ -0,0 +1,930 @@ +nl: + activemodel: + attributes: + answer: + description: Beschrijving + image: Afbeelding + proposals: Verwante voorstellen + title: Aanspreektitel + election: + description: Beschrijving + end_time: Stemming eindigt op + start_time: Stemming begint om + title: Aanspreektitel + question: + max_selections: Maximum aantal selecties + title: Aanspreektitel + voting: + end_time: Stemmen eindigt + start_time: Stemmen begint + voting_type: Soort stemronde + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Moet opnieuw worden gekoppeld + election: + attributes: + attachment: + needs_to_be_reattached: Moet opnieuw worden gekoppeld + activerecord: + models: + decidim/elections/answer: + one: Beantwoorden + other: Antwoorden + decidim/elections/election: + one: Verkiezing + other: Verkiezingen + decidim/elections/question: + one: Vraag + other: vragen + decidim/votings/census/dataset: + one: Dataset + other: Datasets + decidim/votings/census/datum: + one: Datum + other: Data + decidim/votings/polling_officer: + one: Stembeambte + other: Stembeambtes + decidim/votings/polling_station: + one: Stembureau + other: Stembureaus + decidim/votings/voting: + one: Stemronde + other: Stemrondes + decidim: + admin: + filters: + officers_assigned_eq: + values: + assigned: Toegewezen + unassigned: Niet toegewezen + search_placeholder: + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Zoek %{collection} op titel, adres of beambte naam/e-mail/bijnaam. + signed_eq: + label: Ondertekend + values: + 'false': Ondertekend + 'true': Niet ondertekend + validated_eq: + label: Gevalideerd + components: + elections: + actions: + vote: Stemming + name: Verkiezingen + settings: + global: + announcement: Mededeling + step: + announcement: Mededeling + elections: + actions: + confirm_destroy: Weet je het zeker? + destroy: Vernietig + edit: Bewerken + import: Importeer voorstellen naar antwoorden + manage_answers: Antwoorden beheren + manage_questions: Vragen beheren + manage_steps: Stappen beheren + preview: Voorvertoning + publish: Publiceren + title: acties + unpublish: Depubliceer + admin: + answers: + edit: + title: Antwoord bewerken + update: Antwoord bijwerken + index: + title: Antwoorden + new: + create: Antwoord maken + title: Nieuw antwoord + not_selected: Niet geselecteerd + selected: Geselecteerd + elections: + edit: + title: Bewerken van verkiezing + update: Keuze bijwerken + index: + title: Verkiezingen + new: + create: Maak verkiezing aan + title: Nieuwe verkiezing + publish: + success: De verkiezing is succesvol gepubliceerd. + unpublish: + success: De verkiezing is succesvol gedepubliceerd. + exports: + elections: Verkiezingen + menu: + trustees: Trustees + models: + answer: + name: Beantwoorden + proposals_imports: + new: + create: Importeer voorstellen naar antwoorden + no_components: Er zijn geen andere onderdelen van het voorstel in deze participatieve ruimte om de voorstellen in de antwoorden te importeren. + select_component: Selecteer een component + questions: + edit: + title: Vraag bewerken + update: Vraag bijwerken + index: + title: vragen + new: + create: Vraag aanmaken + title: Nieuwe vraag + steps: + create_election: + errors: + max_selections: De vragen hebben geen juiste waarde voor het aantal antwoorden + minimum_answers: Vragen moeten minstens twee antwoorden hebben. + minimum_questions: De stemronde moet ten minste één vraag hebben. + published: De stemronde is niet gepubliceerd. + trustees_number: De participatieve ruimte moet ten minste %{number} trustees met publieke sleutel hebben. + invalid: Er is een fout opgetreden bij het aanmaken van deze stemronde + no_trustees: Er zijn geen Trustees geconfigureerd voor deze participatieve ruimte + not_used_trustee: "(niet gebruikt)" + public_key: + 'false': heeft geen publieke sleutel + 'true': heeft een publieke sleutel + requirements: + max_selections: Alle vragen hebben een correcte waarde voor maximum van antwoorden. + minimum_answers: Elke vraag heeft minstens 2 antwoorden. + minimum_questions: De stemronde heeft minstens 1 vraag. + published: De stemronde is gepubliceerd. + time_before: Alles wordt klaargezet minstens %{hours} uur voor de stemronde begint. + trustees_number: De participatieve ruimte moet minstens %{number} trustees met publieke sleutel hebben. + submit: Nieuwe stemronde instellen + title: Nieuwe stemronde + trustees: Verkiezing Trustees + key_ceremony: + continue: Doorgaan + title: Sleutelceremonie + results_published: + answer: Antwoord + not_selected: Niet geselecteerd + question: Vraag + result: Resultaat + selected: Geselecteerd + submit: Indienen + title: Resultaten gepubliceerd + tally_ended: + answer: Antwoord + not_selected: Niet geselecteerd + question: Vraag + result: Resultaat + selected: Geselecteerd + title: Berekende resultaten + tally_started: + mark_as_missing: Als ontbrekend markeren + tally_completion: Het proces zal worden voltooid wanneer alle trustees actief zijn of als ontbrekend zijn aangeduid. Tenminste %{quorum} trustees zijn vereist om het proces te voltooien. + undo_mark_as_missing: Zolang het proces niet is voltooid zal een trustee die per ongeluk is gemarkeerd als ontbrekend kunnen deelnemen. Ze kunnen doorgaan zoals gebruikelijk en de aanduiding als 'ontbrekend' wordt genegeerd. + vote: + title: Stemperiode + vote_ended: + text: Stem is beëindigd. Je kunt nu beginnen turven. + vote_stats: + no_vote_statistics_yet: Nog geen stemstatistieken + title: Stemstatistieken + voters: Kiezers + votes: Stemmen + trustees_participatory_spaces: + actions: + disable: Uitschakelen + enable: Overweeg + form: + select_user: Selecteer gebruiker + index: + title: Trustees + new: + create: Maak Trustee + title: Nieuwe trustee + admin_log: + election: + create: "%{user_name} heeft de verkiezing %{resource_name} van %{space_name} gemaakt" + delete: "%{user_name} heeft de verkiezing %{resource_name} van %{space_name} verwijderd" + end_vote: "%{user_name} beëindigde de stemperiode voor de verkiezing %{resource_name} van %{space_name} in het Bulletin Board" + publish: "%{user_name} heeft de verkiezing %{resource_name} van %{space_name} gepubliceerd" + publish_results: "%{user_name} publiceerde de resultaten voor de verkiezing %{resource_name} van %{space_name} in het Bulletin Board" + report_missing_trustee: "%{user_name} rapporteerde %{trustee_name} als een ontbrekende trustee tijdens de telling voor de verkiezing %{resource_name} van %{space_name} op het Bulletin Board" + setup: "%{user_name} maakte de verkiezing %{resource_name} van %{space_name} aan in het Bulletin Board" + start_key_ceremony: "%{user_name} startte de sleutelceremonie voor de verkiezing %{resource_name} van %{space_name} in het Bulletin Board" + start_tally: "%{user_name} startte de telling voor de verkiezing %{resource_name} van %{space_name} in het Bulletin Board" + start_vote: "%{user_name} startte de stemperiode voor de verkiezing %{resource_name} van %{space_name} in het Bulletin Board" + unpublish: "%{user_name} heeft de publicatie van de verkiezing %{resource_name} van %{space_name} ongedaan gemaakt" + update: "%{user_name} heeft de verkiezing %{resource_name} van %{space_name} bijgewerkt" + trustee: + create: "%{user_name} heeft de gebruiker %{trustee_user} aangewezen als Trustee" + elections: + count: + elections_count: + one: "%{count} verkiezing" + other: "%{count} verkiezingen" + election_log: + chained_hash: De ketting Hash van dit bericht + complete: Voltooid + creation_description: + complete: De verkiezing is aangemaakt en in het Bulletin Board toegevoegd. + not_created: De verkiezing is nog niet aangemaakt. + creation_title: Verkiezing aangemaakt + description: Dit is het verkiezingslogboek waar je de status van elke stap kunt controleren, bijvoorbeeld als de verkiezingen eenmaal zijn voltooid, als de telling is afgerond en als de verkiezingen zijn afgesloten. + download: Download + key_ceremony_description: + complete: De sleutelceremonie is voltooid. Elke trustee heeft geldige sleutels en heeft de nodige backup-sleutels gedownload. + not_started: De sleutelceremonie is nog niet begonnen. + started: De sleutelceremonie is begonnen maar is nog niet voltooid. + key_ceremony_title: Sleutelceremonie + not_available: Nog niet beschikbaar + not_created: Niet aangemaakt + not_ready: Niet gereed + not_started: Niet begonnen + published: Gepubliceerd + results_description: + not_published: De resultaten zijn nog niet gepubliceerd. + published: De resultaten zijn gepubliceerd. + results_title: Resultaten + started: Gestart + tally_description: + finished: Het tellingsproces is voltooid. + not_started: Het tellingsproces is nog niet begonnen. + started: Het tellingsproces is begonnen. + tally_title: Tellingsproces + title: Verkiezingslogboek + verifiable_results: + checksum: 'Bestand SHA256-controlegetal:' + description: + ready: 'Hier heb je de mogelijkheid om de verkiezing te verifiëren. Eerst moet je het bestand downloaden en ervoor zorgen dat het niet beschadigd is. Voer het volgende commando uit en controleer of de output overeenkomt met de checksum:' + how_to_verify: 'Nadat je het bestand gedownload hebt en zeker weet dat het in orde is, kan je doorgaan met de universele controle. Kopieer deze repository en voer vanuit de hoofdmap de volgende opdracht uit:' + title: Verifieer verkiezingsuitslagen + verifiable_file: 'Verifieerbaar verkiezingsbestand:' + verify: Verifieer verkiezing + vote_description: + finished: Het stemproces is beëindigd. + not_started: Het stemproces is nog niet begonnen. + started: Het stemproces is begonnen. + vote_title: Stemproces + preview: + available_answers: 'Beschikbare antwoorden:' + description: 'Dit zijn de vragen die je tijdens het stemproces tegenkomt:' + results: + description: 'Dit zijn de resultaten van de stemronde, voor elke vraag:' + percentage: "%{count}%" + show: + action_button: + change_vote: Wijzig je stem + vote: Begin met stemmen + vote_again: Stem nogmaals + callout: + already_voted: Je hebt al gestemd in deze verkiezing. Je kunt je stem wijzigen of controleren. + pending_vote: Jouw stem wordt uitgebracht op de server. + vote_rejected: Het was niet mogelijk om je stem te verifiëren. Gelieve je stem opnieuw uit te brengen. + election_log: Verkiezingslogboek + preview: Voorvertoning + verify: + already_voted: Al gestemd? + verify_here: Controleer hier jouw stem. + will_verify: Je kunt je stem controleren zodra de verkiezing start. + voting_period_status: + finished: De stemming begon op %{start_time} en eindigde op %{end_time} + ongoing: 'Actieve stem tot: %{end_time}' + upcoming: Stemmen begint op %{start_time} + feedback: + answer: + invalid: Er is een fout opgetreden bij het indienen van je feedback. + models: + answer: + fields: + proposals: Voorstellen + selected: Geselecteerd + title: Aanspreektitel + votes: Stemmen + election: + fields: + bb_status: Bulletin Bord status + end_time: Beëindigen op + start_time: Begint om + title: Aanspreektitel + verifiable_results_file_hash: SHA256-controlegetal + verifiable_results_file_url: Verifieerbaar verkiezingsbestand + question: + fields: + answers: Antwoorden + max_selections: Max. selectie + title: Aanspreektitel + trustees_participatory_space: + fields: + considered: overwogen + email: E-mail + inactive: inactief + name: Naam + notification: Notificatie verzonden op + public_key: Publieke sleutel + status: Status + trustee_zone: + elections: + backup_modal: + description: Deze verkiezing wordt in het Bulletin Board aangemaakt. Het is erg belangrijk dat elke Trustee die eraan deelneemt een back-up van deze sleutels maakt en deze op een veilige plaats opslaat. Daarna gaat het proces verder. + download_election_keys: Sleutels downloaden + title: Maak een backup van verkiezingssleutels voor %{election} + key_ceremony_steps: + back: Terug + description: De verkiezing wordt aangemaakt in het Bulletin Board. Om dit proces te voltooien, is jouw deelname als Trustee nodig. + keys: + create_election: Sleutels worden aangemaakt + key_ceremony: + joint_election_key: Sleutels gezamenlijk aangemaakt + step_1: Sleutels publiceren + list: + status: Status + task: Taak + process_warning: Zodra het proces is gestart, mag je de pagina niet verlaten totdat het proces is afgerond. Het duurt enkele minuten, omdat alle Trustees verbonden moeten zijn om het te voltooien. + start: Begin + status: + completed: Voltooid + pending: In afwachting + processing: In verwerking + title: Maak verkiezingssleutels voor %{election} + restore_modal: + description: De Bulletin Board beschikt over informatie van jou als Trustee bij deze verkiezingen. Om het proces voort te zetten, upload eerst het back-upbestand dat aangemaakt is tijdens de vorige sessie. + title: Herstel verkiezingssleutels voor %{election} + upload_election_keys: Upload verkiezingssleutels + tally_started_steps: + description: De resultaten voor deze verkiezingen worden berekend in de Bulletin Board. Om dit proces te voltooien, is uw deelname als Trustee nodig. + menu: + trustee_zone: Trustee zone + no_bulletin_board: + body: Er is een geconfigureerd Bulletin Board nodig voor dit onderdeel. Neem contact op met de beheerder voor meer informatie. + title: Sorry, het Bulletin Board is nog niet geconfigureerd. + trustees: + show: + elections: + list: + action_required: + 'false': 'Nee' + name: Actie vereist? + 'true': Actie uitvoeren + bb_status: Status + election: Verkiezing + voting_period: Stemperiode + title: Verkiezingen + identification_keys: + cancel: Annuleer + generate: Genereer identificatiesleutels + generate_error: Er is een fout opgetreden bij het genereren van de identificatiesleutels. + generate_legend: Je moet een paar identificatiesleutels genereren om als Trustee deel te nemen aan verkiezingen. + generate_legend_1: Na het indrukken van de knop moet je het bestand downloaden met de gegenereerde identificatiesleutels. + generate_legend_2: Kopieer het gedownloade bestand naar een veilig USB-apparaat + generate_legend_4: Maak nog een kopie van het bestand op een ander extern apparaat en sla het op een zeer veilige plaats. + submit: Verzenden + submit_title: Vul de publieke identificatiesleutel in + title: Trustee identificatiesleutels + upload: Upload jouw identificatiesleutels + not_supported_browser_title: Upgrade de browser om als Trustee te fungeren + update: + success: Jouw publieke identificatiesleutel werd succesvol opgeslagen. + votes: + ballot_decision: + back: Start het stemproces opnieuw + ballot_hash: 'Je stem identificatie is:' + header: 'Stem is versleuteld: uitbrengen of controleren' + casting: + header: Aan het stemmen... + text: Je stem wordt uitgebracht in de stembus. + confirm: + answer_number: antwoord %{number} + confirm: Bevestigen + edit: bewerken + header: Bevestig uw stem + intro: Hier is een samenvatting van de stem die u gaat uitbrengen.
    Bevestig uw stem of bewerk uw antwoorden. + confirmed: + back: Terug naar verkiezingen + experience: Hoe was uw ervaring? + feedback: Geef ons feedback + header: Stem bevestigd + lead: Je stem is opgeslagen! + text: 'U kunt controleren of uw stem succesvol is toegevoegd aan de stembus met het volgende identificatie: %{e_vote_poll_id}' + verify_link: Om het te controleren, kopieer de identificatiecode en plak ze in de pagina voor het controleren van stemmen + create: + error: Er was een probleem bij het uitbrengen van de stem, probeer het opnieuw. + encrypting: + header: Versleutelen van de stem... + text: Je stem wordt versleuteld om het geheim ervan te waarborgen. + failed: + header: Stem mislukt + lead: Je stem is niet uitgebracht! + text: Er is iets mis gegaan, probeer het alsjeblief nog een keer. + try_again: Probeer opnieuw + header: + ballot_decision: Stem uitbrengen of stem controleren + confirm: Bevestig uw stem + messages: + invalid_token: Je sessie in het stemhokje is niet geldig. Probeer opnieuw te stemmen. + not_allowed: U mag op dit moment niet stemmen over deze verkiezingen. + modal: + close: Afsluiten + proposal_header: 'Voorstellen:' + new: + more_information: Meer informatie + preview_alert: Dit is een voorbeeld van de stembooth. + question_steps: Vraag %{current_step} van %{total_steps} + selections: "%{selected} van %{max_selections}
    selecties" + onboarding_modal: + create_account: Account aanmaken + no_account: Nee, bedankt. + update: + error: Er is een probleem opgetreden bij het bijwerken van stemstatus. Probeer het opnieuw. + verify: + content: + heading: Verifieer je stem + info: Deze verificateur controleert of je stem, geïdentificeerd met een gecodeerde tekststring, correct is uitgebracht en in de stembus zit. + error: + header: Stem niet gevonden! + info: De stemcode werd niet gevonden in de stembus %{link}, probeer het opnieuw. + form: + submit: Controleer + vote_identifier: 'Identificatiecode:' + header: + title: Verifieer je stem + success: + header: Stem gevonden! + voting_step: + back: Achterzijde + continue: Volgende + events: + elections: + election_published: + email_intro: 'De verkiezing van %{resource_title} is nu actief voor %{participatory_space_title}. Je kan het op deze pagina bekijken:' + email_outro: Je hebt deze melding ontvangen omdat je %{participatory_space_title}volgt. Je kan de meldingen uitschakelen door te klikken op de voorgaande link. + email_subject: De %{resource_title} verkiezing is nu actief voor %{participatory_space_title}. + notification_title: De %{resource_title} verkiezing is nu actief voor %{participatory_space_title}. + trustees: + new_election: + email_intro: Je bent toegevoegd als trustee voor de %{resource_title} stemronde. + new_trustee: + email_intro: Een beheerder heeft u toegevoegd als Trustee voor %{resource_name}. Je moet jouw openbare sleutel maken in je Trustee zone + email_subject: Je bent een trustee van %{resource_name}. + votes: + accepted_votes: + email_intro: 'Je stem is geaccepteerd! Met je stem token %{encrypted_vote_hash}, controleer je je stem hier.' + email_subject: Je stem voor %{resource_name} werd geaccepteerd. + notification_title: 'Je stem is geaccepteerd. Controleer je stem hier met behulp van je stem token: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Jij bent toegewezen als %{role} van het Stembureau %{polling_station_name} in %{resource_title}. Je kunt het Stembureau beheren vanuit de daarvoor voorziene Zone Stembeambte. + email_outro: Je hebt deze melding ontvangen omdat je bent toegevoegd als %{role} van %{polling_station_name}. + email_subject: Je bent %{role} van het Stembureau %{polling_station_name}. + notification_title: Je bent %{role} van het stembureau %{polling_station_name} in de stemronde %{resource_title}. + send_access_code: + instruction: 'Hier is de toegangscode waar je om vroeg: %{access_code}. Hiermee kun je deelnemen aan %{voting}.' + subject: Je toegangscode om deel te nemen aan %{voting} + menu: + votings: Stemronde + statistics: + elections_count: Verkiezingen + votings_count: Stemrondes + votings: + admin: + ballot_styles: + edit: + title: Pas stembriefstijl aan + update: Bijwerken + form: + election: Verkiezing + questions: Vragen voor deze stembrief stijl + index: + actions: + confirm_destroy: Weet je het zeker? + destroy: Verwijderen + edit: Bewerken + title: Acties + associated_census_data: Gekoppelde kiezerslijst gegevens + title: Stembriefstijlen + new: + create: Creëren + title: Maak stembriefstijl + content_blocks: + highlighted_votings: + max_results: Maximum aantal elementen dat kan worden weergegeven + index: + published: Gepubliceerd + menu: + votings: Stemrondes + votings_submenu: + attachment_collections: Mappen + attachment_files: Bestanden + attachments: Bijlagen + ballot_styles: Stembriefstijlen + census: Kiezerslijst + components: Onderdelen + landing_page: Landingspagina + monitoring_committee: Controlecomité + monitoring_committee_election_results: Resultaten valideren + monitoring_committee_members: Leden + monitoring_committee_polling_station_closures: Certificaten valideren + monitoring_committee_verify_elections: Verifieer verkiezingen + polling_stations: Stembureau + models: + ballot_style: + fields: + code: Code + monitoring_committee_member: + fields: + email: E-mail + name: Naam + polling_officer: + fields: + polling_station: Stembureau (rol) + polling_station: + fields: + address: Adres + polling_station_managers: Managers + polling_station_president: Voorzitter + title: Titel + voting: + fields: + created_at: Aangemaakt op + published: Gepubliceerd + title: Titel + monitoring_committee_election_results: + actions: + title: Acties + view: Bekijken + index: + title: Kies een verkiezing waarvan je de resultaten wil zien + results: + election_totals: Verkiezingstotalen + polling_stations: Stembureaus + result_types: + blank_answers: Blanco antwoorden + blank_ballots: Blanco stemmen + null_ballots: Geen stemmen + total_ballots: Totaal aantal stemmen + valid_ballots: Geldige stemmen + selected: Geselecteerd + title: Resultaten voor de verkiezing %{election_title} + totals: Totalen + show: + change_election: Verkiezing wijzigen + publish_results: Resultaten publiceren + publishing: Resultaten publiceren... + update: + rejected: De publicatie van de resultaten is geweigerd door het Bulletin Board. Probeer het opnieuw of neem contact op met de systeembeheerder. + monitoring_committee_members: + form: + existing_user: Bestaande deelnemer + non_user: Nodig een nieuwe deelnemer uit + select_user: Zoeken op naam, e-mail of bijnaam + user_type: Type deelnemer + index: + title: Controlecomité + new: + create: Creëren + title: Maak lid van controlecomité aan + monitoring_committee_polling_station_closures: + actions: + title: Acties + validate: Bevestigen + view: Bekijk + closures: + change_election: Verkiezing wijzigen + signed: Ondertekend? + title: Resultaten voor de verkiezing %{election_title} + validated: Gevalideerd? + edit: + change_polling_station: Terug naar stembureaus + monitoring_committee_notes: Opmerkingen + title: Resultaten voor de verkiezing %{election_title} in het stembureau %{polling_station_title} + elections: + title: Kies een verkiezing die je wilt valideren + show: + change_polling_station: Terug naar stembureaus + monitoring_committee_notes: Opmerkingen van het Comité van toezicht + monitoring_committee_verify_elections: + index: + download: Download + how_to_checksum: 'Om zeker te zijn dat het bestand dat je hebt gedownload niet is beschadigd of gemanipuleerd tijdens het downloadproces, voer de volgende opdracht uit op je toestel en controleer of de uitvoer overeenkomt met het controlegetal hierboven:' + how_to_download: Om een verkiezing te verifiëren, download het controleerbare bestand uit bovenstaande tabel. + how_to_run_verifier: 'Nadat je het bestand gedownload hebt en zeker weet dat het in orde is, kan je doorgaan met de universele controle. Kopieer deze repository en voer vanuit de hoofdmap de volgende opdracht uit:' + how_to_title: Hoe de geldigheid van een verkiezing te verifiëren + not_available: Nog niet beschikbaar + title: Verkiezingen + polling_officers: + index: + role_manager: manager + role_president: voorzitter + polling_officers_picker: + choose_polling_officers: Kies de stembeambtes + polling_stations: + edit: + title: Bewerk stembureau + update: Stembureau bijwerken + form: + address_help: 'Adres: gebruikt door Geocoder om de locatie te vinden' + location_help: 'Locatie: boodschap aan de kiezers met de exacte plaats van het stembureau' + location_hints_help: "Locatie hints: aanvullende informatie. Voorbeeld: de verdieping van het gebouw waar het stembureau \nzich bevindt." + polling_station_managers_help: 'Stembureau managers: de beambtes die als beheerder van stembureaus fungeren. Zorg ervoor dat de beambtes al zijn aangemaakt in Stembeambtes en dat ze nog niet zijn toegewezen aan een ander stembureau' + select_president: Selecteer een stembeambte als voorzitter van het stembureau + index: + title: Stembureaus + new: + create: Aanmaken + title: Stembureau aanmaken + titles: + votings: Stemrondes + votings: + actions: + confirm_destroy: Ben je zeker? + destroy: Verwijder + new_voting: Nieuwe stemruimte + edit: + update: Bijwerken + form: + select_a_voting_type: Selecteer een type stemming + title: Titel + voting_type: + hybrid: Hybride + in_person: In persoon + online: Online + new: + create: Aanmaken + title: Nieuwe stemronde + admin_log: + ballot_style: + create: "%{user_name} heeft een stembriefstijl gemaakt met code %{ballot_style_code} in %{space_name}" + delete: "%{user_name} heeft de stembriefstijl verwijderd met code %{ballot_style_code} in %{space_name}" + update: "%{user_name} heeft de stembriefstijl bijgewerkt met code %{ballot_style_code} in %{space_name}" + census: + create: "%{user_name} heeft de census voor %{space_name} gemaakt" + delete: "%{user_name} heeft de census voor %{space_name} verwijderd" + update: "%{user_name} heeft de census voor %{space_name} bijgewerkt" + monitoring_committee_member: + create: "%{user_name} heeft de gebruiker %{monitoring_committee_member_user} aangewezen als lid van de monitoring commissie in %{space_name}" + delete: "%{user_name} heeft de gebruiker %{monitoring_committee_member_user} verwijderd als lid van de monitoring commissie in %{space_name}" + polling_officer: + create: "%{user_name} heeft de gebruiker %{polling_officer_user} aangewezen als polling officer in %{space_name}" + delete: "%{user_name} heeft de gebruiker %{polling_officer_user} verwijderd als polling officer in %{space_name}" + polling_station: + create: "%{user_name} heeft het stembureau %{resource_name} van %{space_name} gemaakt" + delete: "%{user_name} heeft het stembureau %{resource_name} van %{space_name} verwijderd" + update: "%{user_name} heeft het stembureau %{resource_name} van %{space_name} bijgewerkt" + voting: + create: "%{user_name} heeft de stemronde %{resource_name} aangemaakt" + publish: "%{user_name} heeft de stemronde %{resource_name} gepubliceerd" + unpublish: "%{user_name} heeft de publicatie van de stemronde %{resource_name} ongedaan gemaakt" + census: + admin: + census: + create: + invalid: Er is een fout opgetreden bij het uploaden van de kiezerslijst, probeer het later opnieuw. + delete: + button: Verwijder alle kiezerslijstgegevens + destroy: + error: Er is een fout opgetreden bij het verwijderen van de kiezerslijst, probeer het later opnieuw. + export_access_codes: + button: Genereer toegangscodes voor de stemronde + callout: Je kunt nu doorgaan met het exporteren van de toegangscodes. Dit kan slechts één keer gedaan worden. Zodra je de export hebt opgestart, ontvang je een e-mail met de instructies voor %{email} + confirm: Je kunt de toegangscodes slechts één keer exporteren. Controleer of je toegang heeft tot het e-mailadres %{email}. + freeze: + callout: De kiezerslijst is bevroren, en kan niet worden gewijzigd. + generate_access_codes: + button: Genereer stemcodes + callout: Je kunt nu doorgaan met het genereren van de toegangscodes. Na het genereren van de toegangscodes kun je de kiezerslijst niet meer wijzigen. + confirm: Als je doorgaat, kan je het register niet wijzigen. + info_message_all: "Alle rijen zijn met succes geïmporteerd uit bestand %{file} (%{raw_count} van %{data_count})." + info_message_warn: Controleer of er geen gegevens ontbreken, omdat %{data_count} records zijn gemaakt en het geüploade bestand %{file} %{raw_count} rijen had. + launch_error: Probleem bij het starten van de toegangscodes export + launch_success: Codes genereren gestart. + new: + file_help: + explanation: 'Instructie bij het bestand:' + message_1: Alleen CSV-bestanden (.csv) zijn toegestaan. + message_2: Het scheidingsteken tussen kolommen moet een puntkomma (";") zijn. + has_ballot_styles_message: Je stelt StembriefStijlen in. Zorg ervoor dat het "%{ballot_style_code_header}" veld in de CSV overeenkomt met de gewenste StembriefStijlcode. + info_message: "Er is nog geen register. Gebruik het formulier hieronder om een CSV-bestand te importeren." + missing_ballot_styles_message: 'Er is nog geen StembriefStijl voor deze stemming. Als je voorwaardelijke vragen wenst te stellen (wil je de kiezer verschillende vragen voorleggen afhankelijk van bijvoorbeeld de wijk/regio), dan moet je de Ballot Stijlen instellen voordat je de kiezerslijst importeert. Als je alle kiezers dezelfde vragen wilt stellen, kun je doorgaan met de procedure van de kiezerslijstimport.' + title: Stel het register op + show: + heading: Register van de stemming + upload_info: + csv_example_with_ballot_style: 'Een voorbeeld van het bestand met stembriefstijlen:' + csv_example_without_ballot_style: 'Een voorbeeld van het bestand zonder stembriefstijlen:' + csv_header_after: Voeg het laatste veld ("%{ballot_style_code_header}") niet toe als je geen stembriefstijl of voorwaardelijke vragen nodig hebt + csv_header_before: 'De kiezerslijst moet een CSV-bestand zijn met de volgende header:' + document_types: + passport: Paspoort + export_mailer: + access_codes_export: + click_button: 'Klik op de volgende link om je toegangscode te downloaden.
    Het bestand is beschikbaar tot %{date}.
    Je hebt 7-Zip(for Windows), Keka (for MacOS) or PeaZip (for Linux) nodig om het te openen. Wachtwoord: %{password}' + download: Download + subject: De export van de stem toegangscodes voor %{voting_title} is beschikbaar + vote_flow: + already_voted_in_person: Deze gebruiker heeft al persoonlijk gestemd en heeft geen stemrecht. + content_blocks: + highlighted_votings: + name: Stemrondes in de kijker + landing_page: + polling_stations: + heading: Stembureaus + no_polling_stations: Er zijn nog geen stembureaus. + monitoring_committee_members: + actions: + confirm_destroy: Weet je het zeker? + destroy: Verwijderen + new: Nieuw lid + title: Acties + polling_officer_zone: + closures: + back_to_polling_stations: Terug naar stembureaus + certify: + error: Er is een fout opgetreden tijdens het toevoegen van het certificaat, probeer het opnieuw. + heading: Stem hertelling - Upload certificaat + info_text: Upload een foto van het Stemmensluitingscertificaat. + submit: Certificaat uploaden + success: Certificaat succesvol geüpload. + create: + error: Er is een fout opgetreden bij de sluiting, probeer het later opnieuw. + success: Sluiting succesvol aangemaakt. + edit: + heading: Stem hertelling - Hertelling van antwoorden + modal_ballots_results_count_error: + close_modal: Sluit + title: Totaal stemmen stemt niet overeen + save_recount: Bewaar hertelling + total_ballots: Totaal aantal stemmen + total_blank_ballots: Totaal blanco stemmen + total_null_ballots: Totaal ongeldige stemmen + total_valid_ballots: Totaal geldige stemmen + new: + election: 'Verkiezing:' + heading: Hertelling van de stemmen + info_text: 'Voer het totale aantal stemmen (enveloppen) in dat in dit stembureau werden herteld:' + modal_ballots_count_error: + btn_validate_total: Bevestig de totale hertelling van de stemmen + message_for_monitoring_committee: Bericht aan het Comité van toezicht + review_recount: Controleer de hertelling + text_area_placeholder: Schrijf je bericht + title: Totaal stemmen stemt niet overeen + total_ballots: 'Totaal aantal stemmen:' + total_people: 'Totaal aantal mensen:' + polling_station: 'Stembureau:' + submit: Controleer het totale aantal + total_ballots_count: Aantal stemmen + show: + heading: Hertelling van de stemmen + sign: + cancel: Annuleer + confirm: Ok, verdergaan + error: Er is een fout opgetreden, probeer het opnieuw. + heading: Stem hertelling - Onderteken sluiting + submit: Onderteken de sluiting + success: Sluiting succesvol ondertekend. + update: + error: Er is een fout opgetreden bij het bijwerken van de resultaten van de sluiting, probeer het later opnieuw. + success: Sluiting resultaten succesvol bijgewerkt. + in_person_votes: + complete_voting: + available_answers: 'Beschikbare antwoorden:' + complete_voting: Voltooi het stemmen + identify_another: Identificeer een andere deelnemer + voted: De gebruiker heeft gestemd + create: + error: De stemming werd niet geregistreerd. Probeer het opnieuw. + in_person_form: + census_not_present: Deze gebruiker staat niet op het stemregister. + census_not_present_description: Zij moeten zich tot het klachtenbureau voor de verkiezing wenden, of contact opnemen met Support. + date_of_birth: Geboortedatum + day: Dag + day_placeholder: DD + document_number: Documentnummer + document_number_placeholder: Id-nummer + month: Maand + month_placeholder: MM + select: Selecteer het type document + title: 'Selecteer het documenttype en voer het documentnummer van de gebruiker:' + validate_document: Het document valideren + year: Jaar + year_placeholder: JJJJ + new: + back: Terug naar stembureaus + title: Identificeer en verifieer een gebruiker + show: + back: Terug naar stembureaus + title: Wachten op de registratie van de persoonlijke stem + update: + error: Er is een fout opgetreden bij het registreren van de stemming. Probeer het opnieuw. + success: + accepted: De stem is met succes geregistreerd. + rejected: De stemming werd niet geaccepteerd door het Bulletin Board. Neem contact op met de systeembeheerder. + verify_document: + census_present: Deze gebruiker staat op het stemregister. + name: Naam + title: 'Controleer of de volgende gegevens juist zijn:' + verify_document: Document verifiëren + menu: + polling_officer_zone: Zone Stembeambte + polling_officers: + index: + polling_officer_role_description: Je bent aangewezen als Stembureau beambte (Voorzitter of manager) bij enkele van de verkiezingen die op dit platform worden gehouden. + polling_station: + address: Adres + count_votes: Stemmen tellen + election: Verkiezing + identify_person: Identificeer een persoon + name: Naam + no_polling_stations: Je bent nog niet toegewezen aan een Stembureau. + role: Jouw rol + show_closure: Bekijk sluiting + title: Stembureaus + voting: Stemronde + polling_officers: + roles: + manager: Manager + president: Voorzitter + unassigned: Niet toegewezen + polling_station_closure_recount: + nota_option: Leeg/Geen van bovenstaande + polling_officer_notes: 'Notities van de stembeambte:' + polling_officer_notes_blank: Er zijn geen notities + recount_summary: 'Samenvatting hertellen:' + signed: Ondertekend + total_ballots: 'Totaal aantal stemmen:' + total_blank_ballots: 'Totaal blanco stemmen:' + total_null_ballots: 'Totaal ongeldige stemmen:' + total_valid_ballots: 'Totaal geldige stemmen:' + polling_stations: + actions: + confirm_destroy: Ben je zeker? + destroy: Verwijderen + edit: Bewerken + title: Acties + votings: + access_code_modal: + email: Via e-mail verzenden naar %{email} + no_email: Geen e-mail beschikbaar + no_sms: Geen telefoonnummer beschikbaar + sms: Via e-mail verzenden naar %{sms} + title: Toegangscode krijgen + check_census: + check_status: Status controleren + description: Hier heb je de mogelijkheid om je gegevens op de kiezerslijst te controleren om te weten of je stemrecht hebt. Je zou al een toegangscode moeten hebben, maar als je die hebt verloren, kun je er opnieuw om vragen, als je gegevens juist zijn. + form_title: 'Vul het formulier in om de gegevens van je stemlijst te controleren:' + invalid: Er is een probleem opgetreden tijdens het controleren van de kiezerslijst. + success: + access_link_with_sms: via SMS of e-mail. + title: Je gegevens op de kiezerslijst zijn juist! + title: Kan ik stemmen? + check_fields: + date_of_birth: Geboortedatum + day: Dag + day_placeholder: DD + document_number: Documentnummer + document_number_placeholder: Id-nummer + month: Maand + month_placeholder: MM + postal_code: Postcode + postal_code_placeholder: Postcode nummer + select: Selecteer het type document + year: Jaar + year_placeholder: JJJJ + elections_log: + description: Het verkiezingslogboek toont je alle relevante informatie over elke stemming. Zo is bijvoorbeeld de status van de sleutelceremonie of telling, en of de resultaten al bekend zijn gemaakt. Klik op de verkiezing waarover je de logboekinformatie wilt hebben. + title: Verkiezingslogboek + filters: + active: Actief + all: Alle + finished: Afgelopen + search: Zoeken + upcoming: Gepland + index: + no_votings: Er zijn geen stemrondes die voldoen aan jouw zoekcriteria. + only_finished: Momenteel zijn er geen geplande stemrondes, maar hier vind je alle afgelopen stemrondes. + login: + access_code: Toegangscode + access_code_placeholder: Toegangscode + ask_for_a_new_one: Vraag om een nieuwe. + form_title: 'Vul het onderstaande formulier in om toegang te krijgen tot de stemming:' + start_voting: Begin met stemmen + title: Mezelf identificeren aan de hand van mijn stemlijstgegevens + votings_m: + voting_type: + hybrid: Hybride + in_person: In persoon + online: Online + layouts: + decidim: + voting_navigation: + check_census: Kan ik stemmen? + election_log: Verkiezingslogboek + votings: + index: + promoted_votings: Stemrondes in de kijker + promoted_voting: + vote: Stem diff --git a/decidim-elections/config/locales/no.yml b/decidim-elections/config/locales/no.yml new file mode 100644 index 00000000..45736811 --- /dev/null +++ b/decidim-elections/config/locales/no.yml @@ -0,0 +1,442 @@ +"no": + activemodel: + attributes: + answer: + description: Beskrivelse + image: Bilde + proposals: Relaterte forslag + title: Tittel + election: + description: Beskrivelse + end_time: Avstemningen avsluttes ved + start_time: Stemmegivning starter + title: Tittel + question: + max_selections: Maksimalt antall valg + min_selections: Ingen av valgene over + title: Tittel + voting: + end_time: Avstemningen avsluttes + start_time: Avstemningen begynner + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Må festes på nytt + election: + attributes: + attachment: + needs_to_be_reattached: Må festes på nytt + activerecord: + models: + decidim/elections/answer: + one: Svar + other: Svarene + decidim/elections/election: + one: Valg + other: Valg + decidim/elections/question: + one: Spørsmål + other: Spørsmål + decidim/votings/census/dataset: + one: Datasett + other: Datasett + decidim/votings/census/datum: + one: Data + other: Data + decidim/votings/polling_officer: + one: Avstemningansvarlig + other: Valgansvarlige + decidim/votings/polling_station: + one: Valgstasjon + other: Valgstasjoner + decidim/votings/voting: + one: Avstemning + other: Avstemninger + decidim: + admin: + filters: + officers_assigned_eq: + label: Ansvarlige + values: + assigned: Tildelt + unassigned: Ikke tildelt + role_eq: + label: Rolle + values: + manager: Ansvarlig + president: Leder + unassigned: Ikke tildelt + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Søk %{collection} med navn/e-post/kallenavn eller valgstasjon. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Søk %{collection} med tittel, adresse eller brukernavn/e-post/kallenavn. + signed_eq: + label: Signert + values: + 'false': Signert + 'true': Ikke signert + validated_eq: + label: Godkjent + components: + elections: + actions: + vote: Stem + name: Valg + settings: + global: + announcement: Kunngjøring + step: + announcement: Kunngjøring + elections: + actions: + confirm_destroy: Er du sikker? + destroy: Ødelegg + edit: Rediger + feedback: Tilbakemelding fra stemmegiver + import: Importer forslag til svar + manage_answers: Behandle svar + manage_questions: Behandle spørsmål + manage_steps: Behandle trinn + preview: Forhåndsvis + publish: Publiser + title: Handlinger + unpublish: Avpubliser + admin: + answers: + edit: + title: Rediger svar + update: Oppdater svar + index: + title: Svar + new: + create: Opprett svar + title: Nytt svar + not_selected: Ikke valgt + select: + disable: Fjern svar + enable: Marker svar som valgt + selected: Valgt + elections: + edit: + title: Rediger valg + update: Oppdater valg + index: + no_bulletin_board: Det er ingen server for oppslagstavle konfigurert, som er nødvendig for å bruke denne modulen. Denne oppgaven skal utføres av systemadministratoren. + title: Valg + new: + create: Opprett valg + title: Nytt valg + publish: + success: Valget er publisert. + unpublish: + success: Valget har blitt avpublisert. + exports: + elections: Valg + feedback_form_answers: Svar på tilbakemeldingsskjema + menu: + trustees: Tillitsmenn + models: + answer: + name: Svar + proposals_imports: + new: + create: Importer forslag til svar + no_components: Det er ingen andre utkastkomponenter i dette deltakerrommet for å importere forslagene til svar. + select_component: Vennligst velg en komponent + title: Importer forslag + questions: + edit: + title: Rediger spørsmål + update: Oppdater spørsmål + index: + title: Spørsmål + new: + create: Lag spørsmål + title: Nytt spørsmål + steps: + create_election: + errors: + max_selections: Spørsmålene har ingen korrekt verdi for antall svar + minimum_answers: Spørsmål må ha minst to svar. + minimum_questions: Valget må ha minst ett spørsmål. + published: Valget er og ikke publisert. + trustees_number: Deltakerområdet må ha minst %{number} tillitsmenn med offentlig nøkkel. + invalid: Det oppstod et problem med opprettelsen av dette valget + no_trustees: Det er ikke satt opp noen tillitsmenn for dette deltakelsesrommet + not_used_trustee: "(ikke brukt)" + public_key: + 'false': har ikke en offentlig nøkkel + 'true': har en offentlig nøkkel + requirements: + max_selections: Alle spørsmålene har en korrekt verdi for maks antall svar. + minimum_answers: Hvert spørsmål har minst 2 svar. + minimum_questions: Valget har minst 1 spørsmål. + published: Valget er publisert. + time_before: Oppsettet gjøres minst %{hours} timer før valget starter. + trustees_number: Deltakerområdet har minst %{number} tillitsmenn med offentlig nøkkel. + submit: Oppsett av valg + title: Oppsett valg + trustees: Valgets tillitsmenn + created: + submit: Start hovedseremonien + title: Valget opprettet + trustees: Tillitsmenn + key_ceremony: + continue: Fortsett + title: Hovedseremoni + key_ceremony_ended: + errors: + time_before: Valget er klart til å starte. Du må vente %{hours} timer før starttidspunktet (%{start_time}) for å starte avstemmingsperioden. + requirements: + time_before: Valget vil starte snart. Du kan starte valgperioden manuelt, eller den vil bli startet automatisk før starttidspunktet, %{start_time}. + submit: Start valgperiode + title: Klar for å begynne + processing: Behandler... + results_published: + answer: Svar + not_selected: Ikke valgt + question: Spørsmål + result: Resultat + selected: Valgt + submit: Send inn + title: Resultater publisert + tally_ended: + answer: Svar + not_selected: Ikke valgt + question: Spørsmål + result: Resultat + selected: Valgt + submit: Publiser resultater + title: Beregnet resultat + tally_started: + mark_as_missing: Marker som manglende + tally_completion: Prosessen vil bli fullført når alle tillitsmenn er aktive eller merket som manglende. Minst %{quorum} tillitsmenn er nødvendig for å fullføre prosessen. + undo_mark_as_missing: En tillitsmann merket som feilaktig merkes som manglende vil kunne delta før prosessen fullføres. De kan fortsette som vanlig og merket for "manglende" vil bli ignorert. + vote: + errors: + time_after: Valget pågår enda. Du må vente til det er ferdog (%{end_time}) for å avslutte avstemmingen. + requirements: + time_after: Valget er ferdig. Du kan avslutte avstemmingen manuelt, eller den vil automatisk avsluttes om noen minutter. + submit: Avslutt avstemming + title: Avstemming + vote_ended: + submit: Start opptelling + text: Avstemming avsluttet. Du kan starte opptellingen nå. + title: Avstemming avsluttet + vote_stats: + no_vote_statistics_yet: Ingen statistikk enda + title: Valgresultat + voters: Velgere + votes: Stemmer + trustees_participatory_spaces: + actions: + disable: Deaktiver + enable: Vurder + form: + select_user: Velg bruker + index: + title: Tillitsmenn + new: + create: Opprett tillitsmann + title: Ny tillitsmann + admin_log: + election: + create: "%{user_name} opprettet valget %{resource_name} i %{space_name}" + delete: "%{user_name} slettet valget %{resource_name} i %{space_name}" + end_vote: "%{user_name} avsluttet avstemmingen for valget %{resource_name} i %{space_name} på oppslagstavlen" + publish: "%{user_name} publiserte valget %{resource_name} i %{space_name}" + publish_results: "%{user_name} publiserte resultatet for valget %{resource_name} i %{space_name} på opplysningstavlen" + report_missing_trustee: "%{user_name} rapporterte %{trustee_name} som en manglende tillitsmann under opptellingen for valget %{resource_name} i %{space_name} på oppslagstavlen" + setup: "%{user_name} opprettet valget %{resource_name} i %{space_name} på oppslagstavlen" + start_key_ceremony: "%{user_name} startet nøkkelseremonien for valget %{resource_name} i %{space_name} på oppslagstavlen" + start_tally: "%{user_name} startet opptellingen for valget %{resource_name} i %{space_name} på oppslagstavlen" + start_vote: "%{user_name} startet avstemmingen for valget av %{resource_name} i %{space_name} på oppslagstavlen" + unpublish: "%{user_name} avpubliserte %{resource_name} i %{space_name}-valget" + update: "%{user_name} oppdaterte valget %{resource_name} i %{space_name}" + trustee: + create: "%{user_name} gjorde brukeren %{trustee_user} til tillitsmann" + election_m: + badge_name: + finished: Fullført + ongoing: Aktiv + upcoming: Kommende + end_date: Slutter + footer: + remaining_time: + one: "%{count} time %{minutes} minutter gjenstår til å stemme." + other: "%{count} timer %{minutes} minutter gjenstår til å stemme." + view: Vis + vote: Stem + label: + date: Datoer + questions: Spørsmål %{count} + start_date: Starter + unspecified: Ikke spesifisert + elections: + count: + elections_count: + one: "%{count} valg" + other: "%{count} valg" + election_log: + chained_hash: Hashkjedet for meldingen + complete: Fullfør + creation_description: + complete: Valget ble opprettet og ble ført opp på oppslagstavlen. + not_created: Valget er ikke opprettet enda. + creation_title: Valg opprettet + description: Dette er valgloggen hvor du kan sjekke statusen på hvert trinn, f.eks. når valget ble opprettet, om opptellingsprosessen er avsluttet, og når valget er stengt. + download: Last ned + key_ceremony_description: + complete: Nøkkelseremonien er fullført. Hver tillitsmann har gyldige nøkler og har lastet ned de nødvendige backup-nøklene. + not_started: Nøkkelseremonien har ikke startet enda. + started: Nøkkelseremonien har startet, men er ikke ferdig enda. + key_ceremony_title: Nøkkelseremoni + not_available: Foreløpig ikke tilgjengelig + not_created: Ikke opprettet + not_ready: Ikke klar + not_started: Ikke påbegynt + published: Publisert + results_description: + not_published: Resultatene er ikke publisert enda. + published: Resultatene er publisert. + results_title: Resultater + started: Startet + tally_description: + finished: Opptellingsprosessen er fullført. + not_started: Opptellingsprosessen har ikke startet enda. + started: Opptellingsprosessen har startet. + tally_title: Opptellingsprosess + title: Valglogg + verifiable_results: + checksum: 'Fil SHA256 sjekksum:' + description: + ready: 'Her har du mulighet til å verifisere valget. Først må du laste ned filen og sørge for at den ikke er ødelagt. Kjør følgende kommando og sjekk at utfallet samsvarer med kontrollsummen:' + how_to_verify: 'Når du har lastet ned filen og sørget for at den er ok, kan du fortsette å kjøre den universelle verifisereren. Klon dette kodelageret og, fra rotmappen, kjør følgende kommando:' + title: Verifiser valgresultater + verifiable_file: 'Verifiserbar valgfil:' + verify: Verifiser valget + vote_description: + finished: Avstemningsprosessen er fullført. + not_started: Avstemningsprosessen har ikke startet enda. + started: Avstemningsprosessen har startet. + vote_title: Avstemningsprosess + filters: + active: Aktiv + all: Alt + finished: Fullført + upcoming: Kommende + show: + action_button: + change_vote: Endre stemmen din + vote: Start avstemning + vote_again: Stem på nytt + callout: + already_voted: Du har allerede gitt din stemme i denne prosessen. Du kan endre stemmen din eller verifisere den. + vote_rejected: Det var ikke mulig å bekrefte stemmen din. Vennligst avgi den igjen. + preview: Forhåndsvisning + verify: + already_voted: Stemt allerede? + verify_here: Sjekk hva du har stemt på her. + will_verify: Du vil kunne bekrefte stemmen din når avstemningen starter. + voting_period_status: + finished: Stemmegivningen startet %{start_time} og opphørte %{end_time} + upcoming: Avstemning begynner %{start_time} + trustee_zone: + elections: + key_ceremony_steps: + list: + status: Status + task: Oppgave + process_warning: Når prosessen er startet, bør du ikke gå ut av denne siden før prosessen er ferdig. Det vil ta flere minutter fordi alle tillitsmenn skal kobles til dette for å fullføre. + start: Start + status: + completed: Fullført + pending: Ventende + processing: Behandler + title: Opprett valgnøkler for %{election} + restore_modal: + description: Oppslagstavlen har informasjon fra deg som tillitsmann om dette valget. For å fortsette prosessen, last opp sikkerhetskopien generert i løpet av forrige sesjon. + title: Gjenopprett valgnøkler for %{election} + upload_election_keys: Last opp valgnøkler + tally_started_steps: + description: Resultatet av valget utregnes i oppslagstavlen. For å fullføre denne prosessen må du delta som en tillitsmann. + keys: + end_tally: Opptelling avsluttet + tally: + cast: Opptelling ferdig + share: Opptellingsandel + title: Opptelling for %{election} + menu: + trustee_zone: Område for tillitsmenn + no_bulletin_board: + body: En konfigurert oppslagstavle er påkrevd for denne delen. Kontakt administrator for mer informasjon. + title: Beklager, oppslagstavlen er ikke konfigurert enda. + trustees: + show: + elections: + list: + action_required: + 'false': 'Nei' + name: Handling kreves? + 'true': Utfør handling + bb_status: Status + election: Valg + voting_period: Stemmeperiode + title: Valg + identification_keys: + cancel: Avbryt + generate: Generer identifikasjonsnøkler + generate_error: Kunne ikke generere identifikasjonsnøkkelene. + generate_legend: Du må generere et identifikasjonspar med nøkler for å delta i valg som en tillitsmann. + generate_legend_1: Etter å ha trykket på knappen bør du laste ned filen med de genererte identifikasjonsnøklene. + generate_legend_2: Kopier den nedlastede filen til en ren USB-enhet + generate_legend_4: Lag en annen kopi av filen på en annen ekstern enhet og lagre den på et svært trygt sted. + submit: Send inn + submit_title: Send inn den offentlige identifikasjonsnøkkelen + title: Identifikasjonsnøkler for tillitsmenn + upload: Last opp dine identifikasjonsnøkler + votes: + confirm: + answer_number: svar %{number} + confirm: Bekreft + edit: rediger + header: Bekreft stemmen din + intro: Her er et sammendrag av stemmen du er i ferd med å avgi.
    Bekreft stemmen din eller rediger svarene dine. + confirmed: + back: Tilbake til valg + experience: Hvordan var opplevelsen din? + feedback: Gi oss en tilbakemelding + header: Avstemning bekreftet + text: 'Du kan sjekke at stemmen din har blitt lagt til i stemmeboksen med følgende identifikator: %{e_vote_poll_id}' + header: + confirm: Bekreft stemmen din + messages: + not_allowed: Du har ikke lov til å stemme på dette valget for øyeblikket. + modal: + close: Lukk + proposal_header: 'Forslag:' + new: + more_information: Mer informasjon + preview_alert: Dette er en forhåndsvisning av stemmebåsen. + question_steps: Spørsmål %{current_step} av %{total_steps} + selections: "%{selected} av %{max_selections}
    valg" + verify: + success: + header: Stemme plassert! + voting_step: + back: Tilbake + continue: Neste + events: + elections: + election_published: + email_intro: 'Valget %{resource_title} er nå aktivt for %{participatory_space_title}. Du kan se det fra denne siden:' + votings: + admin: + menu: + votings_submenu: + monitoring_committee: Kontrollgruppe + votings: + login: + start_voting: Start avstemning diff --git a/decidim-elections/config/locales/oc-FR.yml b/decidim-elections/config/locales/oc-FR.yml new file mode 100644 index 00000000..325b3488 --- /dev/null +++ b/decidim-elections/config/locales/oc-FR.yml @@ -0,0 +1 @@ +oc: diff --git a/decidim-elections/config/locales/om-ET.yml b/decidim-elections/config/locales/om-ET.yml new file mode 100644 index 00000000..05e2e89c --- /dev/null +++ b/decidim-elections/config/locales/om-ET.yml @@ -0,0 +1 @@ +om: diff --git a/decidim-elections/config/locales/pl.yml b/decidim-elections/config/locales/pl.yml new file mode 100644 index 00000000..dab80831 --- /dev/null +++ b/decidim-elections/config/locales/pl.yml @@ -0,0 +1,1043 @@ +pl: + activemodel: + attributes: + answer: + description: Opis + image: Obraz + proposals: Powiązane propozycje + title: Tytuł + election: + description: Opis + end_time: Głosowanie kończy się o + start_time: Głosowanie rozpoczyna się o + title: Tytuł + question: + max_selections: Maksymalna liczba opcji do wybrania + min_selections: Żadna z powyższych opcji + title: Tytuł + voting: + banner_image: Banner + end_time: Głosowanie kończy się + start_time: Głosowanie rozpoczyna się + voting_type: Typ głosowania + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Załącz ponownie + election: + attributes: + attachment: + needs_to_be_reattached: Załącz ponownie + activerecord: + models: + decidim/elections/answer: + one: Odpowiedź + few: Odpowiedzi + many: Odpowiedzi + other: Odpowiedzi + decidim/elections/election: + one: Wybory + few: Wyborów + many: Wyborów + other: Wybory + decidim/elections/question: + one: Pytanie + few: Pytania + many: Pytania + other: Pytania + decidim/votings/census/dataset: + one: Zestaw danych + few: Zestawów danych + many: Zestawów danych + other: Zestawy danych + decidim/votings/census/datum: + one: Dane + few: Danych + many: Danych + other: Dane + decidim/votings/polling_officer: + one: Oficer wyborczy + few: Oficerów wyborczych + many: Oficerów wyborczych + other: Oficerowie wyborczy + decidim/votings/polling_station: + one: Lokal wyborczy + few: Lokale wyborcze + many: Lokali wyborczych + other: Lokale wyborcze + decidim/votings/voting: + one: Głosowanie + few: Głosowania + many: Głosowań + other: Głosowania + decidim: + admin: + filters: + officers_assigned_eq: + label: Oficerowie + values: + assigned: Przypisano + unassigned: Nie przypisano + role_eq: + label: Rola + values: + manager: Kierownik + president: Przewodniczący + unassigned: Nie przypisano + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Przeszukaj %{collection} według nazwy/adresu e-mail/pseudonimu lub lokalu wyborczego. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Szukaj %{collection} według tytułu, adresu lub imienia/adresu e-mail/pseudonimu oficera. + signed_eq: + label: Podpisane + values: + 'false': Podpisane + 'true': Nie podpisano + validated_eq: + label: Zweryfikowano + components: + elections: + actions: + vote: Głosowanie + name: Wybory + settings: + global: + announcement: Ogłoszenie + step: + announcement: Ogłoszenie + elections: + actions: + confirm_destroy: Na pewno? + destroy: Usuń + edit: Edytuj + feedback: Informacja zwrotna o głosowaniu + import: Importuj propozycje do odpowiedzi + manage_answers: Zarządzaj odpowiedziami + manage_questions: Zarządzaj pytaniami + manage_steps: Zarządzaj krokami + preview: Podgląd + publish: Opublikuj + title: Działania + unpublish: Cofnij publikację + admin: + answers: + edit: + title: Edytuj odpowiedź + update: Aktualizuj odpowiedź + index: + title: Odpowiedzi + new: + create: Utwórz odpowiedź + title: Nowa odpowiedź + not_selected: Nie wybrane + select: + disable: Odznacz odpowiedź + enable: Zaznacz odpowiedź + selected: Wybrane + elections: + edit: + title: Edytuj wybory + update: Aktualizuj wybory + index: + no_bulletin_board: Brak skonfigurowanego serwera tablicy danych wyborczych, który jest potrzebny do korzystania z tego modułu. Zadanie to powinno być wykonane przez administratora systemu. + title: Wybory + new: + create: Utwórz wybory + title: Nowe wybory + publish: + success: Wybory zostały pomyślnie opublikowane. + unpublish: + success: Cofnięto publikacje wyborów. + exports: + elections: Wybory + feedback_form_answers: Odpowiedzi zebrane z formularza + menu: + trustees: Mężowie (osoby) zaufania + models: + answer: + name: Odpowiedź + proposals_imports: + new: + create: Importuj propozycje do odpowiedzi + no_components: W tej przestrzeni partycypacyjnej nie ma już żadnych innych komponentów propozycji do zaimportowania do odpowiedzi. + select_component: Wybierz komponent + title: Importuj propozycje + questions: + edit: + title: Edytuj pytanie + update: Zaktualizuj pytanie + index: + title: Pytania + new: + create: Utwórz pytanie + title: Nowe pytanie + steps: + create_election: + errors: + max_selections: Maksymalna liczba opcji do wybrania nie jest odpowiednia do pytania + minimum_answers: Pytania muszą mieć co najmniej dwie odpowiedzi. + minimum_questions: Wybory muszą mieć co najmniej jedno pytanie. + published: Wybory nie zostały opublikowane. + trustees_number: Przestrzeń partycypacyjna musi mieć co najmniej %{number} mężów (osób) zaufania z kluczem publicznym. + invalid: Wystąpił problem z konfiguracją tych wyborów + no_trustees: Nie skonfigurowano mężów (osób) zaufania dla tej przestrzeni partycypacyjnej + not_used_trustee: "(nieużywane)" + public_key: + 'false': nie posiada klucza publicznego + 'true': posiada klucz publiczny + requirements: + max_selections: Maksymalna liczby opcji do wybrania jest odpowiednia dla wszystkich pytań. + minimum_answers: Pytania muszą mieć co najmniej dwie odpowiedzi. + minimum_questions: Wybory muszą mieć co najmniej jedno pytanie. + published: Wybory zostały opublikowane. + time_before: Konfiguracja odbywa się co najmniej %{hours} godzin(y) przed rozpoczęciem wyborów. + trustees_number: Przestrzeń partycypacyjna musi mieć co najmniej %{number} mężów (osób) zaufania z kluczem publicznym. + submit: Ustaw wybory + title: Skonfiguruj wybory + trustees: Mężowie (osoby) zaufania + created: + submit: Rozpocznij "ceremonię kluczy" + title: Utworzono wybory + trustees: Mężowie (osoby) zaufania + key_ceremony: + continue: Dalej + title: '"Ceremonia kluczy"' + key_ceremony_ended: + errors: + time_before: Wybory są gotowe do otwarcia. Aby rozpocząć głosowanie musisz poczekać do %{hours} godzin(y) przed czasem rozpoczęcia (%{start_time}). + requirements: + time_before: Wybory rozpoczną się wkrótce. Możesz otworzyć głosowanie ręcznie lub rozpocznie się ono automatycznie przed godziną rozpoczęcia o %{start_time}. + submit: Otwórz głosowanie + title: Gotowe do rozpoczęcia + processing: Przetwarzanie... + results_published: + answer: Odpowiedź + not_selected: Nie wybrano + question: Pytanie + result: Wynik + selected: Wybrane + submit: Prześlij + title: Opublikowano wyniki + tally_ended: + answer: Odpowiedź + not_selected: Nie wybrano + question: Pytanie + result: Wynik + selected: Wybrano + submit: Opublikuj wyniki + title: Obliczone wyniki + tally_started: + mark_as_missing: Oznacz jako brakujący + tally_completion: 'Proces zostanie zakończony, gdy wszyscy mężowie zaufania będą aktywni lub zostaną oznaczeni jako brakujący. Wymagana ilość mężów zaufania: %{quorum}.' + undo_mark_as_missing: Przed zakończeniem procesu będzie mógł uczestniczyć mąż zaufania oznaczony jako brakujący omyłkowo. Mężowie zaufania mogą postępować jak zwykle, a oznaczenie jako "brakujący" zostanie zignorowane. + vote: + errors: + time_after: Wybory nadal trwają. Musisz poczekać do daty zakończenia (%{end_time}) aby zamknąć głosowanie. + requirements: + time_after: Wybory zakończyły się. Możesz ręcznie zamknąć głosowanie lub zostanie ono automatycznie zamknięte za kilka minut. + submit: Zamknij głosowanie + title: Czas głosowania + vote_ended: + submit: Ustal wynik głosowania + text: Głosowanie zakończyło się. Możesz rozpocząć proces ustalania wyniku głosowania. + title: Czas głosowania zakończony + vote_stats: + no_vote_statistics_yet: Brak statystyk głosowania + title: Statystyki głosowania + voters: Wyborcy + votes: Głosy + trustees_participatory_spaces: + actions: + disable: Wyłącz + enable: Rozważ + form: + select_user: Wybierz użytkownika + index: + title: Mężowie (osoby) zaufania + new: + create: Utwórz męża (osobę) zaufania + title: Nowy mąż (osoba) zaufania + admin_log: + election: + publish: "%{user_name} opublikował(a) wybory %{resource_name} w %{space_name}" + publish_results: "%{user_name} opublikował wyniki głosowania w %{resource_name} w %{space_name} na tablicy ogłoszeń" + unpublish: "%{user_name} cofnął/cofnęła publikację wyborów %{resource_name} w %{space_name}" + election_m: + badge_name: + finished: Zakończone + ongoing: Aktywne + upcoming: Nadchodzące + end_date: Data zakończenia + footer: + remaining_time: + one: "%{count} godzina %{minutes} minut pozostało do głosowania." + few: "%{count} godzin %{minutes} minut pozostało do głosowania." + many: "%{count} godzin %{minutes} minut pozostało do głosowania." + other: "%{count} godziny %{minutes} minuty pozostały do głosowania." + view: Zobacz + vote: Głosuj + label: + date: Data + questions: Pytania %{count} + start_date: Data rozpoczęcia + unspecified: Nie określono + elections: + count: + elections_count: + one: "%{count} wybory" + few: "%{count} wyborów" + many: "%{count} wyborów" + other: "%{count} wybory" + election_log: + chained_hash: Cyfrowy odcisk (hasz) tej wiadomości + complete: Zamknięty + creation_description: + complete: Wybory zostały utworzone i pomyślnie dodano je do Tablicy danych wyborczych. + not_created: Wybory nie zostały jeszcze utworzone. + creation_title: Utworzono wybory + description: Oto dziennik zdarzeń wyborczych, w którym możesz sprawdzić status każdy krok procesu wyborczego np. kiedy utworzono wybory, czy ustalono wynik głosowania i czy wybory zostały zamknięte. + download: Pobierz + key_ceremony_description: + complete: '"Ceremonia kluczy" została zakończona. Każdy mąż (osoba) zaufania ma ważne klucze i pobrał niezbędne klucze zapasowe.' + not_started: '"Ceremonia kluczy" nie została jeszcze rozpoczęta.' + started: '"Ceremonia kluczy" rozpoczęła się, ale nie została jeszcze zakończona.' + key_ceremony_title: '"Ceremonia kluczy"' + not_available: Jeszcze nie jest dostępny + not_created: Nie został utworzony + not_ready: Nie jest gotowy + not_started: Nie rozpoczął się + published: Ogłoszony + results_description: + not_published: Wyniki głosowania nie zostały jeszcze ogłoszone. + published: Ogłoszono wyniki głosowania. + results_title: Wyniki głosowania + started: Rozpoczęty + tally_description: + finished: Proces ustalania wyniku głosowania został zakończony. + not_started: Proces ustalania wyniku głosowania jeszcze nie rozpoczął się. + started: Proce ustalania wyniku głosowania rozpoczął się. + tally_title: Proces ustalania wyniku głosowania + title: Dziennik zdarzeń wyborczych + verifiable_results: + checksum: 'Suma kontrolna SHA256 pliku:' + description: + ready: 'Możesz tutaj skorzystać z opcji sprawdzenia wyborów. Aby to uczynić, pobierz wpierw plik i upewnij się, że nie został uszkodzony. Użyj następującego polecenia aby sprawdzić czy wynik jest zgodny z sumą kontrolną:' + how_to_verify: 'Po pobraniu pliku i upewnieniu się, że plik jest w porządku, możesz przejść do uruchomienia uniwersalnego weryfikatora. Sklonuj to repozytorium i wykonaj następujące polecenie z katalogu root:' + title: Zweryfikuj wyniki wyborów + verifiable_file: 'Sprawdzalne dane wyborcze:' + verify: Sprawdź wybory + vote_description: + finished: Proces głosowania został zakończony. + not_started: Proces głosowania jeszcze się nie rozpoczął. + started: Proces głosowania rozpoczął się. + vote_title: Proces głosowania + filters: + active: Aktywne + all: Wszystkie + finished: Zakończone + upcoming: Nadchodzące + preview: + available_answers: 'Możliwe odpowiedzi:' + description: 'Oto pytania, które zostaną zadane w procesie głosowania:' + title: Pytania wyborcze + results: + description: 'Takie są wyniki głosowania w odniesieniu do każdego pytania:' + percentage: "%{count}%" + selected: Wybrane + title: Wyniki wyborów + votes: + one: "%{count} głos" + few: "%{count} głosów" + many: "%{count} głosów" + other: "%{count} głosy" + show: + action_button: + change_vote: Zmień swój głos + vote: Rozpocznij głosowanie + vote_again: Zagłosuj ponownie + callout: + already_voted: Już głosowałeś w tych wyborach. Możesz zmienić swój głos lub zweryfikować go. + pending_vote: Twój głos jest właśnie oddawany na serwerze. + vote_rejected: Nie udało się zweryfikować Twojego głosu. Proszę przesłać go ponownie. + election_log: Dziennik zdarzeń wyborczych + preview: Podgląd + verify: + already_voted: Już głosowałem? + verify_here: Sprawdź swój głos tutaj. + will_verify: Po rozpoczęciu wyborów będziesz mógł zweryfikować swój głos. + voting_period_status: + finished: Głosowanie rozpoczęło się %{start_time} i zakończyło się %{end_time} + ongoing: 'Głosowanie aktywne do: %{end_time}' + upcoming: Głosowanie rozpoczyna się %{start_time} + feedback: + answer: + invalid: Wystąpił błąd podczas wysyłania Twojej opinii. + success: Opinia została wysłana. + models: + answer: + fields: + proposals: Propozycje + selected: Wybrano + title: Tytuł + votes: Głosy + election: + fields: + bb_status: Status Tablicy danych wyborczych + end_time: Koniec o + start_time: Zaczyna się o + title: Tytuł + verifiable_results_file_hash: Suma kontrolna pliku SHA256 + verifiable_results_file_url: Sprawdzalne dane wyborcze + question: + fields: + answers: Odpowiedzi + max_selections: Maks. liczba opcji do wybrania + title: Tytuł + trustees_participatory_space: + fields: + considered: uznany + email: E-mail + inactive: nieaktywne + name: Imię + notification: Wysłano powiadomienie o + public_key: Klucz publiczny + status: Status + orders: + label: Sortuj wybory według + older: Najstarsze + recent: Najnowsze + trustee_zone: + elections: + backup_modal: + description: Te wybory są tworzone na Tablicy danych wyborczych. Każdy przypisany do niej mąż (osoba) zaufania powinien stworzyć kopię zapasową kluczy i przechowywać je w bezpiecznym miejscu. Po tym, proces będzie kontynuowany. + download_election_keys: Pobierz klucze + title: Kopia zapasowa kluczy dla głosowania %{election} + key_ceremony_steps: + back: Wróć + description: Te wybory są tworzone na Tablicy danych wyborczych. Aby zakończyć ten proces potrzebny jest Twój udział jako męża (osoby) zaufania. + keys: + create_election: Generowanie kluczy + key_ceremony: + joint_election_key: Wspólna tworzenie kluczy + step_1: Publikowanie kluczy + list: + status: Status + task: Zadanie + process_warning: Po rozpoczęciu procesu, nie powinieneś opuszczać tej strony do czasu jego zakończenia. Zajmie to kilka minut, ponieważ wszyscy mężowie (osoby) zaufania powinni być podłączeni w celu jego ukończenia. + start: Rozpocznij + status: + completed: Zakończono + pending: Oczekujące + processing: Przetwarzanie + title: Utwórz klucze dla wyborów %{election} + restore_modal: + description: Tablica danych wyborczych posiada informacje od Ciebie jako męża (osoby) zaufania w tych wyborach. Aby kontynuować proces, najpierw wgraj plik kopii zapasowej wygenerowany podczas poprzedniej sesji. + title: Przywróć klucze dla wyborów %{election} + upload_election_keys: Prześlij klucze wyborcze + tally_started_steps: + description: Wyniki tego głosowania są generowane na Tablicy danych wyborczych. Aby zakończyć ten proces niezbędne jest twoje uczestnictwo jako męża (osoby) zaufania. + keys: + end_tally: Zakończono proces ustalania wyniku głosowania + tally: + cast: Suma oddanych głosów + share: Udostępnij ustalony wynik głosowania + title: Wynik głosowania dla %{election} + menu: + trustee_zone: Strefa mężów (osób) zaufania + no_bulletin_board: + body: Ta sekcja wymaga uprzedniego skonfigurowania Tablicy danych wyborczych. Aby uzyskać więcej informacji, skontaktuj się z administratorem. + title: Przykro nam, Tablica danych wyborczych nie została jeszcze skonfigurowana. + trustees: + show: + elections: + list: + action_required: + 'false': 'Nie' + name: Wymaga działania? + 'true': Wykonaj działanie + bb_status: Status + election: Wybory + voting_period: Czas głosowania + title: Wybory + identification_keys: + cancel: Anuluj + generate: Utwórz klucze identyfikacyjne + generate_error: Wystąpił błąd podczas tworzenia kluczy identyfikacyjnych. + generate_legend: Musisz utworzyć parę kluczy identyfikacyjnych do udziału w głosowaniu jako mąż (osoba) zaufania. + generate_legend_1: Po kliknięciu przycisku powinieneś pobrać plik z utworzonymi kluczami identyfikacyjnymi. + generate_legend_2: Skopiuj pobrany plik na puste urządzenia USB + generate_legend_4: Utwórz kolejną kopię pliku na innym urządzeniu zewnętrznym i przechowuj go w bezpiecznym miejscu. + submit: Prześlij + submit_title: Prześlij publiczny klucz identyfikacyjny + title: Klucze identyfikacyjne męża (osoby) zaufania + upload: Prześlij swoje klucze identyfikacyjne + not_supported_browser_title: Aktualizuj przeglądarkę by działać jako mąż (osoba) zaufania + update: + success: Twój publiczny klucz identyfikacyjny został zapisany. + votes: + ballot_decision: + audit: ( Sprawdź kartę z oddanym głosem ) + back: Rozpocznij proces głosowania ponownie + ballot_hash: 'Numer Twojej karty do głosowania to:' + header: 'Karta do głosowania została zaszyfrowana. Oddaj głos, albo sprawdź poprawność szyfrowania karty' + casting: + header: Oddawanie głosu... + text: Twoja karta do głosowania jest składana do urny wyborczej. + confirm: + answer_number: odpowiedz %{number} + confirm: Potwierdź + edit: edytuj + header: Potwierdź swój głos + intro: Oto podsumowanie głosu, który zamierzasz oddać.
    Proszę potwierdzić swój głos lub edytuj swoje odpowiedzi. + nota_option: Pusty + confirmed: + back: Wróć do wyborów + experience: Jak oceniasz proces głosowania? + feedback: Podziel się z nami swoją opinią + header: Oddanie głosu potwierdzone + lead: Twój głos został oddany! + text: 'Możesz sprawdzić, czy Twój głos został pomyślnie dodany do urny wyborczej z następującym identyfikatorem: %{e_vote_poll_id}' + verify_link: Aby sprawdzić, skopiuj identyfikator i wklej go na stronie weryfikacji głosowania + create: + error: Wystąpił błąd w czasie oddawania głosu, Spróbuj ponownie. + encrypting: + header: Szyfrowanie głosu... + text: Twoja karta do głosowania podlega szyfrowaniu aby zapewnić tajność Twojego głosu. + failed: + header: Oddanie głosu nie powiodło się + lead: Twój głos nie został oddany! + text: Coś poszło nie tak. Spróbuj ponownie. + try_again: Spróbuj ponownie + header: + ballot_decision: Oddaj głos, albo sprawdź poprawność szyfrowania Twojego głosu + confirm: Potwierdź swój głos + messages: + invalid_token: Twoja sesja w kabinie wyborczej nie jest ważna. Spróbuj zagłosować ponownie. + not_allowed: W tej chwili nie wolno Ci głosować w tych wyborach. + modal: + close: Zamknij + proposal_header: 'Propozycje:' + new: + answer_choices: Możesz wybrać do %{choices} odpowiedzi + more_information: Więcej informacji + nota_option: Pusty/Żadne z powyższych + preview_alert: To jest podgląd kabiny do głosowania. + question_steps: Pytanie %{current_step} z %{total_steps} + selections: "%{selected} z %{max_selections}
    opcji wyboru" + onboarding_modal: + create_account: Utwórz konto + no_account: Nie, dziękuję. + update: + error: Wystąpił błąd z aktualizacją stanu Twojego głosu. Zagłosuj ponownie. + verify: + content: + heading: Zweryfikuj swój głos + info: Ten weryfikator sprawdza, czy Twój głos, zidentyfikowany za pomocą zaszyfrowanego ciągu znaków, został poprawnie oddany i znajduje się w urnie wyborczej. + error: + header: Nie znaleziono głosu! + info: Kod głosu nie został znaleziony na urnie wyborczej %{link}, spróbuj ponownie. + form: + submit: Sprawdź + vote_identifier: 'Kod identyfikacyjny:' + header: + title: Zweryfikuj swój głos + success: + header: Odnaleziono głos! + voting_step: + back: Wróć + continue: Dalej + events: + elections: + election_published: + email_intro: 'Wybory %{resource_title} są teraz aktywne w %{participatory_space_title}. Możesz je zobaczyć na tej stronie:' + email_outro: Otrzymałeś to powiadomienie, ponieważ obserwujesz %{participatory_space_title}. Możesz wyłączyć powiadomienia po kliknięciu w powyższy link. + email_subject: Wybory %{resource_title} są teraz aktywne dla %{participatory_space_title}. + notification_title: Wybory %{resource_title} są teraz aktywne dla %{participatory_space_title}. + trustees: + new_election: + email_intro: Zostałeś powołany do pełnienia funkcji męża (osoby) zaufania do głosowania %{resource_title}. + new_trustee: + email_intro: Administrator ustanowił Cię mężem (osobą) zaufania w %{resource_name}. Powinieneś utworzyć klucz publiczny w strefie osób zaufania + email_subject: Jesteś mężem (osobą) zaufania w %{resource_name}. + votes: + accepted_votes: + email_intro: 'Twój głos został zaakceptowany! Używając tokenu głosowania: %{encrypted_vote_hash}, możesz zweryfikować swój głos tutaj.' + email_subject: Twój głos na %{resource_name} został zaakceptowany. + notification_title: 'Twój głos został zaakceptowany. Zweryfikuj swój głos tutaj używając swojego tokenu głosu: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Zostałeś wyznaczony na %{role} w lokalu wyborczym %{polling_station_name} w %{resource_title}. Możesz zarządzać lokalami wyborczymi z dedykowanej Strefy Wyborczej. + email_outro: Otrzymałeś to powiadomienie ponieważ zostałeś wyznaczony na %{role} w %{polling_station_name}. + email_subject: Posiadasz rolę %{role} w lokalu wyborczym %{polling_station_name}. + notification_title: Posiadasz rolę %{role} lokalu wyborczego %{polling_station_name} w głosowaniu %{resource_title}. + send_access_code: + instruction: 'Oto twój kod dostępu, o który prosiłeś: %{access_code}. Dzięki niemu będziesz mógł uczestniczyć w %{voting}.' + subject: Twój Kod dostępu do wzięcia udziału w %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Głosowanie jest przestrzenią, która pozwala na zadawanie pytań wszystkim użytkownikom organizacji, poinformuj o możliwości uczestnictwa w głosowaniu, rozpocznij i poprowadź debatę za lub przeciw. Gdy data głosowania nadejdzie, możesz oddać głos i opublikować rezultaty głosowania.

    Przykłady: Glosowania mogą dotyczyć niemal każdej rzeczy którą zajmuje się organizacja: przykładami są zmiana nazwy i logo organizacji po przedstawieniu kilku propozycji, decydowanie Tak lub Nie w kwestii dołączenia do większej organizacji, akceptacja lub odrzucenie planów strategicznych lub wyników pracy grup lub określenie, czy stanowiska w organizacji powinny wynosić maksymalnie 1, 2, czy 3 mandaty.

    \n" + page: "

    Głosowanie jest przestrzenią, która pozwala na zadawanie pytań wszystkim użytkownikom organizacji, poinformuj o możliwości uczestnictwa w głosowaniu, rozpocznij i poprowadź debatę za lub przeciw. Gdy data głosowania nadejdzie, możesz oddać głos i opublikować rezultaty głosowania.

    Przykłady: Glosowania mogą dotyczyć niemal każdej rzeczy którą zajmuje się organizacja: przykładami są zmiana nazwy i logo organizacji po przedstawieniu kilku propozycji, decydowanie Tak lub Nie w kwestii dołączenia do większej organizacji, akceptacja lub odrzucenie planów strategicznych lub wyników pracy grup lub określenie, czy stanowiska w organizacji powinny wynosić maksymalnie 1, 2, czy 3 mandaty.

    \n" + title: Czym są głosowania? + menu: + votings: Głosowania + statistics: + elections_count: Wybory + votings_count: Głosowań + votings: + admin: + ballot_styles: + edit: + title: Edytuj rodzaj kart wyborczych + update: Aktualizuj + form: + election: Wybory + questions: Pytania dla tego rodzaju kart wyborczych + index: + actions: + confirm_destroy: Czy na pewno? + destroy: Usuń + edit: Edytuj + title: Działania + associated_census_data: Powiązane wpisy z listy wyborców + title: Rodzaje kart wyborczych + new: + create: Utwórz + title: Utwórz rodzaj kart wyborczych + content_blocks: + highlighted_votings: + max_results: Maksymalna ilość elementów do wyświetlenia + index: + published: Opublikowano + menu: + votings: Głosowania + votings_submenu: + attachment_collections: Foldery + attachment_files: Pliki + attachments: Załączniki + ballot_styles: Rodzaje kart wyborczych + census: Lista wyborców + components: Komponenty + landing_page: Strona Startowa + monitoring_committee: Komisja monitorująca + monitoring_committee_election_results: Zweryfikuj wyniki + monitoring_committee_members: Członkowie + monitoring_committee_polling_station_closures: Zweryfikuj certyfikaty + monitoring_committee_verify_elections: Sprawdź wybory + polling_officers: Oficerowie wyborczy + polling_stations: Lokale wyborcze + models: + ballot_style: + fields: + code: Kod + monitoring_committee_member: + fields: + email: Adres e-mail + name: Imię + polling_officer: + fields: + email: Adres e-mail + name: Imię + polling_station: Lokal wyborczy (role) + polling_station: + fields: + address: Adres + polling_station_managers: Kierownicy + polling_station_president: Przewodniczący + title: Tytuł + voting: + fields: + created_at: Utworzono + published: Opublikowano + title: Tytuł + monitoring_committee_election_results: + actions: + title: Działania + view: Widok + index: + title: Wybierz wybory, których wyniki chcesz zobaczyć + results: + bulletin_board: Tablica danych wyborczych + election_totals: Podsumowanie wyborów + polling_stations: Lokale wyborcze + result_types: + blank_answers: Puste głosy + blank_ballots: Puste głosy + null_ballots: Puste głosy + total_ballots: Suma oddanych głosów + valid_ballots: Suma ważnych głosów + selected: Wybrane + title: Wyniki głosowania w wyborach %{election_title} + totals: Razem + show: + change_election: Zmień wybory + publish_results: Opublikuj wyniki + publishing: Ogłaszanie wyników głosowania... + update: + rejected: Ogłoszenie wyników głosowania zostało odrzucone przez Tablicę danych wyborczych. Spróbuj ponownie, albo skontaktuj się z administratorem systemu. + monitoring_committee_members: + form: + existing_user: Istniejący użytkownik + non_user: Zaproś użytkownika + select_user: Przeszukaj według adresu e-mail, imienia lub nazwy użytkownika + user_type: Typ użytkownika + index: + title: Komisja Wyborcza + new: + create: Utwórz + title: Dodaj członka Komisji Wyborczej + monitoring_committee_polling_station_closures: + actions: + title: Działania + validate: Zweryfikuj + view: Widok + closures: + change_election: Zmień wybór + signed: Podpisane? + title: Lokale wyborcze w wyborach %{election_title} + validated: Zweryfikowano? + edit: + change_polling_station: Wróć do lokali wyborczych + monitoring_committee_notes: Uwagi + title: Wyniki głosowania %{election_title} w lokalu wyborczym %{polling_station_title} + elections: + title: Wybierz wybory, które chcesz zatwierdzić + show: + change_polling_station: Wróć do lokali wyborczych + monitoring_committee_notes: Uwagi Komisji Wyborczej + monitoring_committee_verify_elections: + index: + download: Pobierz + how_to_checksum: 'Aby upewnić się, że pobrany plik nie został uszkodzony lub podmieniony podczas procesu pobierania, uruchom następujące polecenie w konsoli i sprawdź, czy wynik zgadza się z sumą kontrolną powyżej:' + how_to_download: Aby sprawdzić wybory pobierz sprawdzalne dane wyborcze z powyższej tabeli. + how_to_run_verifier: 'Po pobraniu pliku i upewnieniu się, że plik jest w porządku, możesz przejść do uruchomienia uniwersalnego weryfikatora. Sklonuj to repozytorium i wykonaj następujące polecenie z katalogu root:' + how_to_title: Jak sprawdzić ważność wyborów + not_available: Jeszcze nie jest dostępny + title: Wybory + polling_officers: + form: + existing_user: Istniejący użytkownik + non_user: Zaproś użytkownika + select_user: Przeszukaj według adresu e-mail, imienia lub nazwy użytkownika + user_type: Typ użytkownika + index: + role_manager: kierownik + role_president: przewodniczący + title: Oficerowie wyborczy + new: + create: Utwórz + title: Utwórz oficera wyborczego + polling_officers_picker: + choose_polling_officers: Wybierz oficerów wyborczych + polling_stations: + edit: + title: Edytuj lokal wyborczy + update: Zaktualizowano lokal wyborczy + form: + address_help: 'Adres: używany przez Geocoder do znalezienia lokalizacji' + location_help: 'Lokalizacja: wiadomość kierowana do wyborców wskazująca dokładne miejsce lokalu wyborczego' + location_hints_help: 'Wskazówki dotyczące lokalizacji. Przykład: piętro budynku, w którym znajduje się lokal wyborczy.' + polling_station_managers_help: 'Kierownicy lokali wyborczych: oficerowie pełniący funkcję kierowników lokali wyborczej. Upewnij się, że te osoby zostały już powołane na oficerów wyborczych i że nie zostały jeszcze przydzielone do innego lokalu wyborczego' + select_president: Wybierz przewodniczącego lokalu wyborczego spośród oficerów wyborczych + index: + title: Lokale wyborcze + new: + create: Utwórz + title: Utwórz lokal wyborczy + titles: + votings: Głosowania + votings: + actions: + confirm_destroy: Jesteś pewny? + destroy: Usuń + new_voting: Nowa przestrzeń do głosowania + edit: + update: Aktualizuj + form: + select_a_voting_type: Wybierz typ głosowania + title: Tytuł + voting_type: + hybrid: Hybrydowe + in_person: Osobiste + online: Online + voting_type_label: Typ głosowania + new: + create: Utwórz + title: Nowe głosowanie + admin_log: + voting: + create: "%{user_name} utworzył(a) głosowanie %{resource_name}" + publish: "%{user_name} opublikował(a) głosowanie %{resource_name}" + unpublish: "%{user_name} cofnął/cofnęła publikację głosowania %{resource_name}" + census: + admin: + census: + create: + invalid: Wystąpił błąd przy przesyłaniu listy wyborców. Spróbuj ponownie później. + delete: + button: Usuń wszystkie dane listy wyborców + destroy: + error: Wystąpił błąd przy usuwaniu listy wyborców. Spróbuj ponownie później. + export_access_codes: + button: Eksportuj kody dostępu do głosowania + callout: Możesz teraz przejść do eksportu kodów dostępu. Można to zrobić tylko raz. Po uruchomieniu eksportu, otrzymasz e-mail z instrukcjami na adres %{email} + confirm: Możesz wyeksportować kody dostępu tylko raz. Upewnij się, że masz dostęp do konta e-mail %{email}. + freeze: + callout: Lista wyborców została zamknięta i nie może być już zmieniania. + generate_access_codes: + button: Utwórz kody dostępu do głosowania + callout: Teraz możesz utworzyć kody dostępu. Pamiętaj, że po utworzeniu tych kodów nie będziesz mógł już zmieniać listy wyborców. + confirm: Jeśli będziesz kontynuować, nie będziesz mógł już zmieniać listy wyborców. + info_message_all: "Wszystkie wiersze zostały pomyślnie zaimportowane z pliku %{file} (%{raw_count} z %{data_count})." + info_message_warn: Sprawdź, czy nie brakuje żadnych danych, ponieważ utworzono %{data_count} rekordów, a przesłany plik %{file} miał %{raw_count} wierszy. + launch_error: Wystąpił błąd przy uruchomieniu tworzenia kodów dostępu + launch_success: Uruchomiono tworzenie kodów. + new: + file_help: + explanation: 'Wytyczne dotyczące pliku:' + message_1: Dozwolone są tylko pliki CSV (.csv). + message_2: Separatorem kolumn musi być średnik (";"). + has_ballot_styles_message: Ustawiono rodzaje kart wyborczych. Upewnij się, że pole "%{ballot_style_code_header}" w CSV odpowiada żądanemu kodowi rodzaju karty wyborczej. + info_message: "Nie ma jeszcze listy wyborców. Użyj poniższego formularza, aby utworzyć ją i zaimportować dane z pliku CSV." + missing_ballot_styles_message: 'Nie skonfigurowano jeszcze rodzajów kart wyborczych w tym głosowaniu. Jeśli chcesz mieć pytania warunkowe (i..: przedstawiać wyborcy różne pytania w zależności od np. okręg/region zamieszkania), musisz skonfigurować Rodzaje kart wyborczych przed zaimportowaniem listy wyborców. Jeśli chcecie przedstawić wszystkim wyborcom te same pytania, możesz kontynuować procedurę importu listy wyborców.' + title: Utwórz listę wyborców + show: + heading: Lista wyborców w przestrzeni głosowania + upload_info: + csv_example_with_ballot_style: 'Przykład pliku z rodzajami kart wyborczych:' + csv_example_without_ballot_style: 'Przykład pliku bez rodzajów kart wyborczych:' + csv_header_after: Pozostaw ostatnie pole puste ("%{ballot_style_code_header}") jeśli nie potrzebujesz ustawiać rodzajów kart wyborczych/pytań warunkowych + csv_header_before: 'Plik listy wyborców musi być plikiem CSV z następującym nagłówkiem:' + document_types: + passport: Paszport + export_mailer: + access_codes_export: + click_button: 'Kliknij na następny link, aby pobrać dane kodów dostępu.
    Plik będzie dostępny do %{date}.
    Będziesz potrzebował 7-Zip (dla Windows), Keka (dla MacOS) lub PeaZip (dla Linux) aby je otworzyć. Hasło: %{password}' + download: Pobierz + subject: Eksport kodów dostępu do głosowania dla %{voting_title} jest dostępny + vote_flow: + already_voted_in_person: Ten użytkownik zagłosował już osobiście i nie ma prawa głosu. + content_blocks: + highlighted_votings: + name: Wyróżnione głosowania + landing_page: + polling_stations: + heading: Lokale wyborcze + no_polling_stations: Nie ma jeszcze żadnych punktów wyborczych. + monitoring_committee_members: + actions: + confirm_destroy: Jesteś pewny? + destroy: Usuń + new: Nowy członek + title: Działania + polling_officer_zone: + closures: + back_to_polling_stations: Wróć do lokali wyborczych + certify: + error: Wystąpił błąd podczas dołączania certyfikatu, spróbuj ponownie. + heading: Przeliczanie głosów - Prześlij certyfikat + info_text: Prześlij zdjęcie certyfikatu zamknięcia wyborów. + submit: Prześlij certyfikat + success: Certyfikat przesłany pomyślnie. + create: + error: Wystąpił błąd przy zamykaniu. Spróbuj ponownie później. + success: Zamknięto pomyślnie. + edit: + heading: Przeliczanie głosów - Przeliczanie odpowiedzi + modal_ballots_results_count_error: + close_modal: Zamknij + title: Suma kart wyborczych nie zgadza się + save_recount: Zapisz przeliczenie głosów + total_ballots: Suma oddanych głosów + total_blank_ballots: Suma pustych głosów + total_null_ballots: Suma głosów nieważnych + total_valid_ballots: Suma ważnych głosów + new: + election: 'Wybory:' + heading: Przeliczenie głosów + info_text: 'Podaj liczbę oddanych głosów ponownie przeliczonych w tym lokalu wyborczym:' + modal_ballots_count_error: + btn_validate_total: Sprawdź sumę przeliczonych głosów + message_for_monitoring_committee: Wiadomość dla Komisji Wyborczej + review_recount: Zweryfikuje przeliczenie głosów + text_area_placeholder: Wpisz wiadomość + title: Suma rekordów nie zgadza się + total_ballots: 'Suma kart wyborczych:' + total_people: 'Suma osób:' + polling_station: 'Lokal wyborczy:' + submit: Sprawdź całkowitą liczbę + total_ballots_count: Liczba kart wyborczych + show: + heading: Przeliczenie głosów + sign: + cancel: Anuluj + confirm: Ok, kontynuuj + error: Wystąpił błąd, spróbuj ponownie. + heading: Przeliczenie głosów - Podpisz zamknięcie + submit: Podpisz zamknięcie + success: Zamknięcie podpisane pomyślnie. + update: + error: Wystąpił błąd podczas aktualizacji wyników zamknięcia, spróbuj ponownie później. + success: Wyniki zamknięcia zostały zaktualizowane. + in_person_votes: + complete_voting: + available_answers: 'Możliwe odpowiedzi:' + complete_voting: Zakończ głosowanie + identify_another: Zidentyfikuj innego uczestnika + voted: Uczestnik zagłosował + create: + error: Głos nie został zarejestrowany. Spróbuj ponownie. + in_person_form: + census_not_present: Uczestnik nie jest umieszczony na liście wyborców. + census_not_present_description: Wyborca musi złożyć skargę na nieprawidłowości w liście wyborców, albo skontaktować się z pomocą techniczną. + date_of_birth: Data urodzenia + day: Dzień + day_placeholder: DD + document_number: Numer dokumentu + document_number_placeholder: Numer dokumentu tożsamości + month: Miesiąc + month_placeholder: MM + select: Wybierz typ dokumentu + title: 'Wybierz typ dokumentu i wprowadź numer dokumentu uczestnika:' + validate_document: Zweryfikuj dokument + year: Rok + year_placeholder: YYYY + new: + back: Wróć do lokali wyborczych + title: Zidentyfikuj i zweryfikuj uczestnika + show: + back: Wróć do lokali wyborczych + title: Oczekiwanie na rejestrację głosowania osobistego + update: + error: Wystąpił błąd podczas rejestracji głosu. Proszę spróbować ponownie. + success: + accepted: Głos został zarejestrowany pomyślnie. + rejected: Głos nie został zaakceptowany przez Tablicę danych wyborczych. Skontaktuj się z administratorem systemu. + verify_document: + census_present: Uczestnik jest na liście wyborców. + name: Imię + title: 'Sprawdź, czy następujące dane są poprawne:' + verify_document: Zweryfikuj dokument + menu: + polling_officer_zone: Strefa oficera wyborczego + polling_officers: + index: + polling_officer_role_description: Zostałeś powołany do pełnienia funkcji oficera lokalu wyborczego (przewodniczącego lub kierownika) w niektórych wyborach prowadzonych na tej platformie. + polling_station: + address: Adres + count_votes: Policz głosy + election: Wybory + identify_person: Identyfikacja osoby + name: Nazwa + no_polling_stations: Nie jesteś jeszcze przypisany do żadnego lokalu wyborczego. + role: Twoja rola + show_closure: Zobacz zamknięcie + title: Lokale wyborcze + voting: Głosowanie + polling_officers: + actions: + confirm_destroy: Jesteś pewny? + destroy: Usuń + title: Działania + roles: + manager: Kierownik + president: Przewodniczący + unassigned: Nie przypisano + polling_station_closure_recount: + nota_option: Puste / Żadne z powyższych + polling_officer_notes: 'Uwagi oficera wyborczego:' + polling_officer_notes_blank: Brak uwag + recount_summary: 'Podsumowanie przeliczania:' + signed: Podpisane + total_ballots: 'Suma oddanych głosów:' + total_blank_ballots: 'Suma pustych głosów:' + total_null_ballots: 'Suma głosów nieważnych:' + total_valid_ballots: 'Suma ważnych głosów:' + polling_stations: + actions: + confirm_destroy: Jesteś pewny? + destroy: Usuń + edit: Edytuj + title: Działania + votings: + access_code_modal: + email: Wyślij e-mailem do %{email} + no_email: Brak e-mail + no_sms: Brak dostępnego numeru telefonu + sms: Wyślij SMS do %{sms} + title: Uzyskaj kod dostępu + check_census: + check_status: Sprawdź status + description: Tutaj masz możliwość sprawdzenia danych umieszczonych na liście wyborców, aby wiedzieć, czy masz prawo uczestniczyć w tym głosowaniu. Powinieneś mieć już kod dostępu, ale jeśli go zgubiłeś, możesz ponownie o ten kod poprosić, gdy Twoje dane są poprawne. + form_title: 'Wypełnij formularz, aby sprawdzić Twoje dane na liście wyborców:' + invalid: Wystąpił błąd przy sprawdzaniu listy wyborców. + success: + access_link_with_sms: poprzez SMS lub e-mail. + title: Twoje dane z listy wyborców nie są prawidłowe! + title: Mogę głosować? + check_fields: + date_of_birth: Data urodzenia + day: Dzień + day_placeholder: DD + document_number: Numer dokumentu + document_number_placeholder: Numer ID + month: Miesiąc + month_placeholder: MM + postal_code: Kod pocztowy + postal_code_placeholder: Kod pocztowy + select: Wybierz typ dokumentu + year: Rok + year_placeholder: RRRR + count: + title: + one: "%{count} głosowanie" + few: "%{count} głosowania" + many: "%{count} głosowań" + other: "%{count} głosowania" + elections_log: + description: Dziennik zdarzeń wyborczych pokaże Ci wszystkie istotne informacje o każdym głosowaniu. Na przykład status ceremonii kluczy lub też, czy wyniki są już publikowane. Kliknij na wybory o którym chcesz uzyskać informacje z dziennika zdarzeń wyborczych. + title: Dziennik zdarzeń wyborczych + filters: + active: Aktywne + all: Wszystko + finished: Zakończone + search: Szukaj + upcoming: Nadchodzące + index: + no_votings: Brak głosowań spełniających kryteria wyszukiwania. + only_finished: Obecnie nie ma żadnych zaplanowanych głosowań, ale możesz zobaczyć wszystkie zakończone głosowania. + title: Głosowania + login: + access_code: Kod dostępu + access_code_placeholder: Kod dostępu + ask_for_a_new_one: Poproś o nowe. + form_title: 'Wypełnij poniższy formularz, aby uzyskać dostęp do głosowania:' + start_voting: Rozpocznij głosowanie + title: Zidentyfikuj mnie z danymi na liście wyborców + orders: + label: 'Sortuj głosowania według:' + random: Losowo + recent: Najnowsze + votings_m: + badge_name: + finished: Zakończone + ongoing: W toku + upcoming: Nadchodzące + unspecified: Nie określono + voting_type: + hybrid: Hybrydowe + in_person: Osobiste + online: Online + layouts: + decidim: + voting_navigation: + check_census: Mogę głosować? + election_log: Dziennik zdarzeń wyborczych + votings: + index: + promoted_votings: Wyróżnione głosowania + promoted_voting: + vote: Głosuj diff --git a/decidim-elections/config/locales/pt-BR.yml b/decidim-elections/config/locales/pt-BR.yml new file mode 100644 index 00000000..c6e584f0 --- /dev/null +++ b/decidim-elections/config/locales/pt-BR.yml @@ -0,0 +1,1225 @@ +pt-BR: + activemodel: + attributes: + answer: + description: Descrição + image: Imagem + proposals: Propostas relacionadas + title: Título + ballot_style: + code: Código + election: + description: Descrição + end_time: A votação termina às + start_time: Votação começa às + title: Título + monitoring_committee_member: + email: E-mail + name: Nome + polling_officer: + email: E-mail + name: Nome + polling_station: + address: Endereço + location: Localização + location_hints: Dicas de localização + polling_station_managers: Gerentes + polling_station_president_id: Presidente + title: Título + question: + max_selections: Número máximo de seleções + min_selections: Nenhuma das opções acima + title: Título + trustees_participatory_space: + user_id: Participante + voting: + banner_image: Imagem de banner + census_contact_information: Informação de contato do censo + description: Descrição + end_time: Votação termina + introductory_image: Imagem introdutória + promoted: Promovido + scope_id: Escopo + show_check_census: Mostrar página "checar censo" + start_time: A votação começa + title: Título + voting_type: Tipo de votação + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Precisa ser reanexado + ballot_result: + attributes: + base: + total_count_invalid: O número total de respostas não coincide com a falha válida/vazio/nula. + election: + attributes: + attachment: + needs_to_be_reattached: Precisa ser reanexado + question_result: + attributes: + base: + blank_count_invalid: O número total de respostas em branco não pode ser maior que o total de votos em branco. + trustee: + attributes: + name: + cant_be_changed: não pode ser alterado + public_key: + cant_be_changed: não pode ser alterado + voting: + attributes: + voting_type: + inclusion: "%{value} não é um tipo de votação válido" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: O oficial de votação já é presidente/gerente da estação de voto. + voting: + different_organization: A votação deve estar na mesma organização que o usuário. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: O oficial de votação deve estar na mesma votação que a estação de voto. + models: + decidim/elections/answer: + one: Responda + other: Respostas + decidim/elections/election: + one: Eleição + other: Eleições + decidim/elections/question: + one: Questão + other: Questões + decidim/voting: + one: Votação + other: Votações + decidim/votings/census/dataset: + one: Conjunto de dados + other: Conjuntos de dados + decidim/votings/census/datum: + one: Dado + other: Dados + decidim/votings/polling_officer: + one: Agente de votação + other: Agentes de votação + decidim/votings/polling_station: + one: Estação de voto + other: Estação de votos + decidim/votings/voting: + one: Votação + other: Votações + decidim: + admin: + filters: + officers_assigned_eq: + label: Oficiais + values: + assigned: Atribuído + unassigned: Não atribuído + role_eq: + label: Função + values: + manager: Gerente + president: Presidente + unassigned: Não Atribuido + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Pesquisa %{collection} por nome/e-mail/apelido ou estação de voto. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Pesquisar %{collection} por título, endereço ou nome do oficial/e-mail/apelido. + signed_eq: + label: Assinado + values: + 'false': Assinado + 'true': Não assinado + validated_eq: + label: Validado + values: + 'false': Não validado + 'true': Validado + voting_publications: + create: + error: Ocorreu um problema ao publicar esta votação. + success: Votação publicada com sucesso. + destroy: + error: Ocorreu um problema ao despublicar esta votação. + success: Votação despublicada com sucesso. + components: + elections: + actions: + vote: Voto + name: Eleições + settings: + global: + announcement: Anúncio + step: + announcement: Anúncio + elections: + actions: + confirm_destroy: Você tem certeza? + destroy: Destruir + edit: Editar + feedback: Feedback do eleitor + import: Importar propostas para respostas + manage_answers: Gerenciar respostas + manage_questions: Gerenciar perguntas + manage_steps: Gerenciar etapas + new_answer: Nova resposta + new_election: Nova eleição + new_question: Nova questão + new_trustee: Novo curador + preview: Pré-visualização + publish: Publicar + title: Ações + unpublish: Despublicar + admin: + answers: + create: + invalid: Houve um problema ao criar esta resposta. + success: Resposta criada com sucesso. + destroy: + invalid: Houve um problema ao excluir esta resposta. + success: Resposta excluída com sucesso. + edit: + title: Editar resposta + update: Atualizar resposta + index: + invalid_max_selections: Você precisa de mais %{missing_answers} resposta/s para combinar com a seleção máxima. + title: Respostas + new: + create: Criar resposta + title: Nova resposta + not_selected: Não selecionado + select: + disable: Resposta desmarcada + enable: Marcar resposta como selecionada + invalid: Houve um problema ao selecionar esta resposta. + success: Resposta selecionada com sucesso. + selected: Selecionado + unselect: + invalid: Houve um problema ao desmarcar esta resposta. + success: Resposta desselecionada com sucesso. + update: + invalid: Houve um problema ao atualizar esta resposta. + success: Resposta atualizada com sucesso. + elections: + create: + invalid: Houve um problema ao criar essa eleição. + success: Eleição criada com sucesso. + destroy: + invalid: Houve um problema ao excluir essa eleição. + success: Eleição excluída com sucesso. + edit: + title: Editar eleição + update: Atualizar eleição + form: + organization_time_zone: Verifique se o fuso horário da organização está correto nas configurações da organização. A configuração atual é %{time_zone} (%{time}). + index: + no_bulletin_board: Não há Quadro de Notas configurado, que é necessário para utilizar este módulo. Esta tarefa deve ser feita pelo Administrador do Sistema. + title: Eleições + new: + create: Criar uma nova eleição + title: Nova eleição + publish: + success: A eleição foi publicada com sucesso. + unpublish: + success: A eleição deixou de ser publicada com sucesso. + update: + invalid: Houve um problema ao atualizar essa eleição. + success: Eleição atualizada com sucesso. + exports: + elections: Eleições + feedback_form_answers: Respostas do formulário de feedback + mailers: + trustee_mailer: + body: + help_html: |- +

    Olá %{user_name},


    +

    Você foi adicionado para agir como um curador em alguma eleição que ocorrerão em %{organization}.


    +

    Uma nova sessão chamada "Zona de Curador" será ativada na sua conta. Para isso, você precisa realizar as tarefas necessárias. Por enquanto, por favor gere suas chaves de identificação.


    + subject: Você foi adicionado como um curador a %{resource_name} + trustee_zone: Leve-me à zona de fiduciários + menu: + trustees: Curadores + models: + answer: + name: Resposta + proposals_imports: + create: + invalid: Houve um problema ao importar as propostas para respostas. + success: "%{number} propostas importadas com sucesso para respostas." + new: + create: Importar propostas para respostas + no_components: Não há outros componentes de proposta neste espaço participativo para importar as propostas para respostas. + select_component: Por favor, selecione um componente + title: Importar propostas + questions: + create: + election_started: A eleição já começou. + invalid: Ocorreu um erro ao criar esta pergunta. + success: Pergunta criada com sucesso. + destroy: + invalid: Houve um problema ao excluir esta pergunta. + edit: + title: Editar pergunta + update: Atualizar pergunta + index: + title: Questões + new: + create: Criar questão + title: Nova questão + steps: + create_election: + errors: + census_codes_generated: Códigos de acesso do censo não foram gerados. + census_frozen: Códigos de acesso do censo não foram exportados. + census_uploaded: Não há censos carregados para esta eleição. + component_published: O componente de eleição não está publicado. + max_selections: As perguntas não têm um valor correto para a quantidade de respostas + minimum_answers: As perguntas devem ter pelo menos duas respostas. + minimum_questions: A eleição deve ter pelo menos uma questão. + published: A eleição não está publicada. + time_before: O horário de início é em menos que %{hours} antes do começo das eleições. + trustees_number: O espaço participativo deve ter pelo menos %{number} administradores com chave pública. + invalid: Houve um problema ao configurar essa eleição + no_trustees: Não há Agentes fiduciários configurados para este espaço participativo + not_used_trustee: "(não usado)" + public_key: + 'false': não tem uma chave pública + 'true': tem uma chave pública + requirements: + census_codes_generated: Códigos de acesso do censo são gerados. + census_frozen: Os códigos de acesso do censo são exportados e o censo está congelado. + census_uploaded: O Censo foi carregado. + component_published: O componente de eleição está publicado. + max_selections: Todas as questões têm um valor correto para o máximo de respostas. + minimum_answers: Cada questão tem pelo menos 2 respostas. + minimum_questions: A eleição tem pelo menos 1 pergunta. + published: A eleição está publicada. + time_before: A instalação está sendo feita pelo menos %{hours} horas antes do início da eleição. + trustees_number: O espaço participativo tem pelo menos %{number} agentes fiduciários com chave pública. + submit: Configurar eleição + title: Configurar eleição + trustees: Agentes fiduciários da Eleição + created: + submit: Inicie a cerimônia de chaves + title: Eleição criada + trustees: Agentes fiduciários + key_ceremony: + title: Cerimônia de chave + key_ceremony_ended: + errors: + time_before: A eleição está pronta para começar. Você tem que esperar até %{hours} horas antes da hora de início (%{start_time}) para iniciar o período de votação. + requirements: + time_before: A eleição começará em breve. Você pode iniciar o período de votação manualmente ou ele será iniciado automaticamente antes da hora inicial, às %{start_time}. + submit: Iniciar período de votação + title: Preparado para começar + processing: Processando... + results_published: + answer: Resposta + not_selected: Não selecionado + question: Questão + result: Resultado + selected: Selecionado + submit: Enviar + title: Resultados publicados + tally_ended: + answer: Resposta + not_selected: Não selecionado + question: Questão + result: Resultado + selected: Selecionado + submit: Publicar resultados + title: Resultados calculados + tally_started: + continue: Continuar + title: Processo de registro + vote: + errors: + time_after: A eleição ainda está em curso. Você tem que esperar até o fim do período de votação (%{end_time}). + requirements: + time_after: A eleição terminou. Você pode terminar o período de votação manualmente ou terminará automaticamente em alguns minutos. + submit: Fim período de votação + title: Período de votação + vote_ended: + submit: Iniciar a apuração + text: A votação terminou. Você pode iniciar a apuração agora. + title: O período de votação terminou + vote_stats: + no_vote_statistics_yet: Nenhuma estatística de voto ainda + title: Estatísticas de Voto + voters: Votantes + votes: Votos + trustees_participatory_spaces: + actions: + disable: Desabilitado + enable: Considere + create: + exists: Curador existente para este espaço participativo. + invalid: Houve um problema ao criar um curador. + success: Curador criado com sucesso. + delete: + invalid: Houve um problema ao remover o curador. + success: Curador removido com sucesso. + form: + select_user: Selecionar usuário + index: + title: Curadores + new: + create: Criar curador + title: Novo curador + update: + invalid: Houve um problema ao remover o curador %{trustee}. + success: Curador %{trustee} atualizado com sucesso. + admin_log: + election: + report_missing_trustee: "%{user_name} relatou %{trustee_name} como um fiduciário ausente durante o concurso para a eleição %{resource_name} de %{space_name} no Bulletin Board" + connection: + failed: + modal: + communication_lost: Infelizmente, parece que a comunicação com o servidor de votação (Bulletin Board) foi perdida.
    Pode ser que a conexão com a Internet esteja interrompida ou que o servidor de destino esteja muito ocupado.
    Você pode tentar novamente mais tarde ou entrar em contato com o suporte se esse problema persistir. + generic_error: Infelizmente, ocorreu um erro desconhecido. É provável que seu navegador não seja suportado ou que você esteja usando o modo "incognito" ou "privado", que não é suportado. + title: Algo deu errado + election_m: + badge_name: + finished: Finalizado + ongoing: Ativo + upcoming: Próximos + end_date: Finaliza + footer: + remaining_time: + one: "%{count} hora %{minutes} minutos restantes para votar." + other: "%{count} horas %{minutes} minutos restantes para votar." + view: Visualizar + vote: Votar + label: + date: Datas + questions: Questões %{count} + start_date: Começar + unspecified: Não especificado + elections: + count: + elections_count: + one: "%{count} eleição" + other: "%{count} eleições" + election_log: + chained_hash: A Hash encadeada desta mensagem + complete: Completo + creation_description: + complete: A eleição foi criada e foi criada com sucesso no Quadro de Notas. + not_created: A eleição ainda não foi criada. + creation_title: Eleição criada + description: Este é o log de eleições onde você pode verificar o estado de cada etapa; por exemplo, quando a eleição foi criada, se o processo apuração está concluído e quando as eleições foram encerradas. + download: Download + key_ceremony_description: + complete: A cerimônia de chaves está concluída. Cada curador tem chaves válidas e realizou o download de backup das chaves. + not_started: A cerimônia de chaves ainda não começou. + started: A cerimônia de chaves começou, mas ainda não está concluída. + key_ceremony_title: Cerimônia de chaves + not_available: Ainda não está disponível + not_created: Não criado + not_ready: Não está pronto + not_started: Não Iniciado + published: Publicado + results_description: + not_published: Os resultados ainda não foram publicados. + published: Os resultados estão publicados. + results_title: Resultados + started: Iniciado + tally_description: + finished: O processo de apuração está terminado. + not_started: O processo de apuração ainda não foi iniciado. + started: O processo de apuração foi iniciado. + tally_title: Processo de apuração + title: Log de Eleições + verifiable_results: + checksum: 'Verificação de arquivo SHA256:' + description: + ready: 'Aqui, você tem a opção de verificar as eleições. Primeiro, você precisa baixar o arquivo e ter certeza de que ele não foi corrompido. Para fazer isso, execute o seguinte comando e verifique se a saída corresponde ao checksum:' + how_to_verify: 'Uma vez que você baixou o arquivo e tenha certeza que tudo está correto, você pode prosseguir para executar o verificador universal. Clonar este repositório e, na pasta raiz, execute o seguinte comando:' + title: Verificar resultados da eleição + verifiable_file: 'Arquivo de eleição verificável:' + verify: Verificar eleição + vote_description: + finished: O processo de votação está concluído. + not_started: O processo de votação ainda não foi iniciado. + started: O processo de votação já começou. + vote_title: Processo de votação + filters: + active: Ativo + all: Todos + finished: Finalizado + upcoming: Próximos + preview: + available_answers: 'Respostas disponíveis:' + description: 'Estas são as questões que você encontrará no processo de votação:' + title: Perguntas eleitorais + results: + description: 'Estes são os resultados das votações, para cada pergunta:' + percentage: "%{count}%" + selected: Selecionado + title: Resultados da eleição + votes: + one: "%{count} voto" + other: "%{count} votos" + show: + action_button: + change_vote: Mude seu voto + vote: Começar a votar + vote_again: Votar novamente + callout: + already_voted: Você já votou nesta eleição. Você pode alterar seu voto ou verificá-lo. + pending_vote: Seu voto está sendo enviado ao servidor. + vote_rejected: Não foi possível verificar o seu voto. Por favor, vote novamente. + election_log: Log de eleições + preview: Pré-visualização + verify: + already_voted: Já votou? + verify_here: Verifique seu voto aqui. + will_verify: Você poderá verificar seu voto quando as eleições forem iniciadas. + voting_period_status: + finished: A votação começou em %{start_time} e terminou em %{end_time} + ongoing: 'Votação ativa até: %{end_time}' + upcoming: A votação começa em %{start_time} + feedback: + answer: + invalid: Houve um problema ao enviar seu feedback. + success: Feedback enviado com sucesso. + models: + answer: + fields: + proposals: Proposta + selected: Selecionado + title: Título + votes: Votos + election: + fields: + bb_status: Satus do Bulletin Board + end_time: Termina às + start_time: Começa às + title: Título + verifiable_results_file_hash: Verificação de arquivo SHA256 + verifiable_results_file_url: Arquivo de eleição verificável + question: + fields: + answers: Respostas + max_selections: Máx. seleções + title: Título + trustees_participatory_space: + fields: + considered: considerado + email: E-mail + inactive: inativo + name: Nome + notification: Notificação enviada em + public_key: Chave Pública + status: Status + orders: + label: Ordenar eleições por + older: Mais antigo + recent: Recente + trustee_zone: + elections: + backup_modal: + description: Esta eleição está a ser criada no Quadro de Notas. É muito importante que cada curador que participe crie uma cópia de segurança dessas chaves e as armazene em um lugar seguro. Depois disso, o processo prossegue. + download_election_keys: Baixar chaves + title: Backup das chaves eleitorais para %{election} + key_ceremony_steps: + back: Voltar + description: Esta eleição está sendo criada no Quadro de Notas. Para completar este processo, é necessária a sua participação como Curador. + keys: + create_election: Geração de chaves + key_ceremony: + joint_election_key: Geração de chave comum + step_1: Publicação de chaves + list: + status: Status + task: Tarefa + process_warning: Depois que o processo for iniciado, você não deverá sair desta página até que o processo termine. Levará vários minutos, pois todos os curadores deverão estar conectados para completá-lo. + start: Iniciar + status: + completed: Concluído + pending: Pendente + processing: Processando + title: Criar chaves eleitorais para %{election} + restore_modal: + description: O Quadro de Notas tem informações suas, na sua qualidade de membro da Presidência, sobre esta eleição. Para continuar o processo, primeiro carregue o arquivo de backup gerado durante a sessão anterior. + title: Restaurar chaves eleitorais para %{election} + upload_election_keys: Upload das chaves eleitorais + tally_started_steps: + description: Os resultados para esta eleição estão sendo calculados no Quadro de Notas. Para completar este processo, é necessário a sua participação como um curador. + keys: + end_tally: Apuração encerrada + tally: + cast: Transmissão de apuração + share: Compartilhar apuração + process_warning: Depois que o processo for iniciado, você não deverá sair desta página até que o processo termine. Levará vários minutos, pois todos os fiduciários deverão estar conectados para completá-lo. + status: + completed: Concluído + pending: Pendente + processing: Processando + title: Apuração para %{election} + menu: + trustee_zone: Zona dos curadores + no_bulletin_board: + body: Um Quadro de Notas é necessário para esta seção. Entre em contato com o Administrador para obter mais detalhes. + title: Desculpe, o Quadro de Notas ainda não está configurado. + trustees: + show: + elections: + list: + action_required: + 'false': 'Não' + name: Ação necessária? + 'true': Executar ação + bb_status: Status + election: Eleição + voting_period: Período de votação + title: Eleições + identification_keys: + cancel: Cancelar + generate: Gerar chaves de identificação + generate_error: Ocorreu um erro ao gerar as chaves de identificação. + generate_legend: Você precisa gerar um par de chaves de identificação para participar das eleições como um Trustee. + generate_legend_1: Depois de pressionar o botão, você deve baixar o arquivo com as chaves de identificação geradas. + generate_legend_2: Copie o arquivo baixado para um dispositivo USB limpo + generate_legend_4: Faça outra cópia do arquivo em um dispositivo externo diferente e armazene-o em um lugar muito seguro. + submit: Enviar + submit_legend: Depois de seguir todos os passos explicados acima, complete o processo enviando a chave de identificação pública para o servidor. + submit_title: Enviar a chave de identificação pública + title: Chaves de identificação de curadores + upload: Envie suas chaves de identificação + not_supported_browser_title: Atualize o navegador para agir como um curador + update: + success: Sua chave pública de identificação foi armazenada com sucesso. + votes: + ballot_decision: + audit: ( Auditar cédula ) + back: Iniciar o processo de votação novamente + ballot_hash: 'Seu identificador de voto é:' + cast: Envie sua cédula para depositar seu voto + header: 'A cédula está criptografada: transmita-a ou auditá-la' + casting: + header: Transmitindo a cédula... + text: A sua cédula está sendo enviada para a urna. + confirm: + answer_number: responder %{number} + confirm: Confirmar + edit: editar + header: Confirme seu voto + intro: Aqui está um resumo do voto que você está prestes a transmitir.
    Por favor, confirme seu voto ou edite suas respostas. + nota_option: Em Branco + confirmed: + back: Voltar para as eleições + experience: Como foi sua experiência? + feedback: Deixe sua opinião + header: Voto confirmado + lead: Seu voto foi transmitido! + text: 'Você pode verificar se o seu voto foi adicionado com sucesso à urna com o seguinte identificador: %{e_vote_poll_id}' + verify_link: Para verificá-lo, copie o identificador e cole-o na página de verificação do voto + create: + error: Houve um problema ao submeter a votação. Por favor, tente novamente. + encrypting: + header: Criptografando o voto... + text: O seu voto está sendo criptografado para garantir o segredo do seu voto. + failed: + header: Votação falhou + lead: Seu voto não foi transmitido! + text: Algo deu errado, por favor, tente novamente. + try_again: Tente novamente + header: + ballot_decision: Transmita ou audite seu voto + confirm: Confirme seu voto + messages: + invalid_token: A sua sessão na cabine de votação não é válida. Tente votar novamente. + not_allowed: Não está autorizado a votar nesta eleição neste momento. + modal: + close: Fechado + proposal_header: 'Propostas:' + new: + answer_choices: Você pode selecionar até %{choices} respostas + more_information: Mais informações + nota_option: Branco/ Nenhuma das alternativas acima + preview_alert: Esta é uma prévia da cabina de votação. + question_steps: Questão %{current_step} de %{total_steps} + selections: "%{selected} de %{max_selections}
    seleções" + onboarding_modal: + create_account: Criar conta + description: Você quer criar uma nova conta na plataforma? Você poderá participar dos processos e ser parte ativa da organização. + no_account: Não, obrigado. + title: Novo na plataforma? + update: + error: Houve um problema ao atualizar o status do voto. Por favor, vote novamente. + verify: + content: + heading: Verifique seu voto + info: Este verificador verifica se seu voto, identificado com uma string de texto criptografada, foi transmitido corretamente e está dentro das urnas. + error: + header: Voto não encontrado! + info: O código de voto não foi encontrado na urna %{link}. Tente novamente. + form: + back: Voltar à plataforma + submit: Check + vote_identifier: 'Código de identificador:' + vote_identifier_help: Este é o identificador que lhe foi dado depois de você transmitir o seu voto (não o código para digitar a cabine de voto). + header: + title: Verifique seu voto + success: + header: Voto localizado! + voting_step: + back: Voltar + continue: Próxima + events: + elections: + election_published: + email_intro: 'A eleição %{resource_title} agora está ativa para %{participatory_space_title}. Você pode vê-lo a partir desta página:' + email_outro: Você recebeu esta notificação porque está seguindo %{participatory_space_title}. Você pode parar de receber notificações através o link anterior. + email_subject: A eleição %{resource_title} agora está ativa para %{participatory_space_title}. + notification_title: A eleição %{resource_title} agora está ativa para %{participatory_space_title}. + trustees: + new_election: + email_intro: Você foi adicionado como curador para a eleição %{resource_title}. + new_trustee: + email_intro: Um administrador adicionou você como curador em %{resource_name}. Você deve criar sua chave pública na sua zona de curador + email_subject: Você é um curador por %{resource_name}. + votes: + accepted_votes: + email_intro: 'Seu voto foi aceito! Usando o seu token de voto: %{encrypted_vote_hash}, você pode verificar seu voto aqui.' + email_subject: Seu voto para %{resource_name} foi aceito. + notification_title: 'Seu voto foi aceito. Verifique o seu voto aqui usando o seu token de voto: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Você foi designado como %{role} da Estação de votos %{polling_station_name} em %{resource_title}. Você pode gerenciar a Estação de Votos na Zona de Agente de Votação dedicada. + email_outro: Você recebeu esta notificação porque foi designado como %{role} de %{polling_station_name}. + email_subject: Você é %{role} da Estação de voto %{polling_station_name}. + notification_title: Você está em %{role} da Estação de Voto %{polling_station_name} em %{resource_title}. + send_access_code: + instruction: 'Aqui está o seu código de acesso que você pediu: %{access_code}. Com isso você será capaz de participar em %{voting}.' + subject: Seu Código de Acesso para participar de %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Uma votação é um espaço que permite que você faça uma pergunta clara para todas as pessoas que formam uma organização, faça uma chamada para participar na votação, incitar e ordenar o debate a favor ou contra uma resposta. Quando chegar a data da consulta, podem votar e publicar os resultados das votações.

    Exemplos: As consultas podem ser sobre quase qualquer aspecto que afete uma organização: alguns exemplos estão mudando o nome ou o logotipo da organização oferecendo várias alternativas, decidir Sim ou Não para fazer parte de uma organização maior, validar ou rejeitar um novo plano estratégico ou o resultado de um grupo de trabalho. ou definir se as posições devem permanecer um máximo de 1, 2 ou 3 mandatos.

    \n" + page: "

    Uma votação é um espaço que permite que você faça uma pergunta clara para todas as pessoas que formam uma organização, faça uma chamada para participar na votação, incitar e ordenar o debate a favor ou contra uma resposta. Quando chegar a data da consulta, podem votar e publicar os resultados das votações.

    Exemplos: As consultas podem ser sobre quase qualquer aspecto que afete uma organização: alguns exemplos estão mudando o nome ou o logotipo da organização oferecendo várias alternativas, decidir Sim ou Não para fazer parte de uma organização maior, validar ou rejeitar um novo plano estratégico ou o resultado de um grupo de trabalho. ou definir se as posições devem permanecer um máximo de 1, 2 ou 3 mandatos.

    \n" + title: O que são votações? + menu: + votings: Votações + statistics: + elections_count: Eleições + votings_count: Votações + votings: + admin: + ballot_styles: + edit: + title: Editar estilo da cédula + update: Atualizar + form: + election: Eleição + questions: Perguntas para este estilo de cédula + index: + actions: + confirm_destroy: Você tem certeza? + destroy: Excluir + edit: Editar + title: Ações + associated_census_data: Entradas associadas de censo + title: Estilo de cédula + new: + create: Criar + title: Criar estilo de cédula + content_blocks: + attachments_and_folders: + name: Anexos e pastas de votação + header: + name: Cabeçalho de votação + highlighted_votings: + max_results: Quantidade máxima de elementos para mostrar + html_block_1: + name: Bloco de votação html 1 + html_block_2: + name: Bloco de votação html 2 + html_block_3: + name: Bloco de votação html 3 + metrics: + name: Métricas de votação + polling_stations: + name: Estações de voto + related_elections: + name: Votações electorais + stats: + name: Estatísticas de votação + timeline: + name: Linha do tempo da votação + index: + published: Publicado + menu: + votings: Votações + votings_submenu: + attachment_collections: Pastas + attachment_files: Arquivos + attachments: Anexos + ballot_styles: Estilo da cédula + census: Censo + components: Componentes + info: Sobre esta votação + landing_page: Página inicial + monitoring_committee: Comitê de Monitoramento + monitoring_committee_election_results: Validar resultados + monitoring_committee_members: Membros + monitoring_committee_polling_station_closures: Validar certificados + monitoring_committee_verify_elections: Verificar Eleições + polling_officers: Agentes de votação + polling_stations: Estação de votos + models: + ballot_style: + fields: + code: Código + monitoring_committee_member: + fields: + email: E-mail + name: Nome + polling_officer: + fields: + email: E-mail + name: Nome + polling_station: Estação de votação (regra) + polling_station: + fields: + address: Endereço + polling_station_managers: Gerentes + polling_station_president: Presidente + title: Título + voting: + fields: + created_at: Criado em + published: Publicado + title: Título + monitoring_committee_election_results: + actions: + title: Ações + view: Visualizar + index: + title: Escolha uma eleição para a qual deseja ver os resultados + results: + bulletin_board: Quadro de Notas + election_totals: Totais das eleições + polling_stations: Estações de votação + result_types: + blank_answers: Respostas em branco + blank_ballots: Cédulas em branco + null_ballots: Cédulas nulas + total_ballots: Total de cédulas + valid_ballots: Cédulas válidas + selected: Selecionado + title: Resultados para a eleição %{election_title} + totals: Totais + show: + change_election: Alterar eleição + publish_results: Publicar resultados + publishing: Publicando resultados... + update: + rejected: A publicação dos resultados foi rejeitada pelo quadro de avisos. Tente novamente ou entre em contato com o administrador do sistema. + monitoring_committee_members: + form: + existing_user: Usuário existente + non_user: Convidar novo usuário + select_user: Pesquisar por nome, e-mail ou apelido + user_type: Tipo de usuário + index: + title: Comitê de Monitoramento + new: + create: Criar + title: Criar membro do comitê de monitoramento + monitoring_committee_polling_station_closures: + actions: + title: Ações + validate: Validar + view: Visualizar + closures: + change_election: Alterar eleição + signed: Assinado? + title: Estações de voto para a eleição %{election_title} + validated: Validado? + edit: + change_polling_station: Voltar para estações de votação + monitoring_committee_notes: Observações + monitoring_committee_notes_placeholder: Relate qualquer incidente aqui + title: Resultados para a eleição %{election_title} na estação de voto %{polling_station_title} + elections: + title: Escolha uma eleição que deseja validar + show: + change_polling_station: Voltar para estações de votação + monitoring_committee_notes: Observações do Comitê de Monitoramento + monitoring_committee_verify_elections: + index: + download: Download + how_to_checksum: 'Para certificar-se de que o arquivo que você baixou não foi corrompido ou adulterado durante o processo de download, execute o seguinte comando em seu console e verifique se a saída corresponde ao checksum reportado acima:' + how_to_download: Para verificar a eleição, baixe o arquivo verificável da tabela acima. + how_to_run_verifier: 'Uma vez que você baixou o arquivo e tenha certeza que tudo está correto, você pode prosseguir para executar o verificador universal. Clonar este repositório e, na pasta raiz, execute o seguinte comando:' + how_to_title: Como verificar a validade de uma eleição + not_available: Não disponível ainda + title: Eleições + polling_officers: + form: + existing_user: Usuário existente + non_user: Convidar novo usuário + select_user: Pesquisar por nome, e-mail ou apelido + user_type: Tipo de usuário + index: + role_manager: gerente + role_president: presidente + title: Agente de Enquete + new: + create: Criar + title: Criar agente de enquete + polling_officers_picker: + choose_polling_officers: Escolher agentes de enquete + polling_stations: + edit: + title: Editar estação de voto + update: Atualizar estação de voto + form: + address_help: 'Endereço: usado pelo Geocodificador para encontrar a localização' + location_help: 'Local: mensagem direcionada aos eleitores implicando o local exato da estação de voto' + location_hints_help: 'Dicas de localização: informações adicionais. Exemplo: o piso do edifício onde a estação de voto está localizada.' + polling_station_managers_help: 'Gerentes das estações de voto: os oficiais que atuarão como gestores das estações de voto. Assegure-se de que os oficiais já foram criados nos Agentes de Enquete e de que eles ainda não estão atribuídos a outra estação de voto' + select_president: Selecione um Agente de Enquete como presidente da estação de voto + index: + title: Estações de votação + new: + create: Criar + title: Criar estação de votação + titles: + votings: Votações + votings: + actions: + confirm_destroy: Você tem certeza? + destroy: Destruir + new_voting: Novo espaço de votação + edit: + update: Atualizar + form: + banner_image: Imagem de banner + census_contact_information: Informação de contato do censo + census_contact_information_help: Esta informação de contato é para um participante que deseja relatar problemas com o censo. Pode ser um endereço de e-mail, um formulário de contato em outro site, uma pesquisa para visitantes, etc. + introductory_image: Imagem introdutória + promoted: Promovido + select_a_voting_type: Por favor, selecione um tipo de votação + show_check_census_help: Mostrar ou não o link "Eu voto?" no menu de votações públicas. + slug_help_html: 'Os slugs de URL são usados para gerar os URLs que apontam para essa votação. Aceita apenas letras, números e traços e deve começar com uma letra. Exemplo: %{url}' + title: Título + voting_type: + hybrid: Híbrido + in_person: Pessoalmente + online: On-line + voting_type_label: Tipo de votação + new: + create: Criar + title: Nova votação + admin_log: + voting: + create: "%{user_name} criou a votação %{resource_name}" + publish: "%{user_name} publicou a votação %{resource_name}" + unpublish: "%{user_name} despublicizou a votação %{resource_name}" + census: + admin: + census: + create: + invalid: Ocorreu um erro ao atualizar o censo, por favor tente novamente mais tarde. + creating_data: + info_message: "Aguarde, processadas %{processed_count} de %{raw_count} linhas do arquivo %{file} (isso pode levar alguns minutos)." + delete: + button: Excluir todos os dados do censo + confirm: A exclusão de todos os dados do censo não pode ser desfeita. Tem certeza que deseja continuar? + destroy: + error: Ocorreu um erro ao deletando o censo, por favor tente novamente mais tarde. + export_access_codes: + button: Exportar códigos de acesso a votos + callout: Agora você pode exportar os códigos de acesso. Isso só pode ser feito uma vez. Ao iniciar a exportação, você receberá um e-mail com as instruções para %{email} + confirm: Você só pode exportar os códigos de acesso uma vez. Certifique-se de que você tem acesso à conta de e-mail %{email}. + file_not_exist: Este arquivo não existe. + exporting_access_codes: + info_message: "Por favor, aguarde, a exportação está sendo preparada, você o receberá em breve para %{email} (isso pode levar alguns minutos)." + freeze: + callout: O censo está congelado e não pode ser modificado. + help_html: | + Os dados do censo foram carregados, os códigos gerados e exportados com sucesso.
    + Você está pronto para iniciar a eleição.
    + Use o CSV exportado com códigos individuais para distribuí-lo ao longo do seu censo por meios próprios ou ative a guia "Posso votar" para permitir que qualquer pessoa recupere este código utilizando os seus próprios dados do censo. + generate_access_codes: + button: Gerar códigos de acesso a votação + callout: Agora você pode gerar os códigos de acesso. Lembre-se de que após gerar os códigos de acesso, você não será capaz de modificar mais o censo. + confirm: Se você continuar, você não poderá modificar o censo. + info_message_all: "Todas as linhas importadas com sucesso de %{file} arquivo (%{raw_count} de %{data_count})." + info_message_warn: Por favor, verifique se nenhum dado está faltando, porque %{data_count} registros foram criados e o arquivo enviado %{file} possui %{raw_count} linhas. + launch_error: Problema ao lançar a geração de códigos de acesso + launch_success: Geração de códigos iniciada. + start_over: Por favor, apague o censo atual e comece novamente com um arquivo CSV adequado com linhas válidas. + generating_access_codes: + info_message: "Por favor, aguarde, os códigos de acesso para votação estão sendo gerados (isso pode levar alguns minutos)..." + new: + file_help: + explanation: 'Orientação para o arquivo:' + message_1: Somente arquivos CSV (.csv) são permitidos. + message_2: O separador entre colunas deve ser um ponto e vírgula (";"). + has_ballot_styles_message: Você configurou estilos de cédulas. Por favor, certifique-se de que o campo "%{ballot_style_code_header}" no CSV corresponde ao código de estilo de cédula desejada. + info_message: "Ainda não há censo. Por favor, use o formulário abaixo para criá-lo importando um arquivo CSV." + missing_ballot_styles_message: 'Não existe estilo de cédula para esta votação ainda.. Se você deseja ter quetões condicionais (ex.: apresentar ao votante diferentes questões dependendo de, por exemplo, o distrito/região da reidência), e precisa ser adicionado no Estilo de Cédula antes importando o censo. Se você gostaria de apresentar a todos os votantes as mesmas questões, você pode prosseguir com o processo de importação do censo.' + submit: Enviar CSV + title: Criar o censo + show: + heading: Censo do espaço de votação + upload_info: + csv_example_with_ballot_style: 'Um exemplo do arquivo com estilos de voto:' + csv_example_without_ballot_style: 'Um exemplo do arquivo sem estilos de voto:' + csv_header_after: Não incluir o último campo ("%{ballot_style_code_header}") se você não precisa de estilos de voto/questões condicionais + csv_header_before: 'O arquivo do censo deve ser um arquivo CSV com o seguinte cabeçalho:' + document_types: + passport: Passaporte + export_mailer: + access_codes_export: + click_button: 'Clique no próximo link para baixar os dados dos códigos de acesso.
    O arquivo estará disponível até %{date}.
    Você vai precisar de 7-Zip (para Windows), Keka (para MacOS) ou PeaZip (para Linux) para abri-lo. Senha: %{password}' + download: Download + subject: A exportação dos códigos de acesso à votação de %{voting_title} está disponível + vote_flow: + already_voted_in_person: Este usuário já votou e não tem direito a votar novamente. + content_blocks: + highlighted_votings: + name: Destaque da votação + landing_page: + polling_stations: + heading: Estações de votação + no_polling_stations: Ainda não há nenhuma Estação de voto. + monitoring_committee_members: + actions: + confirm_destroy: Você tem certeza? + destroy: Excluir + new: Novo membro + title: Ações + pages: + home: + highlighted_votings: + active_spaces: Votações Ativas + see_all_spaces: Ver todas as votações + polling_officer_zone: + closures: + back_to_polling_stations: Voltar para estações de votação + certify: + error: Ocorreu um erro ao anexar o certificado, por favor tente novamente. + heading: Recontagem de voto - Upload de certificado + info_text: Por favor envie uma foto do Certificado de Encerramento Eleitoral. + submit: Carregar o certificado + success: Upload do certificado efetuado com sucesso. + create: + error: Ocorreu um erro ao criar o encerramento, por favor tente novamente mais tarde. + success: Encerramento criado com sucesso. + edit: + heading: Recontagem de votos - Recontagem de respostas + modal_ballots_results_count_error: + close_modal: Fechar + title: Total de cédulas não somados + save_recount: Salvar recontagem + total_ballots: Total de cédulas + total_blank_ballots: Total de cédulas em branco + total_null_ballots: Total de cédulas nulas + total_valid_ballots: Total de votos válidos + new: + election: 'Eleição:' + heading: Recontagem de votos + info_text: 'Por favor, introduza o número total de votos (envelopes) recontados nesta Estação de Enquete:' + modal_ballots_count_error: + btn_validate_total: Validar a recontagem total de votos + info_explanation_text: 'Por favor, reveja o número total de votos. Se o número total estiver incorreto, você deve fornecer uma explicação para o Comité de Acompanhamento:' + message_for_monitoring_committee: Mensagem para o Comitê de Monitoramento + review_recount: Revisar a recontagem + text_area_placeholder: Por favor, digite sua mensagem + title: Total de cédulas não somados + total_ballots: 'Total de cédulas:' + total_people: 'Total de pessoas:' + polling_station: 'Estação de voto:' + submit: Verificar número total + total_ballots_count: Número de cédulas + show: + heading: Recontagem de votos + sign: + cancel: Cancelar + confirm: Ok, continuar + error: Ocorreu um erro. Por favor, tente novamente. + heading: Recontagem de votos - Assinatura de encerramento + submit: Assine o fechamento + success: Encerramento assinado com sucesso. + update: + error: Ocorreu um erro ao atualizar o encerramento dos resultados, por favor tente novamente mais tarde. + success: Fechamento de resultados atualizado com sucesso. + in_person_votes: + complete_voting: + available_answers: 'Respostas disponíveis:' + census_verified: Este participante ainda não votou em pessoa. + census_verified_with_online_vote: Este usuário já votou online. Se eles votarem em pessoa, os votos anteriores serão invalidados e esta será a votação definitiva. + complete_voting: Votação concluída + identify_another: Identificar outro usuário + questions_title_voted: 'Este participante já votou online e tem direito a votar nas seguintes perguntas:' + voted: O usuário votou + create: + error: A votação não foi registrada. Por favor, tente novamente. + in_person_form: + census_not_present: Este usuário não está listado no censo. + census_not_present_description: Ela deve ir ao escritório de reclamações do censo ou entrar em contato com o suporte. + date_of_birth: Data de nascimento + day: Dia + day_placeholder: DD + document_number: Número do documento + document_number_placeholder: Carteira de identidade (RG) + month: Mês + month_placeholder: MM + select: Selecione o tipo do documento + title: 'Selecione o tipo de documento e digite o número do documento do participante:' + validate_document: Validar documento + year: Ano + year_placeholder: YYYY + new: + back: Voltar para estações de votação + title: Identificar e verificar um usuário + show: + back: Voltar para estações de votação + title: Aguardando que o voto seja registrado + update: + error: Ocorreu um erro ao registrar a votação. Por favor, tente novamente. + success: + accepted: O voto foi registada com sucesso. + rejected: O voto não foi aceito pelo Boletim de avisos. Por favor, entre em contato com o administrador do sistema. + verify_document: + census_present: Este usuário está listado no censo. + name: Nome + title: 'Verifique se os seguintes dados estão corretos:' + verify_document: Verificar documento + menu: + polling_officer_zone: Zona de Agente de Enquete + polling_officers: + index: + polling_officer_role_description: Você foi foi designado como um Agente da Estação de Apuração (Presidente ou Gerente) em alguma das eleições realizadas nesta plataforma. + polling_station: + address: Endereço + count_votes: Contagem de votos + election: Eleição + identify_person: Identificar uma pessoa + name: Nome + no_polling_stations: Você ainda não está atribuído a nenhuma Estação de Apuração no momento. + role: Seu papel + show_closure: Ver fechamento + title: Estação de votos + voting: Votação + polling_officers: + actions: + confirm_destroy: Você tem certeza? + destroy: Excluir + title: Ações + roles: + manager: Gerente + president: Presidente + unassigned: Não Atribuido + polling_station_closure_recount: + nota_option: Branco/ Nenhuma das alternativas acima + polling_officer_notes: 'Notas do Agente de Enquete:' + polling_officer_notes_blank: Não há tarefas + recount_summary: 'Sumário de recontagem:' + signed: Assinado + total_ballots: 'Total de cédulas:' + total_blank_ballots: 'Total de cédulas em branco:' + total_null_ballots: 'Total de cédulas nulas:' + total_valid_ballots: 'Total de cédulas válidas:' + polling_stations: + actions: + confirm_destroy: Você tem certeza? + destroy: Excluir + edit: Editar + title: Ações + votings: + access_code_modal: + email: Enviar por e-mail para %{email} + no_email: Nenhum e-mail disponível + no_sms: Nenhum número de telefone disponível + sms: Enviar por SMS para %{sms} + title: Obter Código de Acesso + check_census: + check_status: Conferir status + description: Aqui, você tem a opção de verificar os dados do censo para saber se tem o direito de participar nesta votação. Você já deve ter um código de acesso, mas se você o perdeu, você pode pedi-lo novamente, quando seus dados estiverem corretos. + error: + info: 'Por favor, tente novamente. Se você acha que os dados no sistema estão incorretos, você pode reportá-los aqui: %{census_contact_information}.' + title: Os dados inseridos não estão no censo para esta votação + form_title: 'Preencha o seguinte formulário para verificar os dados do censo:' + invalid: Houve um problema ao verificar o censo. + success: + access_link: via e-mail. + access_link_with_sms: por SMS ou e-mail. + title: Seus dados do censo estão corretos! + title: Posso votar? + check_fields: + date_of_birth: Data de Nascimento + day: Dia + day_placeholder: DD + document_number: Número do documento + document_number_placeholder: Carteira de identidade (RG) + document_type: Tipo de documento + month: Mês + month_placeholder: MM + postal_code: CEP + postal_code_placeholder: CEP + select: Selecione o tipo do documento + year: Ano + year_placeholder: YYYY + count: + title: + one: "%{count} votação" + other: "%{count} votações" + elections_log: + description: O registro das eleições mostrar-lhe-á todas as informações relevantes sobre cada votação. Por exemplo, o estatuto da cerimonia chave, ou apuração ou se os resultados já estão publicados. Clique sobre a eleição sobre a qual você quer as informações de registro. + title: Log da Eleição + filters: + active: Ativo + all: Todos + finished: Finalizado + search: Pesquisar + upcoming: Próximos + index: + no_votings: Nenhuma votação corresponde ao seu critério de pesquisa. + only_finished: Neste momento, não há votações agendadas, mas aqui podem encontrar as votações já finalizadas. + title: Votações + login: + access_code: Código de Acesso + access_code_placeholder: Código de Acesso + ask_for_a_new_one: Peça um novo. + form_title: 'Preencha o formulário seguinte para acessar a votação:' + start_voting: Começar a votar + title: Identificar com meus dados do recenseamento de voto + orders: + label: 'Classificar votos por:' + random: Aleatório + recent: Mais recente + show: + title: Sobre esta votação + votings_m: + badge_name: + finished: Finalizado + ongoing: Em andamento + upcoming: Próximos + unspecified: Não especificado + voting_type: + hybrid: Híbrido + in_person: Pessoalmente + online: On-line + layouts: + decidim: + voting_navigation: + check_census: Posso votar? + election_log: Log de eleições + votings: + index: + promoted_votings: Destaque da votação + promoted_voting: + vote: Votar diff --git a/decidim-elections/config/locales/pt.yml b/decidim-elections/config/locales/pt.yml new file mode 100644 index 00000000..523a7370 --- /dev/null +++ b/decidim-elections/config/locales/pt.yml @@ -0,0 +1,1045 @@ +pt: + activemodel: + attributes: + answer: + description: Descrição + image: Imagem + proposals: Propostas relacionadas + title: Título + election: + description: Descrição + end_time: Votação termina às + start_time: Votação inicia-se às + title: Título + question: + max_selections: Número máximo de seleções + min_selections: Nenhuma das opções acima + title: Título + voting: + end_time: A votação termina + start_time: A votação inicia + voting_type: Tipo de votação + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Necessita de ser recolocado + election: + attributes: + attachment: + needs_to_be_reattached: Necessita de ser reanexado + activerecord: + models: + decidim/elections/answer: + one: Resposta + other: Respostas + decidim/elections/election: + one: Eleição + other: Eleições + decidim/elections/question: + one: Pergunta + other: Perguntas + decidim/votings/census/dataset: + one: Conjunto de dados + other: Conjuntos de dados + decidim/votings/census/datum: + one: Dado + other: Dados + decidim/votings/polling_officer: + one: Oficial de votação + other: Oficiais de votação + decidim/votings/polling_station: + one: Secção de voto + other: Secções de voto + decidim/votings/voting: + one: Votação + other: Votações + decidim: + admin: + filters: + officers_assigned_eq: + label: Oficiais + values: + assigned: Atribuído + unassigned: Não atribuído + role_eq: + label: Papel + values: + manager: Gestor + president: Presidente + unassigned: Não atribuído + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Pesquisar %{coleção} pelo nome/email/alcunha ou posto de votação. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Pesquisar %{coleção} por título, endereço ou nome do oficial/email/alcunha. + signed_eq: + label: Assinado + values: + 'false': Assinado + 'true': Não assinado + validated_eq: + label: Validado + components: + elections: + actions: + vote: Votar + name: Eleições + settings: + global: + announcement: Anúncio + step: + announcement: Anúncio + elections: + actions: + confirm_destroy: Tem a certeza? + destroy: Destruir + edit: Editar + feedback: Retorno dos eleitores + import: Importar propostas para perguntas + manage_answers: Gerir as respostas + manage_questions: Gerir as perguntas + manage_steps: Gerir as etapas + preview: Pré-visualizar + publish: Publicar + title: Ações + unpublish: Remover publicação + admin: + answers: + edit: + title: Editar resposta + update: Atualizar resposta + index: + title: Respostas + new: + create: Criar resposta + title: Nova resposta + not_selected: Não seleccionado + select: + disable: Resposta não seleccionada + enable: Marcar resposta como seleccionada + selected: Seleccionado + elections: + edit: + title: Editar eleição + update: Atualizar eleição + index: + no_bulletin_board: Não há nenhum servidor do Quadro Informativo configurado, o que é necessário para usar este módulo. + title: Eleições + new: + create: Criar eleição + title: Nova eleição + publish: + success: A eleição foi publicada corretamente. + unpublish: + success: Remoção da publicação da eleição feita corretamente. + exports: + elections: Eleições + feedback_form_answers: Respostas do formulário de retroacção + menu: + trustees: Curadores + models: + answer: + name: Resposta + proposals_imports: + new: + create: Importar propostas para perguntas + no_components: Não há outros componentes de propostas neste espaço participativo para importar as propostas em respostas. + select_component: Selecione um componente + title: Importar propostas + questions: + edit: + title: Editar pergunta + update: Atualizar pergunta + index: + title: Perguntas + new: + create: Criar pergunta + title: Nova pergunta + steps: + create_election: + errors: + max_selections: As perguntas não têm um valor correcto para quantidade de respostas + minimum_answers: As perguntas devem ter pelo menos duas respostas. + minimum_questions: A eleição deve ter pelo menos uma pergunta. + published: A eleição não está publicada. + trustees_number: O espaço participativo deve ter pelo menos %{número} curadores com chave pública. + invalid: Houve um problema ao configurar esta eleição. + no_trustees: Não há curadores configurados para este espaço participativo + not_used_trustee: "(não utilizado)" + public_key: + 'false': não tem uma chave pública + 'true': tem uma chave pública + requirements: + max_selections: Todas as perguntas têm um valor correcto para máximo de respostas. + minimum_answers: As perguntas devem ter pelo menos duas respostas. + minimum_questions: A eleição tem pelo menos 1 pergunta. + published: A eleição está publicada. + time_before: A configuração está a ser feita pelo menos %{hours} horas antes do início da eleição. + trustees_number: O espaço participativo tem pelo menos %{número} curadores com chave pública. + submit: Configurar eleição + title: Configurar eleição + trustees: Curadores da eleição + created: + submit: Iniciar a cerimónia principal + title: Eleição criada + trustees: Curadores + key_ceremony: + continue: Continuar + title: Cerimónia-chave + key_ceremony_ended: + errors: + time_before: A eleição está pronta para começar. Tem de esperar até %{hours} horas antes da hora de início (%{start_time}) para abrir o período de votação. + requirements: + time_before: A eleição começa em breve. Pode abrir o período de votação manualmante, ou será aberto automaticamente antes da hora de início, às %{start_time}. + submit: Abrir período de votação + title: Pronto para começar + processing: A processar... + results_published: + answer: Responder + not_selected: Não seleccionado + question: Pergunta + result: Resultado + selected: Seleccionado + submit: Enviar + title: Resultados publicados + tally_ended: + answer: Resposta + not_selected: Não seleccionado + question: Pergunta + result: Resultado + selected: Seleccionado + submit: Publicar resultados + title: Resultados calculados + tally_started: + mark_as_missing: Assinalar como ausente + tally_completion: O processo será completo quando todos os curadores estiverem activos ou assinalados como ausentes. São necessários pelo menos %{quorum} curadores para completar o processo. + undo_mark_as_missing: Um curador assinalado como ausente poderá participar antes do processo estar completo. Pode proceder como usualmente e o assinalar da ausência será ignorado. + vote: + errors: + time_after: A eleição ainda está a decorrer. Tem que esperar até à hora de fecho (%{end_time}) para poder fechar o período de votação. + requirements: + time_after: A eleição terminou. Pode fechar manualmente o período de votação ou este terminará automaticamente em alguns minutos. + submit: Fechar período de votação + title: Período de votação + vote_ended: + submit: Começar a contagem + text: A votação terminou. Pode comoçar a contagem agora. + title: Período de votação terminou + vote_stats: + no_vote_statistics_yet: Ainda não há estatísticas da votação + title: Estatísticas da votação + voters: Eleitores + votes: Votos + trustees_participatory_spaces: + actions: + disable: Desativar + enable: Considerar + form: + select_user: Seleccionar o utilizador + index: + title: Curadores + new: + create: Criar curador + title: Novo curador + admin_log: + election: + create: "%{user_name} criou a eleição %{resource_name} para o/a %{space_name}" + delete: "%{user_name} apagou a eleição %{resource_name} para o/a %{space_name}" + end_vote: "%{user_name} terminou o período de votação para a eleição %{resource_name} de %{space_name} no Quadro Informativo" + publish: "%{user_name} publicou a eleição %{resource_name} para a/o %{space_name}" + publish_results: "%{user_name} publicou os resultados para a eleição %{resource_name} de %{space_name} no Quadro Informativo" + setup: "%{user_name} criou a eleição %{resource_name} de %{space_name} no Quadro Informativo" + start_key_ceremony: "%{user_name} iniciou a cerimónia chave para a eleição %{resource_name} de %{space_name} no Quadro Informativo" + start_tally: "%{user_name} começou a contagem para a eleição %{resource_name} de %{space_name} no Quadro Informativo" + start_vote: "%{user_name} abriu o período de votação para a eleição %{resource_name} de %{space_name} no Quadro Informativo" + unpublish: "%{user_name} removeu a publicação de %{resource_name} de %{space_name}" + update: "%{user_name} actualizou a eleição %{resource_name} de %{space_name}" + trustee: + create: "%{user_name} atribuiu ao utente %{trustee_user} como curador" + election_m: + badge_name: + finished: Terminado + ongoing: Activo + upcoming: Seguinte + end_date: Termina + footer: + remaining_time: + one: "%{count} hora %{minutos} minutos restante para votar." + other: "%{count} horas %{minutos} minutos restantes para votar." + view: Visualizar + vote: Votar + label: + date: Datas + questions: Perguntas %{contagem} + start_date: Inicia + unspecified: Não especificado + elections: + count: + elections_count: + one: "%{count} eleição" + other: "%{count} eleições" + election_log: + chained_hash: O código Hash desta mensagem + complete: Concluir + creation_description: + complete: A eleição foi criada e foi configurada com êxito no Quadro Informativo. + not_created: A eleição ainda não foi criada. + creation_title: Eleição criada + description: Este é o registo de eleição onde pode verificar o estado de cada etapa, por exemplo, quando a eleição foi criada, se o processo de contagem está concluído e quando a eleição está fechada. + download: Descarregar + key_ceremony_description: + complete: A cerimónia principal está concluída. Cada curador tem chaves válidas e descarregou as chaves de reserva necessárias. + not_started: A cerimónia principal ainda não começou. + started: A cerimónia principal começou, mas ainda não está concluída. + key_ceremony_title: Cerimónia principal + not_available: Ainda não disponível + not_created: Não criado + not_ready: Não está pronto + not_started: Não iniciado + published: Publicado + results_description: + not_published: Os resultados ainda não foram publicados. + published: Os resultados estão publicados. + results_title: Resultado + started: Início + tally_description: + finished: O processo de contagem está terminado. + not_started: O processo de contagem ainda não começou. + started: O processo de contagem começou. + tally_title: Processo de contagem + title: Registo de Eleições + verifiable_results: + checksum: 'Soma de verificação SHA256 do ficheiro:' + description: + ready: 'Aqui é possível verificar a eleição. Primeiro, descarregue o ficheiro e certifique-se que não foi corrompido. Para isso, execute o seguinte comando e verifique se o resultado corresponde à soma de controlo:' + how_to_verify: "Assim que descarregar o ficheiro e se tiver certificado que está conforme, pode continuar para a execução do verificador universal.\nClone este repositório e, a partir da pasta de raiz, execute o comando seguinte:" + title: Verifique o resultado da eleição + verifiable_file: 'Ficheiro da eleição verificável:' + verify: Verificar a eleição + vote_description: + finished: O processo de votação está concluído. + not_started: O processo de votação ainda não começou. + started: O processo de votação já começou. + vote_title: Processo de votação + filters: + active: Activo + all: Todos + finished: Terminado + upcoming: Seguinte + preview: + available_answers: 'Respostas disponíveis:' + description: 'Estas são as perguntas que encontrará no processo de votação:' + title: Questões da eleição + results: + description: 'Estes são os resultados da votação, para cada pergunta:' + percentage: "%{count}%" + selected: Seleccionado + title: Resultados da eleição + votes: + one: "%{count} voto" + other: "%{count} votos" + show: + action_button: + change_vote: Mudar o seu voto + vote: Começar votação + vote_again: Votar de novo + callout: + already_voted: Já votou nesta eleição. Pode alterar o seu voto ou verificá-lo. + pending_vote: O seu voto está a ser registado no servidor. + vote_rejected: Não foi possível certificar o seu voto. Por favor registe-o de novo. + election_log: Registo de eleições + preview: Pré-visualizar + verify: + already_voted: Já votou? + verify_here: Verifique o seu voto aqui. + will_verify: Poderá verificar o seu voto assim que a eleição tenha começado. + voting_period_status: + finished: A votação começou em %{start_time} e terminou em %{end_time} + ongoing: 'Votação ativa até: %{end_time}' + upcoming: A votação começa em %{start_time} + feedback: + answer: + invalid: Houve um problema ao enviar o seu retorno. + success: Retorno enviado com sucesso. + models: + answer: + fields: + proposals: Propostas + selected: Seleccionado + title: Título + votes: Votos + election: + fields: + bb_status: Estado do Quadro Informativo + end_time: Termina às + start_time: Inicia às + title: Título + verifiable_results_file_hash: Soma de controlo SHA256 do ficheiro + verifiable_results_file_url: Processo eleitoral verificável + question: + fields: + answers: Respostas + max_selections: Máx. de seleções + title: Título + trustees_participatory_space: + fields: + considered: considerado + email: E-Mail + inactive: inactivo + name: Nome + notification: Notificação enviada às + public_key: Chave pública + status: Estado + orders: + label: Ordenar eleições por + older: Menos recentes + recent: Mais recentes + trustee_zone: + elections: + backup_modal: + description: Esta eleição está a ser criada no Quadro Informativo. É muito importante que todos os seus Curadores criem uma cópia de segurança destas chaves e as guardem num lugar seguro. Após isso o processo continua. + download_election_keys: Descarregar chaves + title: Chaves %{eleição} em cópia de segurança + key_ceremony_steps: + back: Recuar + description: Esta eleição está a ser criada no Quadro Informativo. Para completar este precesso a sua participação com Curador é necessária. + keys: + create_election: Geração de chaves + key_ceremony: + joint_election_key: Geração de chaves conjunta + step_1: Publicação de chaves + list: + status: Estado + task: Tarefa + process_warning: Uma vez o processo é iniciado, você não deve sair desta página até que o processo termine. Pode demorar vários minutos, visto que os Curadores todos os curadores devem estar ligados para completá-lo. + start: Início + status: + completed: Completo + pending: Pendente + processing: A processar + title: Criar chaves de eleição para %{election} + restore_modal: + description: O Quadro Informativo tem informação sua enquanto Curador desta eleição. Para continuar o processo, primeiro transfira ficheiro de segurança gerado durante a sessão anterior. + title: Restaurar as chaves de eleição para %{election} + upload_election_keys: Enviar chaves de eleição + tally_started_steps: + description: Os resultados desta eleição estão a ser processados no Quadro Informativo. Para completar este processo é necessária a sua participação como curador. + keys: + end_tally: A contagem terminou + tally: + cast: Registar a contagem + share: Partilhar a contagem + title: Contagem para %{election} + menu: + trustee_zone: Área de curadores + no_bulletin_board: + body: Um Quadro Informativo configurado é necessário para esta secção. Contacte o Administrador para mais detalhes. + title: Desculpe, o Quadro Informativo não está ainda configurado. + trustees: + show: + elections: + list: + action_required: + 'false': 'Não' + name: Acção requerida? + 'true': Realizar acção + bb_status: Estado + election: Eleição + voting_period: Período de votação + title: Eleições + identification_keys: + cancel: Cancelar + generate: Gerar chaves de identificação + generate_error: Houve um erro ao gerar as chaves de identificação. + generate_legend: Precisa de gerar um par de chaves de identificação para participar em eleições como curador. + generate_legend_1: Depois de carregar no botão deve descarregar o ficheiro com as chaves de identificação geradas. + generate_legend_2: Copie o ficheiro descarregado para um dispositivo USB limpo + generate_legend_4: Faça uma cópia du ficheiro num dispositivo externo e guarde-o num local seguro. + submit: Submeter + submit_title: Submeter a chave de identificação pública + title: Chaves de identificação de curador + upload: Enviar as suas chaves de identificação + not_supported_browser_title: Actualize o seu navegador para poder agir como Curador + update: + success: A sua chave de identificação pública foi guardada com êxito. + votes: + ballot_decision: + audit: ( Auditar o boletim de voto ) + back: Começar o processo de votação de novo + ballot_hash: 'O seu identificador de boletim de voto é:' + header: 'O boletim de voto está encriptado: registe-o ou audite-o' + casting: + header: A registar o voto... + text: O seu boletim de voto está a ser registado na urna de voto. + confirm: + answer_number: resposta %{number} + confirm: Confirmar + edit: editar + header: Confirmar o seu voto + intro: Aqui está um resumo do voto que está prestes a transmitir.
    Por favor, confirme o seu voto ou edite as suas respostas. + nota_option: Em branco + confirmed: + back: Retroceder às eleições + experience: Como foi a sua experiência? + feedback: Dê-nos algum feedback + header: Voto confirmado + lead: O seu voto foi registado! + text: 'Pode verificar que o seu voto foi adicionado corretamente à caixa de voto com o seguinte identificador: %{e_vote_poll_id}' + verify_link: Para verificar, copie o identificador e cole-o na página de verificação de voto + create: + error: Houve um problema ao registrar o seu voto. Por favor, tente de novo. + encrypting: + header: A encriptar o voto... + text: O seu boletim de voto está a ser encriptado para garantir o segredo do seu voto. + failed: + header: Votação falhou + lead: O seu voto não foi registado! + text: Algo correu mal, por favor tente de novo. + try_again: Tente de novo + header: + ballot_decision: Registe ou audite o seu voto + confirm: Confirmar o seu voto + messages: + invalid_token: A sua sessão na cabine de voto não é válida. Tente votar de novo. + not_allowed: Não está autorizado a votar nesta eleição neste momento. + modal: + close: Fechar + proposal_header: 'Propostas:' + new: + answer_choices: Pode seleccionar até %{choices} respostas + more_information: Mais informações + nota_option: Branco / Nenhum dos itens acima + preview_alert: Isto é uma pré-visualização da cabina de voto. + question_steps: Questão %{current_step} de %{total_steps} + selections: "%{selected} de %{max_selections}
    seleções" + onboarding_modal: + create_account: Criar conta + no_account: Não, obrigado. + update: + error: Houve um problema ao actualizar o seu voto. Por favor, tente de novo. + verify: + content: + heading: Verificar o seu voto + info: Este verificador verifica que o seu voto, identificado com a cadeia de texto encriptada, foi registado correctamente e está dentro da urna de voto. + error: + header: Voto não encontrado! + info: O código do voto não foi encontrado na urna de voto de %{link}, tente de novo. + form: + submit: Verificar + vote_identifier: 'Código de identificação:' + header: + title: Verificar o seu voto + success: + header: Voto localizado! + voting_step: + back: Retroceder + continue: Seguinte + events: + elections: + election_published: + email_intro: 'A eleição %{resource_title} está agora ativa em %{participatory_space_title}. Pode vê-la a partir da página:' + email_outro: Recebeu esta notificação porque segue %{participatory_space_title}. Pode parar de receber notificações seguindo a hiperligação anterior. + email_subject: A eleição %{resource_title} está agora ativa em %{participatory_space_title}. + notification_title: A eleição %{resource_title} está agora ativa em %{participatory_space_title}. + trustees: + new_election: + email_intro: Foi adicionado como curador para a eleição %{resource_title}. + new_trustee: + email_intro: Um administrador adicionou-o como curador para%{resource_name}. Deve criar a sua chave pública na sua área de curador + email_subject: É curador para a eleição %{resource_name}. + votes: + accepted_votes: + email_intro: 'O seu voto foi aceite! Utilizando o seu identificador: %{encrypted_vote_hash}, pode verificar o seu voto aqui.' + email_subject: O seu voto para %{resource_name} foi aceite. + notification_title: 'O seu voto foi aceite. Verifique o seu voto aqui utilizando o seu identificador: %{encrypted_vote_hash}' + votings: + polling_officers: + polling_station_assigned: + email_intro: Foi-lhe atribuído o papel de %{role} na Secção de Voto %{polling_station_name} em %%{resource_title}. Pode gerir a Secção de Voto a partir da Zona do Oficial de Votação dedicada. + email_outro: Recebeu esta notificação porque lhe foi atribuído o papel de %{role} na %{polling_station_name}. + email_subject: É %{role} na Secção de Voto %{polling_station_name}. + notification_title: É %{role} na Secção de Voto %{polling_station_name} na votação %{resource_title}. + send_access_code: + instruction: 'Aqui está o Código de Acesso que pediu: %{access_code}. Com este código pode participar em %{voting}.' + subject: O seu Código de Acesso para participar em %{voting} + help: + participatory_spaces: + votings: + contextual: "

    Uma votação é um espaço que permite perguntar uma questão transparente a todas a pessoas que formam uma organização, fazer uma chamada a participar na votação, espoletar e ordenar o debate a favor ou contra uma resposta. Quando a data duma consultação chega, pode votar e publicar o resultados das votações.

    Exemplos: As votações podem ser sobre praticamente todos os aspectos que afectam uma organização: alguns exemplos são a mudança de nome ou logótipo da organização oferecendo várias alternativas, decidir Sim ou Não ser parte duma organização maior, validar ou rejeitar um plano estratégico novo ou o resultado dum grupo de trabalho, ou definir se os cargos devem durar por um máximo de 1, 2, ou 3 mandatos.

    " + page: "

    Uma votação é um espaço que permite perguntar uma questão transparente a todas a pessoas que formam uma organização, fazer uma chamada a participar na votação, espoletar e ordenar o debate a favor ou contra uma resposta. Quando a data duma consultação chega, pode votar e publicar o resultados das votações.

    Exemplos: As votações podem ser sobre praticamente todos os aspectos que afectam uma organização: alguns exemplos são a mudança de nome ou logótipo da organização oferecendo várias alternativas, decidir Sim ou Não ser parte duma organização maior, validar ou rejeitar um plano estratégico novo ou o resultado dum grupo de trabalho, ou definir se os cargos devem durar por um máximo de 1, 2, ou 3 mandatos.

    " + title: O que são votações? + menu: + votings: Votações + statistics: + elections_count: Eleições + votings_count: Votações + votings: + admin: + ballot_styles: + edit: + title: Editar estilo de boletim de voto + update: Actualizar + form: + election: Eleição + questions: Questões para este estilo de boletim de voto + index: + actions: + confirm_destroy: Tem a certeza? + destroy: Eliminar + edit: Editar + title: Acções + associated_census_data: Entradas de recenseamento associadas + title: Estilos de boletim de voto + new: + create: Criar + title: Criar estilo de boletim de voto + content_blocks: + highlighted_votings: + max_results: Montante máximo dos projectos a mostrar + index: + published: Publicado + menu: + votings: Votações + votings_submenu: + attachment_collections: Pastas + attachment_files: Ficheiros + attachments: Anexos + ballot_styles: Estilos de boletim de voto + census: Recenseamento + components: Componentes + landing_page: Página de entrada + monitoring_committee: Comissão de controlo + monitoring_committee_election_results: Valide Resultados + monitoring_committee_members: Membros + monitoring_committee_polling_station_closures: Certificados válidos + monitoring_committee_verify_elections: Verificar a eleição + polling_officers: Oficiais de votação + polling_stations: Secções de voto + models: + ballot_style: + fields: + code: Código + monitoring_committee_member: + fields: + email: E-Mail + name: Nome + polling_officer: + fields: + email: E-Mail + name: Nome + polling_station: Secção de voto (papel) + polling_station: + fields: + address: Endereço + polling_station_managers: Gestor + polling_station_president: Presidente + title: Título + voting: + fields: + created_at: Criado em + published: Publicado + title: Título + monitoring_committee_election_results: + actions: + title: Acções + view: Visualizar + index: + title: Escolher uma eleição de qual quer ver os resultados + results: + bulletin_board: Quadro Informativo + election_totals: Totais da eleição + polling_stations: Secções de voto + result_types: + blank_answers: Respostas em branco + blank_ballots: Boletins de voto em branco + null_ballots: Boletins de voto nulos + total_ballots: Total de boletins de voto + valid_ballots: Boletins de voto válidos + selected: Seleccionado + title: Resultados para a eleição %{election_title} + totals: Totais + show: + change_election: Mudar eleição + publish_results: Publicar resultados + publishing: Publicar resultados + update: + rejected: A publicação dos resultados foi registada pelo Quadro Informativo. Tente de novo ou contacte o administrador do sistema. + monitoring_committee_members: + form: + existing_user: Participante existente + non_user: Convidar novo participante + select_user: Procurar por nome, e-mail ou alcunha + user_type: Tipo de participante + index: + title: Comissão de controlo + new: + create: Criar + title: Criar membro da comissão de controlo + monitoring_committee_polling_station_closures: + actions: + title: Acções + validate: Validado + view: Visualizar + closures: + change_election: Mudar eleição + signed: Assinado + title: Secções de voto para a eleição %{election_title} + validated: Validado + edit: + change_polling_station: Retroceder para as Secções de Voto + monitoring_committee_notes: Reparos + title: Resultados para a eleição %{election_title} na secção de voto %{polling_station_title} + elections: + title: Escolher a eleição que quer validar + show: + change_polling_station: Retroceder para as Secções de Voto + monitoring_committee_notes: Reparos da Comissão de Controlo + monitoring_committee_verify_elections: + index: + download: Descarregar + how_to_checksum: 'Para se certificar que o ficheiro que descarregou não está corrompido ou não foi alterado durante o processo execute o comando seguinte na sua consola e verifique que o resultado corresponde à soma de controlo anunciada acima:' + how_to_download: Pare verificar uma eleição descarregue o seu ficheiro verificável a partir da taabela acima. + how_to_run_verifier: "Assim que descarregar o ficheiro e se tiver certificado que está conforme, pode continuar para a execução do verificador universal.\nClone este repositório e, a partir da pasta de raiz, execute o comando seguinte:" + how_to_title: Como verificar a validade da eleição + not_available: Não disponível ainda + title: Eleições + polling_officers: + form: + existing_user: Participante existente + non_user: Convidar novo participante + select_user: Procurar por nome, e-mail ou alcunha + user_type: Tipo de participante + index: + role_manager: Gestor + role_president: Presidente + title: Oficiais de votação + new: + create: Criar + title: Criar oficial de votação + polling_officers_picker: + choose_polling_officers: Escolher oficiais de votação + polling_stations: + edit: + title: Editar secção de voto + update: Actualizar secção de voto + form: + address_help: 'Endereço: utilizado pelo Geocoder para descobrir a localização' + location_help: 'Localização: mensagem dirigida aos eleitores com a localização exacta da secção de voto' + location_hints_help: 'Sugestões de localização: informação adicional. Exemplo: o piso do edifício onde a secção de voto está localizada.' + polling_station_managers_help: 'Gestores da secção de voto: os oficiais de votação que irão agir como gestores da secção de voto. Certifique-se que os oficiais de votação já foram criados na Lista de Oficiais e que não estão já designados para outra secção de voto' + select_president: Seleccionar um oficial de votação como presidente da secção de voto + index: + title: Secções de voto + new: + create: Criar + title: Criar secção de voto + titles: + votings: Votações + votings: + actions: + confirm_destroy: Tem a certeza? + destroy: Destruir + new_voting: Novo Espaço de Votação + edit: + update: Actualizar + form: + select_a_voting_type: Por favor seleccione um tipo de votação + title: Título + voting_type: + hybrid: Híbrido + in_person: Presencial + online: On-line + new: + create: Criar + title: Nova Votação + admin_log: + ballot_style: + create: "%{user_name} criou um estilo de boletim de voto com o código %{ballot_style_code} no espaço %{space_name}" + delete: "%{user_name} eliminou um estilo de boletim de voto com o código %{ballot_style_code} no espaço %{space_name}" + update: "%{user_name} actualizou um estilo de boletim de voto com o código %{ballot_style_code} no espaço %{space_name}" + census: + create: "%{user_name} criou o recenseamento para o espaço %{space_name}" + delete: "%{user_name} eliminou o recenseamento para o espaço %{space_name}" + update: "%{user_name} actualizou o recenseamento para o espaço %{space_name}" + monitoring_committee_member: + create: "%{user_name} designou o utilizador %{monitoring_committee_member_user} como membro da comissão de controlo no espaço %{space_name}" + delete: "%{user_name} exonerou o utilizador %{monitoring_committee_member_user} de membro da comissão de controlo no espaço %{space_name}" + polling_officer: + create: "%{user_name} designou o utilizador %{polling_officer_user} como oficial de votação no espaço %{space_name}" + delete: "%{user_name} exonerou o utilizador %{polling_officer_user} de Oficial de Votação de controlo no espaço %{space_name}" + polling_station: + create: "%{user_name} criou a secção de voto %{resource_name} no espaço %{space_name}" + delete: "%{user_name} eliminou a secção de voto %{resource_name} no espaço %{space_name}" + update: "%{user_name} actualizou a secção de voto %{resource_name} no espaço %{space_name}" + voting: + create: "%{user_name} publicou a votação%{resource_name}" + publish: "%{user_name} publicou a votação%{resource_name}" + unpublish: "%{user_name} publicou a votação%{resource_name}" + census: + admin: + census: + create: + invalid: Ocorreu um erro ao enviar o recenseamento, por favor tente de novo. + delete: + button: Eliminar todos os dados do censo + destroy: + error: Ocorreu um erro ao eliminar o recenseamento, por favor tente de novo. + export_access_codes: + button: Exportar Códigos de Acesso de votação + callout: Pode continuar com a exportação dos códigos de acesso. Isto só pode ser feito uma vez. Assim que comece a exportação irá receber um e-mail %{email} em com instruções + confirm: Só pode exportar os códigos de acesso uma vez. Certifique-se que tem acesso à conta de e-mail %{email}. + freeze: + callout: O recenseamento está congelado e não pode ser modificado + generate_access_codes: + button: Gerar Códigos de Acesso à votação + callout: Pode agora continuar para gerar os códigos de acesso. Atenção que depois de gerar os códigos de acesso não poderá depois modificar o recenseamento. + confirm: Se continuar não poderá depois modificar o recenseamento. + info_message_all: "Todas as linhas do ficheiro %{file} importadas com êxito (%{raw_count} de %{data_count})." + info_message_warn: Por favor verifique que não há dados em falta, porque %{data_count} registos foram criados e o ficheiro %{file} enviado tinha %{raw_count} linhas. + launch_error: Problemas ao lançar a geração dos códigos de acesso + launch_success: Geração de códigos lançada. + new: + file_help: + explanation: 'Orientação para o ficheiro:' + message_1: Só são permitidos ficheiros CSV (.csv). + message_2: O separador entre colunas deve ser um ponto e vírgula (“;”) + has_ballot_styles_message: Está a configurar Estilos de Boletim de Voto. Por favor certifique-se de que o campo “%{ballot_style_code_header}” no ficheiro CSV corresponde ao Estilo de Boletim de Voto desejado. + info_message: "Não há ainda recenseamento. Por favor use o formulário abaixo para o criar importando um ficheiro CSV." + missing_ballot_styles_message: 'Não há ainda um Estilo de Boletim de Voto para esta votação. Se ter questões condicionais (exemplo: apresentar ao eleitor questões diferentes dependendo, entre outros, no distrito/regioão de residência), precisa de configurar os Estilos de Boletim de Voto antes de importar o recenseamento. Se quiser apresentar as mesmas questões a todos os eleitores pode continuar com o procedimento de importação de recenseamento.' + title: Criar recenseamento + show: + heading: Recenseamento do espaço de votação + upload_info: + csv_example_with_ballot_style: 'Um exemplo de ficheiro com estilos de boletim de voto:' + csv_example_without_ballot_style: 'Um exemplo de ficheiro sem estilos de boletim de voto:' + csv_header_after: Não inclua o último campo ("%{ballot_style_code_header}") se não precisar de estilos de boletim de voto/questões condicionais + csv_header_before: 'O recenseamento deve ser um ficheiro CSV com o seguinte cabeçalho:' + document_types: + passport: Passaporte + export_mailer: + access_codes_export: + click_button: 'Clique na ligação para descarregar os dados códigos de acesso.
    O ficheiro estará disponível até %{date}.
    Irá precisar de7-Zip (para Windows), Keka (para MacOS), ou PeaZip (para Linux) para abrir o ficheiro. Palavra-chave: %{password}' + download: Descarregar + subject: Estão disponíveis para exportação os códigos de acesso À votação para %{voting_title} + vote_flow: + already_voted_in_person: Este participante já votou presencialmente e não tem o direito de votar. + content_blocks: + highlighted_votings: + name: Votações realçadas + landing_page: + polling_stations: + heading: Secções de voto + no_polling_stations: Não há ainda secções de voto. + monitoring_committee_members: + actions: + confirm_destroy: Tem a certeza? + destroy: Eliminar + new: Novo membro + title: Acções + polling_officer_zone: + closures: + back_to_polling_stations: Retroceder para as secções de voto + certify: + error: Ocorreu um erro ao anexar o certificado, por favor tente de novo. + heading: Recontagem de votos - Envie o certificado + info_text: Por favor envie uma fotografia do Certificado de Encerramento Eleitoral. + submit: Envie o certificado + success: Certificado enviado com êxito + create: + error: Ocorreu um erro ao criar o encerramento, por favor tente de novo. + success: Encerramento criado com êxito. + edit: + heading: Recontagem de votos - Recontagem de respostas + modal_ballots_results_count_error: + close_modal: Fechar + title: A contagem de boletins de voto não está correcta + save_recount: Guardar recontagem + total_ballots: Total de boletins de voto + total_blank_ballots: Total de boletins de voto em branco + total_null_ballots: Total de boletins de voto nulos + total_valid_ballots: 'Total dos boletins de voto válidos:' + new: + election: 'Eleição' + heading: Recontagem de votos + info_text: 'Por favor introduza o número total de boletins de voto (envelopes) recontados nesta Secção de Voto:' + modal_ballots_count_error: + btn_validate_total: Validar total de recontagem de boletins de voto + message_for_monitoring_committee: Mensagem da Comissão de Controlo + review_recount: Rever a recontagem + text_area_placeholder: Por favor escreva a sua mensagem + title: A contagem de registos não está correcta + total_ballots: 'Total de boletins de voto:' + total_people: 'Total de pessoas:' + polling_station: 'Secção de voto' + submit: Verificar números totais + total_ballots_count: Número de boletins de voto + show: + heading: Recontagem de votos + sign: + cancel: Cancelar + confirm: Ok, continuar + error: Ocorreu um erro, por favor tente de novo. + heading: Recontagem de votos - Assine o encerramento + submit: Assinar o encerramento + success: Encerramento assinado com êxito + update: + error: Ocorreu um erro ao actualizar os resultados do encerramento, por favor tente de novo. + success: Resultados de encerramento actualizados com êxito + in_person_votes: + complete_voting: + available_answers: 'Respostas disponíveis:' + complete_voting: Completar votação + identify_another: Identificar outro participante + voted: O participante já votou + create: + error: O voto não foi registado. Por favor tente de novo. + in_person_form: + census_not_present: Este participante está listado no recenseamento. + census_not_present_description: Ela deve ir gabinete de queixas do recenseamento ou contactar o apoio. + date_of_birth: Data de nascimento + day: Dia + day_placeholder: DD + document_number: Número do documento + document_number_placeholder: Número de identificação + month: Mês + month_placeholder: MM + select: Seleccione o tipo de documento + title: 'Seleccione o tipo de documento e introduza o número do documento do participante:' + validate_document: Validar documento + year: Ano + year_placeholder: YYYY + new: + back: Retroceder para as secções de voto + title: Identificar e verificar um participante + show: + back: Retroceder para as secções de voto + title: A aguardar que a votação presencial seja registada + update: + error: Houve um erro ao registrar o seu voto. Por favor, tente de novo. + success: + accepted: A votação foi registada com êxito. + rejected: O voto não foi aceite pelo Quadro Informativo. Por favor contacte o administrador do sistema. + verify_document: + census_present: Este participante está listado no recenseamento. + name: Nome + title: 'Verifique se os seguintes dados estão correctos:' + verify_document: Verificar o documento + menu: + polling_officer_zone: Zona do Oficial de Votação + polling_officers: + index: + polling_officer_role_description: Foi designado para agir como Oficial de Secção de Voto (presidente ou gestor) em algumas eleições celebradas nesta plataforma. + polling_station: + address: Endereço + count_votes: Contagem de votos + election: Eleição + identify_person: Identificar uma pessoa + name: Nome + no_polling_stations: Não está ainda designado a nenhuma Secção de Voto. + role: O seu papel + show_closure: Ver encerramento + title: Secções de voto + voting: Votação + polling_officers: + actions: + confirm_destroy: Tem a certeza? + destroy: Eliminar + title: Acções + roles: + manager: Gestor + president: Presidente + unassigned: Não atribuído + polling_station_closure_recount: + nota_option: Branco / Nenhum dos itens acima + polling_officer_notes: 'Notas do Oficial de Votação:' + polling_officer_notes_blank: Não há notas + recount_summary: 'Resumo da recontagem:' + signed: Assinado + total_ballots: 'Total de boletins de voto:' + total_blank_ballots: 'Total de boletins de voto em branco:' + total_null_ballots: 'Total de boletins de voto nulos:' + total_valid_ballots: 'Total dos boletins de voto válidos:' + polling_stations: + actions: + confirm_destroy: Tem a certeza? + destroy: Eliminar + edit: Editar + title: Acções + votings: + access_code_modal: + email: Enviar e-mail para %{email} + no_email: Nenhum e-mail disponível + no_sms: Nenhum número de telefone disponível + sms: Enviar por SMS para %{sms} + title: Obter código de acesso + check_census: + check_status: Verificar estado + description: Aqui tem a opção de verificar os dados do seu recenseamento para saber se tem o direito a participar nesta votação. Deve ter já um código de acesso mas se o perdeu pode pedi-lo de novo se os seus dados estiverem correctos. + form_title: 'Preencha o formulário seguinte para verificar os seus dados de recenseamento:' + invalid: Ocorreu um problema ao verificar o recenseamento. + success: + access_link_with_sms: via SMS ou e-mail. + title: Os seus dados de recenseamento estão correctos! + title: Posso votar? + check_fields: + date_of_birth: Data de nascimento + day: Dia + day_placeholder: DD + document_number: Número do documento + document_number_placeholder: Número de identificação + month: Mês + month_placeholder: MM + postal_code: Código postal + postal_code_placeholder: Código postal número + select: Seleccione o tipo de documento + year: Ano + year_placeholder: YYYY + count: + title: + one: "%{count} votos" + other: "%{count} votos" + elections_log: + description: O registo da eleição irá mostrar toda a informação relevante sobre cada votação. Por exemplo, o estado da cerimónia-chave ou contagem ou se os resultados já foram publicados. Clique na eleição da qual quer o a informação de registo. + title: Registo de eleições + filters: + active: Activo + all: Todos + finished: Terminado + search: Pesquisar + upcoming: Seguinte + index: + no_votings: Nenhuma votação corresponde aos seus critérios de pesquisa. + only_finished: Actualmente, não há votações agendadas, mas aqui pode encontrar listadas todas as votações passadas. + title: Votações + login: + access_code: Código de acesso + access_code_placeholder: Código de acesso + ask_for_a_new_one: Pedir um novo. + form_title: 'Preencha o formulário seguinte para aceder à votação:' + start_voting: Começar votação + title: Identificar-me com os meus dados de recenseamento para votação + orders: + label: 'Ordenar votações por:' + random: Aleatório + recent: Mais recente + votings_m: + badge_name: + finished: Terminado + ongoing: Em andamento + upcoming: Seguinte + unspecified: Não especificado + voting_type: + hybrid: Híbrido + in_person: Presencial + online: On-line + layouts: + decidim: + voting_navigation: + check_census: Posso votar? + election_log: Registo de eleições + votings: + index: + promoted_votings: Votações realçadas + promoted_voting: + vote: Votar diff --git a/decidim-elections/config/locales/ro-RO.yml b/decidim-elections/config/locales/ro-RO.yml new file mode 100644 index 00000000..56620275 --- /dev/null +++ b/decidim-elections/config/locales/ro-RO.yml @@ -0,0 +1,620 @@ +ro: + activemodel: + attributes: + answer: + description: Descriere + image: Imagine + proposals: Propuneri asociate + title: Titlu + election: + description: Descriere + end_time: Votarea se încheie la + start_time: Votarea începe la + title: Titlu + polling_station: + title: Titlu + question: + max_selections: Numărul maxim de selecții + min_selections: Niciuna dintre opțiunile de mai sus + title: Titlu + voting: + end_time: Votarea se încheie + scope_id: Domeniu de interes + start_time: Votarea începe + title: Titlu + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Trebuie să fie reatașate + election: + attributes: + attachment: + needs_to_be_reattached: Trebuie să fie reatașate + activerecord: + models: + decidim/elections/answer: + one: Răspuns + few: Răspunsuri + other: Răspunsuri + decidim/elections/election: + one: Alegeri + few: Alegeri + other: Alegeri + decidim/elections/question: + one: Întrebare + few: Întrebări + other: Întrebări + decidim/votings/polling_officer: + one: Ofițer de sondaj + few: Ofițer de sondaj + other: Ofiţeri de votare + decidim/votings/polling_station: + one: Stație de votare + few: Stație de votare + other: Secţii de votare + decidim/votings/voting: + one: Votare + few: Votări + other: Votări + decidim: + admin: + filters: + officers_assigned_eq: + label: Ofiţeri + values: + assigned: Atribuit + unassigned: Nu este atribuit + role_eq: + label: Rol + values: + manager: Administrator + president: Președinte + unassigned: Neatribuit + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: Caută %{collection} după nume/e-mail/pseudonim sau stație de votare. + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : Caută %{collection} după titlu, adresă sau nume/e-mail/pseudonim al ofițerului. + signed_eq: + label: Semnat + values: + 'false': Semnat + 'true': Nu este semnat + validated_eq: + label: Validat + components: + elections: + actions: + vote: Votează + name: Alegeri + settings: + global: + announcement: Anunţ + step: + announcement: Anunţ + elections: + actions: + confirm_destroy: Sigur dorești asta? + destroy: Distruge + edit: Editare + feedback: Feedback la votare + import: Importă propunerile pentru răspunsuri + manage_answers: Gestionare răspunsuri + manage_questions: Gestionează întrebările + manage_steps: Gestionați pașii + preview: Previzualizare + publish: Publică + title: Acțiuni + unpublish: De-publicare + admin: + answers: + edit: + title: Editare răspuns + update: Actualizare răspuns + index: + title: Răspunsuri + new: + create: Creează răspuns + title: Răspuns nou + not_selected: Nu este selectat + select: + disable: Deselectare răspuns + enable: Marchează răspunsul ca selectat + selected: Selectate + elections: + edit: + title: Editează alegeri + update: Actualizează alegeri + index: + no_bulletin_board: Nu există serverul Panoul de afișare configurat, care este necesar pentru a utiliza acest modul. Această sarcină trebuie realizată de Administratorul de sistem. + title: Alegeri + new: + create: Creează alegeri + title: Alegeri noi + publish: + success: Alegerile au fost publicate cu succes. + unpublish: + success: Alegerile au fost anulate cu succes. + exports: + elections: Alegeri + feedback_form_answers: Răspunsuri la formularul de feedback + menu: + trustees: Împuterniciți + models: + answer: + name: Răspuns + proposals_imports: + new: + create: Importă propunerile pentru răspunsuri + no_components: Nu există alte componente de propunere în acest spațiu participativ pentru importa propunerile în răspunsuri. + select_component: Te rugăm selectează o componentă + title: Importă propuneri + questions: + edit: + title: Editare întrebare + update: Actualizare întrebare + index: + title: Întrebări + new: + create: Creează întrebare + title: Întrebare nouă + steps: + create_election: + errors: + max_selections: Întrebările nu au o valoare corectă pentru suma de răspunsuri + minimum_answers: Întrebările trebuie să aibă cel puţin două răspunsuri. + minimum_questions: Alegerile trebuie să aibă cel puţin o întrebare. + published: Aceste alegerile nu sunt publicate. + trustees_number: Spațiul participativ trebuie să aibă cel puțin %{number} împuterniciți cu cheie publică. + invalid: A apărut o eroare la organizarea acestor alegeri + no_trustees: Nu există împuterniciți configurați pentru acest spațiu participativ + not_used_trustee: "(nu este folosit)" + public_key: + 'false': nu are o cheie publică + 'true': are o cheie publică + requirements: + max_selections: Toate întrebările au o valoare corectă pentru maximum de răspunsuri. + minimum_answers: Fiecare întrebare are cel puţin 2 răspunsuri. + trustees_number: Spațiul participativ are cel puțin %{number} împuterniciți cu chei publice. + submit: Configurează alegerile + title: Configurează alegerile + trustees: Împuterniciți pentru alegeri + created: + title: Alegeri create + trustees: Împuterniciți + key_ceremony: + continue: Continuă + processing: Se procesează... + results_published: + answer: Răspuns + not_selected: Nu este selectat + question: Întrebare + result: Rezultat + selected: Selectat + submit: Trimite + title: Rezultate publicate + tally_ended: + answer: Răspuns + not_selected: Neselectate + question: Întrebare + result: Rezultat + selected: Selectate + submit: Publică rezultatele + title: Calculează rezultatele + tally_started: + mark_as_missing: Marcheză drept absent + tally_completion: Procesul va fi finalizat cânt toți împuternicții sunt activi sau marcați drept absenți. Pentru a finaliza procesul sunt necesari cel puțin %{quorum} împuterniciți. + undo_mark_as_missing: Un împuternicit marcat din greșeală drept absent va putea participa înainte de finalizarea procesului. Acesta poate proceda ca de obicei, iar marcarea drept absent va fi ingnorată. + vote: + errors: + time_after: Alegerile sunt încă în desfășurare. Trebuie să aștepți până se încheie (%{end_time}) pentru încheia perioada de votare. + requirements: + time_after: Alegerile s-au terminat. Poți încheia perioada de votare manual, sau se va încheia automat în câteva minute. + submit: Încheie perioada de votare + title: Perioada de vot + vote_ended: + submit: Începe numărarea voturilor + text: Votul s-a terminat. Puteţi începe numărătoarea acum. + title: Perioada de vot s-a încheiat + vote_stats: + no_vote_statistics_yet: Încă nu există statistici despre voturi + title: Statistici Votare + voters: Votanți + votes: Voturi + trustees_participatory_spaces: + actions: + disable: Dezactivează + enable: Luați în considerare + form: + select_user: Selectați utilizatorul + index: + title: Împuterniciți + new: + create: Creează împuternicit + title: Împuternicit nou + admin_log: + election: + create: "%{user_name} a creat algerile %{resource_name} în %{space_name}" + delete: "%{user_name} a șters algerile %{resource_name} în %{space_name}" + end_vote: "%{user_name} a încheiat în Panoul de votare perioada pentru algerilor pentru %{resource_name} din %{space_name}" + publish: "%{user_name} a publicat algerile %{resource_name} în %{space_name}" + publish_results: "%{user_name} a publicat în Panoul de afișare rezultatele alegerilor %{resource_name} din %{space_name}" + report_missing_trustee: "%{user_name} a raportat în Panoul de afișare pe %{trustee_name} drept împuternicit absent în timpul numărătorii voturilor pentru alegerile %{resource_name} din spațiul participativ %{space_name}" + setup: "%{user_name} a creat în Panoul de afișare alegerile %{resource_name} din %{space_name}" + start_key_ceremony: "%{user_name} a început în Panoul de afișare ceremonia cheie pentru alegerile %{resource_name} din %{space_name}" + start_tally: "%{user_name} a început numărarea voturilor în Panoul de afișare pentru alegerile %{resource_name} din %{space_name}" + start_vote: "%{user_name} a început în Panoul de afișare perioada de votare pentru alegerile %{resource_name} din %{space_name}" + unpublish: "%{user_name} a anulat publicarea alegerilor %{resource_name} din %{space_name}" + update: "%{user_name} a actualizat algerile %{resource_name} din %{space_name}" + trustee: + create: "%{user_name} a desemnat utilizatorul %{trustee_user} drept imputericit" + election_m: + badge_name: + finished: Terminat + ongoing: Activ + upcoming: Urmează + end_date: Se termină + footer: + remaining_time: + one: "%{count} oră %{minutes} minute rămase de votat." + few: "%{count} ore %{minutes} minute rămase de votat." + other: "%{count} ore %{minutes} minute rămase pentru a vota." + view: Vizualizare + vote: Votează + label: + date: Date + questions: Întrebări %{count} + start_date: Începe + unspecified: Nu este specificat + elections: + count: + elections_count: + one: "%{count} alegeri" + few: "%{count} alegeri" + other: "%{count} alegeri" + election_log: + chained_hash: Hash chained al acestui mesaj + complete: Finalizate + creation_description: + complete: Alegerile au fost create și sunt configurate cu succes în Panoul de afișare. + not_created: Alegerile nu au fost create încă. + creation_title: Alegeri create + description: Acesta este jurnalul alegerilor unde poţi verifica starea fiecărui pas, de ex. când alegerile au fost create, dacă procesul de numărare a voturilor s-a terminat, şi când alegerile au fost închise. + download: Descarcă + key_ceremony_description: + complete: Ceremonia de generare a cheilor este încheiată. Fiecare împuternicit are chei valide și a descărcat cheile de rezervă necesare. + not_started: Ceremonia de generare a cheilor nu a început încă. + started: Ceremonia de generare a cheilor a început dar nu s-a încheiat încă. + key_ceremony_title: Ceremonia de generare a cheilor + not_available: Încă nu e disponibilă + not_created: Nu este creat + not_ready: Nu este gata + not_started: Nu a început + published: Publicat + results_description: + not_published: Rezultatele nu sunt încă publicate. + published: Rezultatele sunt publicate. + results_title: Rezultate + started: Începute + tally_description: + finished: Procesul de numărare a voturilor s-a încheiat. + not_started: Procesul de numărare a voturilor nu a început încă. + started: Procesul de numărare a voturilor a început. + tally_title: Procesul de numărare al voturilor + title: Jurnal al alegerilor + verifiable_results: + checksum: 'Sumă de control pentru fișierul SHA256:' + description: + ready: 'Aici ai opțiunea de a verifica alegerile. Întâi, trebuie să descarci fișierul și să te asiguri că nu a fost corupt. Pentru a face asta, execută următoarea comandă și verifică dacă rezultatul corespunde cu suma de control:' + how_to_verify: 'Odată ce ai descărcat fișierul și te-ai asigurat că este în regulă, poți continua pornind verificatorul universal. Clonează acestă arhivă și, din dosarul rădăcină, rulează comanda următoare:' + title: Verifică rezultatele alegerilor + verify: Verifică alegerile + vote_title: Procesul de votare + filters: + active: Activ + all: Toate + finished: Finalizat + upcoming: Urmează + preview: + available_answers: 'Răspunsuri disponibile:' + description: 'Acestea sunt întrebările pe care le veți găsi în procesul de votare:' + title: Întrebări aferente alegerilor + results: + description: 'Acestea sunt rezultatele votului, pentru fiecare întrebare:' + selected: Selectate + title: Rezultatele alegerilor + show: + action_button: + change_vote: Schimbă-ți votul + vote: Începe să votezi + vote_again: Votează din nou + callout: + already_voted: Ai votat deja pentru aceste alegeri. Îți poți schimba votul sau îl poți verifica. + vote_rejected: Nu am putut verifica votul. Te rugăm să reiei procesul de votare. + election_log: Jurnal al alegerilor + preview: Previzualizare + verify: + already_voted: Ai votat deja? + verify_here: Verifică-ți votul aici. + will_verify: Îți vei putea verifica votul odată cu începerea alegerilor. + voting_period_status: + finished: Votarea a început la %{start_time} și s-a încheiat la %{end_time} + upcoming: Votarea începe la %{start_time} + feedback: + answer: + invalid: A apărut o problemă la trimiterea feedback-ului tău. + success: Feedback trimis cu succes. + models: + answer: + fields: + proposals: Propuneri + title: Titlu + votes: Voturi + election: + fields: + bb_status: Stadiul panoului de afișare + end_time: Sfârșit la + start_time: Începe la + title: Titlu + question: + fields: + answers: Răspunsuri + max_selections: Selecții maxime + title: Titlu + trustees_participatory_space: + fields: + email: E-mail + name: Nume + orders: + label: Ordonează alegerile după + older: Mai vechi + recent: Recente + trustee_zone: + elections: + backup_modal: + title: Copie de rezervă chei pentru %{election} + key_ceremony_steps: + title: Crează chei electorale pentru %{election} + tally_started_steps: + description: Rezultatele acestor alegeri sunt în proces de calculare în Panoul de afișare. Pentru a finaliza procesul este necesară participarea ta drept împuternicit. + keys: + end_tally: Procesul de numărare al voturilor s-a încheiat + tally: + cast: Anunțarea rezultatului votului + share: Împărtășirea rezultatului votului + title: Numărarea voturilor pentru %{election} + menu: + trustee_zone: Zonă împuternicit + no_bulletin_board: + body: Pentru această secţiune este necesar un Panoul de afișare configurat. Contactaţi administratorul pentru mai multe detalii. + title: Ne pare rău, Panoul de afișare nu este configurat încă. + trustees: + show: + elections: + list: + action_required: + 'false': 'Nr' + name: Acțiune necesară? + 'true': Efectuează acțiunea + bb_status: Stare + election: Alegeri + voting_period: Perioada de vot + title: Alegeri + identification_keys: + cancel: Anulează + generate: Generează chei de identificare + generate_error: A apărut o eroare la generarea cheilor de identificare. + generate_legend: Trebuie să generați o pereche de chei pentru a participa la alegeri drept împuternicit. + generate_legend_1: După ce apăsați pe buton, ar trebui să descărcați fișierul cu tastele de identificare generate. + generate_legend_2: Copiaţi fişierul descărcat pe un dispozitiv USB curat + generate_legend_4: Faceți o altă copie a fișierului pe un alt dispozitiv extern și păstrați-l într-un loc foarte sigur. + submit: Trimiteți + submit_title: Trimite cheia publică de identificare + title: Chei de identificare împuternicit + upload: Încărcați-vă cheile de identificare + not_supported_browser_title: Actualizează browser-ul pentru a acționa ca împuternicit + update: + success: Cheia publică de identificare a fost stocată cu succes. + votes: + ballot_decision: + audit: (scrutin de audit) + back: Începe procesul de votare din nou + ballot_hash: 'Identificatorul dvs. de vot este:' + confirm: + answer_number: răspunde la %{number} + confirm: Confirmare + edit: editează + header: Confirmă votul tău + intro: Iată un rezumat al votului pe care urmează să îl exprimați.
    Vă rugăm să confirmați votul sau să editați răspunsurile. + nota_option: Necompletat + confirmed: + back: Înapoi la alegeri + experience: Cum a fost experiența ta? + feedback: Oferă-ne un feedback + header: Votare confirmată + lead: Votul tău a fost adăugat! + text: 'Poți verifica dacă votul tău a fost adăugat cu succes în urna de vot cu următorul identificator: %{e_vote_poll_id}' + verify_link: Pentru a verifica, copiați identificatorul și lipiți-l pe pagina pentru verificarea votului + failed: + header: Vot eșuat + header: + confirm: Confirmă votul tău + messages: + invalid_token: Sesiunea din cabina de vot nu este validă. Încercați să votați din nou. + not_allowed: Nu aveţi voie să votaţi asupra acestor alegeri în acest moment. + modal: + close: Inchide + proposal_header: 'Propuneri:' + new: + more_information: Mai multe informații + preview_alert: Aceasta este o previzualizare a cabinei de votare. + question_steps: Întrebarea %{current_step} din %{total_steps} + selections: "%{selected} din %{max_selections}
    selecții" + onboarding_modal: + create_account: Creează cont + no_account: Nu, mulțumesc. + voting_step: + back: Înapoi + continue: Următoarea + events: + elections: + election_published: + email_intro: 'Alegerea %{resource_title} este acum activă pentru %{participatory_space_title}. O poți vedea de pe această pagină:' + email_outro: Ați primit această notificare deoarece urmăriți %{participatory_space_title}. Puteți înceta să primiți notificări urmând linkul anterior. + email_subject: Alegerea %{resource_title} este acum activă pentru %{participatory_space_title}. + notification_title: Alegerile %{resource_title} sunt acum active pentru %{participatory_space_title}. + trustees: + new_election: + email_intro: Ai fost adăugat drept împuternicit pentru alegerile %{resource_title}. + new_trustee: + email_intro: Un administrator te-a adăugat drept împuternicit pentru %{resource_name}. Ar trebui să își generei cheia de verificare publică în zona ta dedicată rolului de împuternicit + email_subject: Ești împuternicit pentru %{resource_name}. + statistics: + elections_count: Alegeri + votings: + admin: + ballot_styles: + index: + actions: + confirm_destroy: Sigur dorești asta? + destroy: Șterge + edit: Editare + title: Acțiuni + associated_census_data: Intrări asociate recensământului + new: + create: Creează + title: Creează stil de vot + index: + published: Publicate + menu: + votings_submenu: + attachment_collections: Dosare + attachment_files: Fișiere + components: Componente + landing_page: Pagina principală + monitoring_committee: Comitetul de monitorizare + monitoring_committee_members: Membri + monitoring_committee_verify_elections: Verifică alegerile + models: + polling_station: + fields: + title: Titlu + voting: + fields: + title: Titlu + monitoring_committee_election_results: + actions: + title: Acțiuni + results: + bulletin_board: Panoul de afișare + update: + rejected: Pulicarea rezultatelor a fost respinsă de către Panoul de afișare. Încearcă din nou sau contactează un administrator. + monitoring_committee_members: + form: + select_user: Caută după nume, e-mail sau pseudonim + new: + create: Creează + title: Creează un membru al comitetului de monitorizare + monitoring_committee_polling_station_closures: + actions: + title: Acțiuni + validate: Validează + monitoring_committee_verify_elections: + index: + how_to_run_verifier: 'Odată ce ai descărcat fișierul și te-ai asigurat că este în regulă, poți continua pornind verificatorul universal. Clonează acestă arhivă și, din dosarul rădăcină, rulează comanda următoare:' + title: Alegeri + polling_officers: + form: + select_user: Caută după nume, e-mail sau pseudonim + new: + create: Creează + title: Creează verificator al scrutinului + polling_stations: + new: + create: Creează + title: Creează stație de votare + votings: + actions: + confirm_destroy: Sigur dorești asta? + form: + title: Titlu + new: + create: Creează + census: + admin: + census: + create: + invalid: S-a produs o eroare la încărcarea recensământului, vă rugăm să încercați din nou mai târziu. + destroy: + error: S-a produs o eroare la ștergerea recensământului, vă rugăm să încercați din nou mai târziu. + new: + has_ballot_styles_message: Ai configurat Ballot Stiluri. Te rugăm să te asiguri că câmpul "%{ballot_style_code_header}" din CSV corespunde codului dorit al Stilului Balt. + missing_ballot_styles_message: 'Încă nu există un stil de votare pentru acest vot. Dacă doriți să aveți întrebări condiționate (i..: prezintă alegătorului întrebări diferite, în funcţie de exemplu: districtul/regiunea de reședință), trebuie să setezi Ballot Styles înainte de importul recensământului. Dacă doriţi să le adresaţi tuturor alegătorilor aceleaşi întrebări, puteţi continua cu procedura de import de recensământ.' + title: Creează recensământul + upload_info: + csv_example_with_ballot_style: 'Un exemplu de fișier cu stiluri de vot:' + csv_example_without_ballot_style: 'Un exemplu de fișier fără stiluri de vot:' + csv_header_after: Nu include ultimul câmp ("%{ballot_style_code_header}") dacă nu aveţi nevoie de stiluri de vot/întrebări condiţionale + csv_header_before: 'Fișierul de recensământ trebuie să fie un fișier CSV cu următorul antet:' + document_types: + passport: Pașaport + monitoring_committee_members: + actions: + confirm_destroy: Sigur dorești asta? + title: Acțiuni + polling_officer_zone: + closures: + new: + modal_ballots_count_error: + text_area_placeholder: Te rugăm să îți scrii mesajul + in_person_votes: + in_person_form: + census_not_present_description: Trebuie mers la biroul de reclamații pentru recensământ sau să contacteze echipa de suport. + update: + success: + rejected: Votul nu a fost acceptat de către Panoul de afișare. Te rugăm contactează administratorul de sistem. + verify_document: + title: 'Verifică dacă următoarele date sunt corecte:' + polling_officers: + index: + polling_officer_role_description: Ați fost desemnat să acționați în calitate de verificator al scrutinului (președinte sau manager) în unele dintre alegerile celebrate în cadrul acestei platforme. + polling_station: + no_polling_stations: Nu ești încă atribuit niciunei secții de votare. + role: Rolul tău + voting: Votare + polling_officers: + actions: + confirm_destroy: Sigur dorești asta? + title: Acțiuni + polling_stations: + actions: + confirm_destroy: Sigur dorești asta? + title: Acțiuni + votings: + check_census: + check_status: Verifică starea + form_title: 'Completează următorul formular pentru a verifica datele recensământului:' + title: Pot vota? + check_fields: + date_of_birth: Data naşterii + day: Zi + day_placeholder: ZZ + document_number: Numărul documentului + document_number_placeholder: Număr ID + month: Lună + month_placeholder: LL + postal_code: Cod poştal + postal_code_placeholder: Cod poștal + select: Selectaţi tipul documentului + year: Anul + year_placeholder: AAAA + elections_log: + description: Jurnalul alegerilor îți va arăta toate informațiile relevante aferente fiecărui proces de vot. De exemplu, rezultatul numărării voturilor și dacă acesstea sunt deja publicate. Apasă pe alegerile despre care vrei să aflii mai multe detalii. + login: + access_code: Cod de acces + access_code_placeholder: Cod de acces + ask_for_a_new_one: Cere unul nou. + form_title: 'Completați următorul formular pentru a accesa votul:' + start_voting: Începe să votezi + title: Mă identifică cu datele mele de recensământ de vot diff --git a/decidim-elections/config/locales/ru.yml b/decidim-elections/config/locales/ru.yml new file mode 100644 index 00000000..ddc9d1e3 --- /dev/null +++ b/decidim-elections/config/locales/ru.yml @@ -0,0 +1 @@ +ru: diff --git a/decidim-elections/config/locales/si-LK.yml b/decidim-elections/config/locales/si-LK.yml new file mode 100644 index 00000000..b0b50956 --- /dev/null +++ b/decidim-elections/config/locales/si-LK.yml @@ -0,0 +1 @@ +si: diff --git a/decidim-elections/config/locales/sk.yml b/decidim-elections/config/locales/sk.yml new file mode 100644 index 00000000..f634a028 --- /dev/null +++ b/decidim-elections/config/locales/sk.yml @@ -0,0 +1 @@ +sk: diff --git a/decidim-elections/config/locales/sl.yml b/decidim-elections/config/locales/sl.yml new file mode 100644 index 00000000..43488193 --- /dev/null +++ b/decidim-elections/config/locales/sl.yml @@ -0,0 +1,5 @@ +sl: + activemodel: + attributes: + answer: + description: Opis diff --git a/decidim-elections/config/locales/so-SO.yml b/decidim-elections/config/locales/so-SO.yml new file mode 100644 index 00000000..11720879 --- /dev/null +++ b/decidim-elections/config/locales/so-SO.yml @@ -0,0 +1 @@ +so: diff --git a/decidim-elections/config/locales/sq-AL.yml b/decidim-elections/config/locales/sq-AL.yml new file mode 100644 index 00000000..44ddadc9 --- /dev/null +++ b/decidim-elections/config/locales/sq-AL.yml @@ -0,0 +1 @@ +sq: diff --git a/decidim-elections/config/locales/sr-CS.yml b/decidim-elections/config/locales/sr-CS.yml new file mode 100644 index 00000000..9e26af81 --- /dev/null +++ b/decidim-elections/config/locales/sr-CS.yml @@ -0,0 +1 @@ +sr: diff --git a/decidim-elections/config/locales/sv.yml b/decidim-elections/config/locales/sv.yml new file mode 100644 index 00000000..c1a6fd93 --- /dev/null +++ b/decidim-elections/config/locales/sv.yml @@ -0,0 +1,722 @@ +sv: + activemodel: + attributes: + answer: + description: Beskrivning + image: Bild + proposals: Relaterade förslag + title: Titel + ballot_style: + code: Kod + election: + description: Beskrivning + end_time: Omröstningen slutar vid + start_time: Röstningsstart vid + title: Titel + monitoring_committee_member: + email: E-post + name: Namn + polling_officer: + email: E-post + name: Namn + polling_station: + address: Adress + location: Plats + location_hints: Detaljer om platsen + polling_station_managers: Koordinatorer + polling_station_president_id: Ordförande + title: Titel + question: + max_selections: Maximalt antal val + title: Titel + voting: + banner_image: Bannerbild + end_time: Omröstningen avslutas + promoted: Annonserad + scope_id: Omfång + start_time: Omröstningen börjar + title: Titel + voting_type: Rösttyp + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Måste återknytas + election: + attributes: + attachment: + needs_to_be_reattached: Måste bifogas igen + activerecord: + models: + decidim/elections/answer: + one: Svar + other: Svar + decidim/elections/election: + one: Val + other: Val + decidim/elections/question: + one: Fråga + other: Frågor + decidim/voting: + one: Omröstning + other: Omröstningar + decidim/votings/census/dataset: + one: Datamängd + other: Datamängder + decidim/votings/census/datum: + one: Datum + other: Datum + decidim/votings/polling_officer: + one: Röstmottagare + other: Röstmottagare + decidim/votings/polling_station: + one: Vallokal + other: Vallokaler + decidim/votings/voting: + one: Omröstning + other: Omröstningar + decidim: + admin: + filters: + officers_assigned_eq: + label: Handläggare + values: + assigned: Tilldelade + unassigned: Inte tilldelad + role_eq: + label: Roll + values: + manager: Koordinator + president: Ordförande + unassigned: Otilldelad + signed_eq: + label: Signerat + values: + 'false': Signerat + 'true': Inte signerat + validated_eq: + label: Bekräftad + values: + 'false': Ej validerad + 'true': Bekräftad + components: + elections: + actions: + vote: Rösta + name: Val + settings: + global: + announcement: Meddelande + step: + announcement: Meddelande + elections: + actions: + confirm_destroy: Är du säker? + destroy: Förstör + edit: Redigera + feedback: Väljarfeedback + import: Importera förslag till svar + manage_answers: Hantera svar + manage_questions: Hantera frågor + manage_steps: Hanterade steg + preview: Förhandsgranska + publish: Publicera + title: Åtgärder + unpublish: Avpublicera + admin: + answers: + edit: + title: Redigera svar + update: Uppdatera svar + index: + title: Svar + new: + create: Skapa svar + title: Nytt svar + not_selected: Inte vald + select: + disable: Avmarkera svar + selected: Valda + elections: + edit: + title: Redigera val + update: Uppdatera val + index: + title: Val + new: + create: Skapa val + title: Nytt val + publish: + success: Valet har publicerats framgångsrikt. + unpublish: + success: Valet har avpublicerats framgångsrikt. + exports: + elections: Val + menu: + trustees: Förvaltare + models: + answer: + name: Svar + proposals_imports: + new: + create: Importera förslag till svar + no_components: Det finns inga andra förslagskomponenter i det här deltagarutrymmet för att importera förslagen till svar. + select_component: Välj en komponent + title: Importera förslag + questions: + edit: + title: Redigera fråga + update: Uppdatera fråga + index: + title: Frågor + new: + create: Skapa fråga + title: Ny fråga + steps: + create_election: + census: Census + not_used_trustee: "(används inte)" + submit: Sätta upp omröstning + title: Sätta upp omröstning + trustees: Omröstningsförvaltare + created: + title: Omröstningen skapades + trustees: Förvaltare + key_ceremony: + continue: Fortsätt + title: Nyckelceremoni + key_ceremony_ended: + title: Redo att börja + processing: Bearbetar... + results_published: + answer: Svar + not_selected: Inte vald + question: Fråga + result: Resultat + selected: Valda + submit: Skicka in + title: Publicerade resultat + tally_ended: + answer: Svar + not_selected: Inte vald + question: Fråga + result: Resultat + selected: Valda + submit: Publicera resultat + tally_started: + continue: Fortsätt + mark_as_missing: Markera som saknad + title: Rösträkning + vote: + submit: Avsluta röstningsperiod + title: Röstningsperiod + vote_ended: + title: Röstningsperiod avslutad + vote_stats: + no_vote_statistics_yet: Ingen röstningsstatistik ännu + title: Röstningsstatistik + voters: Väjare + votes: Röster + trustees_participatory_spaces: + actions: + disable: Avaktivera + enable: Tillåt + form: + select_user: Välj användare + index: + title: Förvaltare + new: + create: Skapa förvaltare + title: Ny förvaltare + connection: + failed: + modal: + close: Stäng + election_m: + badge_name: + finished: Avslutad + ongoing: Aktiv + upcoming: Kommande + end_date: Slutar + footer: + view: Visa + vote: Omröstning + label: + date: Datum + questions: Frågor %{count} + start_date: Startar + unspecified: Ej angivet + elections: + count: + elections_count: + one: "%{count} val" + other: "%{count} val" + election_log: + complete: Fullständigt + creation_title: Omröstningen skapades + download: Ladda ner + key_ceremony_title: Nyckelceremoni + not_available: Ännu inte publicerade + not_created: Inte skapat + not_ready: Inte klart + not_started: Inte påbörjad + published: Publicerat + results_title: Resultat + started: Startad + tally_title: Rösträkning + title: Omröstningslogg + vote_title: Omröstningsprocess + filters: + active: Aktiv + all: Allt + date: Datum + finished: Avslutad + upcoming: Kommande + results: + percentage: "%{count}%" + selected: Valda + title: Omröstningsresultat + votes: + one: "%{count} röst" + other: "%{count} röster" + show: + action_button: + change_vote: Ändra din röst + vote: Starta omröstningen + vote_again: Rösta om + election_log: Omröstningslogg + preview: Förhandsgranska + verify: + already_voted: Redan röstat? + voting_period_status: + finished: Omröstningen började den %{start_time} och slutade den %{end_time} + upcoming: Omröstningen börjar den %{start_time} + models: + answer: + fields: + proposals: Förslag + selected: Valda + title: Titel + votes: Röster + election: + fields: + end_time: Avsluta vid + start_time: Börjar vid + title: Titel + question: + fields: + answers: Svar + max_selections: Max. val + title: Titel + trustees_participatory_space: + fields: + considered: tillåten + email: E-post + inactive: inaktiv + name: Namn + public_key: Offentlig nyckel + status: Status + orders: + label: Sortera omröstningar efter + older: Äldre + recent: Senaste + trustee_zone: + elections: + backup_modal: + download_election_keys: Hämta nycklar + key_ceremony_steps: + back: Tillbaka + keys: + create_election: Nycklar generering + list: + status: Status + task: Uppgift + start: Start + status: + completed: Slutförd + pending: Avvaktande + processing: Bearbetar + tally_started_steps: + back: Tillbaka + list: + status: Status + task: Uppgift + start: Start + status: + completed: Slutförd + pending: Väntande + processing: Bearbetar + menu: + trustee_zone: Förvaltarzon + trustees: + show: + elections: + list: + action_required: + 'false': 'Nej' + name: Åtgärd krävs? + 'true': Utför åtgärd + bb_status: Status + election: Val + voting_period: Röstningsperiod + title: Omröstningar + identification_keys: + cancel: Avbryt + submit: Skicka in + votes: + ballot_decision: + audit: ( Granska valsedel ) + back: Starta om omröstningsprocessen + confirm: + answer_number: svara %{number} + confirm: Bekräfta + edit: redigera + header: Bekräfta din röst + intro: Här är en sammanfattning av den röst som du ska rösta.
    Vänligen bekräfta din röst eller redigera dina svar. + nota_option: Tom + confirmed: + back: Tillbaka till val + experience: Hur var din erfarenhet? + feedback: Ge oss lite feedback + header: Rösta bekräftad + text: 'Du kan kontrollera att din röst har lagts till i röstsedeln med följande identifierare: %{e_vote_poll_id}' + failed: + header: Röst ej framgångsrik + try_again: Försök igen + header: + ballot_decision: Lägg eller granska din röst + confirm: Bekräfta din röst + election: Omröstning + register: Registrera + messages: + not_allowed: Ni får inte rösta om detta val just nu. + modal: + close: Stäng + proposal_header: 'Förslag:' + new: + more_information: Mer information + preview_alert: Detta är en förhandsvisning av röstlängden. + question_steps: Fråga %{current_step} av %{total_steps} + selections: "%{selected} av %{max_selections}
    val" + onboarding_modal: + create_account: Skapa konto + no_account: Nej tack. + verify: + content: + heading: Verifiera din röst + error: + header: Hittade inga omröstningar! + form: + submit: Kontrollera + header: + title: Verifiera din röst + success: + header: Röst identifierad! + voting_step: + back: Tillbaka + continue: Nästa + events: + elections: + election_published: + email_intro: 'Valet %{resource_title} är nu aktivt för %{participatory_space_title}. Du kan se det från denna sida:' + email_outro: Du har fått det här meddelandet eftersom du följer %{participatory_space_title}. Du kan sluta ta emot meddelanden via föregående länk. + email_subject: Valet %{resource_title} är nu aktivt för %{participatory_space_title}. + notification_title: Valet %{resource_title} är nu aktivt för %{participatory_space_title}. + help: + participatory_spaces: + votings: + contextual: "

    En omröstning är ett utrymme där du kan ställa en tydlig fråga till alla personer som bildar en organisation, där du kan be att få medverka i omröstningen, samt sätta igång och hantera debatten för eller emot ett svar. När omröstningsdatumet anländer kan du rösta och publicera resultatet.

    Exempel: Omröstningar kan handla om nästan allting som påverkar en organisation: till exempel att ändra organisationens namn eller logotyp med flera alternativa förslag, rösta ja eller nej om att bli en del av en större organisation, anta eller avvisa en ny strategisk plan eller resultatet från en arbetsgrupp, eller besluta om förtroendevalda ska kunna sitta högst en, två eller tre mandatperioder.

    \n" + page: "

    En omröstning är ett utrymme där du kan ställa en tydlig fråga till alla personer som bildar en organisation, där du kan be att få medverka i omröstningen, samt sätta igång och hantera debatten för eller emot ett svar. När omröstningsdatumet anländer kan du rösta och publicera resultatet.

    Exempel: Omröstningar kan handla om nästan allting som påverkar en organisation: till exempel att ändra organisationens namn eller logotyp med flera alternativa förslag, rösta ja eller nej om att bli en del av en större organisation, anta eller avvisa en ny strategisk plan eller resultatet från en arbetsgrupp, eller besluta om förtroendevalda ska kunna sitta högst en, två eller tre mandatperioder.

    \n" + title: Vad är omröstningar? + menu: + votings: Omröstningar + statistics: + elections_count: Omröstningar + votings_count: Omröstningar + votings: + admin: + ballot_styles: + edit: + title: Redigera valtyp + update: Uppdatera + form: + election: Omröstning + index: + actions: + confirm_destroy: Är du säker? + destroy: Radera + edit: Redigera + title: Åtgärder + title: Valtyper + new: + create: Skapa + content_blocks: + header: + name: Omröstningsbanner + metrics: + name: Omröstningsmetrik + index: + published: Publicerad + menu: + votings: Omröstningar + votings_submenu: + attachment_collections: Mappar + attachment_files: Filer + attachments: Bilagor + ballot_styles: Valtyper + census: Census + components: Komponenter + landing_page: Landningssida + monitoring_committee_members: Medlemmar + monitoring_committee_verify_elections: Verifiera omröstningar + polling_officers: Röstmottagare + polling_stations: Vallokaler + models: + ballot_style: + fields: + code: Kod + monitoring_committee_member: + fields: + email: E-post + name: Namn + polling_officer: + fields: + email: E-post + name: Namn + polling_station: + fields: + address: Adress + polling_station_managers: Koordinatorer + polling_station_president: Ordförande + title: Titel + voting: + fields: + created_at: Skapad den + published: Publicerad + title: Titel + monitoring_committee_election_results: + actions: + title: Åtgärder + view: Visa + results: + bulletin_board: Anslagstavla + result_types: + blank_answers: Tomma svar + blank_ballots: Tomma röstsedlar + null_ballots: Ogiltiga röstsedlar + total_ballots: Totalt antal röstsedlar + valid_ballots: Giltiga röstsedlar + selected: Valda + totals: Totalt + show: + change_election: Ändra omröstning + publish_results: Publicera resultat + monitoring_committee_members: + form: + user_type: Typ av deltagare + new: + create: Skapa + monitoring_committee_polling_station_closures: + actions: + title: Åtgärder + validate: Validera + view: Visa + closures: + change_election: Ändra omröstning + signed: Signerat? + validated: Bekräftad? + edit: + monitoring_committee_notes: Anmärkningar + monitoring_committee_verify_elections: + index: + download: Ladda ner + not_available: Ännu inte tillgängliga + title: Omröstningar + polling_officers: + form: + user_type: Typ av deltagare + index: + role_manager: chef + role_president: ordförande + title: Röstmottagare + new: + create: Skapa + polling_stations: + index: + title: Vallokaler + new: + create: Skapa + titles: + votings: Omröstningar + votings: + actions: + confirm_destroy: Är du säker? + destroy: Ta bort + new_voting: Ny omröstningsutrymme + edit: + update: Uppdatera + form: + banner_image: Bannerbild + promoted: Annonserad + slug: Slug + title: Titel + voting_type: + hybrid: Hybrid + in_person: Fysisk + online: Online + new: + create: Skapa + title: Ny omröstning + census: + admin: + census: + new: + submit: Skicka in CSV + title: Skapa census + document_types: + passport: Pass + export_mailer: + access_codes_export: + download: Ladda ner + content_blocks: + landing_page: + polling_stations: + heading: Vallokaler + monitoring_committee_members: + actions: + confirm_destroy: Är du säker? + destroy: Radera + new: Ny medlem + title: Åtgärder + pages: + home: + highlighted_votings: + active_spaces: Activa omröstningar + polling_officer_zone: + closures: + edit: + modal_ballots_results_count_error: + close_modal: Stäng + save_recount: Spara omräkning + total_ballots: Totalt antal röstsedlar + new: + election: 'Omröstning:' + heading: Omräkning + modal_ballots_count_error: + total_ballots: 'Totalt antal röstsedlar:' + total_people: 'Totala människor:' + polling_station: 'Vallokal:' + total_ballots_count: Antal röstsedlar + show: + heading: Omräkning + sign: + cancel: Avbryta + confirm: Fortsätt + in_person_votes: + in_person_form: + date_of_birth: Födelsedatum + day: Dag + day_placeholder: DD + document_number: Dokumentnummer + document_number_placeholder: ID-nummer + month: Månad + month_placeholder: MM + validate_document: Validera dokument + year: År + year_placeholder: ÅÅÅÅ + verify_document: + name: Namn + verify_document: Verifiera dokument + polling_officers: + index: + polling_station: + address: Adress + count_votes: Räkna röster + election: Omröstning + identify_person: Identifiera en person + name: Namn + role: Din roll + show_closure: Visa avslutning + title: Vallokaler + voting: Omröstning + polling_officers: + actions: + confirm_destroy: Är du säker? + destroy: Radera + title: Åtgärder + roles: + manager: Koordinator + president: Ordförande + unassigned: Otilldelad + polling_station_closure_recount: + signed: Signerat + total_ballots: 'Totalt antal röstsedlar:' + polling_stations: + actions: + confirm_destroy: Är du säker? + destroy: Radera + edit: Redigera + title: Åtgärder + votings: + access_code_modal: + title: Få behörighetskod + check_census: + check_status: Kontrollera status + success: + access_link: via e-post. + access_link_with_sms: via SMS eller e-post. + title: Kan jag rösta? + check_fields: + date_of_birth: Födelsedatum + day: Dag + day_placeholder: DD + document_number: Dokumentnummer + document_number_placeholder: ID-nummer + document_type: Dokumenttyp + month: Månad + month_placeholder: MM + postal_code: Postnummer + year: År + year_placeholder: ÅÅÅÅ + count: + title: + one: "%{count} omröstning" + other: "%{count} omröstningar" + elections_log: + title: Omröstningslogg + filters: + active: Aktiva + all: Alla + date: Datum + finished: Avslutade + search: Sök + upcoming: Kommande + index: + title: Omröstningar + login: + access_code: Behörighetskod + access_code_placeholder: Behörighetskod + start_voting: Starta omröstningen + orders: + label: 'Sortera omröstningar efter:' + random: Slumpmässig + recent: Senaste + votings_m: + badge_name: + finished: Avslutade + ongoing: Pågående + upcoming: Kommande + unspecified: Ej angivet + voting_type: + hybrid: Hybrid + in_person: Fysisk + online: Online + layouts: + decidim: + voting_navigation: + check_census: Kan jag rösta? + election_log: Omröstningslogg + votings: + promoted_voting: + vote: Omröstning diff --git a/decidim-elections/config/locales/sw-KE.yml b/decidim-elections/config/locales/sw-KE.yml new file mode 100644 index 00000000..7bf73465 --- /dev/null +++ b/decidim-elections/config/locales/sw-KE.yml @@ -0,0 +1 @@ +sw: diff --git a/decidim-elections/config/locales/th-TH.yml b/decidim-elections/config/locales/th-TH.yml new file mode 100644 index 00000000..a4431912 --- /dev/null +++ b/decidim-elections/config/locales/th-TH.yml @@ -0,0 +1 @@ +th: diff --git a/decidim-elections/config/locales/ti-ER.yml b/decidim-elections/config/locales/ti-ER.yml new file mode 100644 index 00000000..39bcd229 --- /dev/null +++ b/decidim-elections/config/locales/ti-ER.yml @@ -0,0 +1 @@ +ti: diff --git a/decidim-elections/config/locales/tr-TR.yml b/decidim-elections/config/locales/tr-TR.yml new file mode 100644 index 00000000..29c58c96 --- /dev/null +++ b/decidim-elections/config/locales/tr-TR.yml @@ -0,0 +1,322 @@ +tr: + activemodel: + attributes: + answer: + description: Açıklama + image: Resim + proposals: İlgili teklifler + title: Başlık + election: + description: Açıklama + end_time: Oylama bitişi + start_time: Oylama başlangıcı + title: Başlık + question: + max_selections: Maksimum seçim sayısı + min_selections: Yukarıdakilerden hiçbiri + title: Başlık + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: Yeniden bağlanması gerekiyor + election: + attributes: + attachment: + needs_to_be_reattached: Yeniden bağlanması gerekiyor + activerecord: + models: + decidim/elections/answer: + one: Cevap + other: Cevaplar + decidim/elections/election: + one: Seçim + other: Seçimler + decidim/elections/question: + one: Soru + other: Sorular + decidim: + admin: + voting_publications: + create: + error: Bu oylama yayınlanırken bir sorun oluştu. + success: Soru/oylama başarıyla yayınlandı. + destroy: + error: Bu soru/Oylama yayından kaldırılırken bir hata oluştu. + success: Soru başarıyla yayından kaldırıldı + components: + elections: + actions: + vote: Oy ver + name: Seçimler + settings: + global: + announcement: Duyuru + step: + announcement: Duyuru + elections: + actions: + confirm_destroy: Emin misin? + destroy: Yok et + edit: Düzenle + feedback: Seçmen geri bildirimi + import: Teklifleri yanıtlara aktarın + manage_answers: Cevapları yönet + manage_questions: Soruları yönet + new_answer: Yeni Yanıt + new_election: Yeni seçim + new_question: Yeni soru + new_trustee: Yeni Vekil + preview: Önizleme + publish: Yayınla + title: Eylemler + unpublish: Yayımdan Kaldır + admin: + answers: + edit: + title: Cevabı düzenle + update: Cevabı güncelle + index: + title: Cevaplar + new: + create: Cevap oluştur + title: Yeni cevap + select: + disable: Cevabın seçimini kaldır + enable: Cevabı seçildi olarak işaretle + elections: + edit: + title: Seçimi düzenle + update: Seçimi güncelle + index: + no_bulletin_board: Bu modülü kullanmak için gerekli olan yapılandırılmış bir Bulletin Board sunucusu yok. Bu görev Sistem Yöneticisi tarafından yapılmalıdır. + title: Seçimler + new: + create: Seçim oluştur + title: Yeni seçim + publish: + success: Seçim başarıyla yayınlandı. + unpublish: + success: Seçim başarılı bir şekilde yayınlanmadı. + exports: + elections: Seçimler + feedback_form_answers: Cevapların geri bildirimi + models: + answer: + name: Cevap + proposals_imports: + new: + create: Teklifleri yanıtlara aktarın + no_components: Bu katılımcı alanda, teklifleri yanıtlara aktarmak için başka teklif bileşeni yoktur. + select_component: Lütfen modülü bileşeni seçin + title: Teklifleri içe aktar + questions: + edit: + title: Soruyu Düzenle + update: Soruyu güncelle + index: + title: Sorular + new: + create: Soru Oluştur + title: Yeni soru + trustees_participatory_spaces: + actions: + disable: Devre dışı + enable: Dikkate al + form: + select_user: Kullanıcı seçin + index: + title: Vekiller + new: + create: Vekil Oluşturun + title: Yeni Vekil + election_m: + badge_name: + finished: Bitti + ongoing: Aktif + upcoming: Yakında + end_date: Son + footer: + view: Görünüm + vote: Seçim + label: + date: Tarihler + questions: '%{count} soru' + start_date: Başlangıç + unspecified: Belirtilmemiş + elections: + count: + elections_count: + one: "%{count} seçim" + other: "%{count} seçim" + filters: + active: Aktif + all: Tümü + finished: Bitti + upcoming: Yakında + preview: + title: Seçim soruları + results: + selected: Seçili + title: Seçim sonuçları + votes: + one: "%{count} oy" + other: "%{count} oy" + show: + preview: Önizleme + voting_period_status: + finished: Oylama %{start_time} tarihinde başladı ve %{end_time} tarihinde sona erdi + upcoming: Oylama %{start_time} tarihinde başlıyor + feedback: + answer: + success: Geri bildirim başarıyla gönderildi. + models: + answer: + fields: + proposals: Teklifler + title: Başlık + election: + fields: + end_time: Bitiyor + start_time: Başlıyor + title: Başlık + question: + fields: + answers: Cevaplar + max_selections: Maks. Alan sayısı seçimler + title: Başlık + trustees_participatory_space: + fields: + considered: dikkate al + email: E-posta + inactive: aktif değil + name: Adı + notification: Bildirim gönderildi + public_key: Herkese açık anahtar + status: Statü + orders: + label: Seçimleri şuna göre sırala + older: Daha eski + recent: En son + trustee_zone: + menu: + trustee_zone: Vekil bölgesi + trustees: + show: + elections: + list: + election: Seçim + voting_period: Oylama süresi + title: Seçimler + identification_keys: + cancel: İptal Et + generate: Kimlik anahtarları oluşturun + generate_error: Kimlik anahtarları oluşturulurken bir hata meydana geldi. + generate_legend: Vekil olarak seçimlere katılmak için bir kimlik anahtarı çifti oluşturmanız gerekir. + generate_legend_1: Düğmeye bastıktan sonra, oluşturulan tanımlama anahtarları ile dosyayı indirmelisiniz. + generate_legend_2: İndirilen dosyayı temiz bir USB cihazına kopyalayın + generate_legend_4: Dosyanın başka bir kopyasını farklı bir harici cihazda oluşturun ve çok güvenli bir yerde saklayın. + submit: Gönder + submit_title: Genel kimlik anahtarını gönderin + title: Vekil kimlik anahtarları + upload: Kimlik anahtarlarınızı yükleyin + not_supported_browser_title: Vekil kişi olarak hareket etmek için tarayıcıyı yükseltin + update: + success: Tanımlama genel anahtarınız başarıyla saklandı. + votes: + ballot_decision: + audit: Oy kontrolü + confirm: + answer_number: '%{number} cevap' + confirm: Onayla + edit: düzenle + header: Oyunu onayla + intro: İşte kullanmak üzere olduğunuz oyların bir özeti.
    Lütfen oyunuzu onaylayın veya yanıtlarınızı düzenleyin. + nota_option: Boş + confirmed: + back: Seçimlere dön + experience: Şu ana kadarki deneyiminiz nasıl? + feedback: Bize geri bildirim verin + header: Oy onaylandı + text: 'Aşağıdaki tanımlayıcıyla oyunuzun başarıyla oy sandığına eklenip eklenmediğini kontrol edebilirsiniz: %{e_vote_poll_id} ' + header: + ballot_decision: Kullanmak + confirm: Oyunu onayla + election: Seçim + register: Kayıt Olmak + vote_for: Oy vermek + messages: + not_allowed: Şu anda bu seçim için oy kullanmanıza izin verilmiyor. + modal: + close: Kapat + proposal_header: 'Teklifler:' + new: + answer_choices: En fazla %{choices} cevap seçebilirsiniz + more_information: Daha fazla bilgi + nota_option: Yukarıdakilerin hiçbiri + preview_alert: Bu, oylama kabininin bir önizlemesidir. + question_steps: Soru %{current_step}/%{total_steps} + selections: "%{selected}/%{max_selections}
    seçim" + voting_step: + back: Geri + continue: Sonraki + events: + elections: + election_published: + email_intro: '%{resource_title} seçimi artık %{participatory_space_title} için aktif. Bu sayfadan görebilirsiniz:' + email_outro: Bu bildirimi, %{participatory_space_title} adlı kişiyi takip ettiğiniz için aldınız. Önceki bağlantıyı izleyerek bildirim almayı durdurabilirsiniz. + email_subject: '%{resource_title} seçimi artık %{participatory_space_title} için aktif.' + notification_title: %{resource_title} seçimi artık %{participatory_space_title} için aktif. + trustees: + new_election: + email_intro: '%{resource_title} seçimi için vekil olarak eklendiniz.' + new_trustee: + email_intro: Bir yönetici sizi %{resource_name} için güvenilen kişi olarak ekledi. Genel anahtarınızı güvenilen bölgenizde oluşturmalısınız + email_subject: '%{resource_name} için vekil birisiniz.' + participatory_spaces: + related_elections: + see_all: Tüm seçimleri görünüz + votings: + admin: + ballot_styles: + index: + actions: + new: Yeni oylama Yöntemi + content_blocks: + attachments_and_folders: + name: Oylama ekleri ve klasörleri + header: + name: Oylama Başlığı + html_block_1: + name: Oylama html bloğu 1 + html_block_2: + name: Oylama html bloğu 2 + html_block_3: + name: Oylama html bloğu 3 + main_data: + name: Başlık ve Açıklama + metrics: + name: Oylama Metrikleri + polling_stations: + name: Oy kullanma yeri + related_elections: + name: Seçim ve Seçmenler + stats: + name: Oylama istatistikleri + timeline: + name: Oylama Zaman Çizelgesi + pages: + home: + highlighted_votings: + active_spaces: Aktif oylamalar + see_all_spaces: Tüm Oylamaları Görün + polling_officers: + actions: + new: Yeni Oylama Memuru + polling_stations: + actions: + new: Oy verme yeri + votings: + show: + title: Bu Oylama Hakkında diff --git a/decidim-elections/config/locales/uk.yml b/decidim-elections/config/locales/uk.yml new file mode 100644 index 00000000..c256c324 --- /dev/null +++ b/decidim-elections/config/locales/uk.yml @@ -0,0 +1 @@ +uk: diff --git a/decidim-elections/config/locales/val-ES.yml b/decidim-elections/config/locales/val-ES.yml new file mode 100644 index 00000000..fa70518d --- /dev/null +++ b/decidim-elections/config/locales/val-ES.yml @@ -0,0 +1 @@ +val: diff --git a/decidim-elections/config/locales/vi-VN.yml b/decidim-elections/config/locales/vi-VN.yml new file mode 100644 index 00000000..326506f0 --- /dev/null +++ b/decidim-elections/config/locales/vi-VN.yml @@ -0,0 +1 @@ +vi: diff --git a/decidim-elections/config/locales/vi.yml b/decidim-elections/config/locales/vi.yml new file mode 100644 index 00000000..326506f0 --- /dev/null +++ b/decidim-elections/config/locales/vi.yml @@ -0,0 +1 @@ +vi: diff --git a/decidim-elections/config/locales/zh-CN.yml b/decidim-elections/config/locales/zh-CN.yml new file mode 100644 index 00000000..b0c10c1e --- /dev/null +++ b/decidim-elections/config/locales/zh-CN.yml @@ -0,0 +1,182 @@ +zh-CN: + activemodel: + attributes: + answer: + description: 描述 + image: 图片 + proposals: 有关提议 + title: 标题 + election: + description: 描述 + end_time: 投票结束于 + start_time: 投票开始于 + title: 标题 + question: + max_selections: 最大选择数 + min_selections: 上面没有一个选项 + title: 标题 + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: 需要重新连接 + election: + attributes: + attachment: + needs_to_be_reattached: 需要重新连接 + activerecord: + models: + decidim/elections/answer: + other: 答案 + decidim/elections/election: + other: 选举 + decidim/elections/question: + other: 问 题 + decidim: + components: + elections: + actions: + vote: 投票 + name: 选举 + settings: + global: + announcement: 通 知 + step: + announcement: 通 知 + elections: + actions: + confirm_destroy: 您确定吗? + destroy: 破坏的 + edit: 编辑 + import: 导入建议以答案 + preview: 预览 + publish: 发布 + title: 行动 + unpublish: 取消发布 + admin: + answers: + edit: + title: 编辑答案 + update: 更新答案 + index: + title: 答案 + new: + create: 创建答案 + title: 新答案 + elections: + edit: + title: 编辑选择 + update: 更新选择 + index: + no_bulletin_board: 没有配置 公告栏服务器 ,需要使用此模块。此任务应由系统管理员完成。 + title: 选举 + new: + create: 创建选择 + title: 新的选举 + publish: + success: 选举已成功发布。 + unpublish: + success: 选举已成功取消发布。 + models: + answer: + name: 答案 + proposals_imports: + new: + create: 导入建议以答案 + no_components: 在这个参与性空间中,没有其他提案组成部分可以将提案引入答案。 + select_component: 请选择一个组件 + questions: + edit: + title: 编辑问题 + update: 更新问题 + index: + title: 问 题 + new: + create: 创建问题 + title: 新问题 + election_m: + badge_name: + finished: 已完成 + ongoing: 已启用 + upcoming: 即将到来的 + end_date: 结束 + footer: + view: 查看 + vote: 投票 + label: + date: 日期 + questions: 问题 %{count} + start_date: 开始 + unspecified: 未指定 + elections: + count: + elections_count: + other: "%{count} 次选择" + filters: + active: 已启用 + all: 所有的 + finished: 已完成 + upcoming: 即将到来的 + show: + preview: 预览 + voting_period_status: + finished: 投票开始于 %{start_time} ,结束于 %{end_time} + upcoming: 投票开始于 %{start_time} + models: + answer: + fields: + proposals: 建议 + title: 标题 + election: + fields: + end_time: 结束于 + start_time: 开始于 + title: 标题 + question: + fields: + answers: 答案 + max_selections: 最大选择 + title: 标题 + orders: + label: 排序选举方式 + older: 更旧的 + recent: 最近的 + votes: + confirm: + answer_number: 答案 %{number} + confirm: 确认 + edit: 编辑 + header: 确认您的投票 + intro: 这是您要投的票的摘要。
    请确认您的投票或编辑您的答案。 + nota_option: 空白 + confirmed: + back: 回到选举 + experience: 您的体验如何? + feedback: 给我们一些反馈 + header: 已确认投票 + text: '您可以检查您的投票是否已成功地添加到选票箱,其标识符如下: %{e_vote_poll_id}' + header: + confirm: 确认您的投票 + messages: + not_allowed: 此时您无权投票于此次选举。 + modal: + close: 关闭 + proposal_header: '建议:' + new: + answer_choices: 您最多可以选择 %{choices} 个答案 + more_information: 更多信息 + nota_option: 空白/上面没有内容 + preview_alert: 这是投票站的预览。 + question_steps: '%{current_step} 的 %{total_steps} 问题' + selections: "%{selected} 的 %{max_selections}
    选择" + voting_step: + back: 后退 + continue: 下一个 + events: + elections: + election_published: + email_intro: '%{resource_title} 的选择现在对 %{participatory_space_title}生效。您可以从这个页面查看:' + email_outro: 您收到此通知是因为您正在关注 %{participatory_space_title}。您可以停止收到跟随上一个链接的通知。 + email_subject: '%{resource_title} 的选举现在对 %{participatory_space_title} 生效。' + notification_title: %{resource_title} 选区现已启用 %{participatory_space_title}。 diff --git a/decidim-elections/config/locales/zh-TW.yml b/decidim-elections/config/locales/zh-TW.yml new file mode 100644 index 00000000..d83a3ade --- /dev/null +++ b/decidim-elections/config/locales/zh-TW.yml @@ -0,0 +1,1298 @@ +zh-TW: + activemodel: + attributes: + answer: + description: 說明 + image: 圖片 + proposals: 相關提案 + title: 標題 + ballot_style: + code: 代碼 + election: + description: 說明 + end_time: 投票截止時間 + start_time: 投票開始時間 + title: 標題 + monitoring_committee_member: + email: 電子郵件 + name: 名稱 + polling_officer: + email: 電子郵件 + name: 姓名 + polling_station: + address: 地址 + location: 位置 + location_hints: 場所提示 + polling_station_managers: 管理員 + polling_station_president_id: 會長 + title: 標題 + question: + max_selections: 最大選擇數量 + min_selections: 以上皆非選項 + title: 標題 + trustees_participatory_space: + user_id: 參與者 + voting: + banner_image: 橫幅圖片 + census_contact_information: 選民名單聯繫資訊 + description: 說明 + end_time: 投票截止 + introductory_image: 導入圖片 + promoted: 推廣 + scope_id: 範圍 + show_check_census: 顯示「查詢選民名單」頁面 + start_time: 開始投票 + title: 標題 + voting_type: 投票類別 + errors: + models: + answer: + attributes: + attachment: + needs_to_be_reattached: 需要重新附加 + election: + attributes: + attachment: + needs_to_be_reattached: 需要重新附加 + trustee: + attributes: + name: + cant_be_changed: 無法被更改 + public_key: + cant_be_changed: 無法被更改 + voting: + attributes: + voting_type: + inclusion: "%{value} 不是有效的投票類型" + activerecord: + errors: + models: + decidim/votings/polling_officer: + attributes: + presided_polling_station: + president_and_manager: 投票站職員已經是一個投票站主席/經理. + voting: + different_organization: 投票必須在使用者所在的組織中進行. + decidim/votings/polling_station: + attributes: + polling_station_president: + different_voting: 投票站人員必須與投票站在同一個投票中. + models: + decidim/elections/answer: + other: 回答 + decidim/elections/election: + other: 選舉 + decidim/elections/question: + other: 問題 + decidim/voting: + other: 投票 + decidim/votings/census/dataset: + other: 資料集 + decidim/votings/census/datum: + other: 數據 + decidim/votings/polling_officer: + other: 投票事務官 + decidim/votings/polling_station: + other: 投票站 + decidim/votings/voting: + other: 投票 + decidim: + admin: + filters: + officers_assigned_eq: + label: 管理員 + values: + assigned: 已指派 + unassigned: 未指定 + role_eq: + label: 角色 + values: + manager: 管理員 + president: 會長 + unassigned: 未指定 + search_placeholder: + name_or_email_or_nickname_or_presided_station_title_or_managed_station_title_cont: 按姓名/電子郵件/暱稱或投票站搜索%{collection}。 + ? title_or_address_or_manager_name_or_manager_email_or_manager_nickname_or_president_name_or_president_email_or_president_nickname_cont + : 按職稱、地址或官員姓名/電子郵件/暱稱搜索%{collection}。 + signed_eq: + label: 已簽署 + values: + 'false': 已簽署 + 'true': 尚未簽署。 + validated_eq: + label: 已驗證 + values: + 'false': 未驗證 + 'true': 已驗證 + components: + elections: + actions: + vote: 投票 + name: 選舉 + settings: + global: + announcement: 公告 + step: + announcement: 公告 + elections: + actions: + confirm_destroy: 您確定嗎? + destroy: 摧毀 + edit: 編輯 + feedback: 投票者回饋 + import: 將提案匯入回應 + manage_answers: 管理答案 + manage_questions: 管理問題 + manage_steps: 管理步驟 + preview: 預覽 + publish: 發布 + title: 操作 + unpublish: 取消發佈 + admin: + answers: + create: + invalid: 建立這個回覆時出現問題. + success: 回答成功建立. + destroy: + invalid: 刪除這個回答時發生問題. + success: 回答成功刪除. + edit: + title: 編輯回答 + update: 更新了回答 + index: + invalid_max_selections: 你需要再填寫 %{missing_answers} 個答案,以符合最大選擇數。 + title: 回答 + new: + create: 更新了回答 + title: 新回答 + not_selected: 未選擇 + select: + disable: 取消選擇答案 + enable: 將回答標記為已選擇 + invalid: 選擇這個回答時出現問題. + success: 答案成功選擇. + selected: 已選取 + unselect: + invalid: 取消選取此答案時發生問題. + success: 成功取消選取回答. + update: + invalid: 更新答案時發生問題. + success: 回答已成功更新. + elections: + create: + invalid: 創建此選舉時發生問題. + success: 選舉成功建立. + destroy: + invalid: 刪除此選舉時出現問題。 + success: 選舉已成功刪除. + edit: + title: 編輯選舉 + update: 更新選舉 + index: + no_bulletin_board: 沒有配置 公告牌伺服器,這是使用此模組所必需的。這個任務應由系統管理員完成。 + title: 選舉 + new: + create: 建立選舉 + title: 新選舉 + publish: + success: 選舉已成功發布 + unpublish: + success: 選舉已成功取消發布。 + update: + invalid: 更新此選舉時發生問題. + success: 選舉成功更新。 + exports: + elections: 選舉 + feedback_form_answers: 反饋表單回答 + mailers: + trustee_mailer: + subject: 你已被添加為%{resource_name} 的受託人。 + trustee_zone: 帶我到受託人區 + menu: + trustees: 受託人 + models: + answer: + name: 回答 + proposals_imports: + create: + invalid: 匯入提案至答案時發生問題. + success: "%{number} 個提案已成功匯入答案." + new: + create: 將提案匯入回應 + no_components: 在此參與空間中沒有其他提案元件可以將提案匯入回應中。 + select_component: 請選擇一個組件 + title: 導入提案 + questions: + create: + election_started: 選舉已經開始了. + invalid: 建立此問題時出現問題。 + success: 問題成功建立。 + destroy: + invalid: 刪除此問題時出現問題. + success: 問題已成功刪除. + edit: + title: 編輯問題 + update: 更新問題 + index: + title: 問題 + new: + create: 新增問題 + title: 新問題 + update: + invalid: 更新問題時出現問題。 + success: 問題成功更新. + steps: + create_election: + census: 選民名單 + errors: + census_codes_generated: 選民名單的存取代碼未被產生。 + census_frozen: 選民名單的存取代碼未被匯出。 + census_uploaded: 此選舉中沒有上傳選民名單。 + component_published: 此選舉元件未被發布。 + max_selections: 這些問題沒有正確數量的答案選項 + minimum_answers: 問題必須至少有兩個答案選項。 + minimum_questions: 該選舉必須至少有一個問題。 + published: 該選舉未被發布。 + time_before: 開始時間在少於 %{hours} 小時選舉開始時間之前。 + trustees_number: 參與空間必須至少有 %{number} 位具有公鑰的受託人。 + invalid: 設置這次選舉出現了問題 + no_trustees: 這個參與空間沒有設置受託人 + not_used_trustee: "(未使用)" + public_key: + 'false': 沒有公鑰 + 'true': 擁有公鑰 + requirements: + census_codes_generated: 選民名單的存取代碼已生成。 + census_frozen: 選民名單的存取代碼已匯出並凍結。 + census_uploaded: 選民名單已經上傳。 + component_published: 選舉組件已經發布。 + max_selections: 所有問題都有正確的最大答案數量。 + minimum_answers: 每個問題都至少有 2 個答案。 + minimum_questions: 此選舉至少有1個問題。 + published: 選舉已經發布。 + time_before: 設置在選舉開始前至少 %{hours} 小時進行。 + trustees_number: 參與空間中至少有%{number} 位帶有公鑰的受託人。 + submit: 設置選舉 + success: 選舉成功發佈到公告欄. + title: 設置選舉 + trustees: 選舉受託人 + created: + invalid: 啟動金鑰儀式時發生問題. + submit: 啟動金鑰儀式 + success: 啟動金鑰儀式請求已成功發送到公告欄. + title: 選舉已建立。 + trustees: 受託人 + key_ceremony: + continue: 繼續 + title: 金鑰儀式 + key_ceremony_ended: + errors: + time_before: 選舉已準備好開始,您需要等待到開始時間 (%{start_time}) 的 %{hours} 小時前才能開始投票期程。 + invalid: 啟動投票期間時發生問題. + requirements: + time_before: 選舉即將開始。您可以手動開始投票期程,或在開始時間 %{start_time} 之前自動開始。 + submit: 開始投票期程 + success: 啟動投票期間請求已成功發送到公告欄. + title: 準備開始 + processing: 處理中... + results_published: + answer: 回答 + not_selected: 未選擇 + question: 問題 + result: 結果 + selected: 已選取 + submit: 提交 + title: 公佈選舉結果 + tally_ended: + answer: 回答 + not_selected: 未選擇 + question: 問題 + result: 結果 + selected: 已選取 + submit: 發佈結果 + success: 發布結果的請求已成功發送到公告欄. + title: 計算結果 + tally_started: + continue: 繼續 + invalid: 報告缺失的受託人時出現問題. + mark_as_missing: 標記成遺失 + mark_as_missing_description: 所有受託人應參與此過程,但如果有受託人無法參加此過程,您可以將其標記為缺席。 + success: 缺失受託人報告已成功發送至公告欄. + tally_completion: 當所有受託人都參與或被標記為遺失時,程序將完成。至少需要 %{quorum} 名受託人才能完成程序。 + title: 計票過程 + undo_mark_as_missing: 一個被錯誤標記為缺席的受託人在流程完成之前將能夠參與,他們可以照常進行,且缺席標記將被忽略。 + vote: + errors: + time_after: 選舉仍在進行中。您需要等到結束時間 (%{end_time}) 才能結束投票期程。 + invalid: 結束投票期間時發生問題. + requirements: + time_after: 選舉已經結束。您可以手動結束投票期程,或者在幾分鐘內自動結束。 + submit: 結束投票期程 + success: 結束投票期的請求已成功發送到公告欄. + title: 投票期程 + vote_ended: + invalid: 啟動點票出現問題. + submit: 開始計票 + success: 開始計票的請求已成功傳送到公告欄。 + text: 投票已結束。現在您可以開始進行計票。 + title: 投票期程已結束 + vote_stats: + no_vote_statistics_yet: 目前沒有投票統計資料 + title: 投票統計 + voters: 選民 + votes: 投票 + trustees_participatory_spaces: + actions: + disable: 停用 + enable: 考慮 + create: + exists: 此參與空間已存在受託人. + invalid: 建立受託人時出現問題. + success: 受託人成功創建. + delete: + invalid: 移除此受託人時發生問題. + success: 受託人已成功移除. + form: + select_user: 選擇使用者 + index: + title: 受託人 + new: + create: 創建受託人 + title: 新建受託人 + update: + invalid: 更新 %{trustee} 受託人 時發生問題. + success: 受託人 %{trustee} 已成功更新. + admin_log: + election: + create: "%{user_name} 創建了 %{space_name} 的選舉 %{resource_name}" + delete: "%{user_name} 刪除了 %{space_name} 的選舉 %{resource_name}" + end_vote: "%{user_name} 結束了在公告欄上 %{space_name} 的選舉 %{resource_name} 的投票期限" + publish: "%{user_name} 在公告欄上發布了 %{space_name} 的選舉 %{resource_name}" + publish_results: "%{user_name} 在公告欄上發布了 %{space_name} 的選舉 %{resource_name} 的結果" + report_missing_trustee: "%{user_name} 在公告欄報告 %{trustee_name} 在 %{space_name} 中 %{resource_name} 的計票期間中遺失了" + setup: "%{user_name} 在公告欄創建了 %{space_name} 中 %{resource_name} 的選舉。" + start_key_ceremony: "%{user_name} 在公告板上為選舉 %{resource_name} 於 %{space_name} 開始金鑰儀式。" + start_tally: "%{user_name} 開始在公告欄上為選舉 %{resource_name} 於 %{space_name} 進行計票" + start_vote: "%{user_name} 在公告欄上%{space_name} 開始了選舉 %{resource_name} 的投票期程。" + unpublish: "%{user_name} 取消發布了 %{space_name} 的 %{resource_name} 選舉" + update: "%{user_name} 更新了 %{space_name} 的 %{resource_name} 選舉" + trustee: + create: "%{user_name} 指定使用者 %{trustee_user} 為受託人" + connection: + failed: + modal: + close: 關閉 + communication_lost: 抱歉,看起來與投票服務器(公告板)的通信已經中斷。
    可能是網絡連接出現問題或目標服務器過於繁忙。
    您可以稍後重試,如果問題仍然存在,請聯繫支持部門。 + generic_error: 抱歉,發生了未知的錯誤。可能是因為您的瀏覽器不受支援,或者您正在使用不受支援的“隱身”或“私密”模式。 + title: 出了些問題 + election_m: + badge_name: + finished: 已完成 + ongoing: 生效 + upcoming: 即將舉行 + end_date: 結束 + footer: + remaining_time: + other: "%{count} 小時 %{minutes} 分鐘 剩餘投票時間。" + view: 檢視 + vote: 投票 + label: + date: 日期 + questions: 問題 %{count} + start_date: 開始 + unspecified: 未指定 + elections: + count: + elections_count: + other: "%{count} 個選舉" + election_log: + chained_hash: 此訊息的鏈式雜湊值 + complete: 完成 + creation_description: + complete: 該選舉已建立並成功設置在公告欄上。 + not_created: 該選舉尚未建立。 + creation_title: 選舉已建立。 + description: 這是選舉日誌,您可以在其中檢查每個步驟的狀態,例如選舉何時被創建,計票過程是否完成,以及選舉何時關閉。 + download: 下載 + key_ceremony_description: + complete: 金鑰儀式已完成。每個受託人都有有效的金鑰並已下載必要的備份金鑰。 + not_started: 金鑰儀式尚未開始。 + started: 金鑰儀式已經開始但尚未完成。 + key_ceremony_title: 金鑰儀式 + not_available: 尚未可用 + not_created: 未創建 + not_ready: 未準備好 + not_started: 尚未啟動 + published: 已發佈 + results_description: + not_published: 結果尚未公佈。 + published: 結果已公佈。 + results_title: 結果 + started: 已開始 + tally_description: + finished: 計票過程已經完成。 + not_started: 計票過程尚未開始。 + started: 計票過程已經開始。 + tally_title: 計票過程 + title: 選舉紀錄 + verifiable_results: + checksum: '文件SHA256校驗和:' + description: + not_ready: 可驗證的選舉檔案和 SHA256 校驗和尚未提供。一旦結果公佈,您將能夠驗證此選舉。 + ready: '此處您可以驗證選舉結果。首先,您必須下載檔案,確保它沒有被損壞。要這樣做,請運行以下命令,檢查輸出是否與檢查和值匹配:' + how_to_verify: '下載並確認檔案正確無誤後,您可以開始執行通用驗證程式。 請複製 此存儲庫,然後從根文件夾運行以下命令:' + title: 驗證選舉結果 + verifiable_file: '可驗證選舉檔案:' + verify: 驗證選舉 + vote_description: + finished: 投票過程已經完成 + not_started: 投票過程尚未開始。 + started: 投票過程已開始。 + vote_title: 投票過程 + filters: + active: 啟用中 + all: 全部 + date: 日期 + finished: 已完成 + upcoming: 即將舉行 + preview: + available_answers: '可選答案:' + description: '以下是您在投票過程中會遇到的問題:' + title: 選舉問題 + results: + description: '這是投票結果,針對每個問題的結果:' + percentage: "%{count}%" + selected: 已選取 + title: 選舉結果 + votes: + other: "%{count} 票" + show: + action_button: + change_vote: 更改您的選票 + vote: 開始投票 + vote_again: 再次投票 + callout: + already_voted: 您已經在此選舉中投票,您可以更改您的選票或進行驗證。 + pending_vote: 您的投票正在服務器上進行。 + vote_rejected: 無法驗證您的選票。請重新投票。 + election_log: 選舉紀錄 + preview: 預覽 + verify: + already_voted: 已經投票? + verify_here: 在此檢查您的投票。 + will_verify: 在選舉開始後,您將能夠驗證您的投票。 + voting_period_status: + finished: 投票開始時間為 %{start_time},結束時間為 %{end_time} + ongoing: '投票有效期至 %{end_time}' + upcoming: 投票於 %{start_time} 開始 + feedback: + answer: + invalid: 提交反饋時出現問題。 + spam_detected: 回答表單時出現問題。也許您回答太快了,可以再試一次嗎? + success: 反饋成功發送。 + models: + answer: + fields: + proposals: 提案 + selected: 已選取 + title: 標題 + votes: 投票 + election: + fields: + bb_status: 公告板狀態 + end_time: 結束於 + start_time: 開始於 + title: 標題 + verifiable_results_file_hash: 文件SHA256校驗和 + verifiable_results_file_url: 可驗證選舉檔案 + question: + fields: + answers: 回答 + max_selections: 最大選擇數量 + title: 標題 + trustees_participatory_space: + fields: + considered: 考慮 + email: 電子郵件 + inactive: 未啟動的 + name: 姓名 + notification: 通知發送時間 + public_key: 公開金鑰 + status: 狀態 + orders: + label: 按照選舉排序 + older: 較舊的 + recent: 最近 + trustee_zone: + elections: + backup_modal: + description: 這次選舉正在公告欄上創建。對於參與其中的每個受託人來說,創建備份並將其存放在安全的地方非常重要。之後,過程將繼續進行。 + download_election_keys: 下載金鑰 + title: 備份 %{election} 的選舉金鑰 + key_ceremony_steps: + back: 返回 + description: 此選舉正在公告欄中創建。為了完成此過程,需要您作為受託人的參與。 + keys: + create_election: 產生金鑰 + key_ceremony: + joint_election_key: 聯合金鑰生成 + step_1: 金鑰發佈 + list: + status: 狀態 + task: 任務 + process_warning: 一旦開始此過程,請勿離開此頁面,直到過程結束。所有參與者都應該連接以完成此過程,需要幾分鐘的時間。 + start: 開始 + status: + completed: 已完成 + pending: 等待中 + processing: 正在處理 + title: 為 %{election} 創建選舉密鑰 + restore_modal: + description: 選舉公告板已收到您作為受託人在此選舉中的相關資訊。為了繼續進行過程,您需要先上傳上一次備份的檔案。 + title: 還原 %{election} 的選舉金鑰 + upload_election_keys: 上傳選舉金鑰 + tally_started_steps: + back: 返回 + description: 這次選舉的結果正在公告欄中計算中。為了完成這個過程,需要您作為一名受託人進行參與。 + keys: + end_tally: 點票結束 + tally: + cast: 投票計算完成 + share: 計票分享 + list: + status: 狀態 + task: 任務 + process_warning: 一旦開始此過程,請勿離開此頁面,直到過程結束。所有參與者都應該連接以完成此過程,需要幾分鐘的時間。 + start: 開始 + status: + completed: 已完成 + pending: 等待中 + processing: 正在處理 + title: 選舉 %{election} 的點票 + update: + error: 選舉狀態未更新。 + success: '選舉狀態為: %{status}.' + menu: + trustee_zone: 受託人區 + no_bulletin_board: + body: 本區域需要配置公告牌。請聯繫管理員以獲取更多詳細信息。 + title: 對不起,公佈欄尚未配置。 + trustees: + show: + elections: + list: + action_required: + 'false': '否' + name: 需要採取行動嗎? + 'true': 執行操作 + bb_status: 狀態 + election: 選舉 + voting_period: 投票期間 + title: 選舉 + identification_keys: + cancel: 取消 + generate: 生成身份識別金鑰 + generate_error: 生成身份識別金鑰時發生錯誤。 + generate_legend: 您需要生成一對身份驗證金鑰,以便作為受託人參與選舉。 + generate_legend_1: 按下按鈕後,您應該下載生成的身份驗證金鑰文件。 + generate_legend_2: 將下載的文件複製到一個乾淨的 USB 設備上 + generate_legend_3: 請確認您的電腦上沒有該文件的任何副本(例如檢查下載和桌面文件夾)。 + generate_legend_4: 在另一個外部設備上製作文件的另一份副本,並將其存儲在非常安全的地方。 + submit: 提交 + submit_legend: 在遵循上述所有步驟後,請將公開身份證明金鑰發送到伺服器,以完成此流程。 + submit_title: 提交公共身份證明金鑰 + title: 受託人身份證明金鑰 + upload: 上傳您的身分識別金鑰 + upload_error: + invalid_format: 上傳的檔案沒有包含任何識別碼。 + invalid_key: 上傳的檔案中無法載入身份識別金鑰。 + invalid_public_key: 上傳檔案中的身份識別金鑰與系統儲存的公用識別金鑰不匹配。 + upload_legend: 伺服器上有您的公開識別金鑰,但您的瀏覽器仍然沒有。您需要從生成金鑰後創建的備份中,將識別金鑰檔案匯入您的電腦中。 + not_supported_browser_description: 看起來您使用的網頁瀏覽器無法用作受託人的身份。請確保您使用的是最新版本的瀏覽器,或嘗試使用任何最流行的瀏覽器,以便能夠完成您的受託人任務。 + not_supported_browser_title: 升級瀏覽器以作為受託人 + update: + success: 您的識別公鑰已成功存儲。 + votes: + ballot_decision: + audit: '(審核選票)' + back: 重新開始投票流程 + ballot_hash: '您的選票識別碼是:' + cast: 提交選票以完成投票 + header: '選票已加密:請投票或進行選票稽核' + casting: + header: 正在投票中... + text: 您的選票正在投入投票箱中。 + confirm: + answer_number: 答案 %{number} + confirm: 確認 + edit: 編輯 + header: 確認您的選票 + intro: 以下是您即將投票的摘要。
    請確認您的選票或編輯您的回答。 + nota_option: 空白 + confirmed: + back: 回到選舉 + experience: 您的經驗如何? + feedback: 給我們一些回饋 + header: 投票已確認 + lead: 您己投票! + text: '你可以使用以下識別碼檢查你的投票是否已成功添加到投票箱中:%{e_vote_poll_id}' + verify_link: 複製此識別碼,並將其貼到投票驗證頁面以進行檢查。 + create: + error: 投票時發生問題,請再試一次。 + encrypting: + header: 正在加密投票... + text: 正在加密您的選票,以確保您的投票秘密。 + failed: + header: 投票失敗 + lead: 您的投票尚未完成! + text: 出現錯誤,請再試一次。 + try_again: 再試一次 + header: + ballot_decision: 投票或查核您的選票 + confirm: 確認您的選票 + messages: + invalid_token: 您在投票站的會話無效。請再次嘗試投票。 + not_allowed: 您目前無法在此選舉中投票。 + modal: + close: 關閉 + proposal_header: '提案:' + new: + answer_choices: 您最多可以選擇 %{choices} 項答案 + more_information: 更多資訊 + nota_option: 空白/以上皆非 + preview_alert: 這是投票站的預覽畫面。 + question_steps: 第 %{current_step} 問題,共 %{total_steps} 題 + selections: "%{selected} 項已選擇,最多可 %{max_selections}
    項選擇" + onboarding_modal: + create_account: 建立帳戶 + description: 您想在平台上建立新帳戶嗎?您將能夠參與流程並成為組織的積極一員。 + no_account: 不用了,謝謝。 + title: 是第一次使用這個平台嗎? + update: + error: 更新投票狀態時出現問題,請重新投票。 + verify: + content: + heading: 驗證您的投票 + info: 這個驗證程式確認您的投票是否已正確投下並存在選票匣中。這個驗證器會檢查您的投票是否已被正確地投入投票箱中,並使用加密的文字串進行識別。 + error: + header: 找不到投票! + info: 投票代碼在 %{link} 選票匣中未找到,請再試一次。 + form: + back: 回到平台 + submit: 檢查 + vote_identifier: '識別碼:' + vote_identifier_help: 這是您投票後獲得的識別碼(不是進入投票站的代碼)。 + header: + title: 驗證您的投票 + success: + header: 投票已被找到! + info: 你的加密投票在 %{link} 投票箱中。 + voting_step: + back: 返回 + continue: 下一個 + events: + elections: + election_published: + email_intro: '%{participatory_space_title} 現在正在進行%{resource_title} 選舉。您可以從此頁面查看它:' + email_outro: 你收到此通知是因為你正在追蹤 %{participatory_space_title}。你可以透過前面的連結停止接收通知。 + email_subject: '%{participatory_space_title}」現在開始進行「%{resource_title}」選舉。' + notification_title: '「%{participatory_space_title}」現在開始進行「%{resource_title}」選舉。' + trustees: + new_election: + email_intro: 您已被新增為 %{resource_title} 選舉的受託人。 + email_outro: 你收到此通知是因為你已被新增為 %{resource_title} 選舉的受託人。 + new_trustee: + email_intro: 管理員已將您添加為%{resource_name} 的受託人。您應該在您的受託人區域中創建您的公鑰。 + email_outro: 你收到了這個通知,因為你已被新增為 %{resource_name} 的受託人。 + email_subject: 您是 %{resource_name} 的受託人。 + votes: + accepted_votes: + email_intro: '您的投票已經被接受!使用您的投票令牌: %{encrypted_vote_hash},您可以在此處驗證您的投票。' + email_outro: 您收到此通知是因為您已經投票參與了 %{resource_name} 選舉。 + email_subject: 您的選票 %{resource_name} 已被接受。 + notification_title: '您的選票已被接受。使用您的選票驗證碼 %{encrypted_vote_hash},您可以在此處驗證您的選票。' + votings: + polling_officers: + polling_station_assigned: + email_intro: 您已被指派為 %{resource_title}中選舉站點 %{polling_station_name} 的 %{role},您可以從專門的 選舉站點工作人員區域進行管理。 + email_outro: 你收到了此通知,因為你已被指定為 %{polling_station_name} 的 %{role}。 + email_subject: 你是 %{polling_station_name} 的 %{role}。 + notification_title: 您是%{resource_title}投票中 %{polling_station_name} 投票站的%{role}。 + send_access_code: + instruction: '這是您所要求的存取代碼:%{access_code},藉此代碼您將能夠參與 %{voting}。' + subject: 參與 %{voting} 的存取代碼 + help: + participatory_spaces: + votings: + contextual: "

    一個投票是一個空間,讓您向構成一個組織的所有人提出明確的問題,呼籲參與投票,為贊成或反對某個回應引發和整理辯論。當投票日期到來時,您可以投票並發布投票結果。

    例如:投票可以涉及影響組織的幾乎任何方面:例如更改組織的名稱或標誌,提供幾種替代方案,決定是或否成為更大組織的一部分,驗證或拒絕新的戰略計劃或工作小組的結果,或定義職位是否應該保持最多1、2或3個任期。

    \n" + page: "

    一個投票是一個空間,讓您向構成一個組織的所有人提出明確的問題,呼籲參與投票,為贊成或反對某個回應引發和整理辯論。當投票日期到來時,您可以投票並發布投票結果。

    例如:投票可以涉及影響組織的幾乎任何方面:例如更改組織的名稱或標誌,提供幾種替代方案,決定是或否成為更大組織的一部分,驗證或拒絕新的戰略計劃或工作小組的結果,或定義職位是否應該保持最多1、2或3個任期。

    \n" + title: 什麼是投票? + menu: + votings: 投票 + statistics: + elections_count: 選舉 + votings_count: 投票 + votings: + admin: + ballot_styles: + create: + error: 創建此選票樣式時出現問題. + success: 選票樣式已成功建立. + destroy: + invalid: 刪除此選票樣式時發生問題. + success: 選票樣式已成功刪除. + edit: + title: 編輯選票樣式 + update: 更新 + form: + code_help: '提示:代碼是調查和投票樣式之間的關聯。在上傳調查數據時,每個條目都會通過匹配代碼來分配投票樣式。' + election: 選舉 + questions: 此投票樣式的問題 + questions_help: '提示:從選舉組件中選擇問題,以呈現給分配給此投票樣式的選民。' + index: + actions: + confirm_destroy: 您確定嗎? + destroy: 刪除 + edit: 編輯 + title: 操作 + associated_census_data: 相關的戶籍項目 + explanation_callout: 選票樣式指定了選民在投票站將會被提出的問題。在選票樣式中,您可以從此次投票的選舉組件中選擇哪些問題屬於該選票。選票樣式代碼用於將選民從選民名單中與他們將在投票站中展示的選票進行匹配。如果您始終要顯示所有問題,請勿創建任何選票樣式。 + title: 選票樣式 + new: + create: 創建 + title: 創建選票樣式 + update: + invalid: 更新此選票樣式時出現問題. + success: 選票樣式更新成功. + content_blocks: + highlighted_votings: + max_results: 顯示的最大元素數量 + index: + published: 已發佈 + menu: + votings: 投票 + votings_submenu: + attachment_collections: 資料夾 + attachment_files: 檔案 + attachments: 附件 + ballot_styles: 選票樣式 + census: 選民名單 + components: 组件 + landing_page: 登陸頁面 + monitoring_committee: 監管委員會 + monitoring_committee_election_results: 驗證結果 + monitoring_committee_members: 成員 + monitoring_committee_polling_station_closures: 驗證憑證 + monitoring_committee_verify_elections: 驗證選舉 + polling_officers: 投票事務官 + polling_stations: 投票站 + models: + ballot_style: + fields: + code: 代碼 + monitoring_committee_member: + fields: + email: 電子郵件 + name: 名稱 + polling_officer: + fields: + email: 電子郵件 + name: 名稱 + polling_station: 投票站(角色) + polling_station: + fields: + address: 地址 + polling_station_managers: 管理員 + polling_station_president: 主席 + title: 標題 + voting: + fields: + created_at: 建立於 + published: 已發佈 + title: 標題 + monitoring_committee_election_results: + actions: + title: 操作 + view: 檢視 + index: + title: 選擇您要查看結果的選舉 + results: + bulletin_board: 公佈欄 + election_totals: 選舉總計 + polling_stations: 投票站 + result_types: + blank_answers: 空白答案 + blank_ballots: 空白選票數量 + null_ballots: 作廢選票數量 + total_ballots: 總選票數量 + valid_ballots: 有效選票數量 + selected: 已選取 + title: %{election_title} 選舉結果 + totals: 總計 + show: + change_election: 更改選舉 + publish_results: 發佈結果 + publishing: 發佈結果中... + update: + invalid: 發布結果時出現問題. + rejected: 公佈結果被公告欄拒絕。請再試一次或聯繫系統管理員 + success: 結果已成功發佈。 + monitoring_committee_members: + create: + invalid: 在建立監察委員會成員時出現問題. + success: 監察委員會成員成功建立。 + destroy: + invalid: 刪除監察委員會成員時發生問題. + success: 監察委員會成員已成功刪除. + form: + existing_user: 現有參與者 + non_user: 邀請新的參與者 + select_user: 以姓名、電子郵件或暱稱搜尋 + user_type: 參與者類型 + index: + title: 監管委員會 + new: + create: 創建 + title: 創建監察委員會成員 + monitoring_committee_polling_station_closures: + actions: + title: 操作 + validate: 驗證 + view: 檢視 + closures: + change_election: 更改選舉 + signed: 已簽署? + title: 選舉 %{election_title} 的投票站 + validated: 已驗證? + edit: + change_polling_station: 返回投票站 + monitoring_committee_notes: 註記 + monitoring_committee_notes_placeholder: 在此處報告任何事件 + title: 選舉%{election_title}在投票站%{polling_station_title}的結果 + elections: + title: 選擇您想要驗證的選舉 + show: + change_polling_station: 返回投票站 + monitoring_committee_notes: 監察委員會的備註 + validate: + error: 驗證結束時發生問題. + success: 結束已經正確地驗證. + monitoring_committee_verify_elections: + index: + download: 下載 + how_to_checksum: '為確保您下載的檔案未在下載過程中損壞或被篡改,請在您的控制台中執行以下命令,檢查輸出是否與上面報告的檢查碼相符:' + how_to_download: 要驗證一個選舉,請從上面的表格中下載它的可驗證檔案。 + how_to_run_verifier: '下載並確認檔案正確無誤後,您可以開始執行通用驗證程式。 請複製 此存儲庫,然後從根文件夾運行以下命令:' + how_to_title: 如何驗證選舉的有效性 + not_available: 尚未可用 + title: 選舉 + polling_officers: + create: + invalid: 建立投票工作人員時出現問題。 + success: 投票站人員建立成功. + destroy: + invalid: 刪除投票管理員時發生問題. + success: 投票站工作人員已成功刪除. + form: + existing_user: 現有參與者 + non_user: 邀請新的參與者 + select_user: 以姓名、電子郵件或暱稱搜尋 + user_type: 參與者類型 + index: + role_manager: 管理員 + role_president: 主席 + title: 投票事務官 + new: + create: 創建 + title: 建立投票站工作人員 + polling_officers_picker: + choose_polling_officers: 選擇投票站工作人員 + no_polling_officers: 沒有符合您搜尋條件的投票站工作人員或是沒有任何投票站工作人員。 + polling_stations: + create: + invalid: 建立投票站時發生問題。 + success: 投票站創建成功. + destroy: + invalid: 刪除此投票站時出現問題. + success: 投票站已成功刪除. + edit: + title: 編輯投票站 + update: 更新投票站 + form: + address_help: '地址:用於地理編碼器找到位置' + location_help: '位置:向選民發送的消息,暗示投票站的確切位置。' + location_hints_help: '位置提示:額外的資訊。例如:投票站所在建築物的樓層。' + polling_station_managers_help: '投票站管理員:擔任投票站管理員的工作人員。請確保這些管理員已經在投票工作人員中被建立,且他們尚未被指派到其他投票站。' + polling_station_president_help: '投票站主席:擔任投票站主席的工作人員。請確保這位主席已經在投票工作人員中被建立,且他/她尚未被指派到其他投票站。' + select_president: 選擇一位投票事務官擔任投票站主席 + index: + title: 投票站 + new: + create: 創建 + title: 建立投票站 + update: + invalid: 更新此投票站時出現問題. + success: 投票站已成功更新. + titles: + votings: 投票 + votings: + actions: + confirm_destroy: 您確定嗎? + destroy: 摧毀 + new_voting: 新投票空間 + create: + invalid: 創建此投票時出現問題. + success: 投票已成功創建. + edit: + add_election_component: 您沒有為此次投票配置任何選舉。請在『組件』部分中添加它。 + assign_missing_officers: 有一些投票站缺少主席和/或管理人員。請從『投票站』部分分配它們。 + update: 更新 + form: + banner_image: 橫幅圖片 + census_contact_information: 選民名單聯繫資訊 + census_contact_information_help: 此聯絡資訊供欲回報選民清冊問題的投票參與者使用,可填入電子郵件地址、其他網站的聯絡表單、訪客調查等。 + introductory_image: 導入圖片 + promoted: 推廣 + select_a_voting_type: 請選擇一種投票類型。 + show_check_census_help: 是否在公開投票菜單中顯示「我可以投票嗎?」鏈接. + slug: 附址 + title: 標題 + voting_type: + hybrid: 混合 + in_person: 親自 + online: 線上 + voting_type_label: 投票類別 + new: + create: 創建 + title: 新投票 + update: + invalid: 更新此投票時出現問題. + success: 投票已成功更新. + admin_log: + ballot_style: + create: "%{user_name} 在%{space_name} 中建立了代碼為%{ballot_style_code} 的選票樣式" + delete: "%{user_name} 刪除了在空間 %{space_name} 中代碼為 %{ballot_style_code} 的選票樣式" + update: "%{user_name} 更新了在空間 %{space_name} 中代碼為 %{ballot_style_code} 的選票樣式" + census: + create: "%{user_name} 創建了空間 %{space_name} 的選民名單" + delete: "%{user_name} 刪除了空間 %{space_name} 的選民名單" + update: "%{user_name} 已更新了 %{space_name} 空間的選民名冊" + monitoring_committee_member: + create: "%{user_name} 指派使用者 %{monitoring_committee_member_user} 為空間 %{space_name} 的監察委員會成員" + delete: "%{user_name} 取消指派使用者 %{monitoring_committee_member_user} 為空間 %{space_name} 的監察委員會成員" + polling_officer: + create: "%{user_name} 指派使用者 %{polling_officer_user} 為空間 %{space_name} 的投票站管理員" + delete: "%{user_name} 取消指派使用者 %{polling_officer_user} 為空間 %{space_name} 的投票站管理員" + polling_station: + create: "%{user_name} 在空間 %{space_name} 建立投票站 %{resource_name}" + delete: "%{user_name} 刪除了空間 %{space_name} 內的投票站 %{resource_name}" + update: "%{user_name} 更新了空間 %{space_name} 內的投票站 %{resource_name}" + voting: + create: "%{user_name} 創建了名為 %{resource_name} 的投票" + publish: "%{user_name} 發布了名為 %{resource_name} 的投票" + unpublish: "%{user_name} 撤銷了名為 %{resource_name} 的投票的發布" + census: + admin: + census: + create: + invalid: 上傳選民名單時發生錯誤,請稍後再試。 + invalid_csv_header: CSV 標題缺失或不正確- 請仔細閱讀說明. + creating_data: + info_message: "請稍候,已處理了 %{processed_count} 條數據中的 %{raw_count} 條,來自檔案 %{file}(這可能需要幾分鐘的時間)" + delete: + button: 刪除所有戶籍資料 + confirm: 刪除所有戶籍資料將無法還原,您確定要繼續嗎? + destroy: + error: 刪除選民名單資料時發生錯誤,請稍後再試。 + success: 戶籍數據已刪除. + export_access_codes: + button: 匯出投票存取代碼 + callout: 你現在可以開始匯出投票存取代碼。這個動作只能執行一次。一旦啟動匯出,你會收到一封電子郵件,其中包含%{email}的操作指示。 + confirm: 你只能匯出投票存取代碼一次。請確保你可以使用%{email}的郵箱。 + file_not_exist: 此檔案不存在。 + launch_error: 啟動存取代碼匯出時出現問題存取代碼. + launch_success: 已啟動存取代碼匯出。稍後您將收到一封至 %{email} 的電子郵件。 + exporting_access_codes: + info_message: "請稍候,正在準備匯出,您將很快收到發送到 %{email} 的匯出檔案(這可能需要幾分鐘時間)。" + freeze: + callout: 選民名單已凍結,無法修改。 + help_html: | + 戶籍資料已經成功上傳,代碼已經生成並匯出。
    您現在可以開始投票了。
    使用匯出的個人代碼 CSV 檔,使用自己的方式將其分發給戶籍,或啟用「我能投票嗎」選項卡,讓任何人可以使用自己的戶籍資料檢索這個代碼。 + generate_access_codes: + button: 生成投票存取代碼 + callout: 您現在可以開始生成存取代碼。請注意,在生成存取代碼之後,您將無法再修改選民名單資料。 + confirm: 如果繼續,您將無法修改選民名單資料。 + info_message_all: "所有列均已成功匯入自 %{file} 檔案(共 %{data_count} 中的 %{raw_count} )。" + info_message_warn: 請檢查是否有缺失的資料,因為已經建立了 %{data_count} 條記錄,上傳的檔案 %{file} 有 %{raw_count} 行。 + launch_error: 生成存取代碼時出現問題 + launch_success: 已啟動代碼生成。 + start_over: 請刪除當前的選民名單資料,然後使用有效行的適當 CSV 檔案重新開始。 + generating_access_codes: + info_message: "請稍等,正在生成投票存取代碼(這可能需要幾分鐘時間)..." + new: + file_help: + explanation: '檔案指南:' + message_1: 僅允許 CSV (.csv) 檔案。 + message_2: 列之間的分隔符號必須是分號 (";")。 + has_ballot_styles_message: 您已設置選票樣式。請確認 CSV 中的「%{ballot_style_code_header}」欄位與所需的選票樣式代碼相對應。 + info_message: "尚未有選民名單資料。 請使用下面的表單匯入 CSV 檔案以創建選民名單資料。" + missing_ballot_styles_message: '尚未設置此投票的選票樣式。如果您希望有條件的問題(例如:根據選民居住的區域/地區提供不同的問題),您需要在導入選民名單之前設置選票樣式。如果您希望向所有選民提出相同的問題,您可以繼續進行選民名單導入程序。' + submit: 提交CSV + title: 創建選民名冊 + show: + heading: 投票空間選民名冊 + upload_info: + csv_example_with_ballot_style: '帶有 投票樣式的檔案範例:' + csv_example_without_ballot_style: '不帶有 投票樣式的檔案範例:' + csv_header_after: 如果您不需要投票樣式/條件問題,請不要包括最後一個欄位 ("%{ballot_style_code_header}")。 + csv_header_before: '選民名冊檔案必須是一個帶有以下表頭的CSV檔案:' + document_types: + passport: 護照 + export_mailer: + access_codes_export: + click_button: '點擊下一個連結下載存取代碼數據。
    該檔案將可用直到 %{date}。
    您需要7-Zip(Windows)、Keka(MacOS)或PeaZip(Linux)來打開它。密碼:%{password}。' + download: 下載 + subject: 這次投票的訪問代碼導出已經可用。 + vote_flow: + already_voted_in_person: 這個參與者已經在現場投票了,不能再行使選舉權利。 + datum_not_found: 提供的資料與任何選民不符。 + content_blocks: + highlighted_votings: + name: 重點顯示的投票 + landing_page: + polling_stations: + heading: 投票站 + no_polling_stations: 目前尚未設置投票站。 + monitoring_committee_members: + actions: + confirm_destroy: 您確定嗎? + destroy: 刪除 + new: 新成員 + title: 操作 + polling_officer_zone: + closures: + back_to_polling_stations: 返回投票站 + certify: + error: 附加憑證時發生錯誤,請稍後再試。 + heading: 投票重新點算 - 上傳憑證 + info_text: 請上傳選舉結束憑證的照片。 + submit: 上傳憑證 + success: 憑證上傳成功 + create: + error: 創建關閉時出現錯誤,請稍後重試。 + success: 關閉成功創建。 + edit: + heading: 選舉重新點票 - 答案重新點票 + modal_ballots_results_count_error: + close_modal: 關閉 + info_text: 選票總數與信封總數不符。請檢查選票總數。 + title: 選票總數不相加 + save_recount: 保存重新計票 + total_ballots: 總選票數量 + total_blank_ballots: 空白選票總數 + total_null_ballots: 無效選票總數 + total_valid_ballots: 有效選票總數 + new: + election: '選舉:' + heading: 重新計票 + info_text: '請輸入在此投票站重新點數的選票總數(信封數):' + modal_ballots_count_error: + btn_validate_total: 驗證選票總數重新點數 + info_explanation_text: '請檢查選票總數是否正確。如果總數不正確,您必須向監察委員會提供解釋:' + info_text: 總票數(信封) 輸入的總數與在此投票站投票的人數記錄不符。 + message_for_monitoring_committee: 監察委員會留言 + review_recount: 複查重新點票 + text_area_placeholder: 請輸入您的留言 + title: 總記錄不相加 + total_ballots: '總選票數:' + total_people: '總人數:' + polling_station: '投票站:' + submit: 驗證總數 + total_ballots_count: 投票數量 + show: + heading: 重新計票 + sign: + cancel: 取消 + check_box: 我已經檢查過了,與實體選舉結束憑證相同。 + confirm: 好的,繼續 + error: 發生錯誤,請再試一次 + heading: 重新點票 - 簽署結束文件 + info_text: 如果您繼續進行,則無法修改任何資訊,此操作無法撤消。 + submit: 簽署選舉結束 + success: 選舉結束簽署成功。 + update: + error: 更新選舉結束結果時出錯,請稍後再試。 + success: 選舉結束結果已成功更新。 + in_person_votes: + complete_voting: + available_answers: '可選答案:' + census_verified: 這位參與者尚未進行現場投票。 + census_verified_with_online_vote: 這位參與者已經線上投票。如果他們進行現場投票,以前的投票將被作廢,現場投票結果將是最終結果。 + complete_voting: 完成投票 + identify_another: 辨識另一個投票者 + questions_title: '他們有權投票回答以下問題:' + questions_title_voted: '該參與者已經在線上投票,並有權回答以下問題:' + voted: 該投票者已經投票 + create: + error: 投票未被註冊,請再試一次。 + in_person_form: + census_not_present: 該投票者未被列入選民名冊中。 + census_not_present_description: 他們必須前往選民名冊申訴辦公室或聯繫支援中心。 + date_of_birth: 出生日期 + day: 日 + day_placeholder: DD + document_number: 文件編號 + document_number_placeholder: 身分證號 + month: 月 + month_placeholder: MM + select: 選擇文件類型 + title: '選擇文件類型並輸入參與者的文件號碼:' + validate_document: 驗證文件 + year: 年 + year_placeholder: YYYY + new: + back: 返回投票站 + title: 識別和驗證參與者 + show: + back: 返回投票站 + title: 等待現場投票被登記 + update: + error: 登記投票時發生錯誤。請再試一次。 + success: + accepted: 投票已成功登記。 + rejected: 投票未被公告欄接受。請聯繫系統管理員。 + verify_document: + census_present: 這個參與者在選民名冊中列出。 + name: 名稱 + title: '請檢查以下資料是否正確:' + verify_document: 驗證文件 + menu: + polling_officer_zone: 投票事務官區域 + polling_officers: + index: + polling_officer_role_description: 您已被指派在此平台舉行的某些選舉中擔任投票站官員(主席或管理員)。 + polling_station: + address: 地址 + count_votes: 計票 + election: 選舉 + identify_person: 識別一個人 + name: 姓名 + no_polling_stations: 您目前還未被指派到任何投票站。 + role: 您的角色 + show_closure: 查看關閉 + title: 投票站 + voting: 投票 + polling_officers: + actions: + confirm_destroy: 您確定嗎? + destroy: 刪除 + title: 操作 + roles: + manager: 管理員 + president: 主席 + unassigned: 未指定 + polling_station_closure_recount: + nota_option: 空白/以上皆非 + polling_officer_notes: '投票事務官筆記:' + polling_officer_notes_blank: 沒有筆記。 + recount_summary: '重新點票摘要:' + signed: 已簽署 + total_ballots: '總選票數量:' + total_blank_ballots: '空白選票總數:' + total_null_ballots: '無效選票總數:' + total_valid_ballots: '有效選票總數:' + polling_stations: + actions: + confirm_destroy: 您確定嗎? + destroy: 刪除 + edit: 編輯 + title: 操作 + votings: + access_code_modal: + email: 發送至 %{email} 的電子郵件 + info: 您需要一個存取代碼才能參與投票。如果您沒有收到郵寄的存取碼,我們可以發送新的存取碼給您。 + no_email: 沒有可用的電子郵件 + no_sms: 沒有可用的電話號碼 + sms: 透過簡訊傳送至 %{sms} + title: 獲取存取代碼 + check_census: + check_status: 查看狀態 + description: 在這裡,您可以選擇檢查您的戶籍數據,以知道您是否有權參與此次投票。您應該已經擁有存取代碼,但如果您丟失了它,您可以在數據正確時再次請求它。 + error: + info: '請再試一次。如果您認為系統中的資料不正確,您可以在此處報告: %{census_contact_information}。' + title: 您輸入的資料並未列入此次投票的選民名單資料。 + form_title: '填寫以下表格以檢查您的選民名單資料:' + invalid: 檢查選民名單時出現了問題。 + success: + access_link: 透過電子郵件。 + access_link_with_sms: 透過簡訊或電子郵件。 + info: 您應該已經收到郵寄的存取代碼了。如果您沒有收到,您可以在此處申請。 + title: 您的選民名單資料是正確的! + title: 我可以投票嗎? + check_fields: + date_of_birth: 出生日期 + day: 日 + day_placeholder: DD + document_number: 文件編號 + document_number_placeholder: 身分證號 + document_type: 文件類型 + month: 月 + month_placeholder: MM + postal_code: 郵政編碼 + postal_code_placeholder: 郵遞區號 + select: 選擇文件類型 + year: 年 + year_placeholder: YYYY + count: + title: + other: "%{count} 票" + elections_log: + description: 選舉日誌將顯示有關每次投票的所有相關信息。例如,金鑰儀式或計票的狀態,或者結果是否已經發佈。點擊您想要查看日誌信息的選舉。 + title: 選舉紀錄 + filters: + active: 生效 + all: 全部 + date: 日期 + finished: 已完成 + search: 搜尋 + upcoming: 即將舉行 + index: + no_votings: 沒有符合您搜尋條件的投票。 + only_finished: 目前沒有已安排的投票,但您可以在此找到已完成的投票列表。 + title: 投票 + login: + access_code: 存取代碼 + access_code_placeholder: 存取代碼 + ask_for_a_new_one: 要求一個新的投票。 + dont_have_access_code: 沒有存存取代碼? + form_title: '填寫以下表格以訪問投票:' + start_voting: 開始投票 + title: 使用投票選民名單資料進行身分驗證 + no_census_contact_information: 目前尚未提供聯繫資訊。 + orders: + label: '排序投票按:' + random: 隨機 + recent: 最近 + send_access_code: + invalid: 發送存取代碼時出現問題。 + success: 您的存取代碼已成功發送。 + votings_m: + badge_name: + finished: 已完成 + ongoing: 進行中 + upcoming: 即將舉行 + unspecified: 未指定 + voting_type: + hybrid: 混合 + in_person: 親自 + online: 線上 + layouts: + decidim: + voting_navigation: + check_census: 我可以投票嗎? + election_log: 選舉紀錄 + votings: + index: + promoted_votings: 重點顯示的投票 + promoted_voting: + vote: 投票 diff --git a/decidim-elections/db/migrate/20200430083618_create_decidim_elections_elections.rb b/decidim-elections/db/migrate/20200430083618_create_decidim_elections_elections.rb new file mode 100644 index 00000000..095b79a1 --- /dev/null +++ b/decidim-elections/db/migrate/20200430083618_create_decidim_elections_elections.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsElections < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_elections do |t| + t.jsonb :title + t.jsonb :subtitle + t.jsonb :description + t.datetime :start_time + t.datetime :end_time + t.references :decidim_component, index: true + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20200518082327_create_decidim_elections_questions.rb b/decidim-elections/db/migrate/20200518082327_create_decidim_elections_questions.rb new file mode 100644 index 00000000..734729c5 --- /dev/null +++ b/decidim-elections/db/migrate/20200518082327_create_decidim_elections_questions.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsQuestions < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_questions do |t| + t.references :decidim_elections_election, null: false, index: { name: "decidim_elections_elections_questions" } + t.jsonb :title, null: false + t.jsonb :description + t.integer :max_selections, null: false, default: 1 + t.integer :weight, null: false, default: 0 + t.boolean :random_answers_order, null: false, default: true + end + end +end diff --git a/decidim-elections/db/migrate/20200518084144_create_decidim_elections_answers.rb b/decidim-elections/db/migrate/20200518084144_create_decidim_elections_answers.rb new file mode 100644 index 00000000..2047a3be --- /dev/null +++ b/decidim-elections/db/migrate/20200518084144_create_decidim_elections_answers.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsAnswers < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_answers do |t| + t.references :decidim_elections_question, null: false, index: { name: "decidim_elections_questions_answers" } + t.jsonb :title, null: false + t.jsonb :description + t.integer :weight, null: false, default: 0 + end + end +end diff --git a/decidim-elections/db/migrate/20200601141412_add_published_at_to_elections.rb b/decidim-elections/db/migrate/20200601141412_add_published_at_to_elections.rb new file mode 100644 index 00000000..077dac02 --- /dev/null +++ b/decidim-elections/db/migrate/20200601141412_add_published_at_to_elections.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddPublishedAtToElections < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_elections, :published_at, :datetime + end +end diff --git a/decidim-elections/db/migrate/20200807125040_remove_subtitle_from_decidim_elections.rb b/decidim-elections/db/migrate/20200807125040_remove_subtitle_from_decidim_elections.rb new file mode 100644 index 00000000..1832f489 --- /dev/null +++ b/decidim-elections/db/migrate/20200807125040_remove_subtitle_from_decidim_elections.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RemoveSubtitleFromDecidimElections < ActiveRecord::Migration[5.2] + def change + remove_column :decidim_elections_elections, :subtitle + end +end diff --git a/decidim-elections/db/migrate/20200910103648_add_min_selections_to_decidim_elections_questions.rb b/decidim-elections/db/migrate/20200910103648_add_min_selections_to_decidim_elections_questions.rb new file mode 100644 index 00000000..92c6a346 --- /dev/null +++ b/decidim-elections/db/migrate/20200910103648_add_min_selections_to_decidim_elections_questions.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddMinSelectionsToDecidimElectionsQuestions < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_questions, :min_selections, :integer, null: false, default: 1 + end +end diff --git a/decidim-elections/db/migrate/20200915142713_add_questionnaire_to_existing_elections.rb b/decidim-elections/db/migrate/20200915142713_add_questionnaire_to_existing_elections.rb new file mode 100644 index 00000000..93e05ca0 --- /dev/null +++ b/decidim-elections/db/migrate/20200915142713_add_questionnaire_to_existing_elections.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class AddQuestionnaireToExistingElections < ActiveRecord::Migration[5.2] + class Election < ApplicationRecord + self.table_name = :decidim_elections_elections + + has_one :questionnaire, + class_name: "Questionnaire", + dependent: :destroy, + inverse_of: :questionnaire_for, + as: :questionnaire_for + end + + class Questionnaire < ApplicationRecord + self.table_name = :decidim_forms_questionnaires + + belongs_to :questionnaire_for, polymorphic: true + end + + def change + Election.find_each do |election| + next unless election.questionnaire + + election.update!( + questionnaire: Questionnaire.new + ) + end + end +end diff --git a/decidim-elections/db/migrate/20200918153813_create_decidim_elections_trustees_participatory_spaces.rb b/decidim-elections/db/migrate/20200918153813_create_decidim_elections_trustees_participatory_spaces.rb new file mode 100644 index 00000000..b8fa8cc3 --- /dev/null +++ b/decidim-elections/db/migrate/20200918153813_create_decidim_elections_trustees_participatory_spaces.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsTrusteesParticipatorySpaces < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_trustees_participatory_spaces do |t| + t.references :participatory_space, polymorphic: true, index: { name: "index_elections_trustees_spaces_on_space_type_and_id" } + t.references :decidim_elections_trustee, null: false, index: { name: "index_elections_trustees_spaces_on_elections_trustee_id" } + t.boolean :considered, null: false, default: true + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20200918153824_create_decidim_elections_elections_trustees.rb b/decidim-elections/db/migrate/20200918153824_create_decidim_elections_elections_trustees.rb new file mode 100644 index 00000000..b742d535 --- /dev/null +++ b/decidim-elections/db/migrate/20200918153824_create_decidim_elections_elections_trustees.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsElectionsTrustees < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_elections_trustees do |t| + t.belongs_to :decidim_elections_election, null: false, index: { name: "index_elections_trustees_on_decidim_elections_election_id" } + t.belongs_to :decidim_elections_trustee, null: false, index: { name: "index_elections_trustees_on_decidim_elections_trustee_id" } + end + end +end diff --git a/decidim-elections/db/migrate/20200918153835_create_decidim_elections_trustees.rb b/decidim-elections/db/migrate/20200918153835_create_decidim_elections_trustees.rb new file mode 100644 index 00000000..bb6a3922 --- /dev/null +++ b/decidim-elections/db/migrate/20200918153835_create_decidim_elections_trustees.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsTrustees < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_trustees do |t| + t.references :decidim_user, null: false, index: true + t.string :public_key + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20201026163334_add_blocked_at_and_bb_status_to_elections.rb b/decidim-elections/db/migrate/20201026163334_add_blocked_at_and_bb_status_to_elections.rb new file mode 100644 index 00000000..d2d2e3b9 --- /dev/null +++ b/decidim-elections/db/migrate/20201026163334_add_blocked_at_and_bb_status_to_elections.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddBlockedAtAndBbStatusToElections < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_elections, :blocked_at, :datetime + add_column :decidim_elections_elections, :bb_status, :string + end +end diff --git a/decidim-elections/db/migrate/20201028090110_add_votes_to_decidim_elections_answers.rb b/decidim-elections/db/migrate/20201028090110_add_votes_to_decidim_elections_answers.rb new file mode 100644 index 00000000..1728ea6d --- /dev/null +++ b/decidim-elections/db/migrate/20201028090110_add_votes_to_decidim_elections_answers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddVotesToDecidimElectionsAnswers < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_answers, :votes_count, :integer, default: 0, null: false + end +end diff --git a/decidim-elections/db/migrate/20201028135614_add_selected_to_decidim_elections_answers.rb b/decidim-elections/db/migrate/20201028135614_add_selected_to_decidim_elections_answers.rb new file mode 100644 index 00000000..bc2d148e --- /dev/null +++ b/decidim-elections/db/migrate/20201028135614_add_selected_to_decidim_elections_answers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddSelectedToDecidimElectionsAnswers < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_answers, :selected, :boolean, null: false, default: false + end +end diff --git a/decidim-elections/db/migrate/20201209110653_create_decidim_elections_votes.rb b/decidim-elections/db/migrate/20201209110653_create_decidim_elections_votes.rb new file mode 100644 index 00000000..999e9cb7 --- /dev/null +++ b/decidim-elections/db/migrate/20201209110653_create_decidim_elections_votes.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsVotes < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_votes do |t| + t.belongs_to :decidim_elections_election, null: false, index: { name: "index_elections_votes_on_decidim_elections_election_id" } + t.string :voter_id, null: false + t.string :encrypted_vote_hash, null: false + t.string :status, null: false + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20201216091123_add_name_to_trustees.rb b/decidim-elections/db/migrate/20201216091123_add_name_to_trustees.rb new file mode 100644 index 00000000..f17f7eb0 --- /dev/null +++ b/decidim-elections/db/migrate/20201216091123_add_name_to_trustees.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddNameToTrustees < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_trustees, :name, :string, null: true, unique: true + end +end diff --git a/decidim-elections/db/migrate/20210113113818_add_message_id_to_decidim_elections_votes.rb b/decidim-elections/db/migrate/20210113113818_add_message_id_to_decidim_elections_votes.rb new file mode 100644 index 00000000..123797cb --- /dev/null +++ b/decidim-elections/db/migrate/20210113113818_add_message_id_to_decidim_elections_votes.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddMessageIdToDecidimElectionsVotes < ActiveRecord::Migration[5.2] + def change + add_column :decidim_elections_votes, :message_id, :string, null: false + end +end diff --git a/decidim-elections/db/migrate/20210113120115_create_decidim_elections_votings.rb b/decidim-elections/db/migrate/20210113120115_create_decidim_elections_votings.rb new file mode 100644 index 00000000..7890f315 --- /dev/null +++ b/decidim-elections/db/migrate/20210113120115_create_decidim_elections_votings.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsVotings < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_votings do |t| + t.string :slug, null: false, index: true + t.jsonb :title, null: false + t.jsonb :description, null: false + t.datetime :start_time + t.datetime :end_time + t.string :banner_image + t.string :introductory_image + + t.timestamps + + t.references :decidim_scope, index: true + t.references :decidim_organization, foreign_key: true, index: true + end + end +end diff --git a/decidim-elections/db/migrate/20210114100749_add_published_at_to_decidim_votings_votings.rb b/decidim-elections/db/migrate/20210114100749_add_published_at_to_decidim_votings_votings.rb new file mode 100644 index 00000000..2ffbd708 --- /dev/null +++ b/decidim-elections/db/migrate/20210114100749_add_published_at_to_decidim_votings_votings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddPublishedAtToDecidimVotingsVotings < ActiveRecord::Migration[5.2] + def change + add_column :decidim_votings_votings, :published_at, :datetime + end +end diff --git a/decidim-elections/db/migrate/20210114111850_add_user_to_decidim_elections_votes.rb b/decidim-elections/db/migrate/20210114111850_add_user_to_decidim_elections_votes.rb new file mode 100644 index 00000000..0b0da219 --- /dev/null +++ b/decidim-elections/db/migrate/20210114111850_add_user_to_decidim_elections_votes.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddUserToDecidimElectionsVotes < ActiveRecord::Migration[5.2] + def change + add_reference :decidim_elections_votes, :decidim_user, null: false + end +end diff --git a/decidim-elections/db/migrate/20210120164634_add_promoted_to_voting.rb b/decidim-elections/db/migrate/20210120164634_add_promoted_to_voting.rb new file mode 100644 index 00000000..3ded893b --- /dev/null +++ b/decidim-elections/db/migrate/20210120164634_add_promoted_to_voting.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddPromotedToVoting < ActiveRecord::Migration[5.2] + def change + add_column :decidim_votings_votings, :promoted, :boolean, default: false, index: true + end +end diff --git a/decidim-elections/db/migrate/20210125124801_add_voting_type_to_voting_voting.rb b/decidim-elections/db/migrate/20210125124801_add_voting_type_to_voting_voting.rb new file mode 100644 index 00000000..1b58857c --- /dev/null +++ b/decidim-elections/db/migrate/20210125124801_add_voting_type_to_voting_voting.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddVotingTypeToVotingVoting < ActiveRecord::Migration[5.2] + def change + add_column :decidim_votings_votings, :voting_type, :string, default: "online" + end +end diff --git a/decidim-elections/db/migrate/20210129124956_create_decidim_elections_actions.rb b/decidim-elections/db/migrate/20210129124956_create_decidim_elections_actions.rb new file mode 100644 index 00000000..cdfb09f3 --- /dev/null +++ b/decidim-elections/db/migrate/20210129124956_create_decidim_elections_actions.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateDecidimElectionsActions < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_actions do |t| + t.belongs_to :decidim_elections_election, null: false, index: { name: "index_elections_actions_on_decidim_elections_election_id" } + t.integer :action, null: false + t.string :message_id, null: false + t.integer :status, null: false + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210204132111_add_votings_polling_stations.rb b/decidim-elections/db/migrate/20210204132111_add_votings_polling_stations.rb new file mode 100644 index 00000000..71ccf429 --- /dev/null +++ b/decidim-elections/db/migrate/20210204132111_add_votings_polling_stations.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddVotingsPollingStations < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_polling_stations do |t| + t.jsonb :title, null: false + t.text :address + t.float :latitude + t.float :longitude + t.jsonb :location + t.jsonb :location_hints + t.references :decidim_votings_voting, + null: false, + index: { name: "decidim_votings_votings_polling_stations" } + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210208090441_add_polling_officers_to_votings.rb b/decidim-elections/db/migrate/20210208090441_add_polling_officers_to_votings.rb new file mode 100644 index 00000000..e1627ed5 --- /dev/null +++ b/decidim-elections/db/migrate/20210208090441_add_polling_officers_to_votings.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class AddPollingOfficersToVotings < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_polling_officers do |t| + t.references :decidim_votings_voting, index: { name: "decidim_votings_votings_polling_officers" } + t.references :decidim_user, index: true + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210210090738_add_votings_polling_station_references_to_votings_polling_officer.rb b/decidim-elections/db/migrate/20210210090738_add_votings_polling_station_references_to_votings_polling_officer.rb new file mode 100644 index 00000000..e56112de --- /dev/null +++ b/decidim-elections/db/migrate/20210210090738_add_votings_polling_station_references_to_votings_polling_officer.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddVotingsPollingStationReferencesToVotingsPollingOfficer < ActiveRecord::Migration[5.2] + def change + add_column :decidim_votings_polling_officers, :managed_polling_station_id, :integer + add_column :decidim_votings_polling_officers, :presided_polling_station_id, :integer + end +end diff --git a/decidim-elections/db/migrate/20210216074707_add_monitoring_committee_member.rb b/decidim-elections/db/migrate/20210216074707_add_monitoring_committee_member.rb new file mode 100644 index 00000000..405cae74 --- /dev/null +++ b/decidim-elections/db/migrate/20210216074707_add_monitoring_committee_member.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class AddMonitoringCommitteeMember < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_monitoring_committee_members do |t| + t.references :decidim_votings_voting, index: { name: "decidim_votings_votings_monitoring_committee_members" } + t.references :decidim_user, index: { name: "decidim_users_votings_monitoring_committee_members" } + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210308104024_add_decidim_votings_census_datasets.rb b/decidim-elections/db/migrate/20210308104024_add_decidim_votings_census_datasets.rb new file mode 100644 index 00000000..1a5edd90 --- /dev/null +++ b/decidim-elections/db/migrate/20210308104024_add_decidim_votings_census_datasets.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddDecidimVotingsCensusDatasets < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_census_datasets do |t| + t.string :file + t.integer :status, null: false + t.integer :data_count + t.integer :csv_row_raw_count, null: false + t.integer :csv_row_processed_count, default: 0 + + t.belongs_to :decidim_votings_voting, null: false, index: { name: "decidim_votings_voting_census_dataset" } + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210308104154_add_decidim_votings_census_data.rb b/decidim-elections/db/migrate/20210308104154_add_decidim_votings_census_data.rb new file mode 100644 index 00000000..66d3b515 --- /dev/null +++ b/decidim-elections/db/migrate/20210308104154_add_decidim_votings_census_data.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddDecidimVotingsCensusData < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_census_data do |t| + t.string :hashed_in_person_data, index: true + t.string :hashed_online_data, index: true + t.string :hashed_check_data, index: true + + t.string :full_name + t.string :full_address + t.string :postal_code + t.string :mobile_phone_number, null: true + t.string :email, null: true + + t.string :access_code, null: true + + t.belongs_to :decidim_votings_census_dataset, null: false, index: { name: "decidim_votings_census_dataset_census_datum" } + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210310120708_add_followable_counter_cache_to_votings.rb b/decidim-elections/db/migrate/20210310120708_add_followable_counter_cache_to_votings.rb new file mode 100644 index 00000000..aa29515e --- /dev/null +++ b/decidim-elections/db/migrate/20210310120708_add_followable_counter_cache_to_votings.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddFollowableCounterCacheToVotings < ActiveRecord::Migration[5.2] + class Voting < ApplicationRecord + self.table_name = :decidim_votings_votings + end + + def change + add_column :decidim_votings_votings, :follows_count, :integer, null: false, default: 0, index: true + + reversible do |dir| + dir.up do + Voting.reset_column_information + Voting.find_each do |record| + record.class.reset_counters(record.id, :follows) + end + end + end + end +end diff --git a/decidim-elections/db/migrate/20210326090435_create_elections_results.rb b/decidim-elections/db/migrate/20210326090435_create_elections_results.rb new file mode 100644 index 00000000..ce009d97 --- /dev/null +++ b/decidim-elections/db/migrate/20210326090435_create_elections_results.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateElectionsResults < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_results do |t| + t.integer :votes_count, default: 0, null: false + + t.belongs_to :decidim_elections_answer, index: true + t.belongs_to :decidim_votings_polling_station, + null: true, + index: { name: "index_decidim_elections_results_on_polling_station_id" } + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210330102348_remove_votes_count_from_answer.rb b/decidim-elections/db/migrate/20210330102348_remove_votes_count_from_answer.rb new file mode 100644 index 00000000..d0ae5fbf --- /dev/null +++ b/decidim-elections/db/migrate/20210330102348_remove_votes_count_from_answer.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RemoveVotesCountFromAnswer < ActiveRecord::Migration[5.2] + def change + remove_column :decidim_elections_answers, :votes_count, :integer + end +end diff --git a/decidim-elections/db/migrate/20210330123606_add_voting_ballot_style.rb b/decidim-elections/db/migrate/20210330123606_add_voting_ballot_style.rb new file mode 100644 index 00000000..e2dab045 --- /dev/null +++ b/decidim-elections/db/migrate/20210330123606_add_voting_ballot_style.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddVotingBallotStyle < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_ballot_styles do |t| + t.string :code + t.references :decidim_votings_voting, null: false, index: false + + t.timestamps + + t.index [:decidim_votings_voting_id, :code], name: "decidim_votings_ballot_styles_on_voting_and_code", unique: true + end + + create_join_table :decidim_votings_ballot_styles, :decidim_elections_questions, table_name: "decidim_votings_ballot_style_questions" do |t| + t.index :decidim_votings_ballot_style_id, name: "decidim_votings_ballot_styles_questions_ballot_style_id" + t.index :decidim_elections_question_id, name: "decidim_votings_ballot_styles_questions_question_id" + end + end +end diff --git a/decidim-elections/db/migrate/20210330183204_add_email_to_votes.rb b/decidim-elections/db/migrate/20210330183204_add_email_to_votes.rb new file mode 100644 index 00000000..aa1ee710 --- /dev/null +++ b/decidim-elections/db/migrate/20210330183204_add_email_to_votes.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AddEmailToVotes < ActiveRecord::Migration[5.2] + def change + change_table :decidim_elections_votes, bulk: true do |t| + t.string :email + t.change :decidim_user_id, :bigint, null: true, index: true + end + end +end diff --git a/decidim-elections/db/migrate/20210331152729_add_census_contact_information_to_votings.rb b/decidim-elections/db/migrate/20210331152729_add_census_contact_information_to_votings.rb new file mode 100644 index 00000000..1ca242d0 --- /dev/null +++ b/decidim-elections/db/migrate/20210331152729_add_census_contact_information_to_votings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddCensusContactInformationToVotings < ActiveRecord::Migration[5.2] + def change + add_column :decidim_votings_votings, :census_contact_information, :string + end +end diff --git a/decidim-elections/db/migrate/20210401095507_add_organization_to_decidim_elections_trustee.rb b/decidim-elections/db/migrate/20210401095507_add_organization_to_decidim_elections_trustee.rb new file mode 100644 index 00000000..28d23f99 --- /dev/null +++ b/decidim-elections/db/migrate/20210401095507_add_organization_to_decidim_elections_trustee.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddOrganizationToDecidimElectionsTrustee < ActiveRecord::Migration[5.2] + def change + add_reference :decidim_elections_trustees, :decidim_organization, index: true, foreign_key: true + add_index :decidim_elections_trustees, + [:name, :decidim_organization_id], + unique: true, + name: "index_decidim_organization_id_and_name" + end +end diff --git a/decidim-elections/db/migrate/20210402102215_add_ballot_style_to_decidim_votings_census_data.rb b/decidim-elections/db/migrate/20210402102215_add_ballot_style_to_decidim_votings_census_data.rb new file mode 100644 index 00000000..8f69ddbb --- /dev/null +++ b/decidim-elections/db/migrate/20210402102215_add_ballot_style_to_decidim_votings_census_data.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddBallotStyleToDecidimVotingsCensusData < ActiveRecord::Migration[5.2] + def change + add_reference :decidim_votings_census_data, :decidim_votings_ballot_style, foreign_key: true, index: { name: "decidim_votings_census_data_on_ballot_style_id" }, null: true + end +end diff --git a/decidim-elections/db/migrate/20210402140402_add_salt_to_elections.rb b/decidim-elections/db/migrate/20210402140402_add_salt_to_elections.rb new file mode 100644 index 00000000..16bd8a46 --- /dev/null +++ b/decidim-elections/db/migrate/20210402140402_add_salt_to_elections.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class AddSaltToElections < ActiveRecord::Migration[5.2] + class Election < ApplicationRecord + self.table_name = :decidim_elections_elections + end + + def up + add_column :decidim_elections_elections, :salt, :string, null: false, default: "" + + Election.find_each do |election| + election.salt = Decidim::Tokenizer.random_salt + election.save! + end + + change_column_default(:decidim_elections_elections, :salt, nil) + end + + def down + remove_column :decidim_elections_elections, :salt + end +end diff --git a/decidim-elections/db/migrate/20210412144721_change_elections_results.rb b/decidim-elections/db/migrate/20210412144721_change_elections_results.rb new file mode 100644 index 00000000..1b5ab452 --- /dev/null +++ b/decidim-elections/db/migrate/20210412144721_change_elections_results.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class ChangeElectionsResults < ActiveRecord::Migration[5.2] + def change + change_table :decidim_elections_results do |t| + t.rename :votes_count, :value + t.remove_belongs_to :decidim_elections_answer + t.remove_belongs_to :decidim_votings_polling_station + + t.integer :result_type, index: true + + t.belongs_to :closurable, + null: false, + polymorphic: true, + index: false + t.belongs_to :decidim_elections_answer, + null: true, + index: false + t.belongs_to :decidim_elections_question, + null: true, + index: false + end + end +end diff --git a/decidim-elections/db/migrate/20210412144740_create_elections_bulletin_board_closures.rb b/decidim-elections/db/migrate/20210412144740_create_elections_bulletin_board_closures.rb new file mode 100644 index 00000000..3cfb3816 --- /dev/null +++ b/decidim-elections/db/migrate/20210412144740_create_elections_bulletin_board_closures.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateElectionsBulletinBoardClosures < ActiveRecord::Migration[5.2] + def change + create_table :decidim_elections_bulletin_board_closures do |t| + t.belongs_to :decidim_elections_election, + null: false, + index: false + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210412144741_create_votings_polling_station_closures.rb b/decidim-elections/db/migrate/20210412144741_create_votings_polling_station_closures.rb new file mode 100644 index 00000000..4a663776 --- /dev/null +++ b/decidim-elections/db/migrate/20210412144741_create_votings_polling_station_closures.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class CreateVotingsPollingStationClosures < ActiveRecord::Migration[5.2] + def change + create_table :decidim_votings_polling_station_closures do |t| + t.integer :phase, index: true + t.string :polling_officer_notes, null: true + + t.belongs_to :decidim_elections_election, + null: false, + index: false + t.belongs_to :decidim_votings_polling_station, + null: true, + index: false + t.belongs_to :decidim_votings_polling_officer, + null: true, + index: false + + t.timestamps + end + end +end diff --git a/decidim-elections/db/migrate/20210420112721_create_decidim_votings_in_person_votes.rb b/decidim-elections/db/migrate/20210420112721_create_decidim_votings_in_person_votes.rb new file mode 100644 index 00000000..f0d4e6ec --- /dev/null +++ b/decidim-elections/db/migrate/20210420112721_create_decidim_votings_in_person_votes.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class CreateDecidimVotingsInPersonVotes < ActiveRecord::Migration[6.0] + def change + create_table :decidim_votings_in_person_votes do |t| + t.belongs_to :decidim_elections_election, null: false, index: false + t.belongs_to :decidim_votings_polling_station, null: true, index: false + t.belongs_to :decidim_votings_polling_officer, null: true, index: false + t.string :message_id, null: false + t.string :voter_id, null: false + t.integer :status, null: false + + t.timestamps + + t.index [:decidim_elections_election_id, :decidim_votings_polling_station_id], + name: "decidim_votings_in_person_votes_polling_station_id" + t.index [:decidim_elections_election_id, :voter_id], + name: "decidim_votings_in_person_votes_voter_id" + end + end +end diff --git a/decidim-elections/db/migrate/20210422124826_add_verifiable_results_to_decidim_elections_election.rb b/decidim-elections/db/migrate/20210422124826_add_verifiable_results_to_decidim_elections_election.rb new file mode 100644 index 00000000..ebf30930 --- /dev/null +++ b/decidim-elections/db/migrate/20210422124826_add_verifiable_results_to_decidim_elections_election.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddVerifiableResultsToDecidimElectionsElection < ActiveRecord::Migration[6.0] + def change + add_column :decidim_elections_elections, :verifiable_results_file_url, :string + add_column :decidim_elections_elections, :verifiable_results_file_hash, :string + end +end diff --git a/decidim-elections/db/migrate/20210426072845_add_signed_at_to_polling_station_closure.rb b/decidim-elections/db/migrate/20210426072845_add_signed_at_to_polling_station_closure.rb new file mode 100644 index 00000000..a44a4ed7 --- /dev/null +++ b/decidim-elections/db/migrate/20210426072845_add_signed_at_to_polling_station_closure.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddSignedAtToPollingStationClosure < ActiveRecord::Migration[6.0] + def change + add_column :decidim_votings_polling_station_closures, :signed_at, :date + end +end diff --git a/decidim-elections/db/migrate/20210427131742_add_validated_at_to_votings_polling_station_closures.rb b/decidim-elections/db/migrate/20210427131742_add_validated_at_to_votings_polling_station_closures.rb new file mode 100644 index 00000000..d9ced675 --- /dev/null +++ b/decidim-elections/db/migrate/20210427131742_add_validated_at_to_votings_polling_station_closures.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddValidatedAtToVotingsPollingStationClosures < ActiveRecord::Migration[6.0] + def change + add_column :decidim_votings_polling_station_closures, :validated_at, :date + add_column :decidim_votings_polling_station_closures, :monitoring_committee_notes, :string + end +end diff --git a/decidim-elections/db/migrate/20220404112802_rename_bb_status_tally_to_tally_started.rb b/decidim-elections/db/migrate/20220404112802_rename_bb_status_tally_to_tally_started.rb new file mode 100644 index 00000000..fe17a8b3 --- /dev/null +++ b/decidim-elections/db/migrate/20220404112802_rename_bb_status_tally_to_tally_started.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class RenameBbStatusTallyToTallyStarted < ActiveRecord::Migration[6.1] + class Election < ApplicationRecord + self.table_name = :decidim_elections_elections + end + + def up + # rubocop:disable Rails/SkipsModelValidations + Election.where(bb_status: "tally").update_all(bb_status: "tally_started") + # rubocop:enable Rails/SkipsModelValidations + end + + def down + # rubocop:disable Rails/SkipsModelValidations + Election.where(bb_status: "tally_started").update_all(bb_status: "tally") + # rubocop:enable Rails/SkipsModelValidations + end +end diff --git a/decidim-elections/db/migrate/20220424121541_add_show_check_census_to_votings.rb b/decidim-elections/db/migrate/20220424121541_add_show_check_census_to_votings.rb new file mode 100644 index 00000000..1781742f --- /dev/null +++ b/decidim-elections/db/migrate/20220424121541_add_show_check_census_to_votings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddShowCheckCensusToVotings < ActiveRecord::Migration[6.1] + def change + add_column :decidim_votings_votings, :show_check_census, :boolean, default: true + end +end diff --git a/decidim-elections/db/migrate/20220615102642_remove_description_from_elections_questions.rb b/decidim-elections/db/migrate/20220615102642_remove_description_from_elections_questions.rb new file mode 100644 index 00000000..c6a791ed --- /dev/null +++ b/decidim-elections/db/migrate/20220615102642_remove_description_from_elections_questions.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RemoveDescriptionFromElectionsQuestions < ActiveRecord::Migration[6.1] + def change + remove_column :decidim_elections_questions, :description, :jsonb + end +end diff --git a/decidim-elections/db/migrate/20220711112802_rename_dataset_file_to_filename.rb b/decidim-elections/db/migrate/20220711112802_rename_dataset_file_to_filename.rb new file mode 100644 index 00000000..c2d7f918 --- /dev/null +++ b/decidim-elections/db/migrate/20220711112802_rename_dataset_file_to_filename.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RenameDatasetFileToFilename < ActiveRecord::Migration[6.1] + def change + rename_column :decidim_votings_census_datasets, :file, :filename + end +end diff --git a/decidim-elections/decidim-elections.gemspec b/decidim-elections/decidim-elections.gemspec new file mode 100644 index 00000000..ec53c65c --- /dev/null +++ b/decidim-elections/decidim-elections.gemspec @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +$LOAD_PATH.push File.expand_path("lib", __dir__) + +require "decidim/elections/version" + +Gem::Specification.new do |s| + s.version = Decidim::Elections.version + s.authors = ["Leonardo Diez", "Agustí B.R."] + s.email = ["leo@codegram.com", "agusti@codegram.com"] + s.license = "AGPL-3.0" + s.homepage = "https://decidim.org" + s.metadata = { + "bug_tracker_uri" => "https://github.com/decidim/decidim/issues", + "documentation_uri" => "https://docs.decidim.org/", + "funding_uri" => "https://opencollective.com/decidim", + "homepage_uri" => "https://decidim.org", + "source_code_uri" => "https://github.com/decidim/decidim" + } + s.required_ruby_version = "~> 3.2.0" + + s.name = "decidim-elections" + s.summary = "A decidim elections module (votings space and elections component)" + s.description = "The Elections module adds elections to any participatory space." + + s.files = Dir.chdir(__dir__) do + `git ls-files -z`.split("\x0").select do |f| + (File.expand_path(f) == __FILE__) || + f.start_with?(*%w(app/ config/ db/ lib/ Rakefile README.md)) + end + end + + s.add_dependency "decidim-bulletin_board", "~> 0.24.4" + s.add_dependency "voting_schemes-dummy", "~> 0.24.4" + s.add_dependency "voting_schemes-electionguard", "~> 0.24.4" + + s.add_dependency "decidim-core", Decidim::Elections.version + s.add_dependency "decidim-forms", Decidim::Elections.version + s.add_dependency "decidim-proposals", Decidim::Elections.version + s.add_dependency "rack-attack", "~> 6.0" + + s.add_development_dependency "decidim-admin", Decidim::Elections.version + s.add_development_dependency "decidim-dev", Decidim::Elections.version +end diff --git a/decidim-elections/docs/docker/bulletin_board/docker-compose.yml b/decidim-elections/docs/docker/bulletin_board/docker-compose.yml new file mode 100644 index 00000000..91198486 --- /dev/null +++ b/decidim-elections/docs/docker/bulletin_board/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.4' +services: + db: + image: postgres:14 + environment: + - POSTGRES_PASSWORD=postgres + volumes: + - pg-data:/var/lib/postgresql/data + app: + image: decidim/decidim-bulletin-board:0.24.4 + environment: + - DATABASE_URL=postgresql://postgres:postgres@db/decidim_bulletin_board_test + - RAILS_ENV=test + - RAILS_LOG_TO_STDOUT=true + - RAILS_SERVE_STATIC_FILES=enabled + - SEED=1 + - IDENTIFICATION_PRIVATE_KEY='{"kty":"RSA","n":"zMXsZpYPKkDlSmezX898y7zNOaJ7ENIN4kj4UhQ95Vm4HlgTpIs2VMMsO0eqynMaOR_G1mXdqbpbaJtXijBe4V8323QwGm6WVAa71E7pDXa5g6-uo5f8GePitN0YER9y2yNQN4uTaNzJiWV2uLBUYfMdj3SIif31YwLULHAOj3B_oleFK8coE_Qr3NzATcYBmsqE8AR4NljxTO6KDmP1SLdf5GBOBhOAIFbnL_Kpj2xkm7MS3hjMVKpiRhqA1UgX5oKZ8ixBv46fNJF0pBsHi3fHNjK9oZzgdx_AI-YFpdE_40-8bh_g9sWzxacqOM2-MdQLHbvRPEVltO3E8tr6I5YWrylcP7l9VD8OJeqjq2qFYHnGYdmLoD2XuXmI9EuBvSb9H4-qcartxZSIQCimKib_fxZvgrG1FSRRhK6YpvIdGv4-G2zfCCRsC4XD80TYI2bf-oYCoy7eU3_eVHFMV2yg4p1Wnuw2Vgq0edPL_bKaV9JvGx7F-U5juxNN0WZR9LzbPl4ReejzN95lyHgbj0nTH_u3bSpZmgJrQF-PwdnPcG46deVjJgUeosrlC4lQxVrRz0GL58BuFunnz2uYDBDrcJCiG60EbdkAFHjOcXU4wrUWATin7je_aqdBXhSnkTafcJAMvL7Y2Ld7vDge8nLqjAVlAi5am3rN0kqKT6M","e":"AQAB","kid":"a8e86f02ca27e1861bfc49e2a9a4614ca9068f8efdb6d42d19d3aab0eb2a31be"}' + ports: + - 8000:3000 + +volumes: + pg-data: diff --git a/decidim-elections/docs/docker/bulletin_board_test/docker-compose.yml b/decidim-elections/docs/docker/bulletin_board_test/docker-compose.yml new file mode 100644 index 00000000..4dac3fda --- /dev/null +++ b/decidim-elections/docs/docker/bulletin_board_test/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.4' +services: + db: + image: postgres:14 + environment: + - POSTGRES_PASSWORD=postgres + volumes: + - pg-data:/var/lib/postgresql/data + app: + image: decidim/decidim-bulletin-board:0.24.4 + environment: + - DATABASE_URL=postgresql://postgres:postgres@db/decidim_bulletin_board_test + - RAILS_ENV=test + - RAILS_LOG_TO_STDOUT=true + - RAILS_SERVE_STATIC_FILES=enabled + - SEED=1 + - IDENTIFICATION_PRIVATE_KEY='{"kty":"RSA","n":"zMXsZpYPKkDlSmezX898y7zNOaJ7ENIN4kj4UhQ95Vm4HlgTpIs2VMMsO0eqynMaOR_G1mXdqbpbaJtXijBe4V8323QwGm6WVAa71E7pDXa5g6-uo5f8GePitN0YER9y2yNQN4uTaNzJiWV2uLBUYfMdj3SIif31YwLULHAOj3B_oleFK8coE_Qr3NzATcYBmsqE8AR4NljxTO6KDmP1SLdf5GBOBhOAIFbnL_Kpj2xkm7MS3hjMVKpiRhqA1UgX5oKZ8ixBv46fNJF0pBsHi3fHNjK9oZzgdx_AI-YFpdE_40-8bh_g9sWzxacqOM2-MdQLHbvRPEVltO3E8tr6I5YWrylcP7l9VD8OJeqjq2qFYHnGYdmLoD2XuXmI9EuBvSb9H4-qcartxZSIQCimKib_fxZvgrG1FSRRhK6YpvIdGv4-G2zfCCRsC4XD80TYI2bf-oYCoy7eU3_eVHFMV2yg4p1Wnuw2Vgq0edPL_bKaV9JvGx7F-U5juxNN0WZR9LzbPl4ReejzN95lyHgbj0nTH_u3bSpZmgJrQF-PwdnPcG46deVjJgUeosrlC4lQxVrRz0GL58BuFunnz2uYDBDrcJCiG60EbdkAFHjOcXU4wrUWATin7je_aqdBXhSnkTafcJAMvL7Y2Ld7vDge8nLqjAVlAi5am3rN0kqKT6M","e":"AQAB","kid":"a8e86f02ca27e1861bfc49e2a9a4614ca9068f8efdb6d42d19d3aab0eb2a31be"}' + - DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL=true + ports: + - 5017:3000 + +volumes: + pg-data: diff --git a/decidim-elections/lib/decidim/api/bulletin_board_closure_type.rb b/decidim-elections/lib/decidim/api/bulletin_board_closure_type.rb new file mode 100644 index 00000000..fb967d23 --- /dev/null +++ b/decidim-elections/lib/decidim/api/bulletin_board_closure_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This type represents a bulletin board closure for an election. + class BulletinBoardClosureType < Decidim::Api::Types::BaseObject + description "A bulletin board election closure" + + field :id, GraphQL::Types::ID, "The internal ID of this result", null: false + field :created_at, Decidim::Core::DateTimeType, "When this result was created", null: true + field :updated_at, Decidim::Core::DateTimeType, "When this result was updated", null: true + field :election, Decidim::Elections::ElectionType, "The election for this closure", null: false + field :results, [Decidim::Elections::ElectionResultType, { null: true }], "The results for this closure", null: false + end + end +end diff --git a/decidim-elections/lib/decidim/api/election_answer_type.rb b/decidim-elections/lib/decidim/api/election_answer_type.rb new file mode 100644 index 00000000..5ec2f32b --- /dev/null +++ b/decidim-elections/lib/decidim/api/election_answer_type.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This type represents an answer to an election question. + # The name is different from the model because to enforce consistency with Question type name. + class ElectionAnswerType < Decidim::Api::Types::BaseObject + implements Decidim::Core::AttachableInterface + implements Decidim::Core::TraceableInterface + + description "An answer for an election's question" + + field :id, GraphQL::Types::ID, "The internal ID of this answer", null: false + field :title, Decidim::Core::TranslatedFieldType, "The title for this answer", null: false + field :description, Decidim::Core::TranslatedFieldType, "The description for this answer", null: true + field :weight, GraphQL::Types::Int, "The ordering weight for this answer", null: true + field :results_total, GraphQL::Types::Int, "The total sum of votes for this answer", null: true, camelize: false + field :selected, GraphQL::Types::Boolean, "Is this answer selected?", null: true + + field :proposals, [Decidim::Proposals::ProposalType, { null: true }], "The proposals related to this answer", null: true if defined?(Decidim::Proposals::ProposalType) + field :results, [Decidim::Elections::ElectionResultType, { null: true }], "The voting results related to this answer", null: true + end + end +end diff --git a/decidim-elections/lib/decidim/api/election_question_type.rb b/decidim-elections/lib/decidim/api/election_question_type.rb new file mode 100644 index 00000000..ed546ff6 --- /dev/null +++ b/decidim-elections/lib/decidim/api/election_question_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This type represents an election Question. + # The name is different from the model because the Question type is already defined on the Forms module. + class ElectionQuestionType < Decidim::Api::Types::BaseObject + implements Decidim::Core::TraceableInterface + + description "A question for an election" + + field :id, GraphQL::Types::ID, "The internal ID of this question", null: false + field :title, Decidim::Core::TranslatedFieldType, "The title for this question", null: false + field :max_selections, GraphQL::Types::Int, "The maximum number of possible selections for this question", null: false + field :weight, GraphQL::Types::Int, "The ordering weight for this question", null: true + field :random_answers_order, GraphQL::Types::Boolean, "Should this question order answers in random order?", null: true + field :min_selections, GraphQL::Types::Int, "The minimum number of possible selections for this question", null: false + field :answers, [Decidim::Elections::ElectionAnswerType, { null: true }], "The answers for this question", null: false + end + end +end diff --git a/decidim-elections/lib/decidim/api/election_result_type.rb b/decidim-elections/lib/decidim/api/election_result_type.rb new file mode 100644 index 00000000..c5b4ae6c --- /dev/null +++ b/decidim-elections/lib/decidim/api/election_result_type.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This type represents a result of an answer to an election question. + class ElectionResultType < Decidim::Api::Types::BaseObject + description "A voting result for an answer" + + field :id, GraphQL::Types::ID, "The internal ID of this result", null: false + field :created_at, Decidim::Core::DateTimeType, "When this result was created", null: true + field :updated_at, Decidim::Core::DateTimeType, "When this result was updated", null: true + field :value, GraphQL::Types::Int, "The value of the recount", null: false + field :result_type, GraphQL::Types::String, "The result type for this result in the closure", null: true, camelize: false + field :answer, Decidim::Elections::ElectionAnswerType, "The answer for this result", null: false + field :question, Decidim::Elections::ElectionQuestionType, "The question for this result", null: false + # field :closurable, [Decidim::Elections::BulletinBoardClosureType, Decidim::Votings::PollingStationClosureType], "The closure for this result", null: false + end + end +end diff --git a/decidim-elections/lib/decidim/api/election_type.rb b/decidim-elections/lib/decidim/api/election_type.rb new file mode 100644 index 00000000..a960ad82 --- /dev/null +++ b/decidim-elections/lib/decidim/api/election_type.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This type represents an Election. + class ElectionType < Decidim::Api::Types::BaseObject + implements Decidim::Core::AttachableInterface + implements Decidim::Core::TraceableInterface + + description "An election" + + field :id, GraphQL::Types::ID, "The internal ID of this election", null: false + field :title, Decidim::Core::TranslatedFieldType, "The title for this election", null: false + field :description, Decidim::Core::TranslatedFieldType, "The description for this election", null: false + field :start_time, Decidim::Core::DateTimeType, "The start time for this election", null: false + field :end_time, Decidim::Core::DateTimeType, "The end time for this election", null: false + field :created_at, Decidim::Core::DateTimeType, "When this election was created", null: true + field :updated_at, Decidim::Core::DateTimeType, "When this election was updated", null: true + field :published_at, Decidim::Core::DateTimeType, "When this election was published", null: true + field :blocked, GraphQL::Types::Boolean, "Whether this election has it is parameters blocked or not", method: :blocked?, null: true + field :bb_status, GraphQL::Types::String, "The status for this election in the bulletin board", null: true, camelize: false + + field :questions, [Decidim::Elections::ElectionQuestionType, { null: true }], "The questions for this election", null: false + field :trustees, [Decidim::Elections::TrusteeType, { null: true }], "The trustees for this election", null: false + end + end +end diff --git a/decidim-elections/lib/decidim/api/elections_type.rb b/decidim-elections/lib/decidim/api/elections_type.rb new file mode 100644 index 00000000..d0d5fd24 --- /dev/null +++ b/decidim-elections/lib/decidim/api/elections_type.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Decidim + module Elections + class ElectionsType < Decidim::Api::Types::BaseObject + implements Decidim::Core::ComponentInterface + + graphql_name "Elections" + description "An elections component of a participatory space." + + field :elections, Decidim::Elections::ElectionType.connection_type, null: true, connection: true + + def elections + ElectionsTypeHelper.base_scope(object).includes(:component) + end + + field :election, Decidim::Elections::ElectionType, null: true do + argument :id, GraphQL::Types::ID, required: true + end + + def election(**args) + ElectionsTypeHelper.base_scope(object).find_by(id: args[:id]) + end + end + + module ElectionsTypeHelper + def self.base_scope(component) + Election.where(component:).where.not(published_at: nil) + end + end + end +end diff --git a/decidim-elections/lib/decidim/api/polling_station_closure_type.rb b/decidim-elections/lib/decidim/api/polling_station_closure_type.rb new file mode 100644 index 00000000..731fe881 --- /dev/null +++ b/decidim-elections/lib/decidim/api/polling_station_closure_type.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This type represents a Polling Station closure for an election. + class PollingStationClosureType < Decidim::Api::Types::BaseObject + description "A polling station election closure" + + field :id, GraphQL::Types::ID, "The internal ID of this result", null: false + field :created_at, Decidim::Core::DateTimeType, "When this result was created", null: true + field :updated_at, Decidim::Core::DateTimeType, "When this result was updated", null: true + field :election, Decidim::Elections::ElectionType, "The election for this closure", null: false + field :polling_officer_notes, GraphQL::Types::String, "The polling officer notes for this closure", null: false + field :results, [Decidim::Elections::ElectionResultType, { null: true }], "The results for this closure", null: false + field :polling_station, Decidim::Votings::PollingStationType, "The polling station for this closure", null: true + field :signed, GraphQL::Types::Boolean, "Whether this closure is signed or not", method: :signed?, null: true + end + end +end diff --git a/decidim-elections/lib/decidim/api/polling_station_type.rb b/decidim-elections/lib/decidim/api/polling_station_type.rb new file mode 100644 index 00000000..cb83e54b --- /dev/null +++ b/decidim-elections/lib/decidim/api/polling_station_type.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This type represents a polling station. + class PollingStationType < Decidim::Api::Types::BaseObject + description "A polling station for a voting" + + field :id, GraphQL::Types::ID, "The internal ID of this polling station", null: false + field :title, Decidim::Core::TranslatedFieldType, "The title for this polling station", null: true + field :address, GraphQL::Types::String, "The physical address of this polling station (used for geolocation)", null: true + field :coordinates, Decidim::Core::CoordinatesType, "Physical coordinates for this polling station", null: true + field :location, Decidim::Core::TranslatedFieldType, "The location of this polling station (free format)", null: true + field :location_hints, Decidim::Core::TranslatedFieldType, "The location of this polling station (free format)", null: true + field :created_at, Decidim::Core::DateTimeType, "When this polling station was created", null: true + field :updated_at, Decidim::Core::DateTimeType, "When this polling station was updated", null: true + field :voting, Decidim::Votings::VotingType, null: false + + def coordinates + [object.latitude, object.longitude] + end + end + end +end diff --git a/decidim-elections/lib/decidim/api/trustee_type.rb b/decidim-elections/lib/decidim/api/trustee_type.rb new file mode 100644 index 00000000..b64c97b4 --- /dev/null +++ b/decidim-elections/lib/decidim/api/trustee_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This type represents an election trustee. + class TrusteeType < Decidim::Api::Types::BaseObject + implements Decidim::Core::TraceableInterface + + description "A trustee for an election" + + field :id, GraphQL::Types::ID, "The internal ID of this trustee", null: false + field :user, Decidim::Core::UserType, "The corresponding decidim user", null: true + field :public_key, GraphQL::Types::String, "The public key of a trustee", null: true + end + end +end diff --git a/decidim-elections/lib/decidim/api/voting_type.rb b/decidim-elections/lib/decidim/api/voting_type.rb new file mode 100644 index 00000000..69fa2c69 --- /dev/null +++ b/decidim-elections/lib/decidim/api/voting_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This type represents a voting space. + class VotingType < Decidim::Api::Types::BaseObject + implements Decidim::Core::ParticipatorySpaceInterface + implements Decidim::Core::ScopableInterface + + description "A voting space" + + field :slug, String, null: false + field :description, Decidim::Core::TranslatedFieldType, "The description of this voting space.", null: true + field :start_time, Decidim::Core::DateTimeType, "The start time for this voting space.", null: false + field :end_time, Decidim::Core::DateTimeType, "The end time for this voting space", null: false + field :created_at, Decidim::Core::DateTimeType, "The time this voting was created", null: false + field :updated_at, Decidim::Core::DateTimeType, "The time this voting was updated", null: false + end + end +end diff --git a/decidim-elections/lib/decidim/elections.rb b/decidim-elections/lib/decidim/elections.rb new file mode 100644 index 00000000..8a9e7491 --- /dev/null +++ b/decidim-elections/lib/decidim/elections.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require "decidim/elections/admin" +require "decidim/elections/api" +require "decidim/elections/trustee_zone" +require "decidim/elections/engine" +require "decidim/elections/admin_engine" +require "decidim/elections/trustee_zone_engine" +require "decidim/elections/component" +require "decidim/bulletin_board" +require "decidim/votings" + +# Note: these gems will be moved to the application in the next release +require "voting_schemes/electionguard" +require "voting_schemes/dummy" + +module Decidim + # This namespace holds the logic of the `Elections` component. This component + # allows users to create elections in a participatory space. + module Elections + autoload :AnswerSerializer, "decidim/elections/answer_serializer" + + include ActiveSupport::Configurable + + def self.bulletin_board + @bulletin_board ||= Decidim::BulletinBoard::Client.new + end + + # Public Setting that defines how many hours should the setup be run before the election starts + config_accessor :setup_minimum_hours_before_start do + 3 + end + + # Public Setting that defines how many hours the ballot box can be opened before the election starts + config_accessor :start_vote_maximum_hours_before_start do + 6 + end + + # Public Setting that defines how many minutes will pass until the token of the voter expires + config_accessor :voter_token_expiration_minutes do + 120 + end + + # Public Setting that defines which kind of documents a participant can have + config_accessor :document_types do + %w(identification_number passport) + end + end +end diff --git a/decidim-elections/lib/decidim/elections/admin.rb b/decidim-elections/lib/decidim/elections/admin.rb new file mode 100644 index 00000000..ae56e520 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/admin.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This module contains all the domain logic associated to Decidim's Elections + # component admin panel. + module Admin + end + end +end diff --git a/decidim-elections/lib/decidim/elections/admin_engine.rb b/decidim-elections/lib/decidim/elections/admin_engine.rb new file mode 100644 index 00000000..be1adcaa --- /dev/null +++ b/decidim-elections/lib/decidim/elections/admin_engine.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "decidim/elections/menu" + +module Decidim + module Elections + # This is the engine that runs on the public interface of `Elections`. + class AdminEngine < ::Rails::Engine + isolate_namespace Decidim::Elections::Admin + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + routes do + get "/answer_options", to: "feedback_forms#answer_options", as: :answer_options_election_feedback, defaults: { format: "json" } + + resources :elections do + resources :steps, only: [:index, :update] do + get :stats + end + member do + put :publish + put :unpublish + resource :feedback_form, only: [:edit, :update] do + collection do + get :answers, to: "feedback_forms#index" + get "/answer/:session_token", to: "feedback_forms#show", as: :answer + get "/answer/:session_token/export", to: "feedback_forms#export_response", as: :answer_export + end + end + end + resources :questions do + resources :answers do + collection do + get :proposals_picker + resource :proposals_import, only: [:new, :create] + end + member do + put :select + put :unselect + end + end + end + end + + resources :trustees, only: [:index, :new, :edit, :create, :destroy], controller: "trustees_participatory_spaces" + + root to: "elections#index" + end + + def self.participatory_space_endpoints + [:trustees] + end + + initializer "decidim_elections_admin.menu_entry" do + Decidim::Elections::Menu.register_participatory_space_registry_manifests! + end + + def load_seed + nil + end + end + end +end diff --git a/decidim-elections/lib/decidim/elections/answer_serializer.rb b/decidim-elections/lib/decidim/elections/answer_serializer.rb new file mode 100644 index 00000000..5127fa16 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/answer_serializer.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This class serializes an Answer so it can be exported to CSV, JSON or other + # formats. + class AnswerSerializer < Decidim::Exporters::Serializer + include Decidim::ApplicationHelper + include Decidim::ResourceHelper + include Decidim::TranslationsHelper + + # Public: Initializes the serializer with an answer. + def initialize(answer) + @answer = answer + end + + # Public: Exports a hash with the serialized data for this answer. + def serialize + { + participatory_space: { + id: election.participatory_space.id, + title: election.participatory_space.title + }, + id: answer.id, + election_id: election.id, + election_title: election.title, + question_id: question.id, + question_title: question.title, + answer_id: answer.id, + answer_title: answer.title, + answer_votes: answer.results_total + } + end + + private + + attr_reader :answer + alias resource answer + + def election + answer.question.election + end + + def question + answer.question + end + end + end +end diff --git a/decidim-elections/lib/decidim/elections/api.rb b/decidim-elections/lib/decidim/elections/api.rb new file mode 100644 index 00000000..594a7e96 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/api.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module Elections + autoload :ElectionResultType, "decidim/api/election_result_type" + autoload :ElectionAnswerType, "decidim/api/election_answer_type" + autoload :ElectionQuestionType, "decidim/api/election_question_type" + autoload :ElectionType, "decidim/api/election_type" + autoload :ElectionsType, "decidim/api/elections_type" + autoload :TrusteeType, "decidim/api/trustee_type" + autoload :BulletinBoardClosureType, "decidim/api/bulletin_board_closure_type" + end +end diff --git a/decidim-elections/lib/decidim/elections/component.rb b/decidim-elections/lib/decidim/elections/component.rb new file mode 100644 index 00000000..553e38d8 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/component.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +Decidim.register_component(:elections) do |component| + component.engine = Decidim::Elections::Engine + component.admin_engine = Decidim::Elections::AdminEngine + component.icon = "media/images/decidim_elections.svg" + component.icon_key = "check-double-fill" + component.stylesheet = "decidim/elections/elections" + component.permissions_class_name = "Decidim::Elections::Permissions" + component.query_type = "Decidim::Elections::ElectionsType" + + component.on(:before_destroy) do |instance| + raise StandardError, "Cannot remove this component" if Decidim::Elections::Election.where(component: instance).any? + end + + # These actions permissions can be configured in the admin panel + component.actions = %w(vote) + + component.settings(:global) do |settings| + settings.attribute :announcement, type: :text, translated: true, editor: true + end + + component.settings(:step) do |settings| + settings.attribute :announcement, type: :text, translated: true, editor: true + end + + component.register_stat :elections_count, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at| + elections = Decidim::Elections::FilteredElections.for(components, start_at, end_at) + elections.published.count + end + + component.register_resource(:election) do |resource| + resource.model_class_name = "Decidim::Elections::Election" + resource.actions = %w(vote) + resource.card = "decidim/elections/election" + end + + component.register_resource(:question) do |resource| + resource.model_class_name = "Decidim::Elections::Question" + end + + component.register_resource(:answer) do |resource| + resource.model_class_name = "Decidim::Elections::Answer" + end + + component.exports :feedback_form_answers do |exports| + exports.collection do |_component, _user, resource_id| + Decidim::Forms::QuestionnaireUserAnswers.for(resource_id) + end + + exports.formats %w(CSV JSON Excel FormPDF) + + exports.serializer Decidim::Forms::UserAnswersSerializer + end + + component.exports :elections do |exports| + exports.collection do |component_instance| + Decidim::Elections::Answer + .where(decidim_elections_question_id: Decidim::Elections::Election.where(component: component_instance).bb_results_published.extract_associated(:questions)) + end + + exports.include_in_open_data = true + + exports.serializer Decidim::Elections::AnswerSerializer + end + + component.seeds do |participatory_space| + require "decidim/elections/seeds" + + Decidim::Elections::Seeds.new(participatory_space:).call + end +end + +Decidim.register_global_engine( + :decidim_elections_trustee_zone, + Decidim::Elections::TrusteeZoneEngine, + at: "/trustee" +) diff --git a/decidim-elections/lib/decidim/elections/engine.rb b/decidim-elections/lib/decidim/elections/engine.rb new file mode 100644 index 00000000..6afd71b4 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/engine.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require "rails" +require "decidim/core" + +module Decidim + module Elections + # This is the engine that runs on the public interface of elections. + class Engine < ::Rails::Engine + isolate_namespace Decidim::Elections + + routes do + resources :elections, only: [:index, :show] do + resource :feedback, only: [:show] do + post :answer + end + + resources :votes, only: [:new, :create, :update, :show] do + get :verify + match "new", action: :new, via: :post, as: :login, on: :collection + end + + get :election_log, on: :member + end + scope "/elections" do + root to: "elections#index" + end + get "/", to: redirect("elections", status: 301) + end + + initializer "decidim_elections.register_icons" do + Decidim.icons.register(name: "Decidim::Elections::Election", icon: "chat-poll-line", description: "Election", category: "activity", engine: :elections) + Decidim.icons.register(name: "safe-line", icon: "safe-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "guide-line", icon: "guide-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "loader-4-line", icon: "loader-4-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "checkbox-multiple-line", icon: "checkbox-multiple-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "book-2-line", icon: "book-2-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "shut-down-line", icon: "shut-down-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "article-line", icon: "article-line", category: "system", description: "", engine: :elections) + Decidim.icons.register(name: "bar-chart-box-line", icon: "bar-chart-box-line", category: "system", description: "", engine: :elections) + end + + initializer "decidim_elections.add_cells_view_paths" do + Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Elections::Engine.root}/app/cells") + Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Elections::Engine.root}/app/views") # for partials + end + + initializer "decidim_elections.webpacker.assets_path" do + Decidim.register_assets_path File.expand_path("app/packs", root) + end + + initializer "decidim_elections.authorization_transfer" do + config.to_prepare do + Decidim::AuthorizationTransfer.register(:elections) do |transfer| + transfer.move_records(Decidim::Elections::Vote, :decidim_user_id) + end + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/elections/menu.rb b/decidim-elections/lib/decidim/elections/menu.rb new file mode 100644 index 00000000..85b39cce --- /dev/null +++ b/decidim-elections/lib/decidim/elections/menu.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Decidim + module Elections + class Menu + def self.register_user_menu! + Decidim.menu :user_menu do |menu| + menu.add_item :decidim_elections_trustee_zone, + I18n.t("menu.trustee_zone", scope: "decidim.elections.trustee_zone"), + decidim.decidim_elections_trustee_zone_path, + active: :inclusive, + if: Decidim::Elections::Trustee.trustee?(current_user) + end + end + + def self.register_participatory_space_registry_manifests! + Decidim.participatory_space_registry.manifests.each do |participatory_space| + menu_id = :"admin_#{participatory_space.name.to_s.singularize}_menu" + Decidim.menu menu_id do |menu| + component = current_participatory_space.try(:components)&.find_by(manifest_name: :elections) + next unless component + + link = Decidim::EngineRouter.admin_proxy(component).trustees_path(locale: I18n.locale) + + has_election_components = current_participatory_space.components.select { |c| c.manifest_name == "elections" }.any? + + menu.add_item :trustees, + I18n.t("trustees", scope: "decidim.elections.admin.menu"), + link, + if: has_election_components && (allowed_to?(:manage, :trustees) || current_user.admin?), + icon_name: "safe-line", + position: 100, + active: is_active_link?(link) + end + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/elections/seeds.rb b/decidim-elections/lib/decidim/elections/seeds.rb new file mode 100644 index 00000000..f26d11a5 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/seeds.rb @@ -0,0 +1,435 @@ +# frozen_string_literal: true + +require "decidim/components/namer" +require "decidim/seeds" + +module Decidim + module Elections + class Seeds < Decidim::Seeds + attr_reader :participatory_space + + def initialize(participatory_space:) + @participatory_space = participatory_space + end + + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def call + admin_user = Decidim::User.find_by( + organization: participatory_space.organization, + email: "admin@example.org" + ) + + params = { + name: Decidim::Components::Namer.new(participatory_space.organization.available_locales, :elections).i18n_name, + manifest_name: :elections, + published_at: Time.current, + participatory_space: + } + + component = Decidim.traceability.perform_action!( + "publish", + Decidim::Component, + admin_user, + visibility: "all" + ) do + Decidim::Component.create!(params) + end + + # upcoming elections that may be published + 2.times do + upcoming_election = Decidim.traceability.create!( + Decidim::Elections::Election, + admin_user, + { + component:, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + start_time: 3.weeks.from_now, + end_time: 3.weeks.from_now + 4.hours, + published_at: ::Faker::Boolean.boolean(true_ratio: 0.5) ? 1.week.ago : nil, + salt: Decidim::Tokenizer.random_salt + }, + visibility: "all" + ) + + rand(1...4).times do + upcoming_question = Decidim.traceability.create!( + Decidim::Elections::Question, + admin_user, + { + election: upcoming_election, + title: Decidim::Faker::Localized.sentence(word_count: 2), + max_selections: ::Faker::Number.between(from: 1, to: 3), + weight: ::Faker::Number.number(digits: 1), + random_answers_order: ::Faker::Boolean.boolean(true_ratio: 0.5), + min_selections: ::Faker::Number.between(from: 0, to: 1) + }, + visibility: "all" + ) + + rand(upcoming_question.max_selections...5).times do + answer = Decidim.traceability.create!( + Decidim::Elections::Answer, + admin_user, + { + question: upcoming_question, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + weight: ::Faker::Number.number(digits: 1), + selected: ::Faker::Boolean.boolean(true_ratio: 0.2) # false + }, + visibility: "all" + ) + + create_attachment(attached_to: answer, filename: "city.jpeg") + end + + questionnaire = Decidim::Forms::Questionnaire.create!( + title: Decidim::Faker::Localized.paragraph, + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + tos: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 2) + end, + questionnaire_for: upcoming_election + ) + + %w(short_answer long_answer).each do |text_question_type| + Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: text_question_type + ) + end + + %w(single_option multiple_option).each do |multiple_choice_question_type| + question = Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: multiple_choice_question_type + ) + + 3.times do + question.answer_options.create!(body: Decidim::Faker::Localized.sentence) + end + end + end + end + + # finished elections that may be published, with questionnaire + 2.times do + finished_election = Decidim.traceability.create!( + Decidim::Elections::Election, + admin_user, + { + component:, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + start_time: 4.weeks.ago, + end_time: 3.weeks.ago, + published_at: 4.weeks.ago, + salt: Decidim::Tokenizer.random_salt + }, + visibility: "all" + ) + + rand(1...4).times do + finished_question = Decidim.traceability.create!( + Decidim::Elections::Question, + admin_user, + { + election: finished_election, + title: Decidim::Faker::Localized.sentence(word_count: 2), + max_selections: 2, + weight: ::Faker::Number.number(digits: 1), + random_answers_order: ::Faker::Boolean.boolean(true_ratio: 0.5), + min_selections: ::Faker::Number.between(from: 0, to: 1) + }, + visibility: "all" + ) + + rand(2...5).times do + answer = Decidim.traceability.create!( + Decidim::Elections::Answer, + admin_user, + { + question: finished_question, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + weight: ::Faker::Number.number(digits: 1), + selected: ::Faker::Boolean.boolean(true_ratio: 0.2) # false + }, + visibility: "all" + ) + + create_attachment(attached_to: answer, filename: "city.jpeg") + end + + questionnaire = Decidim::Forms::Questionnaire.create!( + title: Decidim::Faker::Localized.paragraph, + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + tos: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 2) + end, + questionnaire_for: finished_election + ) + + %w(short_answer long_answer).each do |text_question_type| + Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: text_question_type + ) + end + + %w(single_option multiple_option).each do |multiple_choice_question_type| + question = Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: multiple_choice_question_type + ) + + 3.times do + question.answer_options.create!(body: Decidim::Faker::Localized.sentence) + end + end + end + end + + # finished, published elections with results and with questionnaire + 2.times do + election_with_results = Decidim.traceability.create!( + Decidim::Elections::Election, + admin_user, + { + component:, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + start_time: 4.weeks.ago, + end_time: 3.weeks.ago, + published_at: 3.weeks.ago, + bb_status: "results_published", + salt: Decidim::Tokenizer.random_salt + + }, + visibility: "all" + ) + + bb_closure = Decidim::Elections::BulletinBoardClosure.create!( + election: election_with_results + ) + + valid_ballots = ::Faker::Number.number(digits: 3) + Decidim::Elections::Result.create!( + value: valid_ballots, + closurable: bb_closure, + question: nil, + answer: nil, + result_type: :valid_ballots + ) + + Decidim::Elections::Result.create!( + value: valid_ballots, + closurable: bb_closure, + question: nil, + answer: nil, + result_type: :total_ballots + ) + + rand(1...4).times do + result_question = Decidim.traceability.create!( + Decidim::Elections::Question, + admin_user, + { + election: election_with_results, + title: Decidim::Faker::Localized.sentence(word_count: 2), + max_selections: 2, + weight: ::Faker::Number.number(digits: 1), + random_answers_order: ::Faker::Boolean.boolean(true_ratio: 0.5), + min_selections: ::Faker::Number.between(from: 0, to: 1) + }, + visibility: "all" + ) + + question_pending = valid_ballots + rand(2...5).times do + answer = Decidim.traceability.create!( + Decidim::Elections::Answer, + admin_user, + { + question: result_question, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + weight: ::Faker::Number.number(digits: 1), + selected: ::Faker::Boolean.boolean(true_ratio: 0.5) + }, + visibility: "all" + ) + + create_attachment(attached_to: answer, filename: "city.jpeg") + + answer_value = ::Faker::Number.between(from: 0, to: question_pending) + Decidim::Elections::Result.create!( + value: answer_value, + closurable: bb_closure, + question: result_question, + answer:, + result_type: :valid_answers + ) + question_pending -= answer_value + end + + if result_question.nota_option? && result_question.max_selections == 1 + Decidim::Elections::Result.create!( + value: question_pending, + closurable: bb_closure, + question: result_question, + answer: nil, + result_type: :blank_answers + ) + end + + questionnaire = Decidim::Forms::Questionnaire.create!( + title: Decidim::Faker::Localized.paragraph, + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + tos: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 2) + end, + questionnaire_for: election_with_results + ) + + %w(short_answer long_answer).each do |text_question_type| + Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: text_question_type + ) + end + + %w(single_option multiple_option).each do |multiple_choice_question_type| + question = Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: multiple_choice_question_type + ) + + 3.times do + question.answer_options.create!(body: Decidim::Faker::Localized.sentence) + end + end + end + end + + # ongoing election that is published + ongoing_election = Decidim.traceability.create!( + Decidim::Elections::Election, + admin_user, + { + component:, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + start_time: 2.weeks.ago, + end_time: 2.weeks.from_now + 4.hours, + published_at: 3.weeks.ago, + salt: Decidim::Tokenizer.random_salt + }, + visibility: "all" + ) + + rand(1...4).times do + ongoing_question = Decidim.traceability.create!( + Decidim::Elections::Question, + admin_user, + { + election: ongoing_election, + title: Decidim::Faker::Localized.sentence(word_count: 2), + max_selections: 2, + weight: ::Faker::Number.number(digits: 1), + random_answers_order: ::Faker::Boolean.boolean(true_ratio: 0.5), + min_selections: ::Faker::Number.between(from: 0, to: 1) + }, + visibility: "all" + ) + + rand(3...5).times do + answer = Decidim.traceability.create!( + Decidim::Elections::Answer, + admin_user, + { + question: ongoing_question, + title: Decidim::Faker::Localized.sentence(word_count: 2), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + weight: ::Faker::Number.number(digits: 1), + selected: ::Faker::Boolean.boolean(true_ratio: 0.2) # false + }, + visibility: "all" + ) + + create_attachment(attached_to: answer, filename: "city.jpeg") + end + end + + questionnaire = Decidim::Forms::Questionnaire.create!( + title: Decidim::Faker::Localized.paragraph, + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + tos: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 2) + end, + questionnaire_for: ongoing_election + ) + + %w(short_answer long_answer).each do |text_question_type| + Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: text_question_type + ) + end + + %w(single_option multiple_option).each do |multiple_choice_question_type| + question = Decidim::Forms::Question.create!( + questionnaire:, + body: Decidim::Faker::Localized.paragraph, + question_type: multiple_choice_question_type + ) + + 3.times do + question.answer_options.create!(body: Decidim::Faker::Localized.sentence) + end + end + + %w(admin@example.org user@example.org user2@example.org).each do |email| + trustee = Decidim::Elections::Trustee.find_or_create_by( + user: Decidim::User.find_by(email:), + organization: participatory_space.organization + ) + trustee.trustees_participatory_spaces.create!(participatory_space:) + end + end + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + end + end +end diff --git a/decidim-elections/lib/decidim/elections/test/factories.rb b/decidim-elections/lib/decidim/elections/test/factories.rb new file mode 100644 index 00000000..103272bd --- /dev/null +++ b/decidim-elections/lib/decidim/elections/test/factories.rb @@ -0,0 +1,368 @@ +# frozen_string_literal: true + +require "decidim/components/namer" +require "decidim/core/test/factories" +require "decidim/forms/test/factories" + +FactoryBot.define do + sequence(:private_key) do + JWT::JWK.new(OpenSSL::PKey::RSA.new(4096)) + end + + factory :elections_component, parent: :component do + name { Decidim::Components::Namer.new(participatory_space.organization.available_locales, :elections).i18n_name } + manifest_name { :elections } + participatory_space { create(:participatory_process, :with_steps, organization:) } + end + + factory :election, class: "Decidim::Elections::Election" do + transient do + organization { build(:organization) } + number_of_votes { Faker::Number.number(digits: 2) } + base_id { 10_000 } + end + + upcoming + title { generate_localized_title } + description { Decidim::Faker::Localized.wrapped("

    ", "

    ") { generate_localized_title } } + end_time { 3.days.from_now } + published_at { nil } + blocked_at { nil } + bb_status { nil } + questionnaire + component { create(:elections_component, organization:) } + salt { SecureRandom.hex(32) } + + trait :bb_test do + bb_status { "key_ceremony" } + id { (base_id + Decidim::Elections::Election.bb_statuses.keys.index(bb_status)) } + end + + trait :upcoming do + start_time { 1.day.from_now } + end + + trait :started do + start_time { 2.days.ago } + end + + trait :ongoing do + started + blocked_at { Time.current } + end + + trait :finished do + started + end_time { 1.day.ago } + blocked_at { Time.current } + end + + trait :published do + published_at { Time.current } + end + + trait :complete do + after(:build) do |election, _evaluator| + election.questions << build(:question, :yes_no, election:, weight: 1) + election.questions << build(:question, :candidates, election:, weight: 3) + election.questions << build(:question, :projects, election:, weight: 2) + election.questions << build(:question, :nota, election:, weight: 4) + end + end + + trait :ready_for_setup do + transient do + trustee_keys { 3.times.to_h { [Faker::Name.name, generate(:private_key).export.to_json] } } + end + + upcoming + published + complete + + after(:create) do |election, evaluator| + evaluator.trustee_keys.each do |name, key| + create(:trustee, :with_public_key, name:, election:, public_key: key) + end + end + end + + trait :created do + ready_for_setup + blocked_at { start_time - 1.day } + + start_time { 1.hour.from_now } + bb_status { "created" } + + after(:create) do |election| + trustees_participatory_spaces = Decidim::Elections::TrusteesParticipatorySpace.where(participatory_space: election.component.participatory_space) + election.trustees << trustees_participatory_spaces.map(&:trustee) + end + end + + trait :key_ceremony do + created + bb_status { "key_ceremony" } + end + + trait :key_ceremony_ended do + key_ceremony + bb_status { "key_ceremony_ended" } + end + + trait :vote do + key_ceremony_ended + ongoing + bb_status { "vote" } + end + + trait :vote_ended do + key_ceremony_ended + ongoing + finished + bb_status { "vote_ended" } + + after(:create) do |election, evaluator| + create_list(:vote, evaluator.number_of_votes, :accepted, election:) + end + end + + trait :tally_started do + vote_ended + bb_status { "tally_started" } + end + + trait :tally_ended do + tally_started + bb_status { "tally_ended" } + verifiable_results_file_hash { SecureRandom.hex(32) } + verifiable_results_file_url { Faker::Internet.url } + + after(:create) do |election| + create(:bb_closure, :with_results, election:) + end + end + + trait :results_published do + tally_ended + bb_status { "results_published" } + end + + trait :with_photos do + transient do + photos_number { 2 } + end + + after :create do |election, evaluator| + evaluator.photos_number.times do + election.attachments << create( + :attachment, + :with_image, + attached_to: election + ) + end + end + end + end + + factory :question, class: "Decidim::Elections::Question" do + transient do + more_information { false } + answers { 3 } + end + + election + title { generate_localized_title } + min_selections { 1 } + max_selections { 1 } + weight { Faker::Number.number(digits: 1) } + random_answers_order { true } + + trait :complete do + after(:build) do |question, evaluator| + overrides = { question: } + overrides[:description] = nil unless evaluator.more_information + question.answers = build_list(:election_answer, evaluator.answers, overrides) + end + end + + trait :yes_no do + complete + random_answers_order { false } + end + + trait :candidates do + complete + max_selections { 6 } + answers { 10 } + end + + trait :projects do + complete + max_selections { 3 } + answers { 6 } + more_information { true } + end + + trait :nota do + complete + max_selections { 4 } + answers { 8 } + min_selections { 0 } + end + + trait :with_votes do + after(:build) do |question, evaluator| + overrides = { question: } + overrides[:description] = nil unless evaluator.more_information + question.answers = build_list(:election_answer, evaluator.answers, :with_votes, overrides) + end + end + end + + factory :election_answer, class: "Decidim::Elections::Answer" do + question + title { generate_localized_title } + description { Decidim::Faker::Localized.wrapped("

    ", "

    ") { generate_localized_title } } + weight { Faker::Number.number(digits: 1) } + selected { false } + + trait :with_votes do + after(:build) do |answer| + create(:election_result, election: answer.question.election, question: answer.question, answer:) + end + end + + trait :with_photos do + transient do + photos_number { 2 } + end + + after :create do |election, evaluator| + evaluator.photos_number.times do + election.attachments << create( + :attachment, + :with_image, + attached_to: election + ) + end + end + end + end + + factory :bb_closure, class: "Decidim::Elections::BulletinBoardClosure" do + initialize_with do + Decidim::Elections::BulletinBoardClosure.find_or_create_by( + decidim_elections_election_id: election.id + ) + end + + election { create(:election, :complete) } + + trait :with_results do + after :create do |closure| + total_votes = closure.election.votes.count + closure.election.questions.each do |question| + max = total_votes + question.answers.each do |answer| + value = Faker::Number.between(from: 0, to: max) + closure.results << create(:election_result, election: closure.election, question:, answer:, value:) + max -= value + end + closure.results << create(:election_result, :blank_ballots, election: closure.election, question:, value: max) + end + closure.results << create(:election_result, :total_ballots, election: closure.election, value: total_votes) + end + end + end + + factory :election_result, class: "Decidim::Elections::Result" do + transient do + election { create(:election, :tally_ended) } + end + + closurable { create :bb_closure, election: } + question { create :question, election: } + answer { create :election_answer, question: } + value { Faker::Number.number(digits: 1) } + result_type { "valid_answers" } + + trait :null_ballots do + result_type { "null_ballots" } + answer { nil } + end + + trait :blank_ballots do + result_type { "blank_ballots" } + answer { nil } + end + + trait :total_ballots do + result_type { "total_ballots" } + answer { nil } + question { nil } + end + end + + factory :action, class: "Decidim::Elections::Action" do + election + message_id { "a.message+id" } + status { :pending } + action { :start_key_ceremony } + end + + factory :trustee, class: "Decidim::Elections::Trustee" do + transient do + election { nil } + end + + public_key { nil } + user { build(:user, :confirmed, organization:) } + organization { create(:organization) } + + trait :considered do + after(:build) do |trustee, evaluator| + trustee.trustees_participatory_spaces << build(:trustees_participatory_space, trustee:, election: evaluator.election, organization: evaluator.organization) + end + end + + trait :with_elections do + after(:build) do |trustee, evaluator| + trustee.elections << build(:election, :upcoming, organization: evaluator.organization) + end + end + + trait :with_public_key do + considered + name { Faker::Name.unique.name } + public_key { generate(:private_key).export.to_json } + end + end + + factory :trustees_participatory_space, class: "Decidim::Elections::TrusteesParticipatorySpace" do + transient do + organization { election&.component&.participatory_space&.organization || create(:organization) } + election { nil } + end + participatory_space { election&.component&.participatory_space || create(:participatory_process, organization:) } + considered { true } + trustee { create(:trustee, organization:) } + + trait :trustee_ready do + association :trustee, :with_public_key + end + end + + factory :vote, class: "Decidim::Elections::Vote" do + election { create(:election) } + sequence(:voter_id) { |n| "voter_#{n}" } + encrypted_vote_hash { "adf89asd0f89das7f" } + status { "pending" } + message_id { "decidim-test-authority.2.vote.cast+v.5826de088371d1b15b38f00c8203871caec07041ed0c8fb0c6fb875f0df763b6" } + user { build(:user) } + email { "an_email@example.org" } + + trait :accepted do + status { "accepted" } + end + end +end diff --git a/decidim-elections/lib/decidim/elections/trustee_zone.rb b/decidim-elections/lib/decidim/elections/trustee_zone.rb new file mode 100644 index 00000000..ca8c1248 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/trustee_zone.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Elections + # This module contains all the domain logic associated to Decidim's Elections + # trustee zone. + module TrusteeZone + end + end +end diff --git a/decidim-elections/lib/decidim/elections/trustee_zone_engine.rb b/decidim-elections/lib/decidim/elections/trustee_zone_engine.rb new file mode 100644 index 00000000..66d119f2 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/trustee_zone_engine.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "decidim/elections/menu" + +module Decidim + module Elections + # This is the engine that runs on the public interface for trustees of `decidim-elections`. + # It mostly handles rendering the trustees frontend zone. + class TrusteeZoneEngine < ::Rails::Engine + isolate_namespace Decidim::Elections::TrusteeZone + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + routes do + resource :trustee, path: "/", only: [:show, :update] do + resources :election, only: [] do + resource :elections, only: [:show, :update] + end + end + end + + def load_seed + nil + end + + initializer "decidim_elections.trustee_zone.menu" do + Decidim::Elections::Menu.register_user_menu! + end + end + end +end diff --git a/decidim-elections/lib/decidim/elections/version.rb b/decidim-elections/lib/decidim/elections/version.rb new file mode 100644 index 00000000..0d212e78 --- /dev/null +++ b/decidim-elections/lib/decidim/elections/version.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + # This holds the decidim-meetings version. + module Elections + def self.version + "0.29.0.dev" + end + end +end diff --git a/decidim-elections/lib/decidim/votings.rb b/decidim-elections/lib/decidim/votings.rb new file mode 100644 index 00000000..443cc6ff --- /dev/null +++ b/decidim-elections/lib/decidim/votings.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "decidim/votings/admin" +require "decidim/votings/api" +require "decidim/votings/polling_officer_zone" +require "decidim/votings/census_admin" +require "decidim/votings/census" +require "decidim/votings/engine" +require "decidim/votings/admin_engine" +require "decidim/votings/polling_officer_zone_engine" +require "decidim/votings/participatory_space" +require "decidim/votings/census_engine" +require "decidim/votings/census_admin_engine" +require "rack/attack" + +module Decidim + # This namespace holds the logic of the `Votings` space. + module Votings + autoload :VotingSerializer, "decidim/votings/voting_serializer" + + include ActiveSupport::Configurable + + # Max requests in a time period to check the census. Only applied in production and test. + config_accessor :check_census_max_requests do + 5 + end + + # Time window in which the throttling is applied. + config_accessor :throttling_period do + 1.minute + end + end +end diff --git a/decidim-elections/lib/decidim/votings/admin.rb b/decidim-elections/lib/decidim/votings/admin.rb new file mode 100644 index 00000000..d3cac909 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/admin.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This module contains all the domain logic associated to Decidim's Votings + # space admin panel. + module Admin + end + end +end diff --git a/decidim-elections/lib/decidim/votings/admin_engine.rb b/decidim-elections/lib/decidim/votings/admin_engine.rb new file mode 100644 index 00000000..bd2bb5ca --- /dev/null +++ b/decidim-elections/lib/decidim/votings/admin_engine.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "decidim/votings/menu" + +module Decidim + module Votings + # Decidim's Votings Rails Admin Engine. + class AdminEngine < ::Rails::Engine + isolate_namespace Decidim::Votings::Admin + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + routes do + resources :votings, param: :slug do + resource :publish, controller: "voting_publications", only: [:create, :destroy] + member do + get :available_polling_officers + end + + resource :landing_page, only: [:edit, :update], controller: "votings_landing_page" do + resources :content_blocks, only: [:edit, :update, :destroy, :create], controller: "votings_landing_page_content_blocks" + end + + resources :polling_stations + resources :polling_officers, only: [:new, :create, :destroy, :index] + resources :monitoring_committee_members, only: [:new, :create, :destroy, :index] + resources :monitoring_committee_polling_station_closures, only: [:index, :edit, :show] do + post :validate, on: :member + end + resources :monitoring_committee_verify_elections, only: [:index] + resources :monitoring_committee_election_results, only: [:index, :show, :update] + resources :attachments, controller: "voting_attachments", except: [:show] + resources :attachment_collections, controller: "voting_attachment_collections", except: [:show] + resources :ballot_styles + + resource :census, only: [:show, :destroy, :create], controller: "/decidim/votings/census/admin/census" do + member do + get :status + get :generate_access_codes + get :export_access_codes + get :download_access_codes_file + end + end + end + + scope "/votings/:voting_slug" do + resources :components do + resource :permissions, controller: "component_permissions" + member do + put :publish + put :unpublish + get :share + end + resources :exports, only: :create + resources :imports, only: [:new, :create] do + get :example, on: :collection + end + resources :reminders, only: [:new, :create] + end + end + + scope "/votings/:voting_slug/components/:component_id/manage" do + Decidim.component_manifests.each do |manifest| + next unless manifest.admin_engine + + constraints CurrentComponent.new(manifest) do + mount manifest.admin_engine, at: "/", as: "decidim_admin_voting_#{manifest.name}" + end + end + end + end + + initializer "decidim_votings_admin.menu" do + Decidim::Votings::Menu.register_admin_menu_modules! + Decidim::Votings::Menu.register_admin_votings_components_menu! + Decidim::Votings::Menu.register_votings_admin_attachments_menu! + Decidim::Votings::Menu.register_decidim_votings_monitoring_committee_menu! + Decidim::Votings::Menu.register_admin_voting_menu! + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/api.rb b/decidim-elections/lib/decidim/votings/api.rb new file mode 100644 index 00000000..06711821 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/api.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Decidim + module Votings + autoload :VotingType, "decidim/api/voting_type" + autoload :PollingStationType, "decidim/api/polling_station_type" + autoload :PollingStationClosureType, "decidim/api/polling_station_closure_type" + end +end diff --git a/decidim-elections/lib/decidim/votings/census.rb b/decidim-elections/lib/decidim/votings/census.rb new file mode 100644 index 00000000..b85836aa --- /dev/null +++ b/decidim-elections/lib/decidim/votings/census.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This module contains all the domain logic associated to Decidim's Votings + # Census. + module Census + include ActiveSupport::Configurable + + # How long the census access codes export file will are available in the server + config_accessor :census_access_codes_export_expiry_time do + 2.days + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/census_admin.rb b/decidim-elections/lib/decidim/votings/census_admin.rb new file mode 100644 index 00000000..1b1b5b49 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/census_admin.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module Census + # This module contains all the domain logic associated to Decidim's Votings + # Census admin panel. + module Admin + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/census_admin_engine.rb b/decidim-elections/lib/decidim/votings/census_admin_engine.rb new file mode 100644 index 00000000..5f3a57f2 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/census_admin_engine.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require "decidim/votings/census_menu" + +module Decidim + module Votings + class CensusEngine < ::Rails::Engine + isolate_namespace Decidim::Votings::Census::Admin + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + initializer "decidim_votings_census.admin_voting_menu" do + Decidim::Votings::CensusMenu.register_admin_voting_menu! + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/census_engine.rb b/decidim-elections/lib/decidim/votings/census_engine.rb new file mode 100644 index 00000000..1f8167e0 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/census_engine.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class CensusEngine < ::Rails::Engine + isolate_namespace Decidim::Votings::Census + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + # routes do + # end + + def load_seed + nil + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/census_menu.rb b/decidim-elections/lib/decidim/votings/census_menu.rb new file mode 100644 index 00000000..2195824e --- /dev/null +++ b/decidim-elections/lib/decidim/votings/census_menu.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class CensusMenu + def self.register_admin_voting_menu! + Decidim.menu :admin_voting_menu do |menu| + menu.add_item :voting_census, + I18n.t("census", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_census_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_census_path(current_participatory_space)), + icon_name: "mail-line", + if: allowed_to?(:manage, :census) + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/content_blocks/registry_manager.rb b/decidim-elections/lib/decidim/votings/content_blocks/registry_manager.rb new file mode 100644 index 00000000..0757d663 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/content_blocks/registry_manager.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module Decidim + module Votings + module ContentBlocks + class RegistryManager + def self.register! + Decidim.content_blocks.register(:homepage, :highlighted_votings) do |content_block| + content_block.cell = "decidim/votings/content_blocks/highlighted_votings" + content_block.public_name_key = "decidim.votings.content_blocks.highlighted_votings.name" + content_block.settings_form_cell = "decidim/votings/content_blocks/highlighted_votings_settings_form" + + content_block.settings do |settings| + settings.attribute :max_results, type: :integer, default: 4 + end + end + + Decidim.content_blocks.register(:voting_landing_page, :hero) do |content_block| + content_block.cell = "decidim/votings/content_blocks/hero" + content_block.settings_form_cell = "decidim/content_blocks/participatory_space_hero_settings_form" + content_block.public_name_key = "decidim.content_blocks.hero.name" + + content_block.images = [ + { + name: :background_image, + uploader: "Decidim::BackgroundImageUploader" + } + ] + + content_block.settings do |settings| + settings.attribute :button_text, type: :text, translated: true + settings.attribute :button_url, type: :text, translated: true + end + + content_block.default! + end + + Decidim.content_blocks.register(:voting_landing_page, :title) do |content_block| + content_block.cell = "decidim/votings/content_blocks/main_data" + content_block.public_name_key = "decidim.votings.admin.content_blocks.main_data.name" + content_block.default! + end + + Decidim.content_blocks.register(:voting_landing_page, :related_elections) do |content_block| + content_block.cell = "decidim/elections/content_blocks/related_elections" + content_block.settings_form_cell = "decidim/content_blocks/highlighted_elements_for_component_settings_form" + content_block.public_name_key = "decidim.votings.admin.content_blocks.related_elections.name" + content_block.component_manifest_name = "elections" + + content_block.settings do |settings| + settings.attribute :component_id, type: :select, default: nil + end + end + + Decidim.content_blocks.register(:voting_landing_page, :polling_stations) do |content_block| + content_block.cell = "decidim/votings/content_blocks/polling_stations" + content_block.public_name_key = "decidim.votings.admin.content_blocks.polling_stations.name" + content_block.default! + end + + Decidim.content_blocks.register(:voting_landing_page, :stats) do |content_block| + content_block.cell = "decidim/votings/content_blocks/statistics" + content_block.public_name_key = "decidim.votings.admin.content_blocks.stats.name" + content_block.default! + end + + Decidim.content_blocks.register(:voting_landing_page, :metrics) do |content_block| + content_block.cell = "decidim/votings/content_blocks/metrics" + content_block.public_name_key = "decidim.votings.admin.content_blocks.metrics.name" + end + + Decidim.content_blocks.register(:voting_landing_page, :html) do |content_block| + content_block.cell = "decidim/content_blocks/html" + content_block.public_name_key = "decidim.content_blocks.html.name" + content_block.settings_form_cell = "decidim/content_blocks/html_settings_form" + + content_block.settings do |settings| + settings.attribute :html_content, type: :text, translated: true + end + end + + Decidim.content_blocks.register(:voting_landing_page, :related_documents) do |content_block| + content_block.cell = "decidim/content_blocks/participatory_space_documents" + content_block.public_name_key = "decidim.application.documents.related_documents" + end + + Decidim.content_blocks.register(:voting_landing_page, :related_images) do |content_block| + content_block.cell = "decidim/content_blocks/participatory_space_images" + content_block.public_name_key = "decidim.application.photos.related_photos" + end + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/engine.rb b/decidim-elections/lib/decidim/votings/engine.rb new file mode 100644 index 00000000..79987264 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/engine.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require "decidim/votings/content_blocks/registry_manager" +require "decidim/votings/menu" +require "decidim/votings/query_extensions" + +module Decidim + module Votings + # This is the engine that runs on the public interface for Votings of `decidim-elections`. + class Engine < ::Rails::Engine + isolate_namespace Decidim::Votings + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + routes do + resources :votings, param: :slug, only: [:index, :show, :update] do + get :check_census, action: :show_check_census + post :check_census, action: :check_census + match :login, via: [:get, :post] + post :send_access_code + get :elections_log + end + + get "votings/:voting_id", to: redirect { |params, _request| + voting = Decidim::Votings::Voting.find(params[:voting_id]) + voting ? "/votings/#{voting.slug}" : "/404" + }, constraints: { voting_id: /[0-9]+/ } + + get "/votings/:voting_id/f/:component_id", to: redirect { |params, _request| + voting = Decidim::Votings::Voting.find(params[:voting_id]) + voting ? "/votings/#{voting.slug}/f/#{params[:component_id]}" : "/404" + }, constraints: { voting_id: /[0-9]+/ } + + scope "/votings/:voting_slug/f/:component_id" do + Decidim.component_manifests.each do |manifest| + next unless manifest.engine + + constraints CurrentComponent.new(manifest) do + mount manifest.engine, at: "/", as: "decidim_voting_#{manifest.name}" + end + end + end + end + + initializer "decidim_votings.register_icons" do + Decidim.icons.register(name: "map-line", icon: "map-line", category: "system", description: "Icon used to display Polling Stations on maps", engine: :votings) + Decidim.icons.register(name: "Decidim::Votings::Voting", icon: "check-double-fill", description: "Voting", category: "activity", engine: :votings) + Decidim.icons.register(name: "lock-unlock-line", icon: "lock-unlock-line", category: "system", description: "", engine: :votings) + end + + initializer "decidim_votings.stats" do + Decidim.stats.register :votings_count, priority: StatsRegistry::HIGH_PRIORITY do |organization, _start_at, _end_at| + Decidim::Votings::Voting.where(organization:).published.count + end + end + + initializer "decidim_votings.add_cells_view_paths" do + Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Votings::Engine.root}/app/cells") + Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Votings::Engine.root}/app/views") # for partials + end + + initializer "decidim_votings.menu" do + Decidim::Votings::Menu.register_menu! + Decidim::Votings::Menu.register_home_content_block_menu! + end + + initializer "decidim_votings.content_blocks" do + Decidim::Votings::ContentBlocks::RegistryManager.register! + end + + initializer "decidim_votings.query_extensions" do + Decidim::Api::QueryType.include Decidim::Votings::QueryExtensions + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/menu.rb b/decidim-elections/lib/decidim/votings/menu.rb new file mode 100644 index 00000000..5eb0225a --- /dev/null +++ b/decidim-elections/lib/decidim/votings/menu.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class Menu + def self.register_menu! + Decidim.menu :menu do |menu| + menu.add_item :votings, + I18n.t("menu.votings", scope: "decidim"), + decidim_votings.votings_path, + position: 2.6, + if: Decidim::Votings::Voting.where(organization: current_organization).published.any?, + active: :inclusive + end + end + + def self.register_home_content_block_menu! + Decidim.menu :home_content_block_menu do |menu| + menu.add_item :votings, + I18n.t("menu.votings", scope: "decidim"), + decidim_votings.votings_path, + position: 40, + if: Decidim::Votings::Voting.where(organization: current_organization).published.any?, + active: :inclusive + end + end + + def self.register_admin_menu_modules! + Decidim.menu :admin_menu_modules do |menu| + menu.add_item :votings, + I18n.t("menu.votings", scope: "decidim.votings.admin"), + decidim_admin_votings.votings_path, + icon_name: "mail-line", + position: 2.6, + active: :inclusive, + if: allowed_to?(:enter, :space_area, space_name: :votings) + end + end + + def self.register_admin_votings_components_menu! + Decidim.menu :admin_votings_components_menu do |menu| + current_participatory_space.components.each do |component| + caption = decidim_escape_translated(component.name) + caption += content_tag(:span, component.primary_stat, class: "component-counter") if component.primary_stat.present? + + menu.add_item [component.manifest_name, component.id].join("_"), + caption.html_safe, + manage_component_path(component), + active: is_active_link?(manage_component_path(component)) || + is_active_link?(decidim_admin_votings.edit_component_path(current_participatory_space, component)) || + is_active_link?(decidim_admin_votings.edit_component_permissions_path(current_participatory_space, component)) || + participatory_space_active_link?(component), + if: component.manifest.admin_engine # && user_role_config.component_is_accessible?(component.manifest_name) + end + end + end + + def self.register_votings_admin_attachments_menu! + Decidim.menu :votings_admin_attachments_menu do |menu| + menu.add_item :voting_attachments, + I18n.t("attachment_files", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_attachments_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_attachments_path(current_participatory_space)), + if: allowed_to?(:read, :attachment, voting: current_participatory_space), + icon_name: "attachment-line" + menu.add_item :voting_attachment_collections, + I18n.t("attachment_collections", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_attachment_collections_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_attachment_collections_path(current_participatory_space)), + if: allowed_to?(:read, :attachment_collection, voting: current_participatory_space), + icon_name: "folder-line" + end + end + + def self.register_decidim_votings_monitoring_committee_menu! + Decidim.menu :decidim_votings_monitoring_committee_menu do |menu| + menu.add_item :voting_monitoring_committee_members, + I18n.t("monitoring_committee_members", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_monitoring_committee_members_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_monitoring_committee_members_path(current_participatory_space)), + if: allowed_to?(:read, :monitoring_committee_members) + menu.add_item :monitoring_committee_polling_station_closures, + I18n.t("monitoring_committee_polling_station_closures", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_monitoring_committee_polling_station_closures_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_monitoring_committee_polling_station_closures_path(current_participatory_space)), + if: allowed_to?(:read, :monitoring_committee_polling_station_closures, voting: current_participatory_space) + menu.add_item :monitoring_committee_verify_elections, + I18n.t("monitoring_committee_verify_elections", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_monitoring_committee_verify_elections_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_monitoring_committee_verify_elections_path(current_participatory_space)), + if: allowed_to?(:read, :monitoring_committee_verify_elections, voting: current_participatory_space) + menu.add_item :monitoring_committee_election_results, + I18n.t("monitoring_committee_election_results", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_monitoring_committee_election_results_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.voting_monitoring_committee_election_results_path(current_participatory_space)), + if: allowed_to?(:read, :monitoring_committee_election_results, voting: current_participatory_space) + end + end + + def self.register_admin_voting_menu! + Decidim.menu :admin_voting_menu do |menu| + menu.add_item :edit_voting, + I18n.t("info", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.edit_voting_path(current_participatory_space), + icon_name: "information-line", + if: allowed_to?(:edit, :voting, voting: current_participatory_space) + + menu.add_item :edit_voting_landing_page, + I18n.t("landing_page", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.edit_voting_landing_page_path(current_participatory_space), + icon_name: "layout-masonry-line", + if: allowed_to?(:update, :landing_page) + + menu.add_item :components, + I18n.t("components", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.components_path(current_participatory_space), + active: is_active_link?(decidim_admin_votings.components_path(current_participatory_space), + ["decidim/votings/admin/components", %w(index new edit)]), + icon_name: "tools-line", + if: allowed_to?(:read, :components, voting: current_participatory_space), + submenu: { target_menu: :admin_votings_components_menu } + + menu.add_item :attachments, + I18n.t("attachments", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_attachments_path(current_participatory_space), + icon_name: "attachment-2", + active: is_active_link?(decidim_admin_votings.voting_attachments_path(current_participatory_space)) || + is_active_link?(decidim_admin_votings.voting_attachment_collections_path(current_participatory_space)), + if: allowed_to?(:read, :attachment, voting: current_participatory_space) || + allowed_to?(:read, :attachment_collection, voting: current_participatory_space) + + menu.add_item :voting_polling_stations, + I18n.t("polling_stations", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_polling_stations_path(current_participatory_space), + icon_name: "mail-line", + if: !current_participatory_space.online_voting? && allowed_to?(:read, :polling_stations) + + menu.add_item :voting_polling_officers, + I18n.t("polling_officers", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_polling_officers_path(current_participatory_space), + icon_name: "mail-line", + if: !current_participatory_space.online_voting? && allowed_to?(:read, :polling_officers) + + menu.add_item :voting_monitoring_committee, + I18n.t("monitoring_committee", scope: "decidim.votings.admin.menu.votings_submenu"), + "#", + icon_name: "mail-line", + active: false, + if: !current_participatory_space.online_voting? && allowed_to?(:read, :monitoring_committee_menu, voting: current_participatory_space), + submenu: { target_menu: :decidim_votings_monitoring_committee_menu } + + menu.add_item :voting_ballot_styles, + I18n.t("ballot_styles", scope: "decidim.votings.admin.menu.votings_submenu"), + decidim_admin_votings.voting_ballot_styles_path(current_participatory_space), + icon_name: "mail-line", + if: allowed_to?(:read, :ballot_styles) + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/participatory_space.rb b/decidim-elections/lib/decidim/votings/participatory_space.rb new file mode 100644 index 00000000..27f0f210 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/participatory_space.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +Decidim.register_participatory_space(:votings) do |participatory_space| + participatory_space.icon = "media/images/decidim_votings.svg" + participatory_space.model_class_name = "Decidim::Votings::Voting" + participatory_space.content_blocks_scope_name = "voting_landing_page" + participatory_space.permissions_class_name = "Decidim::Votings::Permissions" + participatory_space.stylesheet = "decidim/votings/votings" + participatory_space.query_type = "Decidim::Votings::VotingType" + + participatory_space.breadcrumb_cell = "decidim/votings/voting_dropdown_metadata" + + participatory_space.participatory_spaces do |organization| + Decidim::Votings::Voting.where(organization:) + end + + participatory_space.register_resource(:voting) do |resource| + resource.model_class_name = "Decidim::Votings::Voting" + resource.card = "decidim/votings/voting" + resource.searchable = true + end + + participatory_space.context(:public) do |context| + context.engine = Decidim::Votings::Engine + context.layout = "layouts/decidim/votings" + context.helper = "Decidim::Votings::ApplicationHelper" + end + + participatory_space.context(:admin) do |context| + context.engine = Decidim::Votings::AdminEngine + context.layout = "layouts/decidim/admin/voting" + end + + participatory_space.exports :votings do |export| + export.collection do |voting| + Decidim::Votings::Voting.where(id: voting.id) + end + + export.include_in_open_data = true + + export.serializer Decidim::Votings::VotingSerializer + end + + participatory_space.seeds do + require "decidim/votings/seeds" + + Decidim::Votings::Seeds.new.call + end +end + +Decidim.register_global_engine( + :decidim_votings_polling_officer_zone, + Decidim::Votings::PollingOfficerZoneEngine, + at: "/polling_officers" +) diff --git a/decidim-elections/lib/decidim/votings/polling_officer_zone.rb b/decidim-elections/lib/decidim/votings/polling_officer_zone.rb new file mode 100644 index 00000000..bc285543 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/polling_officer_zone.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This module contains all the domain logic associated to Decidim's Votings + # polling officer zone. + module PollingOfficerZone + end + end +end diff --git a/decidim-elections/lib/decidim/votings/polling_officer_zone_engine.rb b/decidim-elections/lib/decidim/votings/polling_officer_zone_engine.rb new file mode 100644 index 00000000..7318ba8b --- /dev/null +++ b/decidim-elections/lib/decidim/votings/polling_officer_zone_engine.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "decidim/votings/polling_officer_zone_menu" + +module Decidim + module Votings + # This is the engine that runs on the public interface for polling officers of `decidim-elections`. + # It mostly handles rendering the polling officers frontend zone. + class PollingOfficerZoneEngine < ::Rails::Engine + isolate_namespace Decidim::Votings::PollingOfficerZone + + paths["db/migrate"] = nil + paths["lib/tasks"] = nil + + routes do + resources :polling_officers, path: "/", only: [:index] do + resources :elections, only: [:index] do + resource :closure do + member do + post :certify + post :sign + end + end + resources :in_person_votes, only: [:new, :create, :show, :update] + end + end + end + + def load_seed + nil + end + + initializer "decidim_elections.polling_officer_zone.menu" do + Decidim::Votings::PollingOfficerZoneMenu.register_user_menu! + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/polling_officer_zone_menu.rb b/decidim-elections/lib/decidim/votings/polling_officer_zone_menu.rb new file mode 100644 index 00000000..b807144a --- /dev/null +++ b/decidim-elections/lib/decidim/votings/polling_officer_zone_menu.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Decidim + module Votings + class PollingOfficerZoneMenu + def self.register_user_menu! + Decidim.menu :user_menu do |menu| + menu.add_item :decidim_votings_polling_officer_zone, + I18n.t("menu.polling_officer_zone", scope: "decidim.votings.polling_officer_zone"), + decidim.decidim_votings_polling_officer_zone_path, + active: :inclusive, + if: Decidim::Votings::PollingOfficer.polling_officer?(current_user) + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/query_extensions.rb b/decidim-elections/lib/decidim/votings/query_extensions.rb new file mode 100644 index 00000000..00153d9b --- /dev/null +++ b/decidim-elections/lib/decidim/votings/query_extensions.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This module's job is to extend the API with custom fields related to + # decidim-votings. + module QueryExtensions + # Public: Extends a type with `decidim-votings`'s fields. + # + # type - A GraphQL::BaseType to extend. + # + # Returns nothing. + def self.included(type) + type.field :votings, + [Decidim::Votings::VotingType], + null: true, + description: "Lists all votings" do + argument :filter, Decidim::ParticipatoryProcesses::ParticipatoryProcessInputFilter, "This argument lets you filter the results", required: false + argument :order, Decidim::ParticipatoryProcesses::ParticipatoryProcessInputSort, "This argument lets you order the results", required: false + end + + type.field :voting, + Decidim::Votings::VotingType, + null: true, + description: "Finds a voting" do + argument :id, GraphQL::Types::ID, "The ID of the participatory space", required: false + end + end + + def votings(filter: {}, order: {}) + manifest = Decidim.participatory_space_manifests.select { |m| m.name == :votings }.first + + Decidim::Core::ParticipatorySpaceListBase.new(manifest:).call(object, { filter:, order: }, context) + end + + def voting(id: nil) + manifest = Decidim.participatory_space_manifests.select { |m| m.name == :votings }.first + + Decidim::Core::ParticipatorySpaceFinderBase.new(manifest:).call(object, { id: }, context) + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/seeds.rb b/decidim-elections/lib/decidim/votings/seeds.rb new file mode 100644 index 00000000..fe32e399 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/seeds.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +require "decidim/seeds" + +module Decidim + module Votings + class Seeds < Decidim::Seeds + def call + Decidim::Votings::Voting.voting_types.values.each do |voting_type| + voting = create_voting!(voting_type:) + + unless voting.online_voting? + 3.times do + polling_station = create_polling_station!(voting:) + + create_polling_officer!(voting:, polling_station:) + end + end + + create_landing_page!(voting:) + + 2.times do + create_category!(participatory_space: voting) + end + + Decidim.component_manifests.each do |manifest| + manifest.seed!(voting.reload) + end + + unless voting.online_voting? + voting.reload.published_elections.finished.each do |election| + create_results!(election:, voting:) + end + end + + (1..2).each do |i| + ballot_style = voting.ballot_styles.create!(code: "DISTRICT#{i}") + voting.elections.each do |election| + election.questions.sample(1 + rand(election.questions.count)).each do |question| + ballot_style.ballot_style_questions.create!(question:) + end + end + end + end + end + + def create_voting!(voting_type: Decidim::Votings::Voting.voting_types.values.sample) + n = rand(3) + params = { + organization:, + title: Decidim::Faker::Localized.sentence(word_count: 5), + slug: Decidim::Faker::Internet.unique.slug(words: nil, glue: "-"), + description: Decidim::Faker::Localized.wrapped("

    ", "

    ") do + Decidim::Faker::Localized.paragraph(sentence_count: 3) + end, + scope: n.positive? ? nil : Decidim::Scope.all.sample, + banner_image: ::Faker::Boolean.boolean(true_ratio: 0.5) ? banner_image : nil, # Keep after organization + published_at: 2.weeks.ago, + start_time: n.weeks.from_now, + end_time: (n + 1).weeks.from_now + 4.hours, + voting_type:, + promoted: n.odd? + } + + voting = Decidim.traceability.perform_action!( + "publish", + Decidim::Votings::Voting, + organization.users.first, + visibility: "all" + ) do + Decidim::Votings::Voting.create!(params) + end + voting.add_to_index_as_search_resource + + voting + end + + def create_polling_station!(voting:) + params = { + voting:, + title: Decidim::Faker::Localized.sentence(word_count: 5), + address: ::Faker::Address.full_address, + latitude: ::Faker::Address.latitude, + longitude: ::Faker::Address.longitude, + location: Decidim::Faker::Localized.sentence, + location_hints: Decidim::Faker::Localized.sentence + } + + Decidim.traceability.create!( + Decidim::Votings::PollingStation, + organization.users.first, + params, + visibility: "all" + ) + end + + def create_polling_officer!(voting:, polling_station:) + email = "voting_#{voting.id}_president_#{polling_station.id}@example.org" + user = find_or_initialize_user_by(email:) + + Decidim.traceability.create!( + Decidim::Votings::PollingOfficer, + organization.users.first, + { + voting:, + user:, + presided_polling_station: polling_station + }, + visibility: "all" + ) + end + + def create_landing_page!(voting:) + landing_page_content_blocks = [:hero, :title, :related_elections, :polling_stations, :related_documents, :related_images, :stats, :metrics] + + landing_page_content_blocks.each.with_index(1) do |manifest_name, index| + Decidim::ContentBlock.create( + organization:, + scope_name: :voting_landing_page, + manifest_name:, + weight: index, + scoped_resource_id: voting.id, + published_at: Time.current + ) + end + end + + def create_results!(election:, voting:) + polling_officer = voting.polling_officers.sample + ps_closure = Decidim::Votings::PollingStationClosure.create!( + election:, + polling_officer:, + polling_station: polling_officer.polling_station, + signed_at: Time.current, + phase: :complete + ) + + valid_ballots = ::Faker::Number.number(digits: 3) + Decidim::Elections::Result.create!( + value: valid_ballots, + closurable: ps_closure, + question: nil, + answer: nil, + result_type: :valid_ballots + ) + + null_ballots = ::Faker::Number.number(digits: 1) + Decidim::Elections::Result.create!( + value: null_ballots, + closurable: ps_closure, + question: nil, + answer: nil, + result_type: :null_ballots + ) + + blank_ballots = ::Faker::Number.number(digits: 2) + Decidim::Elections::Result.create!( + value: blank_ballots, + closurable: ps_closure, + question: nil, + answer: nil, + result_type: :blank_ballots + ) + + Decidim::Elections::Result.create!( + value: valid_ballots + null_ballots + blank_ballots, + closurable: ps_closure, + question: nil, + answer: nil, + result_type: :total_ballots + ) + + election.questions.each do |question| + question_pending = valid_ballots + question.answers.shuffle.each do |answer| + answer_value = ::Faker::Number.between(from: 0, to: question_pending) + Decidim::Elections::Result.create!( + value: answer_value, + closurable: ps_closure, + question:, + answer:, + result_type: :valid_answers + ) + question_pending -= answer_value + end + + next unless question.nota_option? + + Decidim::Elections::Result.create!( + value: question_pending, + closurable: ps_closure, + question:, + answer: nil, + result_type: :blank_answers + ) + end + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/test/factories.rb b/decidim-elections/lib/decidim/votings/test/factories.rb new file mode 100644 index 00000000..d95e21dc --- /dev/null +++ b/decidim-elections/lib/decidim/votings/test/factories.rb @@ -0,0 +1,267 @@ +# frozen_string_literal: true + +require "decidim/core/test/factories" +require "decidim/forms/test/factories" + +def format_birthdate(birthdate) + format("%04d%02d%02d", birthdate.year, birthdate.month, birthdate.day) +end + +def hash_for(*data) + Digest::SHA256.hexdigest(data.join(".")) +end + +FactoryBot.define do + sequence(:voting_slug) do |n| + "#{Decidim::Faker::Internet.slug(words: nil, glue: "-")}-#{n}" + end + + factory :voting, class: "Decidim::Votings::Voting" do + organization + slug { generate(:voting_slug) } + title { generate_localized_title } + description { Decidim::Faker::Localized.wrapped("

    ", "

    ") { generate_localized_title } } + published_at { Time.current } + start_time { 1.day.from_now } + end_time { 3.days.from_now } + decidim_scope_id { create(:scope, organization:).id } + banner_image { Decidim::Dev.test_file("city2.jpeg", "image/jpeg") } + introductory_image { Decidim::Dev.test_file("city.jpeg", "image/jpeg") } + voting_type { "hybrid" } + census_contact_information { nil } + show_check_census { true } + + trait :unpublished do + published_at { nil } + end + + trait :published do + published_at { Time.current } + end + + trait :upcoming do + start_time { 7.days.from_now } + end_time { 1.month.from_now + 7.days } + end + + trait :ongoing do + start_time { 7.days.ago } + end_time { 1.month.from_now - 7.days } + end + + trait :finished do + start_time { 1.month.ago - 7.days } + end_time { 7.days.ago } + end + + trait :promoted do + promoted { true } + end + + trait :online do + voting_type { "online" } + end + + trait :in_person do + voting_type { "in_person" } + end + + trait :hybrid do + voting_type { "hybrid" } + end + + trait :with_content_blocks do + transient { blocks_manifests { [:hero] } } + + after(:create) do |voting, evaluator| + evaluator.blocks_manifests.each do |manifest_name| + create( + :content_block, + organization: voting.organization, + scope_name: :voting_landing_page, + manifest_name:, + scoped_resource_id: voting.id + ) + end + end + end + end + + factory :voting_election, parent: :election do + transient do + voting { create(:voting) } + base_id { 20_000 } + end + + component { create(:elections_component, organization:, participatory_space: voting) } + end + + factory :polling_station, class: "Decidim::Votings::PollingStation" do + title { generate_localized_title } + location { Decidim::Faker::Localized.wrapped("

    ", "

    ") { generate_localized_title } } + location_hints { Decidim::Faker::Localized.wrapped("

    ", "

    ") { generate_localized_title } } + address { Faker::Lorem.sentence(word_count: 3) } + latitude { Faker::Address.latitude } + longitude { Faker::Address.longitude } + voting { create(:voting) } + end + + factory :polling_officer, class: "Decidim::Votings::PollingOfficer" do + user { create :user, organization: voting.organization } + voting { create :voting } + + trait :president do + presided_polling_station { create :polling_station, voting: } + end + end + + factory :monitoring_committee_member, class: "Decidim::Votings::MonitoringCommitteeMember" do + user + voting { create :voting, organization: user.organization } + end + + factory :dataset, class: "Decidim::Votings::Census::Dataset" do + voting { create(:voting) } + filename { "file.csv" } + status { "init_data" } + csv_row_raw_count { 1 } + csv_row_processed_count { 1 } + + trait :with_data do + after(:create) do |dataset| + create_list(:datum, 5, dataset:) + end + end + + trait :with_access_code_data do + after(:create) do |dataset| + create_list(:datum, 5, :with_access_code, dataset:) + end + end + + trait :data_created do + status { "data_created" } + end + + trait :codes_generated do + with_access_code_data + status { "codes_generated" } + end + + trait :frozen do + status { "freeze" } + end + end + + factory :datum, class: "Decidim::Votings::Census::Datum" do + dataset + + transient do + document_number { Faker::IDNumber.spanish_citizen_number } + document_type { %w(identification_number passport).sample } + birthdate { Faker::Date.birthday(min_age: 18, max_age: 65) } + end + + hashed_in_person_data { hash_for(document_number, document_type, format_birthdate(birthdate)) } + hashed_check_data { hash_for(document_number, document_type, format_birthdate(birthdate), postal_code) } + + full_name { Faker::Name.name } + full_address { Faker::Address.full_address } + postal_code { Faker::Address.postcode } + mobile_phone_number { Faker::PhoneNumber.cell_phone } + email { Faker::Internet.email } + + trait :with_access_code do + access_code { Faker::Alphanumeric.alphanumeric(number: 8) } + hashed_online_data { hash_for hashed_check_data, access_code } + end + end + + factory :ballot_style, class: "Decidim::Votings::BallotStyle" do + code { Faker::Lorem.word.upcase } + voting { create(:voting) } + + trait :with_questions do + transient do + election { create(:election, :complete, component: create(:elections_component, participatory_space: voting)) } + end + end + + trait :with_ballot_style_questions do + with_questions + + after(:create) do |ballot_style, evaluator| + evaluator.election.reload.questions.first(2).map { |question| create(:ballot_style_question, question:, ballot_style:) } + end + end + end + + factory :ballot_style_question, class: "Decidim::Votings::BallotStyleQuestion" do + question + ballot_style + end + + factory :in_person_vote, class: "Decidim::Votings::InPersonVote" do + transient do + voting { create(:voting) } + component { create(:elections_component, participatory_space: voting) } + end + + election { create(:election, component:) } + sequence(:voter_id) { |n| "voter_#{n}" } + status { "pending" } + message_id { "decidim-test-authority.2.vote.in_person+v.5826de088371d1b15b38f00c8203871caec07041ed0c8fb0c6fb875f0df763b6" } + polling_station { polling_officer.polling_station } + polling_officer { create(:polling_officer, :president, voting:) } + + trait :accepted do + status { "accepted" } + end + + trait :rejected do + status { "rejected" } + end + end + + factory :ps_closure, class: "Decidim::Votings::PollingStationClosure" do + transient do + number_of_votes { Faker::Number.number(digits: 2) } + end + + election { create(:voting_election, :complete) } + polling_station { polling_officer.polling_station } + polling_officer { create(:polling_officer, :president, voting: election.participatory_space) } + polling_officer_notes { Faker::Lorem.paragraph } + monitoring_committee_notes { nil } + signed_at { nil } + phase { :count } + validated_at { nil } + + trait :completed do + phase { :complete } + end + + trait :with_results do + phase { :signature } + + after :create do |closure, evaluator| + total_votes = evaluator.number_of_votes + create_list(:in_person_vote, evaluator.number_of_votes, :accepted, voting: closure.election.participatory_space, election: closure.election) + + closure.election.questions.each do |question| + max = total_votes + question.answers.each do |answer| + value = Faker::Number.between(from: 0, to: max) + closure.results << create(:election_result, closurable: closure, election: closure.election, question:, answer:, value:) + max -= value + end + value = Faker::Number.between(from: 0, to: max) + closure.results << create(:election_result, :null_ballots, election: closure.election, question:, value:) + max -= value + closure.results << create(:election_result, :blank_ballots, election: closure.election, question:, value: max) + end + closure.results << create(:election_result, :total_ballots, closurable: closure, election: closure.election, value: total_votes) + end + end + end +end diff --git a/decidim-elections/lib/decidim/votings/voting_serializer.rb b/decidim-elections/lib/decidim/votings/voting_serializer.rb new file mode 100644 index 00000000..09633953 --- /dev/null +++ b/decidim-elections/lib/decidim/votings/voting_serializer.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Decidim + module Votings + # This class serializes a Voting so it can be exported to CSV, JSON or other + # formats. + class VotingSerializer < Decidim::Exporters::Serializer + include Decidim::ApplicationHelper + include Decidim::ResourceHelper + include Decidim::TranslationsHelper + + # Public: Initializes the serializer with a voting. + def initialize(voting) + @voting = voting + end + + # Public: Exports a hash with the serialized data for this voting. + def serialize + { + participatory_space_id: voting.id, + url:, + title: voting.title, + description: voting.description, + start_time: voting.start_time, + end_time: voting.end_time, + voting_type: translated_voting_type, + scope: { + id: voting.scope.try(:id), + name: voting.scope.try(:name) + }, + banner_image_url: Decidim::Votings::VotingPresenter.new(voting).banner_image_url, + introductory_image_url: Decidim::Votings::VotingPresenter.new(voting).introductory_image_url + } + end + + private + + attr_reader :voting + alias resource voting + + def translated_voting_type + translation_hash = {} + voting.organization.available_locales.each do |locale| + translation_hash[locale] = I18n.t(voting.voting_type, scope: "decidim.votings.admin.votings.form.voting_type") + end + + translation_hash + end + + def url + Decidim::Votings::Engine.routes.url_helpers.voting_url(host: voting.organization.host, slug: voting.slug) + end + end + end +end diff --git a/decidim-elections/lib/tasks/decidim_elections.rake b/decidim-elections/lib/tasks/decidim_elections.rake new file mode 100644 index 00000000..a36d150f --- /dev/null +++ b/decidim-elections/lib/tasks/decidim_elections.rake @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +namespace :decidim_elections do + IDENTIFICATION_PRIVATE_KEY_SIZE = 4096 + + desc "Add a new client to the bulletin board" + task :generate_identification_keys do + identification_jwk_keypair = JWT::JWK.new(OpenSSL::PKey::RSA.new(IDENTIFICATION_PRIVATE_KEY_SIZE)) + + puts "\nPRIVATE KEY:" + puts "\n" + puts Decidim::BulletinBoard::JwkUtils.private_export(identification_jwk_keypair).to_json + puts "\n" + puts "PUBLIC KEY:" + puts "\n" + puts identification_jwk_keypair.export.map { |k, v| "#{k}=#{v}" }.join("&") + puts "\nAbove are the generated private and public keys.\n\nSee Decidim docs at docs/services/bulletin_board.md in order to set them up.\n\n" + end + + desc "Scheduled tasks" + task :scheduled_tasks, [] => :environment do + Decidim::Elections::ElectionsReadyToStart.for.each do |election| + puts "\nStarting vote period for election ##{election.id}:" + form = Decidim::Elections::Admin::VotePeriodForm.new.with_context(election:, current_user: nil) + Decidim::Elections::Admin::StartVote.call(form) do + on(:ok) do + puts "\n✓ Voting period start requested.\n" + end + + on(:invalid) do |message| + puts "\n✗ Voting period not started. Message: #{message}\n" + end + end + puts "\n" + end + + Decidim::Elections::ElectionsFinishedToEnd.for.each do |election| + puts "\nEnding vote period for election ##{election.id}:" + form = Decidim::Elections::Admin::VotePeriodForm.new.with_context(election:, current_user: nil) + Decidim::Elections::Admin::EndVote.call(form) do + on(:ok) do + puts "\n✓ Voting period end requested.\n" + end + + on(:invalid) do |message| + puts "\n✗ Voting period not ended. Message: #{message}\n" + end + end + puts "\n" + end + + Decidim::Elections::Votes::PendingVotes.for.each do |vote| + puts "\nChecking status for Vote ##{vote.id}:" + Decidim::Elections::Voter::UpdateVoteStatus.call(vote) do + on(:ok) do + puts "\n✓ Vote status updated\n" + end + + on(:invalid) do |message| + puts "\n✗ Vote status failed. Message: #{message}\n" + end + end + puts "\n" + end + + Decidim::Elections::Admin::PendingActions.for.each do |action| + puts "\nChecking status for action '#{action.action}' for election ##{action.election.id}:" + Decidim::Elections::Admin::UpdateActionStatus.call(action) do + on(:ok) do + puts "\n✓ Action status updated\n" + end + on(:invalid) do |message| + puts "\n✗ Update status failed. Message: #{message}\n" + end + end + puts "\n" + end + end +end diff --git a/decidim-elections/lib/tasks/decidim_voting_census.rake b/decidim-elections/lib/tasks/decidim_voting_census.rake new file mode 100644 index 00000000..3cdd287c --- /dev/null +++ b/decidim-elections/lib/tasks/decidim_voting_census.rake @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +namespace :decidim_votings_census do + # Removes the census access code export files older than the configured expiry time. + desc "Deletes the census access codes export file `Decidim::Votings::Census.census_access_codes_export_expiry_time` from now." + task delete_census_access_codes_export: :environment do + puts "DELETE CENSUS ACCESS CODES EXPORT: -------------- START" + attachments = ActiveStorage::Attachment.joins(:blob).where( + name: "access_codes_file", + record_type: "Decidim::Votings::Census::Dataset" + ).where( + "active_storage_blobs.created_at < ?", Decidim::Votings::Census.census_access_codes_export_expiry_time.ago + ) + attachments.each do |attachment| + delete_census_access_codes_export_file attachment + end + puts "DELETE CENSUS ACCESS CODES EXPORT: --------------- END" + end + + def delete_census_access_codes_export_file(attachment) + puts "------" + puts "!! deleting: #{attachment.filename}" + attachment.purge + puts "ok----" + end +end diff --git a/decidim-elections/spec/cells/decidim/elections/election_cell_spec.rb b/decidim-elections/spec/cells/decidim/elections/election_cell_spec.rb new file mode 100644 index 00000000..b686fab4 --- /dev/null +++ b/decidim-elections/spec/cells/decidim/elections/election_cell_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::ElectionCell, type: :cell do + controller Decidim::Elections::ElectionsController + + subject { my_cell.call } + + let(:my_cell) { cell("decidim/elections/election", model) } + let!(:election) { create(:election, :complete, :published, :ongoing) } + let!(:current_user) { create(:user, :confirmed, organization: model.participatory_space.organization) } + + before do + allow(controller).to receive(:current_user).and_return(current_user) + end + + context "when rendering an election" do + let(:model) { election } + + it "renders the card" do + expect(subject).to have_css("[id^=election]") + end + + it "renders the title" do + expect(subject).to have_content(translated(election.title)) + end + end +end diff --git a/decidim-elections/spec/cells/decidim/elections/election_g_cell_spec.rb b/decidim-elections/spec/cells/decidim/elections/election_g_cell_spec.rb new file mode 100644 index 00000000..3ac3a0ff --- /dev/null +++ b/decidim-elections/spec/cells/decidim/elections/election_g_cell_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections + describe ElectionGCell, type: :cell do + controller Decidim::Elections::ElectionsController + + subject { cell_html } + + let(:my_cell) { cell("decidim/elections/election_g", election, context: { show_space: }) } + let(:cell_html) { my_cell.call } + let(:start_time) { 2.days.ago } + let(:end_time) { 1.day.from_now } + let!(:election) { create(:election, :complete, :published, start_time:, end_time:) } + let(:model) { election } + let(:user) { create(:user, organization: election.participatory_space.organization) } + + before do + allow(controller).to receive(:current_user).and_return(user) + end + + it_behaves_like "has space in m-cell" + + context "when rendering" do + let(:show_space) { false } + + it "renders the card" do + expect(subject).to have_css(".card__grid") + expect(subject).to have_css("#elections__election_#{election.id}") + end + + it "renders the start and end time" do + election_start = I18n.l(start_time.to_date, format: :decidim_short_with_month_name_short) + election_end = I18n.l(end_time.to_date, format: :decidim_short_with_month_name_short) + + expect(subject).to have_css(".card__grid-metadata span", text: election_start) + expect(subject).to have_css(".card__grid-metadata span", text: election_end) + end + + it "renders the title" do + expect(subject).to have_css(".card__grid-text", text: translated(election.title)) + end + + it "renders the badge name" do + expect(subject).to have_css("span.label.success", text: "Active") + end + + context "with attached image" do + let(:image) { create(:attachment, :with_image, attached_to: election) } + + before do + election.update!(attachments: [image]) + end + + it "shows the attached image" do + expect(subject).to have_css(".card__grid-img img") + end + end + end + end +end diff --git a/decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_cell_spec.rb b/decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_cell_spec.rb new file mode 100644 index 00000000..5335944a --- /dev/null +++ b/decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_cell_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::ContentBlocks::HighlightedVotingsCell, type: :cell do + subject { cell(content_block.cell, content_block).call } + + let(:organization) { create(:organization) } + let(:content_block) { create(:content_block, organization:, manifest_name: :highlighted_votings, scope_name: :homepage, settings:) } + let!(:votings) { create_list(:voting, 5, :ongoing, organization:) } + let(:settings) { {} } + + controller Decidim::PagesController + + before do + allow(controller).to receive(:current_organization).and_return(organization) + end + + context "when the content block has no settings" do + it "shows 4 processes" do + expect(subject).to have_selector("[id^='votings__voting']", count: 4) + end + end + + context "when the content block has customized the max results setting value" do + let(:settings) do + { + "max_results" => "8" + } + end + + it "shows up to 8 votings" do + expect(subject).to have_selector("[id^='votings__voting']", count: 5) + end + end +end diff --git a/decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell_spec.rb b/decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell_spec.rb new file mode 100644 index 00000000..1062816c --- /dev/null +++ b/decidim-elections/spec/cells/decidim/votings/content_blocks/highlighted_votings_settings_form_cell_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::ContentBlocks::HighlightedVotingsSettingsFormCell, type: :cell do + let(:cell) { described_class.new } + + describe "#content_block" do + subject { cell.content_block } + + it { is_expected.to be_nil } + end + + describe "#label" do + subject { cell.label } + + it { is_expected.to eq("Maximum amount of elements to show") } + end +end diff --git a/decidim-elections/spec/cells/decidim/votings/voting_cell_spec.rb b/decidim-elections/spec/cells/decidim/votings/voting_cell_spec.rb new file mode 100644 index 00000000..ec436347 --- /dev/null +++ b/decidim-elections/spec/cells/decidim/votings/voting_cell_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::VotingCell, type: :cell do + controller Decidim::Votings::VotingsController + + subject { my_cell.call } + + let(:my_cell) { cell("decidim/votings/voting", model) } + let!(:voting) { create(:voting, :published, :ongoing) } + let!(:current_user) { create(:user, :confirmed, organization: model.organization) } + + before do + allow(controller).to receive(:current_user).and_return(current_user) + end + + context "when rendering a voting" do + let(:model) { voting } + + it "renders the card" do + expect(subject).to have_css("[id^=votings__voting]") + end + + it "renders the title and text" do + expect(subject).to have_content(translated(model.title)) + end + end +end diff --git a/decidim-elections/spec/cells/decidim/votings/voting_dropdown_metadata_cell_spec.rb b/decidim-elections/spec/cells/decidim/votings/voting_dropdown_metadata_cell_spec.rb new file mode 100644 index 00000000..4d50b2c3 --- /dev/null +++ b/decidim-elections/spec/cells/decidim/votings/voting_dropdown_metadata_cell_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require "spec_helper" + +require "decidim/core/test/shared_examples/participatory_space_dropdown_metadata_cell_examples" + +module Decidim::Votings + describe VotingDropdownMetadataCell, type: :cell do + controller Decidim::ApplicationController + + subject { cell("decidim/votings/voting_dropdown_metadata", model).call } + + let(:model) { create(:voting) } + + include_examples "participatory space dropdown metadata cell" + end +end diff --git a/decidim-elections/spec/cells/decidim/votings/voting_g_cell_spec.rb b/decidim-elections/spec/cells/decidim/votings/voting_g_cell_spec.rb new file mode 100644 index 00000000..3282657c --- /dev/null +++ b/decidim-elections/spec/cells/decidim/votings/voting_g_cell_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/core/test/shared_examples/space_cell_changes_button_text_cta" + +module Decidim::Votings + describe VotingGCell, type: :cell do + controller Decidim::Votings::VotingsController + + subject { cell_html } + + let(:my_cell) { cell("decidim/votings/voting_g", voting, context: { show_space: }) } + let(:cell_html) { my_cell.call } + let(:start_time) { 2.days.ago } + let(:end_time) { 1.day.from_now } + let(:voting) { create(:voting, :published, start_time:, end_time:) } + let(:model) { voting } + let(:user) { create(:user, organization: voting.organization) } + + before do + allow(controller).to receive(:current_user).and_return(user) + end + + context "when rendering" do + let(:show_space) { false } + + it "renders the card" do + expect(subject).to have_css(".card__grid") + expect(subject).to have_css("#votings__voting_#{voting.id}") + end + + it "renders the start and end time" do + voting_start = I18n.l(start_time.to_date, format: :decidim_short_with_month_name_short) + voting_end = I18n.l(end_time.to_date, format: :decidim_short_with_month_name_short) + + expect(subject).to have_css(".card__grid-metadata span", text: voting_start) + expect(subject).to have_css(".card__grid-metadata span", text: voting_end) + end + + it "renders the title" do + expect(subject).to have_css(".card__grid-text", text: translated(voting.title)) + end + + it "renders the banner image" do + expect(subject).to have_css(".card__grid-img img") + end + + describe "render different states" do + context "when the voting is ongoing" do + let!(:voting) { create(:voting, :ongoing) } + + it "renders the ongoing state" do + expect(subject).to have_css("span.label.success", text: "Ongoing") + end + end + + context "when the voting is upcoming" do + let!(:voting) { create(:voting, :upcoming) } + + it "renders the upcoming state" do + expect(subject).to have_css("span.label.warning", text: "Upcoming") + end + end + + context "when the voting is finished" do + let!(:voting) { create(:voting, :finished) } + + it "renders the finished state" do + expect(subject).to have_css("span.label", text: "Finished") + end + end + end + + describe "renders the different voting types" do + context "when the voting is online" do + let(:voting) { create(:voting, :online) } + + it "renders the online type" do + within ".card-data__item" do + expect(page).to have_content("Online") + end + end + end + + context "when the voting is in person" do + let(:voting) { create(:voting, :in_person) } + + it "renders the in person type" do + within ".card-data__item" do + expect(page).to have_content("In person") + end + end + end + + context "when the voting is hybrid" do + let(:voting) { create(:voting, :hybrid) } + + it "renders the hybrid type" do + within ".card-data__item" do + expect(page).to have_content("Hybrid") + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/add_user_as_trustee_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/add_user_as_trustee_spec.rb new file mode 100644 index 00000000..94ace9fc --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/add_user_as_trustee_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::AddUserAsTrustee do + subject { described_class.new(form, current_user) } + + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:current_user) { create(:user, :admin, :confirmed, organization:) } + let(:user) { create(:user, :confirmed) } + let(:organization) { user.organization } + let(:form) do + double( + invalid?: invalid, + user:, + current_user:, + current_participatory_space: current_component.participatory_space + ) + end + let(:invalid) { false } + + let(:trustee) { Decidim::Elections::Trustee.last } + + context "when new trustee" do + let(:trustee) { nil } + + it "adds the user to trustees" do + expect { subject.call }.to change(Decidim::Elections::Trustee, :count).by(1) + end + + it "adds the user organization to trustee" do + subject.call + expect(Decidim::Elections::Trustee.last.organization).to eql(user.organization) + end + + it "sends a notification to a new trustee" do + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.elections.trustees.new_trustee", + event_class: Decidim::Elections::Trustees::NotifyNewTrusteeEvent, + resource: form.current_participatory_space, + affected_users: [form.user] + ) + subject.call + end + end + + it "adds a participatory space to trustee" do + subject.call + expect(trustee.trustees_participatory_spaces.count).to eq 1 + end + + it "sends an email" do + expect { subject.call }.to have_enqueued_job(ActionMailer::MailDeliveryJob) + end + + context "when user and participatory space exist" do + let!(:trustee) do + trustee = create(:trustee, + decidim_user_id: user.id) + trustee.trustees_participatory_spaces.create( + participatory_space: form.current_participatory_space + ) + end + + it "broadcasts exists" do + expect { subject.call }.to broadcast(:exists) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/create_answer_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/create_answer_spec.rb new file mode 100644 index 00000000..9086795b --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/create_answer_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::CreateAnswer do + subject(:command) { described_class.new(form) } + + let(:organization) { component.organization } + let(:participatory_process) { component.participatory_space } + let(:component) { election.component } + let(:question) { create(:question, election:) } + let(:election) { create(:election) } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:form) do + double( + invalid?: invalid, + title: { en: "title" }, + description: { en: "description" }, + weight: 10, + proposals:, + proposal_ids: proposals.map(&:id), + attachment: attachment_params, + photos:, + add_photos: uploaded_photos, + current_user: user, + current_component: component, + current_organization: organization, + election:, + question: + ) + end + let(:proposals) { [] } + let(:photos) { [] } + let(:uploaded_photos) { [] } + let(:attachment_params) { nil } + let(:invalid) { false } + + let(:answer) { Decidim::Elections::Answer.last } + + it "creates the answer" do + expect { subject.call }.to change(Decidim::Elections::Answer, :count).by(1) + end + + it "stores the given data" do + subject.call + expect(translated(answer.title)).to eq "title" + expect(translated(answer.description)).to eq "description" + expect(answer.weight).to eq(10) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create!) + .with( + Decidim::Elections::Answer, + user, + hash_including(:title, :description, :weight), + visibility: "all" + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the election has been created in the bulletin board" do + let(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "with proposals" do + let(:proposals_component) { create(:component, manifest_name: :proposals, participatory_space: component.participatory_space) } + let(:proposals) { create_list(:proposal, 2, component: proposals_component) } + + it "creates the answer" do + expect { subject.call }.to change(Decidim::Elections::Answer, :count).by(1) + end + + it "stores the relations with proposals" do + subject.call + expect(answer.proposals).to match_array(proposals) + end + end + + context "with attachment" do + it_behaves_like "admin creates resource gallery" do + let(:resource_class) { Decidim::Elections::Answer } + let(:attachment_params) do + { + title: "My attachment", + file: Decidim::Dev.test_file("city.jpeg", "image/jpeg") + } + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/create_election_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/create_election_spec.rb new file mode 100644 index 00000000..c30177b9 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/create_election_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::CreateElection do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:form) do + double( + invalid?: invalid, + title: { en: "title" }, + description: { en: "description" }, + start_time:, + end_time:, + attachment: attachment_params, + photos:, + add_photos: uploaded_photos, + current_user: user, + current_component:, + current_organization: organization + ) + end + let(:start_time) { 1.day.from_now } + let(:end_time) { 2.days.from_now } + let(:invalid) { false } + let(:attachment_params) { nil } + let(:photos) { [] } + let(:uploaded_photos) { [] } + + let(:election) { Decidim::Elections::Election.last } + + it "creates the election" do + expect { subject.call }.to change(Decidim::Elections::Election, :count).by(1) + end + + it "stores the given data" do + subject.call + expect(translated(election.title)).to eq "title" + expect(translated(election.description)).to eq "description" + expect(election.start_time).to be_within(1.second).of start_time + expect(election.end_time).to be_within(1.second).of end_time + end + + it "sets the component" do + subject.call + expect(election.component).to eq current_component + end + + it "sets the questionnaire for election feedback" do + subject.call + expect(election.questionnaire).to be_a(Decidim::Forms::Questionnaire) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create!) + .with( + Decidim::Elections::Election, + user, + hash_including(:title, :description, :end_time, :start_time, :component), + visibility: "all" + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "with attachment" do + it_behaves_like "admin creates resource gallery" do + let(:command) { described_class.new(form) } + let(:resource_class) { Decidim::Elections::Election } + let(:attachment_params) do + { + title: "My attachment", + file: Decidim::Dev.test_file("city.jpeg", "image/jpeg") + } + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/create_question_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/create_question_spec.rb new file mode 100644 index 00000000..4e28ecda --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/create_question_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::CreateQuestion do + subject { described_class.new(form) } + + let(:organization) { current_component.organization } + let(:participatory_process) { current_component.participatory_space } + let(:current_component) { election.component } + let(:election) { create(:election) } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:form) do + double( + invalid?: invalid, + title: { en: "title" }, + max_selections: 3, + weight: 10, + random_answers_order: true, + min_selections: 1, + current_user: user, + current_component:, + current_organization: organization, + election: + ) + end + let(:invalid) { false } + + let(:question) { Decidim::Elections::Question.last } + + it "creates the question" do + expect { subject.call }.to change(Decidim::Elections::Question, :count).by(1) + end + + it "stores the given data" do + subject.call + expect(translated(question.title)).to eq "title" + expect(question.max_selections).to eq(3) + expect(question.weight).to eq(10) + expect(question.random_answers_order).to be_truthy + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create!) + .with( + Decidim::Elections::Question, + user, + hash_including(:title, :max_selections, :weight, :random_answers_order), + visibility: "all" + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the election has been created in the bulletin board" do + let(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:election_started) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/destroy_answer_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/destroy_answer_spec.rb new file mode 100644 index 00000000..bd01c34e --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/destroy_answer_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::DestroyAnswer do + subject(:command) { described_class.new(answer, user) } + + let(:election) { create(:election) } + let(:question) { create(:question, election:) } + let!(:answer) { create(:election_answer, question:) } + let(:component) { election.component } + let(:organization) { component.organization } + let(:user) { create(:user, :admin, :confirmed, organization:) } + + it "destroys the answer" do + expect { subject.call }.to change(Decidim::Elections::Answer, :count).by(-1) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:delete, answer, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "destroy" + end + + context "when the election has been created in the bulletin board" do + let!(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "with attachments" do + let!(:image) { create(:attachment, :with_image, attached_to: answer) } + + it_behaves_like "admin destroys resource gallery" do + let(:resource) { answer } + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/destroy_election_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/destroy_election_spec.rb new file mode 100644 index 00000000..75ccc120 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/destroy_election_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::DestroyElection do + subject { described_class.new(election, user) } + + let!(:election) { create(:election) } + let(:organization) { election.component.organization } + let(:user) { create(:user, :admin, :confirmed, organization:) } + + it "destroys the election" do + expect { subject.call }.to change(Decidim::Elections::Election, :count).by(-1) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:delete, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "destroy" + end + + context "when the election has been created in the bulletin board" do + let(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "with attachments" do + let(:command) { described_class.new(election, user) } + let!(:image) { create(:attachment, :with_image, attached_to: election) } + + it_behaves_like "admin destroys resource gallery" do + let(:resource) { election } + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/destroy_question_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/destroy_question_spec.rb new file mode 100644 index 00000000..d9a612fb --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/destroy_question_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::DestroyQuestion do + subject { described_class.new(question, user) } + + let(:election) { create(:election) } + let!(:question) { create(:question, election:) } + let(:organization) { election.component.organization } + let(:user) { create(:user, :admin, :confirmed, organization:) } + + it "destroys the question" do + expect { subject.call }.to change(Decidim::Elections::Question, :count).by(-1) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:delete, question, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "destroy" + end + + context "when the election has been created in the bulletin board" do + let(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/end_vote_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/end_vote_spec.rb new file mode 100644 index 00000000..ed793e8f --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/end_vote_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::EndVote do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:election) { create(:election, :vote) } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + bulletin_board: + ) + end + + let(:method_name) { :end_vote } + let(:response) { OpenStruct.new(status: "enqueued") } + let(:action) { Decidim::Elections::Action.last } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(method_name).and_yield("a.message+id").and_return(response) + end + + context "when valid form" do + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:end_vote, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "creates an action" do + expect { subject.call }.to change(Decidim::Elections::Action, :count).by(1) + + expect(action.election).to eq(election) + expect(action.message_id).to eq "a.message+id" + expect(action).to be_pending + expect(action).to be_end_vote + end + + it "calls the bulletin board method with the correct params" do + subject.call + expect(bulletin_board).to have_received(method_name).with(election.id) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/import_proposals_to_elections_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/import_proposals_to_elections_spec.rb new file mode 100644 index 00000000..7e5ccd94 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/import_proposals_to_elections_spec.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + module Admin + describe ImportProposalsToElections do + describe "call" do + let!(:proposals) { create_list(:proposal, 3, :accepted) } + let!(:proposal) { proposals.first } + let!(:organization) { component.participatory_space.organization } + let!(:user) { create(:user, :admin, :confirmed, organization:) } + let(:component) do + create( + :component, manifest_name: "elections", + participatory_space: proposal.component.participatory_space + ) + end + let(:question) { create(:question, election:) } + let(:election) { create(:election, component:) } + let!(:form) do + instance_double( + AnswerImportProposalsForm, + origin_component: proposal.component, + current_component: component, + current_user: user, + import_all_accepted_proposals?: import_all_accepted_proposals, + invalid?: invalid, + election:, + question:, + weight: 10 + ) + end + + let(:import_all_accepted_proposals) { true } + let(:invalid) { false } + let(:command) { described_class.new(form) } + + describe "when the form is not valid" do + let(:invalid) { true } + + it "broadcasts invalid" do + expect { command.call }.to broadcast(:invalid) + end + + it "does not create the answer" do + expect do + command.call + end.not_to change(Decidim::Elections::Answer, :count) + end + end + + describe "when the form is valid" do + it "broadcasts ok" do + expect { command.call }.to broadcast(:ok) + end + + it "creates the answer" do + expect do + command.call + end.to change { Decidim::Elections::Answer.where(question:).count }.by(1) + end + + context "when a proposal was already imported" do + let(:second_proposal) { create(:proposal, :accepted, component: proposal.component) } + + before do + command.call + second_proposal + end + + it "does not import it again" do + expect do + command.call + end.not_to(change { Decidim::Elections::Answer.where(question:).count }) + + answers = Decidim::Elections::Answer.where(question:) + first_answer = answers.first + last_answer = answers.last + expect(first_answer.title).to eq(proposal.title) + expect(last_answer.title).to eq(proposal.title) + end + + context "and the current component was not published" do + before { component.unpublish! } + + it "does not import it again" do + expect do + command.call + end.not_to(change { Decidim::Elections::Answer.where(question:).count }) + + answers = Decidim::Elections::Answer.where(question:) + first_answer = answers.first + last_answer = answers.last + expect(first_answer.title).to eq(proposal.title) + expect(last_answer.title).to eq(proposal.title) + end + end + end + + it "links the proposals" do + command.call + last_answer = Decidim::Elections::Answer.where(question:).last + + linked = last_answer.linked_resources(:proposals, "related_proposals") + + expect(linked).to include(proposal) + end + + it "imports wanted attributes" do + command.call + + new_answer = Decidim::Elections::Answer.where(question:).last + expect(new_answer.title).to eq(proposal.title) + expect(new_answer.description).to eq(proposal.body) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create!) + .with( + Decidim::Elections::Answer, + user, + hash_including(:title, :description, :weight), + visibility: "all" + ) + .and_call_original + + expect { command.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/publish_election_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/publish_election_spec.rb new file mode 100644 index 00000000..4a94f333 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/publish_election_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections::Admin + describe PublishElection do + subject { described_class.new(election, user) } + + let!(:user) { create(:user, :admin, :confirmed, organization: participatory_process.organization) } + let!(:participatory_process) { election.component.participatory_space } + let(:step) { participatory_process.steps.first } + let!(:election) { create(:election) } + + it "publishes the election" do + expect { subject.call }.to change(election, :published?).from(false).to(true) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:publish, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "fires an event" do + create(:follow, followable: participatory_process, user:) + + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.elections.election_published", + event_class: Decidim::Elections::ElectionPublishedEvent, + resource: election, + followers: [user] + ) + + subject.call + end + + context "when the meeting is being republished" do + let!(:follower) { create(:follow, followable: participatory_process, user:) } + + it "does not fire an event", versioning: true do + subject.call + election.unpublish! + election.reload + + expect(election.previously_published?).to be true + expect(Decidim::EventsManager).not_to receive(:publish) + + subject.call + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/publish_results_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/publish_results_spec.rb new file mode 100644 index 00000000..404ed666 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/publish_results_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::PublishResults do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let!(:election) { create(:election, :complete) } + let(:trustees) { create_list(:trustee, 5, :with_public_key) } + let(:trustee_ids) { trustees.pluck(:id) } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + trustee_ids:, + bulletin_board: + ) + end + let(:scheme) do + { + name: "dummy", + parameters: { + quorum: 2 + } + } + end + let(:method_name) { :publish_results } + let(:response) { OpenStruct.new(status: "enqueued") } + let(:action) { Decidim::Elections::Action.last } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(method_name).and_yield("a.message+id").and_return(response) + end + + context "when valid form" do + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:publish_results, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "creates an action" do + expect { subject.call }.to change(Decidim::Elections::Action, :count).by(1) + + expect(action.election).to eq(election) + expect(action.message_id).to eq "a.message+id" + expect(action).to be_pending + expect(action).to be_publish_results + end + + it "calls the bulletin board method with the correct params" do + subject.call + expect(bulletin_board).to have_received(method_name).with(election.id) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/remove_trustee_from_participatory_space_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/remove_trustee_from_participatory_space_spec.rb new file mode 100644 index 00000000..82c85fed --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/remove_trustee_from_participatory_space_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::RemoveTrusteeFromParticipatorySpace do + subject { described_class.new(trustee_participatory_space) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:trustee_participatory_space) { create(:trustees_participatory_space) } + let(:current_participatory_space) { trustee_participatory_space.participatory_space } + + it "removes participatory space from trustee" do + subject.call + expect(trustee_participatory_space.trustee.trustees_participatory_spaces).not_to include(current_participatory_space) + end + + context "when trustee has elections" do + let(:trustee) { create(:trustee, :with_elections) } + let(:trustee_participatory_space) { create(:trustees_participatory_space, trustee:) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/report_missing_trustee_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/report_missing_trustee_spec.rb new file mode 100644 index 00000000..4afb8f43 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/report_missing_trustee_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::ReportMissingTrustee do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:election) { create(:election, :tally_started) } + let(:trustee) { election.trustees.first } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + bulletin_board:, + trustee:, + trustee_id: trustee.id + ) + end + + let(:method_name) { :report_missing_trustee } + let(:response) { OpenStruct.new(status: "enqueued") } + let(:action) { Decidim::Elections::Action.last } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(method_name).and_yield("a.message+id").and_return(response) + end + + context "when valid form" do + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:report_missing_trustee, election, user, extra: { + trustee_id: trustee.id, + name: trustee.name, + nickname: trustee.user.nickname + }, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "creates an action" do + expect { subject.call }.to change(Decidim::Elections::Action, :count).by(1) + + expect(action.election).to eq(election) + expect(action.message_id).to eq "a.message+id" + expect(action).to be_pending + expect(action).to be_report_missing_trustee + end + + it "calls the bulletin board method with the correct params" do + subject.call + expect(bulletin_board).to have_received(method_name).with(election.id, trustee.slug) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/setup_election_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/setup_election_spec.rb new file mode 100644 index 00000000..e0a04045 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/setup_election_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::SetupElection do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let!(:election) { create(:election, :complete) } + let!(:ballot_style) { create(:ballot_style, :with_ballot_style_questions, election:) } + let(:trustees) { create_list(:trustee, 5, :with_public_key) } + let(:trustee_ids) { trustees.pluck(:id) } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + trustee_ids:, + bulletin_board: + ) + end + let(:scheme) do + { + name: "dummy", + parameters: { + quorum: 2 + } + } + end + let(:method_name) { :create_election } + let(:response) { OpenStruct.new(status: "created") } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(:scheme).and_return(scheme) + allow(bulletin_board).to receive(method_name).and_return(response) + end + + context "when valid form" do + it "updates the election status" do + expect { subject.call }.to change { Decidim::Elections::Election.last.bb_status }.from(nil).to("created") + end + + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:setup, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "adds the trustees to the election" do + expect { subject.call }.to change { election.trustees.count }.by(5) + end + + it "notifies the trustees" do + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.elections.trustees.new_election", + event_class: Decidim::Elections::Trustees::NotifyTrusteeNewElectionEvent, + resource: election, + affected_users: trustees.collect(&:user).sort_by(&:id) + ) + subject.call + end + + it "blocks the election for modifications" do + expect { subject.call }.to change(election, :blocked?).from(false).to(true) + expect(election.blocked_at).to be_within(1.second).of election.updated_at + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/start_key_ceremony_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/start_key_ceremony_spec.rb new file mode 100644 index 00000000..56ef2195 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/start_key_ceremony_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::StartKeyCeremony do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:election) { create(:election, :key_ceremony_ended) } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + bulletin_board: + ) + end + + let(:method_name) { :start_key_ceremony } + let(:response) { OpenStruct.new(status: "enqueued") } + let(:action) { Decidim::Elections::Action.last } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(method_name).and_yield("a.message+id").and_return(response) + end + + context "when valid form" do + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:start_key_ceremony, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "creates an action" do + expect { subject.call }.to change(Decidim::Elections::Action, :count).by(1) + + expect(action.election).to eq(election) + expect(action.message_id).to eq "a.message+id" + expect(action).to be_pending + expect(action).to be_start_key_ceremony + end + + it "calls the bulletin board method with the correct params" do + subject.call + expect(bulletin_board).to have_received(method_name).with(election.id) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/start_tally_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/start_tally_spec.rb new file mode 100644 index 00000000..8709d686 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/start_tally_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::StartTally do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:election) { create(:election, :vote_ended) } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + bulletin_board: + ) + end + + let(:method_name) { :start_tally } + let(:response) { OpenStruct.new(status: "enqueued") } + let(:action) { Decidim::Elections::Action.last } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(method_name).and_yield("a.message+id").and_return(response) + end + + context "when valid form" do + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:start_tally, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "creates an action" do + expect { subject.call }.to change(Decidim::Elections::Action, :count).by(1) + + expect(action.election).to eq(election) + expect(action.message_id).to eq "a.message+id" + expect(action).to be_pending + expect(action).to be_start_tally + end + + it "calls the bulletin board method with the correct params" do + subject.call + expect(bulletin_board).to have_received(method_name).with(election.id) + end + + it "notifies the trustee about the tally" do + allow(Decidim::EventsManager).to receive(:publish) + + subject.call + expect(Decidim::EventsManager) + .to have_received(:publish) + .with( + event: "decidim.events.elections.trustees.start_tally", + event_class: Decidim::Elections::Trustees::NotifyTrusteeTallyProcessEvent, + resource: election, + affected_users: election.trustees.collect(&:user) + ) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/start_vote_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/start_vote_spec.rb new file mode 100644 index 00000000..37df6601 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/start_vote_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::StartVote do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:invalid) { false } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "elections") } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:election) { create(:election, :key_ceremony_ended) } + let(:form) do + double( + invalid?: invalid, + election:, + current_user: user, + current_component:, + current_organization: organization, + bulletin_board: + ) + end + + let(:method_name) { :start_vote } + let(:response) { OpenStruct.new(status: "enqueued") } + let(:action) { Decidim::Elections::Action.last } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(:public_key).and_return({ + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }) + allow(bulletin_board).to receive(:authority_name).and_return("Decidim Test Authority") + allow(bulletin_board).to receive(:authority_slug).and_return("decidim-test-authority") + allow(bulletin_board).to receive(method_name).and_yield("a.message+id").and_return(response) + end + + context "when valid form" do + it "logs the performed action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:start_vote, election, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + + it "creates an action" do + expect { subject.call }.to change(Decidim::Elections::Action, :count).by(1) + + expect(action.election).to eq(election) + expect(action.message_id).to eq "a.message+id" + expect(action).to be_pending + expect(action).to be_start_vote + end + + it "calls the bulletin board method with the correct params" do + subject.call + expect(bulletin_board).to have_received(method_name).with(election.id) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(method_name).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/unpublish_election_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/unpublish_election_spec.rb new file mode 100644 index 00000000..e5df0b36 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/unpublish_election_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections::Admin + describe UnpublishElection do + subject { described_class.new(election, user) } + + let!(:user) { create(:user, :admin, :confirmed, organization: participatory_process.organization) } + let!(:participatory_process) { create(:participatory_process, :with_steps) } + let(:step) { participatory_process.steps.first } + let!(:election) { create(:election, :published) } + + it "unpublishes the election" do + expect { subject.call }.to change(election, :published?).from(true).to(false) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:unpublish, election, user) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/update_answer_selection_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/update_answer_selection_spec.rb new file mode 100644 index 00000000..00617134 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/update_answer_selection_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::UpdateAnswerSelection do + subject(:command) { described_class.new(answer, selected) } + + let(:election) { create(:election, :published, :results_published) } + let(:question) { election.questions.first } + let(:answer) { question.answers.first } + let(:selected) { false } + + it "updates the selected answer attribute" do + subject.call + expect(answer.selected).to be(false) + end + + context "when answer has no votes" do + let(:answer) { create(:election_answer) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/update_answer_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/update_answer_spec.rb new file mode 100644 index 00000000..bf719111 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/update_answer_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::UpdateAnswer do + subject(:command) { described_class.new(form, answer) } + + let(:election) { create(:election) } + let(:question) { create(:question, election:) } + let(:answer) { create(:election_answer, question:) } + let(:component) { election.component } + let(:organization) { component.organization } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:form) do + double( + invalid?: invalid, + current_user: user, + title: { en: "title" }, + description: { en: "description" }, + weight: 10, + proposals:, + proposal_ids: proposals.map(&:id), + attachment: attachment_params, + photos: current_photos, + add_photos: uploaded_photos, + election:, + question: + ) + end + let(:proposals) { [] } + let(:current_photos) { [] } + let(:uploaded_photos) { [] } + let(:attachment_params) { nil } + let(:invalid) { false } + + it "updates the answer" do + subject.call + expect(translated(answer.title)).to eq "title" + expect(translated(answer.description)).to eq "description" + expect(answer.weight).to eq(10) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:update!) + .with(answer, user, hash_including(:title, :description, :weight), visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "update" + end + + context "with proposals" do + let(:proposals_component) { create(:component, manifest_name: :proposals, participatory_space: component.participatory_space) } + let(:proposals) { create_list(:proposal, 2, component: proposals_component) } + + it "creates the answer" do + expect { subject.call }.to change(Decidim::Elections::Answer, :count).by(1) + end + + it "stores the relations with proposals" do + subject.call + expect(answer.proposals).to match_array(proposals) + end + end + + context "with attachment" do + it_behaves_like "admin manages resource gallery" do + let(:resource_class) { Decidim::Elections::Answer } + let(:resource) { answer } + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the election has been created in the bulletin board" do + let(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/update_election_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/update_election_spec.rb new file mode 100644 index 00000000..8eb484c0 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/update_election_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::UpdateElection do + subject { described_class.new(form, election) } + + let(:election) { create(:election) } + let(:organization) { election.component.organization } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:form) do + double( + invalid?: invalid, + current_user: user, + title: { en: "title" }, + description: { en: "description" }, + start_time:, + end_time:, + attachment: attachment_params, + photos: current_photos, + add_photos: uploaded_photos + ) + end + let(:start_time) { 1.day.from_now } + let(:end_time) { 2.days.from_now } + let(:current_photos) { [] } + let(:uploaded_photos) { [] } + let(:attachment_params) { nil } + let(:invalid) { false } + + it "updates the election" do + subject.call + expect(translated(election.title)).to eq "title" + expect(translated(election.description)).to eq "description" + expect(election.start_time).to be_within(1.second).of start_time + expect(election.end_time).to be_within(1.second).of end_time + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:update!) + .with(election, user, hash_including(:title, :description, :start_time, :end_time), visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "update" + end + + context "with attachment" do + it_behaves_like "admin manages resource gallery" do + let(:command) { described_class.new(form, election) } + let(:resource_class) { Decidim::Elections::Election } + let(:resource) { election } + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/update_question_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/update_question_spec.rb new file mode 100644 index 00000000..fe1a5a73 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/update_question_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::UpdateQuestion do + subject { described_class.new(form, question) } + + let(:election) { create(:election) } + let(:question) { create(:question, election:, random_answers_order: false) } + let(:organization) { election.component.organization } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:form) do + double( + invalid?: invalid, + current_user: user, + title: { en: "title" }, + max_selections: 3, + min_selections: 1, + weight: 10, + random_answers_order: true, + election: + ) + end + let(:invalid) { false } + + it "updates the question" do + subject.call + expect(translated(question.title)).to eq "title" + expect(question.max_selections).to eq(3) + expect(question.weight).to eq(10) + expect(question.random_answers_order).to be_truthy + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:update!) + .with(question, user, hash_including(:title, :max_selections, :weight, :random_answers_order), visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "update" + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the election has been created in the bulletin board" do + let(:election) { create(:election, :ongoing) } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/admin/update_trustee_participatory_space_spec.rb b/decidim-elections/spec/commands/decidim/elections/admin/update_trustee_participatory_space_spec.rb new file mode 100644 index 00000000..afe59c01 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/admin/update_trustee_participatory_space_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::UpdateTrusteeParticipatorySpace do + subject { described_class.new(trustee_participatory_space) } + + let(:trustee_participatory_space) { create(:trustees_participatory_space) } + + it "toggles the considered status" do + subject.call + expect(trustee_participatory_space.considered).to be(false) + end + + context "when trustee_participatory_space is missing" do + let(:trustee_participatory_space) { nil } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status_spec.rb b/decidim-elections/spec/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status_spec.rb new file mode 100644 index 00000000..88deb2cc --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/trustee_zone/update_election_bulletin_board_status_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::TrusteeZone::UpdateElectionBulletinBoardStatus do + subject { described_class.new(election, required_status) } + + let(:election) { create(:election, required_status) } + let(:required_status) { :key_ceremony } + let(:new_status) { :key_ceremony_ended } + let(:election_status_response) { new_status } + + before do + allow(Decidim::Elections.bulletin_board).to receive(:get_election_status).and_return(election_status_response) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "updates the election status" do + subject.call + expect(election).to be_bb_key_ceremony_ended + end + + context "when the election status does not match the required status" do + let(:election) { create(:election, :tally_ended) } + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "does not update the election status" do + subject.call + expect(election).to be_bb_tally_ended + end + end + + context "when the new election status is tally_ended" do + let(:election) { create(:election, :complete, bb_status: required_status) } + let(:required_status) { :tally_started } + let(:new_status) { :tally_ended } + let(:bullettin_board_server) { "https://my-bb.com" } + let(:election_results_response) do + { + election_results: { + election.questions.first.slug => { + "#{election.questions.first.slug}_#{election.questions.first.answers.first.slug}" => 1, + "#{election.questions.first.slug}_#{election.questions.first.answers.last.slug}" => 2 + }, + election.questions.last.slug => + { + "#{election.questions.last.slug}_#{election.questions.last.answers.first.slug}" => 3, + "#{election.questions.last.slug}_#{election.questions.last.answers.last.slug}" => 4 + } + }, + verifiable_results: { url: "some-url", hash: "some-hash" } + } + end + + before do + allow(Decidim::Elections.bulletin_board).to receive(:get_election_results).and_return(election_results_response) + allow(Decidim::Elections.bulletin_board).to receive(:bulletin_board_server).and_return(bullettin_board_server) + end + + it "updates the election verifiable results data" do + subject.call + election.reload + + expect(election.verifiable_results_file_url).to eq "https://my-bb.com/some-url" + expect(election.verifiable_results_file_hash).to eq "some-hash" + end + + it "create the election results" do + subject.call + + expect(election.bb_closure.results.select { |r| r.question == election.questions.first && r.answer == election.questions.first.answers.first && r.value == 1 }.count).to eq 1 + expect(election.bb_closure.results.select { |r| r.question == election.questions.first && r.answer == election.questions.first.answers.last && r.value == 2 }.count).to eq 1 + expect(election.bb_closure.results.select { |r| r.question == election.questions.last && r.answer == election.questions.last.answers.first && r.value == 3 }.count).to eq 1 + expect(election.bb_closure.results.select { |r| r.question == election.questions.last && r.answer == election.questions.last.answers.last && r.value == 4 }.count).to eq 1 + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/trustee_zone/update_trustee_spec.rb b/decidim-elections/spec/commands/decidim/elections/trustee_zone/update_trustee_spec.rb new file mode 100644 index 00000000..24e6c149 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/trustee_zone/update_trustee_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::TrusteeZone::UpdateTrustee do + subject { described_class.new(form) } + + let(:trustee) { create(:trustee, public_key: nil) } + let(:form) do + double( + invalid?: invalid, + public_key:, + trustee:, + name: trustee_name, + errors: + ) + end + let(:public_key) { "asadasfdafadssda" } + let(:trustee_name) { "Sheldon" } + let(:invalid) { false } + let(:errors) { double.as_null_object } + + it "updates the trustee" do + subject.call + expect(trustee.public_key).to eq "asadasfdafadssda" + end + + context "when trustee with same name and organization exists" do + let!(:other_trustee) { create(:trustee, name: "Sheldon", organization: trustee.organization) } + + it "adds errors to the form" do + expect(errors).to receive(:add).with(:name, :taken) + subject.call + expect(subject).to broadcast(:invalid) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + expect(trustee.public_key).to be_nil + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/voter/cast_vote_spec.rb b/decidim-elections/spec/commands/decidim/elections/voter/cast_vote_spec.rb new file mode 100644 index 00000000..87e413de --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/voter/cast_vote_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Voter::CastVote do + subject { described_class.new(form) } + + let(:form) do + double( + invalid?: invalid, + encrypted_data:, + encrypted_data_hash:, + election:, + election_id:, + voter_id:, + bulletin_board:, + user:, + email:, + current_organization: organization + ) + end + let(:invalid) { false } + let(:encrypted_data) do + # rubocop:disable Naming/VariableNumber + { question_1: "aNsWeR 1" }.to_json + # rubocop:enable Naming/VariableNumber + end + let(:encrypted_data_hash) { "1234" } + let(:election) { create(:election) } + let(:election_id) { election.id } + let(:voter_id) { "voter.1" } + let(:organization) { create(:organization) } + let(:user) { create(:user, :confirmed, organization:) } + let(:email) { "an_email@example.org" } + let(:cast_vote_method) { :cast_vote } + + let(:response) { OpenStruct.new(id: 1, status: "enqueued") } + let(:message_id) { "#{election.id}.vote.cast+v.#{voter_id}" } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(cast_vote_method).and_yield(message_id).and_return(response) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "stores the vote" do + expect { subject.call }.to change(Decidim::Elections::Vote, :count).by(1) + + last_vote = Decidim::Elections::Vote.last + expect(last_vote.election).to eq(election) + expect(last_vote.voter_id).to eq("voter.1") + expect(last_vote.encrypted_vote_hash).to eq("1234") + expect(last_vote.status).to eq("pending") + expect(last_vote.user).to eq(user) + expect(last_vote.email).to eq(email) + end + + it "calls the bulletin board cast_vote method with the correct params" do + subject.call + expect(bulletin_board).to have_received(cast_vote_method).with(election_id, voter_id, encrypted_data) + end + + context "when there is no user for the vote" do + let(:user) { nil } + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "stores the vote without a user" do + expect { subject.call }.to change(Decidim::Elections::Vote, :count).by(1) + + last_vote = Decidim::Elections::Vote.last + expect(last_vote.election).to eq(election) + expect(last_vote.voter_id).to eq("voter.1") + expect(last_vote.encrypted_vote_hash).to eq("1234") + expect(last_vote.status).to eq("pending") + expect(last_vote.user).to be_nil + expect(last_vote.email).to eq(email) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(cast_vote_method).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/elections/voter/update_vote_status_spec.rb b/decidim-elections/spec/commands/decidim/elections/voter/update_vote_status_spec.rb new file mode 100644 index 00000000..b64d33d1 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/elections/voter/update_vote_status_spec.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Voter::UpdateVoteStatus do + subject { described_class.new(vote) } + + let(:election) { create(:election) } + let(:vote) { create(:vote, user:, email:, election:) } + let(:user) { create(:user, organization:) } + let(:component) { election.component } + let(:organization) { component.organization } + let(:email) { "an_email@example.org" } + let(:method_name) { :get_pending_message_status } + let(:response) { :accepted } + let(:verify_url) { "http://#{organization.host}:#{Capybara.server_port}/processes/#{component.participatory_space.slug}/f/#{component.id}/elections/#{election.id}/votes/#{vote.encrypted_vote_hash}/verify" } + + before do + allow(Decidim::Elections.bulletin_board).to receive(method_name).and_return(response) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "updates the vote status" do + subject.call + expect(vote.status).to eq "accepted" + end + + it "sends a notification to voter" do + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.elections.votes.accepted_votes", + event_class: Decidim::Elections::Votes::VoteAcceptedEvent, + resource: vote.election, + affected_users: [vote.user], + extra: { + vote:, + verify_url: + } + ) + subject.call + end + + it "does not send an extra email" do + expect(Decidim::Elections::VoteAcceptedMailer) + .not_to receive(:notification) + + subject.call + end + + context "when the vote does not have a user, but has an email address" do + let(:user) { nil } + let(:mailer) { double(:mailer) } + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "updates the vote status" do + subject.call + expect(vote.status).to eq "accepted" + end + + it "does not sends a notification" do + expect(Decidim::EventsManager) + .not_to receive(:publish) + + subject.call + end + + it "sends an email" do + allow(Decidim::Elections::VoteAcceptedMailer) + .to receive(:notification) + .with(vote, verify_url, I18n.locale.to_s) + .and_return(mailer) + expect(mailer) + .to receive(:deliver_later) + + subject.call + end + end + + context "when the vote does not have a user not an email address" do + let(:user) { nil } + let(:email) { nil } + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "updates the vote status" do + subject.call + expect(vote.status).to eq "accepted" + end + + it "does not sends a notification" do + expect(Decidim::EventsManager) + .not_to receive(:publish) + + subject.call + end + + it "does not send an email" do + expect(Decidim::Elections::VoteAcceptedMailer) + .not_to receive(:notification) + + subject.call + end + end + + context "when the Bulletin Board has rejected the vote" do + let(:response) { :rejected } + + it "updates the vote status" do + subject.call + expect(vote.status).to eq "rejected" + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/create_ballot_style_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/create_ballot_style_spec.rb new file mode 100644 index 00000000..b808b693 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/create_ballot_style_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe CreateBallotStyle do + subject { described_class.new(form) } + + let(:user) { create(:user, :admin, :confirmed) } + let(:election) { create(:election, :complete, component: elections_component) } + let(:elections_component) { create(:elections_component, participatory_space: voting) } + let(:voting) { create(:voting, organization: user.organization) } + + let(:form) do + double( + valid?: valid, + code:, + question_ids:, + current_participatory_space: voting, + current_user: user, + errors: + ) + end + + let(:valid) { true } + let(:code) { "Code".upcase } + let(:question_ids) { election.questions.sample(2).map(&:id) } + let(:errors) { double.as_null_object } + + let(:ballot_style) { Decidim::Votings::BallotStyle.last } + + it "creates the ballot style" do + expect { subject.call }.to change(Decidim::Votings::BallotStyle, :count).by(1) + end + + it "creates the association between ballot style and questions" do + expect { subject.call }.to change(Decidim::Votings::BallotStyleQuestion, :count).by(2) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "stores the given data" do + subject.call + expect(ballot_style.code).to eq code.upcase + expect(ballot_style.questions.pluck(:id)).to match_array(question_ids) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create!) + .with( + Decidim::Votings::BallotStyle, + user, + kind_of(Hash), + visibility: "all" + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.action).to eq "create" + end + + context "when the form is not valid" do + let(:valid) { false } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when a ballot style with the same code exists" do + context "when it is in the same voting" do + let!(:existing_ballot_style) { create(:ballot_style, voting:, code:) } + + it "is not valid" do + expect(errors).to receive(:add).with(:code, :taken) + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when it is in another voting" do + let!(:existing_ballot_style) { create(:ballot_style, code:) } + + it "is valid" do + expect { subject.call }.to broadcast(:ok) + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/create_monitoring_committee_member_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/create_monitoring_committee_member_spec.rb new file mode 100644 index 00000000..d0b3394e --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/create_monitoring_committee_member_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe CreateMonitoringCommitteeMember do + subject { described_class.new(form, current_user, voting) } + + let!(:existing_user) { create(:user, email: user_email, organization: voting.organization) } + let!(:current_user) { create(:user, email: ::Faker::Internet.email, organization: voting.organization) } + let(:voting) { create(:voting) } + let(:name) { ::Faker::Name.name } + let(:email) { ::Faker::Internet.email } + let(:user) { existing_user } + let(:user_email) { "existing_user@example.org" } + let(:form) do + double( + invalid?: invalid, + email:, + name:, + current_participatory_space: voting, + user: + ) + end + let(:invalid) { false } + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when using an existing user" do + it "creates the memeber" do + subject.call + + expect(Decidim::Votings::MonitoringCommitteeMember.count).to eq 1 + expect(Decidim::Votings::MonitoringCommitteeMember.first.voting).to eq voting + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:create, Decidim::Votings::MonitoringCommitteeMember, current_user, resource: hash_including(:title)) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + end + + context "when inviting a new user" do + let(:user) { nil } + + describe "when the email is not taken" do + let(:email) { "not_yet_existing@example.com" } + + it "creates a new user" do + subject.call + created_user = Decidim::User.last + expect(created_user.email).to eq(email) + end + end + + describe "when the email already exists" do + let(:email) { user_email } + + it "does not create a new user" do + expect { subject.call }.to broadcast(:ok) + + monitoring_committee_members = Decidim::Votings::MonitoringCommitteeMember.where(user: existing_user) + + expect(monitoring_committee_members.count).to eq 1 + end + + it "resends the invitation if the user has not accepted it yet" do + existing_user.invite! + + expect { subject.call }.to have_enqueued_job(ActionMailer::MailDeliveryJob) + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/create_polling_officer_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/create_polling_officer_spec.rb new file mode 100644 index 00000000..87ec302d --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/create_polling_officer_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe CreatePollingOfficer do + subject { described_class.new(form, current_user, voting) } + + let!(:existing_user) { create(:user, email: user_email, organization: voting.organization) } + let!(:current_user) { create(:user, email: ::Faker::Internet.email, organization: voting.organization) } + let(:voting) { create(:voting) } + let(:name) { ::Faker::Name.name } + let(:email) { ::Faker::Internet.email } + let(:user) { existing_user } + let(:user_email) { "existing_user@example.org" } + let(:form) do + double( + invalid?: invalid, + email:, + name:, + current_participatory_space: voting, + user: + ) + end + let(:invalid) { false } + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when using an existing user" do + it "creates the polling officer" do + subject.call + + expect(Decidim::Votings::PollingOfficer.count).to eq 1 + expect(Decidim::Votings::PollingOfficer.first.voting).to eq voting + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:create, Decidim::Votings::PollingOfficer, current_user, resource: hash_including(:title)) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + end + + context "when inviting a new user" do + let(:user) { nil } + + describe "when the email is not taken" do + let(:email) { "not_yet_existing@example.com" } + + it "creates a new user" do + subject.call + created_user = Decidim::User.last + expect(created_user.email).to eq(email) + end + end + + describe "when the email already exists" do + let(:email) { user_email } + + it "does not create a new user" do + expect { subject.call }.to broadcast(:ok) + + polling_officers = Decidim::Votings::PollingOfficer.where(user: existing_user) + + expect(polling_officers.count).to eq 1 + end + + it "resends the invitation if the user has not accepted it yet" do + existing_user.invite! + + expect { subject.call }.to have_enqueued_job(ActionMailer::MailDeliveryJob) + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/create_polling_station_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/create_polling_station_spec.rb new file mode 100644 index 00000000..090c27b4 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/create_polling_station_spec.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe CreatePollingStation do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:voting) { create(:voting, voting_type: "hybrid", organization:) } + let(:president) { nil } + let(:managers) { [] } + + let(:form) do + double( + invalid?: invalid, + title:, + location:, + location_hints:, + address:, + latitude:, + longitude:, + polling_station_president: president, + polling_station_president_id: president&.id, + polling_station_managers: managers, + polling_station_manager_ids: managers.pluck(:id), + current_user: user, + current_organization: organization, + voting: + ) + end + + let(:title) { { en: "Polling Station Deluxe" } } + let(:location) { { en: "A nice location" } } + let(:location_hints) { { en: "Catch me if you can" } } + let(:invalid) { false } + let(:address) { "address" } + let(:latitude) { 40.1234 } + let(:longitude) { 2.1234 } + + let(:polling_station) { Decidim::Votings::PollingStation.last } + + it "creates the voting" do + expect { subject.call }.to change(Decidim::Votings::PollingStation, :count).by(1) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "stores the given data" do + subject.call + expect(translated(polling_station.title)).to eq title[:en] + expect(translated(polling_station.location)).to eq location[:en] + expect(translated(polling_station.location_hints)).to eq location_hints[:en] + expect(polling_station.address).to eq address + expect(polling_station.latitude).to eq latitude + expect(polling_station.longitude).to eq longitude + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create!) + .with( + Decidim::Votings::PollingStation, + user, + kind_of(Hash), + visibility: "all" + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when selecting a president" do + let(:president) { create(:polling_officer, voting:) } + + it "stores the reference correctly" do + subject.call + + expect(president.reload.presided_polling_station).to eq polling_station + expect(polling_station.reload.polling_station_president).to eq president + end + + it "notifies the president" do + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.votings.polling_officers.polling_station_assigned", + event_class: PollingOfficers::PollingStationAssignedEvent, + resource: voting, + affected_users: [president.user], + followers: [], + extra: { polling_officer_id: president.id } + ) + + subject.call + end + end + + context "when selecting managers" do + let(:managers) { create_list(:polling_officer, 3, voting:) } + + it "stores the reference correctly" do + subject.call + + managers.each do |manager| + expect(manager.reload.managed_polling_station).to eq polling_station + expect(polling_station.reload.polling_station_managers).to include(manager) + end + end + + it "notifies the manmagers" do + managers.each do |manager| + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.votings.polling_officers.polling_station_assigned", + event_class: PollingOfficers::PollingStationAssignedEvent, + resource: voting, + affected_users: [manager.user], + followers: [], + extra: { polling_officer_id: manager.id } + ) + end + + subject.call + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/create_voting_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/create_voting_spec.rb new file mode 100644 index 00000000..6aa5d0de --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/create_voting_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe CreateVoting do + subject { described_class.new(form) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:user) { create(:user, :admin, :confirmed, organization:) } + + let(:form) do + double( + invalid?: invalid, + title: { en: "The voting space title" }, + description: { en: "The voting space description" }, + start_time:, + end_time:, + slug:, + scope:, + current_user: user, + current_organization: organization, + banner_image: nil, + introductory_image: nil, + promoted:, + voting_type:, + census_contact_information:, + show_check_census: + ) + end + + let(:invalid) { false } + let(:title) { { en: "The voting space title" } } + let(:description) { { en: "The voting space description" } } + let(:start_time) { 1.day.from_now } + let(:end_time) { start_time + 1.month } + let(:slug) { "voting-slug" } + let(:scope) { create(:scope, organization:) } + let(:promoted) { true } + let(:voting_type) { "online" } + let(:census_contact_information) { nil } + let(:show_check_census) { true } + + let(:voting) { Decidim::Votings::Voting.last } + + it "creates the voting" do + expect { subject.call }.to change(Decidim::Votings::Voting, :count).by(1) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "stores the given data" do + subject.call + expect(translated(voting.title)).to eq title[:en] + expect(translated(voting.description)).to eq description[:en] + expect(voting.start_time).to be_within(1.second).of start_time + expect(voting.end_time).to be_within(1.second).of end_time + expect(voting.slug).to eq slug + expect(voting.scope).to eq scope + expect(voting.organization).to eq organization + expect(voting.promoted).to eq promoted + expect(voting.voting_type).to eq voting_type + expect(voting.show_check_census).to eq show_check_census + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create) + .with( + Decidim::Votings::Voting, + user, + kind_of(Hash) + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/destroy_ballot_style_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/destroy_ballot_style_spec.rb new file mode 100644 index 00000000..6297e310 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/destroy_ballot_style_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe DestroyBallotStyle do + subject { described_class.new(ballot_style, user) } + + let(:ballot_style) { create(:ballot_style) } + let(:user) { create(:user, organization: ballot_style.voting.organization) } + let(:election) { create(:election, :complete, component: elections_component) } + let(:elections_component) { create(:elections_component, participatory_space: ballot_style.voting) } + let!(:ballot_style_questions) do + election.questions.map { |question| create(:ballot_style_question, question:, ballot_style:) } + end + + context "when everything is ok" do + it "destroys the ballot style" do + subject.call + + expect { ballot_style.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "destroys the ballot style questions" do + subject.call + + expect(Decidim::Votings::BallotStyleQuestion.where(decidim_votings_ballot_style_id: ballot_style.id).count).to eq 0 + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:delete, ballot_style, user, { visibility: "all", code: ballot_style.code }) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.action).to eq("delete") + expect(action_log.extra["code"]).to eq(ballot_style.code) + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/destroy_monitoring_committee_member_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/destroy_monitoring_committee_member_spec.rb new file mode 100644 index 00000000..d25a38c4 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/destroy_monitoring_committee_member_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe DestroyMonitoringCommitteeMember do + subject { described_class.new(monitoring_committee_member, current_user) } + + let(:voting) { create(:voting) } + let!(:user) { create(:user, :confirmed, organization: voting.organization) } + let(:monitoring_committee_member) { create(:monitoring_committee_member, user:, voting:) } + let!(:current_user) { create(:user, email: "some_email@example.org", organization: voting.organization) } + + context "when everything is ok" do + let(:log_info) do + { + resource: { + title: user.name + } + } + end + + it "destroys the monitoring committee member" do + subject.call + expect { monitoring_committee_member.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with("delete", monitoring_committee_member, current_user, log_info) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "destroy" + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_officer_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_officer_spec.rb new file mode 100644 index 00000000..6e9be1b3 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_officer_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe DestroyPollingOfficer do + subject { described_class.new(polling_officer, current_user) } + + let(:voting) { create(:voting) } + let!(:user) { create(:user, :confirmed, organization: voting.organization) } + let(:polling_officer) { create(:polling_officer, user:, voting:) } + let!(:current_user) { create(:user, email: "some_email@example.org", organization: voting.organization) } + + context "when everything is ok" do + let(:log_info) do + { + resource: { + title: user.name + } + } + end + + it "destroys the polling_officer" do + subject.call + expect { polling_officer.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with("delete", polling_officer, current_user, log_info) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "destroy" + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_station_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_station_spec.rb new file mode 100644 index 00000000..15697aa1 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/destroy_polling_station_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe DestroyPollingStation do + subject { described_class.new(polling_station, user) } + + let(:polling_station) { create(:polling_station) } + let(:user) { create(:user, :admin, organization: polling_station.voting.organization) } + + context "when everything is ok" do + it "destroys the polling station" do + subject.call + + expect { polling_station.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:delete, polling_station, user, visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure_spec.rb new file mode 100644 index 00000000..4bab2b95 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/monitoring_committee_validate_polling_station_closure_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe MonitoringCommitteeValidatePollingStationClosure do + subject { described_class.new(form, closure) } + + let(:closure) { create(:ps_closure) } + let(:form) do + double( + invalid?: invalid, + monitoring_committee_notes: + ) + end + let(:invalid) { false } + let(:monitoring_committee_notes) { ::Faker::Lorem.paragraph } + + context "when everything is ok" do + it "updates the monitoring_committee_notes and updated_at time" do + expect(subject.call).to broadcast(:ok) + + closure.reload + + expect(closure.monitoring_committee_notes).to eq(monitoring_committee_notes) + expect(closure.validated_at).to be_present + expect(closure.validated_at).to be_a(Date) + end + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/publish_voting_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/publish_voting_spec.rb new file mode 100644 index 00000000..ae026653 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/publish_voting_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Voting can be published", type: :system do + it_behaves_like "Publicable space", :voting +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/unpublish_voting_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/unpublish_voting_spec.rb new file mode 100644 index 00000000..e206496c --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/unpublish_voting_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Voting can be unpublished", type: :system do + it_behaves_like "Unpublicable space", :voting +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/update_ballot_style_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/update_ballot_style_spec.rb new file mode 100644 index 00000000..7f376053 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/update_ballot_style_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe UpdateBallotStyle do + let(:ballot_style) { create(:ballot_style) } + let(:user) { create(:user, organization: ballot_style.voting.organization) } + let!(:other_ballot_style) { create(:ballot_style, voting: ballot_style.voting, code: taken_code.upcase) } + let(:election) { create(:election, :complete, component: elections_component) } + let(:elections_component) { create(:elections_component, participatory_space: ballot_style.voting) } + let(:ballot_style_questions) do + election.questions.first(2).map { |question| create(:ballot_style_question, question:, ballot_style:) } + end + let(:params) do + { + ballot_style: { + id: ballot_style.id, + code: updated_code, + voting: ballot_style.voting, + question_ids: updated_question_ids + } + } + end + let(:updated_code) { "Updated code" } + let(:taken_code) { "Taken code" } + let(:updated_question_ids) { election.questions.last(3).map(&:id) } + let(:form) do + BallotStyleForm.from_params(params).with_context( + voting: ballot_style.voting, + ballot_style_id: ballot_style.id, + current_user: user + ) + end + + subject { described_class.new(form, ballot_style) } + + context "when the form is not valid" do + context "when the code is not present" do + let(:updated_code) { nil } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the code is already in use" do + let(:updated_code) { taken_code } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + end + + describe "when the form is valid" do + it "updates the ballot style attributes" do + expect { subject.call }.to broadcast(:ok) + ballot_style.reload + + expect(ballot_style.code).to eq(updated_code.upcase) + end + + it "updates the ballot style questions" do + expect { subject.call }.to broadcast(:ok) + + ballot_style.reload + expect(ballot_style.questions.map(&:id)).to match_array(updated_question_ids) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:update!) + .with(ballot_style, user, hash_including(:code), visibility: "all") + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.action).to eq "update" + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/update_polling_station_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/update_polling_station_spec.rb new file mode 100644 index 00000000..b5994239 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/update_polling_station_spec.rb @@ -0,0 +1,217 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe UpdatePollingStation do + let(:polling_station) { create(:polling_station) } + let!(:president) { create(:polling_officer, voting: polling_station.voting, presided_polling_station: polling_station) } + let!(:managers) { create_list(:polling_officer, 3, voting: polling_station.voting, managed_polling_station: polling_station) } + + let(:updated_president) { create(:polling_officer, voting: polling_station.voting, presided_polling_station: nil) } + let(:updated_managers) { create_list(:polling_officer, 3, voting: polling_station.voting, managed_polling_station: nil) } + let(:params) do + { + polling_station: { + id: polling_station.id, + title_en: "Updated title", + title_ca: "Título actualizado", + title_es: "Títol actualitzat", + location_en: "Updated location", + location_es: "Location actualizada", + location_ca: "Location actualitzada", + location_hints_en: "Updated location hints", + location_hints_es: "Location hints actualizados", + location_hints_ca: "Location hints actualitzats", + address: "Updated address", + latitude: 40.123, + longitude: 7.321, + voting: polling_station.voting, + polling_station_president_id: updated_president&.id, + polling_station_manager_ids: updated_managers.pluck(:id) + } + } + end + let(:context) do + { + current_organization: polling_station.voting.organization + } + end + let(:form) { PollingStationForm.from_params(params).with_context(context) } + + subject { described_class.new(form, polling_station) } + + context "when the form is not valid" do + let(:params) { { address: nil } } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + + it "does not update the polling station" do + subject.call + polling_station.reload + + expect(polling_station.title["en"]).not_to eq("Updated title") + end + end + + describe "when the form is valid" do + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "updates the polling station title" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + + expect(polling_station.title["en"]).to eq("Updated title") + end + + it "updates the polling station location" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + + expect(polling_station.location["es"]).to eq("Location actualizada") + end + + it "updates the polling station location hints" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + + expect(polling_station.location_hints["ca"]).to eq("Location hints actualitzats") + end + + it "updates the polling station address" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + + expect(polling_station.address).to eq("Updated address") + end + + context "when updating the polling station president" do + let(:updated_managers) { [] } + + context "when the president is nil" do + let(:updated_president) { nil } + + it "unussigns the president" do + expect { subject.call }.to broadcast(:ok) + expect(president.reload.presided_polling_station).to be_nil + expect(polling_station.reload.polling_station_president).to be_nil + end + end + + context "when there is a new president" do + it "assigns the new president" do + expect { subject.call }.to broadcast(:ok) + expect(updated_president.reload.presided_polling_station).to eq polling_station + expect(president.reload.presided_polling_station).to be_nil + expect(polling_station.reload.polling_station_president).to eq updated_president + end + + it "notifies the new president" do + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.votings.polling_officers.polling_station_assigned", + event_class: PollingOfficers::PollingStationAssignedEvent, + resource: polling_station.voting, + affected_users: [updated_president.user], + followers: [], + extra: { polling_officer_id: updated_president.id } + ) + + expect { subject.call }.to broadcast(:ok) + end + end + end + + context "when updating the polling station managers" do + let(:updated_president) { nil } + + context "when the are no managers" do + let(:updated_managers) { [] } + + it "unussigns all the managers" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + expect(polling_station.polling_station_managers.count).to eq updated_managers.count + managers.each do |manager| + expect(manager.reload.managed_polling_station).to be_nil + end + expect(polling_station.polling_station_managers).to be_empty + end + end + + context "when the managers are all new" do + it "assigns the new managers" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + expect(polling_station.polling_station_managers.count).to eq updated_managers.count + updated_managers.each do |updated_manager| + expect(updated_manager.reload.managed_polling_station).to eq polling_station + expect(polling_station.polling_station_managers).to include(updated_manager) + end + managers.each do |manager| + expect(manager.reload.managed_polling_station).to be_nil + expect(polling_station.polling_station_managers).not_to include(manager) + end + end + + it "notifies the new managers" do + updated_managers.each do |updated_manager| + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.votings.polling_officers.polling_station_assigned", + event_class: PollingOfficers::PollingStationAssignedEvent, + resource: polling_station.voting, + affected_users: [updated_manager.user], + followers: [], + extra: { polling_officer_id: updated_manager.id } + ) + end + + expect { subject.call }.to broadcast(:ok) + end + end + + context "when there managers are added and removed" do + let(:old_manager) { managers.first } + let(:new_manager) { create(:polling_officer, voting: polling_station.voting) } + let(:updated_managers) { [old_manager, new_manager] } + + it "assigns the added managers and unussigns the removed ones" do + expect { subject.call }.to broadcast(:ok) + polling_station.reload + expect(polling_station.polling_station_managers.count).to eq updated_managers.count + updated_managers.each do |updated_manager| + expect(updated_manager.reload.managed_polling_station).to eq polling_station + expect(polling_station.polling_station_managers).to include(updated_manager) + end + end + + it "notifies the new managers" do + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.votings.polling_officers.polling_station_assigned", + event_class: PollingOfficers::PollingStationAssignedEvent, + resource: polling_station.voting, + affected_users: [new_manager.user], + followers: [], + extra: { polling_officer_id: new_manager.id } + ) + + expect { subject.call }.to broadcast(:ok) + end + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/admin/update_voting_spec.rb b/decidim-elections/spec/commands/decidim/votings/admin/update_voting_spec.rb new file mode 100644 index 00000000..5f52d118 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/admin/update_voting_spec.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe UpdateVoting do + let(:voting) { create(:voting) } + let(:params) do + { + voting: { + id: voting.id, + title_en: "Foo title", + title_ca: "Foo title", + title_es: "Foo title", + description_en: voting.description["en"], + description_ca: voting.description["ca"], + description_es: voting.description["es"], + slug: voting.slug, + decidim_scope_id: voting.scope.id, + start_time: voting.start_time, + end_time: voting.end_time, + promoted: voting.promoted, + voting_type: voting.voting_type, + census_contact_information: voting.census_contact_information + }.merge(attachment_params) + } + end + let(:attachment_params) do + { + banner_image: voting.banner_image.blob, + introductory_image: voting.introductory_image.blob + } + end + let(:context) do + { + current_organization: voting.organization, + voting_id: voting.id + } + end + let(:form) { VotingForm.from_params(params).with_context(context) } + let(:command) { described_class.new(voting, form) } + + describe "when the form is not valid" do + before do + allow(form).to receive(:invalid?).and_return(true) + end + + it "broadcasts invalid" do + expect { command.call }.to broadcast(:invalid) + end + + it "does not update the voting" do + command.call + voting.reload + + expect(voting.title["en"]).not_to eq("Foo title") + end + end + + describe "when the voting is not valid" do + before do + allow(form).to receive(:invalid?).and_return(false) + expect(voting).to receive(:valid?).at_least(:once).and_return(false) + voting.errors.add(:banner_image, "File resolution is too large") + end + + it "broadcasts invalid" do + expect { command.call }.to broadcast(:invalid) + end + + it "adds errors to the form" do + command.call + + expect(form.errors[:banner_image]).not_to be_empty + end + end + + describe "when the form is valid" do + it "broadcasts ok" do + expect { command.call }.to broadcast(:ok) + end + + it "updates the voting" do + expect { command.call }.to broadcast(:ok) + voting.reload + + expect(voting.title["en"]).to eq("Foo title") + end + + context "when banner image is not updated" do + let(:attachment_params) do + { + introductory_image: voting.introductory_image.blob + } + end + + it "does not replace the banner image" do + expect(voting).not_to receive(:banner_image=) + + command.call + voting.reload + + expect(voting.banner_image).to be_present + end + end + + context "when introductory image is not updated" do + let(:attachment_params) do + { + banner_image: voting.banner_image.blob + } + end + + it "does not replace the introductory image" do + expect(voting).not_to receive(:introductory_image=) + + command.call + voting.reload + + expect(voting.introductory_image).to be_present + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/create_dataset_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/create_dataset_spec.rb new file mode 100644 index 00000000..a0e9a09d --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/create_dataset_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe CreateDataset do + subject { described_class.new(form, user) } + + let(:voting) { create(:voting) } + let(:user) { create(:user, :admin, organization: voting.organization) } + let(:file) { upload_test_file(Decidim::Dev.test_file("import_voting_census.csv", "text/csv")) } + let(:params) { { file: } } + let(:context) { { current_participatory_space: voting } } + + let(:form) { DatasetForm.from_params(params).with_context(context) } + + context "when the form is not valid" do + let(:file) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + + it "does not enqueue any job" do + expect(CreateDatumJob).not_to receive(:perform_later) + + subject.call + end + end + + context "when headers are invalid" do + let(:file) { upload_test_file(Decidim::Dev.test_file("import_voting_census_without_headers.csv", "text/csv")) } + + it "broadcasts invalid_csv_file" do + expect(subject.call).to broadcast(:invalid_csv_header) + end + + it "does not enqueue any job" do + expect(CreateDatumJob).not_to receive(:perform_later) + + subject.call + end + end + + context "when file only contains headers" do + let(:file) { upload_test_file(Decidim::Dev.test_file("import_voting_census_only_headers.csv", "text/csv")) } + + it "broadcasts invalid_csv_file" do + expect(subject.call).to broadcast(:invalid_csv_header) + end + + it "does not enqueue any job" do + expect(CreateDatumJob).not_to receive(:perform_later) + + subject.call + end + end + + it "broadcasts ok" do + expect(subject.call).to broadcast(:ok) + expect(Decidim::Votings::Census::Dataset.last.csv_row_raw_count).to eq(5) + end + + context "when active storage service is not local" do + before do + allow(ActiveStorage::Blob.service).to receive(:respond_to?).and_call_original + # rubocop:disable RSpec/StubbedMock + expect(ActiveStorage::Blob.service).to receive(:respond_to?).with(:path_for).and_return(false) + # rubocop:enable RSpec/StubbedMock + end + + it "still broadcasts ok" do + expect(subject.call).to broadcast(:ok) + end + end + + it "enqueues a job for processing the dataset and strips the data from whitespaces" do + expect { subject.call }.to(have_enqueued_job(CreateDatumJob).at_least(1).times.with do |_user, _dataset, row| + expect(row[3]).to eq("Hugo Doe") if row.first == "55566677B" + end) + end + + it "enqueues a job for processing the dataset" do + expect { subject.call }.to enqueue_job(CreateDatumJob).exactly(5).times + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:create) + .with( + Decidim::Votings::Census::Dataset, + user, + kind_of(Hash), + visibility: "admin-only" + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.where(resource_type: "Decidim::Votings::Census::Dataset").last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "create" + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/create_datum_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/create_datum_spec.rb new file mode 100644 index 00000000..9c7a62d9 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/create_datum_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe CreateDatum do + subject { described_class.new(form, dataset) } + + let(:dataset) { create(:dataset) } + let(:datum) { Decidim::Votings::Census::Datum.last } + let!(:ballot_style) { create(:ballot_style, code: ballot_style_code, voting: dataset.voting) } + let(:ballot_style_code) { "BS1" } + let(:user) { create(:user, :admin, organization: dataset.voting.organization) } + let(:birthdate) { "20010414" } + let(:params) do + { + document_number:, + document_type: "passport", + birthdate:, + full_name: "Jane Doe", + full_address: "Nowhere street 1", + postal_code: "12345", + mobile_phone_number: "123456789", + ballot_style_code: ballot_style_code&.downcase, + email: + } + end + + let(:context) do + { + current_user: user, + dataset:, + voting: dataset.voting + } + end + + let(:form) { DatumForm.from_params(params).with_context(context) } + + let(:document_number) { "123456789Y" } + let(:email) { "example@test.org" } + + context "when the form is not valid" do + let(:document_number) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + context "when the birthdate is not exactly YYYYmmdd" do + let(:birthdate) { "02/12/2001" } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + it "broadcasts ok" do + expect(subject.call).to broadcast(:ok) + end + + it "associates to the correct ballot style" do + subject.call + expect(datum.ballot_style).to eq(ballot_style) + end + + context "when the ballot style code is provided" do + let(:ballot_style_code) { nil } + + it "does not associate any ballot style" do + subject.call + expect(datum.ballot_style).to eq(ballot_style) + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/destroy_dataset_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/destroy_dataset_spec.rb new file mode 100644 index 00000000..5fbab485 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/destroy_dataset_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe DestroyDataset do + subject { described_class.new(dataset, user) } + + let(:dataset) { create(:dataset) } + let(:user) { create(:user, :admin, organization: dataset.voting.organization) } + + context "when everything is ok" do + it "destroys the dataset" do + subject.call + + expect { dataset.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:delete, dataset, user) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.version).to be_present + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/increment_dataset_processed_rows_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/increment_dataset_processed_rows_spec.rb new file mode 100644 index 00000000..3680ba5b --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/increment_dataset_processed_rows_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe IncrementDatasetProcessedRows do + subject { described_class.new(dataset) } + + let!(:dataset) { create(:dataset, csv_row_processed_count: 0, status: "creating_data") } + + context "when everything is ok" do + it "increments the processed rows" do + expect { subject.call }.to change(dataset, :csv_row_processed_count).from(0).to(1) + end + + it "changes the dataset status" do + expect { subject.call }.to change(dataset, :status).from("creating_data").to("data_created") + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_export_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_export_spec.rb new file mode 100644 index 00000000..2361bfb2 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_export_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe LaunchAccessCodesExport do + subject { described_class.new(dataset, user) } + + let(:dataset) { create(:dataset, :codes_generated) } + let(:user) { create(:user, :admin, organization: dataset.organization) } + + context "when the inputs are not valid" do + context "when the user in nil" do + let(:dataset) { create(:dataset) } + let(:user) { nil } + + it { expect(subject).to broadcast(:invalid) } + end + + context "when the dataset is not in the right status" do + let(:dataset) { create(:dataset, status: :init_data) } + + it { expect(subject).to broadcast(:invalid) } + end + end + + context "when the inputs are valid" do + let(:dataset) { create(:dataset, :codes_generated) } + + it "updates the data" do + expect { subject.call }.to enqueue_job(ExportAccessCodesJob) + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_generation_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_generation_spec.rb new file mode 100644 index 00000000..8dca6172 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/launch_access_codes_generation_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe LaunchAccessCodesGeneration do + subject { described_class.new(dataset, user) } + + let(:dataset) { create(:dataset, status: :data_created) } + let(:user) { create(:user, :admin, organization: dataset.organization) } + + context "when the inputs are not valid" do + context "when the user in nil" do + let(:dataset) { create(:dataset) } + let(:user) { nil } + + it { expect(subject).to broadcast(:invalid) } + end + + context "when the is no datum in the dataset" do + it { expect(subject).to broadcast(:invalid) } + end + + context "when the dataset is not in the right status" do + let(:dataset) { create(:dataset, status: :init_data) } + + it { expect(subject).to broadcast(:invalid) } + end + end + + context "when the inputs are valid" do + let(:dataset) { create(:dataset, :with_data, :data_created) } + + it "updates the data" do + expect(subject).to broadcast(:ok) + + expect(dataset.reload).to be_generating_codes + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/census/admin/update_dataset_spec.rb b/decidim-elections/spec/commands/decidim/votings/census/admin/update_dataset_spec.rb new file mode 100644 index 00000000..7a7e8517 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/census/admin/update_dataset_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Census::Admin + describe UpdateDataset do + subject { described_class.new(dataset, attributes, user) } + + let(:dataset) { create(:dataset, status: "init_data") } + let(:user) { create(:user, :admin, organization:) } + let(:organization) { dataset&.organization || create(:organization) } + let(:attributes) { { status: :data_created } } + + context "when the inputs are NOT valid" do + context "when the user is nil" do + let(:dataset) { create(:dataset) } + let(:user) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + context "when the dataset is nil" do + let(:dataset) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + context "when there is no attribute" do + let(:attributes) { {} } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + end + + context "when the inputs are valid" do + it "updates the dataset" do + expect(subject.call).to broadcast(:ok) + expect(dataset.reload.status).to match("data_created") + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:update!) + .with( + dataset, + user, + attributes + ) + .and_call_original + + expect { subject.call }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.where(resource_type: "Decidim::Votings::Census::Dataset").last + expect(action_log.version).to be_present + expect(action_log.version.event).to eq "update" + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/certify_polling_station_closure_spec.rb b/decidim-elections/spec/commands/decidim/votings/certify_polling_station_closure_spec.rb new file mode 100644 index 00000000..72185536 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/certify_polling_station_closure_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe CertifyPollingStationClosure do + subject { described_class.new(form, closure) } + + let(:closure) { create(:ps_closure, :with_results, phase: :certificate) } + let(:add_photos) { [upload_test_file(Decidim::Dev.test_file("city.jpeg", "image/jpeg"))] } + + let(:form) { ClosureCertifyForm.from_params(add_photos:).with_context(closure:) } + + it "saves the attachment" do + expect { subject.call }.to change(Decidim::Attachment, :count).by(1) + expect(closure.photos.first).to be_present + expect(closure.photos.first).to be_a(Decidim::Attachment) + end + + it "changes to signature phase" do + subject.call + + expect(closure.signature_phase?).to be true + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/check_census_spec.rb b/decidim-elections/spec/commands/decidim/votings/check_census_spec.rb new file mode 100644 index 00000000..19420f55 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/check_census_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe CheckCensus do + subject { described_class.new(form) } + + let(:voting) { create(:voting) } + let(:datum) { create(:datum, document_number:, document_type:, birthdate:, postal_code:, dataset:) } + let(:context) { { current_participatory_space: voting } } + let(:dataset) { create(:dataset, voting:) } + let(:params) do + { + document_number:, + document_type:, + year: year.to_s, + month: month.to_s, + day: day.to_s, + postal_code: + } + end + let(:birthdate) { Date.civil(year, month, day) } + let(:document_number) { "123456789Y" } + let(:document_type) { "passport" } + let(:year) { 1985 } + let(:month) { 3 } + let(:day) { 1 } + let(:postal_code) { "12345" } + + let(:form) { Census::CheckForm.from_params(params).with_context(context) } + + context "when the form is not valid" do + let(:document_number) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + context "when census is found" do + let!(:datum) { create(:datum, document_number:, document_type:, birthdate:, postal_code:, dataset:) } + + it "broadcasts ok and returns the datum" do + expect(subject.call).to broadcast(:ok, datum) + end + end + + context "when census is not found" do + let!(:datum) { create(:datum, document_number: "987654321Y", document_type:, birthdate:, postal_code:) } + + it "returns not_found" do + expect(subject.call).to broadcast(:not_found) + end + end + + context "when hashed_checked_data exists in different dataset" do + let!(:datum) { create(:datum, document_number: "987654321Y", document_type:, birthdate:, postal_code:) } + let!(:voting) { create(:voting) } + + it "returns not_found" do + expect(subject.call).to broadcast(:not_found) + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/create_polling_station_closure_spec.rb b/decidim-elections/spec/commands/decidim/votings/create_polling_station_closure_spec.rb new file mode 100644 index 00000000..f68d6516 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/create_polling_station_closure_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe CreatePollingStationClosure do + subject { described_class.new(form) } + + let(:voting) { create(:voting) } + let(:component) { create(:elections_component, participatory_space: voting) } + let(:election) { create(:election, component:) } + let(:polling_station) { create(:polling_station, voting:) } + let(:polling_officer) { create(:polling_officer, voting:) } + + let(:params) do + { + polling_station_id: polling_station.id, + election_id: election.id, + election_votes_count: 100, + total_ballots_count:, + polling_officer_notes: + } + end + + let(:total_ballots_count) { 100 } + let(:polling_officer_notes) { Faker::Lorem.sentence } + + let(:form) { EnvelopesResultForm.new(params).with_context(polling_officer:) } + + context "when the form is not valid" do + let(:total_ballots_count) { 101 } + let(:polling_officer_notes) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + context "when the closure is created" do + it "broadcasts ok" do + expect(subject.call).to broadcast(:ok) + end + + it "changes to results phase" do + subject.call + + expect(PollingStationClosure.last.results_phase?).to be true + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/create_polling_station_results_spec.rb b/decidim-elections/spec/commands/decidim/votings/create_polling_station_results_spec.rb new file mode 100644 index 00000000..240580d6 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/create_polling_station_results_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe CreatePollingStationResults do + subject { described_class.new(form, closure) } + + let(:voting) { create(:voting) } + let(:component) { create(:elections_component, participatory_space: voting) } + let(:election) { create(:election, questions:, component:) } + let(:closure) { create(:ps_closure, election:, polling_station:, polling_officer:) } + let!(:questions) { create_list(:question, 3, :complete) } + let(:polling_station) { create(:polling_station, voting: component.participatory_space) } + let(:polling_officer) { create(:polling_officer, voting: component.participatory_space) } + let(:context) { { polling_officer: } } + + let(:params) do + { + polling_station_id:, + election_id:, + ballot_results:, + answer_results:, + question_results: + } + end + + let(:polling_station_id) { polling_station.id } + let(:election_id) { election.id } + let(:answer_results) do + questions.flat_map do |question| + question.answers.to_h do |answer| + [answer.id.to_s, + { + id: answer.id, + question_id: question.id, + value: 2 + }] + end + end.inject(:merge) + end + let(:question_results) do + questions.to_h do |question| + [question.id.to_s, { + id: question.id, + value: 2 + }] + end + end + let(:ballot_results) do + { + valid_ballots_count: 10, + blank_ballots_count: 5, + null_ballots_count: 5, + total_ballots_count: 20 + } + end + + let(:form) { ClosureResultForm.from_params(params).with_context(context) } + + context "when invalid recounts" do + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + + it "return errors" do + subject.call + expect(form.errors.messages[:base].join).to include("Expected total of blank votes is 5 but the sum of the blank questions is 6") + expect(form.errors.messages[:base].join).to include("Expected total of valid votes is 10 but the sum of the valid questions is 18.") + end + end + + context "when valid recounts" do + let(:ballot_results) do + { + valid_ballots_count: 18, + blank_ballots_count: 6, + null_ballots_count: 5, + total_ballots_count: 29 + } + end + + it "broadcasts ok" do + expect(subject.call).to broadcast(:ok) + end + + it "changes to certificate phase" do + subject.call + expect(closure.certificate_phase?).to be true + end + + context "when no polling station" do + let(:polling_station_id) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/destroy_polling_station_closure_spec.rb b/decidim-elections/spec/commands/decidim/votings/destroy_polling_station_closure_spec.rb new file mode 100644 index 00000000..b471fdcf --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/destroy_polling_station_closure_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe DestroyPollingStationClosure do + subject { described_class.new(closure, user) } + + let(:voting) { create(:voting) } + let(:component) { create(:elections_component, participatory_space: voting) } + let(:election) { create(:election, component:) } + let(:polling_station) { create(:polling_station, voting:) } + let(:polling_officer) { create(:polling_officer, voting:) } + let(:user) { polling_officer } + + let!(:closure) { create(:ps_closure, :with_results, election:, polling_station:, polling_officer:) } + + it "broadcasts valid" do + expect(subject.call).to broadcast(:ok) + end + + it "removes the closure and existing results" do + expect(Decidim::Votings::PollingStationClosure.count).to eq(1) + expect(Decidim::Elections::Result.count).not_to be_zero + subject.call + expect(Decidim::Votings::PollingStationClosure.count).to be_zero + expect(Decidim::Elections::Result.count).to be_zero + end + + context "when closure is completed" do + let(:closure) { create(:ps_closure, :with_results, :completed, election:, polling_station:, polling_officer:) } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + + it "does not remove the closure and existing results" do + subject.call + expect(Decidim::Votings::PollingStationClosure.count).to eq(1) + expect(Decidim::Elections::Result.count).not_to be_zero + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/send_access_code_spec.rb b/decidim-elections/spec/commands/decidim/votings/send_access_code_spec.rb new file mode 100644 index 00000000..e0d26d89 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/send_access_code_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe SendAccessCode do + subject { command } + + let(:command) { described_class.new(datum, medium) } + let(:voting) { create(:voting) } + let(:datum) { create(:datum, :with_access_code, dataset:) } + let(:mobile_phone_number) { datum.mobile_phone_number } + let(:access_code) { datum.access_code } + let(:dataset) { create(:dataset, voting:) } + let(:medium) { "email" } + + context "when datum is nil" do + let(:datum) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + describe "when the datum is given" do + let(:mailer) { double(:mailer) } + + context "when medium is email" do + it "sends an email" do + allow(Decidim::Votings::AccessCodeMailer) + .to receive(:send_access_code) + .with(datum) + .and_return(mailer) + expect(mailer) + .to receive(:deliver_later) + + subject.call + end + end + + context "when medium is sms" do + let(:medium) { "sms" } + + it "does not send an email" do + expect(Decidim::Votings::AccessCodeMailer).not_to receive(:send_access_code) + subject.call + end + + it "sends a SMS" do + allow(command).to receive(:current_organization).and_return(voting.organization) + expect(Decidim::Verifications::Sms::ExampleGateway) + .to(receive(:new).with(mobile_phone_number, access_code, { organization: voting.organization })) + .and_return(double(deliver_code: true)) + subject.call + end + end + + context "when medium missing" do + let(:medium) { "" } + + it "does not send an email or sms and raises an error" do + expect(Decidim::Votings::AccessCodeMailer).not_to receive(:send_access_code) + expect(Decidim::Verifications::Sms::ExampleGateway).not_to(receive(:new).with(mobile_phone_number, access_code)) + + expect { subject.call }.to raise_error(ArgumentError) + end + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/sign_polling_station_closure_spec.rb b/decidim-elections/spec/commands/decidim/votings/sign_polling_station_closure_spec.rb new file mode 100644 index 00000000..32f2acfb --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/sign_polling_station_closure_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe SignPollingStationClosure do + subject { described_class.new(form, closure) } + + let(:closure) { create(:ps_closure) } + let(:context) { { closure: } } + + let(:params) do + { + signed: + } + end + let(:signed) { true } + + let(:form) { ClosureSignForm.from_params(params).with_context(context) } + + context "when the form is not valid" do + let(:signed) { nil } + + it "broadcasts invalid" do + expect(subject.call).to broadcast(:invalid) + end + end + + context "when the closure is signed" do + let(:signed) { true } + + it "saves a timestamp" do + subject.call + + expect(closure.signed_at).to be_present + expect(closure.signed_at).to be_a(Date) + expect(closure.signed?).to be true + end + + it "changes to complete phase" do + subject.call + + expect(closure.complete_phase?).to be true + end + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/voter/in_person_vote_spec.rb b/decidim-elections/spec/commands/decidim/votings/voter/in_person_vote_spec.rb new file mode 100644 index 00000000..dd507f85 --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/voter/in_person_vote_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Voter::InPersonVote do + subject { described_class.new(form) } + + let(:form) do + double( + invalid?: invalid, + election:, + election_id:, + voter_id:, + polling_station:, + polling_station_slug:, + polling_officer:, + bulletin_board: + ) + end + let(:invalid) { false } + let(:election) { create(:election, :complete, :bb_test, :vote, component:) } + let(:election_id) { election.id } + let(:voter_id) { "voter.1" } + let(:organization) { create(:organization) } + let(:component) { create(:elections_component, organization:) } + let(:user) { create(:user, :confirmed, organization:) } + let(:voting) { create(:voting, :published, organization:) } + let(:polling_station) { create(:polling_station, id: 1, voting:) } + let(:polling_station_slug) { polling_station.slug } + let(:polling_officer) { create(:polling_officer, voting:, user:, presided_polling_station: polling_station) } + let(:datum) { create(:datum, dataset:, full_name: "Jon Doe", document_type: "passport", document_number: "12345678X", birthdate: Date.civil(1980, 5, 11)) } + let(:dataset) { create(:dataset, voting:) } + let(:in_person_vote_method) { :in_person_vote } + + let(:response) { OpenStruct.new(id: 1, status: "enqueued") } + let(:message_id) { "#{election.id}.vote.in_person+v.#{voter_id}" } + + let(:bulletin_board) do + double(Decidim::Elections.bulletin_board) + end + + before do + allow(bulletin_board).to receive(in_person_vote_method).and_yield(message_id).and_return(response) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "stores the vote" do + expect { subject.call }.to change(Decidim::Votings::InPersonVote, :count).by(1) + + last_vote = Decidim::Votings::InPersonVote.last + expect(last_vote.election).to eq(election) + expect(last_vote.voter_id).to eq("voter.1") + expect(last_vote.polling_station).to eq(polling_station) + expect(last_vote.polling_officer).to eq(polling_officer) + expect(last_vote.status).to eq("pending") + end + + it "calls the bulletin board cast_vote method with the correct params" do + subject.call + expect(bulletin_board).to have_received(in_person_vote_method).with(election_id, voter_id, polling_station_slug) + end + + context "when the form is not valid" do + let(:invalid) { true } + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid) + end + end + + context "when the bulletin board returns an error message" do + before do + allow(bulletin_board).to receive(in_person_vote_method).and_raise(StandardError.new("An error!")) + end + + it "is not valid" do + expect { subject.call }.to broadcast(:invalid, "An error!") + end + end +end diff --git a/decidim-elections/spec/commands/decidim/votings/voter/update_in_person_vote_status_spec.rb b/decidim-elections/spec/commands/decidim/votings/voter/update_in_person_vote_status_spec.rb new file mode 100644 index 00000000..bbf7a4ce --- /dev/null +++ b/decidim-elections/spec/commands/decidim/votings/voter/update_in_person_vote_status_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Voter::UpdateInPersonVoteStatus do + subject { described_class.new(in_person_vote) } + + let(:in_person_vote) { create(:in_person_vote) } + let(:method_name) { :get_pending_message_status } + let(:response) { :accepted } + + before do + allow(Decidim::Elections.bulletin_board).to receive(method_name).and_return(response) + end + + it "broadcasts ok" do + expect { subject.call }.to broadcast(:ok) + end + + it "updates the vote status" do + subject.call + expect(in_person_vote.status).to eq "accepted" + end + + context "when the Bulletin Board has rejected the vote" do + let(:response) { :rejected } + + it "updates the vote status" do + subject.call + expect(in_person_vote.status).to eq "rejected" + end + end +end diff --git a/decidim-elections/spec/controllers/decidim/elections/admin/answers_controller_spec.rb b/decidim-elections/spec/controllers/decidim/elections/admin/answers_controller_spec.rb new file mode 100644 index 00000000..2911b990 --- /dev/null +++ b/decidim-elections/spec/controllers/decidim/elections/admin/answers_controller_spec.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + module Admin + describe AnswersController do + routes { Decidim::Elections::AdminEngine.routes } + + let(:user) { create(:user, :confirmed, :admin, organization: component.organization) } + + before do + request.env["decidim.current_organization"] = component.organization + request.env["decidim.current_participatory_space"] = component.participatory_space + request.env["decidim.current_component"] = component + sign_in user + end + + describe "PATCH update" do + let(:datetime_format) { I18n.t("time.formats.decidim_short") } + let(:component) { create(:elections_component) } + let(:election) { create(:election, component:) } + let(:question) { create(:question, election:) } + let(:answer) { create(:election_answer, question:) } + let(:answer_title) { answer.title } + let(:answer_params) do + { + title: answer_title, + description: answer.description, + weight: answer.weight, + attachment: { + title: "", + file: nil + }, + photos: election.photos.map { |a| a.id.to_s } + } + end + + let(:params) do + { + id: answer.id, + election_id: election.id, + question_id: question.id, + answer: answer_params + } + end + + it "updates the election" do + allow(controller).to receive(:elections_path).and_return("/elections") + allow(controller).to receive(:election_questions_path).and_return("/elections/#{election.id}/questions") + allow(controller).to receive(:election_question_answers_path).and_return("/answers") + + patch(:update, params:) + + expect(flash[:notice]).not_to be_empty + expect(response).to have_http_status(:found) + end + + context "when the existing election has photos and there are other errors on the form" do + include_context "with controller rendering the view" do + let(:answer_title) { { en: "" } } + let(:answer) { create(:election_answer, :with_photos, question:) } + + controller(AnswersController) do + helper_method :proposals_picker_election_question_answers_path, :elections_path, :election_questions_path + def proposals_picker_election_question_answers_path(_foo, _bar) + "/" + end + + def elections_path + "/elections" + end + + def election_questions_path(election) + "/elections/#{election.id}/questions" + end + end + + it "displays the editing form with errors" do + patch(:update, params:) + + expect(flash[:alert]).not_to be_empty + expect(response).to have_http_status(:ok) + expect(subject).to render_template(:edit) + expect(response.body).to include("There was a problem updating this answer") + end + end + end + end + end + end + end +end diff --git a/decidim-elections/spec/controllers/decidim/elections/admin/components_controller_spec.rb b/decidim-elections/spec/controllers/decidim/elections/admin/components_controller_spec.rb new file mode 100644 index 00000000..5b370705 --- /dev/null +++ b/decidim-elections/spec/controllers/decidim/elections/admin/components_controller_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe ComponentsController do + routes { Decidim::Votings::AdminEngine.routes } + + let(:organization) { create(:organization) } + let(:current_user) { create(:user, :confirmed, :admin, organization:) } + let!(:voting) do + create( + :voting, + :published, + organization: + ) + end + let(:component) do + create( + :component, + manifest_name: :dummy, + participatory_space: voting + ) + end + + before do + request.env["decidim.current_organization"] = organization + request.env["decidim.current_voting"] = voting + sign_in current_user + end + + describe "PATCH update" do + let(:component_params) do + { + name_en: "Dummy component", + settings: { + comments_enabled: true, + dummy_global_translatable_text_en: "Dummy text" + }, + default_step_settings: { + comments_blocked: true, + dummy_step_translatable_text_en: "Dummy text" + } + } + end + + it "publishes the default step settings change" do + expect(Decidim::SettingsChange).to receive(:publish).with( + component, + hash_including("comments_blocked" => false), + hash_including("comments_blocked" => true) + ) + + patch :update, params: { voting_slug: voting.slug, id: component.id, component: component_params } + + expect(response).to redirect_to components_path + end + end + end + end + end +end diff --git a/decidim-elections/spec/controllers/decidim/elections/admin/elections_controller_spec.rb b/decidim-elections/spec/controllers/decidim/elections/admin/elections_controller_spec.rb new file mode 100644 index 00000000..8c01cf18 --- /dev/null +++ b/decidim-elections/spec/controllers/decidim/elections/admin/elections_controller_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::ElectionsController do + routes { Decidim::Elections::AdminEngine.routes } + + let(:user) { create(:user, :confirmed, :admin, organization: component.organization) } + + before do + request.env["decidim.current_organization"] = component.organization + request.env["decidim.current_participatory_space"] = component.participatory_space + request.env["decidim.current_component"] = component + sign_in user + end + + describe "PATCH update" do + let(:datetime_format) { I18n.t("time.formats.decidim_short") } + let(:component) { create(:elections_component) } + let(:election) { create(:election, component:) } + let(:election_title) { election.title } + let(:election_params) do + { + title: election_title, + description: election.description, + start_time: election.start_time.strftime(datetime_format), + end_time: election.end_time.strftime(datetime_format), + attachment: { + title: "", + file: nil + }, + photos: election.photos.map { |a| a.id.to_s } + } + end + let(:params) do + { + id: election.id, + election: election_params + } + end + + it "updates the election" do + allow(controller).to receive(:elections_path).and_return("/elections") + + patch(:update, params:) + + expect(flash[:notice]).not_to be_empty + expect(response).to have_http_status(:found) + end + + context "when the existing election has photos and there are other errors on the form" do + include_context "with controller rendering the view" do + let(:election_title) { { en: "" } } + let(:election) { create(:election, :with_photos, component:) } + + it "displays the editing form with errors" do + patch(:update, params:) + + expect(flash[:alert]).not_to be_empty + expect(response).to have_http_status(:ok) + expect(subject).to render_template(:edit) + expect(response.body).to include("There was a problem updating this election") + end + end + end + end +end diff --git a/decidim-elections/spec/controllers/decidim/votings/exports_controller_spec.rb b/decidim-elections/spec/controllers/decidim/votings/exports_controller_spec.rb new file mode 100644 index 00000000..92f1c965 --- /dev/null +++ b/decidim-elections/spec/controllers/decidim/votings/exports_controller_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe ExportsController do + routes { Decidim::Votings::AdminEngine.routes } + + let!(:organization) { create(:organization) } + let!(:voting) { create(:voting, organization:) } + let!(:user) { create(:user, :admin, :confirmed, organization:) } + let!(:component) { create(:component, participatory_space: voting, manifest_name: "dummy") } + + let(:params) do + { + id: "dummies", + component_id: component.id, + voting_slug: voting.slug + } + end + + before do + request.env["decidim.current_organization"] = organization + sign_in user, scope: :user + end + + describe "POST create" do + context "when a format is provided" do + it "enqueues a job with the provided format" do + params[:format] = "csv" + + expect(ExportJob).to receive(:perform_later) + .with(user, component, "dummies", "csv", nil, { id_in: [] }) + + post(:create, params:) + end + end + + context "when a format is not provided" do + it "enqueues a job with the default format" do + expect(ExportJob).to receive(:perform_later) + .with(user, component, "dummies", "json", nil, { id_in: [] }) + + post(:create, params:) + end + end + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with("export_component", component, user, { name: "dummies", format: "json" }) + .and_call_original + + expect { post(:create, params:) }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.action).to eq("export_component") + expect(action_log.version).to be_present + end + end + end + end +end diff --git a/decidim-elections/spec/controllers/decidim/votings/votings_controller_spec.rb b/decidim-elections/spec/controllers/decidim/votings/votings_controller_spec.rb new file mode 100644 index 00000000..aad80175 --- /dev/null +++ b/decidim-elections/spec/controllers/decidim/votings/votings_controller_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::VotingsController do + routes { Decidim::Votings::Engine.routes } + + let(:organization) { create(:organization) } + let(:voting) { create(:voting, organization:) } + + before do + request.env["decidim.current_organization"] = organization + end + + describe "GET show" do + context "when there is a voting" do + it "can access it" do + get :show, params: { slug: voting.slug } + expect(subject).to render_template(:show) + expect(flash[:alert]).to be_blank + expect(controller.send(:current_participatory_space)).to eq voting + end + end + + context "when there is not a voting" do + it "returns 404" do + expect { get :show, params: { slug: "invalid-voting" } } + .to raise_error(ActionController::RoutingError) + end + end + end + + describe "GET elections_log" do + context "when there is a voting" do + it "can access it" do + get :elections_log, params: { voting_slug: voting.slug } + expect(subject).to render_template(:elections_log) + expect(flash[:alert]).to be_blank + expect(controller.send(:current_participatory_space)).to eq voting + end + end + + context "when there is not a voting" do + it "returns 404" do + expect { get :elections_log, params: { voting_slug: "invalid-voting" } } + .to raise_error(ActionController::RoutingError) + end + end + end +end diff --git a/decidim-elections/spec/events/decidim/elections/election_published_event_spec.rb b/decidim-elections/spec/events/decidim/elections/election_published_event_spec.rb new file mode 100644 index 00000000..723126ab --- /dev/null +++ b/decidim-elections/spec/events/decidim/elections/election_published_event_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::ElectionPublishedEvent do + include_context "when a simple event" + + let(:event_name) { "decidim.events.elections.election_published" } + let(:resource) { create(:election) } + let(:resource_title) { resource.title["en"] } + let(:email_subject) { "The #{resource_title} election is now active for #{decidim_sanitize_translated(participatory_space.title)}." } + let(:email_intro) { "The #{resource_title} election is now active for #{participatory_space_title}. You can see it from this page:" } + let(:email_outro) { "You have received this notification because you are following #{participatory_space_title}. You can stop receiving notifications following the previous link." } + let(:notification_title) { "The #{resource_title} election is now active for #{participatory_space_title}." } + + it_behaves_like "a simple event" + it_behaves_like "a simple event email" + it_behaves_like "a simple event notification" +end diff --git a/decidim-elections/spec/events/decidim/elections/trustees/notify_new_trustee_event_spec.rb b/decidim-elections/spec/events/decidim/elections/trustees/notify_new_trustee_event_spec.rb new file mode 100644 index 00000000..d36030ef --- /dev/null +++ b/decidim-elections/spec/events/decidim/elections/trustees/notify_new_trustee_event_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Trustees::NotifyNewTrusteeEvent do + include_context "when a simple event" + + let(:event_name) { "decidim.events.elections.trustees.new_trustee" } + let(:resource) { create(:participatory_process) } + let(:participatory_space) { resource } + let(:resource_title) { decidim_escape_translated(resource.title) } + + let(:trustee_zone_url) { "http://#{resource.organization.host}/trustee" } + let(:email_subject) { "You are a trustee for #{resource_title}." } + let(:email_intro) { "An admin has added you as trustee for #{participatory_space_title}. You should create your public key in your trustee zone" } + let(:email_outro) { "You have received this notification because you have been added as trustee for #{participatory_space_title}." } + let(:notification_title) { <<-EOTITLE.squish } + You have been added to act as a trustee in #{resource_title} for some elections that will take place in this platform.
    + You will perform tasks as needed. For now, please generate your identification keys. + EOTITLE + + it_behaves_like "a simple event" + it_behaves_like "a simple event email" + it_behaves_like "a simple event notification" +end diff --git a/decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_new_election_event_spec.rb b/decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_new_election_event_spec.rb new file mode 100644 index 00000000..40947eaf --- /dev/null +++ b/decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_new_election_event_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Trustees::NotifyTrusteeNewElectionEvent do + include_context "when a simple event" + + let(:event_name) { "decidim.events.elections.trustees.new_election" } + let(:resource) { create(:election) } + let(:resource_title) { resource.title["en"] } + let(:email_subject) { "You are a trustee for the #{resource_title} election." } + let(:email_intro) { "You got added as a trustee for the #{resource_title} election." } + let(:email_outro) { "You have received this notification because you have been added as trustee for the #{resource_title} election." } + let(:notification_title) { "You have been selected as a Trustee in Election #{resource_title}. Please, do the key ceremony to set up the election." } + + it_behaves_like "a simple event" + it_behaves_like "a simple event email" + it_behaves_like "a simple event notification" +end diff --git a/decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_tally_process_event_spec.rb b/decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_tally_process_event_spec.rb new file mode 100644 index 00000000..1cd900e5 --- /dev/null +++ b/decidim-elections/spec/events/decidim/elections/trustees/notify_trustee_tally_process_event_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Trustees::NotifyTrusteeTallyProcessEvent do + include_context "when a simple event" + + let(:event_name) { "decidim.events.elections.trustees.start_tally" } + let(:resource) { create(:election) } + let(:resource_title) { resource.title["en"] } + + describe "notification_title" do + it "is generated correctly" do + expect(subject.notification_title) + .to include("The voting period for the #{resource_title} election has finished. Now, please, perform the tally of the election to publish the final results") + end + end + + describe "email_subject" do + it "is generated correctly" do + expect(subject.email_subject).to eq("The tally process for the #{resource_title} election has started.") + end + end + + describe "email_intro" do + it "is generated correctly" do + expect(subject.email_intro) + .to eq("The voting period for the #{resource_title} election has finished. Now, please, perform the tally of the election to publish the final results.") + end + end + + describe "email_outro" do + it "is generated correctly" do + expect(subject.email_outro) + .to eq("You have received this notification because you are a trustee for the #{resource_title} election.") + end + end +end diff --git a/decidim-elections/spec/events/decidim/elections/votes/vote_accepted_event_spec.rb b/decidim-elections/spec/events/decidim/elections/votes/vote_accepted_event_spec.rb new file mode 100644 index 00000000..1b323c53 --- /dev/null +++ b/decidim-elections/spec/events/decidim/elections/votes/vote_accepted_event_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Votes::VoteAcceptedEvent do + include_context "when a simple event" + + let(:event_name) { "decidim.events.elections.votes.accepted_votes" } + let(:vote) { create(:vote) } + let(:extra) { { vote:, verify_url: } } + let(:resource) { vote.election } + let(:encrypted_vote_hash) { vote.encrypted_vote_hash } + let(:resource_name) { resource.title["en"] } + let(:verify_url) { Decidim::EngineRouter.main_proxy(resource.component).election_vote_verify_url(resource, vote_id: encrypted_vote_hash) } + let(:email_subject) { "Your vote for #{resource_name} was accepted." } + let(:email_intro) { "Your vote was accepted! Using your voting token: #{encrypted_vote_hash}, you can verify your vote here." } + let(:email_outro) { "You have received this notification because you have voted for the #{resource_name} election." } + let(:notification_title) { "Your vote was accepted. Verify your vote here using your vote token: #{encrypted_vote_hash}" } + + it_behaves_like "a simple event" + it_behaves_like "a simple event email" + it_behaves_like "a simple event notification" +end diff --git a/decidim-elections/spec/events/decidim/votings/polling_officers/polling_station_assigned_event_spec.rb b/decidim-elections/spec/events/decidim/votings/polling_officers/polling_station_assigned_event_spec.rb new file mode 100644 index 00000000..731930df --- /dev/null +++ b/decidim-elections/spec/events/decidim/votings/polling_officers/polling_station_assigned_event_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module PollingOfficers + describe PollingStationAssignedEvent do + include_context "when a simple event" + + let(:event_name) { "decidim.events.votings.polling_officers.polling_station_assigned" } + let(:organization) { create(:organization) } + let(:voting) { create(:voting, organization:) } + let(:polling_station) { create(:polling_station, voting:) } + let(:resource) { voting } + let(:polling_officer) { create(:polling_officer, user:, managed_polling_station: polling_station, voting:) } + let(:extra) { { polling_officer_id: polling_officer.id } } + let(:polling_station_name) { translated(polling_station.title) } + let(:voting_title) { translated(voting.title) } + let(:voting_path) { "/votings/#{voting.slug}?voting_slug=#{voting.slug}" } + let(:voting_url) { "http://#{organization.host}:#{Capybara.server_port}#{voting_path}" } + let(:polling_officer_zone_url) { "http://#{organization.host}/polling_officers" } + let(:email_subject) { "You are Manager of the Polling Station #{polling_station_name}." } + let(:email_intro) { "You have been assigned as Manager of the Polling Station #{polling_station_name} in #{voting_title}. You can manage the Polling Station from the dedicated Polling Officer Zone." } + let(:email_outro) { "You have received this notification because you have been assigned as Manager of #{polling_station_name}." } + let(:notification_title) { "You are Manager of the Polling Station #{polling_station_name} in the voting #{voting_title}." } + + it_behaves_like "a simple event" + it_behaves_like "a simple event email" + it_behaves_like "a simple event notification" + end + end + end +end diff --git a/decidim-elections/spec/factories.rb b/decidim-elections/spec/factories.rb new file mode 100644 index 00000000..4e4558ba --- /dev/null +++ b/decidim-elections/spec/factories.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require "decidim/elections/test/factories" +require "decidim/votings/test/factories" +require "decidim/proposals/test/factories" diff --git a/decidim-elections/spec/forms/decidim/elections/admin/action_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/action_form_spec.rb new file mode 100644 index 00000000..6a6cb2bd --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/action_form_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::ActionForm do + subject(:form) { described_class.from_params(attributes).with_context(context) } + + let(:attributes) { {} } + let(:context) do + { + current_organization: component.organization, + current_component: component, + election: + } + end + let(:election) { create(:election, :created) } + let(:component) { election.component } + + it { is_expected.to be_valid } + + describe "#pending_action" do + subject { form.pending_action } + + it { is_expected.to be_nil } + + context "when there is a pending action" do + let!(:pending_action) { create(:action, election:) } + + it { is_expected.to eq(pending_action) } + end + end + + describe "#main_button?" do + subject { form.main_button? } + + it { is_expected.to be_truthy } + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/answer_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/answer_form_spec.rb new file mode 100644 index 00000000..efa5d206 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/answer_form_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::AnswerForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:context) do + { + current_organization: component.organization, + current_component: component, + election:, + question: + } + end + let(:election) { question.election } + let(:question) { create(:question) } + let(:component) { election.component } + let(:title) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:description) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:weight) { 10 } + let(:attachment_params) { nil } + let(:attributes) do + { + title:, + description:, + weight:, + attachment: attachment_params + } + end + + it { is_expected.to be_valid } + + describe "when title is missing" do + let(:title) { { ca: nil, es: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when description is missing" do + let(:description) { { ca: nil, es: nil } } + + it { is_expected.to be_valid } + end + + context "when the attachment is present" do + let(:attachment_params) do + { + title: "My attachment", + file: Decidim::Dev.test_file("city.jpeg", "image/jpeg") + } + end + + it { is_expected.to be_valid } + + context "when the form has some errors" do + let(:title) { { ca: nil, es: nil } } + + it "adds an error to the `:attachment` field" do + expect(subject).not_to be_valid + expect(subject.errors.full_messages).to contain_exactly("Title en cannot be blank", "Attachment Needs to be reattached") + expect(subject.errors.attribute_names).to contain_exactly(:title_en, :attachment) + end + end + end + + context "with proposals" do + subject { described_class.from_model(answer).with_context(context) } + + let(:proposals_component) { create(:component, manifest_name: :proposals, participatory_space: component.participatory_space) } + let(:proposals) { create_list(:proposal, 2, component: proposals_component) } + let(:answer) { create(:election_answer, question:) } + + describe "#map_model" do + it "sets the proposal_ids correctly" do + answer.link_resources(proposals, "related_proposals") + expect(subject.proposal_ids).to match_array(proposals.map(&:id)) + end + end + + describe "#proposals" do + before { answer.link_resources(proposals, "related_proposals") } + + it "returns the available proposals in a way suitable for the form" do + expect(subject.proposals).to match_array(proposals) + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/answer_import_proposals_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/answer_import_proposals_form_spec.rb new file mode 100644 index 00000000..c52aba1b --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/answer_import_proposals_form_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + module Admin + describe AnswerImportProposalsForm do + subject { form } + + let(:election) { question.election } + let(:question) { create(:question) } + let(:answer) { create(:election_answer) } + let(:component) { election.component } + let(:origin_component) { create(:proposal_component, participatory_space: component.participatory_space) } + let(:weight) { 10 } + let(:import_all_accepted_proposals) { true } + let(:params) do + { + origin_component_id: origin_component.try(:id), + weight:, + import_all_accepted_proposals: + } + end + + let(:form) do + described_class.from_params(params).with_context( + current_component: component, + current_participatory_space: component.participatory_space + ) + end + + context "when everything is OK" do + it { is_expected.to be_valid } + end + + context "when there is no target component" do + let(:origin_component) { nil } + + it { is_expected.to be_invalid } + end + + context "when the import proposals is not accepted" do + let(:import_all_accepted_proposals) { false } + + it { is_expected.to be_valid } + end + + context "when the import proposals is accepted" do + let(:import_all_accepted_proposals) { true } + + it { is_expected.to be_valid } + end + + describe "origin_component" do + let(:origin_component) { create(:proposal_component) } + + it "ignores components from other participatory spaces" do + expect(form.origin_component).to be_nil + end + end + + describe "origin_components" do + before do + create(:component, participatory_space: component.participatory_space) + end + + it "returns available target components" do + expect(form.origin_components).to include(origin_component) + expect(form.origin_components.length).to eq(1) + end + end + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/election_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/election_form_spec.rb new file mode 100644 index 00000000..dbec2ccb --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/election_form_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::ElectionForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:organization) { create(:organization) } + let(:context) do + { + current_organization: organization, + current_component: + } + end + let(:participatory_process) { create(:participatory_process, organization:) } + let(:current_component) { create(:elections_component, participatory_space: participatory_process) } + let(:title) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:description) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:start_time) { 1.day.from_now } + let(:end_time) { 3.days.from_now } + let(:attachment_params) { nil } + let(:attributes) do + { + title:, + description:, + start_time:, + end_time:, + attachment: attachment_params + } + end + + it { is_expected.to be_valid } + + describe "when title is missing" do + let(:title) { { ca: nil, es: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when description is missing" do + let(:description) { { ca: nil, es: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when start_time is missing" do + let(:start_time) { nil } + + it { is_expected.not_to be_valid } + end + + describe "when end_time is missing" do + let(:end_time) { nil } + + it { is_expected.not_to be_valid } + end + + describe "when start_time is after end_time" do + let(:start_time) { end_time + 3.days } + + it { is_expected.not_to be_valid } + end + + describe "when start_time is equal to start_time" do + let(:start_time) { end_time } + + it { is_expected.not_to be_valid } + end + + context "when the attachment is present" do + let(:attachment_params) do + { + title: "My attachment", + file: Decidim::Dev.test_file("city.jpeg", "image/jpeg") + } + end + + it { is_expected.to be_valid } + + context "when the form has some errors" do + let(:title) { { ca: nil, es: nil } } + + it "adds an error to the `:attachment` field" do + expect(subject).not_to be_valid + expect(subject.errors.full_messages).to contain_exactly("Title en cannot be blank", "Attachment Needs to be reattached") + expect(subject.errors.attribute_names).to contain_exactly(:title_en, :attachment) + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/question_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/question_form_spec.rb new file mode 100644 index 00000000..97b292a1 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/question_form_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::QuestionForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:context) do + { + current_organization: component.organization, + current_component: component, + election: + } + end + let(:election) { create(:election) } + let(:component) { election.component } + let(:title) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:max_selections) { 3 } + let(:weight) { 10 } + let(:random_answers_order) { true } + let(:min_selections) { 1 } + let(:attributes) do + { + title:, + max_selections:, + min_selections:, + weight:, + random_answers_order: + } + end + + it { is_expected.to be_valid } + + describe "when title is missing" do + let(:title) { { ca: nil, es: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when max_selections is missing" do + let(:max_selections) { nil } + + it { is_expected.not_to be_valid } + end + + describe "when max_selections is negative" do + let(:max_selections) { -1 } + + it { is_expected.not_to be_valid } + end + + describe "when max_selections is zero" do + let(:max_selections) { 0 } + + it { is_expected.not_to be_valid } + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/report_missing_trustee_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/report_missing_trustee_form_spec.rb new file mode 100644 index 00000000..a9f2c8d1 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/report_missing_trustee_form_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::ReportMissingTrusteeForm do + subject(:form) { described_class.from_params(attributes).with_context(context) } + + let(:context) do + { + current_organization: component.organization, + current_component: component, + election:, + current_step: + } + end + let(:component) { election.component } + let(:current_step) { election.bb_status } + let(:attributes) { { trustee_id: } } + let(:election) { create(:election, :tally_started) } + let(:trustee) { election.trustees.first } + let(:trustee_id) { trustee.id } + + it { is_expected.to be_valid } + + describe "#main_button?" do + subject { form.main_button? } + + it { is_expected.to be_falsey } + end + + describe "#trustee" do + subject { form.trustee } + + it { is_expected.to eq(trustee) } + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/setup_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/setup_form_spec.rb new file mode 100644 index 00000000..fe3a1ed8 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/setup_form_spec.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::SetupForm do + subject(:form) { described_class.from_params(attributes).with_context(context) } + + let(:context) do + { + current_organization: component.organization, + current_component: component, + election:, + current_step: "create_election" + } + end + let(:election) { create(:election, :ready_for_setup, trustee_keys: []) } + let(:component) { election.component } + let(:attributes) do + { + setup: { + trustee_ids: + } + } + end + let!(:trustees) { create_list(:trustee, 5, :with_public_key, election:) } + let(:trustee_ids) { trustees.pluck(:id) } + let(:router) { Decidim::EngineRouter.admin_proxy(component) } + + it { is_expected.to be_valid } + + it "shows messages" do + expect(subject.messages).to match( + hash_including({ + max_selections: { link: router.election_questions_path(election), message: "All the questions have a correct value for maximum of answers." }, + minimum_answers: { link: router.election_questions_path(election), message: "Each question has at least 2 answers." }, + minimum_questions: { link: router.election_questions_path(election), message: "The election has at least 1 question." }, + published: { link: router.publish_election_path(election), message: "The election is published." }, + time_before: { link: router.edit_election_path(election), message: "The setup is being done at least 1 hour before the election starts." }, + trustees_number: { link: router.trustees_path, message: "The participatory space has at least 3 trustees with public key." } + }) + ) + end + + it "does not validate census presence" do + expect(subject).not_to be_needs_census + expect(subject.census_validations).to be_blank + end + + context "when the election is not ready for the setup" do + let(:election) { create(:election) } + + it { is_expected.to be_invalid } + + it "shows errors" do + subject.valid? + expect(subject.errors.messages).to eq({ + minimum_questions: ["The election must have at least one question."], + minimum_answers: ["Questions must have at least two answers."], + max_selections: ["The questions do not have a correct value for amount of answers"], + published: ["The election is not published."] + }) + end + end + + context "when there are no answers created" do + let(:election) { create(:election, :published) } + let!(:question) { create(:question, election:, weight: 1) } + + it { is_expected.to be_invalid } + + it "shows errors" do + subject.valid? + expect(subject.errors.messages).to eq({ + minimum_answers: ["Questions must have at least two answers."], + max_selections: ["The questions do not have a correct value for amount of answers"] + }) + end + end + + context "when there are no trustees for the election" do + let(:trustees) { [] } + + it { is_expected.to be_invalid } + + it "shows errors" do + subject.valid? + expect(subject.errors.messages).to eq({ + trustees_number: ["The participatory space must have at least 3 trustees with public key."] + }) + end + end + + context "when the trustee_ids are not initialized" do + let(:attributes) { {} } + + it { is_expected.to be_valid } + + it "choose random trustees" do + expect(subject.trustees).to be_any + end + end + + describe ".participatory_space_trustees" do + subject { form.participatory_space_trustees.pluck(:id) } + + let!(:other_trustees) { create_list(:trustee, 3, :with_public_key) } + + it { is_expected.to match_array(trustees.pluck(:id)) } + end + + context "when census is required" do + let(:election) { create(:election, :ready_for_setup, trustee_keys: [], component:) } + + let(:voting) { create(:voting) } + let(:component) { create(:elections_component, participatory_space: voting) } + + it { is_expected.not_to be_valid } + + it "validates census presence" do + expect(subject).to be_needs_census + expect(subject.census_validations).not_to be_blank + end + + context "and census is valid" do + let!(:dataset) { create(:dataset, :with_data, :frozen, voting:) } + + it { is_expected.to be_valid } + + it "shows messages" do + expect(subject.census_messages).to match( + hash_including({ + census_uploaded: "Census is uploaded.", + census_codes_generated: "Access codes for the census are generated.", + census_frozen: "Access codes for the census are exported and census is frozen." + }) + ) + end + end + + context "and census is empty" do + let!(:dataset) { create(:dataset, voting:) } + + it { is_expected.to be_invalid } + + it "shows errors" do + subject.valid? + expect(subject.errors.messages).to eq({ + census_uploaded: ["There is no census uploaded for this election."], + census_codes_generated: ["Access codes for the census are not generated."], + census_frozen: ["Access codes for the census are not exported."] + }) + end + end + + context "and census has no codes generated" do + let!(:dataset) { create(:dataset, :with_data, voting:) } + + it { is_expected.to be_invalid } + + it "shows errors" do + subject.valid? + expect(subject.errors.messages).to eq({ + census_codes_generated: ["Access codes for the census are not generated."], + census_frozen: ["Access codes for the census are not exported."] + }) + end + end + + context "and census is not frozen" do + let!(:dataset) { create(:dataset, :codes_generated, voting:) } + + it { is_expected.to be_invalid } + + it "shows errors" do + subject.valid? + expect(subject.errors.messages).to eq({ + census_frozen: ["Access codes for the census are not exported."] + }) + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/admin/vote_period_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/admin/vote_period_form_spec.rb new file mode 100644 index 00000000..c82a4dce --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/admin/vote_period_form_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::VotePeriodForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:context) do + { + current_organization: component.organization, + current_component: component, + election:, + current_step: + } + end + let(:component) { election.component } + let(:current_step) { election.bb_status } + let(:attributes) { {} } + + describe "for an election ready to start" do + let(:election) { create(:election, :key_ceremony_ended, start_time:) } + let(:start_time) { 1.hour.from_now } + let(:formatted_start_time) { I18n.l(start_time, format: :long) } + + it { is_expected.to be_valid } + + it "shows a message" do + expect(subject.messages).to eq({ + time_before: "The election will start soon. You can start the voting period manually, or it will be started automatically before the starting time, at #{formatted_start_time}." + }) + end + + context "when the election is not going to start soon" do + let(:start_time) { 10.days.from_now } + + it { is_expected.to be_invalid } + + it "shows an error message" do + subject.valid? + expect(subject.errors.messages).to eq({ + time_before: ["The election is ready to start. You have to wait until 6 hours before the starting time (#{formatted_start_time}) to start the voting period."] + }) + end + end + end + + describe "for an election recently finished" do + let(:election) { create(:election, :vote, end_time:) } + let(:end_time) { 1.minute.ago } + let(:formatted_end_time) { I18n.l(end_time, format: :long) } + + it { is_expected.to be_valid } + + it "shows a message" do + expect(subject.messages).to eq({ + time_after: "The election has ended. You can end the voting period manually, or it will be ended automatically in a few minutes." + }) + end + + context "when the election did not finish yet" do + let(:end_time) { 1.day.from_now } + + it { is_expected.to be_invalid } + + it "shows an error message" do + subject.valid? + expect(subject.errors.messages).to eq({ + time_after: ["The election is still ongoing. You have to wait until the ending time (#{formatted_end_time}) to end the voting period."] + }) + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/trustee_zone/trustee_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/trustee_zone/trustee_form_spec.rb new file mode 100644 index 00000000..84eaa91f --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/trustee_zone/trustee_form_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::TrusteeZone::TrusteeForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:trustee) { create(:trustee, public_key:) } + let(:public_key) { nil } + let(:trustee_name) { "Shelton Runolfsson Sr." } + let(:new_public_key) { "1234567890abcde" } + let(:current_organization) { trustee.organization } + let(:attributes) do + { + public_key: new_public_key, + name: trustee_name + } + end + let(:context) do + { + trustee:, + current_organization: + } + end + + it { is_expected.to be_valid } + + context "when the new public_key is missing" do + let(:new_public_key) { "" } + + it { is_expected.not_to be_valid } + end + + context "when namey is missing" do + let(:trustee_name) { "" } + + it { is_expected.not_to be_valid } + end + + context "when the trustee already has a public key" do + let(:public_key) { "1234567890abcde" } + + it { is_expected.not_to be_valid } + end + + context "when the trustee already has a name" do + let(:trustee) { create(:trustee, public_key:, name: "Sheldon") } + + it { is_expected.not_to be_valid } + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/voter/verify_vote_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/voter/verify_vote_form_spec.rb new file mode 100644 index 00000000..e8bc9d51 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/voter/verify_vote_form_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Voter::VerifyVoteForm do + subject { described_class.from_params(params).with_context(context) } + + let(:params) do + { + vote_identifier: "f149b928f7a00eae7e634fc5db0c3cc5531eefb81f49febce8da5bb4a153548b" + } + end + let(:context) do + { + election: + } + end + let(:election) { create(:election) } + + context "when everything is fine" do + it { is_expected.to be_valid } + end + + context "when the vote identifier is not present" do + let(:params) do + { + vote_identifier: "" + } + end + + it { is_expected.to be_invalid } + end + + context "when the election is not present" do + let(:context) { {} } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/elections/voter/vote_form_spec.rb b/decidim-elections/spec/forms/decidim/elections/voter/vote_form_spec.rb new file mode 100644 index 00000000..d07d6fa1 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/elections/voter/vote_form_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Voter::VoteForm do + subject { described_class.from_params(params).with_context(context) } + + let(:params) do + { + encrypted_data: "{ \"question_1\": \"aNsWeR 1\" }", + encrypted_data_hash: "f149b928f7a00eae7e634fc5db0c3cc5531eefb81f49febce8da5bb4a153548b", + voter_id: "a voter id", + voter_token: "a voter token" + } + end + let(:context) do + { + user:, + email:, + election: + } + end + let(:user) { create(:user) } + let(:email) { "an_email@example.org" } + let(:election) { create(:election) } + + context "when everything is fine" do + it { is_expected.to be_valid } + end + + context "when the encrypted vote is not present" do + let(:params) do + { + encrypted_vote_hash: "f149b928f7a00eae7e634fc5db0c3cc5531eefb81f49febce8da5bb4a153548b" + } + end + + it { is_expected.to be_invalid } + end + + context "when the encrypted vote hash is not present" do + let(:params) do + { + encrypted_vote: "{ \"question_1\": \"aNsWeR 1\" }" + } + end + + it { is_expected.to be_invalid } + end + + context "when the encrypted vote hash does not match" do + let(:params) do + { + encrypted_vote: "{ \"question_1\": \"aNsWeR 1\" }", + encrypted_vote_hash: "1234" + } + end + + it { is_expected.to be_invalid } + end + + context "when the election is not present" do + let(:election) { nil } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/admin/attachment_collection_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/admin/attachment_collection_form_spec.rb new file mode 100644 index 00000000..122ef5fb --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/admin/attachment_collection_form_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/admin/test/forms/attachment_collection_form_examples" + +module Decidim + module Admin + describe AttachmentCollectionForm do + include_examples "attachment collection form" do + let(:collection_for) do + create(:voting, organization:) + end + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/admin/attachment_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/admin/attachment_form_spec.rb new file mode 100644 index 00000000..430a9649 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/admin/attachment_form_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/admin/test/forms/attachment_form_examples" + +module Decidim + module Admin + describe Decidim::Admin::AttachmentForm do + include_examples "attachment form" do + let(:attached_to) do + create(:voting, organization:) + end + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/admin/ballot_style_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/admin/ballot_style_form_spec.rb new file mode 100644 index 00000000..777edb24 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/admin/ballot_style_form_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe BallotStyleForm do + subject(:form) { described_class.from_params(attributes).with_context(context) } + + let(:voting) { create(:voting) } + let(:context) { { voting: } } + let(:code) { ::Faker::Lorem.word } + let(:question_ids) { [1, 2, 4, 5] } + + let(:attributes) do + { + code:, + question_ids: + } + end + + it { is_expected.to be_valid } + + describe "when the code is missing" do + let(:code) { nil } + + it { is_expected.not_to be_valid } + end + + describe "when the code is lowercase" do + it { expect(subject.code).to eq(code.upcase) } + end + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/admin/polling_station_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/admin/polling_station_form_spec.rb new file mode 100644 index 00000000..c6b9d172 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/admin/polling_station_form_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe PollingStationForm do + subject(:form) { described_class.from_params(attributes).with_context(context) } + + let(:organization) { create(:organization, available_locales: [:en]) } + let(:voting) { create(:voting) } + let(:context) do + { + current_organization: organization, + voting: + } + end + let(:title) do + Decidim::Faker::Localized.sentence(word_count: 3) + end + let(:location) do + Decidim::Faker::Localized.sentence(word_count: 3) + end + let(:location_hints) do + Decidim::Faker::Localized.sentence(word_count: 3) + end + let(:address) { "Somewhere over the rainbow" } + let(:latitude) { 40.1234 } + let(:longitude) { 2.1234 } + + let(:attributes) do + { + title_en: title[:en], + location_en: location[:en], + location_hints_en: location_hints[:en], + address: + } + end + + before do + stub_geocoding(address, [latitude, longitude]) + end + + it { is_expected.to be_valid } + + describe "when title is missing" do + let(:title) { { en: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when location is missing" do + let(:location) { { en: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when location_hints is missing" do + let(:location_hints) { { en: nil } } + + it { is_expected.not_to be_valid } + end + + describe "when address is missing" do + let(:address) { nil } + + it { is_expected.not_to be_valid } + end + + it "validates address and store its coordinates" do + expect(subject).to be_valid + expect(subject.latitude).to eq(latitude) + expect(subject.longitude).to eq(longitude) + end + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/admin/voting_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/admin/voting_form_spec.rb new file mode 100644 index 00000000..85edaba4 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/admin/voting_form_spec.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Admin::VotingForm do + subject { described_class.from_params(attributes).with_context(current_organization: organization) } + + let(:organization) { create(:organization, available_locales: [:en, :ca, :es], default_locale: :en) } + let(:scope) { create(:scope, organization:) } + + let(:title) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:description) { Decidim::Faker::Localized.sentence(word_count: 3) } + let(:slug) { "voting-slug" } + let(:start_time) { 1.day.from_now } + let(:end_time) { start_time + 1.month } + let(:promoted) { true } + let(:banner_image) { upload_test_file(Decidim::Dev.test_file("city2.jpeg", "image/jpeg")) } + let(:voting_type) { "online" } + let(:census_contact_information) { nil } + + let(:attributes) do + { + voting: { + title:, + description:, + slug:, + start_time:, + end_time:, + scope_id: scope&.id, + banner_image:, + promoted:, + voting_type:, + census_contact_information: + } + } + end + + it { is_expected.to be_valid } + + describe "when default language in title is missing" do + let(:title) { { ca: "Títol" } } + + it { is_expected.to be_invalid } + end + + describe "when default language in description is missing" do + let(:description) { { ca: "Descripció" } } + + it { is_expected.to be_invalid } + end + + describe "when slug is missing" do + let(:slug) { nil } + + it { is_expected.to be_invalid } + end + + describe "when slug is not valid" do + let(:slug) { "2021" } + + it { is_expected.to be_invalid } + end + + context "when slug is not unique" do + describe "when in the same organization" do + before do + create(:voting, slug:, organization:) + end + + it "is not valid" do + expect(subject).to be_invalid + expect(subject.errors[:slug]).not_to be_empty + end + end + + describe "when in another organization" do + before do + create(:voting, slug:) + end + + it "is valid" do + expect(subject).to be_valid + end + end + end + + describe "when start_time is missing" do + let(:start_time) { nil } + let(:end_time) { 1.month.from_now } + + it { is_expected.to be_invalid } + end + + describe "when end_time is missing" do + let(:end_time) { nil } + + it { is_expected.to be_invalid } + end + + describe "when start_time is after end_time" do + let(:start_time) { end_time + 3.days } + let(:end_time) { 1.month.from_now } + + it { is_expected.to be_invalid } + end + + describe "when start_time is equal to start_time" do + let(:start_time) { end_time } + let(:end_time) { 1.month.from_now } + + it { is_expected.to be_invalid } + end + + describe "when scope is missing" do + let(:scope) { nil } + + it { is_expected.to be_valid } + end + + context "when banner_image is too big" do + before do + organization.settings.tap do |settings| + settings.upload.maximum_file_size.default = 5 + end + ActiveStorage::Blob.find_signed(banner_image).update(byte_size: 6.megabytes) + end + + it { is_expected.not_to be_valid } + end + + context "when images are not the expected type" do + let(:banner_image) { upload_test_file(Decidim::Dev.test_file("Exampledocument.pdf", "application/pdf")) } + + it { is_expected.not_to be_valid } + end + + describe "when voting_type is missing" do + let(:voting_type) { nil } + + it { is_expected.to be_invalid } + end + + describe "when voting_type is not in the accepted values" do + let(:voting_type) { "invalid option" } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/admin/voting_user_role_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/admin/voting_user_role_form_spec.rb new file mode 100644 index 00000000..c81bd082 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/admin/voting_user_role_form_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe VotingUserRoleForm do + subject(:form) { described_class.from_params(attributes).with_context(context) } + + let(:organization) { create(:organization) } + let(:context) do + { + current_organization: organization + } + end + + let(:name) { ::Faker::Name.name } + let(:email) { ::Faker::Internet.email } + let(:user_id) { nil } + let(:existing_user) { false } + let(:attributes) do + { + name:, + email:, + existing_user:, + user_id: + } + end + + context "when existing user is false" do + describe "when email and name are present" do + it { is_expected.to be_valid } + end + + describe "when email is invalid" do + let(:email) { "invalid#example.org" } + + it { is_expected.not_to be_valid } + end + + describe "when name is invalid" do + let(:name) { "Miao<121" } + + it { is_expected.not_to be_valid } + end + + describe "when name is missing" do + let(:name) { nil } + + it { is_expected.not_to be_valid } + end + + describe "when email is missing" do + let(:email) { nil } + + it { is_expected.not_to be_valid } + end + end + + context "when existing user is true" do + let(:existing_user) { true } + + describe "when name and email are missing but user_id is present" do + let(:name) { nil } + let(:email) { nil } + let(:user_id) { create(:user, organization:).id } + + it { is_expected.to be_valid } + end + + describe "when name, email and user_id are missing" do + let(:name) { nil } + let(:email) { nil } + let(:user_id) { nil } + + it { is_expected.not_to be_valid } + end + end + end + end + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/answer_result_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/answer_result_form_spec.rb new file mode 100644 index 00000000..70722200 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/answer_result_form_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::AnswerResultForm do + subject { described_class.from_params(attributes) } + + let(:answer) { create(:election_answer, question:) } + let(:question) { create(:question) } + let(:answer_id) { answer.id } + let(:question_id) { question.id } + let(:value) { 123 } + + let(:attributes) do + { + id: answer_id, + question_id:, + value: + } + end + + it { is_expected.to be_valid } + + describe "when answer_id is missing" do + let(:answer_id) { nil } + + it { is_expected.to be_invalid } + end + + describe "when question_id is missing" do + let(:question_id) { nil } + + it { is_expected.to be_invalid } + end + + describe "when value is missing" do + let(:value) { nil } + + it { is_expected.to be_invalid } + end + + describe "when value is not a number" do + let(:value) { "abcde" } + + # The value is cast to the correct type so "abcde" becomes 0 which is valid. + it { is_expected.to be_valid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/ballot_result_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/ballot_result_form_spec.rb new file mode 100644 index 00000000..8ac36ba2 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/ballot_result_form_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::BallotResultForm do + subject { described_class.from_params(attributes) } + + let(:total_ballots_count) { valid_ballots_count.to_i + null_ballots_count.to_i + blank_ballots_count.to_i } + let(:valid_ballots_count) { Faker::Number.number(digits: 1) } + let(:blank_ballots_count) { Faker::Number.number(digits: 1) } + let(:null_ballots_count) { Faker::Number.number(digits: 1) } + + let(:attributes) do + { + total_ballots_count:, + valid_ballots_count:, + blank_ballots_count:, + null_ballots_count: + } + end + + it { is_expected.to be_valid } + + context "when valid_ballots_count is missing" do + let(:valid_ballots_count) { nil } + + it { is_expected.to be_invalid } + end + + context "when blank_ballots_count is missing" do + let(:blank_ballots_count) { nil } + + it { is_expected.to be_invalid } + end + + context "when null_ballots_count is missing" do + let(:null_ballots_count) { nil } + + it { is_expected.to be_invalid } + end + + context "when total_ballots_count does not match the breakdown" do + let(:total_ballots_count) { 0 } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/census/admin/dataset_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/census/admin/dataset_form_spec.rb new file mode 100644 index 00000000..7eb5d923 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/census/admin/dataset_form_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::Admin::DatasetForm do + subject { described_class.from_params(file:).with_context(current_participatory_space: voting) } + + let(:voting) { create(:voting) } + let(:file) { upload_test_file(Decidim::Dev.test_file("import_voting_census.csv", "text/csv")) } + + it { is_expected.to be_valid } + + describe "when file is missing" do + let(:file) { nil } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/census/admin/datum_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/census/admin/datum_form_spec.rb new file mode 100644 index 00000000..ef3d57e3 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/census/admin/datum_form_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::Admin::DatumForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:dataset) { create(:dataset) } + let(:user) { create(:user, :admin, organization: dataset.voting.organization) } + let(:document_number) { "123456789Y" } + let(:document_type) { "passport" } + let(:birthdate) { "20010414" } + let(:full_name) { "Jane Doe" } + let(:full_address) { "Nowhere street 1" } + let(:postal_code) { "12345" } + let(:mobile_phone_number) { "123456789" } + let(:email) { "example@test.org" } + let(:ballot_style_code) { "BS1" } + + let(:attributes) do + { + document_number:, + document_type:, + birthdate:, + full_name:, + full_address:, + postal_code:, + mobile_phone_number:, + ballot_style_code:, + email: + } + end + + let(:context) do + { + current_user: user, + dataset: + } + end + + it { is_expected.to be_valid } + + describe "when document_number is missing" do + let(:document_number) { nil } + + it { is_expected.to be_invalid } + end + + describe "when document_type is missing" do + let(:document_type) { nil } + + it { is_expected.to be_invalid } + end + + describe "when birthdate is missing" do + let(:birthdate) { nil } + + it { is_expected.to be_invalid } + end + + describe "when full_name is missing" do + let(:full_name) { nil } + + it { is_expected.to be_invalid } + end + + describe "when full_address is missing" do + let(:full_address) { nil } + + it { is_expected.to be_invalid } + end + + describe "when postal_code is missing" do + let(:postal_code) { nil } + + it { is_expected.to be_invalid } + end + + describe "when mobile_phone_number is missing" do + let(:mobile_phone_number) { nil } + + it { is_expected.to be_valid } + end + + describe "when email is missing" do + let(:email) { nil } + + it { is_expected.to be_valid } + end + + describe "when document_type is not in the accepted values" do + let(:document_type) { "invalid type" } + + it { is_expected.to be_invalid } + end + + describe "when ballot style code is missing" do + let(:ballot_style_code) { nil } + + it { is_expected.to be_valid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/census/check_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/census/check_form_spec.rb new file mode 100644 index 00000000..2f46fca9 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/census/check_form_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::CheckForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:current_participatory_space) { create(:voting) } + let(:document_number) { "123456789Y" } + let(:document_type) { "passport" } + let(:day) { 14 } + let(:month) { 8 } + let(:year) { 1982 } + let(:postal_code) { "12345" } + + let(:attributes) do + { + document_number:, + document_type:, + day:, + month:, + year:, + postal_code: + } + end + + let(:context) do + { + current_participatory_space: + } + end + + it { is_expected.to be_valid } + + describe "when document_number is missing" do + let(:document_number) { nil } + + it { is_expected.to be_invalid } + end + + describe "when day is missing" do + let(:day) { nil } + + it { is_expected.to be_invalid } + end + + describe "when document_type is missing" do + let(:document_type) { nil } + + it { is_expected.to be_invalid } + end + + describe "when postal_code is missing" do + let(:postal_code) { nil } + + it { is_expected.to be_invalid } + end + + describe "creates the birthday" do + it { expect(subject.birthdate).to eql("19820814") } + end + + describe "generate hash for data" do + it { expect(subject.hashed_check_data).to eql("dc97180b483fbe65d9df598323ded13a72b846b87b5648a6db5f92a14c4532b9") } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/census/in_person_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/census/in_person_form_spec.rb new file mode 100644 index 00000000..e7b320d3 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/census/in_person_form_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::InPersonForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:current_participatory_space) { create(:voting) } + let(:document_number) { "123456789Y" } + let(:document_type) { "passport" } + let(:day) { 14 } + let(:month) { 8 } + let(:year) { 1982 } + + let(:attributes) do + { + document_number:, + document_type:, + day:, + month:, + year: + } + end + + let(:context) do + { + current_participatory_space: + } + end + + it { is_expected.to be_valid } + + describe "when document_number is missing" do + let(:document_number) { nil } + + it { is_expected.to be_invalid } + end + + describe "when document_type is missing" do + let(:document_type) { nil } + + it { is_expected.to be_invalid } + end + + describe "when day is missing" do + let(:day) { nil } + + it { is_expected.to be_invalid } + end + + describe "when day is a string" do + let(:day) { "a" } + + it { is_expected.to be_invalid } + end + + describe "creates the birthday" do + it { expect(subject.birthdate).to eql("19820814") } + end + + describe "generate hash for data" do + it { expect(subject.hashed_in_person_data).to eql("7da41362d26ed32dc57a16cffaf600c17a895dabf3b878fd8d2da1b293001116") } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/census/login_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/census/login_form_spec.rb new file mode 100644 index 00000000..67a32292 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/census/login_form_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::LoginForm do + subject { described_class.from_params(attributes).with_context(context) } + + let(:current_participatory_space) { create(:voting) } + let(:document_number) { "123456789Y" } + let(:document_type) { "passport" } + let(:day) { 14 } + let(:month) { 8 } + let(:year) { 1982 } + let(:postal_code) { "12345" } + let(:access_code) { "123" } + + let(:attributes) do + { + document_number:, + document_type:, + day:, + month:, + year:, + postal_code:, + access_code: + } + end + + let(:context) do + { + current_participatory_space: + } + end + + it { is_expected.to be_valid } + + describe "when document_number is missing" do + let(:document_number) { nil } + + it { is_expected.to be_invalid } + end + + describe "when document_type is missing" do + let(:document_type) { nil } + + it { is_expected.to be_invalid } + end + + describe "when day is missing" do + let(:day) { nil } + + it { is_expected.to be_invalid } + end + + describe "when postal_code is missing" do + let(:postal_code) { nil } + + it { is_expected.to be_invalid } + end + + describe "when access_code is missing" do + let(:access_code) { nil } + + it { is_expected.to be_invalid } + end + + describe "creates the birthday" do + it { expect(subject.birthdate).to eql("19820814") } + end + + describe "generate hash for data" do + it { expect(subject.hashed_online_data).to eql("e058aeb3d82f1f4f20bb7204f9aee80256bbdcb1e255e87b891700b42d33b215") } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/closure_certify_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/closure_certify_form_spec.rb new file mode 100644 index 00000000..1183b27f --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/closure_certify_form_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::ClosureCertifyForm do + subject { described_class.from_params(params).with_context(closure:) } + + let(:closure) { create(:ps_closure, :with_results, phase:) } + let(:phase) { :certificate } + let(:add_photos) { [Decidim::Dev.test_file("city.jpeg", "image/jpeg")] } + let(:params) do + { + add_photos: + } + end + + it { is_expected.to be_valid } + + describe "when attachment is missing" do + let(:add_photos) { nil } + + it { is_expected.to be_invalid } + end + + describe "when closure is not in certificate phase" do + let(:phase) { :results } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/closure_result_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/closure_result_form_spec.rb new file mode 100644 index 00000000..943b7b46 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/closure_result_form_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::ClosureResultForm do + subject { described_class.from_params(attributes).with_context(polling_officer:, closure:) } + + let(:voting) { create(:voting) } + let(:component) { create(:elections_component, participatory_space: voting) } + let(:election) { create(:election, questions:, component:) } + let!(:questions) { create_list(:question, 3, :complete, :nota) } + let(:polling_station) { create(:polling_station, voting: component.participatory_space) } + let(:polling_officer) { create(:polling_officer, voting: component.participatory_space) } + let(:closure) { create(:ps_closure, polling_station:, election:) } + let(:total_ballots_count) { valid_ballots_count.to_i + null_ballots_count.to_i + blank_ballots_count.to_i } + let(:valid_ballots_count) { answer_results.values.pluck(:value).sum(&:to_i) } + let(:blank_ballots_count) { question_results.values.pluck(:value).sum(&:to_i) } + let(:null_ballots_count) { Faker::Number.number(digits: 1) } + + let(:ballot_results) do + { + total_ballots_count:, + valid_ballots_count:, + blank_ballots_count:, + null_ballots_count: + } + end + let(:attributes) do + { + election_id: election&.id, + polling_station_id: polling_station&.id, + answer_results:, + question_results:, + ballot_results: + } + end + + let(:answer_results) do + questions.flat_map do |question| + question.answers.map do |answer| + [answer.id.to_s, { + value: Faker::Number.number(digits: 1) + }] + end + end.to_h + end + + let(:question_results) do + questions.to_h do |question| + [question.id.to_s, { + value: Faker::Number.number(digits: 1) + }] + end + end + + it { is_expected.to be_valid } + + context "when polling_station is missing" do + let(:polling_station) { nil } + let(:closure) { nil } + + it { is_expected.to be_invalid } + end + + context "when election is missing" do + let(:election) { nil } + let(:closure) { nil } + + it { is_expected.to be_invalid } + end + + context "when total votes does not match vote breakdown" do + let(:total_ballots_count) { 0 } + + it { is_expected.to be_invalid } + end + + context "when number of blank votes does not match the total blank ballots reported" do + let(:blank_ballots_count) { question_results.values.pluck(:value).sum(&:to_i) + 1 } + + it { is_expected.to be_invalid } + end + + context "when number of answers differs the total valid ballots" do + let(:valid_ballots_count) { question_results.values.pluck(:value).sum(&:to_i) + 1 } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/closure_sign_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/closure_sign_form_spec.rb new file mode 100644 index 00000000..888645cb --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/closure_sign_form_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::ClosureSignForm do + subject { described_class.from_params(attributes) } + + let(:closure) { create(:ps_closure, :with_results) } + let(:signed) { true } + + let(:attributes) do + { + signed: + } + end + + it { is_expected.to be_valid } + + describe "when signed is missing" do + let(:signed) { nil } + + it { is_expected.to be_invalid } + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/envelopes_result_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/envelopes_result_form_spec.rb new file mode 100644 index 00000000..a6f120b5 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/envelopes_result_form_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::EnvelopesResultForm do + subject { described_class.from_params(attributes).with_context(polling_officer:) } + + let(:voting) { create(:voting) } + let(:component) { create(:elections_component, participatory_space: voting) } + let(:election) { create(:election, questions:, component:) } + let!(:questions) { create_list(:question, 3, :complete) } + let(:polling_station) { create(:polling_station, voting: component.participatory_space) } + let(:polling_officer) { create(:polling_officer, voting: component.participatory_space) } + + let(:polling_station_id) { polling_station&.id } + let(:election_id) { election&.id } + let(:total_ballots_count) { Faker::Number.number(digits: 1) } + let(:polling_officer_notes) { nil } + let(:election_votes_count) { total_ballots_count } + + let(:attributes) do + { + polling_station_id:, + election_id:, + total_ballots_count:, + polling_officer_notes:, + election_votes_count: + } + end + + it { is_expected.to be_valid } + + describe "when polling_station_id is missing" do + let(:polling_station_id) { nil } + + it { is_expected.to be_invalid } + end + + describe "when election_id is missing" do + let(:election_id) { nil } + + it { is_expected.to be_invalid } + end + + describe "when total_ballots_count is missing" do + let(:total_ballots_count) { nil } + + it { is_expected.to be_invalid } + end + + describe "when total_ballots_count differ from election_votes_count" do + let(:election_votes_count) { 2 } + let(:total_ballots_count) { 1 } + + context "and polling_officer_notes is missing" do + it { is_expected.to be_invalid } + end + + context "and polling_officer_notes is present" do + let(:polling_officer_notes) { Faker::Lorem.sentence } + + it { is_expected.to be_valid } + end + end +end diff --git a/decidim-elections/spec/forms/decidim/votings/question_result_form_spec.rb b/decidim-elections/spec/forms/decidim/votings/question_result_form_spec.rb new file mode 100644 index 00000000..55dae814 --- /dev/null +++ b/decidim-elections/spec/forms/decidim/votings/question_result_form_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::QuestionResultForm do + subject { described_class.from_params(attributes).with_context(closure:) } + + let(:closure) { create(:ps_closure) } + let(:question) { create(:question, election: closure.election, min_selections:, max_selections:) } + let(:question_id) { question.id } + let(:value) { 123 } + let(:max_selections) { 3 } + let(:min_selections) { 0 } + + let(:attributes) do + { + id: question_id, + value: + } + end + + it { is_expected.to be_valid } + + context "when question_id is missing" do + let(:question_id) { nil } + + it { is_expected.to be_invalid } + end + + context "when value is missing" do + let(:value) { nil } + + it { is_expected.to be_invalid } + end + + context "when value not a number" do + let(:value) { "abcde" } + + # The value is cast to the correct type so "abcde" becomes 0 which is valid. + it { is_expected.to be_valid } + end +end diff --git a/decidim-elections/spec/helpers/decidim/elections/steps_helper_spec.rb b/decidim-elections/spec/helpers/decidim/elections/steps_helper_spec.rb new file mode 100644 index 00000000..0d990295 --- /dev/null +++ b/decidim-elections/spec/helpers/decidim/elections/steps_helper_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + module Admin + describe StepsHelper do + describe "#steps" do + subject { helper.steps(current_step) } + + let(:current_step) { "create_election" } + + it { + expect(subject).to eq([ + ["create_election", "text-warning"], + ["created", "text-muted"], + ["key_ceremony", "text-muted"], + ["key_ceremony_ended", "text-muted"], + ["vote", "text-muted"], + ["vote_ended", "text-muted"], + ["tally_started", "text-muted"], + ["tally_ended", "text-muted"], + ["results_published", "text-muted"] + ]) + } + + context "when current_step is ready to vote" do + let(:current_step) { "key_ceremony_ended" } + + it { + expect(subject).to eq([ + ["create_election", "text-success"], + ["created", "text-success"], + ["key_ceremony", "text-success"], + ["key_ceremony_ended", "text-warning"], + ["vote", "text-muted"], + ["vote_ended", "text-muted"], + ["tally_started", "text-muted"], + ["tally_ended", "text-muted"], + ["results_published", "text-muted"] + ]) + } + end + + context "when current_step is results_published" do + let(:current_step) { "results_published" } + + it { + expect(subject).to eq([ + ["create_election", "text-success"], + ["created", "text-success"], + ["key_ceremony", "text-success"], + ["key_ceremony_ended", "text-success"], + ["vote", "text-success"], + ["vote_ended", "text-success"], + ["tally_started", "text-success"], + ["tally_ended", "text-success"], + ["results_published", "text-warning"] + ]) + } + end + end + + describe "#fix_it_button_with_icon" do + subject { helper.fix_it_button_with_icon(link, icon_name, method) } + + let(:link) { "/path/to/fix" } + let(:icon_name) { "pencil-line" } + let(:method) { :get } + + it "generates the fix it link with icon" do + expect(subject).to have_link("Fix it", href: link) + expect(subject).to have_selector("svg.fix-icon") + end + end + + describe "#technical_configuration_items" do + subject { helper.technical_configuration_items } + + let(:expected_items) do + [ + { key: ".technical_configuration.bulletin_board_server", value: Decidim::BulletinBoard.config[:bulletin_board_server] }, + { key: ".technical_configuration.authority_name", value: Decidim::BulletinBoard.config[:authority_name] }, + { key: ".technical_configuration.scheme_name", value: Decidim::BulletinBoard.config[:scheme_name] } + ] + end + + it "returns the technical configuration items" do + expect(subject).to eq(expected_items) + end + end + end + end + end +end diff --git a/decidim-elections/spec/helpers/decidim/elections/votes_helper_spec.rb b/decidim-elections/spec/helpers/decidim/elections/votes_helper_spec.rb new file mode 100644 index 00000000..35437ff0 --- /dev/null +++ b/decidim-elections/spec/helpers/decidim/elections/votes_helper_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + describe VotesHelper do + let(:question) { create(:question, :complete, answers: 3, random_answers_order:) } + let(:random_answers_order) { true } + + let(:helper) do + Class.new(ActionView::Base) do + include VotesHelper + include TranslatableAttributes + end.new(ActionView::LookupContext.new(ActionController::Base.view_paths), {}, []) + end + + describe "ordered_answers" do + subject(:uniq_results) { repetitions.times.map { helper.ordered_answers(question) }.uniq } + + let(:repetitions) { 100 } + + it "orders answers in different order on different calls" do + # This test could randomly fail with a very low probability (6*(5.0/6)**100, or 1 of 13.802.995 times) + expect(uniq_results.length).to eq 6 + end + + context "when random order is disabled" do + let(:repetitions) { 10 } + let(:random_answers_order) { false } + + it "orders answers with the same order on every calls" do + # This test could randomly result on a false positive with a very low probability ((1.0/6)**9, or 1 of 10.077.696 tiems) + expect(uniq_results.length).to eq(1) + end + + it "orders answers by weight and creation order" do + ordered_ids = question.answers.map { |question| [question.weight, question.id] }.sort.map(&:last) + + expect(uniq_results.first.map(&:id)).to eq(ordered_ids) + end + end + end + + describe "more_information?" do + let(:answer) { question.answers.first } + let(:show_more_information) { helper.more_information?(answer) } + + context "when the answer has a description" do + before do + answer.description = { "en" => "Description" } + answer.save! + end + + it "returns true" do + expect(show_more_information).to be_truthy + end + end + + context "when the answer has no description" do + before do + answer.description = {} + answer.save! + end + + it "returns false" do + expect(show_more_information).to be_falsey + end + end + end + end + end +end diff --git a/decidim-elections/spec/jobs/decidim/votings/census/admin/create_datum_job_spec.rb b/decidim-elections/spec/jobs/decidim/votings/census/admin/create_datum_job_spec.rb new file mode 100644 index 00000000..eff47444 --- /dev/null +++ b/decidim-elections/spec/jobs/decidim/votings/census/admin/create_datum_job_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::Admin::CreateDatumJob do + let!(:dataset) { create(:dataset) } + let(:user) { create(:user, :admin, organization: dataset.voting.organization) } + let!(:csv_row) { ["12345678X", "passport", "20011202", "John Doe", "The full address one", "08001", "123456789", "user@example.org", "BS1"] } + + describe "queue" do + it "is queued to events" do + expect(described_class.queue_name).to eq "default" + end + end + + describe "perform" do + it "delegates the work to a command" do + expect(Decidim::Votings::Census::Admin::CreateDatum).to receive(:call) + + described_class.perform_now(user, dataset, csv_row) + end + + context "when the dataset is missing" do + let!(:dataset) { nil } + let(:user) { create(:user, :admin) } + + it "does not create a datum" do + expect(Decidim::Votings::Census::Admin::CreateDatum).not_to receive(:call) + + described_class.perform_now(user, dataset, csv_row) + end + end + + context "when the user is missing" do + let!(:user) { nil } + + it "does not create a datum" do + expect(Decidim::Votings::Census::Admin::CreateDatum).not_to receive(:call) + + described_class.perform_now(user, dataset, csv_row) + end + end + + context "when the csv_row is missing" do + let!(:csv_row) { nil } + + it "does not create a datum" do + expect(Decidim::Votings::Census::Admin::CreateDatum).not_to receive(:call) + + described_class.perform_now(user, dataset, csv_row) + end + end + end +end diff --git a/decidim-elections/spec/jobs/decidim/votings/census/admin/export_access_codes_job_spec.rb b/decidim-elections/spec/jobs/decidim/votings/census/admin/export_access_codes_job_spec.rb new file mode 100644 index 00000000..afdc5281 --- /dev/null +++ b/decidim-elections/spec/jobs/decidim/votings/census/admin/export_access_codes_job_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Census + module Admin + describe ExportAccessCodesJob do + let(:dataset) { create(:dataset, :codes_generated) } + let!(:user) { create(:user, :admin, organization: dataset.voting.organization) } + + it "sends an email with the result of the export" do + perform_enqueued_jobs do + ExportAccessCodesJob.perform_now(dataset, user) + end + + email = last_email + expect(email.subject).to include("The export of the voting access codes") + expect(email.body.encoded).to match("Click the next link to download the access codes data") + end + + it "delegates the work to the command" do + expect(Decidim::Votings::Census::Admin::UpdateDataset) + .to receive(:call) + .with(dataset, { status: :exporting_codes }, user) + expect(Decidim::Votings::Census::Admin::UpdateDataset) + .to receive(:call) + .with(dataset, { status: :freeze }, user) + + described_class.perform_now(dataset, user) + end + + it "updates the dataset status" do + described_class.perform_now(dataset, user) + + expect(dataset.reload).to be_freeze + end + end + end + end + end +end diff --git a/decidim-elections/spec/jobs/decidim/votings/census/admin/generate_access_codes_job_spec.rb b/decidim-elections/spec/jobs/decidim/votings/census/admin/generate_access_codes_job_spec.rb new file mode 100644 index 00000000..6f7a25ab --- /dev/null +++ b/decidim-elections/spec/jobs/decidim/votings/census/admin/generate_access_codes_job_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::Admin::GenerateAccessCodesJob do + let(:dataset) { create(:dataset, status: :generating_codes) } + let(:user) { create(:user, :admin, organization:) } + let(:organization) { dataset&.organization || create(:organization) } + + describe "queue" do + it "is queued to events" do + expect(described_class.queue_name).to eq "default" + end + end + + describe "perform" do + context "when the input is NOT valid" do + context "when the dataset is missing" do + let(:dataset) { nil } + + it "does not update the dataset nor the data" do + expect(Decidim::Votings::Census::Admin::UpdateDataset).not_to receive(:call) + + described_class.perform_now(dataset, user) + end + end + + context "when the dataset is not in the correct status" do + let(:dataset) { create(:dataset, status: :codes_generated) } + + it "does not update the dataset nor the data" do + expect(Decidim::Votings::Census::Admin::UpdateDataset).not_to receive(:call) + + described_class.perform_now(dataset, user) + end + end + + context "when the user is missing" do + let(:user) { nil } + + it "does not update the dataset nor the data" do + expect(Decidim::Votings::Census::Admin::UpdateDataset).not_to receive(:call) + + described_class.perform_now(dataset, user) + end + end + end + + context "when this input is valid" do + let!(:data) { create_list(:datum, 5, dataset:) } + + it "generates the codes" do + described_class.perform_now(dataset, user) + + data.each do |datum| + datum.reload + expect(datum.access_code.length).to be(8) + expect(datum.hashed_online_data).not_to be_nil + end + end + + it "delegates the work to the command" do + expect(Decidim::Votings::Census::Admin::UpdateDataset) + .to receive(:call) + .with(dataset, { status: :codes_generated }, user) + + described_class.perform_now(dataset, user) + end + + it "updates the dataset status" do + described_class.perform_now(dataset, user) + + expect(dataset.reload).to be_codes_generated + end + end + end +end diff --git a/decidim-elections/spec/lib/components/votings/current_component_spec.rb b/decidim-elections/spec/lib/components/votings/current_component_spec.rb new file mode 100644 index 00000000..f4e93699 --- /dev/null +++ b/decidim-elections/spec/lib/components/votings/current_component_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + describe CurrentComponent do + let(:request) { double(params:, env:) } + let(:params) { {} } + let(:manifest) { Decidim.find_component_manifest("dummy") } + let(:organization) do + create(:organization) + end + let(:current_voting) { create(:voting, organization:) } + let(:other_voting) { create(:voting, organization:) } + let(:env) do + { "decidim.current_organization" => organization } + end + + subject { described_class.new(manifest) } + + context "when the params contain a voting id" do + before do + params["voting_slug"] = current_voting.id.to_s + end + + context "when the params do not contain a component id" do + it "does not match" do + expect(subject.matches?(request)).to be(false) + end + end + + context "when the params contain a component id" do + before do + params["component_id"] = component.id.to_s + end + + context "when the component does not belong to the voting" do + let(:component) { create(:component, participatory_space: other_voting) } + + it "matches" do + expect(subject.matches?(request)).to be(false) + end + end + + context "when the component belongs to the voting" do + let(:component) { create(:component, participatory_space: current_voting) } + + it "matches" do + expect(subject.matches?(request)).to be(true) + end + end + end + end + + context "when the params do not contain an voting id" do + it "does not match" do + expect { subject.matches?(request) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context "when the params contain a non existing voting id" do + before do + params["voting_slug"] = "99999999" + end + + context "when there is no component" do + it "does not match" do + expect { subject.matches?(request) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context "when there is component" do + before do + params["component_id"] = "1" + end + + it "does not match" do + expect { subject.matches?(request) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end + end +end diff --git a/decidim-elections/spec/lib/decidim/elections/admin_engine_spec.rb b/decidim-elections/spec/lib/decidim/elections/admin_engine_spec.rb new file mode 100644 index 00000000..88867f0e --- /dev/null +++ b/decidim-elections/spec/lib/decidim/elections/admin_engine_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::AdminEngine do + it_behaves_like "clean engine" +end diff --git a/decidim-elections/spec/lib/decidim/elections/answer_serializer_spec.rb b/decidim-elections/spec/lib/decidim/elections/answer_serializer_spec.rb new file mode 100644 index 00000000..f763f8ad --- /dev/null +++ b/decidim-elections/spec/lib/decidim/elections/answer_serializer_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + describe AnswerSerializer do + subject do + described_class.new(answer) + end + + let!(:answer) { create(:election_answer, :with_votes) } + let!(:election) { answer.question.election } + + describe "#serialize" do + let(:serialized) { subject.serialize } + + it "serializes the id" do + expect(serialized).to include(id: answer.id) + end + + it "serializes the participatory space" do + expect(serialized[:participatory_space]).to include(id: election.participatory_space.id) + I18n.available_locales.each do |locale| + expect(translated(serialized[:participatory_space][:title], locale:)).to eq(translated(election.participatory_space.title, locale:)) + end + end + + it "serializes the title" do + I18n.available_locales.each do |locale| + expect(translated(serialized[:answer_title], locale:)).to eq(translated(answer.title, locale:)) + end + end + + it "serializes the results total" do + expect(serialized[:answer_votes]).to eq(answer.results_total) + end + + it "serializes the election id" do + expect(serialized[:election_id]).to eq(answer.question.election.id) + end + + it "serializes the election title" do + I18n.available_locales.each do |locale| + expect(translated(serialized[:election_title], locale:)).to eq(translated(answer.question.election.title, locale:)) + end + end + + it "serializes the question id" do + expect(serialized[:question_id]).to eq(answer.question.id) + end + + it "serializes the question title" do + I18n.available_locales.each do |locale| + expect(translated(serialized[:question_title], locale:)).to eq(translated(answer.question.title, locale:)) + end + end + end + end + end +end diff --git a/decidim-elections/spec/lib/decidim/elections/component_spec.rb b/decidim-elections/spec/lib/decidim/elections/component_spec.rb new file mode 100644 index 00000000..02a969cb --- /dev/null +++ b/decidim-elections/spec/lib/decidim/elections/component_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Elections component" do # rubocop:disable RSpec/DescribeClass + subject { component } + + let(:component) { create(:elections_component) } + + describe "before_destroy hooks" do + context "when there are no elections" do + it "does not raise any error" do + expect { subject.manifest.run_hooks(:before_destroy, subject) }.not_to raise_error + end + end + + context "with elections" do + before do + create(:election, component:) + end + + it "raises an error" do + expect { subject.manifest.run_hooks(:before_destroy, subject) }.to raise_error( + StandardError, + "Cannot remove this component" + ) + end + end + end +end diff --git a/decidim-elections/spec/lib/decidim/elections/engine_spec.rb b/decidim-elections/spec/lib/decidim/elections/engine_spec.rb new file mode 100644 index 00000000..3f0ae6de --- /dev/null +++ b/decidim-elections/spec/lib/decidim/elections/engine_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Engine do + it_behaves_like "clean engine" + + describe "decidim_elections.authorization_transfer" do + include_context "authorization transfer" + + let(:election) { create(:election, organization:) } + let(:original_records) do + { votes: create_list(:vote, 3, election:, user: original_user) } + end + let(:transferred_votes) { Decidim::Elections::Vote.where(user: target_user).order(:id) } + + it "handles authorization transfer correctly" do + expect(transferred_votes.count).to eq(3) + expect(transfer.records.count).to eq(3) + expect(transferred_resources).to eq(transferred_votes) + end + end +end diff --git a/decidim-elections/spec/lib/decidim/votings/admin_engine_spec.rb b/decidim-elections/spec/lib/decidim/votings/admin_engine_spec.rb new file mode 100644 index 00000000..27252b25 --- /dev/null +++ b/decidim-elections/spec/lib/decidim/votings/admin_engine_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::AdminEngine do + it_behaves_like "clean engine" +end diff --git a/decidim-elections/spec/lib/decidim/votings/census_engine_spec.rb b/decidim-elections/spec/lib/decidim/votings/census_engine_spec.rb new file mode 100644 index 00000000..76fd125e --- /dev/null +++ b/decidim-elections/spec/lib/decidim/votings/census_engine_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::CensusEngine do + it_behaves_like "clean engine" +end diff --git a/decidim-elections/spec/lib/decidim/votings/engine_spec.rb b/decidim-elections/spec/lib/decidim/votings/engine_spec.rb new file mode 100644 index 00000000..337ae6ec --- /dev/null +++ b/decidim-elections/spec/lib/decidim/votings/engine_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Engine do + it_behaves_like "clean engine" +end diff --git a/decidim-elections/spec/lib/decidim/votings/voting_serializer_spec.rb b/decidim-elections/spec/lib/decidim/votings/voting_serializer_spec.rb new file mode 100644 index 00000000..924b2d6c --- /dev/null +++ b/decidim-elections/spec/lib/decidim/votings/voting_serializer_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + describe VotingSerializer do + subject do + described_class.new(voting) + end + + let!(:voting) { create(:voting) } + let!(:scope) { create(:scope, organization: voting.organization) } + + describe "#serialize" do + let(:serialized) { subject.serialize } + + it "serializes the id" do + expect(serialized).to include(participatory_space_id: voting.id) + end + + it "serializes the url" do + expect(serialized[:url]).to include("http", voting.slug) + end + + it "serializes the title" do + I18n.available_locales.each do |locale| + expect(translated(serialized[:title], locale:)).to eq(translated(voting.title, locale:)) + end + end + + it "serializes the description" do + I18n.available_locales.each do |locale| + expect(translated(serialized[:description], locale:)).to eq(translated(voting.description, locale:)) + end + end + + it "serializes the voting type" do + I18n.available_locales.each do |locale| + expect(translated(serialized[:voting_type], locale:)).to eq(I18n.t(voting.voting_type, scope: "decidim.votings.admin.votings.form.voting_type")) + end + end + + it "serializes the banner image url" do + expect(serialized).to include(banner_image_url: Decidim::Votings::VotingPresenter.new(voting).banner_image_url) + end + + it "serializes the introductory image url" do + expect(serialized).to include(introductory_image_url: Decidim::Votings::VotingPresenter.new(voting).introductory_image_url) + end + + it "serializes the scope" do + expect(serialized[:scope]).to include(id: voting.scope.id) + expect(serialized[:scope]).to include(name: voting.scope.name) + end + end + end + end +end diff --git a/decidim-elections/spec/lib/query_extensions_spec.rb b/decidim-elections/spec/lib/query_extensions_spec.rb new file mode 100644 index 00000000..f8bb6b0d --- /dev/null +++ b/decidim-elections/spec/lib/query_extensions_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" + +module Decidim + module Votings + describe Decidim::Api::QueryType do + include_context "with a graphql class type" + + describe "votings" do + let!(:voting1) { create(:voting, organization: current_organization) } + let!(:voting2) { create(:voting, organization: current_organization) } + let!(:voting3) { create(:voting) } + let!(:voting4) { create(:voting, :unpublished, organization: current_organization) } + + let(:query) { %({ votings { id }}) } + + it "returns all the votings" do + expect(response["votings"]).to include("id" => voting1.id.to_s) + expect(response["votings"]).to include("id" => voting2.id.to_s) + expect(response["votings"]).not_to include("id" => voting3.id.to_s) + expect(response["votings"]).not_to include("id" => voting4.id.to_s) + end + end + + describe "voting" do + let(:query) { %({ voting(id: "#{id}") { id }}) } + + context "with a voting that belongs to the current organization" do + let!(:voting) { create(:voting, organization: current_organization) } + let(:id) { voting.id } + + it "returns the voting" do + expect(response["voting"]).to eq("id" => voting.id.to_s) + end + end + + context "with a conference of another organization" do + let!(:voting) { create(:voting) } + let(:id) { voting.id } + + it "returns nil" do + expect(response["voting"]).to be_nil + end + end + end + end + end +end diff --git a/decidim-elections/spec/lib/tasks/decidim_election_generate_identification_keys_spec.rb b/decidim-elections/spec/lib/tasks/decidim_election_generate_identification_keys_spec.rb new file mode 100644 index 00000000..8b0b46fe --- /dev/null +++ b/decidim-elections/spec/lib/tasks/decidim_election_generate_identification_keys_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "decidim_elections:generate_identification_keys", type: :task do + it "runs gracefully" do + expect { task.execute }.not_to raise_error + end + + context "with the task output" do + before { task.execute } + + it "includes the private key" do + check_message_printed("PRIVATE KEY") + check_message_printed('{"kty":"RSA","n":"') + end + + it "includes the public key" do + check_message_printed("PUBLIC KEY") + check_message_printed("kty=RSA&n=") + end + + it "includes a reference to the documentation guide" do + check_message_printed("docs/services/bulletin_board.md") + end + end +end diff --git a/decidim-elections/spec/lib/tasks/decidim_election_scheduled_tasks_spec.rb b/decidim-elections/spec/lib/tasks/decidim_election_scheduled_tasks_spec.rb new file mode 100644 index 00000000..09aec363 --- /dev/null +++ b/decidim-elections/spec/lib/tasks/decidim_election_scheduled_tasks_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "decidim_elections:scheduled_tasks", type: :task do + include_context "with test bulletin board" + + it "runs gracefully" do + expect { task.execute }.not_to raise_error + end + + context "with elections to start" do + let!(:election) { create(:election, :bb_test, :key_ceremony_ended) } + + before { task.execute } + + it "requests starting the voting period" do + check_message_printed("Starting vote period for election ##{election.id}:") + check_message_printed("✓ Voting period start requested.") + end + end + + context "with elections to end" do + let!(:election) { create(:election, :bb_test, :vote, :finished) } + + before { task.execute } + + it "requests ending the voting period" do + check_message_printed("Ending vote period for election ##{election.id}:") + check_message_printed("✓ Voting period end requested.") + end + end + + context "with elections that should not be affected" do + let!(:election1) { create(:election, :key_ceremony_ended, start_time: 1.day.from_now) } + let!(:election2) { create(:election, :vote, :upcoming) } + let!(:election3) { create(:election, :vote, :ongoing) } + + before { task.execute } + + it "do not modify them" do + expect(election1.reload).to be_bb_key_ceremony_ended + expect(election2.reload).to be_bb_vote + end + end + + context "with pending votes" do + let!(:vote) { create(:vote, message_id: "decidim-test-authority.10004.vote.cast+v.voter-1") } + + before { task.execute } + + it "updates the vote status" do + check_message_printed("Checking status for Vote ##{vote.id}:") + check_message_printed("Vote status updated") + end + end + + context "with votes that should not be affected" do + let!(:vote) { create(:vote, status: "accepted") } + + before { task.execute } + + it "does not update the status" do + expect(vote.reload).to be_accepted + end + end +end diff --git a/decidim-elections/spec/lib/tasks/decidim_tasks_delete_download_your_data_files_spec.rb b/decidim-elections/spec/lib/tasks/decidim_tasks_delete_download_your_data_files_spec.rb new file mode 100644 index 00000000..01d28746 --- /dev/null +++ b/decidim-elections/spec/lib/tasks/decidim_tasks_delete_download_your_data_files_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "rake decidim_votings_census:delete_census_access_codes_export", type: :task do + let!(:original_expiry_time) { Decidim::Votings::Census.census_access_codes_export_expiry_time } + let!(:dataset) { create(:dataset) } + + before do + Decidim::Votings::Census.census_access_codes_export_expiry_time = 0.seconds + end + + after do + Decidim::Votings::Census.census_access_codes_export_expiry_time = original_expiry_time + end + + it "preloads the Rails environment" do + expect(task.prerequisites).to include "environment" + end + + context "when there are no files" do + it "does nothing" do + expect { task.execute }.not_to raise_error + end + end + + context "when there are some files" do + before do + generate_file + end + + it "runs gracefully" do + expect(census_attachments.count).to be_positive + expect { task.execute }.not_to raise_error + expect(census_attachments.count).to be_zero + end + end + + private + + def generate_file + dataset.access_codes_file.attach(io: File.open(Decidim::Dev.asset("city.jpeg")), filename: "city.jpeg") + end + + def census_attachments + ActiveStorage::Attachment.joins(:blob).where( + name: "access_codes_file", + record_type: "Decidim::Votings::Census::Dataset" + ) + end +end diff --git a/decidim-elections/spec/lib/tasks/main_spec.rb b/decidim-elections/spec/lib/tasks/main_spec.rb new file mode 100644 index 00000000..56039aab --- /dev/null +++ b/decidim-elections/spec/lib/tasks/main_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Task loading" do + include_examples "decidim module task loading", "decidim_elections" +end diff --git a/decidim-elections/spec/mailers/decidim/elections/trustee_mailer_spec.rb b/decidim-elections/spec/mailers/decidim/elections/trustee_mailer_spec.rb new file mode 100644 index 00000000..ed969bd3 --- /dev/null +++ b/decidim-elections/spec/mailers/decidim/elections/trustee_mailer_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections + describe TrusteeMailer do + let(:user) { create(:user, :confirmed) } + let(:organization) { user.organization } + let(:participatory_space) { create(:participatory_process, organization:) } + + let!(:trustee) do + trustee = create(:trustee, + decidim_user_id: user.id) + trustee.trustees_participatory_spaces.create( + participatory_space: + ) + end + + describe "#notification" do + subject(:mail) { described_class.notification(user, participatory_space, nil) } + + let(:translated_title) { translated(participatory_space.title, locale: organization.default_locale) } + + context "when using the organization default locale" do + it "sends an email with the right subject" do + expect(mail.subject).to include(translated_title) + end + + it "sends an email with the right body" do + expect(mail.body).to include(user.name) + end + end + end + end +end diff --git a/decidim-elections/spec/mailers/decidim/elections/vote_accepted_mailer_spec.rb b/decidim-elections/spec/mailers/decidim/elections/vote_accepted_mailer_spec.rb new file mode 100644 index 00000000..ea4f03ce --- /dev/null +++ b/decidim-elections/spec/mailers/decidim/elections/vote_accepted_mailer_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections + describe VoteAcceptedMailer do + let(:vote) { create(:vote) } + let(:verify_url) { "https://example.org/verify_url?hash=123" } + let(:locale) { nil } + let(:election) { vote.election } + + describe "#notification" do + subject(:mail) { described_class.notification(vote, verify_url, locale) } + + let(:translated_title) { translated(election.title, locale: locale || election.component.organization.default_locale) } + + context "when using the organization default locale" do + let(:mail_subject) { "Your vote for #{translated_title} was accepted." } + let(:body) do + ["

    Your vote for #{translated_title} was accepted.

    ", + "

    Your vote was accepted! Using your voting token: #{vote.encrypted_vote_hash}, you can verify your vote here.

    ", + "

    You have received this notification because you have voted for the #{translated_title} election.

    "] + end + + it "sends an email with the right subject" do + expect(mail.subject).to eq(mail_subject) + end + + it "sends an email with the right body" do + body.each do |body_part| + expect(mail.body.encoded).to include(body_part) + end + end + end + + context "when setting a locale" do + let(:locale) { "ca" } + let(:mail_subject) { "El teu vot a #{translated_title} s'ha acceptat." } + let(:body) do + ["

    El teu vot a #{translated_title} s'ha acceptat.

    ", + "

    El teu vot s'ha acceptat! Utilitzant el comprovant de vot: #{vote.encrypted_vote_hash}, pots verificar-lo aquí.

    ", + "

    Has rebut aquesta notificació perquè has votat a l'elecció #{translated_title}.

    "] + end + + it "sends an email with the right subject" do + expect(mail.subject).to eq(mail_subject) + end + + it "sends an email with the right body" do + body.each do |body_part| + expect(mail.body.encoded).to include(body_part) + end + end + end + end + end +end diff --git a/decidim-elections/spec/mailers/decidim/votings/access_code_mailer_spec.rb b/decidim-elections/spec/mailers/decidim/votings/access_code_mailer_spec.rb new file mode 100644 index 00000000..087ba99e --- /dev/null +++ b/decidim-elections/spec/mailers/decidim/votings/access_code_mailer_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe AccessCodeMailer do + let!(:datum) { create(:datum, :with_access_code) } + let!(:access_code) { datum.access_code } + let!(:organization) { datum.dataset.voting.organization } + let!(:voting) { datum.dataset.voting } + let(:locale) { nil } + + describe "#send_access_code" do + subject(:mail) { described_class.send_access_code(datum, locale) } + + let(:translated_title) { translated(voting.title, locale: locale || organization.default_locale) } + + context "when using the organization default locale" do + let(:mail_subject) { "Your Access Code to participate in #{translated_title}" } + let(:body) do + ["Hello #{datum.full_name},", + "Here is your Access Code that you asked for: #{access_code}. With this you will be able to participate in #{translated_title}."] + end + + it "sends an email with the right subject" do + expect(mail.subject).to eq(mail_subject) + end + + it "sends an email with the right body" do + body.each do |body_part| + expect(mail.body.encoded).to include(body_part) + end + end + end + + context "when setting a locale" do + let(:locale) { "es" } + let(:body) do + ["Hola #{datum.full_name},"] + end + + it "sends an email with the right body" do + body.each do |body_part| + expect(mail.body.encoded).to include(body_part) + end + end + end + end + end +end diff --git a/decidim-elections/spec/mailers/decidim/votings/census/export_mailer_spec.rb b/decidim-elections/spec/mailers/decidim/votings/census/export_mailer_spec.rb new file mode 100644 index 00000000..85706da9 --- /dev/null +++ b/decidim-elections/spec/mailers/decidim/votings/census/export_mailer_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Census + describe ExportMailer do + let(:voting) { create(:voting) } + let(:user) { create(:user, organization: voting.organization) } + + describe "access codes export" do + let(:filename) { "an_encrypted_zip_archive" } + let(:password) { "secret" } + let(:mail) { described_class.access_codes_export(user, voting, filename, password) } + + it "sets a subject" do + expect(mail.subject).to include(translated(voting.title)) + end + + it "has a link" do + expect(mail).to have_link("Download") + end + end + end + end + end +end diff --git a/decidim-elections/spec/models/decidim/elections/action_spec.rb b/decidim-elections/spec/models/decidim/elections/action_spec.rb new file mode 100644 index 00000000..c9d82086 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/action_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Action do + subject(:action) { build(:action) } + + it { is_expected.to be_valid } +end diff --git a/decidim-elections/spec/models/decidim/elections/answer_spec.rb b/decidim-elections/spec/models/decidim/elections/answer_spec.rb new file mode 100644 index 00000000..c8f37c5f --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/answer_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Answer do + subject(:answer) { build(:election_answer) } + + it { is_expected.to be_valid } + + include_examples "resourceable" + + describe "#proposals" do + subject { answer.proposals } + + let(:answer) { create(:election_answer) } + + it { is_expected.to be_empty } + + context "when the answer has related proposals" do + let(:proposals_component) { create(:component, manifest_name: :proposals, participatory_space: answer.question.election.component.participatory_space) } + let(:proposals) { create_list(:proposal, 2, component: proposals_component) } + let(:other_proposals) { create_list(:proposal, 2) } + + before do + other_proposals + answer.link_resources(proposals, "related_proposals") + end + + it { is_expected.to match_array(proposals) } + end + end +end diff --git a/decidim-elections/spec/models/decidim/elections/bulletin_board_closure_spec.rb b/decidim-elections/spec/models/decidim/elections/bulletin_board_closure_spec.rb new file mode 100644 index 00000000..b7c74535 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/bulletin_board_closure_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::BulletinBoardClosure do + subject(:closure) { build(:bb_closure) } + + it { is_expected.to be_valid } + + it "has an associated election" do + expect(closure.election).to be_a(Decidim::Elections::Election) + end + + context "with results" do + before do + closure.results << build_list(:election_result, 3, closurable: closure, election: closure.election) + end + + it "has many associated results" do + expect(closure.results.first).to be_a(Decidim::Elections::Result) + expect(closure.results.size).to eq(3) + end + end +end diff --git a/decidim-elections/spec/models/decidim/elections/election_spec.rb b/decidim-elections/spec/models/decidim/elections/election_spec.rb new file mode 100644 index 00000000..9ecf1170 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/election_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Election do + subject(:election) { build(:election) } + + it { is_expected.to be_valid } + + include_examples "has component" + include_examples "resourceable" + include_examples "publicable" + + describe "check the log result" do + it "overwrites the log presenter" do + expect(described_class.log_presenter_class_for(:foo)) + .to eq Decidim::Elections::AdminLog::ElectionPresenter + end + end + + it { is_expected.not_to be_started } + it { is_expected.not_to be_ongoing } + it { is_expected.not_to be_finished } + + it "has an association with one questionnaire" do + subject.questionnaire = build_stubbed(:questionnaire) + expect(subject.questionnaire).to be_present + end + + context "when it is ongoing" do + subject(:election) { build(:election, :ongoing) } + + it { is_expected.to be_started } + it { is_expected.to be_ongoing } + it { is_expected.not_to be_finished } + end + + context "when it is finished" do + subject(:election) { build(:election, :finished) } + + it { is_expected.to be_started } + it { is_expected.not_to be_ongoing } + it { is_expected.to be_finished } + end + + describe "start time checks" do + subject(:election) { build(:election, start_time:) } + + let(:start_time) { 4.hours.from_now } + + it { is_expected.to be_minimum_hours_before_start } + it { is_expected.to be_maximum_hours_before_start } + + context "when the election is about to start" do + let(:start_time) { 1.hour.from_now } + + it { is_expected.not_to be_minimum_hours_before_start } + it { is_expected.to be_maximum_hours_before_start } + end + + context "when the election is not near to start" do + let(:start_time) { 10.days.from_now } + + it { is_expected.to be_minimum_hours_before_start } + it { is_expected.not_to be_maximum_hours_before_start } + end + end +end diff --git a/decidim-elections/spec/models/decidim/elections/question_spec.rb b/decidim-elections/spec/models/decidim/elections/question_spec.rb new file mode 100644 index 00000000..2199f265 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/question_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Question do + subject(:question) { build(:question) } + + it { is_expected.to be_valid } + + include_examples "resourceable" +end diff --git a/decidim-elections/spec/models/decidim/elections/result_spec.rb b/decidim-elections/spec/models/decidim/elections/result_spec.rb new file mode 100644 index 00000000..7c4dab04 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/result_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Result do + subject(:result) { build(:election_result) } + + it { is_expected.to be_valid } + + it "has an associated question" do + expect(result.question).to be_a(Decidim::Elections::Question) + end + + it "has an associated answer" do + expect(result.answer).to be_a(Decidim::Elections::Answer) + end + + context "when the result comes from the bulletin board" do + it "has an associated bulletin board closure" do + expect(result.closurable).to be_a(Decidim::Elections::BulletinBoardClosure) + end + end +end diff --git a/decidim-elections/spec/models/decidim/elections/trustee_spec.rb b/decidim-elections/spec/models/decidim/elections/trustee_spec.rb new file mode 100644 index 00000000..3a66d757 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/trustee_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Trustee do + subject { trustee } + + let(:trustee) { build(:trustee, name: "Trustee 1") } + let(:organization) { trustee.organization } + + it { is_expected.to be_valid } + + it { expect(subject.bulletin_board_slug).to eql("#{organization.name.parameterize}-trustee-1") } + + context "when it is considered" do + let(:trustee) { build(:trustee, :considered) } + + it { is_expected.to be_valid } + end + + describe "class methods" do + subject { described_class } + + let(:trustee) { create(:trustee) } + let(:user) { trustee.user } + + before { trustee } + + it { is_expected.to be_trustee(user) } + it { expect(subject.for(user)).to eq(trustee) } + + context "when the user is not a trustee" do + let(:user) { create(:user) } + + it { is_expected.not_to be_trustee(user) } + it { expect(subject.for(user)).to be_nil } + end + end +end diff --git a/decidim-elections/spec/models/decidim/elections/vote_spec.rb b/decidim-elections/spec/models/decidim/elections/vote_spec.rb new file mode 100644 index 00000000..2d0832f3 --- /dev/null +++ b/decidim-elections/spec/models/decidim/elections/vote_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Vote do + subject(:vote) { build(:vote) } + + it { is_expected.to be_valid } + + it "is invalid when the voter_id is not present" do + subject.voter_id = nil + expect(subject).to be_invalid + end + + it "is invalid when the election is not present" do + subject.election = nil + expect(subject).to be_invalid + end + + it "is invalid when encrypted_vote_hash is not present" do + subject.encrypted_vote_hash = nil + expect(subject).to be_invalid + end + + it "is invalid with a status that is not included in the allowed status" do + expect { subject.status("foo") }.to raise_error(ArgumentError) + end +end diff --git a/decidim-elections/spec/models/decidim/votings/ballot_style_spec.rb b/decidim-elections/spec/models/decidim/votings/ballot_style_spec.rb new file mode 100644 index 00000000..428b2461 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/ballot_style_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::BallotStyle do + let(:election) { create(:election, :complete) } + + describe "#questions_for" do + subject { ballot_style.questions_for(election) } + + context "when the ballot style has questions from another election" do + # Adds questions from another election to the ballot style + let(:other_election) { create(:election, :complete) } + let(:ballot_style_questions_from_other_election) do + other_election.questions.first(2).each do |question| + create(:ballot_style_question, question:, ballot_style:) + end + end + + context "when the ballot style has questions from this election" do + let(:ballot_style) { create(:ballot_style, :with_ballot_style_questions, election:) } + + it "returns the questions for the specified election" do + expect(subject).to match_array(election.questions.first(2)) + end + end + + context "when the ballot style has questions from another election" do + let(:ballot_style) { create(:ballot_style) } + + it "returns the questions for the specified election" do + expect(subject).to be_empty + end + end + end + + context "when the ballot style DOES NOT HAVE questions from another election" do + context "when the ballot style has questions from this election" do + let(:ballot_style) { create(:ballot_style, :with_ballot_style_questions, election:) } + + it "returns the questions for the specified election" do + expect(subject).to match_array(election.questions.first(2)) + end + end + + context "when the ballot style has questions from another election" do + let(:ballot_style) { create(:ballot_style) } + + it "returns the questions for the specified election" do + expect(subject).to be_empty + end + end + end + end +end diff --git a/decidim-elections/spec/models/decidim/votings/census/dataset_spec.rb b/decidim-elections/spec/models/decidim/votings/census/dataset_spec.rb new file mode 100644 index 00000000..0ec33c87 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/census/dataset_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::Dataset do + subject { dataset } + + let(:dataset) { build(:dataset) } + + it { is_expected.to be_valid } + it { is_expected.to be_versioned } + + it "overwrites the log presenter" do + expect(described_class.log_presenter_class_for(:foo)) + .to eq Decidim::Votings::Census::AdminLog::DatasetPresenter + end + + it "has an associated voting" do + expect(dataset.voting).to be_a(Decidim::Votings::Voting) + end + + context "without file" do + let(:dataset) { build(:dataset, filename: nil) } + + it { is_expected.not_to be_valid } + end +end diff --git a/decidim-elections/spec/models/decidim/votings/census/datum_spec.rb b/decidim-elections/spec/models/decidim/votings/census/datum_spec.rb new file mode 100644 index 00000000..75dcf1f6 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/census/datum_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Census::Datum do + subject { datum } + + let(:dataset) { create(:dataset) } + + let(:datum) { build(:datum, dataset:) } + + it { is_expected.to be_valid } + + it "has an associated dataset" do + expect(datum.dataset).to be_a(Decidim::Votings::Census::Dataset) + end + + describe "uniqueness" do + context "with hashed_in_person_data" do + let!(:another_datum) do + create(:datum, + dataset:, + hashed_in_person_data: datum.hashed_in_person_data) + end + + it { is_expected.not_to be_valid } + end + + context "with hashed_check_data" do + let!(:another_datum) do + create(:datum, + dataset:, + hashed_check_data: datum.hashed_check_data) + end + + it { is_expected.not_to be_valid } + end + end + + describe "presence" do + context "without full_name" do + let(:datum) { build(:datum, full_name: nil) } + + it { is_expected.not_to be_valid } + end + + context "without full_address" do + let(:datum) { build(:datum, full_address: nil) } + + it { is_expected.not_to be_valid } + end + + context "without postal_code" do + let(:datum) { build(:datum, postal_code: nil) } + + it { is_expected.not_to be_valid } + end + + context "without hashed_in_person_data" do + let(:datum) { build(:datum, hashed_in_person_data: nil) } + + it { is_expected.not_to be_valid } + end + + context "without hashed_check_data" do + let(:datum) { build(:datum, hashed_check_data: nil) } + + it { is_expected.not_to be_valid } + end + end +end diff --git a/decidim-elections/spec/models/decidim/votings/polling_officer_spec.rb b/decidim-elections/spec/models/decidim/votings/polling_officer_spec.rb new file mode 100644 index 00000000..21812aa1 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/polling_officer_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + describe PollingOfficer do + subject(:polling_officer) { build(:polling_officer, user:, voting:) } + + let(:organization) { create(:organization) } + let(:user) { create(:user, organization:) } + let(:voting) { create(:voting, organization:) } + + it { is_expected.to be_valid } + + context "when the voting is from another organization" do + let(:other_organization) { create(:organization) } + let(:voting) { create(:voting, organization: other_organization) } + + it { is_expected.not_to be_valid } + end + + context "when the user is already present" do + context "when in the same voting" do + let!(:other_polling_officer) { create(:polling_officer, user:, voting:) } + + it { is_expected.not_to be_valid } + end + + context "when in another voting" do + let(:other_voting) { create(:voting, organization:) } + let!(:other_polling_officer) { create(:polling_officer, user:, voting: other_voting) } + + it { is_expected.to be_valid } + end + end + end + end +end diff --git a/decidim-elections/spec/models/decidim/votings/polling_station_closure_spec.rb b/decidim-elections/spec/models/decidim/votings/polling_station_closure_spec.rb new file mode 100644 index 00000000..f1283145 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/polling_station_closure_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::PollingStationClosure do + subject(:closure) { build(:ps_closure) } + + it { is_expected.to be_valid } + + it "has an associated election" do + expect(closure.election).to be_a(Decidim::Elections::Election) + end + + it "has an associated polling_station" do + expect(closure.polling_station).to be_a(Decidim::Votings::PollingStation) + end + + it "has an associated polling_officer" do + expect(closure.polling_officer).to be_a(Decidim::Votings::PollingOfficer) + end + + context "with results" do + before do + closure.results << build_list(:election_result, 3, closurable: closure, election: closure.election) + end + + it "has many associated results" do + expect(closure.results.first).to be_a(Decidim::Elections::Result) + expect(closure.results.size).to eq(3) + end + + it "the results have an associated bulletin board closure" do + expect(closure.results.first.closurable).to be_a(Decidim::Votings::PollingStationClosure) + end + end + + describe "#signed?" do + let(:closure) { create(:ps_closure, signed_at:) } + + context "when signed_at is blank" do + let(:signed_at) { nil } + + it "returns false" do + expect(closure.signed?).to be false + end + end + + context "when signed_at is present" do + let(:signed_at) { Time.current } + + it "returns true" do + expect(closure.signed?).to be true + end + end + end +end diff --git a/decidim-elections/spec/models/decidim/votings/polling_station_spec.rb b/decidim-elections/spec/models/decidim/votings/polling_station_spec.rb new file mode 100644 index 00000000..0a464bc0 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/polling_station_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + describe PollingStation do + subject(:polling_station) { build(:polling_station, voting:, polling_station_managers: managers, polling_station_president: president) } + + let(:organization) { create(:organization) } + let(:voting) { create(:voting, organization:) } + let(:managers) { [] } + let(:president) { nil } + + context "when no polling officers are assigned" do + it { is_expected.to be_valid } + it { is_expected.to be_versioned } + end + + context "when a polling station president is assigned" do + context "when the officer is from the same voting" do + let(:president) { create(:polling_officer, voting:, user: create(:user, organization:)) } + + it "is valid" do + expect(subject).to be_valid + expect(subject.polling_station_president).to eq president + end + end + + context "when the officer is from another voting" do + let(:other_voting) { create(:voting, organization:) } + let(:president) { create(:polling_officer, voting: other_voting, user: create(:user, organization:)) } + + it "is not valid" do + expect(subject).not_to be_valid + end + end + end + + context "when several polling station president is assigned" do + let(:managers) { create_list(:polling_officer, 3, voting:) } + + it "is valid" do + expect(subject).to be_valid + expect(subject.polling_station_managers).to eq managers + end + end + end + end +end diff --git a/decidim-elections/spec/models/decidim/votings/voting_spec.rb b/decidim-elections/spec/models/decidim/votings/voting_spec.rb new file mode 100644 index 00000000..b9fa1b75 --- /dev/null +++ b/decidim-elections/spec/models/decidim/votings/voting_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Voting do + subject(:voting) { build(:voting, slug: "my-slug") } + + it { is_expected.to be_valid } + it { is_expected.to be_versioned } + + include_examples "publicable" + + it "overwrites the log presenter" do + expect(described_class.log_presenter_class_for(:foo)) + .to eq Decidim::Votings::AdminLog::VotingPresenter + end + + context "when there is a voting with the same slug in the same organization" do + let!(:another_voting) { create(:voting, organization: voting.organization, slug: "my-slug") } + + it "is not valid" do + expect(subject).not_to be_valid + expect(subject.errors[:slug]).to eq ["has already been taken"] + end + end + + context "when there is a voting with the same slug in another organization" do + let!(:another_voting) { create(:voting, slug: "my-slug") } + + it { is_expected.to be_valid } + end +end diff --git a/decidim-elections/spec/permissions/decidim/elections/admin/permissions_spec.rb b/decidim-elections/spec/permissions/decidim/elections/admin/permissions_spec.rb new file mode 100644 index 00000000..c33f42e0 --- /dev/null +++ b/decidim-elections/spec/permissions/decidim/elections/admin/permissions_spec.rb @@ -0,0 +1,267 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::Permissions do + subject { described_class.new(user, permission_action, context).permissions.allowed? } + + let(:user) { create(:user, organization: elections_component.organization) } + let(:context) do + { + current_component: elections_component, + election:, + question:, + answer:, + trustee_participatory_space:, + questionnaire: + } + end + let(:elections_component) { create(:elections_component) } + let(:election) { create(:election, component: elections_component) } + let(:question) { nil } + let(:answer) { nil } + let(:trustee_participatory_space) { create(:trustees_participatory_space) } + let(:questionnaire) { election&.questionnaire } + let(:permission_action) { Decidim::PermissionAction.new(**action) } + + shared_examples "not allowed when election was created on the bulletin board" do + context "when election was created on the bulletin board" do + let(:election) { create(:election, :created, component: elections_component) } + + it { is_expected.to be false } + end + end + + shared_examples "not allowed when trustee has elections" do + context "when trustee has elections" do + let(:trustee) { create(:trustee, :with_elections) } + let(:trustee_participatory_space) { create(:trustees_participatory_space, trustee:) } + + it { is_expected.to be false } + end + end + + context "when scope is not admin" do + let(:action) do + { scope: :foo, action: :bar, subject: :election } + end + + it_behaves_like "permission is not set" + end + + context "when subject is not an election" do + let(:action) do + { scope: :admin, action: :bar, subject: :foo } + end + + it_behaves_like "permission is not set" + end + + context "when action is a random one" do + let(:action) do + { scope: :admin, action: :bar, subject: :election } + end + + it_behaves_like "permission is not set" + end + + describe "election creation" do + let(:action) do + { scope: :admin, action: :create, subject: :election } + end + let(:election) { nil } + + it { is_expected.to be true } + end + + describe "election update" do + let(:action) do + { scope: :admin, action: :update, subject: :election } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "election publish" do + let(:election) { create(:election, :complete, component: elections_component) } + let(:action) do + { scope: :admin, action: :publish, subject: :election } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "election delete" do + let(:action) do + { scope: :admin, action: :delete, subject: :election } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "election unpublish" do + let(:action) do + { scope: :admin, action: :unpublish, subject: :election } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "questions" do + let(:question) { create(:question, election:) } + + describe "question creation" do + let(:action) do + { scope: :admin, action: :create, subject: :question } + end + let(:question) { nil } + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "question update" do + let(:action) do + { scope: :admin, action: :update, subject: :question } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "question delete" do + let(:action) do + { scope: :admin, action: :delete, subject: :question } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + end + + describe "answers" do + let(:question) { create(:question, election:) } + let(:answer) { create(:election_answer, question:) } + + describe "answer creation" do + let(:action) do + { scope: :admin, action: :create, subject: :answer } + end + let(:answer) { nil } + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "answer update" do + let(:action) do + { scope: :admin, action: :update, subject: :answer } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "answer delete" do + let(:action) do + { scope: :admin, action: :delete, subject: :answer } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "select answer" do + let(:election) { create(:election, :tally_ended, component: elections_component) } + + let(:action) do + { scope: :admin, action: :select, subject: :answer } + end + + it { is_expected.to be true } + end + + describe "import proposals" do + let(:action) do + { scope: :admin, action: :import_proposals, subject: :answer } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election was created on the bulletin board" + end + + describe "add user as trustee" do + let(:action) do + { scope: :admin, action: :create, subject: :trustee_participatory_space } + end + let(:trustee) { nil } + + it { is_expected.to be true } + end + + describe "remove trustee from participatory space" do + let(:action) do + { scope: :admin, action: :delete, subject: :trustee_participatory_space } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when trustee has elections" + end + + describe "update trustee participatory space" do + let(:action) do + { scope: :admin, action: :update, subject: :trustee_participatory_space } + end + + it { is_expected.to be true } + end + + context "when subject is a questionnaire" do + let(:action) do + { scope: :admin, action: :update, subject: :questionnaire } + end + + context "when feedback form is present" do + it { is_expected.to be true } + end + + context "when feedback form is missing" do + let(:questionnaire) { nil } + + it { is_expected.to be false } + end + end + + describe "read election steps" do + let(:action) do + { scope: :admin, action: :read, subject: :steps } + end + + it { is_expected.to be true } + end + + describe "update election steps" do + let(:action) do + { scope: :admin, action: :update, subject: :steps } + end + + it { is_expected.to be true } + end + end +end diff --git a/decidim-elections/spec/permissions/decidim/elections/permissions_spec.rb b/decidim-elections/spec/permissions/decidim/elections/permissions_spec.rb new file mode 100644 index 00000000..098d3acc --- /dev/null +++ b/decidim-elections/spec/permissions/decidim/elections/permissions_spec.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Permissions do + subject { described_class.new(user, permission_action, context).permissions.allowed? } + + let(:organization) { elections_component.organization } + let(:user) { create(:user, organization:) } + let(:context) do + { + current_component: elections_component, + election: + } + end + let(:elections_component) { create(:elections_component) } + let(:election) { create(:election, :published, component: elections_component) } + let(:permission_action) { Decidim::PermissionAction.new(**action) } + + context "when scope is admin" do + let(:action) do + { scope: :admin, action: :foo, subject: :election } + end + + it_behaves_like "delegates permissions to", Decidim::Elections::Admin::Permissions + end + + context "when scope is trustee zone" do + let(:action) do + { scope: :trustee_zone, action: :foo, subject: :election } + end + + it_behaves_like "delegates permissions to", Decidim::Elections::TrusteeZone::Permissions + end + + context "when scope is not public" do + let(:action) do + { scope: :foo, action: :bar, subject: :election } + end + + it_behaves_like "permission is not set" + end + + context "when subject is not an election" do + let(:action) do + { scope: :public, action: :bar, subject: :foo } + end + + it_behaves_like "permission is not set" + end + + context "when action is a random one" do + let(:action) do + { scope: :public, action: :bar, subject: :election } + end + + it_behaves_like "permission is not set" + end + + describe "election view" do + let(:action) do + { scope: :public, action: :view, subject: :election } + end + + it { is_expected.to be_truthy } + + context "when election is not published" do + let(:election) { create(:election, :upcoming, component: elections_component) } + + it { is_expected.to be_falsey } + + context "when user is not logged in" do + let(:user) { nil } + + it { is_expected.to be_falsey } + end + + context "when user is an administrator" do + let(:user) { create(:user, :admin, organization: elections_component.organization) } + + it { is_expected.to be_truthy } + end + end + + context "when user is not logged in" do + let(:user) { nil } + + it { is_expected.to be_truthy } + end + end + + describe "election preview" do + let(:action) do + { scope: :public, action: :preview, subject: :election } + end + + it { is_expected.to be_falsey } + + context "when user is an administrator" do + let(:user) { create(:user, :admin, organization: elections_component.organization) } + + it { is_expected.to be_truthy } + end + end + + describe "election vote" do + let(:action) do + { scope: :public, action: :vote, subject: :election } + end + + context "when election is not published" do + let(:election) { create(:election, :upcoming, component: elections_component) } + + it { is_expected.to be_falsey } + end + + context "when election is upcoming" do + let(:election) { create(:election, :published, :upcoming, component: elections_component) } + + it { is_expected.to be_falsey } + end + + context "when election is ongoing" do + let(:election) { create(:election, :published, :ongoing, component: elections_component) } + + it { is_expected.to be_truthy } + + context "without a user" do + let(:user) { nil } + + it { is_expected.to be_truthy } + end + end + + context "when election has finished" do + let(:election) { create(:election, :published, :finished, component: elections_component) } + + it { is_expected.to be_falsey } + end + end + + describe "election vote with a user flow" do + let(:action) do + { scope: :public, action: :user_vote, subject: :election } + end + + context "when election is not published" do + let(:election) { create(:election, :upcoming, component: elections_component) } + + it { is_expected.to be_falsey } + end + + context "when election is upcoming" do + let(:election) { create(:election, :published, :upcoming, component: elections_component) } + + it { is_expected.to be_falsey } + end + + context "when election is ongoing" do + let(:election) { create(:election, :published, :ongoing, component: elections_component) } + + it { is_expected.to be_truthy } + + context "without a user" do + let(:user) { nil } + + it { is_expected.to be_falsey } + end + + context "when the election has an authorization" do + before do + organization.update!(available_authorizations: %w(dummy_authorization_handler)) + elections_component.update!(permissions: { + vote: { + authorization_handlers: { + "dummy_authorization_handler" => { "options" => {} } + } + } + }) + end + + it { is_expected.to be_falsey } + + context "when user is not authorized to vote" do + let!(:authorization) { create(:authorization, name: "dummy_authorization_handler", user:) } + + it { is_expected.to be_truthy } + end + end + end + + context "when election has finished" do + let(:election) { create(:election, :published, :finished, component: elections_component) } + + it { is_expected.to be_falsey } + end + end +end diff --git a/decidim-elections/spec/permissions/decidim/elections/trustee_zone/permissions_spec.rb b/decidim-elections/spec/permissions/decidim/elections/trustee_zone/permissions_spec.rb new file mode 100644 index 00000000..e4bfcc65 --- /dev/null +++ b/decidim-elections/spec/permissions/decidim/elections/trustee_zone/permissions_spec.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::TrusteeZone::Permissions do + subject { described_class.new(user, permission_action, context).permissions.allowed? } + + let(:user) { create(:user, organization: elections_component.organization) } + let(:context) do + { + current_component: elections_component, + trustee: permission_trustee + } + end + let(:elections_component) { create(:elections_component) } + let(:trustee) { create(:trustee, user:) } + let(:election) do + create(:election, :ready_for_setup, trustees_participatory_space: trustee_participatory_space) + end + let(:trustee_participatory_space) { create(:trustees_participatory_space, trustee:) } + let(:permission_trustee) { trustee } + let(:permission_action) { Decidim::PermissionAction.new(**action) } + + shared_examples "not allowed when the user is not a trustee" do + context "when the user is not a trustee" do + let!(:trustee) { create(:trustee) } + + it { is_expected.to be_falsey } + end + end + + shared_examples "not allowed when the given trustee is not the same than the user trustee" do + context "when the trustee is not the same than the user trustee" do + let(:permission_trustee) { create(:trustee) } + + it { is_expected.to be_falsey } + end + end + + shared_examples "not allowed when election is not attached to trustee" do + context "when the election is not an election for the trustee" do + let(:permission_trustee) { create(:trustee) } + let(:permission_election) { create(:election, :ready_for_setup) } + + it { is_expected.to be_falsey } + end + end + + context "when scope is not trustee zone" do + let(:action) do + { scope: :foo, action: :bar, subject: :election } + end + + it_behaves_like "permission is not set" + end + + context "when subject is not a trustee" do + let(:action) do + { scope: :trustee_zone, action: :bar, subject: :foo } + end + + it_behaves_like "permission is not set" + end + + context "when action is a random one" do + let(:action) do + { scope: :trustee_zone, action: :bar, subject: :trustee } + end + + it_behaves_like "permission is not set" + end + + describe "view trustee" do + let(:action) do + { scope: :trustee_zone, action: :view, subject: :trustee } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when the user is not a trustee" + it_behaves_like "not allowed when the given trustee is not the same than the user trustee" + end + + describe "update trustee" do + let(:action) do + { scope: :trustee_zone, action: :update, subject: :trustee } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when the user is not a trustee" + it_behaves_like "not allowed when the given trustee is not the same than the user trustee" + end + + describe "view election" do + let(:action) do + { scope: :trustee_zone, action: :view, subject: :election } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when the user is not a trustee" + it_behaves_like "not allowed when the given trustee is not the same than the user trustee" + end + + describe "update election" do + let(:action) do + { scope: :trustee_zone, action: :update, subject: :election } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when election is not attached to trustee" + it_behaves_like "not allowed when the user is not a trustee" + it_behaves_like "not allowed when the given trustee is not the same than the user trustee" + end +end diff --git a/decidim-elections/spec/permissions/decidim/votings/admin/permissions_spec.rb b/decidim-elections/spec/permissions/decidim/votings/admin/permissions_spec.rb new file mode 100644 index 00000000..65205821 --- /dev/null +++ b/decidim-elections/spec/permissions/decidim/votings/admin/permissions_spec.rb @@ -0,0 +1,225 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Admin::Permissions do + subject { described_class.new(user, permission_action, context).permissions.allowed? } + + let(:user) { create(:user, :admin, organization:) } + let(:organization) { create(:organization) } + let(:voting) { create(:voting, organization:) } + let(:context) { { participatory_space: voting }.merge(extra_context) } + let(:extra_context) { {} } + let(:permission_action) { Decidim::PermissionAction.new(**action) } + let(:action) do + { scope: :admin, action: action_name, subject: action_subject } + end + + context "when the action is not for the admin part" do + let(:action) do + { scope: :public, action: :foo, subject: :bar } + end + + it_behaves_like "permission is not set" + end + + context "when the user is not an admin" do + let(:user) { create(:user, organization:) } + let(:action) do + { scope: :admin, action: :foo, subject: :bar } + end + + it { is_expected.to be false } + end + + describe "participatory spaces" do + let(:action_subject) { :participatory_space } + let(:action_name) { :read } + + it { is_expected.to be true } + end + + describe "components" do + let(:action_subject) { :components } + let(:action_name) { :read } + let(:extra_context) do + { participatory_space: voting } + end + + it { is_expected.to be true } + end + + describe "votings" do + let(:action_subject) { :voting } + + context "when creating a voting" do + let(:action_name) { :create } + + it { is_expected.to be true } + end + + context "when reading a voting" do + let(:action_name) { :read } + + it { is_expected.to be true } + end + end + + describe "polling stations" do + let(:action_subject) { :polling_station } + let(:action_name) { :create } + let(:polling_station) { create(:polling_station, voting:) } + + it { is_expected.to be true } + + context "when updating a polling station" do + let(:action_name) { :update } + + it { is_expected.to be false } + + context "and there is a polling station" do + let(:extra_context) do + { polling_station: } + end + + it { is_expected.to be true } + end + end + + context "when destroying a polling station" do + let(:action_name) { :delete } + + it { is_expected.to be false } + + context "and there is a polling station" do + let(:extra_context) do + { polling_station: } + end + + it { is_expected.to be true } + + context "and has closures" do + let!(:closure) { create(:ps_closure, polling_station:) } + + it { is_expected.to be false } + end + end + end + end + + describe "census" do + let(:action_subject) { :census } + + context "when managing a census" do + let(:action_name) { :manage } + + it { is_expected.to be true } + end + end + + describe "monitoring committee" do + context "when the user is an admin" do + context "when reading the Committee Members" do + let(:action_subject) { :monitoring_committee_members } + let(:action_name) { :read } + + it { is_expected.to be true } + end + + context "when reading the Certificates" do + let(:action_subject) { :monitoring_committee_polling_station_closures } + let(:action_name) { :read } + + it { is_expected.to be false } + end + end + + context "when the user is a Monitoring Committee Member" do + let(:user) { create(:user, organization:) } + let!(:monitoring_committee_member) { create(:monitoring_committee_member, user:, voting:) } + + context "when accessing the admin panel" do + let(:action_subject) { :admin_dashboard } + let(:action_name) { :read } + + it { is_expected.to be true } + end + + context "when reading the votings" do + let(:action_subject) { :votings } + let(:action_name) { :read } + + it { is_expected.to be true } + end + + context "when listing their voting" do + let(:action_subject) { :voting } + let(:action_name) { :list } + + it { is_expected.to be true } + end + + context "when listing another voting" do + let(:other_voting) { create(:voting, organization:) } + let!(:monitoring_committee_member) { create(:monitoring_committee_member, user:, voting: other_voting) } + let(:action_subject) { :voting } + let(:action_name) { :list } + + it { is_expected.to be false } + end + + context "when reading their voting's Polling Officers" do + let(:action_subject) { :polling_officers } + let(:action_name) { :read } + + it { is_expected.to be false } + end + + context "when reading their voting's Committee Members" do + let(:action_subject) { :monitoring_committee_members } + let(:action_name) { :read } + + it { is_expected.to be false } + end + + context "when reading their voting's Certificates" do + let(:action_subject) { :monitoring_committee_polling_station_closures } + let(:action_name) { :read } + + it { is_expected.to be true } + end + end + end + + describe "ballot styles" do + let(:action_subject) { :ballot_style } + + context "when updating a ballot style" do + let(:action_name) { :update } + let(:extra_context) { { ballot_style: } } + let(:ballot_style) { create(:ballot_style, voting:) } + + context "when census ballot style is not present" do + let(:ballot_style) { nil } + + it { is_expected.to be false } + end + + context "when census dataset is not present" do + it { is_expected.to be true } + end + + context "when census dataset is in init_data status" do + let!(:dataset) { create(:dataset, voting:, status: :init_data) } + + it { is_expected.to be true } + end + + context "when census dataset is in another status" do + let!(:dataset) { create(:dataset, voting:, status: :data_created) } + + it { is_expected.to be false } + end + end + end +end diff --git a/decidim-elections/spec/permissions/decidim/votings/permissions_spec.rb b/decidim-elections/spec/permissions/decidim/votings/permissions_spec.rb new file mode 100644 index 00000000..29fcd2dc --- /dev/null +++ b/decidim-elections/spec/permissions/decidim/votings/permissions_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Votings::Permissions do + subject { described_class.new(user, permission_action, context).permissions.allowed? } + + let(:user) { create(:user, organization:) } + let(:organization) { create(:organization) } + let(:voting) { create(:voting, organization:) } + let(:context) { { voting: } } + let(:permission_action) { Decidim::PermissionAction.new(**action) } + + context "when the action is for the admin part" do + let(:action) do + { scope: :admin, action: :foo, subject: :bar } + end + + it_behaves_like "delegates permissions to", Decidim::Votings::Admin::Permissions + end + + context "when the action is for the public part" do + let(:action_name) { :read } + let(:action) do + { scope: :public, action: action_name, subject: action_subject } + end + + context "when reading a consultation" do + let(:action_subject) { :voting } + + context "when the consultation is published" do + let(:voting) { create(:voting, :published, organization:) } + + it { is_expected.to be true } + end + end + end +end diff --git a/decidim-elections/spec/permissions/decidim/votings/polling_officer_zone/permissions_spec.rb b/decidim-elections/spec/permissions/decidim/votings/polling_officer_zone/permissions_spec.rb new file mode 100644 index 00000000..9aaf086c --- /dev/null +++ b/decidim-elections/spec/permissions/decidim/votings/polling_officer_zone/permissions_spec.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module PollingOfficerZone + describe Permissions do + subject { described_class.new(user, permission_action, context).permissions.allowed? } + + let(:user) { create(:user, organization: voting.organization) } + let(:context) do + { + polling_officer:, + polling_officers: + } + end + let(:voting) { create(:voting) } + let(:other_voting) { create(:voting, organization: voting.organization) } + let(:polling_officer) { create(:polling_officer, user:, voting:) } + let(:polling_officers) { [polling_officer, create(:polling_officer, user:, voting: other_voting)] } + let(:polling_station) { create(:polling_station, voting:) } + let(:closure) { create(:ps_closure, polling_officer:, polling_station:) } + let(:permission_action) { Decidim::PermissionAction.new(**action) } + + shared_examples "not allowed when a polling officer is not attached to the current user" do + context "when a polling officer is not attached to a user" do + let!(:polling_officer) { create(:polling_officer, voting:) } + + it { is_expected.to be_falsey } + end + end + + context "when scope is not polling officer zone" do + let(:action) do + { scope: :foo, action: :bar, subject: :polling_officers } + end + + it_behaves_like "permission is not set" + end + + context "when subject is not correct" do + let(:action) do + { scope: :polling_officer_zone, action: :view, subject: :foo } + end + + it_behaves_like "permission is not set" + end + + context "when action is not correct" do + let(:action) do + { scope: :polling_officer_zone, action: :foo, subject: :polling_officers } + end + + it_behaves_like "permission is not set" + end + + describe "view polling officers" do + let(:action) do + { scope: :polling_officer_zone, action: :view, subject: :polling_officers } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when a polling officer is not attached to the current user" + end + + describe "manage polling station results" do + let(:action) do + { scope: :polling_officer_zone, action: :manage, subject: :polling_station_results } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when a polling officer is not attached to the current user" + end + + describe "create polling station results" do + let(:election) { create(:voting_election, voting:) } + let!(:another_closure) { create(:ps_closure, polling_station:, election:) } + let(:action) do + { scope: :polling_officer_zone, action: :create, subject: :polling_station_results } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when a polling officer is not attached to the current user" + + context "when a closure already exists" do + let!(:closure) { create(:ps_closure, polling_station:, polling_officer:) } + let(:context) do + { polling_officer:, closure: } + end + + it { is_expected.to be_falsey } + end + end + + describe "edit polling station results" do + let(:action) do + { scope: :polling_officer_zone, action: :edit, subject: :polling_station_results } + end + let(:context) do + { polling_officer:, closure: } + end + let(:polling_officer) { create(:polling_officer, :president, user:, voting:) } + + it { is_expected.to be true } + + it_behaves_like "not allowed when a polling officer is not attached to the current user" + + context "when there is no closure" do + let(:context) do + { polling_officer: } + end + + it { is_expected.to be false } + end + + context "and closure is sealed" do + let(:closure) { create(:ps_closure, :completed, polling_officer:, polling_station:) } + + it { is_expected.to be false } + end + end + + describe "manage in person votes" do + let(:action) do + { scope: :polling_officer_zone, action: :manage, subject: :in_person_vote } + end + + it { is_expected.to be true } + + it_behaves_like "not allowed when a polling officer is not attached to the current user" + end + + describe "create in person votes" do + let(:action) do + { scope: :polling_officer_zone, action: :create, subject: :in_person_vote } + end + let(:context) do + { + polling_officer:, + polling_officers:, + closure: + } + end + + it { is_expected.to be false } + + context "when no closure is present" do + let(:context) do + { + polling_officer:, + polling_officers: + } + end + + it { is_expected.to be true } + end + end + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/elections/admin_log/election_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/elections/admin_log/election_presenter_spec.rb new file mode 100644 index 00000000..92a5e662 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/elections/admin_log/election_presenter_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Elections::AdminLog::ElectionPresenter, type: :helper do + subject { described_class.new(action_log, helper) } + + let(:action_log) { create(:action_log, action:) } + + before do + helper.extend(Decidim::ApplicationHelper) + helper.extend(Decidim::TranslationsHelper) + end + + describe "#present" do + context "when the election is created" do + let(:action) { :create } + + it "shows the election has been created" do + expect(subject.present).to include(" created the election ") + expect(subject.present).not_to include(" on the Bulletin Board") + end + end + + context "when the election is updated" do + let(:action) { :update } + + it "shows the election has been updated" do + expect(subject.present).to include(" updated the election ") + end + end + + context "when the election is deleted" do + let(:action) { :delete } + + it "shows the election has been deleted" do + expect(subject.present).to include(" deleted the election ") + end + end + + context "when the election is published" do + let(:action) { :publish } + + it "shows the election has been published" do + expect(subject.present).to include(" published the ") + end + end + + context "when the election is unpublished" do + let(:action) { :unpublish } + + it "shows the election has been unpublished" do + expect(subject.present).to include(" unpublished the ") + end + end + + context "when the election is setup" do + let(:action) { :setup } + + it "shows the election has been setup" do + expect(subject.present).to include(" created the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + + context "when the key ceremony is started" do + let(:action) { :start_key_ceremony } + + it "shows the key ceremony has started" do + expect(subject.present).to include(" started the key ceremony for the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + + context "when the vote period is started" do + let(:action) { :start_vote } + + it "shows the voting period has started" do + expect(subject.present).to include(" started the voting period for the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + + context "when the vote period is ended" do + let(:action) { :end_vote } + + it "shows the voting period has ended" do + expect(subject.present).to include(" ended the voting period for the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + + context "when the tally is started" do + let(:action) { :start_tally } + + it "shows the tally has started" do + expect(subject.present).to include(" started the tally for the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + + context "when a trustee is reported as missing" do + let(:action_log) { build(:action_log, action:) } + let(:action) { :report_missing_trustee } + let(:trustee) { create(:trustee) } + + before do + action_log.extra["extra"] = { + "trustee_id" => trustee.id, + "name" => "Somebody Trustable", + "nickname" => "TrustMe" + } + action_log.save! + end + + it "shows the trustee was reported" do + expect(subject.present).to include("Somebody Trustable") + expect(subject.present).to include(" as a missing trustee during the tally for the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + + context "when the election results are published" do + let(:action) { :publish_results } + + it "shows the election results are published" do + expect(subject.present).to include(" published the results for the election ") + expect(subject.present).to include(" on the Bulletin Board") + end + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/elections/admin_log/trustee_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/elections/admin_log/trustee_presenter_spec.rb new file mode 100644 index 00000000..8c1e0d30 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/elections/admin_log/trustee_presenter_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Elections::AdminLog::TrusteePresenter, type: :helper do + subject { described_class.new(action_log, helper) } + + let(:resource) { create(:trustee) } + let(:action_log) do + create( + :action_log, + action:, + resource: + ) + end + + before do + helper.extend(Decidim::ApplicationHelper) + helper.extend(Decidim::TranslationsHelper) + end + + describe "#present" do + context "when the trustee is created" do + let(:action) { :create } + + it "shows that the trustee has been created" do + expect(subject.present).to include(resource.user.nickname) + expect(subject.present).to include("Trustee") + end + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/elections/election_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/elections/election_presenter_spec.rb new file mode 100644 index 00000000..7414fc2d --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/elections/election_presenter_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + describe ElectionPresenter, type: :helper do + subject(:presenter) { described_class.new(election) } + + let(:election) { create(:election, title:, description:) } + let(:title) do + { + "en" => "A title
    with a lot of HTML & "e;special characters"e;" + } + end + let(:description) do + { + "en" => "Election description and some spam links:" + } + end + + describe "#title" do + subject { presenter.title } + + it "returns the title with html escaped" do + expect(subject).to eq "A title <br/> with a lot of <strong>HTML</strong> &amp; &quote;special characters&quote;" + end + end + + describe "#description" do + subject { presenter.description } + + it "returns the description with html tags" do + expect(subject).to eq "Election description and some spam links:" + end + + context "when stripping tags" do + subject { presenter.description(strip_tags: true) } + + it "returns the description without html tags" do + expect(subject).to eq "Election description and some spam links:\nthis is for SEO\nvisit this" + end + end + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/elections/trustee_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/elections/trustee_presenter_spec.rb new file mode 100644 index 00000000..9e9d0d30 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/elections/trustee_presenter_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Elections + describe TrusteePresenter, type: :helper do + subject(:presenter) { described_class.new(trustee) } + + let(:trustee) { create(:trustee, public_key:) } + let(:public_key) do + { + kty: "RSA", + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" \ + "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" \ + "4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" \ + "W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" \ + "1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" \ + "aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e: "AQAB", + alg: "RS256", + kid: "2011-04-29" + }.to_json + end + + describe "#public_key_thumbprint" do + subject { presenter.public_key_thumbprint } + + it "returns the thumbprint for the JWK public key according the RFC 7638 specification" do + expect(subject).to eq "
    NzbLsXh8uDCcd-6\nMNwXF4W_7noWX\nFZAfHkxZsRGC9Xs
    " + end + + context "when the public key is not present" do + let(:public_key) { nil } + + it { expect(subject).to be_nil } + end + + context "when the public key is empty" do + let(:public_key) { "" } + + it { expect(subject).to be_nil } + end + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/admin_log/ballot_style_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/admin_log/ballot_style_presenter_spec.rb new file mode 100644 index 00000000..4ce3e511 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/admin_log/ballot_style_presenter_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::AdminLog::BallotStylePresenter, type: :helper do + subject { described_class.new(action_log, helper) } + + let(:resource) { create(:ballot_style) } + let(:action_log) do + create( + :action_log, + action:, + resource:, + extra_data: { code: resource.code } + ) + end + + before do + helper.extend(Decidim::ApplicationHelper) + helper.extend(Decidim::TranslationsHelper) + end + + describe "#present" do + context "when the ballot style is created" do + let(:action) { :create } + + it "shows that the ballot style has been created" do + expect(subject.present).to include(resource.code) + expect(subject.present).to include("created a ballot style") + end + end + + context "when the ballot style is updated" do + let(:action) { :update } + + it "shows that the ballot style has been created" do + expect(subject.present).to include(resource.code) + expect(subject.present).to include("updated the ballot style") + end + end + + context "when the ballot style is deleted" do + let(:action) { :delete } + + before do + resource.destroy! + action_log.reload + end + + it "shows that the ballot style has been created" do + expect(subject.present).to include(resource.code) + expect(subject.present).to include("deleted the ballot style") + end + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter_spec.rb new file mode 100644 index 00000000..d1a2f0ce --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::AdminLog::MonitoringCommitteeMemberPresenter, type: :helper do + include_examples "present admin log entry" do + let(:admin_log_resource) { create(:monitoring_committee_member) } + let(:action) { "create" } + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/admin_log/polling_officer_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/admin_log/polling_officer_presenter_spec.rb new file mode 100644 index 00000000..7f9fb492 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/admin_log/polling_officer_presenter_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::AdminLog::PollingOfficerPresenter, type: :helper do + include_examples "present admin log entry" do + let(:admin_log_resource) { create(:polling_officer) } + let(:action) { "create" } + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/admin_log/polling_station_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/admin_log/polling_station_presenter_spec.rb new file mode 100644 index 00000000..b2dd8357 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/admin_log/polling_station_presenter_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::AdminLog::PollingStationPresenter, type: :helper do + include_examples "present admin log entry" do + let(:admin_log_resource) { create(:polling_station) } + let(:action) { "create" } + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/admin_log/voting_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/admin_log/voting_presenter_spec.rb new file mode 100644 index 00000000..a43b9400 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/admin_log/voting_presenter_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::AdminLog::VotingPresenter, type: :helper do + include_examples "present admin log entry" do + let(:admin_log_resource) { create(:voting, organization:) } + let(:action) { "unpublish" } + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/census/admin_log/dataset_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/census/admin_log/dataset_presenter_spec.rb new file mode 100644 index 00000000..3f63773c --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/census/admin_log/dataset_presenter_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::Census::AdminLog::DatasetPresenter, type: :helper do + include_examples "present admin log entry" do + let(:voting) { create(:voting, organization:) } + let(:admin_log_resource) { create(:dataset, voting:) } + let(:action) { "create" } + end + + include_examples "present admin log entry" do + let(:voting) { create(:voting, organization:) } + let(:admin_log_resource) { create(:dataset, voting:) } + let(:action) { "delete" } + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/voting_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/voting_presenter_spec.rb new file mode 100644 index 00000000..dfd51673 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/voting_presenter_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::VotingPresenter do + subject { described_class.new(voting) } + + let!(:voting) { create(:voting) } + let(:organization_host) { voting.organization.host } + + describe "when no images were uploaded" do + before do + voting.introductory_image.purge + voting.banner_image.purge + end + + it "return nil for introductory_image_url" do + expect(subject.introductory_image_url).to be_nil + end + + it "return nil for banner_image_url" do + expect(subject.banner_image_url).to be_nil + end + end + + describe "when images are attached" do + it "resolves introductory_image_url" do + expect(subject.introductory_image_url).to eq("http://#{organization_host}:#{Capybara.server_port}#{voting.attached_uploader(:introductory_image).path}") + end + + it "resolves banner_image_url" do + expect(subject.banner_image_url).to eq("http://#{organization_host}:#{Capybara.server_port}#{voting.attached_uploader(:banner_image).path}") + end + end + end +end diff --git a/decidim-elections/spec/presenters/decidim/votings/voting_stats_presenter_spec.rb b/decidim-elections/spec/presenters/decidim/votings/voting_stats_presenter_spec.rb new file mode 100644 index 00000000..0f98dd29 --- /dev/null +++ b/decidim-elections/spec/presenters/decidim/votings/voting_stats_presenter_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + describe Votings::VotingStatsPresenter do + subject { described_class.new(voting:) } + + let!(:organization) { create(:organization) } + let!(:user) { create(:user, :confirmed, organization:) } + let!(:voting) { create(:voting, organization:) } + let!(:component) { create(:component, participatory_space: voting) } + + describe "#collection" do + let(:manifest) do + Decidim::ComponentManifest.new.tap do |manifest| + manifest.name = "Test" + end + end + + before do + manifest.stats.register :foo, priority: StatsRegistry::HIGH_PRIORITY, &proc { 10 } + manifest.stats.register :bar, priority: StatsRegistry::HIGH_PRIORITY, &proc { 0 } + + I18n.backend.store_translations( + :en, + decidim: { + votings: { + statistics: { + foo: "Foo", + bar: "Bar" + } + } + } + ) + + allow(Decidim).to receive(:component_manifests).and_return([manifest]) + end + + it "return a collection of stats including stats title and value" do + data = subject.collection.first + expect(data).not_to be_nil + expect(data).to have_key(:stat_title) + expect(data).to have_key(:stat_number) + expect(data[:stat_title]).to eq :foo + expect(data[:stat_number]).to eq 10 + end + + it "does not return 0 values" do + data = subject.collection.second + expect(data).to be_nil + end + end + + describe "comments count stat" do + let(:manifest_proposals) do + Decidim::ComponentManifest.new.tap do |manifest| + manifest.name = "proposals" + end + end + + let(:manifest_meetings) do + Decidim::ComponentManifest.new.tap do |manifest| + manifest.name = "meetings" + end + end + + let(:manifest_budgets) do + Decidim::ComponentManifest.new.tap do |manifest| + manifest.name = "budgets" + end + end + + before do + manifest_meetings.stats.register :comments_count, tag: :comments, &proc { 10 } + manifest_proposals.stats.register :comments_count, tag: :comments, &proc { 5 } + manifest_budgets.stats.register :comments_count, tag: :comments, &proc { 3 } + + I18n.backend.store_translations( + :en, + decidim: { + participatory_processes: { + statistics: { + comments_count: "Comments" + } + } + } + ) + + allow(Decidim).to receive(:component_manifests).and_return([manifest_meetings, manifest_proposals, manifest_budgets]) + end + + it "return the sum of all the comments from proposals, meetings and budgets" do + data = subject.collection.first + expect(data).not_to be_nil + expect(data[:stat_title]).to eq :comments_count + expect(data[:stat_number]).to eq 18 + end + + it "contains only one stat" do + data = subject.collection.second + expect(data).to be_nil + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/elections/admin/pending_actions_spec.rb b/decidim-elections/spec/queries/decidim/elections/admin/pending_actions_spec.rb new file mode 100644 index 00000000..f3919eb3 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/elections/admin/pending_actions_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::PendingActions do + let(:pending_action) { create(:action) } + let(:accepted_action) { create(:action, status: "accepted") } + + it "returns only actions with pending status" do + expect(described_class.for).to match_array pending_action + expect(described_class.for).not_to match_array accepted_action + end +end diff --git a/decidim-elections/spec/queries/decidim/elections/admin/votes_for_statistics_spec.rb b/decidim-elections/spec/queries/decidim/elections/admin/votes_for_statistics_spec.rb new file mode 100644 index 00000000..54204e5a --- /dev/null +++ b/decidim-elections/spec/queries/decidim/elections/admin/votes_for_statistics_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Admin::VotesForStatistics do + subject { described_class.for(election) } + + let!(:election) { create(:election, :bb_test, :vote) } + let!(:user1) { create(:user, :confirmed) } + let!(:user2) { create(:user, :confirmed) } + let!(:user1_votes) { create_list(:vote, 3, election:, status: "accepted", voter_id: "voter_#{user1.id}") } + let!(:user2_votes) { create(:vote, election:, status: "rejected", voter_id: "voter_#{user2.id}") } + + describe "query" do + it "returns the id and voter id for accepted votes only" do + expect(subject).to eql([3, 1]) + end + end +end diff --git a/decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_spec.rb b/decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_spec.rb new file mode 100644 index 00000000..32fd8eec --- /dev/null +++ b/decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Trustees::ByParticipatorySpace do + let(:organization) { create(:organization) } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:assembly) { create(:assembly) } + let(:user) { create(:user, :confirmed) } + + let(:participatory_process_trustees) do + create_list(:trustee, 5) do |trustee| + trustee.trustees_participatory_spaces << build( + :trustees_participatory_space, + participatory_space: participatory_process + ) + end + end + + let(:assembly_trustees) do + create_list(:trustee, 5) do |trustee| + trustee.trustees_participatory_spaces << build( + :trustees_participatory_space, + participatory_space: assembly + ) + end + end + + it "returns trustees by participatory space" do + expect(described_class.new(assembly)).to match_array assembly_trustees + expect(described_class.new(assembly)).not_to match_array participatory_process_trustees + end +end diff --git a/decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_trustee_ids_spec.rb b/decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_trustee_ids_spec.rb new file mode 100644 index 00000000..54aec932 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/elections/trustees/by_participatory_space_trustee_ids_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Trustees::ByParticipatorySpaceTrusteeIds do + let(:organization) { create(:organization) } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:assembly) { create(:assembly) } + let(:user) { create(:user, :confirmed) } + + let(:participatory_process_trustees) do + create_list(:trustee, 5) do |trustee| + trustee.trustees_participatory_spaces << build( + :trustees_participatory_space, + participatory_space: participatory_process + ) + end + end + + let(:assembly_trustees) do + create_list(:trustee, 5) do |trustee| + trustee.trustees_participatory_spaces << build( + :trustees_participatory_space, + participatory_space: assembly + ) + end + end + + it "returns trustees by trustee ids" do + expect(described_class.new(assembly_trustees.pluck(:id))).to match_array assembly_trustees + expect(described_class.new(assembly_trustees.pluck(:id))).not_to match_array participatory_process_trustees + end +end diff --git a/decidim-elections/spec/queries/decidim/elections/votes/pending_votes_spec.rb b/decidim-elections/spec/queries/decidim/elections/votes/pending_votes_spec.rb new file mode 100644 index 00000000..9fbc1b3f --- /dev/null +++ b/decidim-elections/spec/queries/decidim/elections/votes/pending_votes_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Elections::Votes::PendingVotes do + let(:pending_vote) { create(:vote) } + let(:accepted_vote) { create(:vote, status: "accepted") } + + it "returns only votes with pending status" do + expect(described_class.for).to match_array pending_vote + expect(described_class.for).not_to match_array accepted_vote + end +end diff --git a/decidim-elections/spec/queries/decidim/elections/votes/user_election_last_vote_spec.rb b/decidim-elections/spec/queries/decidim/elections/votes/user_election_last_vote_spec.rb new file mode 100644 index 00000000..88930f5a --- /dev/null +++ b/decidim-elections/spec/queries/decidim/elections/votes/user_election_last_vote_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections::Votes + describe LastVoteForVoter do + subject { described_class.new(election, voter_id).query } + + let(:election) { create(:election) } + let(:voter_id) { "a voter id" } + + describe "when there are votes for the voter id and the election" do + let!(:last_voter_election_vote) { create(:vote, election:, voter_id:, created_at: 1.day.from_now) } + let!(:voter_election_votes) { create_list(:vote, 3, election:, voter_id:) } + let!(:recent_other_voters_vote) { create(:vote, election:, created_at: 3.days.from_now) } + let!(:other_voters_votes) { create_list(:vote, 2, election:) } + let!(:recent_other_elections_vote) { create(:vote, election:, created_at: 3.days.from_now) } + let!(:other_elections_votes) { create_list(:vote, 2, voter_id:) } + + it "gets the most recent voter's vote in the specified election" do + expect(subject).to eq(last_voter_election_vote) + end + end + + describe "when there are votes for the voter id but not for the election" do + let!(:other_elections_votes) { create_list(:vote, 2, voter_id:) } + + it "returns nil" do + expect(subject).to be_nil + end + end + + describe "when there are votes for other voter ids in the specified election" do + let!(:other_voters_votes) { create_list(:vote, 2, election:) } + + it "returns nil" do + expect(subject).to be_nil + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/votings/admin/admin_users_spec.rb b/decidim-elections/spec/queries/decidim/votings/admin/admin_users_spec.rb new file mode 100644 index 00000000..91dca9a8 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/votings/admin/admin_users_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Admin + describe AdminUsers do + subject { described_class.new(voting) } + + let(:organization) { create(:organization) } + let!(:voting) { create(:voting, organization:) } + let!(:admins) { create_list(:user, 3, :admin, :confirmed, organization:) } + let!(:normal_user) { create(:user, :confirmed, organization:) } + let!(:other_organization_user) { create(:user, :confirmed) } + + it "returns the organization admins" do + expect(subject.query).to match_array(admins) + end + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/votings/organization_prioritized_votings_spec.rb b/decidim-elections/spec/queries/decidim/votings/organization_prioritized_votings_spec.rb new file mode 100644 index 00000000..36fec0b2 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/votings/organization_prioritized_votings_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe OrganizationPrioritizedVotings do + subject { described_class.new(organization) } + + let!(:organization) { create(:organization) } + let!(:local_promoted_voting) do + create(:voting, + :promoted, + organization:) + end + + let!(:local_non_promoted_voting) do + create(:voting, + :published, + organization:) + end + + let!(:external_non_promoted_voting) do + create(:voting, :published) + end + + before { create(:voting) } + + describe "query" do + it "orders by promoted status first" do + expect(subject.to_a).to eq [ + local_promoted_voting, + local_non_promoted_voting + ] + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/votings/organization_promoted_votings_spec.rb b/decidim-elections/spec/queries/decidim/votings/organization_promoted_votings_spec.rb new file mode 100644 index 00000000..5e22b331 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/votings/organization_promoted_votings_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe OrganizationPromotedVotings do + subject { described_class.new(organization) } + + let!(:organization) { create(:organization) } + let!(:local_promoted_voting) do + create(:voting, + :promoted, + organization:) + end + + let!(:local_promoted_unpublished_voting) do + create(:voting, + :unpublished, + organization:) + end + + let!(:local_non_promoted_voting) do + create(:voting, + :published, + organization:) + end + + let!(:external_non_promoted_voting) do + create(:voting, :published) + end + + let!(:external_promoted_voting) do + create(:voting, :promoted) + end + + before { create(:voting) } + + describe "query" do + it "includes the organization's promoted votings" do + expect(subject).to include(*local_promoted_voting) + end + + it "excludes the organization's non promoted votings" do + expect(subject).not_to include(*local_non_promoted_voting) + end + + it "excludes the other organization's promoted votings" do + expect(subject).not_to include(*external_promoted_voting) + end + + it "excludes other organization's non promoted votings" do + expect(subject).not_to include(*external_non_promoted_voting) + end + + it "excludes the organization's promoted non published votings" do + expect(subject).not_to include(*local_promoted_unpublished_voting) + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/votings/organization_published_votings_spec.rb b/decidim-elections/spec/queries/decidim/votings/organization_published_votings_spec.rb new file mode 100644 index 00000000..6f65f641 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/votings/organization_published_votings_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe OrganizationPublishedVotings do + subject { described_class.new(organization) } + + let!(:organization) { create(:organization) } + + let!(:published_votings) do + create_list(:voting, 3, :published, organization:) + end + + let!(:unpublished_votings) do + create_list(:voting, 3, :unpublished, organization:) + end + + let!(:foreign_votings) do + create_list(:voting, 3, :published) + end + + describe "query" do + it "includes the organization's published votings" do + expect(subject).to include(*published_votings) + end + + it "excludes the organization's unpublished votings" do + expect(subject).not_to include(*unpublished_votings) + end + + it "excludes other organization's published votings" do + expect(subject).not_to include(*foreign_votings) + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/votings/organization_votings_spec.rb b/decidim-elections/spec/queries/decidim/votings/organization_votings_spec.rb new file mode 100644 index 00000000..c22291bd --- /dev/null +++ b/decidim-elections/spec/queries/decidim/votings/organization_votings_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + describe OrganizationVotings do + subject { described_class.new(organization).query } + + let!(:organization) { create(:organization) } + let!(:local_votings) do + create_list(:voting, 3, organization:) + end + + let!(:foreign_votings) do + create_list(:voting, 3) + end + + describe "query" do + it "includes the organization's votings" do + expect(subject).to include(*local_votings) + end + + it "excludes the foreign votings" do + expect(subject).not_to include(*foreign_votings) + end + end + end + end +end diff --git a/decidim-elections/spec/queries/decidim/votings/votes/in_person_vote_for_voter_spec.rb b/decidim-elections/spec/queries/decidim/votings/votes/in_person_vote_for_voter_spec.rb new file mode 100644 index 00000000..7abb8bf7 --- /dev/null +++ b/decidim-elections/spec/queries/decidim/votings/votes/in_person_vote_for_voter_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings::Votes + describe InPersonVoteForVoter do + subject { described_class.new(election, voter_id).query } + + let(:election) { create(:election) } + let(:voter_id) { "a voter id" } + + describe "when there is a vote for the voter id and the election" do + let!(:voter_election_vote) { create(:in_person_vote, :accepted, election:, voter_id:) } + let!(:other_voters_votes) { create_list(:in_person_vote, 2, :accepted, election:) } + let!(:other_elections_votes) { create_list(:in_person_vote, 2, :accepted, voter_id:) } + + it "gets the accepted voter's vote in the specified election" do + expect(subject).to eq(voter_election_vote) + end + end + + describe "when there are votes for the voter id but not accepted" do + let!(:rejected_election_vote) { create(:in_person_vote, :rejected, election:, voter_id:) } + let!(:other_elections_votes) { create_list(:in_person_vote, 2, :accepted, voter_id:) } + + it "returns nil" do + expect(subject).to be_nil + end + end + + describe "when there are rejected and accepted votes for the voter id in the specified election" do + let!(:voter_election_vote) { create(:in_person_vote, :accepted, election:, voter_id:) } + let!(:rejected_election_vote) { create(:in_person_vote, :rejected, election:, voter_id:) } + + it "gets the accepted voter's vote in the specified election" do + expect(subject).to eq(voter_election_vote) + end + end + end +end diff --git a/decidim-elections/spec/requests/election_search_spec.rb b/decidim-elections/spec/requests/election_search_spec.rb new file mode 100644 index 00000000..0c8a950c --- /dev/null +++ b/decidim-elections/spec/requests/election_search_spec.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "Election search" do + include Decidim::ComponentPathHelper + + subject { response.body } + + let(:component) { create(:component, manifest_name: "elections") } + let(:participatory_space) { component.participatory_space } + let(:organization) { participatory_space.organization } + let(:filter_params) { {} } + + let!(:active_election1) do + create( + :election, + :published, + :ongoing, + component:, + description: Decidim::Faker::Localized.literal("Chambray chia selvage hammock health goth.") + ) + end + let!(:active_election2) do + create( + :election, + :published, + :ongoing, + component:, + description: Decidim::Faker::Localized.literal("Tacos gentrify celiac mixtape.") + ) + end + let!(:upcoming_election) do + create( + :election, + :published, + :upcoming, + component:, + description: Decidim::Faker::Localized.literal("Selfies kale chips taxidermy adaptogen.") + ) + end + let!(:finished_election) do + create( + :election, + :published, + :finished, + component: + ) + end + let!(:unpublished_election) do + create( + :election, + :upcoming, + component: + ) + end + let!(:external_election) { create(:election) } + + let(:request_path) { Decidim::EngineRouter.main_proxy(component).elections_path } + + before do + get( + request_path, + params: { filter: filter_params }, + headers: { "HOST" => component.organization.host } + ) + end + + it "displays all active elections without any filters" do + expect(subject).to include(translated(active_election1.title)) + expect(subject).to include(translated(active_election2.title)) + expect(subject).not_to include(translated(upcoming_election.title)) + expect(subject).not_to include(translated(finished_election.title)) + expect(subject).not_to include(translated(unpublished_election.title)) + expect(subject).not_to include(translated(external_election.title)) + end + + context "when searching by text" do + let(:filter_params) { { search_text_cont: "mixtape" } } + + it "displays only the election containing the search_text" do + expect(subject).not_to include(translated(active_election1.title)) + expect(subject).to include(translated(active_election2.title)) + expect(subject).not_to include(translated(upcoming_election.title)) + expect(subject).not_to include(translated(finished_election.title)) + expect(subject).not_to include(translated(unpublished_election.title)) + expect(subject).not_to include(translated(external_election.title)) + end + end + + context "when searching by date" do + let(:filter_params) { { with_any_date: date } } + + context "and the date is active" do + let(:date) { %w(active) } + + it "only displays active elections" do + expect(subject).to include(translated(active_election1.title)) + expect(subject).to include(translated(active_election2.title)) + expect(subject).not_to include(translated(upcoming_election.title)) + expect(subject).not_to include(translated(finished_election.title)) + expect(subject).not_to include(translated(unpublished_election.title)) + expect(subject).not_to include(translated(external_election.title)) + end + end + + context "and the date is finished" do + let(:date) { %w(finished) } + + it "only displays finished elections" do + expect(subject).not_to include(translated(active_election1.title)) + expect(subject).not_to include(translated(active_election2.title)) + expect(subject).not_to include(translated(upcoming_election.title)) + expect(subject).to include(translated(finished_election.title)) + expect(subject).not_to include(translated(unpublished_election.title)) + expect(subject).not_to include(translated(external_election.title)) + end + end + + context "and the date is upcoming" do + let(:date) { %w(upcoming) } + + it "only displays upcoming elections" do + expect(subject).not_to include(translated(active_election1.title)) + expect(subject).not_to include(translated(active_election2.title)) + expect(subject).to include(translated(upcoming_election.title)) + expect(subject).not_to include(translated(finished_election.title)) + expect(subject).not_to include(translated(unpublished_election.title)) + expect(subject).not_to include(translated(external_election.title)) + end + end + + context "and the date is finished or upcoming" do + let(:date) { %w(finished upcoming) } + + it "only displays finished and upcoming elections" do + expect(subject).not_to include(translated(active_election1.title)) + expect(subject).not_to include(translated(active_election2.title)) + expect(subject).to include(translated(upcoming_election.title)) + expect(subject).to include(translated(finished_election.title)) + expect(subject).not_to include(translated(unpublished_election.title)) + expect(subject).not_to include(translated(external_election.title)) + end + end + end + + describe "#index" do + let(:url) { "http://#{component.organization.host + request_path}" } + + it "redirects to the index page" do + get( + url_to_root(request_path), + params: {}, + headers: { "HOST" => component.organization.host } + ) + expect(response["Location"]).to eq(url) + end + end + + private + + def url_to_root(url) + parts = url.split("/") + parts[0..-2].join("/") + end +end diff --git a/decidim-elections/spec/requests/voting_search_spec.rb b/decidim-elections/spec/requests/voting_search_spec.rb new file mode 100644 index 00000000..72817489 --- /dev/null +++ b/decidim-elections/spec/requests/voting_search_spec.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "Voting search" do + include Decidim::ComponentPathHelper + + subject { response.body } + + let(:organization) { create(:organization) } + let(:filter_params) { {} } + + let!(:published_voting) { create(:voting, :published, title: Decidim::Faker::Localized.localized { "Tacos gentrify celiac mixtape." }, organization:) } + let!(:upcoming_voting) { create(:voting, :upcoming, organization:) } + let!(:ongoing_voting) { create(:voting, :ongoing, organization:) } + let!(:finished_voting) { create(:voting, :finished, organization:) } + let!(:ongoing_online_voting) { create(:voting, :ongoing, :online, organization:) } + let!(:ongoing_in_person_voting) { create(:voting, :ongoing, :in_person, organization:) } + let!(:ongoing_hybrid_voting) { create(:voting, :ongoing, :hybrid, organization:) } + let!(:unpublished_voting) { create(:voting, :unpublished, organization:) } + let!(:external_voting) { create(:voting, :published) } + + let(:request_path) { decidim_votings.votings_path } + + before do + get( + request_path, + params: { filter: filter_params }, + headers: { "HOST" => organization.host } + ) + end + + it "displays all published votings within the organization without any filters" do + expect(subject).to include(translated(published_voting.title)) + expect(subject).to include(translated(upcoming_voting.title)) + expect(subject).to include(translated(ongoing_voting.title)) + expect(subject).to include(translated(finished_voting.title)) + expect(subject).to include(translated(ongoing_online_voting.title)) + expect(subject).to include(translated(ongoing_in_person_voting.title)) + expect(subject).to include(translated(ongoing_hybrid_voting.title)) + expect(subject).not_to include(translated(unpublished_voting.title)) + expect(subject).not_to include(translated(external_voting.title)) + end + + context "when searching by text" do + let(:filter_params) { { search_text_cont: "mixtape" } } + + it "displays only the votings containing the search_text" do + expect(subject).to include(translated(published_voting.title)) + expect(subject).not_to include(translated(upcoming_voting.title)) + expect(subject).not_to include(translated(ongoing_voting.title)) + expect(subject).not_to include(translated(finished_voting.title)) + expect(subject).not_to include(translated(ongoing_online_voting.title)) + expect(subject).not_to include(translated(ongoing_in_person_voting.title)) + expect(subject).not_to include(translated(ongoing_hybrid_voting.title)) + expect(subject).not_to include(translated(unpublished_voting.title)) + expect(subject).not_to include(translated(external_voting.title)) + end + end + + context "when searching by date" do + let(:filter_params) { { with_any_date: date } } + + context "and the date is active" do + let(:date) { %w(active) } + + it "only displays active votings" do + expect(subject).not_to include(translated(published_voting.title)) + expect(subject).not_to include(translated(upcoming_voting.title)) + expect(subject).to include(translated(ongoing_voting.title)) + expect(subject).not_to include(translated(finished_voting.title)) + expect(subject).to include(translated(ongoing_online_voting.title)) + expect(subject).to include(translated(ongoing_in_person_voting.title)) + expect(subject).to include(translated(ongoing_hybrid_voting.title)) + expect(subject).not_to include(translated(unpublished_voting.title)) + expect(subject).not_to include(translated(external_voting.title)) + end + end + + context "and the date is finished" do + let(:date) { %w(finished) } + + it "only displays finished votings" do + expect(subject).not_to include(translated(published_voting.title)) + expect(subject).not_to include(translated(upcoming_voting.title)) + expect(subject).not_to include(translated(ongoing_voting.title)) + expect(subject).to include(translated(finished_voting.title)) + expect(subject).not_to include(translated(ongoing_online_voting.title)) + expect(subject).not_to include(translated(ongoing_in_person_voting.title)) + expect(subject).not_to include(translated(ongoing_hybrid_voting.title)) + expect(subject).not_to include(translated(unpublished_voting.title)) + expect(subject).not_to include(translated(external_voting.title)) + end + end + + context "and the date is upcoming" do + let(:date) { %w(upcoming) } + + it "only displays upcoming votings" do + expect(subject).to include(translated(published_voting.title)) + expect(subject).to include(translated(upcoming_voting.title)) + expect(subject).not_to include(translated(ongoing_voting.title)) + expect(subject).not_to include(translated(finished_voting.title)) + expect(subject).not_to include(translated(ongoing_online_voting.title)) + expect(subject).not_to include(translated(ongoing_in_person_voting.title)) + expect(subject).not_to include(translated(ongoing_hybrid_voting.title)) + expect(subject).not_to include(translated(unpublished_voting.title)) + expect(subject).not_to include(translated(external_voting.title)) + end + end + + context "and the date is finished or upcoming" do + let(:date) { %w(finished upcoming) } + + it "only displays finished and upcoming votings" do + expect(subject).to include(translated(published_voting.title)) + expect(subject).to include(translated(upcoming_voting.title)) + expect(subject).not_to include(translated(ongoing_voting.title)) + expect(subject).to include(translated(finished_voting.title)) + expect(subject).not_to include(translated(ongoing_online_voting.title)) + expect(subject).not_to include(translated(ongoing_in_person_voting.title)) + expect(subject).not_to include(translated(ongoing_hybrid_voting.title)) + expect(subject).not_to include(translated(unpublished_voting.title)) + expect(subject).not_to include(translated(external_voting.title)) + end + end + end +end diff --git a/decidim-elections/spec/serializers/decidim/votings/census/datum_serializer_spec.rb b/decidim-elections/spec/serializers/decidim/votings/census/datum_serializer_spec.rb new file mode 100644 index 00000000..2a419fb0 --- /dev/null +++ b/decidim-elections/spec/serializers/decidim/votings/census/datum_serializer_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Census + describe DatumSerializer do + subject do + described_class.new(datum) + end + + let!(:datum) { create(:datum, :with_access_code) } + + describe "#serialize" do + let(:serialized) { subject.serialize } + + it "serializes the datum" do + expect(serialized).to include(full_name: datum.full_name) + expect(serialized).to include(full_address: datum.full_address) + expect(serialized).to include(postal_code: datum.postal_code) + expect(serialized).to include(access_code: datum.access_code) + end + end + end + end + end +end diff --git a/decidim-elections/spec/services/decidim/elections/current_user_vote_flow_spec.rb b/decidim-elections/spec/services/decidim/elections/current_user_vote_flow_spec.rb new file mode 100644 index 00000000..9f4d3a0e --- /dev/null +++ b/decidim-elections/spec/services/decidim/elections/current_user_vote_flow_spec.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Elections + describe CurrentUserVoteFlow do + subject(:vote_flow) { described_class.new(election, user) { allowed } } + + let(:election) { create(:election, **election_params) } + let(:election_params) do + { + id: 10_000, + created_at: Time.new(2000, 1, 1, 0, 0, 0, 0), + salt: "bcef77cedb2d1269dd52466873cf5d7695365b91111e9adf08583cfe95e6c607", + **election_params_changes + } + end + let(:election_params_changes) { {} } + let(:user) { create(:user, **user_params) } + let(:user_params) do + { + id: 10_000, + created_at: Time.new(2000, 1, 1, 0, 0, 0, 0), + organization: election.component.organization, + **user_params_changes + } + end + let(:user_params_changes) { {} } + let(:allowed) { true } + let(:valid_voter_id) { "43e9f7d91e3a075cb651ff0e82ac10ff62a1f406dcf32213e3ffce08d2f2f876" } + + describe "#voter_id" do + subject { vote_flow.voter_id } + + it { is_expected.to eq(valid_voter_id) } + + { + "e3108b0731043e94061fd4730b58c03328a04f90a37743e71aa777c7f0bfda8a" => { id: 10_001 }, + "6b2160caf1ff46ada67c43bba3c46396cde776dc5e00e468682824a83a836c14" => { created_at: Time.new(2000, 1, 1, 0, 0, 0, 1) } + }.each do |voter_id, user_changes| + context "when changing the user data with #{user_changes.to_json}" do + let(:user_params_changes) { user_changes } + + it { is_expected.to eq(voter_id) } + end + end + + { + "d28ecf10691236f9e991f149a9afe7dc7a006c761069ccb29052677de97ef853" => { id: 10_001 }, + "341d98eb8494e9a32fcc99dabb766e8b8dfc7ad0e7d6d6d68ebe2570a3454a70" => { created_at: Time.new(2000, 1, 1, 0, 0, 0, 1) }, + "459027d2c994a9491780d68ca5ef043e6419e8806c893b717cd2dcd5b32046c0" => { salt: "ccef77cedb2d1269dd52466873cf5d7695365b91111e9adf08583cfe95e6c607" } + }.each do |voter_id, election_changes| + context "when changing the election data with #{election_changes.to_json}" do + let(:election_params_changes) { election_changes } + + it { is_expected.to eq(voter_id) } + end + end + end + + describe "#voter_id_token" do + subject { vote_flow.voter_id_token(voter_id) } + + let(:voter_id) { nil } + + it { is_expected.to eq("b8489d9390b2b1886f28") } + + context "when receiving a voter id" do + let(:voter_id) { "2f222a608b5e43704bd7c22b8f582eeedb0cd64bc8b99a599d6028f3295ed90a" } + + it { is_expected.to eq("7137159cf614abe35d9a") } + end + end + + context "with time dependant validations" do + before { travel_to(now) } + + let(:now) { Time.new(2000, 1, 1, 0, 0, 0, 0) } + let(:valid_token) { "M3TF3yp1KNfxclpeKGkbvIsjezpKtETOu3iEniMxWkJ86Af0d2GQZB4Yx2PbZNE9WdfleiAYaVuRq+fiC179DzWc+NzlwsdaK6WHjBte2G9LcEr7XnOhIEVcPfLI3G9jdJkL+JTxPt2T3PQnHDNnNAvcCU2sf+bWwekECGzuEZHknpM605Y2qRQfZG78Y6F17pv7u7S0e+z/CzakCcTVwOphcf2x9n+8Sy/Of7zMPO+Rbrl2KImIfpetXSvuEMcH4g/T2omCvtDvDyCPR8e8jHvlp4fAdiDU8nRX28M/xa6Vkx15MjOVfcS/NqrNMU7IxWN+xXimaausObOSkuwgb2Jq0wtoXCcDiw/SgVdr1y+o+LzqfqX+gqFL7nAgQA96WJ2SVHDo0TLybTeiPBV1MQwM/gJbRyaIjvfMKt0Q0EkPkUfJxLbt/MtUizmitLUWVNsCwqJkkV3x--rMnBzxE1CoHEpKEB--IdLlo8iGMec4qhii0/Lzwg==" } + let(:invalid_token) { "A3TF3yp1KNfxclpeKGkbvIsjezpKtETOu3iEniMxWkJ86Af0d2GQZB4Yx2PbZNE9WdfleiAYaVuRq+fiC179DzWc+NzlwsdaK6WHjBte2G9LcEr7XnOhIEVcPfLI3G9jdJkL+JTxPt2T3PQnHDNnNAvcCU2sf+bWwekECGzuEZHknpM605Y2qRQfZG78Y6F17pv7u7S0e+z/CzakCcTVwOphcf2x9n+8Sy/Of7zMPO+Rbrl2KImIfpetXSvuEMcH4g/T2omCvtDvDyCPR8e8jHvlp4fAdiDU8nRX28M/xa6Vkx15MjOVfcS/NqrNMU7IxWN+xXimaausObOSkuwgb2Jq0wtoXCcDiw/SgVdr1y+o+LzqfqX+gqFL7nAgQA96WJ2SVHDo0TLybTeiPBV1MQwM/gJbRyaIjvfMKt0Q0EkPkUfJxLbt/MtUizmitLUWVNsCwqJkkV3x--rMnBzxE1CoHEpKEB--IdLlo8iGMec4qhii0/Lzwg==" } + + context "when a voter token was not received" do + it { expect(subject).not_to be_valid_received_data } + + it "generates a token with the token data" do + generated_data = vote_flow.send(:message_encryptor).decrypt_and_verify(subject.voter_token) + expect(generated_data).to eq(vote_flow.send(:voter_token_data).to_json) + end + end + + context "when a valid voter token was received" do + before { vote_flow.voter_from_token(voter_token: valid_token, voter_id: valid_voter_id) } + + it { expect(subject).to be_valid_received_data } + it { expect(subject.voter_token).to eq(valid_token) } + + context "when the voter token has expired" do + let(:now) { Time.new(2000, 1, 1, 3, 0, 0, 0) } + + it { expect(subject).not_to be_valid_received_data } + it { expect(subject.voter_token).to eq(valid_token) } + end + end + + context "when a wrong voter token was received" do + before { vote_flow.voter_from_token(voter_token: invalid_token, voter_id: valid_voter_id) } + + it { expect(subject).not_to be_valid_received_data } + it { expect(subject.voter_token).to eq(invalid_token) } + end + end + + describe "user based attributes and methods" do + it { expect(subject).to have_voter } + it { expect(subject.user).to eq(user) } + it { expect(subject.email).to eq(user.email) } + it { expect(subject.voter_name).to eq(user.name) } + it { expect(subject.voter_data).to eq(id: user.id, created: user.created_at.to_i) } + + context "when there is no user" do + let(:user) { nil } + + it { expect(subject).not_to have_voter } + it { expect(subject.user).to be_nil } + it { expect(subject.email).to be_nil } + it { expect(subject.voter_name).to be_nil } + it { expect(subject.voter_data).to be_nil } + end + end + + describe "#vote_check" do + subject { vote_flow.vote_check } + + it { expect(subject).to be_allowed } + + context "when there is no user" do + let(:user) { nil } + + it { expect(subject.error_message).to eq("You are not allowed to vote on this election at this moment.") } + end + + context "when the user is not authorized to vote" do + let(:allowed) { false } + + it { expect(subject.error_message).to eq("You are not allowed to vote on this election at this moment.") } + end + end + + describe "#login_path" do + subject { vote_flow.login_path("a vote path") } + + it { expect(subject).to be_nil } + end + + describe "#questions_for" do + subject { vote_flow.questions_for(election) } + + it { expect(subject).to match_array(election.questions) } + end + + describe "#ballot_style_id" do + subject { vote_flow.ballot_style_id } + + it { expect(subject).to be_nil } + end + end +end diff --git a/decidim-elections/spec/services/decidim/votings/census/access_codes_exporter_spec.rb b/decidim-elections/spec/services/decidim/votings/census/access_codes_exporter_spec.rb new file mode 100644 index 00000000..e941efca --- /dev/null +++ b/decidim-elections/spec/services/decidim/votings/census/access_codes_exporter_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Votings + module Census + describe AccessCodesExporter do + subject { AccessCodesExporter.new(dataset, tmp_file_in.path, password) } + + let(:tmp_file_in) { Tempfile.new(["access_codes", ".7z"]) } + let(:tmp_dir_out) { Dir.mktmpdir("access_codes_exporter_spec") } + let(:dataset) { create(:dataset, :with_access_code_data) } + let(:password) { "secret" } + let(:user) { create(:user) } + let(:expected_file) { "#{translated(dataset.voting.title).parameterize}-voting-access-codes.csv" } + + describe "#export" do + it "compresses a password protected file" do + subject.export + + files, data = open_7z_and_extract_zip(tmp_file_in.path) + + expect(files).to contain_exactly(expected_file) + dataset.data.each do |datum| + expect(data).to include(datum.full_name) + expect(data).to include(datum.full_address) + expect(data).to include(datum.postal_code) + expect(data).to include(datum.access_code) + end + end + end + + private + + def open_7z_and_extract_zip(file_path) + files = [] + data = nil + File.open(file_path, "rb") do |file| + SevenZipRuby::Reader.open_file(file, password:) do |szr| + files = szr.entries.map(&:path) + data = szr.extract_data(:all).join + end + end + + [files, data] + end + end + end + end +end diff --git a/decidim-elections/spec/services/decidim/votings/census_vote_flow_spec.rb b/decidim-elections/spec/services/decidim/votings/census_vote_flow_spec.rb new file mode 100644 index 00000000..8fc2e131 --- /dev/null +++ b/decidim-elections/spec/services/decidim/votings/census_vote_flow_spec.rb @@ -0,0 +1,235 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Votings + describe CensusVoteFlow do + subject(:vote_flow) { described_class.new(election) } + + let(:dataset) { create(:dataset, voting:) } + let(:election) { create(:election, component:, **election_params) } + let(:component) { create(:elections_component, participatory_space: voting) } + let(:voting) { create(:voting) } + let(:election_params) do + { + id: 10_000, + created_at: Time.new(2000, 1, 1, 0, 0, 0, 0), + salt: "bcef77cedb2d1269dd52466873cf5d7695365b91111e9adf08583cfe95e6c607", + **election_params_changes + } + end + let(:election_params_changes) { {} } + let!(:datum) { create(:datum, :with_access_code, **datum_params) } + let(:datum_params) do + { + id: 10_000, + created_at: Time.new(2000, 1, 1, 0, 0, 0, 0), + full_name: "A. Very Nice Name", + email: "voter@example.org", + document_type:, + document_number:, + birthdate:, + postal_code:, + access_code:, + dataset:, + **datum_params_changes + } + end + let(:datum_params_changes) { {} } + let(:document_type) { "passport" } + let(:document_number) { "00000001R" } + let(:birthdate) { Date.civil(1980, 1, 1) } + let(:postal_code) { "08001" } + let(:access_code) { "1234ABCD" } + + let(:login_params) do + { + document_type:, + document_number:, + postal_code:, + access_code:, + day: birthdate.day, + month: birthdate.month, + year: birthdate.year + } + end + let(:login_params_changes) { {} } + + let(:valid_voter_id) { "a2a7ad82b9c8bf690436a64459265fe8c7fc9a87ec4283fbbd3cd9363e0b3824" } + + it { expect(subject.user).to be_nil } + + describe "#voter_id" do + subject { vote_flow.voter_id } + + before { vote_flow.voter_login(login_params.merge(login_params_changes)) } + + it { is_expected.to eq(valid_voter_id) } + + { + "dc5e5238cc86efe4473e175605736f961b9ee66d884185d9bba6e5587022fbe8" => { id: 10_001 }, + "dc20d11e54b4038c831152aaa5ff8226f4d5aada47c7072c97c8d5a6ef126254" => { created_at: Time.new(2000, 1, 1, 0, 0, 0, 1) } + }.each do |voter_id, datum_changes| + context "when changing the user data with #{datum_changes.to_json}" do + let(:datum_params_changes) { datum_changes } + + it { is_expected.to eq(voter_id) } + end + end + + { + "a6f5aa84746e3db6602dbb424906afa8a1fd6ef75c9730db4def0a9be4f0a2bb" => { id: 10_001 }, + "ae0b3634b86fdee34a49ef7fbc88021573f0419c8833e20fea40b85641282e02" => { created_at: Time.new(2000, 1, 1, 0, 0, 0, 1) }, + "93b5789b43fe5fbd1c2975137a4b01cf6988126de570e9d7ffd9c6be507c6c2f" => { salt: "ccef77cedb2d1269dd52466873cf5d7695365b91111e9adf08583cfe95e6c607" } + }.each do |voter_id, election_changes| + context "when changing the election data with #{election_changes.to_json}" do + let(:election_params_changes) { election_changes } + + it { is_expected.to eq(voter_id) } + end + end + end + + describe "#voter_id_token" do + subject { vote_flow.voter_id_token(voter_id) } + + before { vote_flow.voter_login(login_params.merge(login_params_changes)) } + + let(:voter_id) { nil } + + it { is_expected.to eq("1af86ff1fb20cb203fd6") } + + context "when receiving a voter id" do + let(:voter_id) { "2f222a608b5e43704bd7c22b8f582eeedb0cd64bc8b99a599d6028f3295ed90a" } + + it { is_expected.to eq("7137159cf614abe35d9a") } + end + end + + context "with time dependant validations" do + before { travel_to(now) } + + let(:now) { Time.new(2000, 1, 1, 0, 0, 0, 0) } + let(:valid_token) { "IBSjHn9uZObqElyMIc+8kDdyoyFSp7Sk5Hotc6miRSdmRHEUpEqtoWnHxAw1WPYMnWjFHdqvnlmSvhLJ6nFTUCFuUQ/BTSP/B8esCC+S12WIkpkbV2KVUt7POTjUefvbL0tL2TgDrpnjVa1iYuEhzUeMHbqTiy3skZ1hicc9G6HkWjKzj+PEVDbe9lZi1n2e7O3myZSYDdNvjfu7JaKQ5ghUP7pY1PDoqVgKq5nyonUwY1Y+dr82gM1Qtz8unxLmhoDg07GIH1bvw2oeAvhZQMdo9IuV8VckDr+8eEAfZ9ugVnxRHPx6P8XKry/TDeeADuhqfd62x7UiLjx3FRMIvqoMQAvJACwbWmWGP0zWtrS/zk9au0Vlj7kHmOmpAwT+rkQHGkyI+mWruv0ynniFqDm9R/CMJSmowpWwi1nExiLLhPNrl7EqP1GrVJSsZIczt0PNA3keTrsAqZPuyFStwdaGS9lKv1n+5WFHT/Pp2bLDNSpSdaD0I/72n99R--anVGgqjxutnUceeM--Pir5PKYxQ4DBfaQ1GAmGzg==" } + let(:invalid_token) { "FBSjHn9uZObqElyMIc+8kDdyoyFSp7Sk5Hotc6miRSdmRHEUpEqtoWnHxAw1WPYMnWjFHdqvnlmSvhLJ6nFTUCFuUQ/BTSP/B8esCC+S12WIkpkbV2KVUt7POTjUefvbL0tL2TgDrpnjVa1iYuEhzUeMHbqTiy3skZ1hicc9G6HkWjKzj+PEVDbe9lZi1n2e7O3myZSYDdNvjfu7JaKQ5ghUP7pY1PDoqVgKq5nyonUwY1Y+dr82gM1Qtz8unxLmhoDg07GIH1bvw2oeAvhZQMdo9IuV8VckDr+8eEAfZ9ugVnxRHPx6P8XKry/TDeeADuhqfd62x7UiLjx3FRMIvqoMQAvJACwbWmWGP0zWtrS/zk9au0Vlj7kHmOmpAwT+rkQHGkyI+mWruv0ynniFqDm9R/CMJSmowpWwi1nExiLLhPNrl7EqP1GrVJSsZIczt0PNA3keTrsAqZPuyFStwdaGS9lKv1n+5WFHT/Pp2bLDNSpSdaD0I/72n99R--anVGgqjxutnUceeM--Pir5PKYxQ4DBfaQ1GAmGzg==" } + + context "when a voter token was not received" do + it { expect(subject).not_to be_valid_received_data } + + it "generates a token with the token data" do + generated_data = vote_flow.send(:message_encryptor).decrypt_and_verify(subject.voter_token) + expect(generated_data).to eq(vote_flow.send(:voter_token_data).to_json) + end + end + + context "when a valid voter token was received" do + before { vote_flow.voter_from_token(voter_token: valid_token, voter_id: valid_voter_id) } + + it { expect(subject).to have_voter } + it { expect(subject).to be_valid_received_data } + it { expect(subject.voter_token).to eq(valid_token) } + + context "when the voter token has expired" do + let(:now) { Time.new(2000, 1, 1, 3, 0, 0, 0) } + + it { expect(subject).not_to have_voter } + it { expect(subject).not_to be_valid_received_data } + it { expect(subject.voter_token).to eq(valid_token) } + end + end + + context "when a wrong voter token was received" do + before { vote_flow.voter_from_token(voter_token: invalid_token, voter_id: valid_voter_id) } + + it { expect(subject).not_to have_voter } + it { expect(subject).not_to be_valid_received_data } + it { expect(subject.voter_token).to eq(invalid_token) } + end + + describe "datum based attributes and methods" do + before { vote_flow.voter_from_token(voter_token: valid_token, voter_id: valid_voter_id) } + + it { expect(subject.email).to eq(datum.email) } + it { expect(subject.voter_name).to eq(datum.full_name) } + it { expect(subject.voter_data).to eq(id: datum.id, created: datum.created_at.to_i, name: datum.full_name) } + end + end + + context "when there is no datum for the given data" do + let(:datum) { nil } + + it { expect(subject).not_to have_voter } + it { expect(subject.email).to be_nil } + it { expect(subject.voter_name).to be_nil } + it { expect(subject.voter_data).to be_nil } + end + + describe "#vote_check" do + subject { vote_flow.vote_check } + + before { vote_flow.voter_login(login_params.merge(login_params_changes)) } + + it { expect(subject).to be_allowed } + + context "when the access code is invalid" do + let(:login_params_changes) { { access_code: "an invalid code" } } + + it { expect(subject.error_message).to eq("The given data does not match any voter.") } + end + + context "when the document type is invalid" do + let(:login_params_changes) { { document_type: "Passport" } } + + it { expect(subject.error_message).to eq("The given data does not match any voter.") } + end + + context "when the birthdate is invalid" do + let(:login_params_changes) { { day: 15 } } + + it { expect(subject.error_message).to eq("The given data does not match any voter.") } + end + end + + describe "#login_path" do + subject { vote_flow.login_path("a vote path") } + + it { expect(subject).to eq("/votings/#{voting.slug}/login?election_id=10000&vote_path=a+vote+path") } + end + + describe "#questions_for" do + subject { vote_flow.questions_for(election) } + + context "when the election has a ballot_style" do + let(:datum_params_changes) { { ballot_style:, dataset: } } + let(:ballot_style) { create(:ballot_style, :with_ballot_style_questions, election:, voting:) } + + it { expect(subject).to match_array(election.questions.first(2)) } + end + + context "when the election does NOT have a ballot_style" do + let(:datum_params_changes) { { dataset: } } + + it { expect(subject).to match_array(election.questions) } + end + end + + describe "#ballot_style_id" do + subject { vote_flow.ballot_style_id } + + before { vote_flow.voter_login(login_params.merge(login_params_changes)) } + + context "when the election has a ballot_style" do + let(:datum_params_changes) { { ballot_style:, dataset: } } + let(:ballot_style) { create(:ballot_style, voting:) } + + it { expect(subject).to eq(ballot_style.slug) } + end + + context "when the election does NOT have a ballot_style" do + let(:datum_params_changes) { { dataset: } } + + it { expect(subject).to be_nil } + end + end + end +end diff --git a/decidim-elections/spec/shared/admin_manages_election_shared_context.rb b/decidim-elections/spec/shared/admin_manages_election_shared_context.rb new file mode 100644 index 00000000..83fe1a43 --- /dev/null +++ b/decidim-elections/spec/shared/admin_manages_election_shared_context.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +shared_context "when admin manages elections" do + let(:manifest_name) { "elections" } + + include_context "when managing a component as an admin" do + let(:admin_component_organization_traits) { [:secure_context] } + end +end diff --git a/decidim-elections/spec/shared/admin_manages_voting_examples.rb b/decidim-elections/spec/shared/admin_manages_voting_examples.rb new file mode 100644 index 00000000..6f6eaef5 --- /dev/null +++ b/decidim-elections/spec/shared/admin_manages_voting_examples.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +shared_context "when admin managing a voting" do + let(:organization) { create(:organization) } + let(:user) { create(:user, :admin, :confirmed, organization:) } + + let(:image1_filename) { "city.jpeg" } + let(:image1_path) { Decidim::Dev.asset(image1_filename) } + let(:image2_filename) { "city2.jpeg" } + let(:image2_path) { Decidim::Dev.asset(image2_filename) } + let(:image3_filename) { "city3.jpeg" } + let(:image3_path) { Decidim::Dev.asset(image3_filename) } + let(:image_invalid_filename) { "Exampledocument.pdf" } + let(:image_invalid_path) { Decidim::Dev.asset(image_invalid_filename) } + + let!(:voting) { create(:voting, organization:) } +end diff --git a/decidim-elections/spec/shared/full_election_process_shared_context.rb b/decidim-elections/spec/shared/full_election_process_shared_context.rb new file mode 100644 index 00000000..f2f322e1 --- /dev/null +++ b/decidim-elections/spec/shared/full_election_process_shared_context.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +shared_context "when performing the whole process" do + include_context "with test bulletin board" + include_context "when admin manages elections" + + let(:trustee_keys) do + { + "Trustee 1" => File.read(Decidim::Dev.asset("public_key.jwk")), + "Trustee 2" => File.read(Decidim::Dev.asset("public_key2.jwk")), + "Trustee 3" => File.read(Decidim::Dev.asset("public_key3.jwk")) + } + end + let(:private_keys) do + [ + Decidim::Dev.asset("private_key.jwk"), + Decidim::Dev.asset("private_key2.jwk"), + Decidim::Dev.asset("private_key3.jwk") + ] + end + let(:election) { create(:election, :ready_for_setup, trustee_keys:, component: current_component) } +end + +module Decidim + module Elections + module FullElectionHelpers + def setup_election + election + + login_as user, scope: :user + visit_component_admin + + within find("tr", text: translated(election.title)) do + page.find(".action-icon--manage-steps").click + end + + click_button "Setup election" + + click_button "Start the key ceremony" + + within ".content.key_ceremony" do + expect(page).to have_content("Key ceremony") + end + + election.reload + end + + def download_election_keys(trustee_index) + trustee = access_trustee_zone(trustee_index) + + perform_key_ceremony_action + + click_link "Download keys" + + content = download_content("#{trustee.slug}-*.bak") + expect(content).to have_content(%(trusteeId":"#{trustee.slug})) + expect(content).to have_content('"status":2') + end + + def access_trustee_zone(trustee_index, upload_keys = true) # rubocop:disable Style/OptionalBooleanParameter + trustee = election.trustees.order(:id)[trustee_index] + + relogin_as trustee.user, scope: :user + visit decidim.decidim_elections_trustee_zone_path + expect(page).to have_content("Participant settings") + expect(page).to have_css("[aria-current='page']", text: "Trustee zone") + + if upload_keys + expect(page).to have_content("Upload your identification keys") + attach_file(private_keys[trustee_index]) do + click_button "Upload your identification keys" + end + expect(page).to have_content("You have been assigned to act as a Trustee in some of the elections celebrated in this platform.") + end + + expect(page).not_to have_content("Upload your identification keys") + + trustee + end + + def perform_key_ceremony_action + expect(page).to have_content("Elections") + + click_link "Perform action" + + expect(page).to have_content("Create election keys for #{translated(election.title, locale: :en)}") + expect(page).to have_css("#create_election", text: "Pending") + expect(page).to have_css("#key_ceremony-step_1", text: "Pending") + expect(page).to have_css("#key_ceremony-joint_election_key", text: "Pending") + + expect(page).to have_selector("#start:not(disabled)") + + sleep(1) + + click_button "Start" + + expect(page).to have_button(id: "start", disabled: true) + end + + def complete_key_ceremony(trustee_index) + trustee = access_trustee_zone(trustee_index, false) + + perform_key_ceremony_action + + expect(page).to have_content("Restore election keys for #{translated(election.title, locale: :en)}") + + attach_file(download_path("#{trustee.slug}-*.bak")) do + page.find_all(:xpath, "//*[normalize-space(text())='Upload election keys']").first.click + end + + expect(page).to have_css("#create_election", text: "Completed") + expect(page).to have_css("#key_ceremony-step_1", text: "Completed") + expect(page).to have_css("#key_ceremony-joint_election_key", text: "Completed") + expect(page).not_to have_button(id: "start") + expect(page).to have_link("Back") + + expect(page).to have_content("The election status is: key_ceremony_ended") + end + + def check_key_ceremony_completed(trustee_index) + access_trustee_zone(trustee_index, false) + + expect(page).to have_content("Elections") + + within "#trustee_zone table" do + expect(page).to have_content(translated(election.title, locale: :en)) + expect(page).to have_content("key_ceremony_ended") + expect(page).not_to have_link("Perform action") + end + end + end + end +end diff --git a/decidim-elections/spec/shared/manage_voting_attachment_collections_examples.rb b/decidim-elections/spec/shared/manage_voting_attachment_collections_examples.rb new file mode 100644 index 00000000..b2916f53 --- /dev/null +++ b/decidim-elections/spec/shared/manage_voting_attachment_collections_examples.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +shared_examples "manage voting attachment collections examples" do + let(:collection_for) { voting } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Attachments" + end + click_link "Folders" + end + + it_behaves_like "manage attachment collections examples" +end diff --git a/decidim-elections/spec/shared/manage_voting_attachments_examples.rb b/decidim-elections/spec/shared/manage_voting_attachments_examples.rb new file mode 100644 index 00000000..3dbf1797 --- /dev/null +++ b/decidim-elections/spec/shared/manage_voting_attachments_examples.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +shared_examples "manage voting attachments examples" do + let(:attached_to) { voting } + let(:attachment_collection) { create(:attachment_collection, collection_for: voting) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Attachments" + end + end + + it_behaves_like "manage attachments examples" +end diff --git a/decidim-elections/spec/shared/manage_voting_components_examples.rb b/decidim-elections/spec/shared/manage_voting_components_examples.rb new file mode 100644 index 00000000..b8a66715 --- /dev/null +++ b/decidim-elections/spec/shared/manage_voting_components_examples.rb @@ -0,0 +1,223 @@ +# frozen_string_literal: true + +shared_examples "manage voting components" do + before do + switch_to_host(organization.host) + login_as user, scope: :user + end + + describe "add a component" do + before do + visit decidim_admin_votings.components_path(voting) + + find("button[data-toggle=add-component-dropdown]").click + + within "#add-component-dropdown" do + find(".dummy").click + end + + within ".item__edit-form" do + fill_in_i18n( + :component_name, + "#component-name-tabs", + en: "My component", + ca: "La meva funcionalitat", + es: "Mi funcionalitat" + ) + + within ".global-settings" do + fill_in_i18n_editor( + :component_settings_dummy_global_translatable_text, + "#global-settings-dummy_global_translatable_text-tabs", + en: "Dummy Text" + ) + all("input[type=checkbox]").last.click + end + + within ".default-step-settings" do + fill_in_i18n_editor( + :component_default_step_settings_dummy_step_translatable_text, + "#default-step-settings-dummy_step_translatable_text-tabs", + en: "Dummy Text for Step" + ) + all("input[type=checkbox]").first.click + end + + click_button "Add component" + end + end + + it "is successfully created" do + expect(page).to have_admin_callout("successfully") + expect(page).to have_content("My component") + end + + context "and then edit it" do + before do + within find("tr", text: "My component") do + click_link "Configure" + end + end + + it "successfully displays initial values in the form" do + within ".global-settings" do + expect(all("input[type=checkbox]").last).to be_checked + end + + within ".default-step-settings" do + expect(all("input[type=checkbox]").first).to be_checked + end + end + + it "successfully edits it" do + click_button "Update" + + expect(page).to have_admin_callout("successfully") + end + end + end + + describe "edit a component" do + let(:component_name) do + { + en: "My component", + ca: "La meva funcionalitat", + es: "Mi funcionalitat" + } + end + + let!(:component) do + create(:component, name: component_name, participatory_space: voting) + end + + before do + visit decidim_admin_votings.components_path(voting) + end + + it "updates the component" do + within ".component-#{component.id}" do + click_link "Configure" + end + + within ".edit_component" do + fill_in_i18n( + :component_name, + "#component-name-tabs", + en: "My updated component", + ca: "La meva funcionalitat actualitzada", + es: "Mi funcionalidad actualizada" + ) + + within ".global-settings" do + all("input[type=checkbox]").last.click + end + + within ".default-step-settings" do + all("input[type=checkbox]").first.click + end + + click_button "Update" + end + + expect(page).to have_admin_callout("successfully") + expect(page).to have_content("My updated component") + + within find("tr", text: "My updated component") do + click_link "Configure" + end + + within ".global-settings" do + expect(all("input[type=checkbox]").last).to be_checked + end + + within ".default-step-settings" do + expect(all("input[type=checkbox]").first).to be_checked + end + end + end + + describe "remove a component" do + let(:component_name) do + { + en: "My component", + ca: "La meva funcionalitat", + es: "Mi funcionalitat" + } + end + + let!(:component) do + create(:component, name: component_name, participatory_space: voting) + end + + before do + visit decidim_admin_votings.components_path(voting) + end + + it "removes the component" do + within ".component-#{component.id}" do + click_link "Delete" + end + + expect(page).not_to have_content("My component") + end + end + + describe "publish and unpublish a component" do + let!(:component) do + create(:component, participatory_space: voting, published_at:) + end + + let(:published_at) { nil } + + before do + visit decidim_admin_votings.components_path(voting) + end + + context "when the component is unpublished" do + it "publishes the component" do + within ".component-#{component.id}" do + click_link "Publish" + end + + within ".component-#{component.id}" do + expect(page).to have_css(".action-icon--unpublish") + end + end + + it "notifies its followers" do + follower = create(:user, organization: voting.organization) + create(:follow, followable: voting, user: follower) + + within ".component-#{component.id}" do + click_link "Publish" + end + + expect(enqueued_jobs.last[:args]).to include("decidim.events.components.component_published") + end + + it_behaves_like "manage component share tokens" + end + + context "when the component is published" do + let(:published_at) { Time.current } + + it "unpublishes the component" do + within ".component-#{component.id}" do + click_link "Unpublish" + end + + within ".component-#{component.id}" do + expect(page).to have_css(".action-icon--publish") + end + end + end + end + + def participatory_space + voting + end + + def participatory_space_components_path(participatory_space) + decidim_admin_votings.components_path(participatory_space) + end +end diff --git a/decidim-elections/spec/shared/monitoring_committee_member_manages_voting_examples.rb b/decidim-elections/spec/shared/monitoring_committee_member_manages_voting_examples.rb new file mode 100644 index 00000000..e44075ea --- /dev/null +++ b/decidim-elections/spec/shared/monitoring_committee_member_manages_voting_examples.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +shared_context "when monitoring committee member manages voting" do + let(:organization) { create(:organization) } + let(:user) { create(:user, :confirmed, :admin_terms_accepted, organization:) } + let(:voting) { create(:voting, organization:) } + + let!(:monitoring_committee_member) { create(:monitoring_committee_member, user:, voting:) } +end diff --git a/decidim-elections/spec/shared/test_bulletin_board_shared_context.rb b/decidim-elections/spec/shared/test_bulletin_board_shared_context.rb new file mode 100644 index 00000000..38bf34ba --- /dev/null +++ b/decidim-elections/spec/shared/test_bulletin_board_shared_context.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +shared_context "with test bulletin board" do + before do |test| + unless test.metadata[:bulletin_board_reset] + Decidim::Elections.bulletin_board.reset_test_database + test.metadata[:bulletin_board_reset] = true + end + end +end diff --git a/decidim-elections/spec/shared/vote_examples.rb b/decidim-elections/spec/shared/vote_examples.rb new file mode 100644 index 00000000..c2b9d28e --- /dev/null +++ b/decidim-elections/spec/shared/vote_examples.rb @@ -0,0 +1,186 @@ +# frozen_string_literal: true + +shared_examples "does not allow to vote" do + it "does not allow clicking in the vote button" do + visit router.election_path(id: election.id) + + expect(page).not_to have_link("Vote") + end + + it "does not allow to access directly to the vote page" do + visit router.new_election_vote_path(election_id: election.id) + + expect(page).to have_content("You are not allowed to vote on this election at this moment.") + end +end + +shared_examples "allows admins to preview the voting booth" do + let(:user) { create(:user, :admin, :confirmed, organization: component.organization) } + + before do + visit router.election_path(id: election.id) + + click_link "Preview" + end + + it { uses_the_voting_booth } + + it "shows the preview alert" do + expect(page).to have_content("This is a preview of the voting booth.") + end +end + +shared_examples "does not allow admins to preview the voting booth" do + let(:user) { create(:user, :admin, :confirmed, organization: component.organization) } + + it "does not allow clicking the preview button" do + visit router.election_path(id: election.id) + + expect(page).not_to have_link("Preview") + end + + it "does not allow to access directly to the vote page" do + visit router.new_election_vote_path(election_id: election.id) + + expect(page).to have_content("You are not allowed to vote on this election at this moment.") + end +end + +def uses_the_voting_booth + selected_answers = [] + non_selected_answers = [] + + # shows a yes/no/abstention question: radio buttons, no random order, no extra information + question_step(1) do |question| + expect_not_valid + + select_answers(question, 1, selected_answers, non_selected_answers) + end + + # shows a projects question: checkboxes, 6 maximum selections, random order with extra information + question_step(2) do |question| + select_answers(question, 3, selected_answers, non_selected_answers) + + expect_valid + + check(translated(non_selected_answers.last.title), allow_label_click: true) + + expect_not_valid + + uncheck(translated(non_selected_answers.last.title), allow_label_click: true) + end + + # shows a candidates question: checkboxes, random order without extra information + question_step(3) do |question| + select_answers(question, 5, selected_answers, non_selected_answers) + end + + # shows a nota question: checkboxes, random order without extra information, nota checked + question_step(4) do |_question| + check(I18n.t("decidim.elections.votes.new.nota_option"), allow_label_click: true) + + expect(page).to have_selector("label[aria-disabled='true']").exactly(8).times + + expect_valid + end + + # confirm step + non_question_step("#step-confirm") do + expect(page).to have_content("Confirm your vote") + + selected_answers.each { |answer| expect(page).to have_i18n_content(answer.title) } + non_selected_answers.each { |answer| expect(page).not_to have_i18n_content(answer.title) } + + within "#edit-step-2" do + click_button("edit") + end + end + + # edit step 2 + question_step(2) do |question| + change_answer(question, selected_answers, non_selected_answers) + end + + question_step(3) + + question_step(4) + + # confirm step + non_question_step("#step-confirm") do + expect(page).to have_content("Confirm your vote") + + selected_answers.each { |answer| expect(page).to have_i18n_content(answer.title) } + non_selected_answers.each { |answer| expect(page).not_to have_i18n_content(answer.title) } + + click_button("Confirm") + end + + # cast ballot + non_question_step("#step-ballot_decision") do + click_button("Cast ballot") + end + + # confirmed vote page + expect(page).to have_content("Vote confirmed") + expect(page).to have_content("Your vote has been cast!") +end + +def question_step(number) + expect_only_one_step + within "#step-#{number - 1}" do + question = election.questions[number - 1] + + expect(page).to have_content("Question #{number} of 4") + expect(page).to have_i18n_content(question.title) + + yield question if block_given? + + click_button("Next") + end +end + +def non_question_step(id) + expect_only_one_step + within id do + yield + end +end + +def select_answers(question, number, selected, non_selected) + answers = question.answers.to_a + number.times do + answer = answers.delete(answers.sample) + selected << answer + if number == 1 + choose(translated(answer.title), allow_label_click: true) + else + check(translated(answer.title), allow_label_click: true) + end + end + non_selected.concat answers +end + +def change_answer(question, selected, non_selected) + new_answer = question.answers.select { |answer| non_selected.member?(answer) }.first + old_answer = question.answers.select { |answer| selected.member?(answer) }.first + + selected.delete(old_answer) + uncheck(translated(old_answer.title), allow_label_click: true) + non_selected << old_answer + + non_selected.delete(new_answer) + check(translated(new_answer.title), allow_label_click: true) + selected << new_answer +end + +def expect_only_one_step + expect(page).to have_selector('[id^="step"]:not([hidden])', count: 1) +end + +def expect_not_valid + expect(page).not_to have_button("Next") +end + +def expect_valid + expect(page).to have_button("Next") +end diff --git a/decidim-elections/spec/spec_helper.rb b/decidim-elections/spec/spec_helper.rb new file mode 100644 index 00000000..02141c4f --- /dev/null +++ b/decidim-elections/spec/spec_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "decidim/dev" + +ENV["ENGINE_ROOT"] = File.dirname(__dir__) + +Decidim::Dev.dummy_app_path = File.expand_path(File.join("..", "spec", "decidim_dummy_app")) + +require "decidim/dev/test/base_spec_helper" +require "decidim/forms/test" + +WebMock.disable_net_connect!(allow_localhost: true, allow: Decidim::BulletinBoard.bulletin_board_server) diff --git a/decidim-elections/spec/system/admin/admin_manages_answers_spec.rb b/decidim-elections/spec/system/admin/admin_manages_answers_spec.rb new file mode 100644 index 00000000..4622433f --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_answers_spec.rb @@ -0,0 +1,194 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages answers" do + let!(:proposals) { create_list(:proposal, 3, :accepted, component: origin_component) } + let!(:origin_component) { create(:proposal_component, participatory_space: current_component.participatory_space) } + let(:election) { create(:election, component: current_component) } + let(:question) { create(:question, election:) } + let(:answer) { create(:election_answer, question:) } + let(:manifest_name) { "elections" } + + include_context "when managing a component as an admin" + + before do + answer + switch_to_host(organization.host) + login_as user, scope: :user + visit_component_admin + + within find("tr", text: translated(election.title)) do + page.find(".action-icon--manage-questions").click + end + + within find("tr", text: translated(question.title)) do + page.find(".action-icon--manage-answers").click + end + end + + describe "importing proposals" do + it "imports proposals" do + page.find(".imports").click + click_on "Import proposals to answers" + + within ".import_proposals" do + select origin_component.name["en"], from: :proposals_import_origin_component_id + check :proposals_import_import_all_accepted_proposals + end + + click_button "Import proposals to answers" + + expect(page).to have_admin_callout("3 proposals successfully imported") + end + end + + describe "admin form" do + before { click_link "New answer" } + + it_behaves_like "having a rich text editor", "new_answer", "full" + end + + it "creates a new answer" do + click_link "New answer" + + within ".new_answer" do + fill_in_i18n( + :answer_title, + "#answer-title-tabs", + en: "My answer", + es: "Mi respuesta", + ca: "La meva resposta" + ) + fill_in_i18n_editor( + :answer_description, + "#answer-description-tabs", + en: "Long description", + es: "Descripción más larga", + ca: "Descripció més llarga" + ) + end + + within ".new_answer" do + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("Answer successfully created.") + + within "table" do + expect(page).to have_content("My answer") + end + end + + context "when the election was created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "cannot add a new answer" do + expect(page).not_to have_content("New answer") + end + end + + context "when max selections is higher than answers count" do + let(:question) { create(:question, election:, max_selections: 5) } + + it "shows alert" do + expect(page).to have_content("You need 4 more answer/s") + end + end + + describe "updating an answer" do + it "updates an answer" do + within find("tr", text: translated(answer.title)) do + page.find(".action-icon--edit").click + end + + within ".edit_answer" do + fill_in_i18n( + :answer_title, + "#answer-title-tabs", + en: "My new answer", + es: "Mi nueva respuesta", + ca: "La meva nova resposta" + ) + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("Answer successfully updated.") + + within "table" do + expect(page).to have_content("My new answer") + end + end + + context "when the election was created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "cannot update the answer" do + within find("tr", text: translated(answer.title)) do + expect(page).not_to have_selector(".action-icon--edit") + end + end + end + end + + describe "deleting an answer" do + it "deletes an answer" do + within find("tr", text: translated(answer.title)) do + accept_confirm do + page.find(".action-icon--remove").click + end + end + + # As there is more than one alert, we need to use have_content + expect(page).to have_content("Answer successfully deleted.") + + within "table" do + expect(page).not_to have_content(translated(answer.title)) + end + end + + context "when the election was created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "cannot delete the question" do + within find("tr", text: translated(answer.title)) do + expect(page).not_to have_selector(".action-icon--remove") + end + end + end + end + + context "when answer has votes" do + let!(:election) { create(:election, :tally_ended, component: current_component) } + let!(:question) { election.questions.first } + let!(:answer) { question.answers.first } + + it "shows the votes and selected columns" do + expect(page).to have_content("Votes") + expect(page).to have_content("Selected") + end + + it "can change selected status" do + within find("tr", text: translated(answer.title)) do + expect(page).to have_selector(".action-icon") + end + end + + it "toggles selected status" do + within find("tr", text: translated(answer.title)) do + expect(page).to have_content("Not selected") + end + + within find("tr", text: translated(answer.title)) do + click_link "Mark answer as selected" + end + + within find("tr", text: translated(answer.title)) do + expect(page).to have_content("Selected") + end + + expect(page).to have_admin_callout("Answer successfully selected") + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_election_steps_spec.rb b/decidim-elections/spec/system/admin/admin_manages_election_steps_spec.rb new file mode 100644 index 00000000..52e081dd --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_election_steps_spec.rb @@ -0,0 +1,371 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages election steps", :slow do + include Decidim::Elections::FullElectionHelpers + include_context "with test bulletin board" + include_context "when admin manages elections" + + describe "setup an election" do + let(:election) { create(:election, :ready_for_setup, component: current_component, title: { en: "English title", es: "" }) } + let(:bulletin_board_server) { Decidim::BulletinBoard.config[:bulletin_board_server] } + let(:authority_name) { Decidim::BulletinBoard.config[:authority_name] } + let(:scheme_name) { Decidim::BulletinBoard.config[:scheme_name] } + + it "shows the election technical information" do + visit_steps_page + click_link "View technical information" + + within ".form.step.create_election" do + expect(page).to have_content("Bulletin Board server") + expect(page).to have_content(bulletin_board_server) + expect(page).to have_content("Authority name") + expect(page).to have_content(authority_name) + expect(page).to have_content("Scheme name") + expect(page).to have_content(scheme_name) + end + end + + it "performs the action successfully" do + visit_steps_page + + within "form.create_election" do + expect(page).to have_content("The election has at least 1 question.") + expect(page).to have_content("Each question has at least 2 answers.") + expect(page).to have_content("All the questions have a correct value for maximum of answers.") + expect(page).to have_content("The election is published.") + expect(page).to have_content("The election component is published.") + expect(page).to have_content("The setup is being done at least 1 hour before the election starts.") + expect(page).to have_content("The participatory space has at least 3 trustees with public key.") + expect(page).to have_content("has a public key", minimum: 2) + expect(page).not_to have_content("Census is uploaded.") + expect(page).not_to have_content("Census codes are generated.") + expect(page).not_to have_content("Codes are exported and census is frozen.") + + click_button "Setup election" + end + + expect(page).to have_admin_callout("successfully") + + within ".form.created" do + expect(page).to have_content("Election created") + expect(page).to have_content("Start the key ceremony") + end + end + + context "when election is not ready for setup" do + let(:election) { create(:election, component: current_component, start_time: 5.minutes.ago) } + let(:router) { Decidim::EngineRouter.admin_proxy(component) } + let(:errors_and_links) do + { + "The election must have at least one question.": router.election_questions_path(election), + "Questions must have at least two answers.": router.election_questions_path(election), + "The questions do not have a correct value for amount of answers": router.election_questions_path(election), + "The election is not published.": router.publish_election_path(election), + "The start time is in less than 1 hour before the election starts.": router.edit_election_path(election), + "The participatory space must have at least 3 trustees with public key.": router.trustees_path + } + end + + before do + visit_steps_page + end + + it "shows all the error texts and links to fix them" do + within("form.create_election") do + errors_and_links.each do |error_text, link| + expect(page).to have_content(error_text) + li = find("li", text: error_text) + expect(li).to have_link("Fix it", href: link) + end + end + end + end + + context "when census is required" do + let!(:voting) { create(:voting, organization:) } + let(:participatory_space) { voting } + + it "shows invalid census messages" do + visit_steps_page + + within "form.create_election" do + expect(page).to have_content("The election has at least 1 question.") + expect(page).to have_content("Each question has at least 2 answers.") + expect(page).to have_content("All the questions have a correct value for maximum of answers.") + expect(page).to have_content("The election is published.") + expect(page).to have_content("The election component is published.") + expect(page).to have_content("The setup is being done at least 1 hour before the election starts.") + expect(page).to have_content("The participatory space has at least 3 trustees with public key.") + expect(page).to have_content("has a public key", minimum: 2) + expect(page).to have_content("There is no census uploaded for this election.") + expect(page).to have_content("Access codes for the census are not generated.") + expect(page).to have_content("Access codes for the census are not exported.") + expect(page).not_to have_content("Fix it") + end + end + + context "with valid census" do + let!(:dataset) { create(:dataset, :codes_generated, :frozen, voting:) } + + it "shows valid census messages" do + visit_steps_page + + within "form.create_election" do + expect(page).to have_content("Census is uploaded.") + expect(page).to have_content("Access codes for the census are generated.") + expect(page).to have_content("Access codes for the census are exported and census is frozen.") + end + end + end + end + end + + describe "start the key ceremony" do + let(:election) { create(:election, :bb_test, :created, component: current_component) } + + it "performs the action successfully" do + visit_steps_page + + within ".form.created" do + expect(page).to have_content("Trustees") + + click_button "Start the key ceremony" + end + + expect(page).to have_admin_callout("successfully") + + within ".form.created" do + expect(page).to have_content("Processing...") + end + + within ".content.key_ceremony" do + expect(page).to have_content("Key ceremony") + end + end + end + + describe "view key ceremony step", :slow, download: true do + include_context "when performing the whole process" + + it "shows the step information" do + setup_election + + visit_steps_page + expect(page).to have_content("Key ceremony") + expect(page).to have_css(".loading") # It shows the loading icon + expect(page).not_to have_css(".active") # The trustees did not participate yet + expect(page).to have_link("Continue", class: "disabled") + + download_election_keys(0) + download_election_keys(1) + download_election_keys(2) + + visit_steps_page + expect(page).to have_content("Key ceremony") + expect(page).not_to have_css(".loading") # It is not waiting for any trustee + expect(page).to have_css(".active") # All the trustees are active + expect(page).not_to have_link("Continue", class: "disabled") + expect(page).to have_link("Continue") + end + end + + describe "start the voting period" do + let(:election) { create(:election, :bb_test, :key_ceremony_ended, component: current_component) } + + it "performs the action successfully" do + visit_steps_page + + within ".form.key_ceremony_ended" do + expect(page).to have_content("The election will start soon.") + + click_button "Start voting period" + end + + expect(page).to have_admin_callout("successfully") + + within ".form.key_ceremony_ended" do + expect(page).to have_content("Processing...") + end + + within ".form.vote" do + expect(page).to have_content("Vote period") + end + end + end + + describe "voting period" do + let(:election) { create(:election, :bb_test, :vote, component: current_component) } + + context "with no vote statistics" do + it "shows text about vote statistics" do + visit_steps_page + + within "#vote-stats" do + expect(page).to have_content("Vote Statistics") + expect(page).to have_content("No vote statistics yet") + end + end + end + + context "with vote statistics" do + let!(:user1) { create(:user, :confirmed) } + let!(:user2) { create(:user, :confirmed) } + let!(:user1_votes) { create_list(:vote, 3, election:, status: "accepted", voter_id: "voter_#{user1.id}") } + let!(:user2_votes) { create(:vote, election:, status: "accepted", voter_id: "voter_#{user2.id}") } + + it "shows votes and unique voters" do + visit_steps_page + + within "#vote-stats" do + expect(page).to have_content("Votes") + expect(page).to have_content("Voters") + + votes = find(:xpath, '//*[@id="vote-stats"]/div/div[2]/table/tbody/tr/td[2]') + expect(votes).to have_content("4") + + voters = find(:xpath, '//*[@id="vote-stats"]/div/div[2]/table/tbody/tr/td[3]') + expect(voters).to have_content("2") + end + end + end + end + + describe "end the voting period" do + let(:election) { create(:election, :bb_test, :vote, :finished, component: current_component) } + + it "performs the action successfully" do + visit_steps_page + + within ".form.vote" do + expect(page).to have_content("The election has ended.") + + click_button "End voting period" + end + + expect(page).to have_admin_callout("successfully") + + within ".form.vote" do + expect(page).to have_content("Processing...") + end + + within ".form.vote_ended" do + expect(page).to have_content("Start tally") + end + end + end + + describe "start the tally" do + let(:election) { create(:election, :bb_test, :vote_ended, component: current_component) } + + it "performs the action successfully" do + visit_steps_page + + within ".form.vote_ended" do + expect(page).to have_content("Vote period ended") + + click_button "Start tally" + end + + expect(page).to have_admin_callout("successfully") + + within ".form.vote_ended" do + expect(page).to have_content("Processing...") + end + + within ".form.tally_started" do + expect(page).to have_content("Tally process") + end + end + end + + describe "report missing trustee" do + let(:election) { create(:election, :bb_test, :tally_started, component: current_component) } + let(:trustee) { election.trustees.first } + + it "marks the trustee as missing" do + visit_steps_page + + # allows admin to mark trustees as missing + expect(page).to have_button(text: "Mark as missing") + + within find("tr", text: trustee.name) do + click_button "Mark as missing" + end + + expect(page).to have_admin_callout("successfully") + + # shows the trustee as missing + within find("tr", text: trustee.name) do + expect(page).to have_css(".missing") + end + + # do not allow to mark more trustees as missing + expect(page).not_to have_button(text: "Mark as missing") + end + end + + describe "tally ended" do + let(:election) { create(:election, :tally_ended, component: current_component) } + let(:question) { election.questions.first } + let(:answer) { question.answers.first } + + it "shows the calculated results" do + visit_steps_page + + within ".form.tally_ended" do + expect(page).to have_content("Calculated results") + expect(page).to have_content(translated(question.title)) + expect(page).to have_content(translated(answer.title)) + expect(page).to have_content(answer.results_total) + end + end + end + + describe "publishing results" do + let(:election) { create(:election, :bb_test, :tally_ended, component: current_component) } + let(:question) { election.questions.first } + let(:answer) { question.answers.first } + + it "performs the action successfully" do + visit_steps_page + + within ".form.tally_ended" do + expect(page).to have_content("Calculated results") + + click_button "Publish results" + end + + expect(page).to have_admin_callout("successfully") + + within ".form.tally_ended" do + expect(page).to have_content("Processing...") + end + + within ".content.results_published" do + expect(page).to have_content("Results published") + end + end + end + + def visit_steps_page + election + + relogin_as user, scope: :user + visit_component_admin + + within find("tr", text: translated(election.title)) do + page.find(".action-icon--manage-steps").click + end + + # Ensure the correct page is loaded before proceeding further + if election.bb_status.nil? + within ".form.step.create_election .card .card-divider", match: :first do + expect(page).to have_css(".card-title", text: "Setup election") + end + else + expect(page).to have_css(".item_show__header .item_show__header-title", text: translated(election.title)) + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_elections_spec.rb b/decidim-elections/spec/system/admin/admin_manages_elections_spec.rb new file mode 100644 index 00000000..3172369e --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_elections_spec.rb @@ -0,0 +1,221 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages elections" do + let(:election) { create(:election, :upcoming, :published, component: current_component) } + let(:questionnaire) { election.questionnaire } + let(:manifest_name) { "elections" } + + include_context "when managing a component as an admin" + + before do + election + switch_to_host(organization.host) + login_as user, scope: :user + visit_component_admin + end + + it_behaves_like "manage announcements" + + it_behaves_like "manage questionnaires" do + let(:election) { create(:election, :ongoing, :published, component: current_component) } + end + + describe "admin form" do + before { click_on "New election" } + + it_behaves_like "having a rich text editor", "new_election", "full" + end + + describe "creating an election" do + it "creates a new election" do + click_link "New election" + + within ".new_election" do + fill_in_i18n( + :election_title, + "#election-title-tabs", + en: "My election", + es: "Mi elección", + ca: "La meva elecció" + ) + fill_in_i18n_editor( + :election_description, + "#election-description-tabs", + en: "Long description", + es: "Descripción más larga", + ca: "Descripció més llarga" + ) + end + + expect(page).to have_content("Check that the organization time zone is correct") + expect(page).to have_content("The current configuration is UTC") + + fill_in :election_start_time, with: Time.current.change(day: 12, hour: 10, min: 50) + fill_in :election_end_time, with: Time.current.change(day: 12, hour: 12, min: 50) + + within ".new_election" do + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("Election successfully created") + + within "table" do + expect(page).to have_content("My election") + end + end + + context "when the organization has a different time zone" do + let(:organization) { create(:organization, time_zone: "Madrid") } + + it "shows the correct time zone" do + click_link "New election" + + expect(page).to have_content("Check that the organization time zone is correct") + expect(page).to have_content("The current configuration is Madrid") + end + end + end + + describe "updating an election" do + let(:election) { create(:election, :published, component: current_component) } + + it "updates an election" do + within find("tr", text: translated(election.title)) do + page.find(".action-icon--edit").click + end + + within ".edit_election" do + fill_in_i18n( + :election_title, + "#election-title-tabs", + en: "My new title", + es: "Mi nuevo título", + ca: "El meu nou títol" + ) + + expect(page).to have_content("Check that the organization time zone is correct") + expect(page).to have_content("The current configuration is UTC") + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("Election successfully updated") + + within "table" do + expect(page).to have_content("My new title") + end + end + + context "when the organization has a different time zone" do + let(:organization) { create(:organization, time_zone: "Madrid") } + + it "shows the correct time zone" do + within find("tr", text: translated(election.title)) do + page.find(".action-icon--edit").click + end + + expect(page).to have_content("Check that the organization time zone is correct") + expect(page).to have_content("The current configuration is Madrid") + end + end + end + + describe "previewing elections" do + it "links the election correctly" do + link = find("a[title=Preview]") + expect(link[:href]).to include(resource_locator(election).path) + end + end + + describe "publishing an election" do + context "when the election is unpublished" do + let!(:election) { create(:election, :complete, component: current_component) } + + it "publishes the election" do + within find("tr", text: translated(election.title)) do + page.find(".action-icon--publish").click + end + + expect(page).to have_admin_callout("The election has been successfully published") + + within find("tr", text: translated(election.title)) do + expect(page).not_to have_selector(".action-icon--publish") + end + end + end + end + + describe "unpublishing an election" do + let!(:election) { create(:election, :published, :ready_for_setup, component: current_component) } + + it "unpublishes an election" do + within find("tr", text: translated(election.title)) do + page.find(".action-icon--unpublish").click + end + + expect(page).to have_admin_callout("The election has been successfully unpublished") + + within find("tr", text: translated(election.title)) do + expect(page).not_to have_selector(".action-icon--unpublish") + end + end + + context "when the election is ongoing" do + let!(:election) { create(:election, :started, component: current_component) } + + it "cannot unpublish the election" do + within find("tr", text: translated(election.title)) do + expect(page).not_to have_selector(".action-icon--unpublish") + end + end + end + + context "when the election is published and has finished" do + let!(:election) { create(:election, :published, :finished, component: current_component) } + + it "cannot unpublish the election" do + within find("tr", text: translated(election.title)) do + expect(page).not_to have_selector(".action-icon--unpublish") + end + end + end + end + + describe "deleting an election" do + let!(:election) { create(:election, component: current_component) } + + it "deletes an election" do + within find("tr", text: translated(election.title)) do + accept_confirm do + page.find(".action-icon--remove").click + end + end + + expect(page).to have_admin_callout("Election successfully deleted") + + within "table" do + expect(page).not_to have_content(translated(election.title)) + end + end + + context "when the election has created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "cannot delete the election" do + within find("tr", text: translated(election.title)) do + expect(page).not_to have_selector(".action-icon--remove") + end + end + end + end + + def questionnaire_edit_path + Decidim::EngineRouter.admin_proxy(current_component).edit_feedback_form_path(id: election.id) + end + + def questionnaire_public_path + Decidim::EngineRouter.main_proxy(current_component).election_feedback_path(election) + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_questions_spec.rb b/decidim-elections/spec/system/admin/admin_manages_questions_spec.rb new file mode 100644 index 00000000..630464fc --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_questions_spec.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages questions" do + let(:election) { create(:election, component: current_component) } + let(:question) { create(:question, election:) } + let(:manifest_name) { "elections" } + + include_context "when managing a component as an admin" + + before do + question + switch_to_host(organization.host) + login_as user, scope: :user + visit_component_admin + + within find("tr", text: translated(election.title)) do + page.find(".action-icon--manage-questions").click + end + end + + it "creates a new question" do + click_on "New question" + + within ".new_question" do + fill_in_i18n( + :question_title, + "#question-title-tabs", + en: "My question", + es: "Mi pregunta", + ca: "La meva pregunta" + ) + end + + within ".new_question" do + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("Question successfully created.") + + within "table" do + expect(page).to have_content("My question") + end + end + + context "when the election has been created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "does not create a new question" do + visit Decidim::EngineRouter.admin_proxy(component).new_election_question_path(election) + + expect(page).to have_admin_callout("You are not authorized to perform this action") + end + + it "cannot add a new question" do + expect(page).not_to have_content("New Question") + end + end + + describe "updating a question" do + it "updates a question" do + within find("tr", text: translated(question.title)) do + page.find(".action-icon--edit").click + end + + within ".edit_question" do + fill_in_i18n( + :question_title, + "#question-title-tabs", + en: "My new question", + es: "Mi nueva pregunta", + ca: "La meva nova pregunta" + ) + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("Question successfully updated.") + + within "table" do + expect(page).to have_content("My new question") + end + end + + context "when the election has created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "cannot update the question" do + within find("tr", text: translated(question.title)) do + expect(page).not_to have_selector(".action-icon--edit") + end + end + end + end + + describe "deleting a question" do + it "deletes a question" do + within find("tr", text: translated(question.title)) do + accept_confirm do + page.find(".action-icon--remove").click + end + end + + expect(page).to have_admin_callout("Question successfully deleted.") + + within "table" do + expect(page).not_to have_content(translated(question.title)) + end + end + + context "when the election has created on the bulletin board" do + let(:election) { create(:election, :created, component: current_component) } + + it "cannot delete the question" do + within find("tr", text: translated(question.title)) do + expect(page).not_to have_selector(".action-icon--remove") + end + end + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_trustees_spec.rb b/decidim-elections/spec/system/admin/admin_manages_trustees_spec.rb new file mode 100644 index 00000000..e0c99a62 --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_trustees_spec.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages trustees" do + let(:manifest_name) { "elections" } + + include_context "when managing a component as an admin" + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit_component_admin + within_admin_sidebar_menu do + click_link "Trustees" + end + end + + context "without existing trustee" do + it "creates a new trustee" do + click_link "New trustee" + + within ".new_trustee" do + autocomplete_select "#{user.name} (@#{user.nickname})", from: :user_id + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#trustees table" do + expect(page).to have_content(user.name.to_s) + expect(page).to have_content(user.email.to_s) + end + end + end + + context "when paginating" do + let!(:collection_size) { 20 } + let!(:collection) do + create_list(:trustee, collection_size) do |trustee| + trustee.trustees_participatory_spaces << build( + :trustees_participatory_space, + participatory_space: + ) + end + end + + let!(:resource_selector) { "#trustees tbody tr" } + + before do + visit current_path + end + + it "lists 15 trustees per page by default" do + expect(page).to have_css(resource_selector, count: 15) + expect(page).to have_css("[data-pages] [data-page]", count: 2) + click_link "Next" + + expect(page).to have_selector("[data-pages] [data-page][aria-current='page']", text: "2") + + expect(page).to have_css(resource_selector, count: 5) + end + end + + context "when updating status" do + let!(:trustees) do + create_list(:trustee, 4) do |trustee| + trustee.trustees_participatory_spaces << build( + :trustees_participatory_space, + participatory_space: + ) + end + end + + before do + visit current_path + end + + it "toggles considered status" do + first("a.action-icon--edit").click + + within "#trustees table" do + expect(page).to have_content("inactive") + end + end + end + + context "when removing trustee from participatory space" do + let!(:trustee_participatory_space) { create(:trustees_participatory_space, participatory_space:) } + + before do + visit current_path + end + + it "removes trustee" do + accept_confirm do + page.first("a.action-icon--remove").click + end + + expect(page).to have_admin_callout("successfully") + + within "#trustees table" do + expect(page).not_to have_content(trustee_participatory_space.trustee.user.name) + end + end + end + + context "when inside an assembly" do + let(:participatory_space) { create(:assembly, organization:) } + + it "shows the trustees page" do + expect(page).to have_content("New trustee") + end + end + + context "when inside a voting" do + let(:participatory_space) { create(:voting, organization:) } + + it "shows the trustees page" do + expect(page).to have_content("New trustee") + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_attachment_collections_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_attachment_collections_spec.rb new file mode 100644 index 00000000..40803acd --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_attachment_collections_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages voting attachment collections" do + include_context "when admin managing a voting" + + it_behaves_like "manage voting attachment collections examples" +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_attachments_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_attachments_spec.rb new file mode 100644 index 00000000..6fbaf650 --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_attachments_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages voting attachments" do + include_context "when admin managing a voting" + + it_behaves_like "manage voting attachments examples" +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_ballot_styles_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_ballot_styles_spec.rb new file mode 100644 index 00000000..200d7cba --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_ballot_styles_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages ballot styles" do + let(:address) { "Somewhere over the rainbow" } + let(:latitude) { 42.123 } + let(:longitude) { 2.123 } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Ballot Styles" + end + end + + include_context "when admin managing a voting" + + context "when processing the ballot styles" do + let!(:ballot_style) { create(:ballot_style, :with_ballot_style_questions, voting:) } + + before do + visit current_path + end + + context "when listing the ballot styles" do + it "lists all the ballot styles for the voting" do + within "#ballot_styles table" do + expect(page).to have_content(ballot_style.code) + each_question do |question| + expect(page).to have_content(translated(question.title).slice(0, 2)) + expect(page).to have_content(translated(question.election.title)) + end + end + end + end + + it "can add a ballot style" do + click_link("New ballot style") + + within ".new_ballot_style" do + fill_in :ballot_style_code, with: "new code" + + check translated(ballot_style.questions.sample.title) + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#ballot_styles table" do + expect(page).to have_text("NEW CODE") + expect(page).to have_selector(".ballot-style__question--checked", count: ballot_style.questions.count + 1) + end + end + + it "can delete a ballot style" do + within find("tr", text: ballot_style.code) do + accept_confirm { click_link "Delete" } + end + + expect(page).to have_admin_callout("successfully") + + expect(page).not_to have_content(ballot_style.code) + end + + it "can update a ballot style" do + within "#ballot_styles" do + within find("tr", text: ballot_style.code) do + click_link "Edit" + end + end + + within ".edit_ballot_style" do + fill_in :ballot_style_code, with: "updated code" + + each_question do |question| + uncheck translated(question.title) + end + + check translated(ballot_style.questions.sample.title) + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#ballot_styles table" do + expect(page).to have_text("UPDATED CODE") + expect(page).to have_selector(".ballot-style__question--checked", count: 1) + end + end + end + + def each_question + ballot_style.questions.each do |question| + yield question + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_census_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_census_spec.rb new file mode 100644 index 00000000..9a25edfb --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_census_spec.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages polling officers" do + include_context "when admin managing a voting" + + let(:csv_file) { upload_test_file(Decidim::Dev.test_file("import_voting_census.csv", "text/csv")) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Census" + end + end + + context "when init_data" do + it "shows a form to upload a csv file" do + expect(page).to have_content("There is no census yet") + expect(page).to have_content("Create the census") + end + + it "uploads a csv" do + dynamically_attach_file(:dataset_file, Decidim::Dev.asset("import_voting_census.csv")) + within ".form.new_census" do + find("*[type=submit]").click + end + + expect(page).to have_content("Please wait") + end + end + + context "when data is invalid" do + before do + create(:dataset, :data_created, voting:) + visit decidim_admin_votings.voting_census_path(voting) + end + + it "shows the processed file result" do + expect(page).to have_admin_callout("Finished processing") + expect(page).not_to have_content("You can now proceed to generate the access codes") + expect(page).to have_content("Please delete the current census and start over") + end + + it "shows an option to delete the census" do + expect(page).to have_link("Delete all census data", count: 2) + end + + context "when deleting the census" do + it "deletes the census" do + within "#wrapper-action-view" do + accept_confirm { click_link "Delete all census data" } + end + + expect(page).to have_admin_callout("Census data deleted") + expect(page).to have_content("There is no census yet") + end + end + end + + context "when data exists" do + before do + create(:dataset, :data_created, :with_data, voting:) + visit decidim_admin_votings.voting_census_path(voting) + end + + it "shows the processed file result" do + expect(page).to have_admin_callout("Finished processing") + expect(page).to have_content("You can now proceed to generate the access codes") + end + + it "shows an option to delete the census" do + expect(page).to have_link("Delete all census data") + end + + context "when deleting the census" do + it "deletes the census" do + within ".voting-content" do + accept_confirm { click_link "Delete all census data" } + end + + expect(page).to have_admin_callout("Census data deleted") + expect(page).to have_content("There is no census yet") + end + end + + it "shows an option to generate the access codes" do + expect(page).to have_link("Generate voting Access Codes") + end + + context "when generating the access codes" do + it "deletes the census" do + within ".voting-content" do + accept_confirm { click_link "Generate voting Access Codes" } + end + + expect(page).to have_content("Please wait") + end + end + end + + context "when access codes have been generated" do + before do + create(:dataset, :codes_generated, voting:) + visit decidim_admin_votings.voting_census_path(voting) + end + + it "exports the access codes" do + within ".voting-content" do + accept_confirm { click_link "Export voting Access Codes" } + end + + expect(page).to have_admin_callout("Access codes export launched") + expect(page).to have_admin_callout(user.email) + end + end + + context "when census is frozen" do + before do + create(:dataset, :frozen, voting:) + visit decidim_admin_votings.voting_census_path(voting) + end + + it "Shows that the census is frozen" do + expect(page).to have_content("The census is frozen") + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_component_permissions_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_component_permissions_spec.rb new file mode 100644 index 00000000..be349e48 --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_component_permissions_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages voting component permissions" do + include_examples "Managing component permissions" do + let(:participatory_space_engine) { decidim_admin_votings } + let(:user) { create(:user, :admin, :confirmed, organization:) } + + let!(:participatory_space) do + create(:voting, organization:) + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_components_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_components_spec.rb new file mode 100644 index 00000000..3b5a2f22 --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_components_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages voting components" do + include_context "when admin managing a voting" + + it_behaves_like "manage voting components" +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_monitoring_committee_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_monitoring_committee_spec.rb new file mode 100644 index 00000000..f9ab365e --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_monitoring_committee_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages the monitoring committee" do + include_context "when admin managing a voting" + + let(:other_user) { create(:user, organization:, email: "my_email@example.org") } + let!(:monitoring_committee_member) { create(:monitoring_committee_member, user: other_user, voting:) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Members" + end + end + + it "shows all members in the monitoring committee page" do + within "#monitoring_committee_members table" do + expect(page).to have_content(monitoring_committee_member.user.email) + end + end + + context "when creating a new member" do + let(:existing_user) { create(:user, organization: voting.organization) } + + before do + click_link("New member") + end + + it "creates a new user" do + within ".new_monitoring_committee_member" do + fill_in :voting_user_role_email, with: "joe@doe.com" + fill_in :voting_user_role_name, with: "Joe Doe" + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#monitoring_committee_members table" do + expect(page).to have_content(other_user.email) + expect(page).to have_content("joe@doe.com") + end + end + + it "uses an existing user" do + within ".new_monitoring_committee_member" do + select "Existing participant", from: :voting_user_role_existing_user + autocomplete_select "#{existing_user.name} (@#{existing_user.nickname})", from: :user_id + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#monitoring_committee_members table" do + expect(page).to have_content(other_user.email) + expect(page).to have_content(existing_user.email) + end + end + end + + context "when deleting a member" do + it "deletes the member" do + within find("#monitoring_committee_members tr", text: other_user.email) do + accept_confirm { click_link "Delete" } + end + + expect(page).to have_admin_callout("successfully") + + within "#monitoring_committee_members table" do + expect(page).not_to have_content(other_user.email) + end + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_polling_officers_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_polling_officers_spec.rb new file mode 100644 index 00000000..52f20fa7 --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_polling_officers_spec.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages polling officers" do + include_context "when admin managing a voting" + + let(:other_user) { create(:user, organization:, email: "my_email@example.org") } + let!(:polling_officer) { create(:polling_officer, user: other_user, voting:) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Polling Officers" + end + end + + context "when listing the polling officers" do + let(:model_name) { polling_officer.class.model_name } + let(:resource_controller) { Decidim::Votings::Admin::PollingOfficersController } + + include_context "with filterable context" + + it "shows polling officer list" do + within "#polling_officers table" do + expect(page).to have_content(polling_officer.user.email) + end + end + + context "when searching by name" do + let(:searched_officer) { create(:polling_officer, voting:) } + + it "filters the results as expected" do + search_by_text(searched_officer.name) + expect(page).to have_content(searched_officer.name) + expect(page).not_to have_content(polling_officer.name) + end + end + + context "when searching by polling station" do + let(:polling_station) { create(:polling_station, voting:) } + let!(:searched_officer) { create(:polling_officer, voting:, presided_polling_station: polling_station) } + + it "filters the results as expected" do + search_by_text(translated(polling_station.title)) + expect(page).to have_content(searched_officer.name) + expect(page).not_to have_content(translated(polling_officer.name)) + end + end + + context "when filtering by polling officer role" do + let!(:president) do + create(:polling_officer, voting:, presided_polling_station: create(:polling_station, voting:)) + end + + let!(:manager) do + create(:polling_officer, voting:, managed_polling_station: create(:polling_station, voting:)) + end + + let!(:unassigned) do + create(:polling_officer, voting:) + end + + it_behaves_like "a filtered collection", options: "Role", filter: "President" do + let(:in_filter) { president.name } + let(:not_in_filter) { manager.name } + end + + it_behaves_like "a filtered collection", options: "Role", filter: "Manager" do + let(:in_filter) { manager.name } + let(:not_in_filter) { president.name } + end + + it_behaves_like "a filtered collection", options: "Role", filter: "Unassigned" do + let(:in_filter) { unassigned.name } + let(:not_in_filter) { president.name } + end + end + end + + context "when creating a new polling officer" do + let(:existing_user) { create(:user, organization: voting.organization) } + + before do + click_link("New polling officer") + end + + it "creates a new user" do + within ".new_polling_officer" do + fill_in :voting_user_role_email, with: "joe@doe.com" + fill_in :voting_user_role_name, with: "Joe Doe" + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#polling_officers table" do + expect(page).to have_content(other_user.email) + expect(page).to have_content("joe@doe.com") + end + end + + it "uses an existing user" do + within ".new_polling_officer" do + select "Existing participant", from: :voting_user_role_existing_user + autocomplete_select "#{existing_user.name} (@#{existing_user.nickname})", from: :user_id + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#polling_officers table" do + expect(page).to have_content(other_user.email) + expect(page).to have_content(existing_user.email) + end + end + end + + context "when deleting a polling officer" do + it "deletes the polling officer" do + within find("#polling_officers tr", text: other_user.email) do + accept_confirm { click_link "Delete" } + end + + expect(page).to have_admin_callout("successfully") + + within "#polling_officers table" do + expect(page).not_to have_content(other_user.email) + end + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_polling_stations_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_polling_stations_spec.rb new file mode 100644 index 00000000..28bed477 --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_polling_stations_spec.rb @@ -0,0 +1,241 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages polling stations", serves_geocoding_autocomplete: true do + let(:address) { "Somewhere over the rainbow" } + let(:latitude) { 42.123 } + let(:longitude) { 2.123 } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Polling Stations" + end + end + + include_context "when admin managing a voting" + + context "when processing polling stations" do + let!(:polling_officers) { create_list(:polling_officer, 3, voting:) } + let!(:polling_station) { create(:polling_station, voting:) } + + before do + stub_geocoding(address, [latitude, longitude]) + visit current_path + end + + context "when listing the polling stations" do + let(:model_name) { polling_station.class.model_name } + let(:resource_controller) { Decidim::Votings::Admin::PollingStationsController } + + include_context "with filterable context" + + it "lists all the polling stations for the voting" do + within "#polling_stations table" do + expect(page).to have_content(translated(polling_station.title, locale: :en)) + expect(page).to have_content(polling_station.address) + end + end + + it "has the callout warning of missing officers" do + expect(page).to have_text("There are Polling Stations without President and/or Managers") + end + + context "when searching by title" do + let(:searched_station) { create(:polling_station, voting:) } + + it "filters the results as expected" do + search_by_text(translated(searched_station.title)) + expect(page).to have_content(translated(searched_station.title)) + expect(page).not_to have_content(translated(polling_station.title)) + end + end + + context "when searching by president name" do + let(:searched_station) { create(:polling_station, voting:) } + let(:president) { create(:polling_officer, voting:, presided_polling_station: searched_station) } + + it "filters the results as expected" do + search_by_text(president.name) + expect(page).to have_content(translated(searched_station.title)) + expect(page).not_to have_content(translated(polling_station.title)) + end + end + + context "when searching by manager email" do + let(:searched_station) { create(:polling_station, voting:) } + let(:manager) { create(:polling_officer, voting:, managed_polling_station: searched_station) } + + it "filters the results as expected" do + search_by_text(manager.email) + expect(page).to have_content(translated(searched_station.title)) + expect(page).not_to have_content(translated(polling_station.title)) + end + end + + context "when filtering by assigned/unussigned" do + let(:polling_station_with_president) { create(:polling_station, voting:) } + let(:polling_station_with_both) { create(:polling_station, voting:) } + let!(:polling_station_unassigned) { create(:polling_station, voting:) } + + let!(:president) { create(:polling_officer, voting:, presided_polling_station: polling_station_with_president) } + let!(:manager) { create(:polling_officer, voting:, managed_polling_station: polling_station_with_both) } + let!(:other_president) { create(:polling_officer, voting:, presided_polling_station: polling_station_with_both) } + + it_behaves_like "a filtered collection", options: "Officers", filter: "Assigned" do + let(:in_filter) { translated(polling_station_with_both.title) } + let(:not_in_filter) { translated(polling_station_with_president.title) } + end + + it_behaves_like "a filtered collection", options: "Officers", filter: "Not assigned" do + let(:in_filter) { translated(polling_station_unassigned.title) } + let(:not_in_filter) { translated(polling_station_with_both.title) } + end + end + end + + it "can add a polling station to a process", :serves_geocoding_autocomplete do + click_link("New polling station") + + within ".new_polling_station" do + fill_in_i18n( + :polling_station_title, + "#polling_station-title-tabs", + en: "Polling station", + es: "Colegio electoral", + ca: "Col·legi electoral" + ) + + fill_in_geocoding :polling_station_address, with: address + + fill_in_i18n( + :polling_station_location, + "#polling_station-location-tabs", + en: "Location", + es: "Location", + ca: "Location" + ) + + fill_in_i18n( + :polling_station_location_hints, + "#polling_station-location_hints-tabs", + en: "Location hints", + es: "Location hints", + ca: "Location hints" + ) + + autocomplete_select "#{polling_officers.first.name} (@#{polling_officers.first.nickname})", from: :polling_station_president_id + + tom_select("#polling_officers_filter", option_id: polling_officers.last(2).map(&:id)) + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#polling_stations table" do + expect(page).to have_text("Polling station") + expect(page).to have_text(polling_officers.first.name) + polling_officers.last(2).each do |polling_officer| + expect(page).to have_text(polling_officer.name) + end + end + end + + it "can delete a polling station from a voting" do + within find("tr", text: translated(polling_station.title)) do + accept_confirm { click_link "Delete" } + end + + expect(page).to have_admin_callout("successfully") + + expect(page).not_to have_content(translated(polling_station.title, locale: :en)) + end + + it "can update a polling_station" do + within "#polling_stations" do + within find("tr", text: translated(polling_station.title)) do + click_link "Edit" + end + end + + within ".edit_polling_station" do + fill_in_i18n( + :polling_station_title, + "#polling_station-title-tabs", + en: "Another polling station", + es: "Otro colegio electoral", + ca: "Un altre col·legi electoral" + ) + fill_in :polling_station_address, with: address + + find("#autoComplete_list_1 > li").click + + autocomplete_select "#{polling_officers.last.name} (@#{polling_officers.last.nickname})", from: :polling_station_president_id + + tom_select("#polling_officers_filter", option_id: polling_officers.first(2).map(&:id)) + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + expect(page).not_to have_text("There are Polling Stations without President and/or Managers") + + within "#polling_stations table" do + expect(page).to have_text("Another polling station") + expect(page).to have_text(polling_officers.last.name) + polling_officers.first(2).each do |polling_officer| + expect(page).to have_text(polling_officer.name) + end + end + end + end + + context "when using the front-end geocoder", :serves_geocoding_autocomplete do + before do + stub_geocoding(address, [latitude, longitude]) + end + + it_behaves_like( + "a record with front-end geocoding address field", + Decidim::Votings::PollingStation, + within_selector: ".new_polling_station", + address_field: :polling_station_address + ) do + let(:geocoded_address_value) { address } + let(:geocoded_address_coordinates) { [latitude, longitude] } + + before do + # Prepare the view for submission (other than the address field) + click_link("New polling station") + + fill_in_i18n( + :polling_station_title, + "#polling_station-title-tabs", + en: "Polling station", + es: "Colegio electoral", + ca: "Col·legi electoral" + ) + + fill_in_i18n( + :polling_station_location, + "#polling_station-location-tabs", + en: "Location", + es: "Location", + ca: "Location" + ) + + fill_in_i18n( + :polling_station_location_hints, + "#polling_station-location_hints-tabs", + en: "Location hints", + es: "Location hints", + ca: "Location hints" + ) + end + end + end +end diff --git a/decidim-elections/spec/system/admin/admin_manages_voting_publication_spec.rb b/decidim-elections/spec/system/admin/admin_manages_voting_publication_spec.rb new file mode 100644 index 00000000..15d6c9aa --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_voting_publication_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages voting publication" do + include_context "when admin managing a voting" + + let(:admin_page_path) { decidim_admin_votings.edit_voting_path(participatory_space) } + let(:public_collection_path) { decidim_votings.votings_path } + let(:title) { "My space" } + let!(:participatory_space) { voting } + + it_behaves_like "manage participatory space publications" +end diff --git a/decidim-elections/spec/system/admin/admin_manages_votings_spec.rb b/decidim-elections/spec/system/admin/admin_manages_votings_spec.rb new file mode 100644 index 00000000..0a5689cf --- /dev/null +++ b/decidim-elections/spec/system/admin/admin_manages_votings_spec.rb @@ -0,0 +1,309 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages votings" do + include_context "when admin managing a voting" + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.votings_path + end + + describe "when listing votings" do + let(:model_name) { voting.class.model_name } + let(:resource_controller) { Decidim::Votings::Admin::VotingsController } + + it_behaves_like "filtering collection by published/unpublished" + end + + describe "creating a voting" do + before do + within ".layout-content" do + click_link("New") + end + end + + it_behaves_like "having a rich text editor for field", ".tabs-content[data-tabs-content='voting-description-tabs']", "full" + + it "creates a new voting" do + fill_in :voting_start_time, with: Time.current.change(day: 12, hour: 10, min: 50) + fill_in :voting_end_time, with: Time.current.change(day: 12, hour: 12, min: 50) + + within ".new_voting" do + fill_in_i18n( + :voting_title, + "#voting-title-tabs", + en: "My voting", + es: "Mi votación", + ca: "La meva votació" + ) + fill_in_i18n_editor( + :voting_description, + "#voting-description-tabs", + en: "A longer description", + es: "Descripción más larga", + ca: "Descripció més llarga" + ) + fill_in :voting_slug, with: "slug" + end + + dynamically_attach_file(:voting_banner_image, image1_path) + dynamically_attach_file(:voting_introductory_image, image2_path) + + within ".new_voting" do + select "Online", from: :voting_voting_type + select translated(organization.scopes.first.name), from: :voting_scope_id + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "[data-content]" do + expect(page).to have_current_path decidim_admin_votings.votings_path + expect(page).to have_content("My voting") + end + end + end + + describe "trying to create a voting with invalid data" do + before do + within ".layout-content" do + click_link("New") + end + end + + it "fails to create a new voting" do + fill_in :voting_start_time, with: Time.current.change(day: 12, hour: 10, min: 50) + fill_in :voting_end_time, with: Time.current.change(day: 12, hour: 12, min: 50) + + within ".new_voting" do + fill_in_i18n( + :voting_title, + "#voting-title-tabs", + en: "", + es: "", + ca: "" + ) + fill_in_i18n_editor( + :voting_description, + "#voting-description-tabs", + en: "A longer description", + es: "Descripción más larga", + ca: "Descripció més llarga" + ) + fill_in :voting_slug, with: "slug" + select "Online", from: :voting_voting_type + end + + dynamically_attach_file(:voting_banner_image, image1_path) + dynamically_attach_file(:voting_introductory_image, image2_path) + + within ".new_voting" do + select translated(organization.scopes.first.name), from: :voting_scope_id + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("problem") + end + end + + describe "updating a voting" do + let(:elections_component) { create(:elections_component, participatory_space: voting) } + + before do + within find("tr", text: translated(voting.title)) do + click_link "Configure" + end + end + + it "updates a voting" do + create(:election, component: elections_component) + + fill_in_i18n( + :voting_title, + "#voting-title-tabs", + en: "My new title", + es: "Mi nuevo título", + ca: "El meu nou títol" + ) + dynamically_attach_file(:voting_banner_image, image3_path, remove_before: true) + select "Online", from: :voting_voting_type + + within ".edit_voting" do + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + expect(page).not_to have_admin_callout("You do not have any election configured") + + within "[data-content]" do + expect(page).to have_selector("input[value='My new title']") + expect(page).not_to have_css("img[src*='#{image2_filename}']") + expect(page).to have_css("img[src*='#{image3_filename}']") + end + end + end + + describe "updating a voting with invalid values" do + before do + within find("tr", text: translated(voting.title)) do + click_link "Configure" + end + end + + it "does not update the voting" do + fill_in_i18n( + :voting_title, + "#voting-title-tabs", + en: "", + es: "", + ca: "" + ) + + within ".edit_voting" do + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("problem") + end + end + + describe "updating a voting with invalid image" do + before do + within find("tr", text: translated(voting.title)) do + click_link "Configure" + end + end + + it "shows an error inside the upload modal" do + find_by_id("voting_banner_image_button").click + + within ".upload-modal" do + click_button "Remove" + input_element = find("input[type='file']", visible: :all) + input_element.attach_file(image_invalid_path) + + expect(page).to have_content("only files with the following extensions are allowed: jpeg, jpg, png", count: 1) + expect(page).to have_css("div[data-template='error']", text: "Validation error!", count: 1) + end + end + end + + describe "updating a voting without images" do + let!(:voting3) { create(:voting, organization:) } + + before do + visit decidim_admin_votings.votings_path + end + + it "does not delete them" do + within find("tr", text: translated(voting3.title)) do + click_link "Configure" + end + + within ".edit_voting" do + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + expect(page).to have_css("img[src*='#{voting3.attached_uploader(:banner_image).path}']") + end + end + + describe "previewing votings" do + let!(:voting) { create(:voting, :unpublished, :with_content_blocks, organization:, blocks_manifests: [:title]) } + + it "allows the user to preview the unpublished voting" do + within find("tr", text: translated(voting.title)) do + preview_window = window_opened_by do + click_link "Preview" + end + + within_window(preview_window) do + expect(page).to have_i18n_content(voting.title) + expect(page).to have_i18n_content(voting.description) + end + end + end + end + + describe "viewing a missing voting" do + it_behaves_like "a 404 page" do + let(:target_path) { decidim_admin_votings.voting_path(99_999_999) } + end + end + + describe "publishing a voting" do + let!(:voting) { create(:voting, :unpublished, organization:) } + + before do + within find("tr", text: translated(voting.title)) do + click_link "Configure" + end + end + + it "publishes the voting" do + click_link "Publish" + expect(page).to have_content("successfully published") + expect(page).to have_content("Unpublish") + expect(page).to have_current_path decidim_admin_votings.edit_voting_path(voting) + + voting.reload + expect(voting).to be_published + end + end + + describe "unpublishing a voting" do + let!(:voting) { create(:voting, :published, organization:) } + + before do + within find("tr", text: translated(voting.title)) do + click_link "Configure" + end + end + + it "unpublishes the voting" do + click_link "Unpublish" + expect(page).to have_content("successfully unpublished") + expect(page).to have_content("Publish") + expect(page).to have_current_path decidim_admin_votings.edit_voting_path(voting) + + voting.reload + expect(voting).not_to be_published + end + end + + context "when there are multiple organizations in the system" do + let!(:external_voting) { create(:voting) } + + before do + visit decidim_admin_votings.votings_path + end + + it "does not let the admin manage votings from other organizations" do + within "table" do + expect(page).not_to have_content(external_voting.title["en"]) + end + end + end + + it "renders the sub nav to manage voting's settings" do + within ".table-list" do + within find("tr", text: translated(voting.title)) do + click_link "Configure" + end + end + + within_admin_sidebar_menu do + expect(page).to have_content("About this voting") + expect(page).to have_content("Landing Page") + expect(page).to have_content("Components") + expect(page).to have_content("Attachments") + expect(page).to have_content("Polling Stations") + expect(page).to have_content("Polling Officers") + expect(page).to have_css(".is-active", text: "About this voting") + end + end +end diff --git a/decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_polling_station_closures_spec.rb b/decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_polling_station_closures_spec.rb new file mode 100644 index 00000000..7a2dc5ef --- /dev/null +++ b/decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_polling_station_closures_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Monitoring committee member manages voting polling station closures" do + include_context "when monitoring committee member manages voting" + let(:elections_component) { create(:elections_component, participatory_space: voting) } + let!(:election) { create(:election, :complete, :published, component: elections_component) } + let!(:polling_station) { create(:polling_station, voting:) } + let!(:closure) { create(:ps_closure, phase: :complete, signed_at: Time.current, polling_station:, election:) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + within_admin_sidebar_menu do + click_link "Validate Certificates" + end + end + + context "when listing the polling stations" do + let(:model_name) { polling_station.class.model_name } + let(:resource_controller) { Decidim::Votings::Admin::MonitoringCommitteePollingStationClosuresController } + + include_context "with filterable context" + + it "lists all the polling stations for the voting" do + within "#monitoring_committee_polling_station_closures table" do + expect(page).to have_content(translated(polling_station.title, locale: :en)) + expect(page).to have_content(polling_station.address) + end + end + + it "has the callout warning of missing officers" do + expect(page).to have_text("There are Polling Stations without President and/or Managers") + end + + context "when searching by title" do + let(:searched_station) { create(:polling_station, voting:) } + + it "filters the results as expected" do + search_by_text(translated(searched_station.title)) + expect(page).to have_content(translated(searched_station.title)) + expect(page).not_to have_content(translated(polling_station.title)) + end + end + + context "when searching by president name" do + let(:searched_station) { create(:polling_station, voting:) } + let(:president) { create(:polling_officer, voting:, presided_polling_station: searched_station) } + + it "filters the results as expected" do + search_by_text(president.name) + expect(page).to have_content(translated(searched_station.title)) + expect(page).not_to have_content(translated(polling_station.title)) + end + end + + context "when searching by manager email" do + let(:searched_station) { create(:polling_station, voting:) } + let(:manager) { create(:polling_officer, voting:, managed_polling_station: searched_station) } + + it "filters the results as expected" do + search_by_text(manager.email) + expect(page).to have_content(translated(searched_station.title)) + expect(page).not_to have_content(translated(polling_station.title)) + end + end + end + + context "when validating a polling station closure" do + before do + within find("tr", text: translated(polling_station.title)) do + page.find(".action-icon--validate").click + end + end + + it "validates the closure" do + click_button "Validate" + + expect(page).to have_content("correctly") + expect(page).to have_content(translated(polling_station.title)) + within find("tr", text: translated(polling_station.title)) do + expect(page).to have_selector(".action-icon--view") + end + end + end +end diff --git a/decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_results_spec.rb b/decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_results_spec.rb new file mode 100644 index 00000000..0e874bdb --- /dev/null +++ b/decidim-elections/spec/system/admin/monitoring_committee_member_manages_voting_results_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Monitoring committee member manages voting results" do + include_context "when monitoring committee member manages voting" + + let(:elections_component) { create(:elections_component, participatory_space: voting) } + let!(:election) { create(:election, :bb_test, :tally_ended, component: elections_component, number_of_votes: 10) } + let!(:ps_closure) { create_list(:ps_closure, 4, :with_results, election:, number_of_votes: 5) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + end + + it_behaves_like "needs admin TOS accepted" do + let(:user) { create(:user, :confirmed, organization:) } + end + + context "when there are more than one finished elections" do + let!(:other_election) { create(:election, :complete, :published, :finished, component: elections_component) } + + it "lists all the finished elections for the voting" do + within_admin_sidebar_menu do + click_link "Validate Results" + end + + expect(page).to have_content(translated(other_election.title)) + click_link translated(election.title) + + expect(page).to have_content("Results for the election\n#{translated(election.title)}") + end + end + + describe "results verification and publishing", :slow do + include_context "with test bulletin board" + + it "shows the results for the election" do + within_admin_sidebar_menu do + click_link "Validate Results" + end + + expect(page).to have_content("Results for the election\n#{translated(election.title)}") + + within ".question_" do + expect(page).to have_content("Total ballots") + expect(page).to have_content("20") + expect(page).to have_content("10") + expect(page).to have_content("30") + end + + election.questions.each do |question| + within ".question_#{question.id}" do + expect(page).to have_content(translated(question.title)) + + question.answers.each do |answer| + within ".answer_#{answer.id}" do + expect(page).to have_content(translated(answer.title)) + expect(page).to have_content(answer.results_total) + end + end + end + end + + click_button "Publish results" + + expect(page).to have_content("Publishing results...") + expect(page).to have_content("The results were successfully published") + end + end +end diff --git a/decidim-elections/spec/system/admin/monitoring_committee_member_manages_votings_spec.rb b/decidim-elections/spec/system/admin/monitoring_committee_member_manages_votings_spec.rb new file mode 100644 index 00000000..7d107d50 --- /dev/null +++ b/decidim-elections/spec/system/admin/monitoring_committee_member_manages_votings_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Monitoring committee member manages votings" do + include_context "when monitoring committee member manages voting" + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.votings_path + end + + it_behaves_like "needs admin TOS accepted" do + let(:user) { create(:user, :confirmed, organization:) } + end + + context "when the user has not accepted the admin TOS" do + let(:user) { create(:user, :confirmed, organization:) } + + it "shows a message to accept the admin TOS" do + expect(page).to have_content("Please take a moment to review the admin terms of service") + end + end + + describe "when listing votings" do + let(:other_voting) { create(:voting, organization:) } + + it "only lists the voting I am a monitoring committee member of" do + within "#votings table" do + expect(page).to have_text(translated(voting.title)) + expect(page).not_to have_text(translated(other_voting.title)) + end + end + end +end diff --git a/decidim-elections/spec/system/admin/monitoring_committee_member_verifies_elections_spec.rb b/decidim-elections/spec/system/admin/monitoring_committee_member_verifies_elections_spec.rb new file mode 100644 index 00000000..c261bd32 --- /dev/null +++ b/decidim-elections/spec/system/admin/monitoring_committee_member_verifies_elections_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Monitoring committee member verifies elections" do + include_context "when monitoring committee member manages voting" + + let(:elections_component) { create(:elections_component, participatory_space: voting) } + let!(:election) { create(:election, :tally_ended, :published, component: elections_component) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + visit decidim_admin_votings.edit_voting_path(voting) + end + + it_behaves_like "needs admin TOS accepted" do + let(:user) { create(:user, :confirmed, organization:) } + end + + context "when listing the elections" do + it "lists all the polling stations for the voting" do + within_admin_sidebar_menu do + click_link "Verify Elections" + end + + within "#monitoring_committee_verify_elections table" do + expect(page).to have_content(translated(election.title)) + expect(page).to have_link("Download", href: election.verifiable_results_file_url) + expect(page).to have_content(election.verifiable_results_file_hash) + end + end + end +end diff --git a/decidim-elections/spec/system/check_census_spec.rb b/decidim-elections/spec/system/check_census_spec.rb new file mode 100644 index 00000000..2c92f73c --- /dev/null +++ b/decidim-elections/spec/system/check_census_spec.rb @@ -0,0 +1,209 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Check Census" do + include Rack::Test::Methods + + let!(:organization) { create(:organization) } + let!(:voting) { create(:voting, :published, organization:, census_contact_information: "census_help@example.com") } + let!(:dataset) { create(:dataset, :data_created, voting:) } + let!(:datum) do + create(:datum, document_type: "passport", document_number: "12345678X", birthdate: Date.civil(1980, 5, 11), postal_code: "04001", dataset:, mobile_phone_number:, email:) + end + let!(:user) { create(:user, :confirmed, organization:) } + let(:mobile_phone_number) { "123456789" } + let(:email) { "foo@example.com" } + let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) } + + before do + allow(Rails).to receive(:cache).and_return(memory_store) + Rails.cache.clear + switch_to_host(organization.host) + end + + after do + Rack::Attack.reset! + end + + context "when requesting the check census path" do + before do + visit decidim_votings.voting_check_census_path(voting) + end + + it "shows the title of the page" do + expect(page).to have_content("Can I vote?") + end + end + + context "when the page has been hidden" do + before do + voting.update!(show_check_census: false) + end + + it "returns a not found page" do + visit decidim_votings.voting_path(voting) + + expect(page).not_to have_content("CAN I VOTE?") + end + end + + context "when census data is correct" do + before do + visit decidim_votings.voting_check_census_path(voting) + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Postal code", with: "04001" + fill_in "Day", with: "11" + fill_in "Month", with: "05" + fill_in "Year", with: "1980" + find("*[type=submit]").click + end + end + + it "shows note that census data is correct" do + within "[data-announcement]" do + expect(page).to have_content("Your census data is correct") + expect(page).not_to have_content("Fill the following form to check your census data:") + end + end + + it "shows instructions to ask for access code again, mentioning email and SMS" do + within "[data-announcement]" do + expect(page).to have_content("You should have received your Access Code by postal mail already. In case, you do not have it, you can request it here\nvia SMS or email") + end + end + end + + context "when census data is correct but there is no SMS gateway configured" do + before do + Decidim.sms_gateway_service = "FooBar" + + visit decidim_votings.voting_check_census_path(voting) + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Postal code", with: "04001" + fill_in "Day", with: "11" + fill_in "Month", with: "05" + fill_in "Year", with: "1980" + find("*[type=submit]").click + end + end + + after do + Decidim.sms_gateway_service = "Decidim::Verifications::Sms::ExampleGateway" + end + + it "shows instructions to ask for access code again, mentioning only email" do + within "[data-announcement]" do + expect(page).to have_content("You should have received your Access Code by postal mail already. In case, you do not have it, you can request it here\nvia email") + end + end + end + + describe "when asking for access code" do + before do + visit decidim_votings.voting_check_census_path(voting) + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Postal code", with: "04001" + fill_in "Day", with: "11" + fill_in "Month", with: "05" + fill_in "Year", with: "1980" + find("*[type=submit]").click + end + end + + context "when asking by email" do + it "sends email" do + click_button "via SMS or email" + + expect(page).to have_content("Get Access Code") + + click_button "Send by email to ****@example.com" + + within "[data-alert-box]" do + expect(page).to have_content("successfully") + end + end + end + + context "when asking by sms" do + it "sends sms" do + click_button "via SMS or email" + + expect(page).to have_content("Get Access Code") + + click_button "Send by SMS" + + within "[data-alert-box]" do + expect(page).to have_content("successfully") + end + end + end + + context "when datum has no mobile phone number" do + let(:mobile_phone_number) { nil } + + it "cannot receive access code by SMS" do + click_button "via SMS or email" + + expect(page).to have_content("Get Access Code") + + expect(page).to have_button("No phone number available", disabled: true) + end + end + end + + context "when no census data is found" do + before do + visit decidim_votings.voting_check_census_path(voting) + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "987654321X" + fill_in "Postal code", with: "04004" + fill_in "Day", with: "01" + fill_in "Month", with: "12" + fill_in "Year", with: "1982" + find("*[type=submit]").click + end + end + + it "shows note that census data is not correct" do + within "[data-announcement]" do + expect(page).to have_content("The data you have entered are not in the census for this vote") + end + expect(page).to have_content("Fill the following form to check your census data:") + end + + it "shows contact information to edit census data if wrong" do + within "[data-announcement]" do + expect(page).to have_content("census_help@example.com") + end + end + end + + context "when post request gets attacked" do + before do + visit decidim_votings.voting_check_census_path(voting) + 6.times do + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "987654321X" + fill_in "Postal code", with: "04004" + fill_in "Day", with: "01" + fill_in "Month", with: "12" + fill_in "Year", with: "1982" + find("*[type=submit]").click + end + end + end + + it "throttles after 5 attempts per minute" do + expect(page).to have_content("Retry later") + end + end +end diff --git a/decidim-elections/spec/system/election_log_spec.rb b/decidim-elections/spec/system/election_log_spec.rb new file mode 100644 index 00000000..613e1793 --- /dev/null +++ b/decidim-elections/spec/system/election_log_spec.rb @@ -0,0 +1,193 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Election log", :slow do + include_context "with a component" + + let(:manifest_name) { "elections" } + + before do + election + visit resource_locator(election).path + click_link "Election log" + end + + context "when election is not set up" do + let(:election) { create(:election, :complete, :published, :ready_for_setup, component:) } + + it "shows not started entries" do + expect(page).to have_content("Election created") + expect(page).to have_content("The election is not created yet.") + expect(page).to have_content("The key ceremony has not started yet.") + expect(page).to have_content("The voting process has not started yet.") + expect(page).to have_content("The tally process has not started yet.") + expect(page).to have_content("The results are not published yet.") + expect(page).not_to have_content("The chained Hash of this message") + end + end + + context "when election is created" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :created, component:) } + + it "shows that election is created" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony has not started yet.") + expect(page).to have_content("The voting process has not started yet.") + expect(page).to have_content("The tally process has not started yet.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when the key ceremony started but is not ended" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :key_ceremony, component:) } + + it "shows that key ceremony has started" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony has started but is not completed yet.") + expect(page).to have_content("The voting process has not started yet.") + expect(page).to have_content("The tally process has not started yet.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when the key ceremony is finished" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :key_ceremony_ended, component:) } + + it "shows that key ceremony is ended" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys.") + expect(page).to have_content("The voting process has not started yet.") + expect(page).to have_content("The tally process has not started yet.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when voting has started" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :vote, component:) } + + it "shows that vote has started" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys.") + expect(page).to have_content("The voting process has started.") + expect(page).to have_content("The tally process has not started yet.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when voting has ended" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :vote_ended, component:) } + + it "shows that voting process has ended" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys.") + expect(page).to have_content("The voting process is finished.") + expect(page).to have_content("The tally process has not started yet.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when tally has started" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :tally_started, component:) } + + it "shows that tally has started" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys.") + expect(page).to have_content("The voting process is finished.") + expect(page).to have_content("The tally process has started.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when tally is completed" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :tally_ended, component:) } + + it "shows that tally has finished" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys.") + expect(page).to have_content("The voting process is finished.") + expect(page).to have_content("The tally process is finished.") + expect(page).to have_content("The results are not published yet.") + expect(page).to have_content("The chained Hash of this message") + end + end + + context "when results are published" do + include_context "with test bulletin board" + + let(:election) { create(:election, :bb_test, :results_published, component:) } + + it "shows that results are published" do + expect(page).to have_content("The election got created and is successfully set up on the Bulletin Board.") + expect(page).to have_content("The key ceremony is completed. Every trustee has valid keys and has downloaded the necessary backup keys.") + expect(page).to have_content("The voting process is finished.") + expect(page).to have_content("The tally process is finished.") + expect(page).to have_content("The results are published.") + expect(page).to have_content("The chained Hash of this message") + end + end + + describe "verify election" do + include_context "with test bulletin board" + + context "when election does not have correct bb_status" do + let(:election) { create(:election, :bb_test, :tally_ended, component:) } + + it "does not show instructions to verify election" do + expect(page).to have_content("Verify Election results") + expect(page).to have_content("The verifiable election file and SHA256 checksum are not available yet") + expect(page).to have_content("Not ready") + end + end + + context "when election has correct bb_status but no verifiable file nor checksum" do + let(:election) { create(:election, :bb_test, :results_published, component:, verifiable_results_file_hash: nil, verifiable_results_file_url: nil) } + + it "shows instructions to verify election" do + expect(page).to have_content("Verify Election") + expect(page).to have_content("Here, you have the option to verify the election.") + end + + it "shows that file and checksum are not available" do + within "#verifiable-results-step" do + expect(page).to have_content("Not yet available") + expect(page).not_to have_content("Download") + end + end + end + + context "when election has correct bb_status and verifiable file and checksum" do + let(:election) { create(:election, :bb_test, :results_published, component:) } + + it "shows instructions to verify election" do + expect(page).to have_content("Verify Election") + expect(page).to have_content("Here, you have the option to verify the election.") + end + + it "shows that file and checksum are not available" do + expect(page).not_to have_content("Not yet available") + expect(page).to have_content("Download") + end + end + end +end diff --git a/decidim-elections/spec/system/elections_log_spec.rb b/decidim-elections/spec/system/elections_log_spec.rb new file mode 100644 index 00000000..711343b2 --- /dev/null +++ b/decidim-elections/spec/system/elections_log_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Elections log" do + let(:manifest_name) { "elections" } + let!(:organization) { create(:organization) } + let!(:election) { create(:election, :bb_test, :created, component:) } + + include_context "with a component" do + let!(:voting) { create(:voting, :published, organization:) } + let(:participatory_space) { voting } + let(:organization_traits) { [:secure_context] } + end + + include_context "with test bulletin board" + + describe "when voting has only one election" do + it "redirects to election log" do + visit decidim_votings.voting_elections_log_path(voting) + + expect(page).to have_content("Election created") + end + end + + describe "when elections with bb_status are present" do + let!(:vote_election) { create(:election, :bb_test, :vote, component:) } + let!(:finished_election) { create(:election, :bb_test, :results_published, component:) } + let!(:key_ceremony_election) { create(:election, :bb_test, :key_ceremony_ended, component:) } + + it "shows list of elections" do + visit decidim_votings.voting_elections_log_path(voting) + expect(page).to have_content("The election log will show you all relevant information about each voting. For example, the status of the key ceremony or tally or if results are published already. Click on the election you want the log information about.") + expect(page).to have_selector("[data-log-entry]").exactly(4).times + expect(page).to have_content(translated(vote_election.title)) + expect(page).to have_content(strip_tags(translated(vote_election.description))) + expect(page).to have_content(vote_election.start_time) + expect(page).to have_content(vote_election.bb_status.to_s.titlecase) + end + end +end diff --git a/decidim-elections/spec/system/explore_elections_spec.rb b/decidim-elections/spec/system/explore_elections_spec.rb new file mode 100644 index 00000000..3c9a3b9e --- /dev/null +++ b/decidim-elections/spec/system/explore_elections_spec.rb @@ -0,0 +1,233 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Explore elections", :slow do + include_context "with a component" + let(:manifest_name) { "elections" } + + let(:elections_count) { 5 } + let!(:elections) do + create_list(:election, elections_count, :complete, :published, :ongoing, component:) + end + + describe "index" do + context "with only one election" do + before do + Decidim::Elections::Election.destroy_all + end + + let!(:single_elections) { create_list(:election, 1, :complete, :published, :ongoing, component:) } + + it "redirects to the only election" do + visit_component + + expect(page).to have_content("Active voting until") + expect(page).not_to have_content("All elections") + expect(page).to have_content("These are the questions you will find in the voting process") + end + end + + context "with many elections" do + it "shows all elections for the given process" do + visit_component + within "#elections" do + expect(page).to have_css("[id^=elections]", count: elections_count) + end + + elections.each do |election| + expect(page).to have_content(translated(election.title)) + end + end + end + + context "when filtering" do + it "allows searching by text" do + visit_component + + within "[data-filters]" do + fill_in "filter[search_text_cont]", with: translated(elections.first.title) + + within "div.filter-search" do + click_button + end + end + + expect(page).to have_content("1 election") + + within "#elections" do + expect(page).to have_css("[id^=elections]", count: 1) + expect(page).to have_content(translated(elections.first.title)) + end + end + + it "allows filtering by date" do + finished_election = create(:election, :complete, :published, :finished, component:) + upcoming_election = create(:election, :complete, :published, :upcoming, component:) + visit_component + + within "#panel-dropdown-menu-date" do + uncheck "Active" + check "Finished" + end + + within "#elections" do + expect(page).to have_css("[id^=elections]", count: 1) + expect(page).to have_content(translated(finished_election.title)) + end + + within "#panel-dropdown-menu-date" do + uncheck "Finished" + check "Active" + end + + within "#elections" do + expect(page).to have_css("[id^=elections]", count: 5) + end + + within "#panel-dropdown-menu-date" do + uncheck "Active" + check "Upcoming" + end + + within "#elections" do + expect(page).to have_css("[id^=elections]", count: 1) + expect(page).to have_content(translated(upcoming_election.title)) + end + + within "#panel-dropdown-menu-date" do + check "All" + uncheck "All" + end + + within "#elections" do + expect(page).to have_css("[id^=elections]", count: 7) + end + end + + context "when there are no elections in a filter" do + it "shows the filters empty message" do + visit_component + + within "#panel-dropdown-menu-date" do + uncheck "Active" + check "Upcoming" + end + + within ".flash" do + expect(page).to have_content("There are no elections with this criteria.") + end + end + end + end + + context "when no active or upcoming elections scheduled" do + before do + Decidim::Elections::Election.destroy_all + end + + let!(:finished_elections) do + create_list(:election, elections_count, :complete, :published, :finished, component:) + end + + it "shows the correct warning" do + visit_component + + within ".flash" do + expect(page).to have_content("Currently, there are no scheduled elections, but here you can find all the past elections listed.") + end + end + + it "does not show the date filter" do + visit_component + + within "aside.layout-2col__aside" do + expect(page).not_to have_content("Date") + end + end + end + + context "when no elections is given" do + before do + Decidim::Elections::Election.destroy_all + + visit_component + end + + it "shows the correct warning" do + within ".flash" do + expect(page).to have_content("There is not any election scheduled") + end + end + + it "shows the date filter" do + within "aside.layout-2col__aside" do + expect(page).to have_content("Date") + end + end + end + + context "when paginating" do + before do + Decidim::Elections::Election.destroy_all + end + + let!(:collection) { create_list(:election, collection_size, :complete, :published, :ongoing, component:) } + let!(:resource_selector) { "[id^=elections__election]" } + + it_behaves_like "a paginated resource" + end + end + + describe "show" do + let(:elections_count) { 1 } + let(:description) { Decidim::Faker::Localized.wrapped("

    ", "

    ") { generate_localized_title } } + let(:election) { create(:election, :complete, :published, :ongoing, component:, description:) } + let(:question) { election.questions.first } + let(:image) { create(:attachment, :with_image, attached_to: election) } + + before do + election.update!(attachments: [image]) + visit resource_locator(election).path + end + + it_behaves_like "has embedded video in description", :description + + it "shows all election info" do + expect(page).to have_i18n_content(election.title) + expect(page).to have_i18n_content(election.description) + expect(page).to have_content(election.end_time.day) + end + + it "shows accordion with questions and answers" do + expect(page).to have_css("#accordion-preview li", count: election.questions.count) + expect(page).not_to have_css("[id^='accordion-panel']") + + within "#accordion-preview li", match: :first do + click_button translated(question.title) + expect(page).to have_css("li", count: question.answers.count) + end + end + + context "with attached photos" do + it "shows the image" do + expect(page).to have_selector("img[src*=\"city.jpeg\"]", count: 1) + end + end + end + + context "with results" do + let(:election) { create(:election, :published, :results_published, component:) } + let(:question) { create(:question, :with_votes, election:) } + + before do + election.update!(questions: [question]) + visit resource_locator(election).path + end + + it "shows result information" do + expect(page).to have_i18n_content(question.title) + expect(page).to have_content("Election results") + end + end +end diff --git a/decidim-elections/spec/system/homepage_content_blocks_spec.rb b/decidim-elections/spec/system/homepage_content_blocks_spec.rb new file mode 100644 index 00000000..5f6ec0be --- /dev/null +++ b/decidim-elections/spec/system/homepage_content_blocks_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Homepage votings content blocks" do + let(:organization) { create(:organization) } + let(:show_statistics) { true } + let!(:promoted_voting) { create(:voting, :promoted, :with_content_blocks, organization:, blocks_manifests: [:title]) } + + before do + create(:content_block, organization:, scope_name: :homepage, manifest_name: :highlighted_votings) + switch_to_host(organization.host) + end + + context "when there are multiple votings" do + let!(:unpromoted_voting) { create(:voting, organization:) } + let!(:promoted_external_voting) { create(:voting, :promoted) } + + it "includes active votings to the homepage" do + visit decidim.root_path + + within "#highlighted-votings" do + expect(page).to have_i18n_content(promoted_voting.title) + expect(page).to have_i18n_content(unpromoted_voting.title) + expect(page).not_to have_i18n_content(promoted_external_voting.title) + end + end + end + + context "when there is only one voting" do + it "redirects to the voting page" do + visit decidim.root_path + click_link "Votings" + + expect(page).to have_i18n_content(promoted_voting.title) + expect(page).to have_i18n_content(promoted_voting.description) + expect(page).to have_current_path(decidim_votings.voting_path(promoted_voting)) + end + end +end diff --git a/decidim-elections/spec/system/key_ceremony_spec.rb b/decidim-elections/spec/system/key_ceremony_spec.rb new file mode 100644 index 00000000..d04a9f49 --- /dev/null +++ b/decidim-elections/spec/system/key_ceremony_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Key ceremony" do + include Decidim::Elections::FullElectionHelpers + context "when performing the key ceremony", :slow, download: true do + include_context "when performing the whole process" + + it "generates backup keys, restores them and creates election keys" do + setup_election + + download_election_keys(0) + download_election_keys(1) + download_election_keys(2) + + complete_key_ceremony(0) + check_key_ceremony_completed(1) + check_key_ceremony_completed(2) + end + end + + context "when the comunication with bulletin board fails" do + include_context "when performing the whole process" + before do + allow(Decidim::Elections.bulletin_board).to receive(:bulletin_board_server).and_return("http://idontexist.tld/api") + end + + it "alerts the user about the error" do + election + + login_as user, scope: :user + visit_component_admin + + within find("tr", text: translated(election.title)) do + page.find(".action-icon--manage-steps").click + end + + click_button "Setup election" + + click_button "Start the key ceremony" + + within "#server-failure" do + expect(page).to have_content("Something went wrong") + end + end + end +end diff --git a/decidim-elections/spec/system/polling_officer_zone_spec.rb b/decidim-elections/spec/system/polling_officer_zone_spec.rb new file mode 100644 index 00000000..e63ea466 --- /dev/null +++ b/decidim-elections/spec/system/polling_officer_zone_spec.rb @@ -0,0 +1,179 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Polling Officer zone" do + let(:organization) { create(:organization, :secure_context) } + let(:user) { create(:user, :confirmed, organization:) } + let(:polling_officers) { [assigned_polling_officer, unassigned_polling_officer] } + let(:voting) { create(:voting, organization:) } + let(:other_voting) { create(:voting, organization:) } + let(:polling_station) { create(:polling_station, voting:) } + let(:assigned_polling_officer) { create(:polling_officer, voting:, user:, presided_polling_station: polling_station) } + let(:unassigned_polling_officer) { create(:polling_officer, voting: other_voting, user:) } + + before do + polling_officers + switch_to_secure_context_host + login_as user, scope: :user + end + + it "can access to the polling officer zone" do + visit decidim.account_path + + expect(page).to have_content("Polling Officer zone") + + within "#dropdown-menu-profile" do + click_link "Polling Officer zone" + end + + expect(page).to have_content("You are not assigned to any Polling Station yet.") + end + + context "when the user is not a polling officer" do + let(:polling_officers) { [create(:polling_officer)] } + + it "cannot access to the polling officer zone" do + visit decidim.account_path + + expect(page).not_to have_content("Polling Officer zone") + + visit decidim.decidim_votings_polling_officer_zone_path + + expect(page).to have_content("You are not authorized to perform this action") + + expect(page).to have_current_path(decidim.root_path) + end + end + + context "when the user is a polling officer and an election has finished" do + let(:component) { create(:elections_component, participatory_space: voting) } + let!(:election) { create(:election, :published, :finished, questions:, component:) } + let(:questions) { [create(:question, :complete)] } + + it "can access the new results form for the polling station" do + visit decidim.decidim_votings_polling_officer_zone_path + + within "[data-polling-station]" do + expect(page).to have_content(translated(election.title)) + expect(page).to have_content("Count votes") + end + end + + describe "creates a closure" do + it "can add results for the polling station" do + visit decidim_votings_polling_officer_zone.new_polling_officer_election_closure_path(assigned_polling_officer, election) + expect(page).to have_content("Vote recount") + within ".form.new_closure" do + fill_in "envelopes_result_total_ballots_count", with: 0 + find_by_id("envelopes_result_total_ballots_count").native.send_keys(:tab) + find("*[type=submit]").click + end + + expect(page).to have_content("Closure successfully created") + end + + context "when a closure is already created" do + let!(:closure) { create(:ps_closure, election:, polling_station:) } + + it "cannot create a new one" do + visit decidim_votings_polling_officer_zone.new_polling_officer_election_closure_path(assigned_polling_officer, election) + expect(page).to have_content("You are not authorized to perform this action") + end + end + end + + describe "when adding results to the closure" do + before do + visit decidim_votings_polling_officer_zone.new_polling_officer_election_closure_path(assigned_polling_officer, election) + within ".form.new_closure" do + fill_in "envelopes_result_total_ballots_count", with: 20 + find_by_id("envelopes_result_total_ballots_count").native.send_keys(:tab) + click_button "Verify total number" + end + within "#modal-closure-count-error-content" do + fill_in "modal-polling-officer-notes", with: "Some notes" + click_button "Validate total recount of ballots" + end + end + + it "can add results for the polling station" do + expect(page).to have_content("Vote recount - Answers recount") + + within ".form.edit_closure" do + fill_in "closure_result__ballot_results__valid_ballots_count", with: 5 + fill_in "closure_result__ballot_results__blank_ballots_count", with: 4 + fill_in "closure_result__ballot_results__null_ballots_count", with: 5 + find_by_id("closure_result__ballot_results__null_ballots_count").native.send_keys(:tab) + + questions.each do |question| + question.answers.each do |answer| + fill_in "closure_result__answer_results__#{answer.id}_value", with: 2 + end + end + + click_button "Save recount" + end + + expect(page).to have_content("Total records do not add up") + expect(page).to have_content("Expected total of valid votes is 5 but the sum of the valid questions is 6") + expect(page).to have_content("Expected total of blank votes is 4 but the sum of the blank questions is 0") + expect(page).to have_content("Expected total is 20 but the sum of the valid, blank and null ballots is 14") + click_button "Close" + + within ".form.edit_closure" do + fill_in "closure_result__ballot_results__blank_ballots_count", with: 0 + fill_in "closure_result__ballot_results__valid_ballots_count", with: 6 + fill_in "closure_result__ballot_results__null_ballots_count", with: 14 + find_by_id("closure_result__ballot_results__null_ballots_count").native.send_keys(:tab) + end + + click_button "Save recount" + expect(page).to have_content("Closure results successfully updated") + end + end + + describe "when attaching the physical certificate image to the closure", processing_uploads_for: Decidim::AttachmentUploader do + let!(:closure) { create(:ps_closure, :with_results, phase: :certificate, election:, polling_station:) } + + before do + visit decidim_votings_polling_officer_zone.polling_officer_election_closure_path(assigned_polling_officer, election) + end + + it "can attach images to the closure" do + expect(page).to have_content("Vote recount - Upload certificate") + + dynamically_attach_file(:closure_certify_photos, Decidim::Dev.asset("city.jpeg")) + within ".form.certify_closure" do + find("*[type=submit]").click + end + + expect(page).to have_content("Certificate uploaded successfully.") + expect(page.html).to include("city.jpeg") + end + end + + describe "when signing the closure" do + let!(:closure) { create(:ps_closure, :with_results, phase: :signature, election:, polling_station:) } + + before do + visit decidim_votings_polling_officer_zone.polling_officer_election_closure_path(assigned_polling_officer, election) + end + + it "can sign the closure" do + expect(page).to have_content("Vote recount - Sign closure") + + within ".form.sign_closure" do + check "I have reviewed this and is the same as the physical electoral closure certificate" + click_button "Sign the closure", wait: 2 + end + + within "#modal-closure-sign-content" do + click_button "Ok, continue" + end + + expect(page).to have_content("Closure signed successfully") + end + end + end +end diff --git a/decidim-elections/spec/system/preview_elections_with_share_token_spec.rb b/decidim-elections/spec/system/preview_elections_with_share_token_spec.rb new file mode 100644 index 00000000..5642c7fa --- /dev/null +++ b/decidim-elections/spec/system/preview_elections_with_share_token_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Preview elections with share token" do + let(:manifest_name) { "elections" } + + include_context "with a component" + it_behaves_like "preview component with share_token" +end diff --git a/decidim-elections/spec/system/sorting_elections_spec.rb b/decidim-elections/spec/system/sorting_elections_spec.rb new file mode 100644 index 00000000..85f9db0d --- /dev/null +++ b/decidim-elections/spec/system/sorting_elections_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Sorting elections" do + include_context "with a component" + let(:manifest_name) { "elections" } + + let(:organization) { create(:organization) } + let!(:user) { create(:user, :confirmed, organization:) } + let!(:election1) { create(:election, :complete, :published, :ongoing, component:, start_time: 1.day.ago) } + let!(:election2) { create(:election, :complete, :published, :ongoing, component:, start_time: 2.days.ago) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + end + + context "when ordering by recent" do + it "lists the elections in desc start_time order" do + visit_component + within ".order-by" do + expect(page).to have_selector("div.order-by a", text: "Recent") + end + + expect(page).to have_selector("[id='elections__election_#{election1.id}']:first-child", text: translated(election1.title)) + expect(page).to have_selector("[id='elections__election_#{election2.id}']:last-child", text: translated(election2.title)) + end + end + + context "when ordering by older" do + it "lists the elections in asc start_time order" do + visit_component + within ".order-by" do + expect(page).to have_selector("div.order-by a", text: "Recent") + page.find("a", text: "Recent").click + click_link "Older" + end + + expect(page).to have_selector("[id='elections__election_#{election2.id}']:first-child", text: translated(election2.title)) + expect(page).to have_selector("[id='elections__election_#{election1.id}']:last-child", text: translated(election1.title)) + end + end +end diff --git a/decidim-elections/spec/system/trustee_zone_spec.rb b/decidim-elections/spec/system/trustee_zone_spec.rb new file mode 100644 index 00000000..022ccc5e --- /dev/null +++ b/decidim-elections/spec/system/trustee_zone_spec.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Trustee zone" do + let(:organization) { create(:organization, :secure_context) } + let(:user) { create(:user, :confirmed, organization:) } + let(:trustee) { create(:trustee, user:, public_key:, organization:) } + let(:public_key) { nil } + + before do + trustee + switch_to_secure_context_host + login_as user, scope: :user + end + + context "when the user name exists in this organization", download: true do + let!(:other_trustee) { create(:trustee, :with_public_key, name: user.name, organization: user.organization) } + + it "cannot generate identification keys" do + visit decidim.decidim_elections_trustee_zone_path + + expect(page).to have_content("Generate identification keys") + + click_button "Generate identification keys" + + wait_for_download + + expect(download_content).to have_content('"alg":"RS256"') + + find("label", text: "Submit").click + + expect(page).to have_content("Name has already been taken") + end + end + + it "can access to the trustee zone" do + visit decidim.account_path + + expect(page).to have_content("Trustee zone") + + within "#dropdown-menu-profile" do + click_link "Trustee zone" + end + + expect(page).to have_content("Trustee identification keys") + expect(page).to have_content("You have been assigned to act as a Trustee. Please, generate and upload your identification keys.") + end + + it "can generate their identification keys", download: true do + visit decidim.decidim_elections_trustee_zone_path + + expect(page).to have_content("Generate identification keys") + + click_button "Generate identification keys" + + wait_for_download + + expect(download_content).to have_content('"alg":"RS256"') + + find("label", text: "Submit").click + + expect(page).to have_content("Your identification public key was successfully stored.") + expect(page).to have_content("Upload your identification keys") + + attach_file(downloads.first) do + click_button "Upload your identification keys" + end + + expect(page).not_to have_content("Upload your identification keys") + expect(page).not_to have_content("Trustee identification keys") + expect(page).to have_content("You have been assigned to act as a Trustee in some of the elections celebrated in this platform") + end + + context "when the trustee already has a public key" do + let(:public_key) { File.read(Decidim::Dev.asset("public_key.jwk")) } + + it "can upload their identification keys" do + visit decidim.decidim_elections_trustee_zone_path + + expect(page).not_to have_content("Generate identification keys") + expect(page).to have_content("Upload your identification keys") + + attach_file(Decidim::Dev.asset("private_key.jwk")) do + click_button "Upload your identification keys" + end + + expect(page).not_to have_content("Upload your identification keys") + end + + { + "a different private key" => "private_key2.jwk", + "a public_key" => "public_key.jwk", + "an image" => "city.jpeg" + }.each do |description, filename| + it "cannot upload #{description}" do + visit decidim.decidim_elections_trustee_zone_path + + expect(page).to have_content("Upload your identification keys") + + accept_alert do + attach_file(Decidim::Dev.asset(filename)) do + click_button "Upload your identification keys" + end + end + + expect(page).to have_content("Upload your identification keys") + end + end + end + + context "when the user is not a trustee" do + let(:trustee) { create(:trustee) } + + it "cannot access to the trustee zone" do + visit decidim.account_path + + expect(page).not_to have_content("Trustee zone") + + visit decidim.decidim_elections_trustee_zone_path + + expect(page).to have_content("You are not authorized to perform this action") + + expect(page).to have_current_path(decidim.root_path) + end + end + + context "when the bulletin_board is not configured" do + before do + allow(Decidim::Elections.bulletin_board).to receive(:configured?).and_return(false) + trustee + login_as user, scope: :user + end + + it "notifies that it is not configured" do + visit decidim.account_path + + expect(page).to have_content("Trustee zone") + + visit decidim.decidim_elections_trustee_zone_path + + expect(page).to have_content("Sorry, the Bulletin Board is not configured yet") + end + end +end diff --git a/decidim-elections/spec/system/vote_in_person_spec.rb b/decidim-elections/spec/system/vote_in_person_spec.rb new file mode 100644 index 00000000..6c4aa465 --- /dev/null +++ b/decidim-elections/spec/system/vote_in_person_spec.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Polling Officer zone" do + let(:manifest_name) { "elections" } + let(:user) { create(:user, :confirmed, organization:) } + let!(:election) { create(:election, :complete, :bb_test, :vote, component:) } + let(:polling_station) { create(:polling_station, id: 1, voting:) } + let!(:polling_officer) { create(:polling_officer, voting:, user:, presided_polling_station: polling_station) } + let!(:datum) { create(:datum, dataset:, full_name: "Jon Doe", document_type: "passport", document_number: "12345678X", birthdate: Date.civil(1980, 5, 11)) } + let(:dataset) { create(:dataset, voting:) } + + include_context "with a component" do + let(:voting) { create(:voting, :published, organization:) } + let(:participatory_space) { voting } + let(:organization_traits) { [:secure_context] } + end + + before do + switch_to_secure_context_host + login_as user, scope: :user + + visit decidim.account_path + + expect(page).to have_content("Polling Officer zone") + + within "#dropdown-menu-profile" do + click_link "Polling Officer zone" + end + end + + shared_examples "a polling officer registers an in person vote" do + include_context "with test bulletin board" + + let(:questions_title) { "They are entitled to vote in the following questions:" } + let(:census_verified) { "This participant has not voted in person yet." } + + it "can identify a person and register their vote", :slow do + click_link "Identify a person" + + fill_person_data + + expect(page).to have_content("This participant is listed in the census.") + expect(page).to have_content("Jon Doe") + click_button "Verify document" + + election.reload + expect(page).to have_content(census_verified) + expect(page).to have_content(questions_title) + + election.questions.each do |question| + expect(page).to have_content(translated(question.title)) + click_button translated(question.title) + question.answers.each do |answer| + expect(page).to have_content(translated(answer.title)) + end + end + + within "[data-content]" do + check "The participant has voted" + end + + click_button "Complete voting" + + expect(page).to have_content("The vote was registered successfully.") + expect(page).to have_content("Identify and verify a participant") + end + end + + it_behaves_like "a polling officer registers an in person vote" + + context "when the participant already voted online" do + let!(:vote) { create(:vote, election:, voter_id:) } + let(:voter_id) { vote_flow.voter_id } + let(:vote_flow) do + ret = Decidim::Votings::CensusVoteFlow.new(election) + ret.voter_in_person(document_type: "passport", document_number: "12345678X", day: "11", month: "5", year: "1980") + ret + end + + it_behaves_like "a polling officer registers an in person vote" do + let(:census_verified) { "This participant has already voted online. If they vote in person, the previous votes will be invalidated and this will be the definitive vote." } + let(:questions_title) { "This participant has already voted online and is entitled to vote in the following questions:" } + end + end + + context "when the participant already voted in person" do + let!(:in_person_vote) { create(:in_person_vote, :accepted, election:, polling_officer:, voter_id:) } + let(:voter_id) { vote_flow.voter_id } + let(:vote_flow) do + ret = Decidim::Votings::CensusVoteFlow.new(election) + ret.voter_in_person(document_type: "passport", document_number: "12345678X", day: "11", month: "5", year: "1980") + ret + end + + it "does not allow them to vote" do + click_link "Identify a person" + + fill_person_data + + expect(page).to have_content("This participant is listed in the census.") + expect(page).to have_content("Jon Doe") + click_button "Verify document" + + expect(page).to have_content("This participant has already voted in person and is not entitled to vote.") + expect(page).not_to have_content("Complete voting") + + click_link "Identify another participant" + + expect(page).to have_content("Identify and verify a participant") + end + end + + context "when there is a pending in person vote to be registered" do + let!(:in_person_vote) { create(:in_person_vote, election:, polling_officer:) } + + it "redirects to the waiting page" do + click_link "Identify a person" + + expect(page).to have_content("Waiting for the in person vote to be registered") + end + end + + context "when the person data is not valid" do + it "returns a not found message" do + click_link "Identify a person" + + fill_person_data(correct: false) + + expect(page).to have_content("This participant is not listed in the census.") + end + end + + context "when a closure already exists" do + let!(:closure) { create(:ps_closure, :complete, election:, polling_station:, polling_officer:) } + + it "does not allow to identify a person" do + within "#dropdown-menu-profile" do + click_link "Polling Officer zone" + end + + expect(page).not_to have_link("Identify a person") + end + end + + def fill_person_data(correct: true) + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Day", with: correct ? "11" : "1" + fill_in "Month", with: "5" + fill_in "Year", with: "1980" + click_button "Validate document" + end + end +end diff --git a/decidim-elections/spec/system/vote_online_inside_a_voting_spec.rb b/decidim-elections/spec/system/vote_online_inside_a_voting_spec.rb new file mode 100644 index 00000000..9eddc70b --- /dev/null +++ b/decidim-elections/spec/system/vote_online_inside_a_voting_spec.rb @@ -0,0 +1,227 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Vote online in an election inside a Voting" do + let(:manifest_name) { "elections" } + let(:questionnaire) { create(:questionnaire, :with_questions) } + let!(:election) { create(:election, :bb_test, :vote, component:, questionnaire:) } + let(:user) { create(:user, :confirmed, organization: component.organization) } + let!(:datum) do + create(:datum, :with_access_code, document_type: "passport", document_number: "12345678X", birthdate: Date.civil(1980, 5, 11), postal_code: "04001", access_code: "1234", dataset:) + end + let!(:elections) { create_list(:election, 2, :vote, component:) } # prevents redirect to single election page + let(:router) { Decidim::EngineRouter.main_proxy(component).decidim_voting_elections } + + include_context "with a component" do + let(:voting) { create(:voting, :published, organization:) } + let!(:dataset) { create(:dataset, voting:) } + let(:participatory_space) { voting } + let(:organization_traits) { [:secure_context] } + end + + include_context "with test bulletin board" + + before do + election.reload # forces to reload the questions in the right order + login_as(user, scope: :user) if user + end + + describe "when the user is logged in" do + it "can vote and then change the vote", :slow do + vote_with_census_data + + click_link "Back to elections" + click_link(id: "elections__election_#{election.id}") + + expect(page).to have_current_path router.election_path(id: election.id) + expect(page).not_to have_content("You have already voted in this election.") + + click_link "Start voting" + end + end + + context "when there are different ballot styles" do + before do + ballot_style = create(:ballot_style, voting:) + ballot_style2 = create(:ballot_style, voting:) + + election.questions.each do |question| + create( + :ballot_style_question, + question:, + ballot_style: + ) + end + + dataset.data.each do |datum| + datum.update(ballot_style:) + end + + create( + :ballot_style_question, + question: create(:question, election:), + ballot_style: ballot_style2 + ) + end + + it "lets the user vote the questions from their ballot style" do + vote_with_census_data + end + end + + describe "when there is no user logged in" do + let(:user) { nil } + + context "when questionnaire has questions" do + it "can vote and sign up after feedback when questionnaire" do + vote_with_census_data + + click_link "Give us some feedback" + + expect(page).to have_i18n_content(election.questionnaire.title) + expect(page).to have_i18n_content(election.questionnaire.description) + + fill_in election.questionnaire.questions.first.body["en"], with: "My first answer" + + check "questionnaire_tos_agreement" + + accept_confirm do + click_button "Submit" + end + + expect(page).to have_content("New to the platform?") + + within "#onboarding-modal" do + click_button "No, thanks." + end + + expect(page).to have_current_path router.election_path(id: election.id, onboarding: true) + end + end + + context "when questionnaire has no questions" do + let!(:questionnaire) { create(:questionnaire) } # by default questionnaire does not have any questions + + it "can vote and sign up without feedback" do + vote_with_census_data + + expect(page).to have_content("New to the platform?") + + within "#onboarding-modal" do + click_button "No, thanks." + end + + expect(page).not_to have_content("Give us some feedback") + + click_link "Back to elections" + + expect(page).to have_current_path router.elections_path + + expect(page).not_to have_content("New to the platform?") + end + end + end + + context "when the voting is not published" do + let(:election) { create(:election, :upcoming, :complete, component:) } + + it_behaves_like "does not allow to vote" + it_behaves_like "allows admins to preview the voting booth" + end + + context "when the census data is not right" do + it "cannot vote", :slow do + visit_component + click_link translated(election.title) + click_link "Start voting" + + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Postal code", with: "04001" + fill_in "Day", with: "11" + fill_in "Month", with: "05" + fill_in "Year", with: "1980" + fill_in "Access code", with: "1235" + find("*[type=submit]").click + end + + expect(page).to have_content("Document number") + expect(page).to have_content("The given data does not match any voter.") + end + end + + context "when the voter already voted in person" do + let!(:in_person_vote) { create(:in_person_vote, election:, polling_officer:, voter_id:) } + let(:polling_station) { create(:polling_station, voting:) } + let(:polling_officer) { create(:polling_officer, voting:, user:, presided_polling_station: polling_station) } + let(:voter_id) { vote_flow.voter_id } + let(:vote_flow) do + ret = Decidim::Votings::CensusVoteFlow.new(election) + ret.voter_in_person(document_type: "passport", document_number: "12345678X", day: "11", month: "05", year: "1980") + ret + end + + it "does not allow to vote again" do + visit_component + click_link translated(election.title) + click_link "Start voting" + + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Postal code", with: "04001" + fill_in "Day", with: "11" + fill_in "Month", with: "05" + fill_in "Year", with: "1980" + fill_in "Access code", with: "1234" + find("*[type=submit]").click + end + + expect(page).to have_content("This participant has already voted in person and is not entitled to vote.") + end + end + + context "when the comunication with bulletin board fails" do + before do + election.questions.last.destroy! + election.questions.last.destroy! + election.questions.last.destroy! + allow(Decidim::Elections.bulletin_board).to receive(:bulletin_board_server).and_return("http://idontexist.tld/api") + end + + it "alerts the user about the error" do + fill_census_data + + within "#server-failure" do + expect(page).to have_content("Something went wrong") + end + end + end + + def fill_census_data + visit_component + click_link translated(election.title) + click_link "Start voting" + + within "[data-content]" do + select("Passport", from: "Document type") + fill_in "Document number", with: "12345678X" + fill_in "Postal code", with: "04001" + fill_in "Day", with: "11" + fill_in "Month", with: "05" + fill_in "Year", with: "1980" + fill_in "Access code", with: "1234" + find("*[type=submit]").click + end + end + + def vote_with_census_data + fill_census_data + + expect(page).not_to have_content("This is a preview of the voting booth.") + + uses_the_voting_booth + end +end diff --git a/decidim-elections/spec/system/vote_online_spec.rb b/decidim-elections/spec/system/vote_online_spec.rb new file mode 100644 index 00000000..2171c9c4 --- /dev/null +++ b/decidim-elections/spec/system/vote_online_spec.rb @@ -0,0 +1,188 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Vote online in an election" do + let(:manifest_name) { "elections" } + let!(:election) { create(:election, :bb_test, :vote, component:) } + let(:user) { create(:user, :confirmed, organization: component.organization) } + let!(:elections) { create_list(:election, 2, :vote, component:) } # prevents redirect to single election page + let(:router) { Decidim::EngineRouter.main_proxy(component).decidim_participatory_process_elections } + + before do + election.reload # forces to reload the questions in the right order + login_as user, scope: :user + end + + include_context "with a component" do + let(:organization_traits) { [:secure_context] } + end + + describe "voting with the current user" do + include_context "with test bulletin board" + + it "can vote and then change the vote", :slow do + visit_component + click_link translated(election.title) + click_link "Start voting" + + expect(page).not_to have_content("This is a preview of the voting booth.") + + uses_the_voting_booth + + click_link "Back to elections" + click_link(id: "elections__election_#{election.id}") + + expect(page).to have_current_path router.election_path(id: election.id) + expect(page).to have_content("You have already voted in this election.") + + click_link "Change your vote" + + uses_the_voting_booth + end + + context "when there is description in a question" do + before do + # rubocop:disable Rails/SkipsModelValidations + Decidim::Elections::Answer.update_all(description: { en: "Some text" }) + # rubocop:enable Rails/SkipsModelValidations + end + + it "shows a link to view more information about the election" do + visit_component + click_link translated(election.title) + click_link "Start voting" + expect(page).to have_content("More information") + end + end + + context "when there is no description in a question" do + before do + # rubocop:disable Rails/SkipsModelValidations + Decidim::Elections::Answer.update_all(description: {}) + # rubocop:enable Rails/SkipsModelValidations + end + + it "does not show the more information link" do + visit_component + click_link translated(election.title) + click_link "Start voting" + expect(page).not_to have_content("More information") + end + end + end + + context "when the election is not published" do + let(:election) { create(:election, :upcoming, :complete, component:) } + + it_behaves_like "does not allow to vote" + it_behaves_like "allows admins to preview the voting booth" + end + + context "when the election has not started yet" do + let(:election) { create(:election, :upcoming, :published, :complete, component:) } + + it_behaves_like "does not allow to vote" + it_behaves_like "allows admins to preview the voting booth" + end + + context "when the election has finished" do + let(:election) { create(:election, :finished, :published, :complete, component:) } + + it_behaves_like "does not allow to vote" + it_behaves_like "does not allow admins to preview the voting booth" + end + + context "when the component requires permissions to vote" do + before do + permissions = { + vote: { + authorization_handlers: { + "dummy_authorization_handler" => { "options" => {} } + } + } + } + + component.update!(permissions:) + end + + it "shows a modal dialog" do + visit_component + + click_link translated(election.title) + click_link "Start voting" + + expect(page).to have_content("Authorization required") + end + + context "when the election has not started yet" do + let(:election) { create(:election, :upcoming, :published, :complete, component:) } + + it_behaves_like "allows admins to preview the voting booth" + end + end + + context "when the election requires permissions to vote" do + before do + permissions = { + vote: { + authorization_handlers: { + "dummy_authorization_handler" => { "options" => {} } + } + } + } + + election.create_resource_permission(permissions:) + end + + it "shows a modal dialog" do + visit_component + + click_link translated(election.title) + click_link "Start voting" + + expect(page).to have_content("Authorization required") + end + + context "when the election has not started yet" do + let(:election) { create(:election, :upcoming, :published, :complete, component:) } + + it_behaves_like "allows admins to preview the voting booth" + end + end + + context "when the ballot was not send" do + it "is alerted when trying to leave the component before completing" do + visit_component + + click_link translated(election.title) + click_link "Start voting" + + dismiss_prompt do + # click anything outside of the #vote-wrapper element + page.find("#main-bar [aria-label='Go to front page']").click + end + + expect(page).to have_content("Next") + end + end + + context "when the comunication with bulletin board fails" do + before do + election.questions.last.destroy! + election.questions.last.destroy! + election.questions.last.destroy! + allow(Decidim::Elections.bulletin_board).to receive(:bulletin_board_server).and_return("http://idontexist.tld/api") + end + + it "alerts the user about the error" do + visit_component + click_link translated(election.title) + click_link "Start voting" + + within "#server-failure" do + expect(page).to have_content("Something went wrong") + end + end + end +end diff --git a/decidim-elections/spec/system/voting_spec.rb b/decidim-elections/spec/system/voting_spec.rb new file mode 100644 index 00000000..35984549 --- /dev/null +++ b/decidim-elections/spec/system/voting_spec.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Voting" do + let!(:organization) { create(:organization) } + let!(:voting) { create(:voting, :published, :with_content_blocks, organization:, blocks_manifests: [:title]) } + let!(:user) { create(:user, :confirmed, organization:) } + + before do + switch_to_host(organization.host) + end + + it_behaves_like "editable content for admins" do + let(:target_path) { decidim_votings.voting_path(voting) } + end + + context "when requesting the voting path" do + before do + visit decidim_votings.voting_path(voting) + end + + it "shows the basic voting data" do + expect(page).to have_i18n_content(voting.title) + expect(page).to have_i18n_content(voting.description) + end + + describe "follow button" do + let(:followable) { voting } + let(:followable_path) { decidim_votings.voting_path(voting) } + + include_examples "follows" + end + + it_behaves_like "has embedded video in description", :description do + before do + voting.update!(description:) + visit decidim_votings.voting_path(voting) + end + end + + context "when the voting is unpublished" do + let!(:voting) do + create(:voting, :unpublished, organization:) + end + + before do + switch_to_host(organization.host) + end + + it "redirects to sign in path" do + visit decidim_votings.voting_path(voting) + expect(page).to have_current_path("/users/sign_in") + end + + context "with signed in user" do + let!(:user) { create(:user, :confirmed, organization:) } + + before do + login_as user, scope: :user + end + + it "redirects to root path" do + visit decidim_votings.voting_path(voting) + expect(page).to have_current_path("/") + end + end + end + + context "when the voting has census" do + let!(:census) { create(:dataset, voting:) } + + before do + switch_to_host(organization.host) + visit decidim_votings.voting_path(voting) + end + + it "shows 'Can I vote' tab" do + expect(page).to have_link("Can I vote?") + end + end + + context "when the voting does not has a census" do + before do + switch_to_host(organization.host) + visit decidim_votings.voting_path(voting) + end + + it "does not has 'Can I vote' tab" do + expect(page).not_to have_link("Can I vote?") + end + end + end +end diff --git a/decidim-elections/spec/system/votings_spec.rb b/decidim-elections/spec/system/votings_spec.rb new file mode 100644 index 00000000..85d69a1a --- /dev/null +++ b/decidim-elections/spec/system/votings_spec.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/core/test/shared_examples/has_contextual_help" + +describe "Votings" do + let(:organization) { create(:organization) } + + before do + switch_to_host(organization.host) + end + + context "with only one voting in the votings space" do + let!(:single_voting) { create(:voting, :published, :with_content_blocks, organization:, blocks_manifests: [:title]) } + + before do + visit decidim_votings.votings_path + end + + it "redirects to the voting" do + expect(page).to have_content(translated(single_voting.title)) + expect(page).not_to have_selector("#votings") + end + end + + context "with many votings" do + let!(:votings) { create_list(:voting, 2, :published, organization:) } + + it_behaves_like "shows contextual help" do + let(:index_path) { decidim_votings.votings_path } + let(:manifest_name) { :votings } + end + + context "when ordering by 'Most recent'" do + let!(:older_voting) do + create(:voting, :published, organization:, created_at: 1.month.ago) + end + + let!(:recent_voting) do + create(:voting, :published, organization:, created_at: Time.now.utc) + end + + before do + switch_to_host(organization.host) + end + + it_behaves_like "editable content for admins" do + let(:target_path) { visit decidim_votings.votings_path } + end + + context "when requesting the votings path" do + before do + visit decidim_votings.votings_path + end + + it "lists the votings ordered by created at" do + within ".order-by" do + expect(page).to have_selector("div.order-by a", text: "Random") + page.find("a", text: "Random").click + click_link "Most recent" + end + + expect(page).to have_selector("[id='votings__voting_#{recent_voting.id}']:first-child", text: recent_voting.title[:en]) + expect(page).to have_selector("[id='votings__voting_#{older_voting.id}']:last-child", text: older_voting.title[:en]) + end + end + end + + context "when ordering by 'Random'" do + let!(:votings) { create_list(:voting, 2, :published, organization:) } + + before do + switch_to_host(organization.host) + visit decidim_votings.votings_path + end + + it "shows all votings" do + within ".order-by" do + expect(page).to have_selector("div.order-by a", text: "Random") + end + + expect(page).to have_selector("[id^='votings__voting']", count: 2) + expect(page).to have_content(translated(votings.first.title)) + expect(page).to have_content(translated(votings.last.title)) + end + end + + context "when there are promoted votings" do + let!(:highlighted_voting) { create(:voting, :published, :promoted, organization:) } + let!(:other_voting) { create(:voting, :published, organization:) } + + before do + switch_to_host(organization.host) + visit decidim_votings.votings_path + end + + it "lists all the highlighted votings" do + within "#highlighted-votings" do + expect(page).to have_content(translated(highlighted_voting.title, locale: :en)) + expect(page).to have_selector("[id^='votings__voting']", count: 1) + end + end + end + + context "when filtering" do + let!(:voting) { create(:voting, :published, organization:) } + + it "allows searching by text" do + visit decidim_votings.votings_path + within "[data-filters]" do + fill_in "filter[search_text_cont]", with: translated(voting.title) + + within "div.filter-search" do + click_button + end + end + + expect(page).to have_content("1 voting") + expect(page).to have_css("[id^='votings__voting']", count: 1) + expect(page).to have_content(translated(voting.title)) + end + + describe "when by different dates" do + let!(:finished_voting) { create(:voting, :finished, organization:) } + let!(:upcoming_voting) { create(:voting, :upcoming, organization:) } + let!(:ongoing_voting) { create(:voting, :ongoing, organization:) } + + before do + visit decidim_votings.votings_path + end + + it "allows filtering by finished date" do + within "#panel-dropdown-menu-date" do + uncheck "All" + check "Finished" + end + + expect(page).to have_css("[id^='votings__voting']", count: 1) + expect(page).to have_content(translated(finished_voting.title)) + end + + it "allows filtering by active date" do + within "#panel-dropdown-menu-date" do + uncheck "All" + check "Active" + end + + expect(page).to have_css("[id^='votings__voting']", count: 1) + end + + it "allows filtering by upcoming date" do + within "#panel-dropdown-menu-date" do + uncheck "All" + check "Upcoming" + end + + expect(page).to have_css("[id^='votings__voting']", count: 4) + expect(page).to have_content(translated(upcoming_voting.title)) + end + + it "allows filtering by all date" do + within "#panel-dropdown-menu-date" do + uncheck "All" + end + + expect(page).to have_css("[id^='votings__voting']", count: 6) + end + end + end + + context "when all votings are finished" do + let!(:votings) { create_list(:voting, 2, :finished, organization:) } + + before do + switch_to_host(organization.host) + visit decidim_votings.votings_path + end + + it "I see an alert" do + expect(page).to have_content("Currently, there are no scheduled votings, but here you can find the finished votings listed.") + expect(page).to have_selector("[data-announcement]", count: 1) + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/bulletin_board_closure_type_spec.rb b/decidim-elections/spec/types/decidim/elections/bulletin_board_closure_type_spec.rb new file mode 100644 index 00000000..0b2fceef --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/bulletin_board_closure_type_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" + +module Decidim + module Elections + describe BulletinBoardClosureType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:bb_closure) } + + describe "id" do + let(:query) { "{ id }" } + + it "returns the closure id" do + expect(response["id"]).to eq(model.id.to_s) + end + end + + describe "createdAt" do + let(:query) { "{ createdAt }" } + + it "returns when the closure was created" do + expect(response["createdAt"]).to eq(model.created_at.to_time.iso8601) + end + end + + describe "updatedAt" do + let(:query) { "{ updatedAt }" } + + it "returns when the closure was updated" do + expect(response["updatedAt"]).to eq(model.updated_at.to_time.iso8601) + end + end + + describe "election" do + let(:query) { "{ election { id } }" } + + it "returns the election for this closure" do + expect(response["election"]["id"]).to eq(model.election.id.to_s) + end + end + + describe "results" do + let(:query) { "{ results { id } }" } + + it "returns the results" do + ids = response["results"].map { |result| result["id"].to_i } + expect(ids).to eq(model.results.map(&:id)) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/election_answer_type_spec.rb b/decidim-elections/spec/types/decidim/elections/election_answer_type_spec.rb new file mode 100644 index 00000000..9509e162 --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/election_answer_type_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" +require "decidim/core/test/shared_examples/attachable_interface_examples" +require "decidim/core/test/shared_examples/traceable_interface_examples" + +module Decidim + module Elections + describe ElectionAnswerType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:election_answer) } + + it_behaves_like "attachable interface" + + it_behaves_like "traceable interface" do + let(:author) { create(:user, :admin, organization: model.component.organization) } + end + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response).to include("id" => model.id.to_s) + end + end + + describe "title" do + let(:query) { '{ title { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["title"]["translation"]).to eq(model.title["en"]) + end + end + + describe "description" do + let(:query) { '{ description { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["description"]["translation"]).to eq(model.description["en"]) + end + end + + describe "weight" do + let(:query) { "{ weight }" } + + it "returns the answers weight" do + expect(response["weight"]).to eq(model.weight) + end + end + + describe "results_total" do + let(:query) { "{ results_total }" } + + it "returns the votes for this answer" do + expect(response["results_total"]).to eq(model.results_total) + end + end + + describe "selected" do + let(:query) { "{ selected }" } + + it "returns if answer is selected" do + expect(response["selected"]).to eq(model.selected) + end + end + + describe "proposals" do + let(:component) { create(:proposal_component, organization: model.question.election.component.organization) } + let!(:proposals) { create_list(:proposal, 2, component:) } + let(:query) { "{ proposals { id } }" } + + it "returns the answer related proposals" do + ids = response["proposals"].map { |proposal| proposal["id"] } + expect(ids).to include(*model.proposals.map(&:id).map(&:to_s)) + expect(ids).not_to include(*proposals.map(&:id).map(&:to_s)) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/election_question_type_spec.rb b/decidim-elections/spec/types/decidim/elections/election_question_type_spec.rb new file mode 100644 index 00000000..2b1b3efa --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/election_question_type_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" +require "decidim/core/test/shared_examples/traceable_interface_examples" + +module Decidim + module Elections + describe ElectionQuestionType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:question) } + + it_behaves_like "traceable interface" do + let(:author) { create(:user, :admin, organization: model.component.organization) } + end + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response).to include("id" => model.id.to_s) + end + end + + describe "title" do + let(:query) { '{ title { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["title"]["translation"]).to eq(model.title["en"]) + end + end + + describe "maxSelections" do + let(:query) { "{ maxSelections }" } + + it "returns the election max_selections" do + expect(response["maxSelections"]).to eq(model.max_selections) + end + end + + describe "weight" do + let(:query) { "{ weight }" } + + it "returns the election weight" do + expect(response["weight"]).to eq(model.weight) + end + end + + describe "randomAnswersOrder" do + let(:query) { "{ randomAnswersOrder }" } + + it "returns the election random_answers_order" do + expect(response["randomAnswersOrder"]).to eq(model.random_answers_order) + end + end + + describe "answers" do + let!(:question2) { create(:question, :complete) } + let(:query) { "{ answers { id } }" } + + it "returns the question answers" do + ids = response["answers"].map { |question| question["id"] } + expect(ids).to include(*model.answers.map(&:id).map(&:to_s)) + expect(ids).not_to include(*question2.answers.map(&:id).map(&:to_s)) + end + end + + describe "minSelections" do + let(:query) { "{ minSelections }" } + + it "returns the election min_selections" do + expect(response["minSelections"]).to eq(model.min_selections) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/election_result_type_spec.rb b/decidim-elections/spec/types/decidim/elections/election_result_type_spec.rb new file mode 100644 index 00000000..021d7dd2 --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/election_result_type_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" +require "decidim/core/test/shared_examples/traceable_interface_examples" + +module Decidim + module Elections + describe ElectionResultType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:election_result) } + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response["id"]).to eq(model.id.to_s) + end + end + + describe "value" do + let(:query) { "{ value }" } + + it "returns the votes count" do + expect(response["value"]).to eq(model.value) + end + end + + describe "createdAt" do + let(:query) { "{ createdAt }" } + + it "returns when the voting was created" do + expect(response["createdAt"]).to eq(model.created_at.to_time.iso8601) + end + end + + describe "updatedAt" do + let(:query) { "{ updatedAt }" } + + it "returns when the voting was updated" do + expect(response["updatedAt"]).to eq(model.updated_at.to_time.iso8601) + end + end + + describe "answer" do + let(:query) { "{ answer { id } }" } + + it "returns the answer for this result" do + expect(response["answer"]["id"]).to eq(model.answer.id.to_s) + end + end + + describe "question" do + let(:query) { "{ question { id } }" } + + it "returns the question for this result" do + expect(response["question"]["id"]).to eq(model.question.id.to_s) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/election_type_spec.rb b/decidim-elections/spec/types/decidim/elections/election_type_spec.rb new file mode 100644 index 00000000..5ba72371 --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/election_type_spec.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" +require "decidim/core/test/shared_examples/attachable_interface_examples" +require "decidim/core/test/shared_examples/traceable_interface_examples" + +module Decidim + module Elections + describe ElectionType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:election, :published, :complete) } + + it_behaves_like "attachable interface" + + it_behaves_like "traceable interface" do + let(:author) { create(:user, :admin, organization: model.component.organization) } + end + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response).to include("id" => model.id.to_s) + end + end + + describe "title" do + let(:query) { '{ title { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["title"]["translation"]).to eq(model.title["en"]) + end + end + + describe "description" do + let(:query) { '{ description { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["description"]["translation"]).to eq(model.description["en"]) + end + end + + describe "startTime" do + let(:query) { "{ startTime }" } + + it "returns the election's start time" do + expect(Time.zone.parse(response["startTime"])).to be_within(1.second).of(model.start_time) + end + end + + describe "endTime" do + let(:query) { "{ endTime }" } + + it "returns the election's end time" do + expect(Time.zone.parse(response["endTime"])).to be_within(1.second).of(model.end_time) + end + end + + describe "publishedAt" do + let(:query) { "{ publishedAt }" } + + it "returns the election's published time" do + expect(Time.zone.parse(response["publishedAt"])).to be_within(1.second).of(model.published_at) + end + end + + describe "blocked" do + let(:query) { "{ blocked }" } + + context "when the election's parameters are blocked" do + let!(:model) { create(:election, :created) } + + it "returns true" do + expect(response["blocked"]).to be true + end + end + + context "when the election's parameters are not blocked" do + let(:model) { create(:election) } + + it "returns false" do + expect(response["blocked"]).to be_falsey + end + end + end + + describe "bb_status" do + let(:query) { "{ bb_status }" } + + it "returns the bb_status" do + expect(response["bb_status"]).to eq(model.bb_status) + end + end + + describe "questions" do + let!(:election2) { create(:election, :complete) } + let(:query) { "{ questions { id } }" } + + it "returns the election questions" do + ids = response["questions"].map { |question| question["id"] } + expect(ids).to include(*model.questions.map(&:id).map(&:to_s)) + expect(ids).not_to include(*election2.questions.map(&:id).map(&:to_s)) + end + end + + describe "trustees" do + let(:query) { "{ trustees { id } }" } + + it "returns the election trustees" do + ids = response["trustees"].map { |trustee| trustee["id"] } + expect(ids).to include(*model.trustees.map(&:id).map(&:to_s)) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/elections_type_spec.rb b/decidim-elections/spec/types/decidim/elections/elections_type_spec.rb new file mode 100644 index 00000000..2f730e0a --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/elections_type_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" +require "decidim/core/test" + +module Decidim + module Elections + describe ElectionsType, type: :graphql do + include_context "with a graphql class type" + let(:model) { create(:elections_component) } + + it_behaves_like "a component query type" + + describe "elections" do + let!(:component_elections) { create_list(:election, 2, :published, component: model) } + let!(:component_elections_hidden) { create_list(:election, 2, component: model) } + let!(:other_elections) { create_list(:election, 2) } + + let(:query) { "{ elections { edges { node { id } } } }" } + + it "returns the elections" do + ids = response["elections"]["edges"].map { |edge| edge["node"]["id"] } + expect(ids).to include(*component_elections.map(&:id).map(&:to_s)) + expect(ids).not_to include(*component_elections_hidden.map(&:id).map(&:to_s)) + expect(ids).not_to include(*other_elections.map(&:id).map(&:to_s)) + end + end + + describe "election" do + let(:query) { "query Election($id: ID!){ election(id: $id) { id } }" } + let(:variables) { { id: election.id.to_s } } + + context "when the election belongs to the component" do + let!(:election) { create(:election, :published, component: model) } + + it "finds the election" do + expect(response["election"]["id"]).to eq(election.id.to_s) + end + end + + context "when the election does not belong to the component" do + let!(:election) { create(:election) } + + it "returns null" do + expect(response["election"]).to be_nil + end + end + + context "when the election belongs to the component and its publication time is nil" do + let!(:election) { create(:election, component: model) } + + it "returns null" do + expect(response["election"]).to be_nil + end + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/elections/trustee_type_spec.rb b/decidim-elections/spec/types/decidim/elections/trustee_type_spec.rb new file mode 100644 index 00000000..8f5e700a --- /dev/null +++ b/decidim-elections/spec/types/decidim/elections/trustee_type_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" + +module Decidim + module Elections + describe TrusteeType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:trustee) } + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response).to include("id" => model.id.to_s) + end + end + + describe "user" do + let(:query) { "{ user { name } }" } + + it "returns the user field" do + expect(response["user"]["name"]).to eq(model.user.name) + end + end + + describe "publicKey" do + let(:query) { "{ publicKey }" } + + it "returns the public key field" do + expect(response["publicKey"]).to eq(model.public_key) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/votings/polling_station_closure_type_spec.rb b/decidim-elections/spec/types/decidim/votings/polling_station_closure_type_spec.rb new file mode 100644 index 00000000..66610d60 --- /dev/null +++ b/decidim-elections/spec/types/decidim/votings/polling_station_closure_type_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" + +module Decidim + module Votings + describe PollingStationClosureType, type: :graphql do + include_context "with a graphql class type" + + let(:signed_at) { nil } + let(:model) { create(:ps_closure, :with_results, signed_at:) } + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response["id"]).to eq(model.id.to_s) + end + end + + describe "polling_officer_notes" do + let(:query) { "{ pollingOfficerNotes }" } + + it "returns the polling officer notes" do + expect(response["pollingOfficerNotes"]).to eq(model.polling_officer_notes) + end + end + + describe "createdAt" do + let(:query) { "{ createdAt }" } + + it "returns when the closure was created" do + expect(response["createdAt"]).to eq(model.created_at.to_time.iso8601) + end + end + + describe "updatedAt" do + let(:query) { "{ updatedAt }" } + + it "returns when the closure was updated" do + expect(response["updatedAt"]).to eq(model.updated_at.to_time.iso8601) + end + end + + describe "election" do + let(:query) { "{ election { id } }" } + + it "returns the election for this closure" do + expect(response["election"]["id"]).to eq(model.election.id.to_s) + end + end + + describe "polling_station" do + let(:query) { "{ pollingStation { id } }" } + + it "returns the polling_station for this closure" do + expect(response["pollingStation"]["id"]).to eq(model.polling_station.id.to_s) + end + end + + describe "results" do + let(:query) { "{ results { id } }" } + + it "returns the results" do + ids = response["results"].map { |result| result["id"].to_i } + expect(ids).to eq(model.results.map(&:id)) + end + end + + describe "signed" do + let(:query) { "{ signed }" } + + context "when the closure is signed" do + let(:signed_at) { Time.current } + + it "returns true" do + expect(response["signed"]).to be true + end + end + + context "when the closure is not signed" do + it "returns false" do + expect(response["signed"]).to be_falsey + end + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/votings/polling_station_type_spec.rb b/decidim-elections/spec/types/decidim/votings/polling_station_type_spec.rb new file mode 100644 index 00000000..768ebbae --- /dev/null +++ b/decidim-elections/spec/types/decidim/votings/polling_station_type_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" + +module Decidim + module Votings + describe PollingStationType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:polling_station) } + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response["id"]).to eq(model.id.to_s) + end + end + + describe "title" do + let(:query) { "{ title { translation(locale: \"ca\") } }" } + + it "returns the polling station's title" do + expect(response["title"]["translation"]).to eq(model.title["ca"]) + end + end + + describe "address" do + let(:query) { "{ address }" } + + it "returns the polling station's address" do + expect(response["address"]).to eq(model.address) + end + end + + describe "coordinates" do + let(:query) { "{ coordinates { latitude longitude } }" } + + it "returns the polling station's address" do + expect(response["coordinates"]).to include( + "latitude" => model.latitude, + "longitude" => model.longitude + ) + end + end + + describe "location" do + let(:query) { "{ location { translation(locale: \"ca\") } }" } + + it "returns the polling station's location" do + expect(response["location"]["translation"]).to eq(model.location["ca"]) + end + end + + describe "locationHints" do + let(:query) { "{ locationHints { translation(locale: \"ca\") } }" } + + it "returns the polling station's location_hints" do + expect(response["locationHints"]["translation"]).to eq(model.location_hints["ca"]) + end + end + + describe "createdAt" do + let(:query) { "{ createdAt }" } + + it "returns when the polling station was created" do + expect(response["createdAt"]).to eq(model.created_at.to_time.iso8601) + end + end + + describe "updatedAt" do + let(:query) { "{ updatedAt }" } + + it "returns when the polling station was updated" do + expect(response["updatedAt"]).to eq(model.updated_at.to_time.iso8601) + end + end + + describe "voting" do + let(:query) { "{ voting { id } }" } + + it "returns the voting space for this polling station" do + expect(response["voting"]["id"]).to eq(model.voting.id.to_s) + end + end + end + end +end diff --git a/decidim-elections/spec/types/decidim/votings/voting_type_spec.rb b/decidim-elections/spec/types/decidim/votings/voting_type_spec.rb new file mode 100644 index 00000000..2b2206d0 --- /dev/null +++ b/decidim-elections/spec/types/decidim/votings/voting_type_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/type_context" + +module Decidim + module Votings + describe VotingType, type: :graphql do + include_context "with a graphql class type" + + let(:model) { create(:voting) } + + describe "id" do + let(:query) { "{ id }" } + + it "returns all the required fields" do + expect(response).to include("id" => model.id.to_s) + end + end + + describe "title" do + let(:query) { '{ title { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["title"]["translation"]).to eq(model.title["en"]) + end + end + + describe "slug" do + let(:query) { "{ slug }" } + + it "returns the voting's slug" do + expect(response["slug"]).to eq(model.slug) + end + end + + describe "createdAt" do + let(:query) { "{ createdAt }" } + + it "returns when the voting was created" do + expect(response["createdAt"]).to eq(model.created_at.to_time.iso8601) + end + end + + describe "updatedAt" do + let(:query) { "{ updatedAt }" } + + it "returns when the voting was updated" do + expect(response["updatedAt"]).to eq(model.updated_at.to_time.iso8601) + end + end + + describe "description" do + let(:query) { '{ description { translation(locale: "en")}}' } + + it "returns all the required fields" do + expect(response["description"]["translation"]).to eq(model.description["en"]) + end + end + + describe "startTime" do + let(:query) { "{ startTime }" } + + it "returns the voting's start time" do + expect(Time.zone.parse(response["startTime"])).to be_within(1.second).of(model.start_time) + end + end + + describe "endTime" do + let(:query) { "{ endTime }" } + + it "returns the voting's end time" do + expect(Time.zone.parse(response["endTime"])).to be_within(1.second).of(model.end_time) + end + end + + describe "scope" do + let(:query) { "{ scope { id } }" } + + it "has a scope" do + expect(response).to include("scope" => { "id" => model.scope.id.to_s }) + end + end + end + end +end diff --git a/decidim-elections/spec/types/integration_schema_spec.rb b/decidim-elections/spec/types/integration_schema_spec.rb new file mode 100644 index 00000000..8ec345fb --- /dev/null +++ b/decidim-elections/spec/types/integration_schema_spec.rb @@ -0,0 +1,231 @@ +# frozen_string_literal: true + +require "spec_helper" +require "decidim/api/test/component_context" + +describe "Decidim::Api::QueryType" do + include_context "with a graphql decidim component" + let(:component_type) { "Elections" } + let!(:current_component) { create(:elections_component, participatory_space: participatory_process) } + let!(:election) { create(:election, :complete, :published, :finished, component: current_component) } + + let(:election_single_result) do + { + "attachments" => [], + "bb_status" => election.bb_status, + "blocked" => election.blocked?, + "createdAt" => election.created_at.iso8601.to_s.gsub("Z", "+00:00"), + "description" => { "translation" => election.description[locale] }, + "endTime" => election.end_time.iso8601.to_s.gsub("Z", "+00:00"), + "id" => election.id.to_s, + "publishedAt" => election.published_at.iso8601.to_s.gsub("Z", "+00:00"), + + "questions" => election.questions.order(:id).map do |q| + { + "answers" => q.answers.order(:id).map do |a| + { + "attachments" => [], + "description" => begin + { "translation" => a.description[locale] } + rescue StandardError + nil + end, + "id" => a.id.to_s, + "proposals" => a.proposals.map { |p| { "id" => p.id.to_s } }, + "selected" => a.selected?, + "title" => { "translation" => a.title[locale] }, + "versions" => [], + "versionsCount" => 0, + "results_total" => a.results_total.to_i, + "weight" => a.weight.to_i + } + end, + "id" => q.id.to_s, + "maxSelections" => q.max_selections, + "minSelections" => q.min_selections, + "randomAnswersOrder" => q.random_answers_order, + "title" => { "translation" => q.title[locale] }, + "versions" => [], + "versionsCount" => 0, + "weight" => q.weight + } + end, + "startTime" => election.start_time.iso8601.to_s.gsub("Z", "+00:00"), + "title" => { "translation" => election.title[locale] }, + "trustees" => [], + "updatedAt" => election.updated_at.iso8601.to_s.gsub("Z", "+00:00"), + "versions" => [], + "versionsCount" => 0 + } + end + + let(:elections_data) do + { + "__typename" => "Elections", + "id" => current_component.id.to_s, + "name" => { "translation" => "Elections" }, + "elections" => { + "edges" => [ + { + "node" => election_single_result + } + ] + }, + "weight" => 0 + } + end + + describe "valid connection query" do + let(:component_fragment) do + %( + fragment fooComponent on Elections { + elections{ + edges{ + node{ + attachments { + thumbnail + } + bb_status + blocked + createdAt + description { + translation(locale: "en") + } + endTime + id + publishedAt + questions { + answers { + attachments { type} + description { + translation(locale: "en") + } + id + proposals {id } + selected + title { + translation(locale: "en") + } + versions { + id + } + versionsCount + results_total + weight + } + id + maxSelections + minSelections + randomAnswersOrder + title { + translation(locale: "en") + } + versions { + id + } + versionsCount + weight + } + startTime + title { + translation(locale: "en") + } + trustees { + id + } + updatedAt + versions { + id + } + versionsCount + } + } + } + } + ) + end + + it "executes sucessfully" do + expect { response }.not_to raise_error + end + + it do + expect(response["participatoryProcess"]["components"].first).to eq(elections_data) + end + end + + describe "valid query" do + let(:component_fragment) do + %( + fragment fooComponent on Elections { + election(id: #{election.id}){ + attachments { + thumbnail + } + bb_status + blocked + createdAt + description { + translation(locale: "en") + } + endTime + id + publishedAt + questions { + answers { + attachments { type} + description { + translation(locale: "en") + } + id + proposals {id } + selected + title { + translation(locale: "en") + } + versions { + id + } + versionsCount + results_total + weight + } + id + maxSelections + minSelections + randomAnswersOrder + title { + translation(locale: "en") + } + versions { + id + } + versionsCount + weight + } + startTime + title { + translation(locale: "en") + } + trustees { + id + } + updatedAt + versions { + id + } + versionsCount + } + } +) + end + + it "executes sucessfully" do + expect { response }.not_to raise_error + end + + it do + expect(response["participatoryProcess"]["components"].first["election"]).to eq(election_single_result) + end + end +end