decidim-action_delegator 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -58
  3. data/Rakefile +2 -2
  4. data/app/commands/decidim/action_delegator/admin/create_delegation.rb +1 -3
  5. data/app/commands/decidim/action_delegator/admin/create_setting.rb +5 -2
  6. data/app/commands/decidim/action_delegator/admin/update_setting.rb +4 -2
  7. data/app/controllers/concerns/decidim/action_delegator/devise/sessions_controller_override.rb +1 -1
  8. data/app/controllers/concerns/decidim/action_delegator/election_per_question_votes_controller_override.rb +65 -0
  9. data/app/controllers/concerns/decidim/action_delegator/election_votes_controller_override.rb +38 -0
  10. data/app/controllers/concerns/decidim/action_delegator/votes_controller_methods.rb +46 -0
  11. data/app/controllers/decidim/action_delegator/admin/application_controller.rb +11 -5
  12. data/app/controllers/decidim/action_delegator/admin/delegations_controller.rb +2 -10
  13. data/app/controllers/decidim/action_delegator/admin/invite_participants_controller.rb +0 -8
  14. data/app/controllers/decidim/action_delegator/admin/manage_delegations_controller.rb +13 -20
  15. data/app/controllers/decidim/action_delegator/admin/manage_participants_controller.rb +11 -19
  16. data/app/controllers/decidim/action_delegator/admin/participants_controller.rb +1 -9
  17. data/app/controllers/decidim/action_delegator/admin/permissions_controller.rb +1 -24
  18. data/app/controllers/decidim/action_delegator/admin/ponderations_controller.rb +0 -8
  19. data/app/controllers/decidim/action_delegator/admin/settings_controller.rb +12 -12
  20. data/app/controllers/decidim/action_delegator/application_controller.rb +3 -3
  21. data/app/controllers/decidim/action_delegator/elections/admin/results_controller.rb +58 -0
  22. data/app/controllers/decidim/action_delegator/elections/results_controller.rb +36 -0
  23. data/app/controllers/decidim/action_delegator/user_delegations_controller.rb +1 -1
  24. data/app/controllers/decidim/action_delegator/verifications/delegations_verifier/authorizations_controller.rb +14 -18
  25. data/app/forms/decidim/action_delegator/admin/action_delegator_census_form.rb +44 -0
  26. data/app/forms/decidim/action_delegator/admin/csv_import_form.rb +17 -0
  27. data/app/forms/decidim/action_delegator/admin/ponderation_form.rb +2 -1
  28. data/app/forms/decidim/action_delegator/admin/setting_form.rb +16 -10
  29. data/app/forms/decidim/action_delegator/censuses/internal_users_form.rb +32 -0
  30. data/app/forms/decidim/action_delegator/verifications/delegations_verifier_form.rb +41 -25
  31. data/app/helpers/decidim/action_delegator/admin/{delegation_helper.rb → settings_helper.rb} +3 -16
  32. data/app/helpers/decidim/action_delegator/settings_helper.rb +124 -0
  33. data/app/jobs/decidim/action_delegator/admin/invite_participants_job.rb +1 -1
  34. data/app/jobs/decidim/action_delegator/send_sms_job.rb +2 -2
  35. data/app/jobs/decidim/action_delegator/twilio_send_sms_job.rb +2 -2
  36. data/app/models/decidim/action_delegator/delegation.rb +15 -14
  37. data/app/models/decidim/action_delegator/participant.rb +7 -10
  38. data/app/models/decidim/action_delegator/ponderation.rb +0 -2
  39. data/app/models/decidim/action_delegator/setting.rb +14 -32
  40. data/app/overrides/decidim/elections/admin/dashboard/_results/_add_results_contents.html.erb.deface +10 -0
  41. data/app/overrides/decidim/elections/admin/dashboard/_results/_add_results_tabs.html.erb.deface +3 -0
  42. data/app/overrides/decidim/elections/elections/_election_aside/add_delegation_buttons.html.erb.deface +3 -0
  43. data/app/overrides/decidim/elections/elections/_vote_results/replace_results_div.html.erb.deface +8 -0
  44. data/app/overrides/decidim/elections/per_question_votes/show/add_delegation_hidden_input.html.erb.deface +3 -0
  45. data/app/overrides/decidim/elections/per_question_votes/show/add_delegation_id_to_links.html.erb.deface +3 -0
  46. data/app/overrides/decidim/elections/per_question_votes/waiting/add_delegation_buttons.html.erb.deface +3 -0
  47. data/app/overrides/decidim/elections/votes/receipt/add_delegation_buttons.html.erb.deface +3 -0
  48. data/app/packs/entrypoints/decidim_action_delegator_elections.js +1 -0
  49. data/app/packs/src/decidim/action_delegator/elections_live_results.js +160 -0
  50. data/app/permissions/decidim/action_delegator/admin/permissions.rb +29 -0
  51. data/app/permissions/decidim/action_delegator/permissions.rb +6 -30
  52. data/app/presenters/decidim/action_delegator/setting_presenter.rb +33 -0
  53. data/app/queries/decidim/action_delegator/action_delegator_census_users.rb +51 -0
  54. data/app/queries/decidim/action_delegator/authorized_resources.rb +28 -0
  55. data/app/queries/decidim/action_delegator/election_settings.rb +23 -0
  56. data/app/queries/decidim/action_delegator/{delegated_votes_versions.rb → elections_delegated_votes_versions.rb} +8 -8
  57. data/app/queries/decidim/action_delegator/elections_question_responses_by_type.rb +33 -0
  58. data/app/queries/decidim/action_delegator/elections_question_weighted_responses.rb +28 -0
  59. data/app/queries/decidim/action_delegator/elections_votes_with_ponderations.rb +62 -0
  60. data/app/services/decidim/action_delegator/participants_csv_importer.rb +1 -1
  61. data/app/services/decidim/action_delegator/sms_gateway.rb +2 -2
  62. data/app/views/decidim/action_delegator/admin/censuses/_action_delegator_census_form.html.erb +44 -0
  63. data/app/views/decidim/action_delegator/admin/censuses/_action_delegator_census_options_form.html.erb +25 -0
  64. data/app/views/decidim/action_delegator/admin/delegations/index.html.erb +12 -13
  65. data/app/views/decidim/action_delegator/admin/delegations/new.html.erb +29 -27
  66. data/app/views/decidim/action_delegator/admin/manage_delegations/new.html.erb +36 -24
  67. data/app/views/decidim/action_delegator/admin/manage_participants/new.html.erb +47 -33
  68. data/app/views/decidim/action_delegator/admin/participants/_form.html.erb +14 -8
  69. data/app/views/decidim/action_delegator/admin/participants/edit.html.erb +11 -13
  70. data/app/views/decidim/action_delegator/admin/participants/index.html.erb +20 -25
  71. data/app/views/decidim/action_delegator/admin/participants/new.html.erb +11 -13
  72. data/app/views/decidim/action_delegator/admin/ponderations/_form.html.erb +13 -7
  73. data/app/views/decidim/action_delegator/admin/ponderations/edit.html.erb +11 -13
  74. data/app/views/decidim/action_delegator/admin/ponderations/index.html.erb +8 -11
  75. data/app/views/decidim/action_delegator/admin/ponderations/new.html.erb +11 -13
  76. data/app/views/decidim/action_delegator/admin/settings/_form.html.erb +26 -12
  77. data/app/views/decidim/action_delegator/admin/settings/_participants_sync_check.html.erb +1 -1
  78. data/app/views/decidim/action_delegator/admin/settings/_setting_checks.html.erb +11 -16
  79. data/app/views/decidim/action_delegator/admin/settings/edit.html.erb +11 -12
  80. data/app/views/decidim/action_delegator/admin/settings/index.html.erb +22 -19
  81. data/app/views/decidim/action_delegator/admin/settings/new.html.erb +11 -12
  82. data/app/views/decidim/action_delegator/admin/shared/_tabs_menu.html.erb +15 -0
  83. data/app/views/decidim/action_delegator/censuses/_internal_users_form.html.erb +61 -0
  84. data/app/views/decidim/action_delegator/elections/_delegation_buttons.html.erb +10 -0
  85. data/app/views/decidim/action_delegator/elections/_normal_election_buttons.html.erb +13 -0
  86. data/app/views/decidim/action_delegator/elections/_per_question_buttons.html.erb +9 -0
  87. data/app/views/decidim/action_delegator/elections/_per_question_waiting_buttons.html.erb +19 -0
  88. data/app/views/decidim/action_delegator/elections/_vote_results.html.erb +10 -0
  89. data/app/views/decidim/action_delegator/elections/_vote_results_question.html.erb +13 -0
  90. data/app/views/decidim/action_delegator/elections/admin/dashboard/_by_type_and_weight.html.erb +45 -0
  91. data/app/views/decidim/action_delegator/elections/admin/dashboard/_results_tabs.html.erb +5 -0
  92. data/app/views/decidim/action_delegator/elections/admin/dashboard/_results_type_info.html.erb +4 -0
  93. data/app/views/decidim/action_delegator/elections/admin/dashboard/_sum_of_weights.html.erb +43 -0
  94. data/app/views/decidim/action_delegator/elections/admin/dashboard/_totals.html.erb +27 -0
  95. data/app/views/decidim/action_delegator/user_delegations/index.html.erb +13 -14
  96. data/app/views/decidim/action_delegator/verifications/delegations_verifier/authorizations/edit.html.erb +19 -29
  97. data/app/views/decidim/action_delegator/verifications/delegations_verifier/authorizations/new.html.erb +42 -40
  98. data/config/assets.rb +3 -36
  99. data/config/i18n-tasks.yml +27 -5
  100. data/config/locales/ca.yml +103 -146
  101. data/config/locales/cs.yml +161 -145
  102. data/config/locales/en.yml +110 -75
  103. data/config/locales/es.yml +108 -154
  104. data/db/migrate/20200824113801_create_settings.rb +1 -1
  105. data/db/migrate/20250729104037_add_title_to_action_delegator_settings.rb +31 -0
  106. data/lib/decidim/action_delegator/admin_engine.rb +72 -45
  107. data/lib/decidim/action_delegator/common_rake.rb +13 -0
  108. data/lib/decidim/action_delegator/engine.rb +48 -19
  109. data/lib/decidim/action_delegator/test/delegation_examples.rb +144 -0
  110. data/lib/decidim/action_delegator/test/factories.rb +11 -6
  111. data/lib/decidim/action_delegator/verifications/delegations_authorizer.rb +80 -47
  112. data/lib/decidim/action_delegator/version.rb +3 -3
  113. data/lib/decidim/action_delegator.rb +8 -26
  114. data/lib/tasks/migrate_consultations.rake +382 -0
  115. data/lib/tasks/upgrade_tasks.rake +5 -0
  116. data/package.json +10 -27
  117. metadata +72 -122
  118. data/app/commands/concerns/decidim/action_delegator/consultations/multiple_vote_question_override.rb +0 -31
  119. data/app/commands/concerns/decidim/action_delegator/consultations/vote_question_override.rb +0 -44
  120. data/app/commands/decidim/action_delegator/admin/fix_resource_permissions.rb +0 -46
  121. data/app/commands/decidim/action_delegator/vote_delegation.rb +0 -28
  122. data/app/controllers/concerns/decidim/action_delegator/consultations/consultations_controller_override.rb +0 -16
  123. data/app/controllers/concerns/decidim/action_delegator/consultations/question_multiple_votes_controller_override.rb +0 -29
  124. data/app/controllers/concerns/decidim/action_delegator/consultations/question_votes_controller_override.rb +0 -57
  125. data/app/controllers/concerns/decidim/action_delegator/consultations/questions_controller_override.rb +0 -16
  126. data/app/controllers/concerns/decidim/action_delegator/needs_consultation_styles.rb +0 -24
  127. data/app/controllers/decidim/action_delegator/admin/consultations/exports_controller.rb +0 -27
  128. data/app/controllers/decidim/action_delegator/admin/consultations_controller.rb +0 -47
  129. data/app/controllers/decidim/action_delegator/admin/exports/_sum_of_weights_controller.rb +0 -15
  130. data/app/controllers/decidim/action_delegator/questions_summary_controller.rb +0 -14
  131. data/app/forms/concerns/decidim/action_delegator/consultations/vote_form_override.rb +0 -15
  132. data/app/helpers/decidim/action_delegator/delegation_helper.rb +0 -13
  133. data/app/jobs/decidim/action_delegator/export_consultation_results_job.rb +0 -51
  134. data/app/models/concerns/decidim/action_delegator/consultations/question_override.rb +0 -36
  135. data/app/models/concerns/decidim/action_delegator/consultations/vote_override.rb +0 -15
  136. data/app/models/decidim/action_delegator/unversioned_vote.rb +0 -19
  137. data/app/models/decidim/action_delegator/whodunnit_vote.rb +0 -28
  138. data/app/overrides/decidim/consultations/admin/consultations/results/add_ongoing_warning.html.erb.deface +0 -3
  139. data/app/overrides/decidim/consultations/consultations/_question/add_delegation_link.html.erb.deface +0 -3
  140. data/app/overrides/decidim/consultations/consultations/_question/replace_vote_info.html.erb.deface +0 -4
  141. data/app/overrides/decidim/consultations/consultations/_regular_questions/prevent_empty_questions.html.erb.deface +0 -10
  142. data/app/overrides/decidim/consultations/consultations/_regular_questions/remove_highlighted_scopes.html.erb.deface +0 -5
  143. data/app/overrides/decidim/consultations/question_multiple_votes/_form/add_delegation_notice.html.erb.deface +0 -8
  144. data/app/overrides/decidim/consultations/questions/_results/replace_results.html.erb.deface +0 -3
  145. data/app/overrides/decidim/consultations/questions/_vote_button/add_delegations_link.html.erb.deface +0 -3
  146. data/app/overrides/decidim/consultations/questions/_vote_button/add_modal.html.erb.deface +0 -3
  147. data/app/overrides/decidim/consultations/questions/_vote_button/add_modal_javascript.html.erb.deface +0 -3
  148. data/app/overrides/decidim/consultations/questions/_vote_button/replace_delegation_to_multivote_link.html.erb.deface +0 -8
  149. data/app/overrides/decidim/consultations/questions/_vote_modal/add_delegation_callout.html.erb.deface +0 -3
  150. data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_delegation_callout.html.erb.deface +0 -3
  151. data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_hidden_field.html.erb.deface +0 -3
  152. data/app/overrides/layouts/decidim/_consultation_header/add_consultation_callout.html.erb.deface +0 -9
  153. data/app/overrides/layouts/decidim/_question_header/add_consultation_callout.html.erb.deface +0 -9
  154. data/app/overrides/layouts/decidim/admin/remove_deprecation.rb +0 -10
  155. data/app/packs/entrypoints/decidim_action_delegator.scss +0 -1
  156. data/app/packs/entrypoints/decidim_action_delegator_questions.js +0 -5
  157. data/app/packs/entrypoints/decidim_action_delegator_questions_summary.js +0 -1
  158. data/app/packs/src/decidim/action_delegator/questions.js +0 -33
  159. data/app/packs/src/decidim/action_delegator/summary.js +0 -24
  160. data/app/packs/stylesheets/decidim/action_delegator/questions.scss +0 -26
  161. data/app/permissions/concerns/decidim/action_delegator/consultations/permissions_override.rb +0 -35
  162. data/app/presenters/decidim/action_delegator/question_with_totals.rb +0 -24
  163. data/app/queries/decidim/action_delegator/consultation_delegations.rb +0 -25
  164. data/app/queries/decidim/action_delegator/delegates_votes_by_consultation.rb +0 -24
  165. data/app/queries/decidim/action_delegator/delegates_votes_by_question.rb +0 -26
  166. data/app/queries/decidim/action_delegator/delegation_votes.rb +0 -30
  167. data/app/queries/decidim/action_delegator/grantee_delegations.rb +0 -24
  168. data/app/queries/decidim/action_delegator/organization_delegations.rb +0 -26
  169. data/app/queries/decidim/action_delegator/organization_settings.rb +0 -31
  170. data/app/queries/decidim/action_delegator/responses.rb +0 -24
  171. data/app/queries/decidim/action_delegator/responses_by_membership.rb +0 -58
  172. data/app/queries/decidim/action_delegator/scrutiny.rb +0 -87
  173. data/app/queries/decidim/action_delegator/setting_delegations.rb +0 -19
  174. data/app/queries/decidim/action_delegator/sum_of_membership_weight.rb +0 -44
  175. data/app/queries/decidim/action_delegator/sum_of_weights.rb +0 -25
  176. data/app/queries/decidim/action_delegator/type_and_weight.rb +0 -26
  177. data/app/queries/decidim/action_delegator/voted_with_ponderations.rb +0 -30
  178. data/app/queries/decidim/action_delegator/votes_count_aggregation.rb +0 -34
  179. data/app/serializers/decidim/action_delegator/consultation_results_serializer.rb +0 -19
  180. data/app/views/decidim/action_delegator/admin/consultations/_ongoing_consultation_warning.html.erb +0 -3
  181. data/app/views/decidim/action_delegator/admin/consultations/results.html.erb +0 -65
  182. data/app/views/decidim/action_delegator/admin/consultations/weighted_results.html.erb +0 -66
  183. data/app/views/decidim/action_delegator/consultations/_link_to_question.html.erb +0 -11
  184. data/app/views/decidim/action_delegator/consultations/_link_with_results.html.erb +0 -11
  185. data/app/views/decidim/action_delegator/consultations/questions/_callout.html.erb +0 -5
  186. data/app/views/decidim/action_delegator/consultations/questions/_delegations_modal.html.erb +0 -34
  187. data/app/views/decidim/action_delegator/consultations/questions/_link_to_delegations.html.erb +0 -15
  188. data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_active.html.erb +0 -32
  189. data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_finished.html.erb +0 -9
  190. data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_upcoming.html.erb +0 -8
  191. data/app/views/decidim/action_delegator/consultations/questions/_weight_results.html.erb +0 -8
  192. data/app/views/decidim/consultations/question_votes/_callout.html.erb +0 -47
  193. data/app/views/decidim/consultations/question_votes/update_vote_button.js.erb +0 -82
  194. data/lib/tasks/import_direct_verification.rake +0 -30
