decidim-action_delegator 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +98 -35
  3. data/app/commands/{decidim → concerns/decidim}/action_delegator/consultations/vote_question_override.rb +9 -1
  4. data/app/commands/decidim/action_delegator/admin/create_delegation.rb +5 -1
  5. data/app/commands/decidim/action_delegator/admin/create_participant.rb +41 -0
  6. data/app/commands/decidim/action_delegator/admin/create_ponderation.rb +38 -0
  7. data/app/commands/decidim/action_delegator/admin/create_setting.rb +49 -0
  8. data/app/commands/decidim/action_delegator/admin/fix_resource_permissions.rb +46 -0
  9. data/app/commands/decidim/action_delegator/admin/update_participant.rb +42 -0
  10. data/app/commands/decidim/action_delegator/admin/update_ponderation.rb +41 -0
  11. data/app/commands/decidim/action_delegator/admin/update_setting.rb +66 -0
  12. data/app/controllers/concerns/decidim/action_delegator/consultations/consultations_controller_override.rb +16 -0
  13. data/app/controllers/{decidim → concerns/decidim}/action_delegator/consultations/question_multiple_votes_controller_override.rb +2 -1
  14. data/app/controllers/{decidim → concerns/decidim}/action_delegator/consultations/question_votes_controller_override.rb +2 -0
  15. data/app/controllers/concerns/decidim/action_delegator/consultations/questions_controller_override.rb +16 -0
  16. data/app/controllers/concerns/decidim/action_delegator/needs_consultation_styles.rb +24 -0
  17. data/app/controllers/decidim/action_delegator/admin/consultations_controller.rb +22 -8
  18. data/app/controllers/decidim/action_delegator/admin/delegations_controller.rb +9 -9
  19. data/app/controllers/decidim/action_delegator/admin/invite_participants_controller.rb +84 -0
  20. data/app/controllers/decidim/action_delegator/admin/manage_delegations_controller.rb +48 -0
  21. data/app/controllers/decidim/action_delegator/admin/manage_participants_controller.rb +59 -0
  22. data/app/controllers/decidim/action_delegator/admin/participants_controller.rb +102 -0
  23. data/app/controllers/decidim/action_delegator/admin/permissions_controller.rb +40 -0
  24. data/app/controllers/decidim/action_delegator/admin/ponderations_controller.rb +101 -0
  25. data/app/controllers/decidim/action_delegator/admin/settings_controller.rb +51 -12
  26. data/app/controllers/decidim/action_delegator/verifications/delegations_verifier/authorizations_controller.rb +108 -0
  27. data/app/forms/decidim/action_delegator/admin/delegation_form.rb +23 -4
  28. data/app/forms/decidim/action_delegator/admin/invitation_participant_form.rb +10 -0
  29. data/app/forms/decidim/action_delegator/admin/participant_form.rb +44 -0
  30. data/app/forms/decidim/action_delegator/admin/ponderation_form.rb +28 -0
  31. data/app/forms/decidim/action_delegator/admin/setting_form.rb +28 -0
  32. data/app/forms/decidim/action_delegator/verifications/delegations_verifier_form.rb +131 -0
  33. data/app/helpers/decidim/action_delegator/admin/delegation_helper.rb +40 -3
  34. data/app/helpers/decidim/action_delegator/delegation_helper.rb +13 -0
  35. data/app/jobs/decidim/action_delegator/admin/import_csv_job.rb +27 -0
  36. data/app/jobs/decidim/action_delegator/admin/invite_participants_job.rb +39 -0
  37. data/app/jobs/decidim/action_delegator/sync_participants_job.rb +27 -0
  38. data/app/mailers/decidim/action_delegator/import_mailer.rb +31 -0
  39. data/app/models/decidim/action_delegator/delegation.rb +11 -0
  40. data/app/models/decidim/action_delegator/participant.rb +106 -0
  41. data/app/models/decidim/action_delegator/ponderation.rb +28 -0
  42. data/app/models/decidim/action_delegator/setting.rb +50 -1
  43. data/app/overrides/decidim/consultations/consultations/_question/add_delegation_link.html.erb.deface +3 -0
  44. data/app/overrides/decidim/consultations/consultations/_regular_questions/remove_highlighted_scopes.html.erb.deface +4 -0
  45. data/app/overrides/decidim/consultations/question_multiple_votes/_form/add_delegation_notice.html.erb.deface +8 -0
  46. data/app/overrides/decidim/consultations/questions/_vote_button/add_delegations_link.html.erb.deface +3 -0
  47. data/app/overrides/decidim/consultations/questions/_vote_button/add_modal.html.erb.deface +3 -0
  48. data/app/overrides/decidim/consultations/questions/_vote_button/replace_delegation_to_multivote_link.html.erb.deface +8 -0
  49. data/app/overrides/decidim/consultations/questions/_vote_modal/add_delegation_callout.html.erb.deface +3 -0
  50. data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_delegation_callout.html.erb.deface +3 -0
  51. data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_hidden_field.html.erb.deface +3 -0
  52. data/app/overrides/layouts/decidim/admin/remove_deprecation.rb +10 -0
  53. data/app/packs/entrypoints/decidim_action_delegator_questions.js +2 -0
  54. data/app/packs/src/decidim/action_delegator/questions.js +27 -0
  55. data/app/packs/stylesheets/decidim/action_delegator/questions.scss +4 -3
  56. data/app/permissions/concerns/decidim/action_delegator/consultations/permissions_override.rb +35 -0
  57. data/app/permissions/decidim/action_delegator/permissions.rb +6 -2
  58. data/app/queries/decidim/action_delegator/delegated_votes_versions.rb +5 -11
  59. data/app/queries/decidim/action_delegator/organization_settings.rb +6 -0
  60. data/app/queries/decidim/action_delegator/responses_by_membership.rb +3 -20
  61. data/app/queries/decidim/action_delegator/sum_of_membership_weight.rb +1 -5
  62. data/app/queries/decidim/action_delegator/sum_of_weights.rb +1 -1
  63. data/app/queries/decidim/action_delegator/type_and_weight.rb +1 -1
  64. data/app/queries/decidim/action_delegator/voted_with_ponderations.rb +30 -0
  65. data/app/services/decidim/action_delegator/csv_importer.rb +86 -0
  66. data/app/services/decidim/action_delegator/delegations_csv_importer.rb +54 -0
  67. data/app/services/decidim/action_delegator/participants_csv_importer.rb +131 -0
  68. data/app/views/decidim/action_delegator/admin/consultations/results.html.erb +3 -3
  69. data/app/views/decidim/action_delegator/admin/{results/sum_of_weights/index.html.erb → consultations/weighted_results.html.erb} +3 -3
  70. data/app/views/decidim/action_delegator/admin/delegations/index.html.erb +8 -3
  71. data/app/views/decidim/action_delegator/admin/delegations/new.html.erb +15 -12
  72. data/app/views/decidim/action_delegator/admin/manage_delegations/new.html.erb +25 -0
  73. data/app/views/decidim/action_delegator/admin/manage_participants/new.html.erb +33 -0
  74. data/app/views/decidim/action_delegator/admin/participants/_form.html.erb +9 -0
  75. data/app/views/decidim/action_delegator/admin/participants/_missing_registered_check.html.erb +17 -0
  76. data/app/views/decidim/action_delegator/admin/participants/edit.html.erb +21 -0
  77. data/app/views/decidim/action_delegator/admin/participants/index.html.erb +72 -0
  78. data/app/views/decidim/action_delegator/admin/participants/new.html.erb +21 -0
  79. data/app/views/decidim/action_delegator/admin/ponderations/_form.html.erb +8 -0
  80. data/app/views/decidim/action_delegator/admin/ponderations/edit.html.erb +21 -0
  81. data/app/views/decidim/action_delegator/admin/ponderations/index.html.erb +40 -0
  82. data/app/views/decidim/action_delegator/admin/ponderations/new.html.erb +21 -0
  83. data/app/views/decidim/action_delegator/admin/settings/_check_verifier.html.erb +5 -0
  84. data/app/views/decidim/action_delegator/admin/settings/_form.html.erb +16 -0
  85. data/app/views/decidim/action_delegator/admin/settings/_participants_email_check.html.erb +7 -0
  86. data/app/views/decidim/action_delegator/admin/settings/_participants_sync_check.html.erb +11 -0
  87. data/app/views/decidim/action_delegator/admin/settings/_setting_checks.html.erb +81 -0
  88. data/app/views/decidim/action_delegator/admin/settings/edit.html.erb +18 -0
  89. data/app/views/decidim/action_delegator/admin/settings/index.html.erb +25 -5
  90. data/app/views/decidim/action_delegator/admin/settings/new.html.erb +14 -26
  91. data/app/views/decidim/action_delegator/consultations/_link_to_question.html.erb +11 -0
  92. data/app/views/decidim/action_delegator/consultations/questions/_delegations_modal.html.erb +36 -0
  93. data/app/views/decidim/action_delegator/consultations/questions/_link_to_delegations.html.erb +11 -0
  94. data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_active.html.erb +32 -0
  95. data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_finished.html.erb +9 -0
  96. data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_upcoming.html.erb +8 -0
  97. data/app/views/decidim/action_delegator/import_mailer/import.html.erb +11 -0
  98. data/app/views/decidim/{verifications/sms/authorizations/new.html.erb → action_delegator/verifications/delegations_verifier/authorizations/edit.html.erb} +12 -7
  99. data/app/views/decidim/action_delegator/verifications/delegations_verifier/authorizations/new.html.erb +49 -0
  100. data/config/assets.rb +1 -3
  101. data/config/i18n-tasks.yml +8 -0
  102. data/config/locales/ca.yml +287 -23
  103. data/config/locales/cs.yml +286 -18
  104. data/config/locales/en.yml +331 -24
  105. data/config/locales/es.yml +288 -24
  106. data/db/migrate/20230323101247_create_decidim_action_delegator_ponderations.rb +12 -0
  107. data/db/migrate/20230323210000_create_decidim_action_delegator_participants.rb +13 -0
  108. data/db/migrate/20230323223752_add_decidim_action_delegator_verification_method.rb +7 -0
  109. data/db/migrate/20230412105710_add_decidim_action_delegator_participants_user_id.rb +7 -0
  110. data/lib/decidim/action_delegator/admin_engine.rb +68 -6
  111. data/lib/decidim/action_delegator/engine.rb +25 -15
  112. data/lib/decidim/action_delegator/test/factories.rb +22 -7
  113. data/lib/decidim/action_delegator/verifications/delegations_authorizer.rb +85 -0
  114. data/lib/decidim/action_delegator/verifications/delegations_verifier/engine.rb +25 -0
  115. data/lib/decidim/action_delegator/verifications/delegations_verifier.rb +12 -0
  116. data/lib/decidim/action_delegator/version.rb +3 -3
  117. data/lib/decidim/action_delegator.rb +41 -0
  118. data/lib/tasks/import_direct_verification.rake +30 -0
  119. data/package.json +57 -0
  120. metadata +123 -57
  121. data/app/controllers/decidim/action_delegator/admin/results/sum_of_weights_controller.rb +0 -37
  122. data/app/controllers/decidim/action_delegator/verifications/sms/authorizations_controller_override.rb +0 -38
  123. data/app/packs/entrypoints/decidim_action_delegator_admin_action_delegator_js.js +0 -1
  124. data/app/packs/entrypoints/decidim_action_delegator_questions_js.js +0 -1
  125. data/app/packs/src/decidim/action_delegator/admin/action_delegator.js.es6 +0 -3
  126. data/app/packs/src/decidim/action_delegator/questions.js.es6 +0 -26
  127. data/app/permissions/decidim/action_delegator/consultations_permissions_extension.rb +0 -27
  128. data/app/presenters/decidim/action_delegator/admin/consultation_presenter.rb +0 -15
  129. data/app/presenters/decidim/action_delegator/admin/setting_presenter.rb +0 -13
  130. data/app/queries/decidim/action_delegator/decrypted_authorizations.rb +0 -112
  131. data/app/queries/decidim/action_delegator/json_build_object_query.rb +0 -45
  132. data/app/queries/decidim/action_delegator/voted_with_direct_verification.rb +0 -53
  133. data/app/views/decidim/action_delegator/_delegations_modal.html.erb +0 -93
  134. data/app/views/decidim/action_delegator/_link_to_delegations.html.erb +0 -5
  135. data/app/views/decidim/action_delegator/_link_to_question.html.erb +0 -5
  136. data/app/views/decidim/consultations/consultations/_question.html.erb +0 -41
  137. data/app/views/decidim/consultations/consultations/show.html.erb +0 -14
  138. data/app/views/decidim/consultations/question_multiple_votes/_form.html.erb +0 -25
  139. data/app/views/decidim/consultations/questions/_vote_button.html.erb +0 -106
  140. data/app/views/decidim/consultations/questions/_vote_modal.html.erb +0 -30
  141. data/app/views/decidim/consultations/questions/_vote_modal_confirm.html.erb +0 -38
  142. data/app/views/layouts/decidim/action_delegator/admin/_users_sidebar.html.erb +0 -56
  143. data/app/views/layouts/decidim/action_delegator/admin/delegations.html.erb +0 -13
  144. data/app/views/layouts/decidim/admin/consultation.html.erb +0 -56
  145. data/app/views/layouts/decidim/admin/question.html.erb +0 -98
  146. data/app/views/layouts/decidim/admin/users.html.erb +0 -7
  147. data/lib/json_key.rb +0 -10
  148. /data/app/commands/{decidim → concerns/decidim}/action_delegator/consultations/multiple_vote_question_override.rb +0 -0
  149. /data/app/controllers/decidim/action_delegator/admin/exports/{sum_of_weights_controller.rb → _sum_of_weights_controller.rb} +0 -0
  150. /data/app/forms/{decidim → concerns/decidim}/action_delegator/consultations/vote_form_override.rb +0 -0
  151. /data/app/models/{decidim → concerns/decidim}/action_delegator/consultations/vote_override.rb +0 -0
  152. /data/app/views/decidim/action_delegator/{_callout.html.erb → consultations/questions/_callout.html.erb} +0 -0
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Verifications
6
+ module DelegationsVerifier
7
+ class AuthorizationsController < ApplicationController
8
+ include Decidim::FormFactory
9
+ include Decidim::Verifications::Renewable
10
+
11
+ helper_method :authorization, :setting
12
+
13
+ def new
14
+ enforce_permission_to :create, :authorization, authorization: authorization
15
+
16
+ @form = form(DelegationsVerifierForm).instance(setting: setting)
17
+ end
18
+
19
+ def create
20
+ enforce_permission_to :create, :authorization, authorization: authorization
21
+
22
+ @form = form(DelegationsVerifierForm).from_params(params, setting: setting)
23
+ participant = @form&.participant
24
+
25
+ Decidim::Verifications::PerformAuthorizationStep.call(authorization, @form) do
26
+ on(:ok) do
27
+ if setting.phone_required?
28
+ flash[:notice] = t("authorizations.create.success", scope: "decidim.verifications.sms")
29
+ authorization_method = Decidim::Verifications::Adapter.from_element(authorization.name)
30
+ redirect_to authorization_method.resume_authorization_path(redirect_url: redirect_url)
31
+ else
32
+ authorization.grant!
33
+ participant.update!(decidim_user: authorization.user)
34
+ flash[:notice] = t("authorizations.update.success", scope: "decidim.verifications.sms")
35
+
36
+ if redirect_url
37
+ redirect_to redirect_url
38
+ else
39
+ redirect_to decidim_verifications.authorizations_path
40
+ end
41
+ end
42
+ end
43
+ on(:invalid) do
44
+ flash.now[:alert] = t("authorizations.create.error", scope: "decidim.verifications.sms")
45
+ render :new
46
+ end
47
+ end
48
+ end
49
+
50
+ def edit
51
+ enforce_permission_to :update, :authorization, authorization: authorization
52
+
53
+ @form = form(Decidim::Verifications::Sms::ConfirmationForm).from_params(params)
54
+ end
55
+
56
+ def update
57
+ enforce_permission_to :update, :authorization, authorization: authorization
58
+
59
+ @form = form(Decidim::Verifications::Sms::ConfirmationForm).from_params(params)
60
+
61
+ Decidim::Verifications::ConfirmUserAuthorization.call(authorization, @form, session) do
62
+ on(:ok) do
63
+ flash[:notice] = t("authorizations.update.success", scope: "decidim.verifications.sms")
64
+
65
+ if redirect_url
66
+ redirect_to redirect_url
67
+ else
68
+ redirect_to decidim_verifications.authorizations_path
69
+ end
70
+ end
71
+
72
+ on(:invalid) do
73
+ flash.now[:alert] = t("authorizations.update.error", scope: "decidim.verifications.sms")
74
+ render :edit
75
+ end
76
+ end
77
+ end
78
+
79
+ def destroy
80
+ enforce_permission_to :destroy, :authorization, authorization: authorization
81
+
82
+ authorization.destroy!
83
+ flash[:notice] = t("authorizations.destroy.success", scope: "decidim.verifications.sms")
84
+
85
+ redirect_to action: :new
86
+ end
87
+
88
+ private
89
+
90
+ def authorization
91
+ @authorization ||= Decidim::Authorization.find_or_initialize_by(
92
+ user: current_user,
93
+ name: "delegations_verifier"
94
+ )
95
+ end
96
+
97
+ def setting
98
+ @setting ||= all_settings.first
99
+ end
100
+
101
+ def all_settings
102
+ @all_settings ||= OrganizationSettings.new(current_user.organization).active
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -6,18 +6,37 @@ module Decidim
6
6
  # A form object used to create a Delegation
