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,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class Participant < ApplicationRecord
6
+ self.table_name = "decidim_action_delegator_participants"
7
+
8
+ belongs_to :setting,
9
+ foreign_key: "decidim_action_delegator_setting_id",
10
+ class_name: "Decidim::ActionDelegator::Setting"
11
+
12
+ belongs_to :ponderation,
13
+ foreign_key: "decidim_action_delegator_ponderation_id",
14
+ class_name: "Decidim::ActionDelegator::Ponderation",
15
+ optional: true
16
+
17
+ belongs_to :decidim_user,
18
+ class_name: "Decidim::User",
19
+ optional: true
20
+
21
+ delegate :consultation, to: :setting
22
+ delegate :organization, to: :setting
23
+
24
+ validates :setting, presence: true
25
+ validates :decidim_user, uniqueness: { scope: :setting }, if: -> { decidim_user.present? }
26
+ validates :email, uniqueness: { scope: :setting }, if: -> { email.present? }
27
+ validates :phone, uniqueness: { scope: :setting }, if: -> { phone.present? }
28
+
29
+ # sets the decidim user if found
30
+ before_save :set_decidim_user
31
+
32
+ # prevents destroy if has voted
33
+ before_destroy { |record| throw(:abort) if record.voted? }
34
+
35
+ def user
36
+ @user ||= decidim_user || user_from_metadata
37
+ end
38
+
39
+ def user_from_metadata
40
+ @user_from_metadata ||= if setting.email_required?
41
+ Decidim::User.find_by(email: email, organization: setting.organization)
42
+ else
43
+ Decidim::Authorization.find_by(unique_id: uniq_ids)&.user
44
+ end
45
+ end
46
+
47
+ def uniq_ids
48
+ @uniq_ids ||= Participant.verifier_ids(Participant.phone_combinations(["#{phone}-#{organization.id}"]))
49
+ end
50
+
51
+ def self.verifier_ids(seeds)
52
+ seeds.map { |seed| Digest::MD5.hexdigest("#{seed}-#{Digest::MD5.hexdigest(Rails.application.secrets.secret_key_base)}") }
53
+ end
54
+
55
+ def self.phone_combinations(phones)
56
+ phones.map do |phone|
57
+ phone_prefixes.map do |prefix|
58
+ [
59
+ "#{prefix}#{phone}",
60
+ phone.delete_prefix(prefix)
61
+ ]
62
+ end
63
+ end.flatten.uniq
64
+ end
65
+
66
+ def self.phone_prefixes
67
+ prefixes = [""]
68
+ prefixes += ActionDelegator.phone_prefixes if ActionDelegator.phone_prefixes.respond_to?(:map)
69
+ prefixes
70
+ end
71
+
72
+ def user_name
73
+ user&.name
74
+ end
75
+
76
+ def last_login
77
+ user&.last_sign_in_at
78
+ end
79
+
80
+ def ponderation_title
81
+ ponderation&.title
82
+ end
83
+
84
+ # checks if the user has voted in the setting's consultation
85
+ def voted?
86
+ return false if user.blank?
87
+
88
+ @voted ||= if Decidim::Consultations::Vote
89
+ .joins(question: :consultation)
90
+ .where(decidim_consultations_questions: {
91
+ decidim_consultation_id: setting.consultation.id
92
+ }, author: user).any?
93
+ true
94
+ else
95
+ false
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def set_decidim_user
102
+ self.decidim_user = user_from_metadata if decidim_user.blank?
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class Ponderation < ApplicationRecord
6
+ self.table_name = "decidim_action_delegator_ponderations"
7
+
8
+ belongs_to :setting,
9
+ foreign_key: "decidim_action_delegator_setting_id",
10
+ class_name: "Decidim::ActionDelegator::Setting"
11
+
12
+ has_many :participants,
13
+ foreign_key: "decidim_action_delegator_ponderation_id",
14
+ class_name: "Decidim::ActionDelegator::Participant",
15
+ dependent: :restrict_with_error
16
+
17
+ delegate :consultation, to: :setting
18
+
19
+ def title
20
+ @title ||= "#{name} (x#{weight})"
21
+ end
22
+
23
+ def destroyable?
24
+ participants.empty?
25
+ end
26
+ end
27
+ end
28
+ end
@@ -13,10 +13,59 @@ module Decidim
13
13
  has_many :delegations,
14
14
  inverse_of: :setting,
