decidim-proposals 0.29.1 → 0.30.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +12 -12
  3. data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
  4. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +1 -1
  5. data/app/cells/decidim/proposals/proposal_g/show.erb +13 -0
  6. data/app/cells/decidim/proposals/proposal_g_cell.rb +34 -0
  7. data/app/cells/decidim/proposals/proposal_history_cell.rb +107 -0
  8. data/app/cells/decidim/proposals/proposal_l/show.erb +37 -0
  9. data/app/cells/decidim/proposals/proposal_l_cell.rb +26 -18
  10. data/app/cells/decidim/proposals/proposal_metadata_cell.rb +2 -2
  11. data/app/cells/decidim/proposals/proposal_vote/show.erb +75 -0
  12. data/app/cells/decidim/proposals/proposal_vote_cell.rb +43 -0
  13. data/app/commands/decidim/proposals/accept_coauthorship.rb +62 -0
  14. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +14 -0
  15. data/app/commands/decidim/proposals/admin/create_proposal.rb +6 -14
  16. data/app/commands/decidim/proposals/admin/create_proposal_note.rb +20 -11
  17. data/app/commands/decidim/proposals/admin/import_proposals.rb +0 -5
  18. data/app/commands/decidim/proposals/admin/merge_proposals.rb +2 -2
  19. data/app/commands/decidim/proposals/admin/proposal_notes_methods.rb +48 -0
  20. data/app/commands/decidim/proposals/admin/reply_proposal_note.rb +92 -0
  21. data/app/commands/decidim/proposals/admin/split_proposals.rb +2 -2
  22. data/app/commands/decidim/proposals/admin/update_proposal.rb +10 -16
  23. data/app/commands/decidim/proposals/admin/update_proposal_taxonomies.rb +34 -0
  24. data/app/commands/decidim/proposals/cancel_coauthorship.rb +32 -0
  25. data/app/commands/decidim/proposals/create_collaborative_draft.rb +1 -2
  26. data/app/commands/decidim/proposals/create_proposal.rb +1 -2
  27. data/app/commands/decidim/proposals/invite_coauthor.rb +45 -0
  28. data/app/commands/decidim/proposals/publish_collaborative_draft.rb +1 -2
  29. data/app/commands/decidim/proposals/reject_coauthorship.rb +54 -0
  30. data/app/commands/decidim/proposals/update_collaborative_draft.rb +1 -2
  31. data/app/commands/decidim/proposals/update_proposal.rb +1 -2
  32. data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +6 -2
  33. data/app/controllers/concerns/decidim/proposals/admin/needs_interpolations.rb +40 -0
  34. data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +50 -2
  35. data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +18 -0
  36. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +41 -85
  37. data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +2 -4
  38. data/app/controllers/decidim/proposals/invite_coauthors_controller.rb +87 -0
  39. data/app/controllers/decidim/proposals/proposals_controller.rb +11 -40
  40. data/app/events/decidim/proposals/accepted_coauthorship_event.rb +8 -0
  41. data/app/events/decidim/proposals/admin/proposal_assigned_to_valuator_event.rb +27 -0
  42. data/app/events/decidim/proposals/admin/proposal_note_created_event.rb +5 -0
  43. data/app/events/decidim/proposals/coauthor_accepted_invite_event.rb +49 -0
  44. data/app/events/decidim/proposals/coauthor_invited_event.rb +45 -0
  45. data/app/events/decidim/proposals/coauthor_rejected_invite_event.rb +8 -0
  46. data/app/events/decidim/proposals/rejected_coauthorship_event.rb +8 -0
  47. data/app/events/decidim/proposals/update_proposal_taxonomies_event.rb +9 -0
  48. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +4 -3
  49. data/app/forms/decidim/proposals/admin/proposal_base_form.rb +3 -31
  50. data/app/forms/decidim/proposals/admin/proposal_form.rb +12 -7
  51. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +6 -14
  52. data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +4 -1
  53. data/app/forms/decidim/proposals/collaborative_draft_form.rb +0 -8
  54. data/app/forms/decidim/proposals/proposal_form.rb +5 -32
  55. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +26 -1
  56. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +0 -1
  57. data/app/helpers/decidim/proposals/application_helper.rb +23 -14
  58. data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +7 -7
  59. data/app/helpers/decidim/proposals/map_helper.rb +0 -18
  60. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +15 -2
  61. data/app/jobs/decidim/proposals/admin/proposal_answer_job.rb +20 -0
  62. data/app/models/decidim/proposals/collaborative_draft.rb +10 -1
  63. data/app/models/decidim/proposals/proposal.rb +67 -10
  64. data/app/models/decidim/proposals/proposal_note.rb +11 -0
  65. data/app/models/decidim/proposals/proposal_state.rb +1 -1
  66. data/app/packs/entrypoints/decidim_proposals.js +1 -0
  67. data/app/packs/entrypoints/decidim_proposals_geocoding.js +2 -0
  68. data/app/packs/src/decidim/proposals/admin/proposals.js +16 -1
  69. data/app/packs/src/decidim/proposals/exit_handler.js +73 -0
  70. data/app/packs/stylesheets/decidim/proposals/proposals.scss +246 -5
  71. data/app/permissions/decidim/proposals/admin/permissions.rb +2 -5
  72. data/app/permissions/decidim/proposals/permissions.rb +46 -3
  73. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
  74. data/app/presenters/decidim/proposals/proposal_presenter.rb +1 -1
  75. data/app/queries/decidim/proposals/filtered_proposals.rb +2 -2
  76. data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +2 -2
  77. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +10 -10
  78. data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +4 -4
  79. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +6 -6
  80. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +6 -6
  81. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +6 -6
  82. data/app/services/decidim/proposals/collaborative_draft_diff_renderer.rb +22 -0
  83. data/app/services/decidim/proposals/diff_renderer.rb +2 -0
  84. data/app/services/decidim/proposals/proposal_builder.rb +2 -2
  85. data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +3 -3
  86. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_note.html.erb +28 -0
  87. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_note_reply.html.erb +9 -0
  88. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +4 -28
  89. data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +1 -1
  90. data/app/views/decidim/proposals/admin/proposals/_actions.html.erb +21 -0
  91. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +3 -2
  92. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +16 -23
  93. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +12 -28
  94. data/app/views/decidim/proposals/admin/proposals/_proposals-thead.html.erb +45 -0
  95. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_apply_answer_template.html.erb +22 -0
  96. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +15 -11
  97. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_taxonomy_change.html.erb +23 -0
  98. data/app/views/decidim/proposals/admin/proposals/index.html.erb +17 -48
  99. data/app/views/decidim/proposals/admin/proposals/manage_trash.html.erb +18 -0
  100. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
  101. data/app/views/decidim/proposals/admin/proposals/show.html.erb +10 -22
  102. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +1 -1
  103. data/app/views/decidim/proposals/admin/proposals_imports/new.html.erb +2 -5
  104. data/app/views/decidim/proposals/collaborative_drafts/_collaborative_actions.html.erb +9 -0
  105. data/app/views/decidim/proposals/collaborative_drafts/_collaborative_draft_aside.html.erb +0 -15
  106. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +4 -6
  107. data/app/views/decidim/proposals/collaborative_drafts/index.html.erb +6 -2
  108. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +27 -11
  109. data/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +29 -9
  110. data/app/views/decidim/proposals/proposals/_actions.html.erb +4 -7
  111. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +17 -22
  112. data/app/views/decidim/proposals/proposals/_exit_modal.html.erb +17 -0
  113. data/app/views/decidim/proposals/proposals/_notification_alert_box.html.erb +1 -0
  114. data/app/views/decidim/proposals/proposals/_proposal_actions.html.erb +19 -0
  115. data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +9 -32
  116. data/app/views/decidim/proposals/proposals/_proposal_voting_rules.html.erb +33 -0
  117. data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +2 -2
  118. data/app/views/decidim/proposals/proposals/_remaining_votes_notification.html.erb +12 -0
  119. data/app/views/decidim/proposals/proposals/_update_proposal_voting_rules.html.erb +6 -0
  120. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +12 -8
  121. data/app/views/decidim/proposals/proposals/_votes_count.html.erb +2 -1
  122. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +1 -7
  123. data/app/views/decidim/proposals/proposals/index.html.erb +10 -18
  124. data/app/views/decidim/proposals/proposals/index.js.erb +12 -0
  125. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +3 -1
  126. data/app/views/decidim/proposals/proposals/show.html.erb +36 -16
  127. data/config/locales/ar.yml +19 -75
  128. data/config/locales/bg.yml +7 -91
  129. data/config/locales/bn-BD.yml +1 -0
  130. data/config/locales/bs-BA.yml +87 -0
  131. data/config/locales/ca.yml +213 -73
  132. data/config/locales/cs.yml +229 -75
  133. data/config/locales/de.yml +217 -78
  134. data/config/locales/el.yml +9 -85
  135. data/config/locales/en.yml +209 -69
  136. data/config/locales/es-MX.yml +220 -80
  137. data/config/locales/es-PY.yml +215 -75
  138. data/config/locales/es.yml +222 -82
  139. data/config/locales/eu.yml +293 -147
  140. data/config/locales/fi-plain.yml +222 -81
  141. data/config/locales/fi.yml +239 -98
  142. data/config/locales/fr-CA.yml +119 -90
  143. data/config/locales/fr.yml +118 -89
  144. data/config/locales/ga-IE.yml +0 -19
  145. data/config/locales/gl.yml +9 -47
  146. data/config/locales/he-IL.yml +7 -0
  147. data/config/locales/hu.yml +7 -68
  148. data/config/locales/id-ID.yml +9 -36
  149. data/config/locales/is-IS.yml +0 -21
  150. data/config/locales/it.yml +10 -56
  151. data/config/locales/ja.yml +164 -91
  152. data/config/locales/lt.yml +9 -86
  153. data/config/locales/lv.yml +8 -47
  154. data/config/locales/nl.yml +7 -54
  155. data/config/locales/no.yml +9 -46
  156. data/config/locales/pl.yml +7 -91
  157. data/config/locales/pt-BR.yml +7 -77
  158. data/config/locales/pt.yml +9 -57
  159. data/config/locales/ro-RO.yml +17 -63
  160. data/config/locales/ru.yml +0 -25
  161. data/config/locales/sk.yml +9 -48
  162. data/config/locales/sl.yml +0 -4
  163. data/config/locales/sr-CS.yml +0 -14
  164. data/config/locales/sv.yml +132 -88
  165. data/config/locales/tr-TR.yml +9 -54
  166. data/config/locales/uk.yml +0 -25
  167. data/config/locales/zh-CN.yml +9 -54
  168. data/config/locales/zh-TW.yml +9 -87
  169. data/db/migrate/20171220084719_add_published_at_to_proposals.rb +1 -1
  170. data/db/migrate/20181016132225_add_organization_as_author.rb +1 -1
  171. data/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb +1 -1
  172. data/db/migrate/20200827154156_add_commentable_counter_cache_to_proposals.rb +3 -3
  173. data/db/migrate/20210310102839_add_followable_counter_cache_to_proposals.rb +1 -1
  174. data/db/migrate/20240110203504_create_default_proposal_states.rb +4 -3
  175. data/db/migrate/20240404202756_add_valuation_assignments_count_to_decidim_proposals_proposals.rb +1 -1
  176. data/db/migrate/20240617091140_add_email_on_assigned_proposals_to_users.rb +7 -0
  177. data/db/migrate/20240617170052_add_parent_relation_to_decidim_proposal_notes.rb +7 -0
  178. data/db/migrate/20240828103755_add_deleted_at_to_decidim_proposals_proposals.rb +8 -0
  179. data/decidim-proposals.gemspec +2 -2
  180. data/lib/decidim/api/functions/proposal_finder_helper.rb +12 -0
  181. data/lib/decidim/api/functions/proposal_list_helper.rb +12 -0
  182. data/lib/decidim/api/proposal_type.rb +30 -25
  183. data/lib/decidim/api/proposals_type.rb +5 -22
  184. data/lib/decidim/proposals/admin_engine.rb +12 -3
  185. data/lib/decidim/proposals/admin_filter.rb +3 -6
  186. data/lib/decidim/proposals/component.rb +4 -5
  187. data/lib/decidim/proposals/download_your_data_proposal_serializer.rb +15 -0
  188. data/lib/decidim/proposals/engine.rb +5 -0
  189. data/lib/decidim/proposals/import/proposal_creator.rb +4 -4
  190. data/lib/decidim/proposals/proposal_serializer.rb +15 -29
  191. data/lib/decidim/proposals/seeds.rb +21 -17
  192. data/lib/decidim/proposals/test/factories.rb +8 -6
  193. data/lib/decidim/proposals/version.rb +1 -1
  194. data/lib/decidim/proposals.rb +4 -0
  195. metadata +69 -30
  196. data/app/commands/decidim/proposals/admin/update_proposal_category.rb +0 -70
  197. data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +0 -75
  198. data/app/events/decidim/proposals/admin/update_proposal_category_event.rb +0 -11
  199. data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +0 -11
  200. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +0 -15
  201. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +0 -21
  202. data/app/views/decidim/proposals/collaborative_drafts/_actions.html.erb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2109029e1165afe1fe039f4f5e1d2f0b8af26d8e80bb2e0db0c11053da033e61
