decidim-decidim_awesome 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +121 -8
  4. data/Rakefile +1 -0
  5. data/app/cells/concerns/decidim/decidim_awesome/proposal_m_cell_override.rb +38 -0
  6. data/app/cells/decidim/decidim_awesome/voting/voting_cards_base_cell.rb +40 -0
  7. data/app/cells/decidim/decidim_awesome/voting/voting_cards_counter/show.erb +15 -0
  8. data/app/cells/decidim/decidim_awesome/voting/voting_cards_counter/vote_button.erb +15 -0
  9. data/app/cells/decidim/decidim_awesome/voting/voting_cards_counter_cell.rb +21 -0
  10. data/app/cells/decidim/decidim_awesome/voting/voting_cards_proposal/show.erb +35 -0
  11. data/app/cells/decidim/decidim_awesome/voting/voting_cards_proposal/vote_block_for.erb +16 -0
  12. data/app/cells/decidim/decidim_awesome/voting/voting_cards_proposal_cell.rb +87 -0
  13. data/app/cells/decidim/decidim_awesome/voting/voting_cards_proposal_modal/show.erb +16 -0
  14. data/app/cells/decidim/decidim_awesome/voting/voting_cards_proposal_modal_cell.rb +20 -0
  15. data/app/cells/decidim/proposals/proposal_m/footer.erb +13 -0
  16. data/app/controllers/concerns/decidim/decidim_awesome/not_found_redirect.rb +2 -2
  17. data/app/controllers/concerns/decidim/decidim_awesome/proposals/orderable_override.rb +100 -0
  18. data/app/controllers/concerns/decidim/decidim_awesome/proposals/proposal_votes_controller_override.rb +64 -0
  19. data/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb +2 -6
  20. data/app/controllers/decidim/decidim_awesome/admin/config_controller.rb +1 -1
  21. data/app/controllers/decidim/decidim_awesome/admin/custom_redirects_controller.rb +1 -1
  22. data/app/controllers/decidim/decidim_awesome/admin/menu_hacks_controller.rb +1 -1
  23. data/app/forms/decidim/decidim_awesome/admin/config_form.rb +7 -0
  24. data/app/forms/decidim/decidim_awesome/admin/custom_redirect_form.rb +1 -1
  25. data/app/forms/decidim/decidim_awesome/proposals/proposal_wizard_create_step_form_override.rb +1 -1
  26. data/app/helpers/decidim/decidim_awesome/admin/system_checker_helpers.rb +4 -0
  27. data/app/models/concerns/decidim/decidim_awesome/has_proposal_extra_fields.rb +51 -0
  28. data/app/models/concerns/decidim/decidim_awesome/has_vote_weight.rb +30 -0
  29. data/app/models/decidim/decidim_awesome/proposal_extra_field.rb +11 -0
  30. data/app/models/decidim/decidim_awesome/vote_weight.rb +30 -0
  31. data/app/overrides/decidim/proposals/admin/proposals/_form/replace_editor.html.erb.deface +3 -0
  32. data/app/overrides/decidim/proposals/proposals/_vote_button/replace_vote_button.html.erb.deface +7 -0
  33. data/app/overrides/decidim/proposals/proposals/_votes_count/replace_counter.html.erb.deface +10 -0
  34. data/app/{views/layouts/decidim/_head.html.erb → overrides/layouts/decidim/_application/add_intergram.html.erb.deface} +1 -1
  35. data/app/overrides/layouts/decidim/_head/add_awesome_legacy_scripts.rb +11 -0
  36. data/app/overrides/layouts/decidim/_head/add_awesome_tags.html.erb.deface +11 -0
  37. data/app/{views/layouts/decidim/admin/_header.html.erb → overrides/layouts/decidim/admin/_application/add_intergram.html.erb.deface} +1 -1
  38. data/app/{views/v0.26/layouts/decidim/admin/_header.html.erb → overrides/layouts/decidim/admin/_header/replace_scripts.html.erb.deface} +1 -6
  39. data/app/overrides/layouts/decidim/admin/_header/replace_styles.html.erb.deface +3 -0
  40. data/app/packs/images/decidim/decidim_awesome/handcard.svg +14 -0
  41. data/app/packs/images/decidim/decidim_awesome/handcheck.svg +17 -0
  42. data/app/packs/src/decidim/decidim_awesome/admin/proposal_sortings.js +13 -0
  43. data/app/packs/src/decidim/decidim_awesome/awesome_admin.js +1 -0
  44. data/app/packs/src/decidim/decidim_awesome/awesome_application.js +1 -0
  45. data/app/packs/src/decidim/decidim_awesome/awesome_map/controllers/controller.js +1 -7
  46. data/app/packs/src/decidim/decidim_awesome/editors/editor.js +10 -4
  47. data/app/packs/src/decidim/decidim_awesome/forms/custom_fields_renderer.js +1 -1
  48. data/app/packs/src/decidim/decidim_awesome/voting/voting_cards.js +63 -0
  49. data/app/packs/stylesheets/decidim/decidim_awesome/awesome_application.scss +1 -0
  50. data/app/packs/stylesheets/decidim/decidim_awesome/voting/voting_cards.scss +223 -0
  51. data/app/serializers/concerns/decidim/decidim_awesome/proposal_serializer_override.rb +64 -0
  52. data/app/types/concerns/decidim/decidim_awesome/proposal_type_override.rb +18 -0
  53. data/app/views/decidim/decidim_awesome/admin/checks/_assets_tester.html.erb +1 -1
  54. data/app/views/decidim/decidim_awesome/admin/checks/index.html.erb +1 -0
  55. data/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb +19 -1
  56. data/app/views/decidim/decidim_awesome/voting/voting_cards/_proposal_m_cell_footer.erb +15 -0
  57. data/app/views/decidim/decidim_awesome/voting/voting_cards/_show_vote_button.html.erb +6 -0
  58. data/app/views/decidim/decidim_awesome/voting/voting_cards/_show_votes_count.html.erb +1 -0
  59. data/app/views/layouts/decidim/decidim_awesome/_awesome_config.html.erb +1 -0
  60. data/app/views/layouts/decidim/{admin/decidim_awesome.html.erb → decidim_awesome/admin/application.html.erb} +1 -1
  61. data/config/locales/ca.yml +64 -1
  62. data/config/locales/cs.yml +46 -1
  63. data/config/locales/de.yml +63 -0
  64. data/config/locales/en.yml +73 -1
  65. data/config/locales/es.yml +65 -1
  66. data/config/locales/eu.yml +5 -0
  67. data/config/locales/fr.yml +58 -1
  68. data/config/locales/hu.yml +227 -0
  69. data/config/locales/it.yml +133 -4
  70. data/config/locales/ja.yml +64 -1
  71. data/config/locales/lt.yml +102 -0
  72. data/config/locales/nl.yml +5 -1
  73. data/config/locales/pt-BR.yml +5 -1
  74. data/config/locales/pt-PT.yml +1 -0
  75. data/config/locales/ro-RO.yml +148 -0
  76. data/config/locales/sv.yml +5 -0
  77. data/db/migrate/20231006113837_create_decidim_awesome_vote_weights.rb +13 -0
  78. data/db/migrate/20231006113841_create_decidim_awesome_proposal_extra_fields.rb +14 -0
  79. data/lib/decidim/decidim_awesome/awesome.rb +43 -4
  80. data/lib/decidim/decidim_awesome/awesome_helpers.rb +7 -0
  81. data/lib/decidim/decidim_awesome/checksums.yml +17 -0
  82. data/lib/decidim/decidim_awesome/config.rb +10 -2
  83. data/lib/decidim/decidim_awesome/engine.rb +80 -2
  84. data/lib/decidim/decidim_awesome/system_checker.rb +4 -0
  85. data/lib/decidim/decidim_awesome/test/factories.rb +18 -0
  86. data/lib/decidim/decidim_awesome/test/initializer.rb +10 -1
  87. data/lib/decidim/decidim_awesome/version.rb +1 -1
  88. data/lib/decidim/decidim_awesome/voting_manifest.rb +60 -0
  89. data/lib/tasks/decidim_awesome_upgrade_tasks.rake +9 -0
  90. data/package.json +5 -4
  91. metadata +61 -11
  92. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +0 -101
  93. data/app/views/v0.26/layouts/decidim/_head.html.erb +0 -45
  94. data/app/views/v0.27/layouts/decidim/_head.html.erb +0 -49
  95. data/app/views/v0.27/layouts/decidim/admin/_header.html.erb +0 -11
  96. /data/app/models/{decidim → concerns/decidim}/decidim_awesome/user_override.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc4186635bb63b150df6c031e3ef9337fcfb71ce7057eddf5a990564ca1cd065