15
15
  foreign_key: "decidim_action_delegator_setting_id",
16
- dependent: :destroy
16
+ class_name: "Decidim::ActionDelegator::Delegation",
17
+ dependent: :restrict_with_error
18
+ has_many :ponderations,
19
+ inverse_of: :setting,
20
+ foreign_key: "decidim_action_delegator_setting_id",
21
+ class_name: "Decidim::ActionDelegator::Ponderation",
22
+ dependent: :restrict_with_error
23
+ has_many :participants,
24
+ inverse_of: :setting,
25
+ foreign_key: "decidim_action_delegator_setting_id",
26
+ class_name: "Decidim::ActionDelegator::Participant",
27
+ dependent: :restrict_with_error
17
28
 
18
29
  validates :max_grants, presence: true
19
30
  validates :max_grants, numericality: { greater_than: 0 }
31
+ validates :consultation, uniqueness: true
32
+
33
+ enum authorization_method: { phone: 0, email: 1, both: 2 }, _prefix: :verify_with
34
+
35
+ delegate :title, to: :consultation
36
+ delegate :organization, to: :consultation
37
+
38
+ default_scope { order(created_at: :desc) }
39
+
40
+ def state
41
+ @state ||= if consultation.end_voting_date < Time.zone.now
42
+ :closed
43
+ elsif consultation.start_voting_date <= Time.zone.now
44
+ :ongoing
45
+ else
46
+ :pending
47
+ end
48
+ end
49
+
50
+ def ongoing?
51
+ state == :ongoing
52
+ end
53
+
54
+ def editable?
55
+ state != :closed
56
+ end
57
+
58
+ def destroyable?
59
+ participants.empty? && ponderations.empty? && delegations.empty?
60
+ end
61
+
62
+ def phone_required?
63
+ verify_with_phone? || verify_with_both?
64
+ end
65
+
66
+ def email_required?
67
+ verify_with_email? || verify_with_both?
68
+ end
20
69
  end
21
70
  end
22
71
  end
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('display_take_part_button_for(question)')" -->
2
+
3
+ <%= render partial: "decidim/action_delegator/consultations/link_to_question", locals: { question: question } %>
@@ -0,0 +1,4 @@
1
+ <!-- replace 'h3.heading6' -->
2
+
3
+ <% next if current_consultation.highlighted_scope == questions.first.scope %>
4
+ <h3 class="heading6"><%= translated_attribute questions&.first&.scope&.name %></h3>
@@ -0,0 +1,8 @@
1
+ <!-- insert_before ".card__content.multiple_votes_form" -->
2
+
3
+ <% if delegation %>
4
+ <div class="delegation-callout callout warning">
5
+ <p><%= t "action_delegator.delegations_modal.callout", scope: "decidim" %> <strong><%= delegation.granter.name %></strong></p>
6
+ </div>
7
+ <%= hidden_field_tag :decidim_consultations_delegation_id, delegation.id %>
8
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_before "erb[silent]:contains('elsif question.consultation.active?')" -->
2
+
3
+ <%= render partial: "decidim/action_delegator/consultations/questions/link_to_delegations", locals: { question: question } %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_before "erb[loud]:contains('javascript_pack_tag')" -->
2
+
3
+ <%= render partial: "decidim/action_delegator/consultations/questions/delegations_modal", locals: { question: question } %>
@@ -0,0 +1,8 @@
1
+ <!-- replace_contents "erb[silent]:contains('if question.multiple?')"
2
+ closing_selector "erb[silent]:contains('else')" -->
3
+
4
+ <%= link_to decidim_consultations.question_question_multiple_votes_path(question, delegation: 0),
5
+ class: "card__button button expanded",
6
+ id: "multivote_button" do %>
7
+ <div class="vote-button-caption"><%= t "questions.vote_button.vote", scope: "decidim" %></div>
8
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_after ".reveal__header" -->
2
+
3
+ <%= render partial: "decidim/action_delegator/consultations/questions/callout", locals: { consultation: question.consultation } %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_after ".reveal__header" -->
2
+
3
+ <%= render partial: "decidim/action_delegator/consultations/questions/callout", locals: { consultation: question.consultation } %>
@@ -0,0 +1,3 @@
1
+ <!-- insert_after "erb[loud]:contains('form.hidden_field :decidim_consultations_response_id')" -->
2
+
3
+ <%= hidden_field_tag :decidim_consultations_delegation_id, nil, id: "decidim_consultations_delegation_id" %>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(virtual_path: "layouts/decidim/admin/consultations",
4
+ name: "remove_deprecation_warning",
5
+ remove: ".callout.warning",
6
+ disabled: !Decidim::ActionDelegator.remove_consultation_deprecation_warning)
7
+ Deface::Override.new(virtual_path: "layouts/decidim/admin/consultation",
8
+ name: "remove_deprecation_warning",
9
+ remove: ".callout.warning",
10
+ disabled: !Decidim::ActionDelegator.remove_consultation_deprecation_warning)
@@ -1,3 +1,5 @@
1
+ import "src/decidim/action_delegator/questions.js";
2
+
1
3
  require.context("../images", true)