4
- data.tar.gz: 9e72ff8d19ba6f4442eed68fa4c5625e2d22367b3c00a8faf2b36fa58c888dbe
3
+ metadata.gz: df9005202c6078251b19d437ce9dad80b29d44bd6bec63b9d64e5fd89009f385
4
+ data.tar.gz: 02a8ec7149f40e004177f0fb8fc25e223f7c482f81c666ccedb8a9292d284f61
5
5
  SHA512:
6
- metadata.gz: 3d2b4e79ae0fb506248ca119acca6ce128649457f53b6047a9def36168110a63a36bccb6abde7fd8300194cd8bf785546d9d21d066e4f628da6742c2601fb8d6
7
- data.tar.gz: 3ad98b72afdc5b9b2601cffa7f5c872b5bf5202ed35f9bff830014af78d7b1de2c5aaae4b5ffc4e721f2d21594a8229bb96ef549f653a0caf36ae8f2a36b0cf9
6
+ metadata.gz: 274abec4fe629268688e921100ffde55cdf4b6184a21bb47c6101fab45a697741ffe77937848b04637f427af7a8e14b222e52f990fd2e516e458fffcf7375e77
7
+ data.tar.gz: 67248a8cbd8d13029e82780ad4a6e10780567c92dbb7e94da8d8c752cb6e549ca7602fd40b2ed9539cb68ede4e7ac130e2633b5bc364cd119d665f8f41d31999
@@ -11,21 +11,21 @@
11
11
  <% end %>