@@ -14,25 +14,25 @@ module Decidim
14
14
  # this is the SmsGateway provided by this module
15
15
  # Note that it will be ignored if you provide your own SmsGateway in Decidim.sms_gateway_service
16
16
  config_accessor :sms_gateway_service do
17
- "Decidim::ActionDelegator::SmsGateway"
17
+ Decidim::Env.new("AD_SMS_GATEWAY_SERVICE", "Decidim::ActionDelegator::SmsGateway").to_s
18
18
  end
19
19
 
20
20
  # The default expiration time for the integrated authorization
21
21
  # if zero, the authorization won't be registered
22
22
  config_accessor :authorization_expiration_time do
23
- 3.months
23
+ Decidim::Env.new("AD_AUTHORIZATION_EXPIRATION_TIME").presence&.to_i || 3.months
24
24
  end
25
25
 
26
26
  # Put this to false if you don't want to allow administrators to invite users not registered
27
27
  # in the platform when uploading a census (inviting users without permission can be a GDPR offense).
28
28
  config_accessor :allow_to_invite_users do
29
- true
29
+ Decidim::Env.new("AD_ALLOW_TO_INVITE_USERS", true).present?
30
30
  end
31
31
 
32
32
  # If true, tries to automatically authorize users when they log in with the "Corporate Governance Verifier"