2
4
 
3
5
  import "entrypoints/decidim_action_delegator.scss";
@@ -0,0 +1,27 @@
1
+ $(() => {
2
+ const
3
+ delegationCallouts = $(".delegation-callout"),
4
+ delegationCalloutsMessage = $(".delegation-callout-message"),
5
+ delegationDialog = $("#delegations-modal"),
6
+ delegationField = $("#decidim_consultations_delegation_id"),
7
+ delegationVoteButtons = $(".delegation-vote-button"),
8
+ delegationsButton = $("#delegations-button"),
9
+ voteButton = $("#vote_button"),
10
+ voteDialog = $("#question-vote-modal");
11
+
12
+ delegationsButton.click(() => {
13
+ delegationDialog.foundation("open");
14
+ });
15
+
16
+ delegationVoteButtons.click((evt) => {
17
+ delegationDialog.foundation("close");
18
+ voteDialog.foundation("open");
19
+ delegationField.val($(evt.currentTarget).data("delegation-id"));
20
+ delegationCalloutsMessage.text($(evt.currentTarget).data("delegation-granter-name"));
21
+ delegationCallouts.removeClass("is-hidden");
22
+ });
23
+
24
+ voteButton.click(() => {
25
+ delegationCallouts.addClass("is-hidden");
26
+ });
27
+ });
@@ -1,10 +1,11 @@
1
1
  .delegations-notice {
2
2
  a {
3
+ display: block;
3
4
  color: white;
4
- background:rgba(0,0,0,0.5);
5
+ background: rgba(0, 0, 0, .5);
5
6
  line-height: 1;
6
7
  text-align: center;
7
- padding: 0.5em;
8
+ padding: .5em;
8
9
  }
9
10
  }
10
11
 
@@ -13,7 +14,7 @@
13
14
  a {
14
15
  color: var(--secondary);
15
16
  font-weight: 600;
16
- background:transparent;
17
+ background: transparent;
17
18
  }
18
19
  }
19
20
  }
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Consultations
6
+ module PermissionsOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ private
11
+
12
+ # Overrides Decidim::Consultations::Permissions to account for delegation votes
13
+ def allowed_public_action?
14
+ return unless permission_action.scope == :public
15
+ return unless permission_action.subject == :question
16
+
17
+ # check if question has been limited by admins first
18
+ return unless authorized? :vote
19
+
20
+ case permission_action.action
21
+ when :vote
22
+ toggle_allow(question.can_be_voted_by?(user) || can_be_delegated?(user))
23
+ when :unvote
24
+ toggle_allow(question.can_be_unvoted_by?(user))
25
+ end
26
+ end
27
+
28
+ def can_be_delegated?(user)
29
+ Delegation.granted_to?(user, question.consultation)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -3,12 +3,12 @@
3
3
  module Decidim
4
4
  module ActionDelegator
5
5
  class Permissions < Decidim::DefaultPermissions
6
- SUBJECTS_WHITELIST = [:delegation, :setting, :consultation].freeze
6
+ SUBJECTS_WHITELIST = [:delegation, :ponderation, :participant, :setting, :consultation].freeze
7
7
 
8
8
  def permissions
9
9
  allowed_delegation_action?
10
10
 
11
- return permission_action unless user.admin?
11
+ return permission_action unless user && user.admin?
12
12
  return permission_action unless permission_action.scope == :admin
13
13
  return permission_action unless action_delegator_subject?
14
14
 
@@ -46,6 +46,10 @@ module Decidim
46
46
  @delegation ||= context.fetch(:delegation, nil)
47
47
  end
48
48
 
49
+ def ponderation
50
+ @ponderation ||= context.fetch(:ponderation, nil)
51
+ end
52
+
49
53
  def consultation_results_exports_action?
