decidim-elections 0.25.2 → 0.26.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/elections/election_m_cell.rb +1 -1
  3. data/app/cells/decidim/votings/content_blocks/highlighted_votings_cell.rb +12 -0
  4. data/app/cells/decidim/votings/content_blocks/landing_page/description_cell.rb +1 -1
  5. data/app/cells/decidim/votings/voting_m_cell.rb +1 -1
  6. data/app/commands/decidim/elections/admin/add_user_as_trustee.rb +1 -1
  7. data/app/commands/decidim/elections/admin/report_missing_trustee.rb +68 -0
  8. data/app/commands/decidim/elections/admin/setup_election.rb +1 -1
  9. data/app/commands/decidim/elections/admin/start_tally.rb +1 -1
  10. data/app/commands/decidim/votings/admin/create_ballot_style.rb +8 -5
  11. data/app/commands/decidim/votings/admin/destroy_ballot_style.rb +11 -3
  12. data/app/commands/decidim/votings/admin/update_ballot_style.rb +6 -1
  13. data/app/commands/decidim/votings/census/admin/create_dataset.rb +13 -5
  14. data/app/controllers/concerns/decidim/monitoring_committee_polling_station_closures/admin/filterable.rb +1 -1
  15. data/app/controllers/concerns/decidim/polling_stations/admin/filterable.rb +1 -1
  16. data/app/controllers/decidim/elections/admin/steps_controller.rb +26 -7
  17. data/app/controllers/decidim/votings/admin/ballot_styles_controller.rb +1 -1
  18. data/app/forms/decidim/elections/admin/action_form.rb +4 -0
  19. data/app/forms/decidim/elections/admin/report_missing_trustee_form.rb +22 -0
  20. data/app/forms/decidim/elections/admin/setup_form.rb +4 -0
  21. data/app/models/decidim/elections/action.rb +1 -1
  22. data/app/models/decidim/elections/trustee.rb +4 -0
  23. data/app/models/decidim/votings/ballot_style.rb +6 -0
  24. data/app/models/decidim/votings/census/dataset.rb +2 -0
  25. data/app/models/decidim/votings/monitoring_committee_member.rb +6 -0
  26. data/app/models/decidim/votings/polling_officer.rb +5 -0
  27. data/app/models/decidim/votings/polling_station.rb +6 -0
  28. data/app/packs/entrypoints/decidim_elections_admin_trustees_process.js +1 -0
  29. data/app/packs/src/decidim/elections/admin/pending_action.js +1 -1
  30. data/app/packs/src/decidim/elections/admin/trustees_process.js +125 -0
  31. data/app/packs/src/decidim/elections/election_log.js +89 -73
  32. data/app/packs/src/decidim/elections/trustee/key_ceremony.js +3 -3
  33. data/app/packs/src/decidim/elections/trustee/tally.js +3 -3
  34. data/app/packs/src/decidim/elections/trustee/trustee_zone.js +29 -18
  35. data/app/packs/src/decidim/elections/voter/casting-vote.js +1 -1
  36. data/app/packs/src/decidim/elections/voter/setup-vote.js +5 -5
  37. data/app/packs/src/decidim/elections/voter/verify-vote.js +1 -1
  38. data/app/packs/src/decidim/votings/in-person-vote.js +1 -1
  39. data/app/presenters/decidim/elections/admin_log/election_presenter.rb +34 -9
  40. data/app/presenters/decidim/elections/admin_log/trustee_presenter.rb +50 -0
  41. data/app/presenters/decidim/elections/trustee_presenter.rb +5 -1
  42. data/app/presenters/decidim/votings/admin_log/ballot_style_presenter.rb +35 -0
  43. data/app/presenters/decidim/votings/admin_log/monitoring_committee_member_presenter.rb +50 -0
  44. data/app/presenters/decidim/votings/admin_log/polling_officer_presenter.rb +50 -0
  45. data/app/presenters/decidim/votings/admin_log/polling_station_presenter.rb +29 -0
  46. data/app/presenters/decidim/votings/admin_log/voting_presenter.rb +1 -1
  47. data/app/presenters/decidim/votings/census/admin_log/dataset_presenter.rb +7 -6
  48. data/app/services/decidim/votings/voting_search.rb +2 -2
  49. data/app/views/decidim/elections/admin/steps/_create_election.html.erb +3 -3
  50. data/app/views/decidim/elections/admin/steps/_key_ceremony.html.erb +44 -9
  51. data/app/views/decidim/elections/admin/steps/_tally.html.erb +60 -10
  52. data/app/views/decidim/elections/admin/steps/index.html.erb +17 -15
  53. data/app/views/decidim/elections/elections/_filters_small_view.html.erb +3 -3
  54. data/app/views/decidim/elections/elections/election_log.html.erb +1 -1
  55. data/app/views/decidim/elections/elections/show.html.erb +1 -1
  56. data/app/views/decidim/elections/trustee_zone/elections/show.html.erb +1 -1
  57. data/app/views/decidim/elections/trustee_zone/trustees/show.html.erb +1 -1
  58. data/app/views/decidim/elections/votes/_onboarding_modal.html.erb +1 -1
  59. data/app/views/decidim/elections/votes/_show_casted.html.erb +1 -1
  60. data/app/views/decidim/elections/votes/_show_casting.html.erb +1 -1
  61. data/app/views/decidim/elections/votes/new.html.erb +3 -3
  62. data/app/views/decidim/elections/votes/verify.html.erb +1 -1
  63. data/app/views/decidim/votings/admin/votings/index.html.erb +1 -1
  64. data/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_count_error.html.erb +3 -2
  65. data/app/views/decidim/votings/polling_officer_zone/closures/_modal_ballots_results_count_error.html.erb +3 -2
  66. data/app/views/decidim/votings/polling_officer_zone/closures/_sign_form.html.erb +4 -3
  67. data/app/views/decidim/votings/polling_officer_zone/closures/edit.html.erb +1 -1
  68. data/app/views/decidim/votings/polling_officer_zone/closures/new.html.erb +1 -1
  69. data/app/views/decidim/votings/polling_officer_zone/in_person_votes/new.html.erb +1 -1
  70. data/app/views/decidim/votings/polling_officer_zone/in_person_votes/show.html.erb +1 -1
  71. data/app/views/decidim/votings/votings/_filters_small_view.html.erb +3 -3
  72. data/app/views/decidim/votings/votings/_promoted_voting.html.erb +1 -1
  73. data/config/assets.rb +1 -0
  74. data/config/locales/ca.yml +2 -17
  75. data/config/locales/cs.yml +45 -13
  76. data/config/locales/de.yml +0 -15
  77. data/config/locales/en.yml +41 -11
  78. data/config/locales/es-MX.yml +2 -17
  79. data/config/locales/es-PY.yml +2 -17
  80. data/config/locales/es.yml +43 -15
  81. data/config/locales/eu.yml +24 -17
  82. data/config/locales/fi-plain.yml +41 -13
  83. data/config/locales/fi.yml +41 -13
  84. data/config/locales/fr-CA.yml +32 -14
  85. data/config/locales/fr.yml +32 -14
  86. data/config/locales/ga-IE.yml +0 -9
  87. data/config/locales/gl.yml +21 -9
  88. data/config/locales/hu.yml +0 -5
  89. data/config/locales/it.yml +36 -12
  90. data/config/locales/ja.yml +234 -16
  91. data/config/locales/lb-LU.yml +24 -0
  92. data/config/locales/nl.yml +224 -11
  93. data/config/locales/no.yml +0 -5
  94. data/config/locales/pl.yml +13 -12
  95. data/config/locales/pt-BR.yml +5 -16
  96. data/config/locales/pt.yml +43 -10
  97. data/config/locales/ro-RO.yml +24 -17
  98. data/config/locales/sv.yml +3 -8
  99. data/config/locales/tr-TR.yml +3 -9
  100. data/config/locales/val-ES.yml +1 -0
  101. data/config/locales/zh-CN.yml +0 -4
  102. data/lib/decidim/elections/component.rb +2 -1
  103. data/lib/decidim/elections/test/factories.rb +2 -2
  104. data/lib/decidim/elections/version.rb +1 -1
  105. metadata +31 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f09833ea70c1970efdc0d52cf8014a4bc8cbb2a09d4ca15d49e315d7b02d88f