33
33
  # Note that this is only possible when the verifier is configured to use only the email (if SMS is required, the user will have to do the standard verification process)
34
34
  config_accessor :authorize_on_login do
35
- true
35
+ Decidim::Env.new("AD_AUTHORIZE_ON_LOGIN", true).present?
36
36
  end
37
37
 
38
38
  # used for comparing phone numbers from a census list and the ones introduced by the user
@@ -40,30 +40,12 @@ module Decidim
40
40
  # if you have a census list with +34 666 666 666 and the user introduces 0034666666666 or 666666666, they will be considered the same
41
41
  # can be empty or null if yo don't want to check different combinations of prefixes
42
42
  config_accessor :phone_prefixes do
43
- %w(+34 0034 34)
43
+ Decidim::Env.new("AD_PHONE_PREFIXES", "+34,0034,34").to_array
44
44
  end
45
45
 
46
46
  # The regex for validating phone numbers
47
47
  config_accessor :phone_regex do
48
- /^\d{6,15}$/ # 6 to 15 digits
49
- end
50
-
51
- # Consultations has an annoying and totally useless deprecation warning
52
- # This plugin removes it by default.
53
- # If you want to keep it, you can set this config to false
54
- config_accessor :remove_consultation_deprecation_warning do
55
- true
56
- end
57
-
58
- # In a consultation the highlighted questions are duplicated in the list of regular questions
59
- # this maintains the highlighted questions in the highlighted list and removes them from the regular list
60
- config_accessor :remove_duplicated_highlighted_questions do
61
- true
62
- end
63
-
64
- # If true, admins can view the result of a consultation even if the consultation is ongoing
65
- config_accessor :admin_preview_results do
66
- true
48
+ Decidim::Env.new("AD_PHONE_REGEX", '^\d{6,15}$').to_s # 6 to 15 digits
67
49
  end