50
54
  permission_action.subject == :consultation && permission_action.action == :export_results
51
55
  end
@@ -10,17 +10,11 @@ module Decidim
10
10
  end
11
11
 
12
12
  def query
13
- statement = <<-SQL.squish
14
- SELECT *
15
- FROM versions
16
- INNER JOIN decidim_action_delegator_delegations
17
- ON decidim_action_delegator_delegations.id = versions.decidim_action_delegator_delegation_id
18
- INNER JOIN decidim_action_delegator_settings
19
- ON decidim_action_delegator_settings.id = decidim_action_delegator_delegations.decidim_action_delegator_setting_id
20
- WHERE decidim_action_delegator_settings.decidim_consultation_id = #{consultation.id}
21
- SQL
22
-
23
- ActiveRecord::Base.connection.execute(statement).to_a
13
+ PaperTrail::Version
14
+ .joins("INNER JOIN decidim_action_delegator_delegations ON decidim_action_delegator_delegations.id = versions.decidim_action_delegator_delegation_id")
15
+ .joins("INNER JOIN decidim_action_delegator_settings ON decidim_action_delegator_settings.id = decidim_action_delegator_delegations.decidim_action_delegator_setting_id")
16
+ .where(decidim_action_delegator_settings: { decidim_consultation_id: consultation.id })
17
+ .order("versions.created_at ASC")
24
18
  end
25
19
 
26
20
  private
@@ -13,6 +13,12 @@ module Decidim
13
13
  .merge(organization_consultations)
14
14
  end
15
15
 
16
+ def active
17
+ Setting
18
+ .joins(:consultation)
19
+ .merge(organization_consultations.active)
20
+ end
21
+
16
22
  private
17
23
 
18
24
  attr_reader :organization
@@ -3,14 +3,6 @@
3
3
  module Decidim
4
4
  module ActionDelegator
5
5
  # Returns total votes of each response by memberships' type and weight.
6
- #
7
- # This query completely relies on the schema of the `metadata` of the relevant
8
- # `decidim_authorizations` records, which is expected to be like:
9
- #
10
- # "{ membership_type: '', membership_weight: '' }"
11
- #
12
- # Note that although we assume `membership_type` to be a string and `membership_weight` to be an
13
- # integer, there are no implications in the code for their actual data types.
14
6
  class ResponsesByMembership < Rectify::Query
15
7
  DEFAULT_METADATA = I18n.t("decidim.admin.consultations.results.default_metadata")
16
8
 
@@ -23,8 +15,8 @@ module Decidim
23
15
  .select(
24
16
  responses[:decidim_consultations_questions_id],
25
17
  responses[:title],
26
- membership(:type),
27
- membership(:weight),
18
+ coalesce(Ponderation.arel_table[:name], default_metadata).as("membership_type"),
19
+ coalesce(Ponderation.arel_table[:weight], 1).as("membership_weight"),
28
20
  votes_count
29
21
  )