4
- data.tar.gz: e000962fcfe04a7d1acee472798e65208c7fa9a22dba090a5da6119aaf7013ce
3
+ metadata.gz: bc8a779cca40d6e069fdbdff54905690f2f58dbd33cdcca28c81e8b0c63a2a81
4
+ data.tar.gz: c8453d445d3d6406ef3dc6f27aed2b1ecbeaa8f69e9e8f221d3a0dae8e61c3c7
5
5
  SHA512:
6
- metadata.gz: 95f86d7f46367adeb8f5acbfa57b7cb0a78a0ca1aa4657b5d1e3d77f54bab4fabb20e143559c7e4ce071dbdbb4797dd00ec0d5cd270699461ee7be40a531db79
7
- data.tar.gz: d6048e8719815cb218327d71c12f9a6c81a2a5a6093ea05c9b0ba77585885b76ee62e8069b8510594d32da8340d7667c92688aba9a8dfb499920f20a896784f8
6
+ metadata.gz: 89e34b56ab3fe554dd329b790d60fe79e8d5a53ddfd8829b8aaf29c624ee6e860f4d80d7d4dd6d96fa7daa6bb6bbc4d5dd5eb307df187efdc08d6b579f1ebae5
7
+ data.tar.gz: f0c23adcd2198063a4ce7e9e27a3100c57fe881123d94e1922a891cab82fa1d1fbc8b26c00a10291eb3cc52f8cc26572870489794cad903e3f8483f9c7fae088
@@ -53,7 +53,7 @@ module Decidim
53
53
 