7
7
  #
8
8
  class DelegationForm < Form
9
+ mimic :delegation
10
+
9
11
  attribute :granter_id, Integer
10
12
  attribute :grantee_id, Integer
11
13
 
12
- validates :granter_id, presence: true
13
- validates :grantee_id, presence: true
14
+ attribute :granter_email, String
15
+ attribute :grantee_email, String
16
+
17
+ validate :granter_exists
18
+ validate :grantee_exists
14
19
 
15
20
  def granter
16
- User.find_by(id: granter_id)
21
+ User.find_by(id: granter_id) || User.find_by(email: granter_email)
17
22
  end
18
23
 
19
24
  def grantee
20
- User.find_by(id: grantee_id)
25
+ User.find_by(id: grantee_id) || User.find_by(email: grantee_email)
26
+ end
27
+
28
+ private
29
+
30
+ def granter_exists
31
+ return if granter.present?
32
+
33
+ errors.add :granter_email, I18n.t("decidim.action_delegator.admin.delegations.granter_missing")
34
+ end
35
+
36
+ def grantee_exists
37
+ return if grantee.present?
38
+
39
+ errors.add :grantee_email, I18n.t("decidim.action_delegator.admin.delegations.grantee_missing")
21
40
  end
22
41
  end
23
42
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class InvitationParticipantForm < OpenStruct
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class ParticipantForm < Form
7
+ mimic :participant
8
+
9
+ attribute :email, String
10
+ attribute :phone, String
11
+ attribute :decidim_action_delegator_ponderation_id, Integer
12
+ attribute :weight, String
13
+
14
+ validates :email, presence: true, if: ->(form) { form.authorization_method.in? %w(email both) }
15
+ validates :phone, presence: true, if: ->(form) { form.authorization_method.in? %w(phone both) }
16
+ validate :ponderation_belongs_to_setting
17
+
18
+ # When there's a phone number, sanitize it allowing only numbers and +.
19
+ def phone
20
+ return unless super
21
+
22
+ super.gsub(/[^+0-9]/, "")
23
+ end
24
+
25
+ def setting
26
+ @setting ||= context[:setting]
27
+ end
28
+
29
+ def authorization_method
30
+ @authorization_method ||= setting&.authorization_method
31
+ end
32
+
33
+ private
34
+
35
+ def ponderation_belongs_to_setting
36
+ return if decidim_action_delegator_ponderation_id.blank?
37
+ return if setting.ponderations.where(id: decidim_action_delegator_ponderation_id).any?
38
+
39
+ errors.add(:decidim_action_delegator_ponderation_id, :invalid)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class PonderationForm < Form
7
+ mimic :ponderation
8
+
9
+ attribute :weight, Decimal, default: 1.0
10
+ attribute :name, String
11
+
12
+ validates :weight, :name, presence: true
13
+ validate :name_uniqueness
14
+
15
+ def name_uniqueness
16
+ return unless setting
17
+ return unless setting.ponderations.where(name: name).where.not(id: id).any?
18
+
19
+ errors.add(:name, :taken)
20
+ end
21
+
22
+ def setting
23
+ @setting ||= context[:setting]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class SettingForm < Form
7
+ mimic :setting
8
+
9
+ attribute :max_grants, Integer
10
+ attribute :decidim_consultation_id, Integer
11
+ attribute :authorization_method, String
12
+ attribute :copy_from_setting_id, Integer
13
+
14
+ validates :max_grants, :decidim_consultation_id, presence: true
15
+ validate :consultation_uniqueness
16
+
17
+ # TODO: validate consultation vote starting in the future
18
+ def consultation_uniqueness
19
+ errors.add(:decidim_consultation_id, :taken) if record.exists?(decidim_consultation_id: decidim_consultation_id)
20
+ end
21
+
22
+ def record
23
+ Setting.where.not(id: id)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Decidim
6
+ module ActionDelegator
7
+ module Verifications
8
+ # A form object to be used when public users want to get verified using their phone.
9
+ class DelegationsVerifierForm < AuthorizationHandler
10
+ attribute :email, String
11
+ attribute :phone, String
12
+
13
+ validates :verification_code, :sms_gateway, presence: true
14
+ validates :phone, presence: true, if: ->(form) { form.setting&.phone_required? }
15
+ validates :email, presence: true, if: ->(form) { form.setting&.email_required? }
16
+ validate :setting_exists
17
+ validate :user_in_census
18
+
19
+ alias user current_user
20
+
21
+ def handler_name
22
+ "delegations_verifier"
23
+ end
24
+
25
+ def unique_id
26
+ Digest::MD5.hexdigest(
27
+ "#{setting&.phone_required? ? phone : email}-#{setting&.organization&.id}-#{Digest::MD5.hexdigest(Rails.application.secrets.secret_key_base)}"
28
+ )
29
+ end
30
+
31
+ # email is predefined always
32
+ delegate :email, to: :current_user
33
+
34
+ # When there's a phone number, sanitize it allowing only numbers and +.
35
+ def phone
36
+ return find_phone if setting&.verify_with_both?
37
+ return unless super
38
+
39
+ super.gsub(/[^+0-9]/, "")
40
+ end
41
+
42
+ def metadata
43
+ {
44
+ phone: phone
45
+ }
46
+ end
47
+
48
+ # The verification metadata to validate in the next step.
49
+ def verification_metadata
50
+ {
51
+ verification_code: verification_code,
52
+ code_sent_at: Time.current
53
+ }
54
+ end
55
+
56
+ # currently, we rely on the last setting.
57
+ # This could be improved by allowing the user to select the setting (or related phone).
58
+ def setting
59
+ @setting ||= context[:setting]
60
+ end
61
+
62
+ def participants
63
+ @participants ||= Decidim::ActionDelegator::Participant.where(setting: setting)
64
+ end
65
+
66
+ def participant
67
+ return unless setting
68
+
69
+ @participant ||= begin
70
+ params = {}
71
+ params[:email] = email if setting.email_required?
72
+ if setting.phone_required?
73
+ return setting.participants.none if phone.blank?
74
+
75
+ params[:phone] = phone
76
+ params[:phone] = phone_prefixes.map { |prefix| "#{prefix}#{phone}" }
77
+ params[:phone] += phone_prefixes.map { |prefix| phone.delete_prefix(prefix).to_s }
78
+ end
79
+
80
+ setting.participants.find_by(params)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def phone_prefixes
87
+ return [] unless ActionDelegator.phone_prefixes.respond_to?(:map)
88
+
89
+ ActionDelegator.phone_prefixes
90
+ end
91
+
92
+ def user_in_census
93
+ return if errors.any?
94
+ return if participant
95
+
96
+ errors.add(:phone, :phone_not_found) if setting.phone_required?
97
+ errors.add(:email, :email_not_found) if setting.email_required?
98
+ end
99
+
100
+ def setting_exists
101
+ return if errors.any?
102
+ return if setting
103
+
104
+ errors.add(:phone, :invalid)
105
+ errors.add(:email, :invalid)
106
+ end
107
+
108
+ def verification_code
109
+ return unless sms_gateway
110
+ return @verification_code if defined?(@verification_code)
111
+
112
+ return unless sms_gateway.new(phone, generated_code).deliver_code
113
+
114
+ @verification_code = generated_code
115
+ end
116
+
117
+ def sms_gateway
118
+ (Decidim.sms_gateway_service || ActionDelegator.sms_gateway_service).to_s.safe_constantize
119
+ end
120
+
121
+ def generated_code
122
+ @generated_code ||= SecureRandom.random_number(1_000_000).to_s
123
+ end
124
+
125
+ def find_phone
126
+ @find_phone ||= setting.participants.find_by(email: email)&.phone
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -13,14 +13,51 @@ module Decidim
13
13
  end