12
12
  </div>
13
13
 
14
+ <div class="flex items-center justify-between space-x-6">
15
+ <span class="content-block__span flex-shrink-0">
16
+ <%= t("decidim.participatory_spaces.highlighted_proposals.last") %>
17
+ </span>
18
+ <% if single_component? %>
19
+ <%= link_to decidim_proposals.new_proposal_path, class: "button button__xs md:button__lg button__secondary" do %>
20
+ <span class="text-center"><%= t("decidim.proposals.actions.new") %></span>
21
+ <%= icon "add-line" %>
22
+ <% end %>
23
+ <% end %>
24
+ </div>
14
25
  <div class="flex items-start justify-between">
15
26
  <div class="grow space-y-6">
16
- <span class="content-block__span">
17
- <%= t("decidim.participatory_spaces.highlighted_proposals.last") %>
18
- </span>
19
-
20
27
  <% proposals_to_render.each do |p| %>
21
- <%= card_for p, link_whole_card: true, title_tag: :h3, **options.slice(:show_space) %>
28
+ <%= card_for p, link_whole_card: true, hide_voting: true, title_tag: :h3, **options.slice(:show_space) %>
22
29
  <% end %>
23
30
  </div>
24
-
25
- <% if single_component? %>
26
- <%= link_to decidim_proposals.new_proposal_path, class: "button button__sm md:button__lg button__secondary" do %>
27
- <span><%= t("decidim.proposals.actions.new") %></span>
28
- <%= icon "add-line" %>
29
- <% end %>
30
- <% end %>
31
31
  </div>