30
22
  .group(
@@ -40,17 +32,8 @@ module Decidim
40
32
 
41
33
  attr_reader :relation
42
34
 
43
- def membership(field)
44
- full_field = "membership_#{field}"
45
- coalesce(sql(full_field), default_metadata).as(full_field)
46
- end
47
-
48
- def memberships
49
- Arel::Table.new(:memberships)
50
- end
51
-
52
35
  def default_metadata
53
- sql("'#{DEFAULT_METADATA}'")
36
+ sql(ActiveRecord::Base.sanitize_sql(["?", DEFAULT_METADATA]))
54
37
  end
55
38
 
56
39
  def votes_count
@@ -35,12 +35,8 @@ module Decidim
35
35
  Decidim::Consultations::Response.arel_table
36
36
  end
37
37
 
38
- def authorizations
39
- Decidim::Authorization.arel_table
40
- end
41
-
42
38
  def votes_count
43
- field = Arel.sql("membership_weight")
39
+ field = Ponderation.arel_table[:weight]
44
40
  VotesCountAggregation.new(field, "votes_count").to_sql
45
41
  end
46
42
  end
@@ -16,7 +16,7 @@ module Decidim
16
16
  attr_reader :consultation
17
17
 
18
18
  def published_questions_responses
19
- VotedWithDirectVerification.new(
19
+ VotedWithPonderations.new(
20
20
  PublishedResponses.new(consultation).query
21
21
  ).query
22
22
  end
@@ -8,7 +8,7 @@ module Decidim
8
8
  end
9
9
 
10
10
  def query
11
- relation = VotedWithDirectVerification.new(published_questions_responses).query
11
+ relation = VotedWithPonderations.new(published_questions_responses).query
12
12
  ResponsesByMembership.new(relation).query
13
13
  end
14
14
 
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class VotedWithPonderations < Rectify::Query
6
+ def initialize(relation)
7
+ @relation = relation
8
+ end
9
+
10
+ def query
11
+ relation
12
+ .joins(:votes)
13
+ .joins(ponderation_sql)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :relation
19
+
20
+ def ponderation_sql
21
+ <<~SQL.squish
22
+ LEFT OUTER JOIN "decidim_action_delegator_settings" ON "decidim_action_delegator_settings"."decidim_consultation_id" = "decidim_consultations_questions"."decidim_consultation_id"
23
+ LEFT OUTER JOIN "decidim_action_delegator_participants" ON "decidim_action_delegator_participants"."decidim_action_delegator_setting_id" = "decidim_action_delegator_settings"."id"
24
+ AND "decidim_action_delegator_participants"."decidim_user_id" = "decidim_consultations_votes"."decidim_author_id"
25
+ LEFT OUTER JOIN "decidim_action_delegator_ponderations" ON "decidim_action_delegator_ponderations"."id" = "decidim_action_delegator_participants"."decidim_action_delegator_ponderation_id"
26
+ SQL
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class CsvImporter
6
+ include Decidim::FormFactory
7
+
8
+ def initialize(csv_file, current_user, current_setting)
9
+ @csv_file = csv_file
10
+ @current_user = current_user
11
+ @current_setting = current_setting
12
+ end
13
+
14
+ def import!
15
+ import_summary = {
16
+ total_rows: 0,
17
+ imported_rows: 0,
18
+ error_rows: [],
19
+ skipped_rows: [],
20
+ details_csv_path: nil
21
+ }
22
+
23
+ details_csv_file = File.join(File.dirname(@csv_file), "details.csv")
24
+
25
+ i = 1
26
+ csv = CSV.new(@csv_file, headers: true, col_sep: ",")
27
+
28
+ CSV.open(details_csv_file, "wb") do |details_csv|
29
+ while (row = csv.shift).present?
30
+ i += 1
31
+
32
+ params = extract_params(row)
33
+
34
+ next if row&.empty?
35
+
36
+ handle_form_validity(row, details_csv, import_summary, i) if process(row, params, details_csv, import_summary, i)
37
+ end
38
+ end
39
+
40
+ import_summary[:total_rows] = i - 1
41
+ import_summary[:details_csv_path] = details_csv_file
42
+
43
+ import_summary
44
+ end
45
+
46
+ def handle_skipped_row(row, details_csv, import_summary, row_number, reason)
47
+ import_summary[:skipped_rows] << { row_number: row_number - 1 }
48
+ row["reason"] = reason
49
+ details_csv << row
50
+ end
51
+
52
+ def handle_import_error(row, details_csv, import_summary, row_number, error_messages)
53
+ import_summary[:error_rows] << { row_number: row_number - 1, error_messages: error_messages }
54
+ row["reason"] = error_messages
55
+ details_csv << row
56
+ end
57
+
58
+ def handle_form_validity(row, details_csv, import_summary, row_number)
59
+ raise NotImplementedError
60
+ end
61
+
62
+ def generate_info_message(mismatch_fields)
63
+ with_mismatched_fields = mismatch_fields.present? ? I18n.t("decidim.action_delegator.participants_csv_importer.import.with_mismatched_fields", fields: mismatch_fields) : ""
64
+ I18n.t("decidim.action_delegator.participants_csv_importer.import.skip_import_info", with_mismatched_fields: with_mismatched_fields)
65
+ end
66
+
67
+ def headers(csv, details_csv)
68
+ headers = csv.first.headers
69
+ headers << I18n.t("decidim.action_delegator.participants_csv_importer.import.error_field")
70
+ details_csv << headers
71
+ end
72
+
73
+ def invalid_email?(email)
74
+ email.blank? || !email.match?(::Devise.email_regexp)
75
+ end
76
+
77
+ def process(row)
78
+ raise NotImplementedError
79
+ end
80
+
81
+ def extract_params
82
+ raise NotImplementedError
83
+ end
84
+ end
85
+ end
86
+ end