54
54
  def description
55
55
  text = super
56
- text.gsub!(/^<p>/, "<p>#{render :badge}")
56
+ text.sub!(/<p>/, "<p>#{render :badge}")
57
57
  html_truncate(text, length: 100)
58
58
  end
59
59
 
@@ -6,6 +6,10 @@ module Decidim
6
6
  class HighlightedVotingsCell < Decidim::ViewModel
7
7
  delegate :current_user, to: :controller
8
8
 
9
+ cache :show, expires_in: 10.minutes, if: :perform_caching? do
10
+ cache_hash
11
+ end
12
+
9
13
  def show
10
14
  render if highlighted_votings.any?
11
15
  end
@@ -27,6 +31,14 @@ module Decidim
27
31
  def decidim_votings
28
32
  Decidim::Votings::Engine.routes.url_helpers
29
33
  end
34
+
35
+ private
36
+
37
+ def cache_hash
38
+ hash = []
39
+ hash.push(I18n.locale)
40
+ hash.join(Decidim.cache_key_separator)
41
+ end
30
42
  end
31
43
  end
32
44
  end
@@ -16,7 +16,7 @@ module Decidim
16
16
  end
17
17
 
18
18
  def description_text
19
- decidim_sanitize(translated_attribute(current_participatory_space.description))
19
+ decidim_sanitize_editor(translated_attribute(current_participatory_space.description))
20
20
  end
21
21
 
22
22
  def button_show_more_text
@@ -41,7 +41,7 @@ module Decidim
41
41
  # makes the layout look good.
42
42
  def description
43
43
  text = super
44
- text.gsub!(/^<p>/, "<p>#{render :badge}")
44
+ text.sub!(/<p>/, "<p>#{render :badge}")
45
45
  html_truncate(text, length: 100)
46
46
  end
47
47
 
@@ -74,7 +74,7 @@ module Decidim
74
74
  resource: form.current_participatory_space,
75
75
  affected_users: [form.user]
76
76
  }
77
- Decidim::EventsManager.publish(data)
77
+ Decidim::EventsManager.publish(**data)
78
78
  end
79
79
  end