4
- data.tar.gz: c9eaffa1131d1cd81ddb14b45e35928d76901a89e6c5141b3bb03ebb5acac244
3
+ metadata.gz: 20437252da9942039c146c8b99dd93e0268bc4c988ffaf38b050580a1f93434f
4
+ data.tar.gz: 6ca1cad28ea84836b317cde0bd8094ca2607bbf644c2daf607e80bf599f54e1a
5
5
  SHA512:
6
- metadata.gz: 4c42aced89189c8f99c5ad6370ba1f490e0ec77ce9df796d007754f2f45bc9e3fbba0583ef2db6a4e1d63127d1d299123a69f1ac6fa23b8a3e5c7a65e20d706b
7
- data.tar.gz: 51cefc6f281d1006ac905ac0fcd3e8c5461caa47403d815077d7fe9cb42e5df213f60a9998de4528c1b2caca47e87878f313752452c739a8186b75b8ed3cece5
6
+ metadata.gz: b98a417b7ac9ade6622f7689dd8f8a68b0a48824c03f1580f53a8f38f7b16afbf4786fecc1da33d44a4b235eb5ffd99a9b3ec31fbe60d7e93ad918ba7b12a333
7
+ data.tar.gz: 2fdc3eda2f8c77bef0fb25b25fa809b3bb9c64b75015d597439af830c7c76ad1fc1cc60a0ba64e6aefc5158692e8eaa90d1406b38785457fe93a961739bcf766
data/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  CHANGELOG
2
2
  =========