14
14
 
15
15
  def consultations_for_select
16
- organization_consultations.map do |consultation|
17
- ConsultationPresenter.new(consultation)
18
- end
16
+ organization_consultations.map { |consultation| [translated_attribute(consultation.title), consultation.id] }
17
+ end
18
+
19
+ def ponderations_for_select(setting)
20
+ setting.ponderations.map { |ponderation| [ponderation.title, ponderation.id] }
19
21
  end
20
22
 
21
23
  def organization_consultations
22
24
  Decidim::Consultations::OrganizationConsultations.new(current_organization).query
23
25
  end
26
+
27
+ def missing_verifications_for(resources, action)
28
+ resources.where.not(id: Decidim::ResourcePermission.select(:resource_id)
29
+ .where(resource: resources)
30
+ .where(Arel.sql("permissions->'#{action}'->'authorization_handlers'->>'delegations_verifier' IS NOT NULL")))
31
+ end
32
+
33
+ def missing_decidim_users(participants)
34
+ participants.where(decidim_user: nil).or(Participant.where.not(decidim_user: current_organization.users)).where.not(id: missing_registered_users(participants))
35
+ end
36
+
37
+ def missing_registered_users(participants)
38
+ participants.where.not(email: current_organization.users.select(:email))
39
+ .where.not("MD5(CONCAT(phone,'-',?,'-',?)) IN (?)",
40
+ current_organization.id,
41
+ Digest::MD5.hexdigest(Rails.application.secret_key_base),
42
+ Authorization.select(:unique_id).where.not(unique_id: nil))
43
+ end
44
+
45
+ def participants_uniq_ids(participants)
46
+ phones = Participant.phone_combinations(participants.pluck(:phone))
47
+ Participant.verifier_ids(Participant.phone_combinations(phones.map { |phone| "#{phone}-#{current_organization.id}" }))
48
+ end
49
+
50
+ def existing_authorizations(participants)
51
+ uniq_ids = participants_uniq_ids(participants)
52
+ user_ids = current_organization.users.select(:id).where(email: participants.select(:email))
53
+ Decidim::Authorization.select(:decidim_user_id).where(unique_id: uniq_ids).or(
54
+ Decidim::Authorization.select(:decidim_user_id).where(decidim_user_id: user_ids)
55
+ )
56
+ end
57
+
58
+ def total_missing_authorizations(participants)
59
+ participants.count - existing_authorizations(participants).count
60
+ end
24
61
  end