@@ -49,7 +49,7 @@ module Decidim
49
49
  end
50
50
 
51
51
  def proposals_to_render
52
- @proposals_to_render ||= proposals.includes([:amendable, :category, :component, :scope]).limit(Decidim::Proposals.config.participatory_space_highlighted_proposals_limit)
52
+ @proposals_to_render ||= proposals.includes([:amendable, :component, :taxonomies]).limit(Decidim::Proposals.config.participatory_space_highlighted_proposals_limit)
53
53
  end
54
54
 
55
55
  def cache_hash
@@ -69,7 +69,7 @@ module Decidim
69
69
  end
70
70
 
71
71
  def amendment_creation_enabled?
72
- (current_component.settings.amendments_enabled? && current_settings.amendment_creation_enabled?)
72
+ current_component.settings.amendments_enabled? && current_settings.amendment_creation_enabled?
73
73
  end
74
74
 
75
75
  def amend_button_disabled?
@@ -1,3 +1,4 @@
1
+ <div class="card__proposals-item">
1
2
  <%= link_to resource_path, class: classes[:default], id: resource_id do %>
2
3
  <div class="<%= classes[:img] %>">
3
4
  <% if has_image? %>
@@ -21,3 +22,15 @@
21
22
  <% end %>
22
23
  </div>
23
24
  <% end %>
25
+ <% if has_actions? %>
26
+ <% if current_settings.votes_hidden? %>
27
+ <div class="card__proposals-votes-hidden">
28
+ <%= cell proposal_vote_cell, resource, **options %>
29
+ </div>
30
+ <% else %>
31
+ <div class="card__proposals-votes">
32
+ <%= cell proposal_vote_cell, resource, **options %>
33
+ </div>
34
+ <% end %>
35
+ <% end %>
36
+ </div>
@@ -24,6 +24,18 @@ module Decidim
24
24
  "decidim/proposals/proposal_metadata"
25
25
  end
26
26
 
27
+ def proposal_vote_cell
28
+ "decidim/proposals/proposal_vote"
29
+ end
30
+
31
+ def has_actions?
32
+ model.component.current_settings.votes_enabled? && !model.draft? && !model.withdrawn? && !model.rejected?
33
+ end
34
+
35
+ def proposal_votes_count
36
+ model.proposal_votes_count || 0
37
+ end
38
+
27
39
  def metadata_cell_instance
28
40
  @metadata_cell_instance ||= cell("decidim/proposals/proposal_metadata", model)
29
41
  end
@@ -34,6 +46,28 @@ module Decidim
34
46
 
35
47
  private
36
48
 
