decidim-action_delegator 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +97 -36
- data/app/commands/{decidim → concerns/decidim}/action_delegator/consultations/vote_question_override.rb +9 -1
- data/app/commands/decidim/action_delegator/admin/create_delegation.rb +5 -1
- data/app/commands/decidim/action_delegator/admin/create_participant.rb +41 -0
- data/app/commands/decidim/action_delegator/admin/create_ponderation.rb +38 -0
- data/app/commands/decidim/action_delegator/admin/create_setting.rb +49 -0
- data/app/commands/decidim/action_delegator/admin/fix_resource_permissions.rb +46 -0
- data/app/commands/decidim/action_delegator/admin/update_participant.rb +42 -0
- data/app/commands/decidim/action_delegator/admin/update_ponderation.rb +41 -0
- data/app/commands/decidim/action_delegator/admin/update_setting.rb +66 -0
- data/app/controllers/concerns/decidim/action_delegator/consultations/consultations_controller_override.rb +16 -0
- data/app/controllers/{decidim → concerns/decidim}/action_delegator/consultations/question_multiple_votes_controller_override.rb +2 -1
- data/app/controllers/{decidim → concerns/decidim}/action_delegator/consultations/question_votes_controller_override.rb +2 -0
- data/app/controllers/concerns/decidim/action_delegator/consultations/questions_controller_override.rb +16 -0
- data/app/controllers/concerns/decidim/action_delegator/needs_consultation_styles.rb +24 -0
- data/app/controllers/decidim/action_delegator/admin/consultations_controller.rb +22 -8
- data/app/controllers/decidim/action_delegator/admin/delegations_controller.rb +9 -9
- data/app/controllers/decidim/action_delegator/admin/invite_participants_controller.rb +84 -0
- data/app/controllers/decidim/action_delegator/admin/manage_delegations_controller.rb +48 -0
- data/app/controllers/decidim/action_delegator/admin/manage_participants_controller.rb +59 -0
- data/app/controllers/decidim/action_delegator/admin/participants_controller.rb +102 -0
- data/app/controllers/decidim/action_delegator/admin/permissions_controller.rb +40 -0
- data/app/controllers/decidim/action_delegator/admin/ponderations_controller.rb +101 -0
- data/app/controllers/decidim/action_delegator/admin/settings_controller.rb +51 -12
- data/app/controllers/decidim/action_delegator/verifications/delegations_verifier/authorizations_controller.rb +108 -0
- data/app/forms/decidim/action_delegator/admin/delegation_form.rb +23 -4
- data/app/forms/decidim/action_delegator/admin/invitation_participant_form.rb +10 -0
- data/app/forms/decidim/action_delegator/admin/participant_form.rb +44 -0
- data/app/forms/decidim/action_delegator/admin/ponderation_form.rb +28 -0
- data/app/forms/decidim/action_delegator/admin/setting_form.rb +28 -0
- data/app/forms/decidim/action_delegator/verifications/delegations_verifier_form.rb +131 -0
- data/app/helpers/decidim/action_delegator/admin/delegation_helper.rb +40 -3
- data/app/helpers/decidim/action_delegator/delegation_helper.rb +13 -0
- data/app/jobs/decidim/action_delegator/admin/import_csv_job.rb +27 -0
- data/app/jobs/decidim/action_delegator/admin/invite_participants_job.rb +39 -0
- data/app/jobs/decidim/action_delegator/sync_participants_job.rb +27 -0
- data/app/mailers/decidim/action_delegator/import_mailer.rb +31 -0
- data/app/models/decidim/action_delegator/delegation.rb +11 -0
- data/app/models/decidim/action_delegator/participant.rb +106 -0
- data/app/models/decidim/action_delegator/ponderation.rb +28 -0
- data/app/models/decidim/action_delegator/setting.rb +50 -1
- data/app/overrides/decidim/consultations/consultations/_question/add_delegation_link.html.erb.deface +3 -0
- data/app/overrides/decidim/consultations/consultations/_regular_questions/remove_highlighted_scopes.html.erb.deface +4 -0
- data/app/overrides/decidim/consultations/question_multiple_votes/_form/add_delegation_notice.html.erb.deface +8 -0
- data/app/overrides/decidim/consultations/questions/_vote_button/add_delegations_link.html.erb.deface +3 -0
- data/app/overrides/decidim/consultations/questions/_vote_button/add_modal.html.erb.deface +3 -0
- data/app/overrides/decidim/consultations/questions/_vote_button/replace_delegation_to_multivote_link.html.erb.deface +8 -0
- data/app/overrides/decidim/consultations/questions/_vote_modal/add_delegation_callout.html.erb.deface +3 -0
- data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_delegation_callout.html.erb.deface +3 -0
- data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_hidden_field.html.erb.deface +3 -0
- data/app/overrides/layouts/decidim/admin/remove_deprecation.rb +10 -0
- data/app/packs/entrypoints/decidim_action_delegator_questions.js +2 -0
- data/app/packs/src/decidim/action_delegator/questions.js +27 -0
- data/app/packs/stylesheets/decidim/action_delegator/questions.scss +4 -3
- data/app/permissions/concerns/decidim/action_delegator/consultations/permissions_override.rb +35 -0
- data/app/permissions/decidim/action_delegator/permissions.rb +6 -2
- data/app/queries/decidim/action_delegator/delegated_votes_versions.rb +5 -11
- data/app/queries/decidim/action_delegator/organization_settings.rb +6 -0
- data/app/queries/decidim/action_delegator/responses_by_membership.rb +3 -20
- data/app/queries/decidim/action_delegator/sum_of_membership_weight.rb +1 -5
- data/app/queries/decidim/action_delegator/sum_of_weights.rb +1 -1
- data/app/queries/decidim/action_delegator/type_and_weight.rb +1 -1
- data/app/queries/decidim/action_delegator/voted_with_ponderations.rb +30 -0
- data/app/services/decidim/action_delegator/csv_importer.rb +86 -0
- data/app/services/decidim/action_delegator/delegations_csv_importer.rb +54 -0
- data/app/services/decidim/action_delegator/participants_csv_importer.rb +131 -0
- data/app/views/decidim/action_delegator/admin/consultations/results.html.erb +3 -3
- data/app/views/decidim/action_delegator/admin/{results/sum_of_weights/index.html.erb → consultations/weighted_results.html.erb} +3 -3
- data/app/views/decidim/action_delegator/admin/delegations/index.html.erb +8 -3
- data/app/views/decidim/action_delegator/admin/delegations/new.html.erb +15 -12
- data/app/views/decidim/action_delegator/admin/manage_delegations/new.html.erb +25 -0
- data/app/views/decidim/action_delegator/admin/manage_participants/new.html.erb +33 -0
- data/app/views/decidim/action_delegator/admin/participants/_form.html.erb +9 -0
- data/app/views/decidim/action_delegator/admin/participants/_missing_registered_check.html.erb +17 -0
- data/app/views/decidim/action_delegator/admin/participants/edit.html.erb +21 -0
- data/app/views/decidim/action_delegator/admin/participants/index.html.erb +72 -0
- data/app/views/decidim/action_delegator/admin/participants/new.html.erb +21 -0
- data/app/views/decidim/action_delegator/admin/ponderations/_form.html.erb +8 -0
- data/app/views/decidim/action_delegator/admin/ponderations/edit.html.erb +21 -0
- data/app/views/decidim/action_delegator/admin/ponderations/index.html.erb +40 -0
- data/app/views/decidim/action_delegator/admin/ponderations/new.html.erb +21 -0
- data/app/views/decidim/action_delegator/admin/settings/_check_verifier.html.erb +5 -0
- data/app/views/decidim/action_delegator/admin/settings/_form.html.erb +16 -0
- data/app/views/decidim/action_delegator/admin/settings/_participants_email_check.html.erb +7 -0
- data/app/views/decidim/action_delegator/admin/settings/_participants_sync_check.html.erb +11 -0
- data/app/views/decidim/action_delegator/admin/settings/_setting_checks.html.erb +81 -0
- data/app/views/decidim/action_delegator/admin/settings/edit.html.erb +18 -0
- data/app/views/decidim/action_delegator/admin/settings/index.html.erb +25 -5
- data/app/views/decidim/action_delegator/admin/settings/new.html.erb +14 -26
- data/app/views/decidim/action_delegator/consultations/_link_to_question.html.erb +11 -0
- data/app/views/decidim/action_delegator/consultations/questions/_delegations_modal.html.erb +36 -0
- data/app/views/decidim/action_delegator/consultations/questions/_link_to_delegations.html.erb +11 -0
- data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_active.html.erb +32 -0
- data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_finished.html.erb +9 -0
- data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_upcoming.html.erb +8 -0
- data/app/views/decidim/action_delegator/import_mailer/import.html.erb +11 -0
- data/app/views/decidim/{verifications/sms/authorizations/new.html.erb → action_delegator/verifications/delegations_verifier/authorizations/edit.html.erb} +12 -7
- data/app/views/decidim/action_delegator/verifications/delegations_verifier/authorizations/new.html.erb +49 -0
- data/config/assets.rb +1 -3
- data/config/i18n-tasks.yml +8 -0
- data/config/locales/ca.yml +287 -23
- data/config/locales/cs.yml +286 -18
- data/config/locales/en.yml +331 -24
- data/config/locales/es.yml +288 -24
- data/db/migrate/20230323101247_create_decidim_action_delegator_ponderations.rb +12 -0
- data/db/migrate/20230323210000_create_decidim_action_delegator_participants.rb +13 -0
- data/db/migrate/20230323223752_add_decidim_action_delegator_verification_method.rb +7 -0
- data/db/migrate/20230412105710_add_decidim_action_delegator_participants_user_id.rb +7 -0
- data/lib/decidim/action_delegator/admin_engine.rb +68 -6
- data/lib/decidim/action_delegator/engine.rb +25 -15
- data/lib/decidim/action_delegator/test/factories.rb +22 -7
- data/lib/decidim/action_delegator/verifications/delegations_authorizer.rb +85 -0
- data/lib/decidim/action_delegator/verifications/delegations_verifier/engine.rb +25 -0
- data/lib/decidim/action_delegator/verifications/delegations_verifier.rb +12 -0
- data/lib/decidim/action_delegator/version.rb +3 -3
- data/lib/decidim/action_delegator.rb +41 -0
- data/lib/tasks/import_direct_verification.rake +30 -0
- data/package.json +57 -0
- metadata +115 -50
- data/app/controllers/decidim/action_delegator/admin/results/sum_of_weights_controller.rb +0 -37
- data/app/controllers/decidim/action_delegator/verifications/sms/authorizations_controller_override.rb +0 -38
- data/app/packs/entrypoints/decidim_action_delegator_admin_action_delegator_js.js +0 -1
- data/app/packs/entrypoints/decidim_action_delegator_questions_js.js +0 -1
- data/app/packs/src/decidim/action_delegator/admin/action_delegator.js.es6 +0 -3
- data/app/packs/src/decidim/action_delegator/questions.js.es6 +0 -26
- data/app/permissions/decidim/action_delegator/consultations_permissions_extension.rb +0 -27
- data/app/presenters/decidim/action_delegator/admin/consultation_presenter.rb +0 -15
- data/app/presenters/decidim/action_delegator/admin/setting_presenter.rb +0 -13
- data/app/queries/decidim/action_delegator/decrypted_authorizations.rb +0 -112
- data/app/queries/decidim/action_delegator/json_build_object_query.rb +0 -45
- data/app/queries/decidim/action_delegator/voted_with_direct_verification.rb +0 -53
- data/app/views/decidim/action_delegator/_delegations_modal.html.erb +0 -93
- data/app/views/decidim/action_delegator/_link_to_delegations.html.erb +0 -5
- data/app/views/decidim/action_delegator/_link_to_question.html.erb +0 -5
- data/app/views/decidim/consultations/consultations/_question.html.erb +0 -41
- data/app/views/decidim/consultations/consultations/show.html.erb +0 -14
- data/app/views/decidim/consultations/question_multiple_votes/_form.html.erb +0 -25
- data/app/views/decidim/consultations/questions/_vote_button.html.erb +0 -106
- data/app/views/decidim/consultations/questions/_vote_modal.html.erb +0 -30
- data/app/views/decidim/consultations/questions/_vote_modal_confirm.html.erb +0 -38
- data/app/views/layouts/decidim/action_delegator/admin/_users_sidebar.html.erb +0 -56
- data/app/views/layouts/decidim/action_delegator/admin/delegations.html.erb +0 -13
- data/app/views/layouts/decidim/admin/consultation.html.erb +0 -56
- data/app/views/layouts/decidim/admin/question.html.erb +0 -98
- data/app/views/layouts/decidim/admin/users.html.erb +0 -7
- data/config/initializers/doorkeeper.rb +0 -492
- data/lib/json_key.rb +0 -10
- /data/app/commands/{decidim → concerns/decidim}/action_delegator/consultations/multiple_vote_question_override.rb +0 -0
- /data/app/controllers/decidim/action_delegator/admin/exports/{sum_of_weights_controller.rb → _sum_of_weights_controller.rb} +0 -0
- /data/app/forms/{decidim → concerns/decidim}/action_delegator/consultations/vote_form_override.rb +0 -0
- /data/app/models/{decidim → concerns/decidim}/action_delegator/consultations/vote_override.rb +0 -0
- /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
|
-
|
13
|
-
|
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,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
|
17
|
-
|
18
|
-
|
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
|