3
3
 
4
+ v0.10.0
5
+ ------
6
+
7
+ Compatibility:
8
+ - Decidim v0.27.4
9
+ - Decidim v0.26.8
10
+
11
+ Features:
12
+ - Migrate to [Deface](https://github.com/spree/deface) for overrides
13
+ - Introduce Weighted Voting with configurable manifests for different types of votings with grades
14
+ - Fix wrong behaviour showing proposals on map
15
+ - Introduced new sorting options for proposals. Added alphabetical sorting, reverse sorting, sorting by votes first and last.
16
+
4
17
  v0.9.3
5
18
  ------
6
19
 
data/README.md CHANGED
@@ -12,7 +12,7 @@ Usability and UX tweaks for Decidim.
12
12
  This plugin allows the administrators to expand the possibilities of Decidim beyond some existing limitations.
13
13
  All tweaks are provided in a optional fashion with granular permissions that let the administrator to choose exactly where to apply those mods. Some tweaks can be applied to any assembly, other in an specific participatory process or even in type of component only.
14
14
 
15
- **DISCLAIMER: This module is heavily tested and widely used, howevever we do not accept any responsibility for breaking anything. Feedback is appreciated though.**
15
+ **DISCLAIMER: This module is heavily tested and widely used, however we do not accept any responsibility for breaking anything. Feedback is appreciated though.**
16
16
 
17
17
  ## Why this plugin?
18
18
 
@@ -104,7 +104,7 @@ Feel free to hide, modify or add items in the Decidim's main menu. You can also
104
104
 
105
105
  #### 11. Assign admins to specific scopes and prevent them modify anything else
106
106
 
107
- Convert any user on the platform (that is not currently an admin) to a limited subset of participatory spaces or event compoponents. Just add users to a box and scope them to some constraints. These users will se the "Edit" button in everywhere they have permissions. Any access to unallowed zones will redirect the user to the admin index page.
107
+ Convert any user on the platform (that is not currently an admin) to a limited subset of participatory spaces or event components. Just add users to a box and scope them to some constraints. These users will see the "Edit" button in everywhere they have permissions. Any access to non allowed zones will redirect the user to the admin index page.
108
108
 
109
109
  ![Scoped admins authorized](examples/scoped_admins_authorized.png)
110
110
  ![Scoped admins unauthorized](examples/scoped_admins_unauthorized.png)
@@ -136,7 +136,7 @@ Using a link with a query string (ie: `/take-me-somewhere?locale=es`) that will
136
136
  * `/processes/canary-islands?locale=es` if query string is not sanitized
137
137
 
138
138
  > Redirections work only after all other routes have been processed, you cannot override an existing route.
139
- > The admin panel comes with a button to check if the redirection works (meaning that no other route is used by te application).
139
+ > The admin panel comes with a button to check if the redirection works (meaning that no other route is used by the application).
140
140
  > Non-working routes will simply be ignored.
141
141
 
142
142
  ![Custom redirections screenshot](examples/custom-redirections.png)
@@ -149,7 +149,7 @@ Rules available:
149
149
 
150
150
  * Minimum title and body length (defaults to 15 chars).
151
151
  * Maximum percentage of capital letters for title and body (defaults to 25%).
152
- * Maximum number of "marks" (aka: exclamation and interrogation signs) that can be consective in the title or the body (defaults to 1).
152
+ * Maximum number of "marks" (aka: exclamation and interrogation signs) that can be consecutive in the title or the body (defaults to 1).
153
153
  * Enable/disable forcing to start the title or the body with a capital letter (defaults to "enabled").
154
154
 
155
155
  ![Custom validations](examples/custom_validations.png)
@@ -162,6 +162,119 @@ Results can be filtered by role and by time range and also exported as CSV or ot
162
162
 
163
163
  ![Admin accountability](examples/admin_accountability.png)
164
164
 
165
+ #### 16. Additional proposal sortings
166
+
167
+ ![Proposal sorting](examples/proposal_sorting.png)
168
+ ![Proposal sorting admin](examples/proposal_sorting-admin.png)
169
+
170
+ This feature allows you to add additional sorting options to the proposals component. By default 4 additional sortings are included:
171
+
172
+ - `supported_first`: Sort proposals supported by me first.
173
+ - `supported_last`: Sort proposals supported by me last.
174
+ - `az`: Sort proposals alphabetically.
175
+ - `za`: Sort proposals alphabetically (reverse).
176
+
177
+ By enabling this feature the user choosed sorting method will be stored in the browser's session. This means that if the user changes the sorting method and then navigates to another page, the same sorting will be applied.
178
+
179
+ You can disable or configure the available sorting types by setting the variable `additional_proposal_sortings` configuration in an initializer:
180
+
181
+ ```ruby
182
+ # config/initializers/awesome_defaults.rb
183
+ Decidim::DecidimAwesome.configure do |config|
184
+ config.additional_proposal_sortings = :disabled
185
+ end
186
+
187
+ # Or, to disable alphabetical sorting:
188
+
189
+ Decidim::DecidimAwesome.configure do |config|
190
+ config.additional_proposal_sortings = [:supported_first, :supported_last]
191
+ end
192
+ ```
193
+
194
+ #### 17. Weighted voting
195
+
196
+ This feature allows you to configure a proposals component to use a weighted voting system. This means that each vote can have a different weight and the result of the vote is calculated as the sum of all the weights.
197
+
198
+ Weighted voting can have different presentations that can be registered in a manifest. Admins can then choose between what type of voting they want for their proposals according to the different manifests registered (classic is always available).
199
+
200
+ Some manifests are included by default in Decidim Awesome, if you consider to create (or pay) for a new one, please open a PR or contact us.
201
+
202
+ For instance, here is how the 3-flag voting system looks like:
203
+
204
+ ![Weighted voting](examples/weighted_voting.png)
205
+
206
+ ##### Creating a new manifest for weighted voting
207
+
208
+ A manifest is defined in a initializer in this way:
209
+
210
+ ```ruby
211
+ if Decidim::DecidimAwesome.enabled?(:weighted_proposal_voting)
212
+ # register available processors
213
+ Decidim::DecidimAwesome.voting_registry.register(:no_admins_vote) do |voting|
214
+ voting.show_vote_button_view = "decidim/decidim_awesome/voting/no_admins_vote/show_vote_button"
215
+ voting.show_votes_count_view = "decidim/decidim_awesome/voting/no_admins_vote/show_votes_count"
216
+ # voting.show_votes_count_view = "" # hide votes count if needed
217
+ voting.proposal_m_cell_footer = "decidim/decidim_awesome/voting/no_admins_vote/proposal_m_cell_footer"
218
+ # define a weight validator (optional, by default all weights are valid)
219
+ voting.weight_validator do |weight, context|
220
+ # don't allow admins to vote
221
+ next if context[:user].admin?
222
+ # don't allow to vote official proposals
223
+ next if context[:proposal].official?
224
+
225
+ weight.in? [1, 2, 3, 5]
226
+ end
227
+
228
+ # optionally, define a label generator block
229
+ # by default labels are extracted from a I18n key following this rule
230
+ # "decidim.decidim_awesome.voting.{MANIFEST_NAME}.weights.weight_{WEIGHT}"
231
+ #
232
+ # voting.label_generator do |weight, context|
233
+ # "Weight #{weight.round}"
234
+ # end
235
+ end
236
+ end
237
+ ```
238
+
239
+ A manifest must define a vote button view for the main proposal view, a vote count view for the proposal list view, a footer for the proposal cell (used in lists) and a validator for the weight value.
240
+
241
+ All views are optional, if set to `nil` they will use the original ones. If set to an empty string `""` they will be hidden.
242
+
243
+ The `weight_validator` is a Proc that receives the weight value and the context with the current user and the proposal and returns true or false if the weight is valid or not.
244
+
245
+ **Notes for view `show_vote_button_view`**
246
+
247
+ When building a new view for the vote button ([see the original](https://github.com/decidim/decidim/blob/release/0.27-stable/decidim-proposals/app/views/decidim/proposals/proposals/_vote_button.html.erb)) is important to take into account the following situations:
248
+
249
+ - If there's a `current_user` logged in
250
+ - If votes are blocked `if current_settings.votes_blocked?`
251
+ - If the user has already voted `if @voted_proposals ? @voted_proposals.include?(proposal.id) : proposal.voted_by?(current_user)`
252
+ - If maximum votes have already reached `if proposal.maximum_votes_reached?`
253
+ - If the proposal can accumulate supports beyond maximum `if proposal.can_accumulate_supports_beyond_threshold`
254
+ - If the current component allows the user to participate `if current_component.participatory_space.can_participate?(current_user)`
255
+ - Note that the [original view](https://github.com/decidim/decidim/blob/release/0.27-stable/decidim-proposals/app/views/decidim/proposals/proposals/_vote_button.html.erb) is overridden only inside the tag `<div id="proposal-<%= proposal.id %>-vote-button" class="button--vote-button">`. You only need to substitute the part inside.
256
+
257
+ To cast a vote a `POST` action is needed with the parameters `proposal_id`, `from_proposals_list` and `weight`. The route where to send the vote can be constructed such as:
258
+
259
+ ```erb
260
+ <%= link_to "Vote with weight=3", proposal_proposal_vote_path(proposal_id: proposal, from_proposals_list: from_proposals_list, weight: 3), remote: true, method: :post %>
261
+ ```
262
+
263
+ To delete a vote, you just need to change the method to `:delete`
264
+
265
+ **Notes for view `show_votes_count_view`**
266
+
267
+ This view must implement the number of votes already cast. It requires an HTML tag with the id `proposal-<%= proposal.id %>-votes-count`, this is used by the Ajax vote re-loader feature: It will replace it's content with the new number.
268
+
269
+ You can also completely hide this view (using `voting.show_votes_count_view = ""` in the manifest declaration). This is useful if you are using the same `show_vote_button_view` to also display the total counters (or your implementation does not use that).
270
+
271
+ **Notes for view `proposal_m_cell_footer`**
272
+
273
+ This view is used by the proposal cell in lists. It must implement the vote button and the vote count. The vote button must be a link with the same characteristics as the one explained above for the `show_vote_button_view` (typically you can just render the same view using `<%= render partial: my/path/to/view, { locals: model: proposal, from_proposals_list: true } %>`).
274
+
275
+ Note that, it is strongly recommended to add and HTML tag element with the id `proposal-<%= proposal.id %>-votes-count` so the Ajax vote re-loader can work. Even if you don't use (in this case use a `style="display:none"` attribute), this is because the Ajax reloader always look for this element and throw JavaScript errors if not.
276
+
277
+
165
278
  #### To be continued...
166
279
 
167
280
  We're not done! Please check the [issues](/decidim-ice/decidim-module-decidim_awesome/issues) (and participate) to see what's on our mind
@@ -181,12 +294,10 @@ And then execute:
181
294
  ```bash
182
295
  bundle
183
296
  bundle exec rails decidim_decidim_awesome:install:migrations
184
- bundle exec rails decidim_decidim_awesome:webpacker:install
297
+ bundle exec rails decidim:upgrade
185
298
  bundle exec rails db:migrate
186
299
  ```
187
300
 
188
- > NOTE: the `decidim_decidim_awesome:webpacker:install` is only necessary for Decidim versions starting at 0.25.
189
-
190
301
  If you are upgrading from a version prior to 0.8, make sure to visit the URL `/admin/decidim_awesome/checks` and run image migrations for the old images:
191
302
 
192
303
  ![Check image migrations](examples/check_image_migrations.png)
@@ -203,12 +314,13 @@ RAILS_ENV=production bin/rails decidim_awesome:active_storage_migrations:check_m
203
314
  ```
204
315
 
205
316
  The correct version of Decidim Awesome should resolved automatically by the Bundler.
206
- However you can force some specific version using `gem "decidim-decidim_awesome", "~> 0.8.0"` in the Gemfile.
317
+ However you can force some specific version using `gem "decidim-decidim_awesome", "~> 0.10.0"` in the Gemfile.
207
318
 
208
319
  Depending on your Decidim version, choose the corresponding Awesome version to ensure compatibility:
209
320
 
210
321
  | Awesome version | Compatible Decidim versions |
211
322
  |---|---|
323
+ | 0.10.0 | >= 0.26.7, >= 0.27.3 |
212
324
  | 0.9.2 | >= 0.26.7, >= 0.27.3 |
213
325
  | 0.9.x | 0.26.x, 0.27.x |
214
326
  | 0.8.x | 0.25.x, 0.26.x |
@@ -217,6 +329,7 @@ Depending on your Decidim version, choose the corresponding Awesome version to e
217
329
  | 0.5.x | 0.21.x, 0.22.x |
218
330
 
219
331
  > *Heads up!*
332
+ > * version 0.10.0 requires database migrations! Don't forget the migrations step when updating.
220
333
  > * version 0.8.0 removes CSS Themes for tenants. If you have been using them you will have to manually migrate them to custom styles.
221
334
  > * version 0.8.0 uses ActiveStorage, same as Decidim 0.25. 2 new rake task have been introduced to facilitate the migration: `bin/rails decidim_awesome:active_storage_migrations:check_migration_from_carrierwave` and
222
335
  `bin/rails decidim_awesome:active_storage_migrations:migrate_from_carrierwave`
data/Rakefile CHANGED
@@ -26,6 +26,7 @@ def copy_helpers
26
26
  FileUtils.mkdir_p "spec/decidim_dummy_app/app/views/v0.11", verbose: true
27
27
  FileUtils.cp_r "lib/decidim/decidim_awesome/test/layouts", "spec/decidim_dummy_app/app/views/v0.11/layouts", verbose: true
28
28
  FileUtils.cp "lib/decidim/decidim_awesome/test/initializer.rb", "spec/decidim_dummy_app/config/initializers/decidim_awesome.rb", verbose: true
29
+ FileUtils.cp "spec/fixtures/files/tile-0.png", "spec/decidim_dummy_app/public/tile-0.png", verbose: true
29
30
  end
30
31
 
31
32
  desc "Generates a dummy app for testing"
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module ProposalMCellOverride
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # rubocop:disable Metrics/CyclomaticComplexity
10
+ def cache_hash
11
+ hash = []
12
+ hash << I18n.locale.to_s
13
+ hash << model.cache_key_with_version
14
+ hash << model.proposal_votes_count
15
+ hash << model.extra_fields&.vote_weight_totals
16
+ hash << model.endorsements_count
17
+ hash << model.comments_count
18
+ hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
19
+ hash << Digest::MD5.hexdigest(resource_image_path) if resource_image_path
20
+ hash << render_space? ? 1 : 0
21
+ if current_user
22
+ hash << current_user.cache_key_with_version
23
+ hash << current_user.follows?(model) ? 1 : 0
24
+ end
25
+ hash << model.follows_count
26
+ hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
27
+ hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
28
+ hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
29
+ hash << has_footer?
30
+ hash << has_actions?
31
+
32
+ hash.join(Decidim.cache_key_separator)
33
+ end
34
+ # rubocop:enable Metrics/CyclomaticComplexity
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Voting
6
+ class VotingCardsBaseCell < Decidim::ViewModel
7
+ include Decidim::IconHelper
8
+ include Decidim::ComponentPathHelper
9
+ include Decidim::Proposals::ProposalVotesHelper
10
+ include Decidim::Proposals::Engine.routes.url_helpers
11
+
12
+ delegate :current_settings, to: :current_component
13
+
14
+ def proposal
15
+ model
16
+ end
17
+
18
+ def sanitized_title
19
+ strip_tags(translated_attribute(proposal.title))
20
+ end
21
+
22
+ def current_component
23
+ proposal.component
24
+ end
25
+
26
+ def component_settings
27
+ current_component.settings
28
+ end
29
+
30
+ def current_vote
31
+ @current_vote ||= Decidim::Proposals::ProposalVote.find_by(author: current_user, proposal: model)
32
+ end
33
+
34
+ def user_voted_weight
35
+ current_vote&.weight
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ <% unless proposal.rejected? || proposal.withdrawn? %>
2
+ <% unless current_settings.votes_hidden? %>
3
+ <span class="votes_counter">
4
+ <span data-weight="3" title="<%= proposal.manifest.label_for(3) %>" class="weight_3"><%= t("decidim.decidim_awesome.voting.voting_cards.weights.weight_3_short") %> <%= model.weight_count(3) %></span> |
5
+ <span data-weight="2" title="<%= proposal.manifest.label_for(2) %>" class="weight_2"><%= t("decidim.decidim_awesome.voting.voting_cards.weights.weight_2_short") %> <%= model.weight_count(2) %></span> |
6
+ <span data-weight="1" title="<%= proposal.manifest.label_for(1) %>" class="weight_1"><%= t("decidim.decidim_awesome.voting.voting_cards.weights.weight_1_short") %> <%= model.weight_count(1) %></span>
7
+ <% if current_component.settings.voting_cards_show_abstain %>
8
+ | <span title="<%= proposal.manifest.label_for(0) %>" data-weight="0" class="weight_0"><%= t("decidim.decidim_awesome.voting.voting_cards.weights.weight_0_short") %> <%= model.weight_count(0) %></span>
9
+ <% end %>
10
+ </span>
11
+ <% end %>
12
+ <div id="proposal-<%= model.id %>-vote-button">
13
+ <%= render "vote_button" %>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%= link_to resource_path,
2
+ class: "button #{vote_btn_class} small button--sc#{" disabled" if current_settings.votes_blocked?}",
3
+ title: t("decidim.decidim_awesome.voting.voting_cards.vote_button") do %>
4
+ <% if user_voted_weight %>
5
+ <%= icon "actions", class: "icon" %> <%= t("decidim.decidim_awesome.voting.voting_cards.voted") %>
6
+ <% elsif proposal.maximum_votes_reached? && !proposal.can_accumulate_supports_beyond_threshold && current_component.participatory_space.can_participate?(current_user) %>
7
+ <%= t("decidim.proposals.proposals.vote_button.maximum_votes_reached") %>
8
+ <% elsif vote_limit_enabled? && remaining_votes_count_for(current_user) <= 0 %>
9
+ <%= t("decidim.proposals.proposals.vote_button.no_votes_remaining") %>
10
+ <% elsif current_settings.votes_blocked? || !current_component.participatory_space.can_participate?(current_user) %>
11
+ <%= t("decidim.proposals.proposals.vote_button.votes_blocked") %>
12
+ <% else %>
13
+ <%= t("decidim.decidim_awesome.voting.voting_cards.vote_button") %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Voting
6
+ class VotingCardsCounterCell < VotingCardsBaseCell
7
+ def show
8
+ render :show
9
+ end
10
+
11
+ def resource_path
12
+ resource_locator(model).path
13
+ end
14
+
15
+ def vote_btn_class
16
+ user_voted_weight ? "weight_#{user_voted_weight.to_i}" : "hollow"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ <h4 class="heading4 vote-title"><%= title %></h4>
2
+
3
+ <div class="flex--sbc">
4
+ <%= vote_block_for(proposal, 3) %>
5
+ <%= vote_block_for(proposal, 2) %>
6
+ <%= vote_block_for(proposal, 1) %>
7
+ </div>
8
+
9
+ <% if component_settings.voting_cards_show_abstain? %>
10
+ <%= action_authorized_link_to :vote,
11
+ voted_for?(0) ? t("decidim.decidim_awesome.voting.voting_cards.abstained") : proposal.manifest.label_for(0),
12
+ proposal_vote_path(0),
13
+ link_options(0).merge({
14
+ title: t("decidim.decidim_awesome.voting.voting_cards.voting_for", proposal: sanitized_title, type: proposal.manifest.label_for(0)),
15
+ class: "button expanded vote-action abstain-button small #{classes_for(0)}"
16
+ }) %>
17
+ <% end %>
18
+
19
+ <% if voted_for_any? && !current_settings.votes_blocked? %>
20
+ <p class="text-center">
21
+ <%= action_authorized_link_to :unvote,
22
+ t("decidim.decidim_awesome.voting.voting_cards.change_vote"),
23
+ proposal_vote_path(current_vote&.weight),
24
+ remote: true,
25
+ method: :delete,
26
+ id: "change-vote",
27
+ class: "change-vote-button vote-action" %>
28
+ </p>
29
+ <% elsif proposal.maximum_votes_reached? && !proposal.can_accumulate_supports_beyond_threshold && current_component.participatory_space.can_participate?(current_user) %>
30
+ <p class="text-center"><%= t("decidim.proposals.proposals.vote_button.maximum_votes_reached") %></p>
31
+ <% elsif vote_limit_enabled? && remaining_votes_count_for(current_user) <= 0 %>
32
+ <p class="text-center"><%= t("decidim.proposals.proposals.vote_button.no_votes_remaining") %></p>
33
+ <% elsif current_settings.votes_blocked? || !current_component.participatory_space.can_participate?(current_user) %>
34
+ <p class="text-center"><%= t("decidim.proposals.proposals.vote_button.votes_blocked") %></p>
35
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <div id="vote-container" data-proposal-id="<%= proposal.id %>">
2
+ <div class="vote-block">
3
+ <% unless current_settings.votes_hidden? %>
4
+ <p class="vote-count" data-weight="<%= weight %>"><%= proposal_votes(weight) %></p>
5
+ <% end %>
6
+ <%= action_authorized_link_to :vote,
7
+ proposal_vote_path(weight),
8
+ **link_options(weight) do %>
9
+ <span class="vote-label"><%= proposal.manifest.label_for(weight) %></span>
10
+ <%= content_tag :svg, role: "img" do
11
+ content_tag(:title, t("decidim.decidim_awesome.voting.voting_cards.voting_for", proposal: sanitized_title, type: proposal.manifest.label_for(weight))) +
12
+ content_tag(:use, "", "href" => svg_path(weight))
13
+ end %>
14
+ <% end %>
15
+ </div>
16
+ </div>
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Voting
6
+ class VotingCardsProposalCell < VotingCardsBaseCell
7
+ VOTE_WEIGHTS = [0, 1, 2, 3].freeze
8
+
9
+ def show
10
+ render :show
11
+ end
12
+
13
+ def vote_block_for(proposal, weight)
14
+ render partial: "vote_block", locals: {
15
+ proposal: proposal,
16
+ weight: weight
17
+ }
18
+ end
19
+
20
+ def proposal_votes(weight)
21
+ model.weight_count(weight)
22
+ end
23
+
24
+ def voted_for?(option)
25
+ user_voted_weight == option
26
+ end
27
+
28
+ def from_proposals_list
29
+ options[:from_proposals_list]
30
+ end
31
+
32
+ def proposal_vote_path(weight)
33
+ proposal_proposal_vote_path(proposal_id: proposal.id, from_proposals_list: from_proposals_list, weight: weight)
34
+ end
35
+
36
+ def link_options(weight)
37
+ ops = {
38
+ class: "vote-action vote-card #{classes_for(weight)}"
39
+ }
40
+ if current_user
41
+ ops.merge!({
42
+ remote: true,
43
+ method: :post
44
+ })
45
+ end
46
+ ops
47
+ end
48
+
49
+ def svg_path(weight)
50
+ card = "handcard"
51
+ card = "handcheck" if voted_for?(weight)
52
+ "#{asset_pack_path("media/images/#{card}.svg")}#handcard"
53
+ end
54
+
55
+ def classes_for(weight)
56
+ ops = ["weight_#{weight}"]
57
+ ops << "voted" if voted_for?(weight)
58
+ ops << "dim" if voted_for_any? && !voted_for?(weight)
59
+ ops << "disabled" if disabled?
60
+
61
+ ops.join(" ")
62
+ end
63
+
64
+ def disabled?
65
+ return true if voted_for_any? || current_settings.votes_blocked?
66
+
67
+ if proposal.maximum_votes_reached? && !proposal.can_accumulate_supports_beyond_threshold && current_component.participatory_space.can_participate?(current_user)
68
+ return true
69
+ end
70
+
71
+ return true if vote_limit_enabled? && remaining_votes_count_for(current_user) <= 0
72
+ end
73
+
74
+ def voted_for_any?
75
+ VOTE_WEIGHTS.any? { |opt| voted_for?(opt) }
76
+ end
77
+
78
+ def title
79
+ txt ||= translated_attribute(current_component.settings.voting_cards_box_title)
80
+ return "" if txt == "-"
81
+
82
+ txt.presence || t("decidim.decidim_awesome.voting.voting_cards.default_box_title")
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,16 @@
1
+ <!--the modal window will be used in another pr-->
2
+ <div class="reveal vote_proposal_modal voting-voting_cards" id="VotingCardsModalHelp" data-reveal role="dialog" aria-modal="true" aria-labelledby="VotingCardsModalHelp-label">
3
+ <div class="reveal__content">
4
+ <div class="instructions"><%= vote_instructions %></div>
5
+ <div class="current-choice voting-voting_cards"><div class="vote-card"></div></div>
6
+ <div class="future-dismiss">
7
+ <%= check_box_tag "voting_cards-skip_help", current_component.id, false %>
8
+ <%= label_tag "voting_cards-skip_help", t("decidim.decidim_awesome.voting.voting_cards.modal.skip") %>
9
+ </div>
10
+
11
+ <div class="text-center">
12
+ <button class="button vote-action"><%= t("decidim.decidim_awesome.voting.voting_cards.modal.proceed") %></button>
13
+ <button class="button cancel-action hollow secondary" data-close><%= t("decidim.decidim_awesome.voting.voting_cards.modal.cancel") %></button>
14
+ </div>
15
+ </div>
16
+ </div>
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Voting
6
+ class VotingCardsProposalModalCell < VotingCardsBaseCell
7
+ include Decidim::Proposals::Engine.routes.url_helpers
8
+
9
+ def show
10
+ render :show
11
+ end
12
+
13
+ def vote_instructions
14
+ translated_attribute(current_component.settings.voting_cards_instructions).presence || t("decidim.decidim_awesome.voting.voting_cards.default_instructions_html",
15
+ organization: current_organization.name)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ <%#
2
+ A hack for the Trailblazer Cell component, here we obtain the "parent" view
3
+ https://github.com/trailblazer/cells/blob/master/lib/cell/view_model.rb#L102
4
+ renders parent view by removing the awesome override from the paths
5
+ %>
6
+ <% original = normalize_options(:footer) %>
7
+ <% original[:prefixes] = original[:prefixes].filter {|path| path != Decidim::DecidimAwesome::Engine.root.join("app/cells/decidim/proposals/proposal_m").to_s } %>
8
+
9
+ <% if view = awesome_voting_manifest_for(current_component)&.proposal_m_cell_footer %>
10
+ <%= render partial: view if view.present? %>
11
+ <% else %>
12
+ <%= render original %>
13
+ <% end %>
@@ -43,8 +43,8 @@ module Decidim
43
43
  return unless redirects.is_a? Hash
44
44
 
45
45
  path, query = fullpath.split("?")
46
- destination = redirects.dig(path.downcase, "destination")
47
- pass_query = redirects.dig(path.downcase, "pass_query")
46
+ destination = redirects.dig(path, "destination")
47
+ pass_query = redirects.dig(path, "pass_query")
48
48
  if pass_query.present?
49
49
  union = destination.include?("?") ? "&" : "?"
50
50
  destination = "#{destination}#{union}#{query}"