68
50
  end
69
51
  end
@@ -74,7 +56,7 @@ end
74
56
  # manage their delegations
75
57
  Decidim.register_global_engine(
76
58
  :decidim_action_delegator, # this is the name of the global method to access engine routes
77
- ::Decidim::ActionDelegator::Engine,
59
+ Decidim::ActionDelegator::Engine,
78
60
  at: "/action_delegator"
79
61
  )
80
62
 
@@ -82,6 +64,6 @@ Decidim.register_global_engine(
82
64
  # create their own
83
65
  Decidim.register_global_engine(
84
66
  :decidim_admin_action_delegator,
85
- ::Decidim::ActionDelegator::AdminEngine,
67
+ Decidim::ActionDelegator::AdminEngine,
86
68
  at: "/admin/action_delegator"
87
69
  )
@@ -0,0 +1,382 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :action_delegator do
4
+ desc "Migrate old decidim-consultations data to decidim-elections component"
5
+ task :migrate_consultations, [:component_id, :consultation_id] => :environment do |_task, args|
6
+ component_id = args[:component_id]
7
+ consultation_id = args[:consultation_id]
8
+
9
+ if component_id.blank?
10
+ puts "ERROR: component_id is required"
11
+ puts "Usage: rake action_delegator:migrate_consultations[COMPONENT_ID] or rake action_delegator:migrate_consultations[COMPONENT_ID,CONSULTATION_ID]"
12
+ exit 1
13
+ end
14
+
15
+ component = Decidim::Component.find_by(id: component_id)
16
+ unless component&.manifest_name == "elections"
17
+ puts "ERROR: Component ##{component_id} not found or is not an elections component"
18
+ exit 1
19
+ end
20
+
21
+ # Define inline models for old consultation tables (module no longer exists in Decidim)
22
+ module Legacy
23
+ class Consultation < ApplicationRecord
24
+ self.table_name = "decidim_consultations"
25
+ has_many :questions, class_name: "Legacy::Question", foreign_key: :decidim_consultation_id
26
+ end
27
+
28
+ class Question < ApplicationRecord
29
+ self.table_name = "decidim_consultations_questions"
30
+ belongs_to :consultation, class_name: "Legacy::Consultation", foreign_key: :decidim_consultation_id
31
+ has_many :responses, class_name: "Legacy::Response", foreign_key: :decidim_consultations_questions_id
32
+ has_many :votes, class_name: "Legacy::Vote", foreign_key: :decidim_consultation_question_id
33
+ end
34
+
35
+ class Response < ApplicationRecord
36
+ self.table_name = "decidim_consultations_responses"
37
+ belongs_to :question, class_name: "Legacy::Question", foreign_key: :decidim_consultations_questions_id
38
+ belongs_to :response_group, class_name: "Legacy::ResponseGroup", foreign_key: :decidim_consultations_response_group_id, optional: true
39
+ end
40
+
41
+ class ResponseGroup < ApplicationRecord
42
+ self.table_name = "decidim_consultations_response_groups"
43
+ belongs_to :question, class_name: "Legacy::Question", foreign_key: :decidim_consultations_questions_id
44
+ has_many :responses, class_name: "Legacy::Response", foreign_key: :decidim_consultations_response_group_id
45
+ end
46
+
47
+ class Vote < ApplicationRecord
48
+ self.table_name = "decidim_consultations_votes"
49
+ belongs_to :question, class_name: "Legacy::Question", foreign_key: :decidim_consultation_question_id
50
+ belongs_to :response, class_name: "Legacy::Response", foreign_key: :decidim_consultations_response_id
51
+ belongs_to :author, class_name: "Decidim::User", foreign_key: :decidim_author_id
52
+ end
53
+ end
54
+
55
+ # Check if consultations table exists
56
+ unless ActiveRecord::Base.connection.table_exists?("decidim_consultations")
57
+ puts "ERROR: decidim_consultations table not found. Make sure you have legacy data to migrate."
58
+ exit 1
59
+ end
60
+
61
+ # Fetch consultations to migrate
62
+ consultations = if consultation_id.present?
63
+ Legacy::Consultation.where(id: consultation_id)
64
+ else
65
+ Legacy::Consultation.where(decidim_organization_id: component.organization.id)
66
+ end
67
+
68
+ if consultations.empty?
69
+ puts "No consultations found"
70
+ exit 0
71
+ end
72
+
73
+ # Calculate source data statistics
74
+ source_stats = {
75
+ consultations: consultations.count,
76
+ questions: 0,
77
+ responses: 0,
78
+ votes: 0
79
+ }
80
+
81
+ consultations.each do |c|
82
+ source_stats[:questions] += c.questions.count
83
+ c.questions.each do |q|
84
+ source_stats[:responses] += q.responses.count
85
+ source_stats[:votes] += q.votes.count
86
+ end
87
+ end
88
+
89
+ migrated_stats = {
90
+ elections: 0,
91
+ questions: 0,
92
+ responses: 0,
93
+ votes: 0,
94
+ skipped_votes: 0,
95
+ skipped_consultations: 0,
96
+ errors: []
97
+ }
98
+
99
+ puts "\nStarting migration of #{source_stats[:consultations]} consultations from decidim-consultations to decidim-elections"
100
+ puts "Component: ##{component.id} - #{component.name}"
101
+ puts "Consultations:"
102
+ consultations.each do |c|
103
+ puts " • ##{c.id} - #{c.title}"
104
+ end
105
+ puts "\nSource data summary:"
106
+ puts "-" * 70
107
+ puts "TYPE Elections Questions Responses Votes"
108
+ puts "-" * 70
109
+ puts format("%-12s %12d %12d %12d %12d", "SOURCE", source_stats[:consultations], source_stats[:questions], source_stats[:responses], source_stats[:votes])
110
+ puts "-" * 70
111
+ puts ""
112
+ unless ENV["CI"].present? || ENV["FORCE_MIGRATION"].to_s.downcase == "true"
113
+ print "Continue migration? (y/N): "
114
+ answer = $stdin.gets.chomp.downcase
115
+ unless answer == "y"
116
+ puts "Aborted by user."
117
+ exit 0
118
+ end
119
+ end
120
+
121
+ def determine_census_config(consultation, setting)
122
+ resource_handlers = extract_authorization_handlers(consultation)
123
+ census_manifest = determine_census_manifest(setting, resource_handlers)
124
+ authorization_handlers = determine_authorization_handlers(setting, resource_handlers, census_manifest)
125
+ census_settings = build_census_settings(census_manifest, setting, authorization_handlers)
126
+
127
+ [census_manifest, census_settings]
128
+ end
129
+
130
+ def extract_authorization_handlers(consultation)
131
+ question_ids = consultation.questions.pluck(:id)
132
+ return [] unless ActiveRecord::Base.connection.table_exists?("decidim_resource_permissions") && question_ids.any?
133
+
134
+ handlers = []
135
+ perms = ActiveRecord::Base.connection.execute(<<-SQL.squish)
136
+ SELECT permissions FROM decidim_resource_permissions
137
+ WHERE resource_type = 'Decidim::Consultations::Question'
138
+ AND resource_id IN (#{question_ids.join(",")})
139
+ AND permissions::jsonb -> 'vote' -> 'authorization_handlers' IS NOT NULL
140
+ SQL
141
+
142
+ perms.each do |perm|
143
+ perm_handlers = begin
144
+ JSON.parse(perm["permissions"])["vote"]["authorization_handlers"].keys
145
+ rescue StandardError
146
+ []
147
+ end
148
+ handlers.concat(perm_handlers)
149
+ end
150
+ handlers.uniq
151
+ end
152
+
153
+ def determine_census_manifest(setting, resource_handlers)
154
+ return "action_delegator_census" if setting&.delegations&.any?
155
+ return "internal_users" if setting&.authorization_method.present? || resource_handlers.any?
156
+
157
+ "internal_users"
158
+ end
159
+
160
+ def determine_authorization_handlers(setting, resource_handlers, census_manifest)
161
+ return [] if census_manifest == "action_delegator_census"
162
+ return ["delegations_verifier"] if setting&.authorization_method.present?
163
+
164
+ resource_handlers
165
+ end
166
+
167
+ def build_census_settings(census_manifest, setting, authorization_handlers)
168
+ return { "setting_id" => setting.id } if census_manifest == "action_delegator_census" && setting
169
+
170
+ return {} if authorization_handlers.empty?
171
+
172
+ {
173
+ authorization_handlers: authorization_handlers.index_with do |handler|
174
+ handler == "delegations_verifier" && setting ? { "options" => { "setting" => setting.id.to_s } } : { "options" => {} }
175
+ end
176
+ }
177
+ end
178
+
179
+ def build_question_description(old_question)
180
+ parts = [old_question.subtitle, old_question.what_is_decided].compact.compact_blank
181
+ return {} if parts.empty?
182
+
183
+ parts_by_locale = parts.each_with_object({}) do |part, result|
184
+ part.each do |locale, text|
185
+ result[locale] ||= []
186
+ result[locale] << text if text.present?
187
+ end
188
+ end
189
+ parts_by_locale.transform_values { |arr| arr.join("\n\n") }
190
+ end
191
+
192
+ def migrate_question_votes(old_question, new_question, response_mapping, migrated_stats)
193
+ votes_migrated = 0
194
+ votes_skipped = 0
195
+
196
+ old_question.votes.find_each do |old_vote|
197
+ user = old_vote.author
198
+ unless user
199
+ votes_skipped += 1
200
+ migrated_stats[:skipped_votes] += 1
201
+ next
202
+ end
203
+
204
+ new_response_id = response_mapping[old_vote.decidim_consultations_response_id]
205
+ unless new_response_id
206
+ votes_skipped += 1
207
+ migrated_stats[:skipped_votes] += 1
208
+ next
209
+ end
210
+
211
+ new_vote = Decidim::Elections::Vote.new(
212
+ question: new_question,
213
+ response_option_id: new_response_id,
214
+ voter_uid: user.to_global_id.to_s,
215
+ created_at: old_vote.created_at,
216
+ updated_at: old_vote.updated_at
217
+ )
218
+
219
+ if new_vote.save
220
+ votes_migrated += 1
221
+ migrated_stats[:votes] += 1
222
+ else
223
+ votes_skipped += 1
224
+ migrated_stats[:skipped_votes] += 1
225
+ puts " ✗ Failed to migrate Vote ##{old_vote.id}: #{new_vote.errors.full_messages.join(", ")}"
226
+ end
227
+ end
228
+
229
+ puts " ✓ Migrated #{votes_migrated} votes, skipped #{votes_skipped} votes"
230
+
231
+ [votes_migrated, votes_skipped]
232
+ end
233
+
234
+ consultations.find_each do |consultation|
235
+ ActiveRecord::Base.transaction do
236
+ setting = Decidim::ActionDelegator::Setting.find_by(decidim_consultation_id: consultation.id)
237
+
238
+ # Check if already migrated (idempotency check by matching title)
239
+ existing_election = Decidim::Elections::Election.joins(:component)
240
+ .where(component: component, title: consultation.title)
241
+ .first
242
+
243
+ if existing_election
244
+ cons_title = consultation.title["en"] || consultation.title.values.first
245
+ puts " ✓ Consultation ##{consultation.id} (#{cons_title}) already migrated to Election ##{existing_election.id}"
246
+ migrated_stats[:skipped_consultations] += 1
247
+ next
248
+ end
249
+
250
+ census_manifest, census_settings = determine_census_config(consultation, setting)
251
+
252
+ # Create Election from Consultation
253
+ election = Decidim::Elections::Election.new(
254
+ component: component,
255
+ title: consultation.title,
256
+ description: consultation.description,
257
+ start_at: consultation.start_voting_date&.to_time&.beginning_of_day,
258
+ end_at: consultation.end_voting_date&.to_time&.end_of_day,
259
+ published_at: consultation.published_at,
260
+ published_results_at: consultation.results_published_at,
261
+ results_availability: "after_end",
262
+ census_manifest: census_manifest,
263
+ census_settings: census_settings,
264
+ created_at: consultation.created_at,
265
+ updated_at: consultation.updated_at
266
+ )
267
+
268
+ if election.save
269
+ migrated_stats[:elections] += 1
270
+ puts " ✓ Created Election ##{election.id} from Consultation ##{consultation.id}"
271
+
272
+ consultation.questions.order(:order).each_with_index do |old_question, index|
273
+ new_question = Decidim::Elections::Question.new(
274
+ election: election,
275
+ body: old_question.title.transform_values { |t| ActionController::Base.helpers.strip_tags(t) },
276
+ description: build_question_description(old_question),
277
+ position: old_question.order || index,
278
+ question_type: (old_question.max_votes.to_i > 1 ? "multiple_option" : "single_option"),
279
+ created_at: old_question.created_at,
280
+ updated_at: old_question.updated_at
281
+ )
282
+
283
+ unless new_question.save
284
+ migrated_stats[:errors] << "Question creation failed: #{new_question.errors.full_messages.join(", ")}"
285
+ puts " ✗ Failed to create Question from Old Question ##{old_question.id}: #{new_question.errors.full_messages.join(", ")}"
286
+ next
287
+ end
288
+
289
+ migrated_stats[:questions] += 1
290
+
291
+ response_mapping = {}
292
+ old_question.responses.each do |old_response|
293
+ title = old_response.title
294
+ if old_response.response_group
295
+ title = title.to_h do |locale, text|
296
+ group_text = old_response.response_group.title[locale]
297
+ combined_text = [group_text, text].compact.join(" - ")
298
+ [locale, combined_text]
299
+ end
300
+ end
301
+ new_response = Decidim::Elections::ResponseOption.new(
302
+ question: new_question,
303
+ body: title,
304
+ created_at: old_response.created_at,
305
+ updated_at: old_response.updated_at
306
+ )
307
+
308
+ if new_response.save
309
+ response_mapping[old_response.id] = new_response.id
310
+ migrated_stats[:responses] += 1
311
+ else
312
+ migrated_stats[:errors] << "Response failed: #{new_response.errors.full_messages.join(", ")}"
313
+ puts " ✗ Failed to create Response from Old Response ##{old_response.id}: #{new_response.errors.full_messages.join(", ")}"
314
+ end
315
+ end
316
+
317
+ puts " ✓ Created Question ##{new_question.id} with #{response_mapping.size} Responses"
318
+
319
+ migrate_question_votes(old_question, new_question, response_mapping, migrated_stats)
320
+
321
+ new_question.response_options.each { |ro| Decidim::Elections::ResponseOption.reset_counters(ro.id, :votes) }
322
+ Decidim::Elections::Question.reset_counters(new_question.id, :votes)
323
+ end
324
+
325
+ # Update election votes count
326
+ election.update_votes_count!
327
+ else
328
+ cons_title = consultation.title["en"] || consultation.title.values.first
329
+ migrated_stats[:errors] << "Consultation ##{consultation.id} (#{cons_title}): #{election.errors.full_messages.join(", ")}"
330
+ puts " ✗ Failed to create Election from Consultation ##{consultation.id}: #{election.errors.full_messages.join(", ")}"
331
+ raise ActiveRecord::Rollback
332
+ end
333
+ rescue StandardError => e
334
+ cons_title = begin
335
+ consultation.title["en"] || consultation.title.values.first
336
+ rescue StandardError
337
+ "ID #{consultation.id}"
338
+ end
339
+ migrated_stats[:errors] << "Consultation ##{consultation.id} (#{cons_title}): #{e.message}"
340
+ puts " ✗ Exception migrating Consultation ##{consultation.id} (#{cons_title}): #{e.message}"
341
+ Rails.logger.error e.backtrace.join("\n")
342
+ raise ActiveRecord::Rollback
343
+ end
344
+ end
345
+
346
+ puts "\n#{"-" * 70}"
347
+ puts "Migration results: Consultations → Elections"
348
+ puts "-" * 70
349
+ puts " Elections Questions Responses Votes"
350
+ puts "-" * 70
351
+ puts format("%-12s %12d %12d %12d %12d", "SOURCE", source_stats[:consultations], source_stats[:questions], source_stats[:responses], source_stats[:votes])
352
+ puts format("%-12s %12d %12d %12d %12d", "MIGRATED", migrated_stats[:elections], migrated_stats[:questions], migrated_stats[:responses], migrated_stats[:votes])
353
+
354
+ puts format("%-12s %12d %12s %12s %12s", "EXISTED", migrated_stats[:skipped_consultations], "-", "-", "-") if migrated_stats[:skipped_consultations].positive?
355
+
356
+ puts format("%-12s %12s %12s %12s %12d", "SKIPPED", "-", "-", "-", migrated_stats[:skipped_votes]) if migrated_stats[:skipped_votes].positive?
357
+
358
+ if migrated_stats[:errors].any?
359
+ puts "\nErrors (#{migrated_stats[:errors].size}):"
360
+ migrated_stats[:errors].each { |err| puts " • #{err}" }
361
+ end
362
+
363
+ puts ""
364
+ total_processed = migrated_stats[:elections] + migrated_stats[:skipped_consultations]
365
+ success = total_processed == source_stats[:consultations] &&
366
+ migrated_stats[:questions] == source_stats[:questions] &&
367
+ migrated_stats[:responses] == source_stats[:responses] &&
368
+ migrated_stats[:votes] == source_stats[:votes] &&
369
+ migrated_stats[:errors].empty? &&
370
+ (migrated_stats[:skipped_votes]).zero?
371
+
372
+ if success
373
+ if migrated_stats[:skipped_consultations].positive?
374
+ puts "✓ All data processed (#{migrated_stats[:elections]} migrated, #{migrated_stats[:skipped_consultations]} already existed)"
375
+ else
376
+ puts "✓ All data migrated successfully"
377
+ end
378
+ else
379
+ puts "⚠ Some data was not migrated (see details above)"
380
+ end
381
+ end
382
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rake::Task["decidim:choose_target_plugins"].enhance do
4
+ ENV["FROM"] = "#{ENV.fetch("FROM", nil)},decidim_action_delegator"
5
+ end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decidim-action_delegator",
3
- "version": "0.6.0",
3
+ "version": "0.9.0",
4
4
  "description": "Delgation actions (votes) for Decidim",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/coopdevs/decidim-module-action_delegator.git"
19
+ "url": "git+https://github.com/Openpoke/decidim-module-action_delegator.git"
20
20
  },
21
21
  "keywords": [
22
22
  "decidim"
@@ -24,34 +24,17 @@
24
24
  "author": "ivan@pokecode.net",
25
25
  "license": "AGPL-3.0-or-later",
26
26
  "bugs": {
27
- "url": "https://github.com/coopdevs/decidim-module-action_delegator/issues"
27
+ "url": "https://github.com/Openpoke/decidim-module-action_delegator/issues"
28
28
  },
29
- "homepage": "https://github.com/coopdevs/decidim-module-action_delegator#readme",
29
+ "homepage": "https://github.com/Openpoke/decidim-module-action_delegator#readme",
30
30
  "stylelint": {
31
- "extends": "@decidim/stylelint-config",
32
- "rules" : {
33
- "block-opening-brace-space-before": "always"
34
- }
31
+ "extends": "@decidim/stylelint-config"
35
32
  },
36
33
  "devDependencies": {
37
- "@babel/eslint-parser": "^7.19.1",
38
- "@babel/plugin-proposal-class-properties": "^7.18.6",
39
- "@babel/plugin-proposal-private-methods": "^7.18.6",
40
- "@babel/plugin-proposal-private-property-in-object": "^7.20.5",
41
- "@babel/plugin-transform-classes": "^7.20.7",
42
- "@babel/plugin-transform-regenerator": "^7.20.5",
43
- "@babel/plugin-transform-runtime": "^7.19.6",
44
- "@babel/preset-env": "^7.20.2",
45
- "@babel/preset-react": "^7.18.6",
46
- "@decidim/dev": "0.27.1",
47
- "@decidim/eslint-config": "0.27.1",
48
- "@decidim/stylelint-config": "0.27.1",
49
- "eslint-config-prettier": "^8.2.0",
50
- "eslint-config-standard": "^11.0.0",
51
- "eslint-plugin-import": "^2.22.0",
52
- "eslint-plugin-standard": "^3.1.0",
53
- "sass-embedded": "~1.57.1"
54
- },
55
- "dependencies": {
34
+ "@decidim/dev": "0.30",
35
+ "@decidim/eslint-config": "0.30",
36
+ "@decidim/prettier-config": "0.30",
37
+ "@decidim/stylelint-config": "0.30",
38
+ "postcss-scss": "^4.0.9"
56
39
  }
57
40
  }