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
data/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # Decidim::ActionDelegator
2
+
3
+ ![[CI] Build](https://github.com/coopdevs/decidim-module-action_delegator/workflows/%5BCI%5D%20Build/badge.svg)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/6ec3c39e8dc2075808e1/maintainability)](https://codeclimate.com/github/coopdevs/decidim-module-action_delegator/maintainability)
5
+ [![Codecov](https://codecov.io/gh/coopdevs/decidim-module-action_delegator/branch/master/graph/badge.svg)](https://codecov.io/gh/coopdevs/decidim-module-action_delegator)
6
+
7
+
8
+ A tool for Decidim that provides extended functionalities for cooperatives.
9
+
10
+ Combines a CSV-like verification method with impersonation capabilities that allow users to delegate some actions to others.
11
+
12
+ Admin can set limits to the number of delegation per users an other characteristics.
13
+
14
+ Initially, only votes can be delegated.
15
+
16
+ ## Dependencies
17
+
18
+ * [decidim-consultations](https://github.com/decidim/decidim/tree/master/decidim-consultations) >= v0.24.0
19
+ * [decidim-admin](https://github.com/decidim/decidim/tree/master/decidim-admin) >= v0.24.0
20
+ * [decidim-core](https://github.com/decidim/decidim/tree/master/decidim-core) >= v0.24.0
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem "decidim-action_delegator"
28
+ ```
29
+
30
+ And then execute:
31
+
32
+ ```bash
33
+ bundle
34
+ bundle exec rails decidim_action_delegator:install:migrations
35
+ bundle exec rails db:migrate
36
+ ```
37
+
38
+ Depending on your Decidim version, choose the corresponding version to ensure compatibility:
39
+
40
+ | Version | Compatible Decidim versions |
41
+ |---|---|
42
+ | 0.4 | 0.24.x |
43
+ | 0.3 | 0.24.x |
44
+ | 0.2 | 0.23.x |
45
+ | 0.1 | 0.22.0 |
46
+
47
+ > *Heads up!* [Consultations module will be deprecated in the near future.](https://github.com/decidim/decidim/issues/7097)
48
+
49
+ ## Usage
50
+
51
+ ActionDelegator does not provides new Components or Participatory Spaces but enhances some functionalities in them.
52
+
53
+ ### Extended consultation results
54
+
55
+ This gem modifies the consultation's results page adding two extra columns
56
+ `Membership type` and `Membership weight`. This requires a Decidim verification
57
+ that creates `decidim_authorizations` records which include the following JSON
58
+ structure in the `metadata` column:
59
+
60
+ ```json
61
+ "{ membership_type: '', membership_weight: '' }"
62
+ ```
63
+
64
+ See https://github.com/Platoniq/decidim-verifications-direct_verifications/pull/2
65
+ as an example of such verification.
66
+
67
+ ### SMS gateway setup
68
+
69
+ In order to use this new sms gateway you need to configure your application. In config/initializers/decidim.rb set:
70
+
71
+ ```ruby
72
+ config.sms_gateway_service = 'Decidim::ActionDelegator::SmsGateway'
73
+
74
+ ```
75
+ #### Som Connexió
76
+
77
+ You can use Som Connexió as SMS provider which uses [this SOAP API](https://websms.masmovil.com/api_php/smsvirtual.wsdl). Reach out to Som Connexió to sign up first.
78
+
79
+ Then you'll need to set the following ENV vars:
80
+
81
+ ```bash
82
+ SMS_USER= # Username provided by Som Connexió
83
+ SMS_PASS= # Password provided by Som Connexió
84
+ SMS_SENDER= # (optional) Name or phone number used as sender of the SMS
85
+ ```
86
+
87
+ #### Twilio
88
+
89
+ Alternatively, you can use Twilio as provider by specifying the folowing ENV vars
90
+
91
+ ```bash
92
+ TWILIO_ACCOUNT_SID # SID from your Twilio account
93
+ TWILIO_AUTH_TOKEN # Token from your Twilio account
94
+ SMS_SENDER # Twilio's phone number. You need to purchase one there with SMS capability.
95
+ ```
96
+
97
+ ### Track delegated votes and unvotes
98
+
99
+ Votes and revocations done on behalf of other members are tracked through the
100
+ `versions` table using `PaperTrail`. This enables fetching a log of actions
101
+ involving a particular delegation or consultation for auditing purposes. This
102
+ keeps out regular votes and unvotes.
103
+
104
+ When performing votes and unvotes of delegations you'll see things like the
105
+ following in your `versions` table:
106
+
107
+ ```sql
108
+ id | item_type | item_id | event | whodunnit | decidim_action_delegator_delegation_id
109
+ ------+------------------------------+---------+---------+-----------+----------------------------------------
110
+ 2019 | Decidim::Consultations::Vote | 143 | destroy | 1 | 22
111
+ 2018 | Decidim::Consultations::Vote | 143 | create | 1 | 22
112
+ 2017 | Decidim::Consultations::Vote | 142 | create | 1 | 23
113
+ 2016 | Decidim::Consultations::Vote | 138 | destroy | 1 | 23
114
+ ```
115
+
116
+ Note that the `item_type` is `Decidim::Consultations::Vote` and `whoddunit`
117
+ refers to a `Decidim::User` record. This enables joining `versions` and
118
+ `decidim_users` tables although this doesn't follow Decidim's convention of
119
+ using gids, such as `gid://decidim/Decidim::User/1`.
120
+
121
+ You can use `Decidim::ActionDelegato::DelegatedVotesVersions` query object for
122
+ that matter.
123
+
124
+ ## Contributing
125
+
126
+ See [Decidim](https://github.com/decidim/decidim).
127
+
128
+ ### Developing
129
+
130
+ To start contributing to this project, first:
131
+
132
+ - Install the basic dependencies (such as Ruby and PostgreSQL)
133
+ - Clone this repository
134
+
135
+ Decidim's main repository also provides a Docker configuration file if you
136
+ prefer to use Docker instead of installing the dependencies locally on your
137
+ machine.
138
+
139
+ You can create the development app by running the following commands after
140
+ cloning this project:
141
+
142
+ ```bash
143
+ bundle
144
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake development_app
145
+ ```
146
+
147
+ Note that the database user has to have rights to create and drop a database in
148
+ order to create the dummy test app database.
149
+
150
+ Then to test how the module works in Decidim, start the development server:
151
+
152
+ ```bash
153
+ cd development_app
154
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rails s
155
+ ```
156
+
157
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
158
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
159
+ can add the environment variables to the root directory of the project in a file
160
+ named `.rbenv-vars`. If these are defined for the environment, you can omit
161
+ defining these in the commands shown above.
162
+
163
+ #### Code Styling
164
+
165
+ Please follow the code styling defined by the different linters that ensure we
166
+ are all talking with the same language collaborating on the same project. This
167
+ project is set to follow the same rules that Decidim itself follows.
168
+
169
+ [Rubocop](https://rubocop.readthedocs.io/) linter is used for the Ruby language.
170
+
171
+ You can run the code styling checks by running the following commands from the
172
+ console:
173
+
174
+ ```bash
175
+ bundle exec rubocop
176
+ ```
177
+
178
+ To ease up following the style guide, you should install the plugin to your
179
+ favorite editor, such as:
180
+
181
+ - Atom - [linter-rubocop](https://atom.io/packages/linter-rubocop)
182
+ - Sublime Text - [Sublime RuboCop](https://github.com/pderichs/sublime_rubocop)
183
+ - Visual Studio Code - [Rubocop for Visual Studio Code](https://github.com/misogi/vscode-ruby-rubocop)
184
+
185
+ ### Testing
186
+
187
+ To run the tests run the following in the gem development path:
188
+
189
+ ```bash
190
+ bundle
191
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake test_app
192
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rspec
193
+ ```
194
+
195
+ Note that the database user has to have rights to create and drop a database in
196
+ order to create the dummy test app database.
197
+
198
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
199
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
200
+ can add these environment variables to the root directory of the project in a
201
+ file named `.rbenv-vars`. In this case, you can omit defining these in the
202
+ commands shown above.
203
+
204
+ ### Test code coverage
205
+
206
+ If you want to generate the code coverage report for the tests, you can use
207
+ the `SIMPLECOV=1` environment variable in the rspec command as follows:
208
+
209
+ ```bash
210
+ SIMPLECOV=1 bundle exec rspec
211
+ ```
212
+
213
+ ### Localization
214
+
215
+ If you would like to see this module in your own language, you can help with its
216
+ translation at Crowdin:
217
+
218
+ https://crowdin.com/project/decidim-action-delegator-vote
219
+
220
+ This will generate a folder named `coverage` in the project root which contains
221
+ the code coverage report.
222
+
223
+ ## License
224
+
225
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+
5
+ def install_module(path)
6
+ Dir.chdir(path) do
7
+ system("bundle exec rake decidim_consultations:install:migrations")
8
+ system("bundle exec rake decidim_action_delegator:install:migrations")
9
+ system("bundle exec rake db:migrate")
10
+ end
11
+ end
12
+
13
+ def seed_db(path)
14
+ Dir.chdir(path) do
15
+ system("bundle exec rake db:seed")
16
+ end
17
+ end
18
+
19
+ desc "Generates a dummy app for testing"
20
+ task test_app: "decidim:generate_external_test_app" do
21
+ ENV["RAILS_ENV"] = "test"
22
+ install_module("spec/decidim_dummy_app")
23
+ end
24
+
25
+ desc "Generates a development app."
26
+ task :development_app do
27
+ Bundler.with_original_env do
28
+ generate_decidim_app(
29
+ "development_app",
30
+ "--app_name",
31
+ "#{base_app_name}_development_app",
32
+ "--path",
33
+ "..",
34
+ "--recreate_db",
35
+ "--demo"
36
+ )
37
+ end
38
+
39
+ install_module("development_app")
40
+ seed_db("development_app")
41
+ end
@@ -0,0 +1 @@
1
+ //= link decidim/action_delegator/admin/action_delegator.js
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= link decidim/action_delegator/questions.css
3
+ */
@@ -0,0 +1,2 @@
1
+ //= link decidim/action_delegator/admin/action_delegator.js
2
+ //= link decidim/action_delegator/questions.js
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,3 @@
1
+ ((exports) => {
2
+ // Nothing here yet
3
+ })(window);
@@ -0,0 +1,26 @@
1
+ $(() => {
2
+ const voteButton = $("#vote_button"),
3
+ delegationsButton = $("#delegations-button"),
4
+ delegationCallouts = $(".delegation-callout"),
5
+ delegationCalloutsMessage = $(".delegation-callout-message"),
6
+ delegationDialog = $("#delegations-modal"),
7
+ delegationVoteButtons = $(".delegation-vote-button"),
8
+ delegationField = $("#decidim_consultations_delegation_id"),
9
+ voteDialog = $("#question-vote-modal");
10
+
11
+ delegationsButton.click(() => {
12
+ delegationDialog.foundation("open");
13
+ });
14
+
15
+ delegationVoteButtons.click((e) => {
16
+ delegationDialog.foundation("close");
17
+ voteDialog.foundation("open");
18
+ delegationField.val($(e.currentTarget).data("delegation-id"));
19
+ delegationCalloutsMessage.text($(e.currentTarget).data("delegation-granter-name"));
20
+ delegationCallouts.removeClass("is-hidden");
21
+ });
22
+
23
+ voteButton.click(() => {
24
+ delegationCallouts.addClass("is-hidden");
25
+ });
26
+ });
@@ -0,0 +1,25 @@
1
+ .delegations-notice {
2
+ a {
3
+ color: white;
4
+ background:rgba(0,0,0,0.5);
5
+ line-height: 1;
6
+ text-align: center;
7
+ padding: 0.5em;
8
+ }
9
+ }
10
+
11
+ .question-vote-cabin {
12
+ .delegations-notice {
13
+ a {
14
+ color: var(--secondary);
15
+ font-weight: 600;
16
+ background:transparent;
17
+ }
18
+ }
19
+ }
20
+
21
+ #delegations-modal {
22
+ .vote-button-caption {
23
+ font-weight: bold;
24
+ }
25
+ }
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class CreateDelegation < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # form - A form object with the params.
10
+ # delegated_by - The user performing the operation
11
+ def initialize(form, performed_by, current_setting)
12
+ @form = form
13
+ @performed_by = performed_by
14
+ @current_setting = current_setting
15
+ end
16
+
17
+ # Executes the command. Broadcasts these events:
18
+ #
19
+ # - :ok when everything is valid.
20
+ # - :invalid if the form wasn't valid and we couldn't proceed.
21
+ #
22
+ # Returns nothing.
23
+ def call
24
+ return broadcast(:error, generic_error_message) if form.invalid? || current_setting.nil?
25
+ return broadcast(:error, above_max_grants_error_message) if above_max_grants?
26
+
27
+ create_delegation
28
+
29
+ return broadcast(:ok) if delegation.persisted?
30
+
31
+ broadcast(:error, delegation.errors.full_messages.first)
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :form, :performed_by, :current_setting, :delegation
37
+
38
+ def above_max_grants?
39
+ grants_count >= current_setting.max_grants
40
+ end
41
+
42
+ def above_max_grants_error_message
43
+ I18n.t("delegations.create.error_max_grants", scope: "decidim.action_delegator.admin")
44
+ end
45
+
46
+ def generic_error_message
47
+ I18n.t("delegations.create.error", scope: "decidim.action_delegator.admin")
48
+ end
49
+
50
+ def grants_count
51
+ SettingDelegations.new(current_setting).query
52
+ .where(grantee_id: form.grantee_id)
53
+ .count
54
+ end
55
+
56
+ def create_delegation
57
+ @delegation = Delegation.create(form.attributes.merge(setting: current_setting))
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Recreate validations to take into account custom fields and ignore the length limit in proposals
4
+ module Decidim
5
+ module ActionDelegator
6
+ module Consultations
7
+ module MultipleVoteQuestionOverride
8
+ extend ActiveSupport::Concern
9
+ include VoteQuestionOverride
10
+
11
+ included do
12
+ private
13
+
14
+ def create_vote!(vote_form)
15
+ vote = if delegation
16
+ form.context.delegation = delegation
17
+ Decidim::ActionDelegator::VoteDelegation.new(vote_form.response, form.context).call
18
+ else
19
+ normal_vote = form.context.current_question.votes.build(
20
+ author: @current_user,
21
+ response: vote_form.response
22
+ )
23
+ Decidim::ActionDelegator::UnversionedVote.new(normal_vote)
24
+ end
25
+ vote.save!
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Recreate validations to take into account custom fields and ignore the length limit in proposals
4
+ module Decidim
5
+ module ActionDelegator
6
+ module Consultations
7
+ module VoteQuestionOverride
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ private
12
+
13
+ def build_vote
14
+ if delegation
15
+ form.context.delegation = delegation
16
+ Decidim::ActionDelegator::VoteDelegation.new(form.response, form.context).call
17
+ else
18
+ vote = form.context.current_question.votes.build(
19
+ author: form.context.current_user,
20
+ response: form.response
21
+ )
22
+ Decidim::ActionDelegator::UnversionedVote.new(vote)
23
+ end
24
+ end
25
+
26
+ def delegation
27
+ @delegation ||= Decidim::ActionDelegator::GranteeDelegations.for(
28
+ form.context.current_question.consultation,
29
+ form.context.current_user
30
+ ).find_by(id: form.decidim_consultations_delegation_id)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ class VoteDelegation
6
+ def initialize(response, context)
7
+ @response = response
8
+ @context = context
9
+ end
10
+
11
+ def call
12
+ PaperTrail.request.controller_info = { decidim_action_delegator_delegation_id: context.delegation.id }
13
+ WhodunnitVote.new(build_vote, context.current_user)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :context, :response
19
+
20
+ def build_vote
21
+ context.current_question.votes.build(
22
+ author: context.delegation.granter,
23
+ response: response
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module ActionDelegator
7
+ module Admin
8
+ module Filterable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Decidim::Admin::Filterable
13
+
14
+ private
15
+
16
+ def base_query
17
+ collection
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ class ApplicationController < Decidim::Admin::ApplicationController
9
+ register_permissions(ApplicationController,
10
+ ActionDelegator::Permissions,
11
+ Decidim::Consultations::Permissions,
12
+ Decidim::Admin::Permissions)
13
+ def permission_class_chain
14
+ Decidim.permissions_registry.chain_for(ApplicationController)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ module Consultations
7
+ class ExportsController < ActionDelegator::Admin::ApplicationController
8
+ include NeedsPermission
9
+ include Decidim::Consultations::NeedsConsultation
10
+
11
+ def create
12
+ enforce_permission_to :export_consultation_results, :consultation, consultation: current_consultation
13
+
14
+ ExportConsultationResultsJob.perform_later(current_user, current_consultation, type)
15
+
16
+ flash[:notice] = t("decidim.admin.exports.notice")
17
+ redirect_back(fallback_location: decidim_admin_consultations.results_consultation_path(current_consultation))
18
+ end
19
+
20
+ def type
21
+ "type_and_weight"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class ConsultationsController < Decidim::Consultations::Admin::ConsultationsController
7
+ def results
8
+ enforce_permission_to :read, :consultation, consultation: current_consultation
9
+
10
+ @questions = Scrutiny.new(current_consultation).questions
11
+ @responses = responses.group_by(&:decidim_consultations_questions_id)
12
+ @total_delegates = DelegatesVotesByConsultation.new(current_consultation).query
13
+
14
+ render layout: "decidim/admin/consultation"
15
+ end
16
+
17
+ private
18
+
19
+ def permission_class_chain
20
+ Decidim.permissions_registry.chain_for(ActionDelegator::Admin::ApplicationController)
21
+ end
22
+
23
+ def responses
24
+ ResponsesByMembership.new(published_questions_responses).query
25
+ end
26
+
27
+ def published_questions_responses
28
+ VotedWithDirectVerification.new(PublishedResponses.new(current_consultation).query).query
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ActionDelegator
5
+ module Admin
6
+ class DelegationsController < ActionDelegator::Admin::ApplicationController
7
+ include NeedsPermission
8
+ include Paginable
9
+
10
+ helper DelegationHelper
11
+ helper_method :current_setting
12
+
13
+ layout "decidim/action_delegator/admin/delegations"
14
+
15
+ def index
16
+ enforce_permission_to :index, :delegation
17
+
18
+ @delegations = paginate(collection)
19
+ end
20
+
21
+ def new
22
+ enforce_permission_to :create, :delegation
23
+
24
+ @form = form(DelegationForm).instance
25
+ end
26
+
27
+ def create
28
+ enforce_permission_to :create, :delegation
29
+
30
+ @form = DelegationForm.from_params(params)
31
+
32
+ CreateDelegation.call(@form, current_user, current_setting) do
33
+ on(:ok) do
34
+ notice = I18n.t("delegations.create.success", scope: "decidim.action_delegator.admin")
35
+ redirect_to setting_delegations_path(current_setting), notice: notice
36
+ end
37
+
38
+ on(:error) do |error|
39
+ flash.now[:error] = error
40
+ render :new
41
+ end
42
+ end
43
+ end
44
+
45
+ def destroy
46
+ enforce_permission_to :destroy, :delegation, resource: delegation
47
+
48
+ setting_id = delegation.setting.id
49
+
50
+ if delegation.destroy
51
+ notice = I18n.t("delegations.destroy.success", scope: "decidim.action_delegator.admin")
52
+ redirect_to setting_delegations_path(setting_id), notice: notice
53
+ else
54
+ error = I18n.t("delegations.destroy.error", scope: "decidim.action_delegator.admin")
55
+ redirect_to setting_delegations_path(setting_id), flash: { error: error }
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def delegation
62
+ @delegation ||= collection.find_by(id: params[:id])
63
+ end
64
+
65
+ def collection
66
+ @collection ||= SettingDelegations.new(current_setting).query
67
+ end
68
+
69
+ def current_setting
70
+ @current_setting ||= organization_settings.find_by(id: params[:setting_id])
71
+ end
72
+
73
+ def organization_settings
74
+ ActionDelegator::OrganizationSettings.new(current_organization).query
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end