49
+ def cache_hash
50
+ @cache_hash ||= begin
51
+ hash = []
52
+ hash << I18n.locale.to_s
53
+ hash << self.class.name.demodulize.underscore
54
+ hash << model.cache_key_with_version
55
+ hash << model.proposal_votes_count
56
+ hash << options[:hide_voting] ? 1 : 0
57
+ hash << model.endorsements_count
58
+ hash << model.comments_count
59
+ hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
60
+ hash << Digest::MD5.hexdigest(resource_image_url) if resource_image_url
61
+ hash << 0 # render space
62
+ hash << model.follows_count
63
+ hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
64
+ hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
65
+ hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
66
+
67
+ hash.join(Decidim.cache_key_separator)
68
+ end
69
+ end
70
+
37
71
  def classes
38
72
  super.merge(metadata: "card__list-metadata")
39
73
  end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ # This cell is used to render the proposal history panel of a resource
6
+ # inside a tab of a show view
7
+ #
8
+ # The `model` must be a proposal resource to get the history from.
9
+ #
10
+ # Example:
11
+ #
12
+ # cell(
13
+ # "decidim/proposal_history",
14
+ # proposal
15
+ # )
16
+ class ProposalHistoryCell < Decidim::ResourceHistoryCell
17
+ include Decidim::Proposals::ApplicationHelper
18
+
19
+ private
20
+
21
+ def linked_resources_items
22
+ # linked resources generate from this proposal
23
+ [
24
+ {
25
+ resources: @model.linked_resources_from(:proposals, "copied_from_component"),
26
+ link_name: "copied_from_component",
27
+ text_key: "decidim.proposals.proposal.import_from_proposal_text",
28
+ icon_key: "Decidim::Proposals::Proposal"
29
+ },
30
+ {
31
+ resources: @model.linked_resources_from(:proposals, "splitted_from_component"),
32
+ link_name: "splitted_from_component",
33
+ text_key: "decidim.proposals.proposal.split_from_proposal_text",
34
+ icon_key: "Decidim::Proposals::Proposal"
35
+ },
36
+ {
37
+ resources: @model.linked_resources_from(:proposals, "merged_from_component"),
38
+ link_name: "merged_from_component",
39
+ text_key: "decidim.proposals.proposal.merge_from_proposal_text",
40
+ icon_key: "Decidim::Proposals::Proposal"
41
+ },
42
+ {
43
+
44
+ resources: @model.linked_resources(:projects, "included_proposals"),
45
+ link_name: "included_proposals",
46
+ text_key: "decidim.budgets.project.text",
47
+ icon_key: "Decidim::Budgets::Project"
48
+ },
49
+ {
50
+ resources: @model.linked_resources(:results, "included_proposals"),
51
+ link_name: "included_proposals",
52
+ text_key: "decidim.accountability.result.text",
53
+ icon_key: "Decidim::Accountability::Result"
54
+ },
55
+ {
56
+ resources: @model.linked_resources(:meetings, "proposals_from_meeting"),
57
+ link_name: "proposals_from_meeting",
58
+ text_key: "decidim.meetings.meeting.text",
59
+ icon_key: "Decidim::Meetings::Meeting"
60
+ },
61
+ {
62
+
63
+ # linked resource generate to this proposal
64
+ resources: @model.linked_resources_to(:proposals, "copied_from_component"),
65
+ link_name: "copied_to_component",
66
+ text_key: "decidim.proposals.proposal.import_to_proposal_text",
67
+ icon_key: "Decidim::Proposals::Proposal"
68
+ },
69
+ {
70
+ resources: @model.linked_resources_to(:proposals, "splitted_from_component"),
71
+ link_name: "splitted_to_component",
72
+ text_key: "decidim.proposals.proposal.split_to_proposal_text",
73
+ icon_key: "Decidim::Proposals::Proposal"
74
+ },
75
+ {
76
+ resources: @model.linked_resources_to(:proposals, "merged_from_component"),
77
+ link_name: "merged_to_component",
78
+ text_key: "decidim.proposals.proposal.merge_to_proposal_text",
79
+ icon_key: "Decidim::Proposals::Proposal"
80
+ }
81
+ ]
82
+ end
83
+
84
+ def creation_item
85
+ creation_text = if history_items_contains?(:merged_to_component)
86
+ t("decidim.proposals.creation.merged_text")
87
+ elsif history_items_contains?(:splitted_to_component)
88
+ t("decidim.proposals.creation.splitted_text")
89
+ elsif history_items_contains?(:copied_to_component)
90
+ t("decidim.proposals.creation.imported_text")
91
+ else
92
+ t("decidim.proposals.creation.text")
93
+ end
94
+ {
95
+ id: "proposal_creation",
96
+ date: @model.created_at,
97
+ text: creation_text,
98
+ icon: resource_type_icon_key("Decidim::Proposals::Proposal")
99
+ }
100
+ end
101
+
102
+ def history_cell_id
103
+ "proposal"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,37 @@
1
+ <% if has_link_to_resource? && link_whole_card? %>
2
+ <div class="card__proposals-item">
3
+ <%= link_to resource_path, class: item_list_class, id: resource_id, **html_options do %>
4
+ <%= render :content %>
5
+ <%= render :extra_data if render_extra_data? %>
6
+ <% end %>
7
+ <% if has_actions? && options.fetch(:hide_voting, false) == false %>
8
+ <% if current_settings.votes_hidden? %>
9
+ <div class="card__proposals-votes-hidden">
10
+ <%= cell proposal_vote_cell, resource, **options %>
11
+ </div>
12
+ <% else %>
13
+ <div class="card__proposals-votes">
14
+ <%= cell proposal_vote_cell, resource, **options %>
15
+ </div>
16
+ <% end %>
17
+ <% end %>
18
+ </div>
19
+ <% else %>
20
+ <%= content_tag :div, class: wrapper_class, id: resource_id do %>
21
+ <%= content_tag :div, class: item_list_class do %>
22
+ <%= render :content %>
23
+ <% end %>
24
+ <%= render :extra_data if render_extra_data? %>
25
+ <% if has_actions? %>
26
+ <% if current_settings.votes_hidden? %>
27
+ <div class="card__proposals-votes-hidden">
28
+ <%= cell proposal_vote_cell, resource, **options %>
29
+ </div>
30
+ <% else %>
31
+ <div class="card__proposals-votes">
32
+ <%= cell proposal_vote_cell, resource, **options %>
33
+ </div>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+ <% end %>
@@ -19,26 +19,34 @@ module Decidim
19
19
  "decidim/proposals/proposal_metadata"