25
62
  end
26
63
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module DelegationHelper
6
+ def has_any_delegate_vote?(question)
7
+ Decidim::ActionDelegator::GranteeDelegations.for(question.consultation, current_user).detect do |delegation|
8
+ question.voted_by?(delegation.granter)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class ImportCsvJob < ApplicationJob
7
+ queue_as :exports
8
+
9
+ def perform(importer_type, csv_file, current_user, current_setting)
10
+ importer = if importer_type == "DelegationsCsvImporter"
11
+ Decidim::ActionDelegator::DelegationsCsvImporter.new(csv_file, current_user, current_setting)
12
+ else
13
+ Decidim::ActionDelegator::ParticipantsCsvImporter.new(csv_file, current_user, current_setting)
14
+ end
15
+
16
+ import_summary = importer.import!
17
+
18
+ Decidim::ActionDelegator::ImportMailer
19
+ .import(current_user, import_summary, import_summary[:details_csv_path])
20
+ .deliver_later
21
+
22
+ import_summary
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class InviteParticipantsJob < ApplicationJob
7
+ queue_as :invite_participants
8
+
9
+ def perform(current_setting, organization)
10
+ @current_setting = current_setting
11
+ @organization = organization
12
+
13
+ users_list_to_invite.find_each do |participant|
14
+ form = InvitationParticipantForm.new(
15
+ name: participant.email.split("@").first&.gsub(/\W/, ""),
16
+ email: participant.email.downcase,
17
+ organization: organization,
18
+ admin: false
19
+ )
20
+
21
+ Decidim::InviteUser.call(form)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def users_list_to_invite
28
+ @users_list_to_invite ||= @current_setting.participants.where(decidim_user: nil)
29
+ .where.not(email: @organization.users.select(:email))
30
+ .where.not("MD5(CONCAT(phone,'-',?,'-',?)) IN (?)",
31
+ @organization.id,
32
+ Digest::MD5.hexdigest(Rails.application.secret_key_base),
33
+ Authorization.select(:unique_id)
34
+ .where.not(unique_id: nil))
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class SyncParticipantsJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(setting)
9
+ @setting = setting
10
+
11
+ return unless setting&.participants
12
+
13
+ setting.participants.each do |participant|
14
+ next if participant.decidim_user.present?
15
+ next if participant.user_from_metadata.blank?
16
+
17
+ participant.decidim_user = participant.user_from_metadata
18
+ participant.save
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :setting
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ # This mailer sends a notification email containing the result of importing a
6
+ # CSV of results.
7
+ class ImportMailer < Decidim::ApplicationMailer
8
+ # Public: Sends a notification email with the result of a CSV import
9
+ # of results.
10
+ #
11
+ # user - The user to be notified.
12
+ # errors - The list of errors generated by the import
13
+ #
14
+ # Returns nothing.
15
+ def import(user, import_summary, csv_file_path)
16
+ @user = user
17
+ @organization = user.organization
18
+ @import_summary = import_summary
19
+ @csv_file_path = csv_file_path
20
+
21
+ @csv_file_path = "" if @import_summary[:total_rows] == @import_summary[:imported_rows]
22
+
23
+ attachments["details.csv"] = File.read(@csv_file_path) if @csv_file_path.present? && File.exist?(@csv_file_path)
24
+
25
+ with_user(user) do
26
+ mail(to: "#{user.name} <#{user.email}>", subject: I18n.t("decidim.action_delegator.import_mailer.import.subject"))
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -18,9 +18,20 @@ module Decidim
18
18
 
19
19
  delegate :consultation, to: :setting
20
20
 
21
+ before_destroy { |record| throw(:abort) if record.grantee_voted? }
22
+
21
23
  def self.granted_to?(user, consultation)
22
24
  GranteeDelegations.for(consultation, user).exists?
23
25
  end
26
+
27
+ def grantee_voted?
28
+ return false unless consultation.questions.any?
29
+
30
+ @grantee_voted ||= begin
31
+ granter_votes = Decidim::Consultations::Vote.where(author: granter, question: consultation.questions)
32
+ granter_votes&.detect { |vote| vote.versions.exists?(whodunnit: grantee&.id) } ? true : false
33
+ end
34
+ end
24
35
  end
25
36
  end
26
37
  end