80
80
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Elections
5
+ module Admin
6
+ # This command gets called to report a missing trustee during the tally process.
7
+ class ReportMissingTrustee < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # form - A ReportMissingTrusteeForm object with the information needed to report the missing trustee.
11
+ def initialize(form)
12
+ @form = form
13
+ end
14
+
15
+ # Public: Reports the missing trustee for the Election tally process.
16
+ #
17
+ # Broadcasts :ok if it worked, :invalid otherwise.
18
+ def call
19
+ return broadcast(:invalid) if form.invalid?
20
+
21
+ transaction do
22
+ log_action
23
+ report_missing_trustee
24
+ end
25
+
26
+ broadcast(:ok)
27
+ rescue StandardError => e
28
+ broadcast(:invalid, e.message)
29
+ end
30
+
31
+ private
32
+
33
+ attr_accessor :form
34
+
35
+ delegate :election, :bulletin_board, :trustee, to: :form
36
+
37
+ def log_action
38
+ Decidim.traceability.perform_action!(
39
+ :report_missing_trustee,
40
+ election,
41
+ form.current_user,
42
+ extra: {
43
+ trustee_id: form.trustee_id,
44
+ name: trustee.name,
45
+ nickname: trustee.user.nickname
46
+ },
47
+ visibility: "all"
48
+ )
49
+ end
50
+
51
+ def report_missing_trustee
52
+ bulletin_board.report_missing_trustee(election.id, form.trustee.slug) do |message_id|
53
+ create_election_action(message_id)
54
+ end
55
+ end
56
+
57
+ def create_election_action(message_id)
58
+ Decidim::Elections::Action.create!(
59
+ election: election,
60
+ action: :report_missing_trustee,
61
+ message_id: message_id,
62
+ status: :pending
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -142,7 +142,7 @@ module Decidim
142
142
  affected_users: trustee
143
143
  }
144
144
 
145
- Decidim::EventsManager.publish(data)
145
+ Decidim::EventsManager.publish(**data)
146
146
  end
147
147
 
148
148
  # Since machine_translations return a nested hash but Electionguard and other
@@ -7,7 +7,7 @@ module Decidim
7
7
  class StartTally < Rectify::Command
8
8
  # Public: Initializes the command.
9
9
  #
10
- # form - A VotePeriodForm object with the information needed to start or end the vote period
10
+ # form - An ActionForm object with the information needed to perform an action
11
11
  def initialize(form)
12
12
  @form = form
13
13
  end
@@ -34,13 +34,16 @@ module Decidim
34
34
  attr_reader :form, :ballot_style
35
35
 
36
36
  def create_ballot_style!
