decidim-action_delegator 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +225 -0
  4. data/Rakefile +41 -0
  5. data/app/assets/config/admin/decidim_action_delegator_manifest.css +0 -0
  6. data/app/assets/config/admin/decidim_action_delegator_manifest.js +1 -0
  7. data/app/assets/config/decidim_action_delegator_manifest.css +3 -0
  8. data/app/assets/config/decidim_action_delegator_manifest.js +2 -0
  9. data/app/assets/images/decidim/action_delegator/icon.svg +1 -0
  10. data/app/assets/javascripts/decidim/action_delegator/admin/action_delegator.js.es6 +3 -0
  11. data/app/assets/javascripts/decidim/action_delegator/questions.js.es6 +26 -0
  12. data/app/assets/stylesheets/decidim/action_delegator/questions.scss +25 -0
  13. data/app/commands/decidim/action_delegator/admin/create_delegation.rb +62 -0
  14. data/app/commands/decidim/action_delegator/consultations/multiple_vote_question_override.rb +31 -0
  15. data/app/commands/decidim/action_delegator/consultations/vote_question_override.rb +36 -0
  16. data/app/commands/decidim/action_delegator/vote_delegation.rb +28 -0
  17. data/app/controllers/concerns/decidim/action_delegator/admin/filterable.rb +23 -0
  18. data/app/controllers/decidim/action_delegator/admin/application_controller.rb +19 -0
  19. data/app/controllers/decidim/action_delegator/admin/consultations/exports_controller.rb +27 -0
  20. data/app/controllers/decidim/action_delegator/admin/consultations_controller.rb +33 -0
  21. data/app/controllers/decidim/action_delegator/admin/delegations_controller.rb +79 -0
  22. data/app/controllers/decidim/action_delegator/admin/exports/sum_of_weights_controller.rb +15 -0
  23. data/app/controllers/decidim/action_delegator/admin/results/sum_of_weights_controller.rb +37 -0
  24. data/app/controllers/decidim/action_delegator/admin/settings_controller.rb +71 -0
  25. data/app/controllers/decidim/action_delegator/application_controller.rb +14 -0
  26. data/app/controllers/decidim/action_delegator/consultations/question_multiple_votes_controller_override.rb +28 -0
  27. data/app/controllers/decidim/action_delegator/consultations/question_votes_controller_override.rb +55 -0
  28. data/app/controllers/decidim/action_delegator/user_delegations_controller.rb +22 -0
  29. data/app/controllers/decidim/action_delegator/verifications/sms/authorizations_controller_override.rb +38 -0
  30. data/app/forms/decidim/action_delegator/admin/delegation_form.rb +25 -0
  31. data/app/forms/decidim/action_delegator/consultations/vote_form_override.rb +15 -0
  32. data/app/helpers/decidim/action_delegator/admin/delegation_helper.rb +27 -0
  33. data/app/helpers/decidim/action_delegator/application_helper.rb +10 -0
  34. data/app/jobs/decidim/action_delegator/export_consultation_results_job.rb +51 -0
  35. data/app/jobs/decidim/action_delegator/send_sms_job.rb +60 -0
  36. data/app/jobs/decidim/action_delegator/twilio_send_sms_job.rb +43 -0
  37. data/app/models/decidim/action_delegator/application_record.rb +10 -0
  38. data/app/models/decidim/action_delegator/consultations/vote_override.rb +15 -0
  39. data/app/models/decidim/action_delegator/delegation.rb +26 -0
  40. data/app/models/decidim/action_delegator/setting.rb +22 -0
  41. data/app/models/decidim/action_delegator/unversioned_vote.rb +19 -0
  42. data/app/models/decidim/action_delegator/whodunnit_vote.rb +28 -0
  43. data/app/permissions/decidim/action_delegator/consultations_permissions_extension.rb +27 -0
  44. data/app/permissions/decidim/action_delegator/permissions.rb +74 -0
  45. data/app/presenters/decidim/action_delegator/admin/consultation_presenter.rb +15 -0
  46. data/app/presenters/decidim/action_delegator/admin/setting_presenter.rb +13 -0
  47. data/app/presenters/decidim/action_delegator/question_with_totals.rb +24 -0
  48. data/app/queries/decidim/action_delegator/consultation_delegations.rb +25 -0
  49. data/app/queries/decidim/action_delegator/decrypted_authorizations.rb +112 -0
  50. data/app/queries/decidim/action_delegator/delegated_votes_versions.rb +31 -0
  51. data/app/queries/decidim/action_delegator/delegates_votes_by_consultation.rb +24 -0
  52. data/app/queries/decidim/action_delegator/delegates_votes_by_question.rb +26 -0
  53. data/app/queries/decidim/action_delegator/delegation_votes.rb +30 -0
  54. data/app/queries/decidim/action_delegator/grantee_delegations.rb +24 -0
  55. data/app/queries/decidim/action_delegator/json_build_object_query.rb +45 -0
  56. data/app/queries/decidim/action_delegator/organization_delegations.rb +26 -0
  57. data/app/queries/decidim/action_delegator/organization_settings.rb +25 -0
  58. data/app/queries/decidim/action_delegator/published_responses.rb +25 -0
  59. data/app/queries/decidim/action_delegator/responses_by_membership.rb +75 -0
  60. data/app/queries/decidim/action_delegator/scrutiny.rb +87 -0
  61. data/app/queries/decidim/action_delegator/setting_delegations.rb +19 -0
  62. data/app/queries/decidim/action_delegator/sum_of_membership_weight.rb +48 -0
  63. data/app/queries/decidim/action_delegator/sum_of_weights.rb +25 -0
  64. data/app/queries/decidim/action_delegator/type_and_weight.rb +26 -0
  65. data/app/queries/decidim/action_delegator/voted_with_direct_verification.rb +53 -0
  66. data/app/queries/decidim/action_delegator/votes_count_aggregation.rb +34 -0
  67. data/app/serializers/decidim/action_delegator/consultation_results_serializer.rb +19 -0
  68. data/app/serializers/decidim/action_delegator/sum_of_weights_serializer.rb +17 -0
  69. data/app/services/decidim/action_delegator/sms_gateway.rb +51 -0
  70. data/app/views/decidim/action_delegator/_callout.html.erb +5 -0
  71. data/app/views/decidim/action_delegator/_delegations_modal.html.erb +93 -0
  72. data/app/views/decidim/action_delegator/_link_to_delegations.html.erb +5 -0
  73. data/app/views/decidim/action_delegator/_link_to_question.html.erb +5 -0
  74. data/app/views/decidim/action_delegator/admin/consultations/results.html.erb +62 -0
  75. data/app/views/decidim/action_delegator/admin/delegations/index.html.erb +36 -0
  76. data/app/views/decidim/action_delegator/admin/delegations/new.html.erb +30 -0
  77. data/app/views/decidim/action_delegator/admin/results/sum_of_weights/index.html.erb +63 -0
  78. data/app/views/decidim/action_delegator/admin/settings/index.html.erb +39 -0
  79. data/app/views/decidim/action_delegator/admin/settings/new.html.erb +30 -0
  80. data/app/views/decidim/action_delegator/user_delegations/index.html.erb +19 -0
  81. data/app/views/decidim/consultations/consultations/_question.html.erb +41 -0
  82. data/app/views/decidim/consultations/consultations/show.html.erb +14 -0
  83. data/app/views/decidim/consultations/question_multiple_votes/_form.html.erb +25 -0
  84. data/app/views/decidim/consultations/question_votes/update_vote_button.js.erb +32 -0
  85. data/app/views/decidim/consultations/questions/_vote_button.html.erb +107 -0
  86. data/app/views/decidim/consultations/questions/_vote_modal.html.erb +30 -0
  87. data/app/views/decidim/consultations/questions/_vote_modal_confirm.html.erb +38 -0
  88. data/app/views/decidim/verifications/sms/authorizations/new.html.erb +33 -0
  89. data/app/views/layouts/decidim/action_delegator/admin/_users_sidebar.html.erb +56 -0
  90. data/app/views/layouts/decidim/action_delegator/admin/delegations.html.erb +13 -0
  91. data/app/views/layouts/decidim/admin/consultation.html.erb +56 -0
  92. data/app/views/layouts/decidim/admin/question.html.erb +98 -0
  93. data/app/views/layouts/decidim/admin/users.html.erb +7 -0
  94. data/config/i18n-tasks.yml +10 -0
  95. data/config/locales/ca.yml +101 -0
  96. data/config/locales/cs.yml +101 -0
  97. data/config/locales/en.yml +109 -0
  98. data/config/locales/es.yml +101 -0
  99. data/db/migrate/20200729194540_create_decidim_action_delegator_delegations.rb +12 -0
  100. data/db/migrate/20200824113801_create_settings.rb +13 -0
  101. data/db/migrate/20200828113755_add_setting_id_to_delegations.rb +11 -0
  102. data/db/migrate/20200831141540_make_granter_id_and_grantee_id_non_nullable.rb +8 -0
  103. data/db/migrate/20201001172345_remove_expires_at_from_delegations.rb +7 -0
  104. data/db/migrate/20201005203554_add_setting_granter_unique_index_to_delegations.rb +10 -0
  105. data/db/migrate/20201006084522_remove_setting_id_index_from_delegations.rb +7 -0
  106. data/db/migrate/20201030164808_add_delegation_id_to_versions.rb +8 -0
  107. data/db/seeds.rb +7 -0
  108. data/lib/decidim/action_delegator/admin.rb +10 -0
  109. data/lib/decidim/action_delegator/admin_engine.rb +42 -0
  110. data/lib/decidim/action_delegator/engine.rb +53 -0
  111. data/lib/decidim/action_delegator/test/factories.rb +25 -0
  112. data/lib/decidim/action_delegator/version.rb +10 -0
  113. data/lib/decidim/action_delegator.rb +29 -0
  114. data/lib/json_key.rb +10 -0
  115. metadata +267 -0
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ module Exports
7
+ class SumOfWeightsController < Admin::Consultations::ExportsController
8
+ def type
9
+ "sum_of_weights"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ module Results
7
+ class SumOfWeightsController < Decidim::Consultations::Admin::ConsultationsController
8
+ def index
9
+ params[:slug] = params[:consultation_slug]
10
+
11
+ enforce_permission_to :read, :consultation, consultation: current_consultation
12
+
13
+ @questions = Scrutiny.new(current_consultation).questions
14
+ @responses = responses.group_by(&:question_id)
15
+ @total_delegates = DelegatesVotesByConsultation.new(current_consultation).query
16
+
17
+ render layout: "decidim/admin/consultation"
18
+ end
19
+
20
+ private
21
+
22
+ def permission_class_chain
23
+ Decidim.permissions_registry.chain_for(ActionDelegator::Admin::ApplicationController)
24
+ end
25
+
26
+ def questions
27
+ current_consultation.questions.published.includes(:responses)
28
+ end
29
+
30
+ def responses
31
+ SumOfWeights.new(current_consultation).query
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class SettingsController < ActionDelegator::Admin::ApplicationController
7
+ helper DelegationHelper
8
+ include Filterable
9
+
10
+ layout "decidim/admin/users"
11
+
12
+ def index
13
+ enforce_permission_to :index, :setting
14
+
15
+ @settings = filtered_collection.map do |setting|
16
+ SettingPresenter.new(setting)
17
+ end
18
+ end
19
+
20
+ def new
21
+ enforce_permission_to :create, :setting
22
+
23
+ @setting = Setting.new(max_grants: 1)
24
+ end
25
+
26
+ def create
27
+ enforce_permission_to :create, :setting
28
+
29
+ @setting = build_setting
30
+
31
+ if @setting.save
32
+ flash[:notice] = I18n.t("settings.create.success", scope: "decidim.action_delegator.admin")
33
+ redirect_to decidim_admin_action_delegator.settings_path
34
+ else
35
+ flash.now[:error] = I18n.t("settings.create.error", scope: "decidim.action_delegator.admin")
36
+ end
37
+ end
38
+
39
+ def destroy
40
+ enforce_permission_to :destroy, :setting, resource: setting
41
+
42
+ if setting.destroy
43
+ flash[:notice] = I18n.t("settings.destroy.success", scope: "decidim.action_delegator.admin")
44
+ else
45
+ flash[:error] = I18n.t("settings.destroy.error", scope: "decidim.action_delegator.admin")
46
+ end
47
+
48
+ redirect_to settings_path
49
+ end
50
+
51
+ private
52
+
53
+ def setting_params
54
+ params.require(:setting).permit(:max_grants, :decidim_consultation_id)
55
+ end
56
+
57
+ def build_setting
58
+ Setting.new(setting_params)
59
+ end
60
+
61
+ def setting
62
+ @setting ||= collection.find_by(id: params[:id])
63
+ end
64
+
65
+ def collection
66
+ @collection ||= ActionDelegator::OrganizationSettings.new(current_organization).query
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ # This controller is the abstract class from which all other controllers of
6
+ # this engine inherit.
7
+ #
8
+ class ApplicationController < Decidim::ApplicationController
9
+ # def permission_class_chain
10
+ # [::Decidim::ActionDelegator::Permissions] + super
11
+ # end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Consultations
6
+ module QuestionMultipleVotesControllerOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ helper_method :delegation
11
+ before_action do
12
+ session[:delegation_id] = params[:delegation] if params[:delegation]
13
+ end
14
+
15
+ private
16
+
17
+ def delegation
18
+ @delegation ||= Decidim::ActionDelegator::Delegation.find_by(id: delegation_id)
19
+ end
20
+
21
+ def delegation_id
22
+ @delegation_id ||= params[:decidim_consultations_delegation_id] || params[:delegation] || session[:delegation_id]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Consultations
6
+ module QuestionVotesControllerOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ def destroy
11
+ enforce_permission_to_unvote
12
+
13
+ user = delegation.blank? ? current_user : delegation.granter
14
+
15
+ PaperTrail.request(enabled: delegation.present?, whodunnit: current_user.id) do
16
+ Decidim::Consultations::UnvoteQuestion.call(current_question, user) do
17
+ on(:ok) do
18
+ current_question.reload
19
+ render :update_vote_button
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def info_for_paper_trail
28
+ if delegation.present?
29
+ { decidim_action_delegator_delegation_id: delegation.id }
30
+ else
31
+ {}
32
+ end
33
+ end
34
+
35
+ def delegation
36
+ @delegation ||= Decidim::ActionDelegator::Delegation.find_by(id: params[:decidim_consultations_delegation_id])
37
+ end
38
+
39
+ def enforce_permission_to_unvote
40
+ if delegation.blank?
41
+ enforce_permission_to :unvote, :question, question: current_question
42
+ else
43
+ raise Decidim::ActionForbidden unless allowed_to?(
44
+ :unvote_delegation,
45
+ :question,
46
+ { question: current_question, delegation: delegation },
47
+ [Decidim::ActionDelegator::Permissions, Decidim::Admin::Permissions, Decidim::Permissions]
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ # This controller handles user profile actions for this module
6
+ class UserDelegationsController < ActionDelegator::ApplicationController
7
+ include Decidim::UserProfile
8
+
9
+ helper_method :delegations
10
+
11
+ def index
12
+ enforce_permission_to :read, :user, current_user: current_user
13
+ end
14
+
15
+ private
16
+
17
+ def delegations
18
+ @delegations ||= Delegation.where(grantee_id: current_user.id)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Verifications
6
+ module Sms
7
+ module AuthorizationsControllerOverride
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ def new
12
+ enforce_permission_to :create, :authorization, authorization: authorization
13
+
14
+ flash.now[:error] = I18n.t("decidim.action_delegator.authorizations.new.missing_phone_error") unless direct_authorization && membership_phone
15
+
16
+ @form = Decidim::Verifications::Sms::MobilePhoneForm.new mobile_phone_number: membership_phone
17
+ end
18
+
19
+ private
20
+
21
+ def direct_authorization
22
+ @direct_authorization ||= Decidim::Authorization.find_by(
23
+ user: current_user,
24
+ name: "direct_verifications"
25
+ )
26
+ end
27
+
28
+ def membership_phone
29
+ return nil unless direct_authorization
30
+
31
+ direct_authorization.metadata["membership_phone"]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ # A form object used to create a Delegation
7
+ #
8
+ class DelegationForm < Form
9
+ attribute :granter_id, Integer
10
+ attribute :grantee_id, Integer
11
+
12
+ validates :granter_id, presence: true
13
+ validates :grantee_id, presence: true
14
+
15
+ def granter
16
+ User.find_by(id: granter_id)
17
+ end
18
+
19
+ def grantee
20
+ User.find_by(id: grantee_id)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Consultations
6
+ module VoteFormOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ attribute :decidim_consultations_delegation_id, Integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ module DelegationHelper
7
+ def granters_for_select
8
+ current_organization.users
9
+ end
10
+
11
+ def grantees_for_select
12
+ current_organization.users
13
+ end
14
+
15
+ def consultations_for_select
16
+ organization_consultations.map do |consultation|
17
+ ConsultationPresenter.new(consultation)
18
+ end
19
+ end
20
+
21
+ def organization_consultations
22
+ Decidim::Consultations::OrganizationConsultations.new(current_organization).query
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ # Custom helpers, scoped to the action_delegator engine.
6
+ #
7
+ module ApplicationHelper
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class ExportConsultationResultsJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(user, consultation, results_type)
9
+ @consultation = consultation
10
+ @results_type = results_type.to_sym
11
+
12
+ export_data = Decidim::Exporters
13
+ .find_exporter("CSV")
14
+ .new(collection, serializer)
15
+ .export
16
+
17
+ Decidim::ExportMailer.export(user, filename, export_data).deliver_now
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :consultation, :results_type
23
+
24
+ def collection
25
+ query_class.new(consultation).query
26
+ end
27
+
28
+ def query_class
29
+ case results_type
30
+ when :sum_of_weights
31
+ SumOfWeights
32
+ when :type_and_weight
33
+ TypeAndWeight
34
+ end
35
+ end
36
+
37
+ def serializer
38
+ case results_type
39
+ when :sum_of_weights
40
+ SumOfWeightsSerializer
41
+ when :type_and_weight
42
+ ConsultationResultsSerializer
43
+ end
44
+ end
45
+
46
+ def filename
47
+ I18n.t("decidim.admin.consultations.results.export_filename")
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "savon"
4
+
5
+ module Decidim
6
+ module ActionDelegator
7
+ class SendSmsJobException < StandardError; end
8
+ class SendSmsJob < ApplicationJob
9
+ queue_as :default
10
+
11
+ SMSVIRTUAL_WSDL_URL = "https://websms.masmovil.com/api_php/smsvirtual.wsdl"
12
+
13
+ def perform(sender, mobile_phone_number, message)
14
+ @sender = sender
15
+ @mobile_phone_number = mobile_phone_number
16
+ @message = message
17
+
18
+ send_sms!
19
+
20
+ raise SendSmsJobException, response unless success?
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :sender, :mobile_phone_number, :message, :response
26
+
27
+ def send_sms!
28
+ @response = client.call(:send_sms,
29
+ message: {
30
+ user: ENV["SMS_USER"],
31
+ pass: ENV["SMS_PASS"],
32
+ src: sender,
33
+ dst: mobile_phone_number,
34
+ msg: message
35
+ })
36
+ end
37
+
38
+ def success?
39
+ parsed_response[:code] == "200"
40
+ end
41
+
42
+ def client
43
+ @client ||= ::Savon.client(wsdl: SMSVIRTUAL_WSDL_URL)
44
+ end
45
+
46
+ def parsed_response
47
+ return @parsed_response if @parsed_response
48
+
49
+ doc = Nokogiri::XML response.body[:send_sms_response][:result]
50
+
51
+ @parsed_response = {
52
+ code: doc.xpath("//codigo").text,
53
+ description: doc.xpath("//descripcion").text
54
+ }
55
+
56
+ @parsed_response
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "twilio-ruby"
4
+
5
+ module Decidim
6
+ module ActionDelegator
7
+ class TwilioSendSmsJob < ApplicationJob
8
+ queue_as :default
9
+
10
+ def perform(sender, mobile_phone_number, message)
11
+ @sender = sender
12
+ @mobile_phone_number = mobile_phone_number
13
+ @message = message
14
+
15
+ send_sms!
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :sender, :mobile_phone_number, :message
21
+
22
+ def send_sms!
23
+ client.messages.create(
24
+ from: sender,
25
+ to: mobile_phone_number,
26
+ body: message
27
+ )
28
+ end
29
+
30
+ def client
31
+ ::Twilio::REST::Client.new twilio_account_sid, twilio_auth_token
32
+ end
33
+
34
+ def twilio_account_sid
35
+ ENV["TWILIO_ACCOUNT_SID"]
36
+ end
37
+
38
+ def twilio_auth_token
39
+ ENV["TWILIO_AUTH_TOKEN"]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ # Abstract class from which all models in this engine inherit.
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Consultations
6
+ module VoteOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ has_paper_trail
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class Delegation < ApplicationRecord
6
+ self.table_name = "decidim_action_delegator_delegations"
7
+
8
+ belongs_to :granter, class_name: "Decidim::User"
9
+ belongs_to :grantee, class_name: "Decidim::User"
10
+ belongs_to :setting,
11
+ foreign_key: "decidim_action_delegator_setting_id",
12
+ class_name: "Decidim::ActionDelegator::Setting"
13
+
14
+ validates :granter, uniqueness: {
15
+ scope: [:setting],
16
+ message: I18n.t("delegations.create.error_granter_unique", scope: "decidim.action_delegator.admin")
17
+ }
18
+
19
+ delegate :consultation, to: :setting
20
+
21
+ def self.granted_to?(user, consultation)
22
+ GranteeDelegations.for(consultation, user).exists?
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ # Contains the delegation settings of a consultation. Rather than a single attribute here
6
+ # a setting is the record itself: a bunch of configuration values.
7
+ class Setting < ApplicationRecord
8
+ self.table_name = "decidim_action_delegator_settings"
9
+
10
+ belongs_to :consultation,
11
+ foreign_key: "decidim_consultation_id",
12
+ class_name: "Decidim::Consultation"
13
+ has_many :delegations,
14
+ inverse_of: :setting,
15
+ foreign_key: "decidim_action_delegator_setting_id",
16
+ dependent: :destroy
17
+
18
+ validates :max_grants, presence: true
19
+ validates :max_grants, numericality: { greater_than: 0 }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class UnversionedVote < SimpleDelegator
6
+ def save
7
+ PaperTrail.request(enabled: false) do
8
+ super
9
+ end
10
+ end
11
+
12
+ def save!
13
+ PaperTrail.request(enabled: false) do
14
+ super
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class WhodunnitVote < DelegateClass(Decidim::Consultations::Vote)
6
+ def initialize(vote, user)
7
+ @user = user
8
+ super(vote)
9
+ end
10
+
11
+ def save
12
+ PaperTrail.request(whodunnit: user.id) do
13
+ super
14
+ end
15
+ end
16
+
17
+ def save!
18
+ PaperTrail.request(whodunnit: user.id) do
19
+ super
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :user
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module ConsultationsPermissionsExtension
6
+ # Overrides Decidim::Consultations::Permissions to account for delegation votes
7
+ def allowed_public_action?
8
+ return unless permission_action.scope == :public
9
+ return unless permission_action.subject == :question
10
+
11
+ # check if question has been limited by admins first
12
+ return unless authorized? :vote
13
+
14
+ case permission_action.action
15
+ when :vote
16
+ toggle_allow(question.can_be_voted_by?(user) || can_be_delegated?(user))
17
+ when :unvote
18
+ toggle_allow(question.can_be_unvoted_by?(user))
19
+ end
20
+ end
21
+
22
+ def can_be_delegated?(user)
23
+ Delegation.granted_to?(user, question.consultation)
24
+ end
25
+ end
26
+ end
27
+ end