20
20
  end
21
21
 
22
+ def has_actions?
23
+ model.component.current_settings.votes_enabled? && !model.draft? && !model.withdrawn? && !model.rejected?
24
+ end
25
+
26
+ def proposal_vote_cell
27
+ "decidim/proposals/proposal_vote"
28
+ end
29
+
22
30
  def cache_hash
23
- hash = []
24
- hash << I18n.locale.to_s
25
- hash << model.cache_key_with_version
26
- hash << model.proposal_votes_count
27
- hash << model.endorsements_count
28
- hash << model.comments_count
29
- hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
30
- hash << Digest::MD5.hexdigest(resource_image_url) if resource_image_url
31
- hash << render_space? ? 1 : 0
32
- if current_user
33
- hash << current_user.cache_key_with_version
34
- hash << current_user.follows?(model) ? 1 : 0
35
- end
36
- hash << model.follows_count
37
- hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
38
- hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
39
- hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
31
+ @cache_hash ||= begin
32
+ hash = []
33
+ hash << I18n.locale.to_s
34
+ hash << self.class.name.demodulize.underscore
35
+ hash << model.cache_key_with_version
36
+ hash << model.proposal_votes_count
37
+ hash << options[:hide_voting] ? 1 : 0
38
+ hash << model.endorsements_count
39
+ hash << model.comments_count
40
+ hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
41
+ hash << Digest::MD5.hexdigest(resource_image_url) if resource_image_url
42
+ hash << render_space? ? 1 : 0
43
+ hash << model.follows_count
44
+ hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
45
+ hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
46
+ hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
40
47
 
41
- hash.join(Decidim.cache_key_separator)
48
+ hash.join(Decidim.cache_key_separator)
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -44,10 +44,10 @@ module Decidim
44
44
  private
45
45
 
46
46
  def proposal_items
47
- [coauthors_item, comments_count_item, endorsements_count_item, state_item, emendation_item]
47
+ [coauthors_item] + taxonomy_items + [comments_count_item, endorsements_count_item, state_item, emendation_item]
48
48
  end
49
49
 
50
- def proposal_items_for_map
50
+ def items_for_map
51
51
  [coauthors_item_for_map, comments_count_item, endorsements_count_item, state_item, emendation_item].compact_blank.map do |item|