37
- attributes = {
38
- code: form.code
37
+ params = {
38
+ code: form.code,
39
+ voting: form.current_participatory_space
39
40
  }
40
41
 
41
- @ballot_style = Decidim::Votings::BallotStyle.create!(
42
- voting: form.current_participatory_space,
43
- attributes: attributes
42
+ @ballot_style = Decidim.traceability.create!(
43
+ Decidim::Votings::BallotStyle,
44
+ form.current_user,
45
+ params,
46
+ visibility: "all"
44
47
  )
45
48
  end
46
49
 
@@ -5,8 +5,9 @@ module Decidim
5
5
  module Admin
6
6
  # A command with the business logic to delete the ballot style
7
7
  class DestroyBallotStyle < Rectify::Command
8
- def initialize(ballot_style)
8
+ def initialize(ballot_style, current_user)
9
9
  @ballot_style = ballot_style
10
+ @current_user = current_user
10
11
  end
11
12
 
12
13
  # Executes the command. Broadcast this events:
@@ -22,10 +23,17 @@ module Decidim
22
23
 
23
24
  private
24
25
 
25
- attr_reader :ballot_style
26
+ attr_reader :ballot_style, :current_user
26
27
 
27
28
  def destroy_ballot_style!
28
- ballot_style.destroy!
29
+ Decidim.traceability.perform_action!(
30
+ :delete,
31
+ ballot_style,
32
+ current_user,
33
+ visibility: "all"
34
+ ) do
35
+ ballot_style.destroy!
36
+ end
29
37
  end
30
38
  end
31
39
  end
@@ -42,7 +42,12 @@ module Decidim
42
42
  code: form.code
43
43
  }
44
44
 
45
- ballot_style.update!(attributes)
45
+ Decidim.traceability.update!(
46
+ ballot_style,
47
+ form.current_user,
48
+ attributes,
49
+ visibility: "all"
50
+ )
46
51
  end
47
52
 
48
53
  def destroy_removed_ballot_style_questions!
@@ -31,7 +31,7 @@ module Decidim
31
31
  end
32
32
 
33
33
  if dataset
34
- CSV.foreach(form.file.tempfile.path, col_sep: ";", headers: true) do |row|
34
+ CSV.foreach(form.file.tempfile.path, col_sep: ";", headers: true, converters: ->(f) { f&.strip }) do |row|
35
35
  CreateDatumJob.perform_later(current_user, dataset, row.fields)
36
36
  end
37
37
  end
@@ -57,11 +57,19 @@ module Decidim
57
57
  end
58
58
 
59
59
  def csv_header_invalid?
60
- CSV.parse_line(File.open(form.file.tempfile.path, &:readline), col_sep: ";").size != expected_header_size
60
+ CSV.parse_line(File.open(form.file.tempfile.path), col_sep: ";", headers: true, header_converters: :symbol).headers != expected_headers
61
61
  end
62
62
 
63
- def expected_header_size
64
- @expected_header_size ||= form.current_participatory_space.has_ballot_styles? ? 9 : 8
63
+ def headers
64
+ [:document_id, :document_type, :date_of_birth, :full_name, :full_address, :postal_code, :mobile_phone_number, :email_address]
65
+ end
66
+
67
+ def ballot_style_headers
68
+ headers.push(:ballot_style_code)
69
+ end
70
+
71
+ def expected_headers
72
+ @expected_headers ||= form.current_participatory_space.has_ballot_styles? ? ballot_style_headers : headers
65
73
  end
66
74
 
67
75
  def csv_rows
@@ -73,7 +81,7 @@ module Decidim
73
81
  end
74
82
 
75
83
  def file_lines_count(file_path)
76
- `wc -l "#{file_path}"`.strip.split(" ")[0].to_i
84
+ `wc -l "#{file_path.shellescape}"`.strip.split(" ")[0].to_i
77
85
  end
78
86
  end
79
87
  end
@@ -14,9 +14,9 @@ module Decidim
14
14
  private
15
15
 
16
16
  def base_query
17
+ # Includes the officers (president and managers) and their correspective decidim users when they(=officers) are present
17
18
  query =
18
19
  collection
19
- # Includes the officers (president and managers) and their correspective decidim users when they(=officers) are present
20
20
  .joins("LEFT JOIN decidim_votings_polling_officers president ON president.presided_polling_station_id = decidim_votings_polling_stations.id
21
21
  LEFT JOIN decidim_users president_user ON president_user.id = president.decidim_user_id
22
22
  LEFT JOIN decidim_votings_polling_officers managers ON managers.managed_polling_station_id = decidim_votings_polling_stations.id
@@ -14,8 +14,8 @@ module Decidim
14
14
  private
15
15
 
16
16
  def base_query
17
+ # Includes the officers (president and managers) and their correspective decidim users when they(=officers) are present
17
18
  query = collection
18
- # Includes the officers (president and managers) and their correspective decidim users when they(=officers) are present
19
19
  .joins("LEFT JOIN decidim_votings_polling_officers president ON president.presided_polling_station_id = decidim_votings_polling_stations.id
20
20
  LEFT JOIN decidim_users president_user ON president_user.id = president.decidim_user_id
21
21
  LEFT JOIN decidim_votings_polling_officers managers ON managers.managed_polling_station_id = decidim_votings_polling_stations.id
@@ -5,8 +5,9 @@ module Decidim
5
5
  module Admin
6
6
  # This controller allows to manage the steps of an election.
7
7
  class StepsController < Admin::ApplicationController
8
+ helper Decidim::ApplicationHelper
8
9
  helper StepsHelper
9
- helper_method :elections, :election, :current_step, :vote_stats, :bulletin_board_server
10
+ helper_method :elections, :election, :current_step, :vote_stats, :bulletin_board_server, :authority_public_key, :election_unique_id, :quorum, :missing_trustees_allowed
10
11
 
11
12
  def index
12
13
  enforce_permission_to :read, :steps, election: election
@@ -22,10 +23,12 @@ module Decidim
22
23
  redirect_to election_steps_path(election) && return unless params[:id] == current_step
23
24
 
24
25
  @form = form(current_step_form_class).from_params(params, election: election)
25
- if @form.pending_action
26
- Decidim::Elections::Admin::UpdateActionStatus.call(@form.pending_action)
27
- return redirect_to election_steps_path(election)
28
- end
26
+ Decidim::Elections::Admin::UpdateActionStatus.call(@form.pending_action) if @form.pending_action
27
+
28
+ # check pending action status mode
29
+ return render json: { status: @form.pending_action&.status } if params[:check_pending_action]
30
+
31
+ return redirect_to election_steps_path(election) if @form.pending_action
29
32
 
30
33
  current_step_command_class.call(@form) do
31
34
  on(:ok) do
@@ -45,8 +48,22 @@ module Decidim
45
48
 
46
49
  private
47
50
 
48
- def bulletin_board_server
49
- Decidim::Elections.bulletin_board.bulletin_board_server
51
+ delegate :bulletin_board_server, :authority_slug, :quorum, to: :bulletin_board_client
52
+
53
+ def bulletin_board_client
54
+ Decidim::Elections.bulletin_board
55
+ end
56
+
57
+ def missing_trustees_allowed
58
+ @missing_trustees_allowed ||= Decidim::Elections.bulletin_board.number_of_trustees - Decidim::Elections.bulletin_board.quorum
59
+ end
60
+
61
+ def election_unique_id
62
+ @election_unique_id ||= Decidim::BulletinBoard::MessageIdentifier.unique_election_id(authority_slug, election.id)
63
+ end
64
+
65
+ def authority_public_key
66
+ @authority_public_key ||= bulletin_board_client.authority_public_key.to_json
50
67
  end
51
68
 
52
69
  def current_step_form_class
@@ -56,6 +73,7 @@ module Decidim
56
73
  "key_ceremony_ended" => VotePeriodForm,
57
74
  "vote" => VotePeriodForm,
58
75
  "vote_ended" => ActionForm,
76
+ "tally" => ReportMissingTrusteeForm,
59
77
  "tally_ended" => ActionForm
60
78
  }[current_step]
61
79
  end
@@ -67,6 +85,7 @@ module Decidim
67
85
  "key_ceremony_ended" => StartVote,
68
86
  "vote" => EndVote,
69
87
  "vote_ended" => StartTally,
88
+ "tally" => ReportMissingTrustee,
70
89
  "tally_ended" => PublishResults
71
90
  }[current_step]
72
91
  end
@@ -60,7 +60,7 @@ module Decidim
60
60
  def destroy
61
61
  enforce_permission_to :delete, :ballot_style, ballot_style: current_ballot_style, voting: current_voting
62
62
 
63
- DestroyBallotStyle.call(current_ballot_style) do
63
+ DestroyBallotStyle.call(current_ballot_style, current_user) do
64
64
  on(:ok) do
65
65
  flash[:notice] = t("destroy.success", scope: "decidim.votings.admin.ballot_styles")
66
66
  end
@@ -7,6 +7,10 @@ module Decidim
7
7
  class ActionForm < Decidim::Form
8
8
  validates :pending_action, absence: true
9
9
 
10
+ def main_button?
11
+ true
12
+ end
13
+
10
14
  def messages
11
15
  @messages ||= {}
12
16
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Elections
5
+ module Admin
6
+ # This class holds a form to report a missing trustee during the tally process.
7
+ class ReportMissingTrusteeForm < ActionForm
8
+ attribute :trustee_id, Integer
9
+
10
+ validates :trustee_id, presence: true
11
+
12
+ def trustee
13
+ @trustee ||= Decidim::Elections::Trustee.find(trustee_id)
14
+ end
15
+
16
+ def main_button?
17
+ false
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -57,6 +57,10 @@ module Decidim
57
57
  @bulletin_board ||= context[:bulletin_board] || Decidim::Elections.bulletin_board
58
58
  end
59
59
 
60
+ def main_button?
61
+ true
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def choose_random_trustees
@@ -7,7 +7,7 @@ module Decidim
7
7
  belongs_to :election, foreign_key: "decidim_elections_election_id", class_name: "Decidim::Elections::Election"
8
8
 
9
9
  enum status: [:pending, :accepted, :rejected]
10
- enum action: [:start_key_ceremony, :start_vote, :end_vote, :start_tally, :publish_results]
10
+ enum action: [:start_key_ceremony, :start_vote, :end_vote, :start_tally, :report_missing_trustee, :publish_results]
11
11
  end
12
12
  end
13
13
  end
@@ -15,6 +15,10 @@ module Decidim
15
15
  exists?(user: user)
16
16
  end
17
17
 
18
+ def self.log_presenter_class_for(_log)
19
+ Decidim::Elections::AdminLog::TrusteePresenter
20
+ end
21
+
18
22
  def self.for(user)
19
23
  find_by(user: user)
20
24
  end
@@ -17,6 +17,8 @@ module Decidim
17
17
  inverse_of: :ballot_style,
18
18
  dependent: :nullify
19
19
 
20
+ alias participatory_space voting
21
+
20
22
  def slug
21
23
  "#{voting.slug}_#{code.parameterize}-#{id}"
22
24
  end
@@ -24,6 +26,10 @@ module Decidim
24
26
  def questions_for(election)
25
27
  questions.where(election: election)
26
28
  end
29
+
30
+ def self.log_presenter_class_for(_log)
31
+ Decidim::Votings::AdminLog::BallotStylePresenter
32
+ end
27
33
  end
28
34
  end
29
35
  end
@@ -23,6 +23,8 @@ module Decidim
23
23
 
24
24
  validates :file, presence: true
25
25
 
26
+ alias participatory_space voting
27
+
26
28
  def self.log_presenter_class_for(_log)
27
29
  Decidim::Votings::Census::AdminLog::DatasetPresenter
28
30
  end
@@ -11,6 +11,12 @@ module Decidim
11
11
 
12
12
  validate :user_and_voting_same_organization
13
13
 
14
+ alias participatory_space voting
15
+
16
+ def self.log_presenter_class_for(_log)
17
+ Decidim::Votings::AdminLog::MonitoringCommitteeMemberPresenter
18
+ end
19
+
14
20
  private
15
21
 
16
22
  # Private: check if the voting and the user have the same organization
@@ -22,6 +22,7 @@ module Decidim
22
22
  validate :either_president_or_manager
23
23
 
24
24
  delegate :name, :nickname, :email, to: :user
25
+ alias participatory_space voting
25
26
 
26
27
  # Allow ransacker to search by presided/managed polling station title
27
28
  %w(managed_station presided_station).each do |table|
@@ -56,6 +57,10 @@ module Decidim
56
57
  presided_polling_station || managed_polling_station
57
58
  end
58
59
 
60
+ def self.log_presenter_class_for(_log)
61
+ Decidim::Votings::AdminLog::PollingOfficerPresenter
62
+ end
63
+
59
64
  private
60
65
 
61
66
  # Private: check if the voting and the user have the same organization
@@ -34,6 +34,8 @@ module Decidim
34
34
  validate :polling_station_managers_same_voting
35
35
  validate :polling_station_president_same_voting
36
36
 
37
+ alias participatory_space voting
38
+
37
39
  # Allow ransacker to search for a key in a hstore column (`title`.`en`)
38
40
  ransacker :title do |parent|
39
41
  Arel::Nodes::InfixOperation.new("->>", parent.table[:title], Arel::Nodes.build_quoted(I18n.locale.to_s))
@@ -61,6 +63,10 @@ module Decidim
61
63
  closures.find_by(election: election)
62
64
  end
63
65
 
66
+ def self.log_presenter_class_for(_log)
67
+ Decidim::Votings::AdminLog::PollingStationPresenter
68
+ end
69
+
64
70
  private
65
71
 
66
72
  # Private: check if the president is in the same voting
@@ -0,0 +1 @@
1
+ import "src/decidim/elections/admin/trustees_process";
@@ -1,4 +1,4 @@
1
- import { Client } from "@codegram/decidim-bulletin_board";
1
+ import { Client } from "@decidim/decidim-bulletin_board";
2
2
 
3
3
  $(() => {
4
4
  const $form = $("form.step");
@@ -0,0 +1,125 @@
1
+ import {
2
+ Client,
3
+ Election,
4
+ MessageParser
5
+ } from "@decidim/decidim-bulletin_board";
6
+
7
+ const WAIT_TIME_MS = 10 * 1_000;
8
+
9
+ $(async () => {
10
+ const $trusteesProcess = $("#trustees_process");
11
+ const $checkingTrustees = $trusteesProcess.find(".trustee");
12
+ const electionUniqueId = $trusteesProcess.data("electionUniqueId");
13
+ const processType = $trusteesProcess.data("processType");
14
+ const bulletinBoardClient = new Client({
15
+ apiEndpointUrl: $trusteesProcess.data("apiEndpointUrl")
16
+ });
17
+ const election = new Election({
18
+ uniqueId: electionUniqueId,
19
+ bulletinBoardClient,
20
+ typesFilter: ["create_election", processType]
21
+ });
22
+
23
+ const authorityPublicKeyJSON = JSON.stringify(
24
+ $trusteesProcess.data("authorityPublicKey")
25
+ );
26
+ const parser = new MessageParser({ authorityPublicKeyJSON });
27
+ const trusteesStatuses = {};
28
+ let lastMessageIndex = 0;
29
+
30
+ const missingTrusteesAllowed = $trusteesProcess.data("missingTrusteesAllowed") || 0;
31
+ const checkPendingActionPath = $trusteesProcess.data("checkPendingActionPath");
32
+
33
+ // Fix buttons formaction, that is not working properly
34
+ const $form = $("form.step");
35
+ $form.find("button").on("click", (event) => {
36
+ $form.attr("action", $(event.currentTarget).attr("formaction"));
37
+ $form.trigger("submit");
38
+ });
39
+
40
+ const updateTrusteesStatuses = async () => {
41
+ await election.getLogEntries();
42
+
43
+ for (
44
+ ;
45
+ lastMessageIndex < election.logEntries.length;
46
+ lastMessageIndex += 1
47
+ ) {
48
+ const { messageIdentifier, decodedData } = await parser.parse(
49
+ election.logEntries[lastMessageIndex]
50
+ );
51
+
52
+ if (messageIdentifier.author.type === "t") {
53
+ trusteesStatuses[messageIdentifier.author.id] = true;
54
+ } else if (
55
+ messageIdentifier.type === "tally" &&
56
+ messageIdentifier.subtype === "missing_trustee" &&
57
+ !(decodedData.trustee_id in trusteesStatuses)
58
+ ) {
59
+ trusteesStatuses[decodedData.trustee_id] = false;
60
+ }
61
+ }
62
+ }
63
+
64
+ const checkPendingAction = async () => {
65
+ if (!checkPendingActionPath) {
66
+ return false
67
+ }
68
+
69
+ try {
70
+ const response = await $.ajax({
71
+ url: checkPendingActionPath,
72
+ method: "PATCH",
73
+ contentType: "application/json",
74
+ headers: {
75
+ "X-CSRF-Token": $("meta[name=csrf-token]").attr("content")
76
+ }
77
+ })
78
+
79
+ return response && response.status === "pending";
80
+ } catch (err) {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ const checkTrusteesActivity = async () => {
86
+ await updateTrusteesStatuses();
87
+ const pendingAction = await checkPendingAction();
88
+ const missingTrustees = Object.values(trusteesStatuses).filter(
89
+ (present) => !present
90
+ ).length;
91
+ const allowReportMissing = missingTrustees < missingTrusteesAllowed;
92
+
93
+ $checkingTrustees.each((_index, trustee) => {
94
+ const $trustee = $(trustee);
95
+ const trusteeSlug = $trustee.data("trusteeSlug");
96
+
97
+ if (trusteeSlug in trusteesStatuses) {
98
+ if (missingTrusteesAllowed > 0) {
99
+ $trustee.find(".js-report-missing-trustee").addClass("hide");
100
+ }
101
+ $trustee.removeClass("loading");
102
+ $trustee.find(".loading").hide();
103
+ if (trusteesStatuses[trusteeSlug]) {
104
+ $trustee.find(".active").removeClass("hide");
105
+ $trustee.find(".missing").addClass("hide");
106
+ } else {
107
+ $trustee.find(".missing").removeClass("hide");
108
+ }
109
+ } else if (allowReportMissing && !pendingAction) {
110
+ $trustee.find(".js-report-missing-trustee").removeClass("hide");
111
+ }
112
+ });
113
+
114
+ if (
115
+ Object.keys(trusteesStatuses).length === $checkingTrustees.length &&
116
+ missingTrustees <= missingTrusteesAllowed && !pendingAction
117
+ ) {
118
+ $(".js-continue-link").removeClass("disabled");
119
+ } else {
120
+ setTimeout(checkTrusteesActivity, WAIT_TIME_MS);
121
+ }
122
+ };
123
+
124
+ await checkTrusteesActivity();
125
+ });