52
52
  {
53
53
  text: item[:text].to_s.html_safe,
@@ -0,0 +1,75 @@
1
+ <% if !current_settings.votes_hidden? && current_component.participatory_space.can_participate?(current_user) %>
2
+ <% if component_settings.participatory_texts_enabled? && from_proposals_list %>
3
+ <%= render partial: "decidim/proposals/proposals/participatory_texts/proposal_votes_count", locals: { proposal: resource, from_proposals_list: true } %>
4
+ <% else %>
5
+ <% progress ||= resource.proposal_votes_count || 0 %>
6
+ <% total ||= resource.maximum_votes || 0 %>
7
+ <%= cell(
8
+ "decidim/progress_bar",
9
+ progress,
10
+ total:,
11
+ units_name: "decidim.proposals.proposals.votes_count.count",
12
+ element_id: "proposal-#{resource.id}-votes-count",
13
+ class: total.positive? ? "card__proposals-votes-limited" : "card__proposals-votes-unlimited"
14
+ ) %>
15
+ <% end %>
16
+ <% end %>
17
+
18
+ <% button_classes = "button button__lg button__secondary w-full" %>
19
+
20
+ <% unless resource.rejected? || resource.withdrawn? %>
21
+ <% if component_settings.participatory_texts_enabled? && from_proposals_list %>
22
+ <%= render partial: "decidim/proposals/proposals/participatory_texts/proposal_vote_button", locals: { resource:, from_proposals_list: true } %>
23
+ <% else %>
24
+ <div id="proposal-<%= resource.id %>-vote-button" class="card__proposals-votes-container">
25
+ <% if !current_user %>
26
+ <% if current_settings.votes_blocked? %>
27
+ <%= action_authorized_button_to :vote, t("decidim.proposals.proposals.vote_button.votes_blocked"), proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , class: button_classes, disabled: true %>
28
+ <% else %>
29
+ <%= action_authorized_button_to :vote, proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , class: button_classes, data: { "proposal-vote-button": true, disable: true, "redirect-url": proposal_path(resource) } do %>
30
+ <%= t("decidim.proposals.proposals.vote_button.vote") %>
31
+ <span class="sr-only"><%= decidim_html_escape(present(resource).title) %></span>
32
+ <% end %>
33
+ <% end %>
34
+ <% else %>
35
+ <% if @voted_proposals ? @voted_proposals.include?(resource.id) : resource.voted_by?(current_user) %>
36
+ <%= action_authorized_button_to(
37
+ :vote,
38
+ proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:),
39
+ resource:,
40
+ method: :delete,
41
+ remote: true,
42
+ data: {
43
+ disable: true,
44
+ original: t("decidim.proposals.proposals.vote_button.already_voted"),
45
+ replace: t("decidim.proposals.proposals.vote_button.already_voted_hover"),
46
+ "redirect-url": proposal_path(resource)
47
+ },
48
+ disabled: current_settings.votes_blocked?,
49
+ class: "#{button_classes}",
50
+ id: "vote_button-#{resource.id}"
51
+ ) do %>
52
+ <%= t("decidim.proposals.proposals.vote_button.already_voted") %>
53
+ <%= icon "check-line", class: "already-voted-icon" %>
54
+ <span class="sr-only"><%= decidim_html_escape(present(resource).title) %></span>
55
+ <% end %>
56
+ <% else %>
57
+ <% if resource.maximum_votes_reached? && !resource.can_accumulate_votes_beyond_threshold && current_component.participatory_space.can_participate?(current_user) %>
58
+ <%= content_tag :button, t("decidim.proposals.proposals.vote_button.maximum_votes_reached"), class: button_classes, disabled: true %>
59
+ <% else %>
60
+ <% if vote_limit_enabled? && remaining_votes_count_for(current_user) == 0 %>
61
+ <%= content_tag :button, t("decidim.proposals.proposals.vote_button.no_votes_remaining"), class: button_classes, disabled: true %>
62
+ <% elsif current_settings.votes_blocked? || !current_component.participatory_space.can_participate?(current_user) %>
63
+ <%= content_tag :button, t("decidim.proposals.proposals.vote_button.votes_blocked"), class: button_classes, disabled: true %>
64
+ <% else %>
65
+ <%= action_authorized_button_to :vote, proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , remote: true, data: { disable: true, "redirect-url": proposal_path(resource) }, class: button_classes do %>
66
+ <%= t("decidim.proposals.proposals.vote_button.vote") %>
67
+ <span class="sr-only"><%= decidim_html_escape(present(resource).title) %></span>
68
+ <% end %>
69
+ <% end %>
70
+ <% end %>
71
+ <% end %>
72
+ <% end %>
73
+ </div>
74
+ <% end %>
75
+ <% end %>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This cell is used to display votes inside a card
5
+ module Proposals
6
+ class ProposalVoteCell < Decidim::ViewModel
7
+ include Decidim::DateRangeHelper
8
+ include Cell::ViewModel::Partial
9
+ include Decidim::ViewHooksHelper
10
+
11
+ alias resource model
12
+
13
+ def show
14
+ return if @items.blank?
15
+
16
+ render
17
+ end
18
+
19
+ def initialize(*)
20
+ super
21
+
22
+ @items ||= []
23
+ @items << votes_count_item
24
+ end
25
+
26
+ private
27
+
28
+ def from_proposals_list
29
+ true
30
+ end
31
+
32
+ def votes_count_item
33
+ return unless resource.respond_to?(:proposal_votes_count)
34
+
35
+ {
36
+ text: resource.proposal_votes_count,
37
+ icon: resource_type_icon_key(:like),
38
+ data_attributes: { votes_count: "" }
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ # A command with all the business logic when a user accepts an invitation to be a coauthor of a proposal.
6
+ class AcceptCoauthorship < Decidim::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # proposal - The proposal to add a coauthor to.
10
+ # coauthor - The user to invite as coauthor.
11
+ def initialize(proposal, coauthor)
12
+ @proposal = proposal
13
+ @coauthor = coauthor
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid, together with the proposal.
19
+ # - :invalid if the coauthor is not valid.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) unless @coauthor
24
+ return broadcast(:invalid) if @proposal.authors.include?(@coauthor)
25
+
26
+ begin
27
+ transaction do
28
+ @proposal.add_coauthor(@coauthor)
29
+ @proposal.coauthor_invitations_for(@coauthor).destroy_all
30
+ end
31
+
32
+ generate_notifications
33
+ rescue ActiveRecord::RecordInvalid
34
+ return broadcast(:invalid)
35
+ end
36
+
37
+ broadcast(:ok)
38
+ end
39
+
40
+ private
41
+
42
+ def generate_notifications
43
+ # notify the author that the co-author has accepted the invitation
44
+ Decidim::EventsManager.publish(
45
+ event: "decidim.events.proposals.coauthor_accepted_invite",
46
+ event_class: Decidim::Proposals::CoauthorAcceptedInviteEvent,
47
+ resource: @proposal,
48
+ affected_users: @proposal.authors.reject { |author| author == @coauthor },
49
+ extra: { coauthor_id: @coauthor.id }
50
+ )
51
+
52
+ # notify the co-author of the new co-authorship
53
+ Decidim::EventsManager.publish(
54
+ event: "decidim.events.proposals.accepted_coauthorship",
55
+ event_class: Decidim::Proposals::AcceptedCoauthorshipEvent,
56
+ resource: @proposal,
57
+ affected_users: [@coauthor]
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
@@ -37,6 +37,7 @@ module Decidim
37
37
  form.proposals.flat_map do |proposal|
38
38
  form.valuator_roles.each do |valuator_role|
39
39
  find_assignment(proposal, valuator_role) || assign_proposal(proposal, valuator_role)
40
+ notify_valuator(proposal, valuator_role)
40
41
  end
41
42
  end
42
43
  end
@@ -57,6 +58,19 @@ module Decidim
57
58
  valuator_role:
58
59
  )
59
60
  end
61
+
62
+ def notify_valuator(proposal, valuator_role)
63
+ return unless valuator_role.user.email_on_assigned_proposals?
64
+
65
+ data = {
66
+ event: "decidim.events.proposals.admin.proposal_assigned_to_valuator",
67
+ event_class: Decidim::Proposals::Admin::ProposalAssignedToValuatorEvent,
68
+ resource: proposal,
69
+ affected_users: [valuator_role.user]
70
+ }
71
+
72
+ Decidim::EventsManager.publish(**data)
73
+ end
60
74
  end
61
75
  end
62
76
  end
@@ -5,8 +5,7 @@ module Decidim
5
5
  module Admin
6
6
  # A command with all the business logic when a user creates a new proposal.
7
7
  class CreateProposal < Decidim::Command
8
- include ::Decidim::AttachmentMethods
9
- include GalleryMethods
8
+ include ::Decidim::MultipleAttachmentsMethods
10
9
  include HashtagsMethods
11
10
 
12
11
  # Public: Initializes the command.
@@ -26,19 +25,13 @@ module Decidim
26
25
  return broadcast(:invalid) if form.invalid?
27
26
 
28
27
  if process_attachments?
29
- build_attachment
30
- return broadcast(:invalid) if attachment_invalid?
31
- end
32
-
33
- if process_gallery?
34
- build_gallery
35
- return broadcast(:invalid) if gallery_invalid?
28
+ build_attachments
29
+ return broadcast(:invalid) if attachments_invalid?
36
30
  end
37
31
 
38
32
  transaction do
39
33
  create_proposal
40
- create_gallery if process_gallery?
41
- create_attachment(weight: first_attachment_weight) if process_attachments?
34
+ create_attachments(first_weight: first_attachment_weight) if process_attachments?
42
35
  link_author_meeting if form.created_in_meeting?
43
36
  end
44
37
 
@@ -49,7 +42,7 @@ module Decidim
49
42
 
50
43
  private
51
44
 
52
- attr_reader :form, :proposal, :attachment, :gallery
45
+ attr_reader :form, :proposal, :attachment
53
46
 
54
47
  def create_proposal
55
48
  @proposal = Decidim::Proposals::ProposalBuilder.create(
@@ -69,8 +62,7 @@ module Decidim
69
62
  {
70
63
  title: parsed_title,
71
64
  body: parsed_body,
72
- category: form.category,
73
- scope: form.scope,
65
+ taxonomizations: form.taxonomizations,
74
66
  component: form.component,
75
67
  address: form.address,
76
68
  latitude: form.latitude,