decidim-proposals 0.29.2 → 0.30.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +1 -1
  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 +13 -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 +9 -0
  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 +5 -1
  33. data/app/controllers/concerns/decidim/proposals/admin/needs_interpolations.rb +40 -0
  34. data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +55 -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 +7 -32
  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 +1 -0
  49. data/app/forms/decidim/proposals/admin/proposal_base_form.rb +3 -31
  50. data/app/forms/decidim/proposals/admin/proposal_form.rb +11 -6
  51. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +0 -5
  52. data/app/forms/decidim/proposals/collaborative_draft_form.rb +0 -8
  53. data/app/forms/decidim/proposals/proposal_form.rb +5 -32
  54. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +25 -0
  55. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +0 -1
  56. data/app/helpers/decidim/proposals/application_helper.rb +24 -14
  57. data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +7 -7
  58. data/app/helpers/decidim/proposals/map_helper.rb +0 -18
  59. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +15 -2
  60. data/app/jobs/decidim/proposals/admin/proposal_answer_job.rb +20 -0
  61. data/app/models/decidim/proposals/collaborative_draft.rb +10 -1
  62. data/app/models/decidim/proposals/proposal.rb +66 -5
  63. data/app/models/decidim/proposals/proposal_note.rb +11 -0
  64. data/app/models/decidim/proposals/proposal_state.rb +1 -1
  65. data/app/packs/entrypoints/decidim_proposals.js +1 -0
  66. data/app/packs/entrypoints/decidim_proposals_geocoding.js +2 -0
  67. data/app/packs/src/decidim/proposals/admin/proposals.js +16 -1
  68. data/app/packs/src/decidim/proposals/exit_handler.js +73 -0
  69. data/app/packs/stylesheets/decidim/proposals/proposals.scss +248 -3
  70. data/app/permissions/decidim/proposals/admin/permissions.rb +2 -5
  71. data/app/permissions/decidim/proposals/permissions.rb +42 -0
  72. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
  73. data/app/presenters/decidim/proposals/proposal_presenter.rb +1 -1
  74. data/app/queries/decidim/proposals/filtered_proposals.rb +2 -2
  75. data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +2 -2
  76. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +10 -10
  77. data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +4 -4
  78. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +6 -6
  79. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +6 -6
  80. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +6 -6
  81. data/app/services/decidim/proposals/proposal_builder.rb +1 -1
  82. data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +3 -3
  83. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_note.html.erb +28 -0
  84. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_note_reply.html.erb +9 -0
  85. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +4 -28
  86. data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +1 -1
  87. data/app/views/decidim/proposals/admin/proposals/_actions.html.erb +21 -0
  88. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +3 -2
  89. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +17 -24
  90. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +12 -28
  91. data/app/views/decidim/proposals/admin/proposals/_proposals-thead.html.erb +45 -0
  92. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_apply_answer_template.html.erb +22 -0
  93. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +15 -11
  94. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_taxonomy_change.html.erb +23 -0
  95. data/app/views/decidim/proposals/admin/proposals/index.html.erb +17 -48
  96. data/app/views/decidim/proposals/admin/proposals/manage_trash.html.erb +18 -0
  97. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
  98. data/app/views/decidim/proposals/admin/proposals/show.html.erb +14 -26
  99. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +1 -1
  100. data/app/views/decidim/proposals/admin/proposals_imports/new.html.erb +0 -3
  101. data/app/views/decidim/proposals/collaborative_drafts/_collaborative_actions.html.erb +9 -0
  102. data/app/views/decidim/proposals/collaborative_drafts/_collaborative_draft_aside.html.erb +0 -15
  103. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +4 -6
  104. data/app/views/decidim/proposals/collaborative_drafts/index.html.erb +6 -2
  105. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +27 -11
  106. data/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +29 -9
  107. data/app/views/decidim/proposals/proposals/_actions.html.erb +4 -7
  108. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +17 -22
  109. data/app/views/decidim/proposals/proposals/_exit_modal.html.erb +17 -0
  110. data/app/views/decidim/proposals/proposals/_notification_alert_box.html.erb +1 -0
  111. data/app/views/decidim/proposals/proposals/_proposal_actions.html.erb +19 -0
  112. data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +9 -32
  113. data/app/views/decidim/proposals/proposals/_proposal_voting_rules.html.erb +33 -0
  114. data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +2 -2
  115. data/app/views/decidim/proposals/proposals/_remaining_votes_notification.html.erb +12 -0
  116. data/app/views/decidim/proposals/proposals/_update_proposal_voting_rules.html.erb +6 -0
  117. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +12 -8
  118. data/app/views/decidim/proposals/proposals/_votes_count.html.erb +2 -1
  119. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +1 -7
  120. data/app/views/decidim/proposals/proposals/index.html.erb +10 -18
  121. data/app/views/decidim/proposals/proposals/index.js.erb +1 -1
  122. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +3 -1
  123. data/app/views/decidim/proposals/proposals/show.html.erb +35 -15
  124. data/config/locales/ar.yml +18 -72
  125. data/config/locales/bg.yml +7 -89
  126. data/config/locales/bs-BA.yml +0 -13
  127. data/config/locales/ca.yml +212 -72
  128. data/config/locales/cs.yml +213 -73
  129. data/config/locales/de.yml +215 -75
  130. data/config/locales/el.yml +8 -82
  131. data/config/locales/en.yml +209 -69
  132. data/config/locales/es-MX.yml +213 -73
  133. data/config/locales/es-PY.yml +213 -73
  134. data/config/locales/es.yml +215 -75
  135. data/config/locales/eu.yml +217 -78
  136. data/config/locales/fi-plain.yml +216 -75
  137. data/config/locales/fi.yml +216 -75
  138. data/config/locales/fr-CA.yml +118 -87
  139. data/config/locales/fr.yml +118 -87
  140. data/config/locales/ga-IE.yml +0 -19
  141. data/config/locales/gl.yml +8 -43
  142. data/config/locales/hu.yml +6 -66
  143. data/config/locales/id-ID.yml +8 -40
  144. data/config/locales/is-IS.yml +0 -14
  145. data/config/locales/it.yml +8 -53
  146. data/config/locales/ja.yml +162 -87
  147. data/config/locales/lt.yml +8 -83
  148. data/config/locales/lv.yml +8 -50
  149. data/config/locales/nl.yml +6 -55
  150. data/config/locales/no.yml +8 -42
  151. data/config/locales/pl.yml +6 -88
  152. data/config/locales/pt-BR.yml +6 -74
  153. data/config/locales/pt.yml +8 -54
  154. data/config/locales/ro-RO.yml +10 -54
  155. data/config/locales/ru.yml +0 -18
  156. data/config/locales/sk.yml +8 -50
  157. data/config/locales/sr-CS.yml +0 -14
  158. data/config/locales/sv.yml +128 -85
  159. data/config/locales/tr-TR.yml +8 -51
  160. data/config/locales/uk.yml +0 -18
  161. data/config/locales/zh-CN.yml +8 -51
  162. data/config/locales/zh-TW.yml +8 -84
  163. data/db/migrate/20171220084719_add_published_at_to_proposals.rb +1 -1
  164. data/db/migrate/20181016132225_add_organization_as_author.rb +1 -1
  165. data/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb +1 -1
  166. data/db/migrate/20200827154156_add_commentable_counter_cache_to_proposals.rb +3 -3
  167. data/db/migrate/20210310102839_add_followable_counter_cache_to_proposals.rb +1 -1
  168. data/db/migrate/20240110203504_create_default_proposal_states.rb +1 -1
  169. data/db/migrate/20240404202756_add_valuation_assignments_count_to_decidim_proposals_proposals.rb +1 -1
  170. data/db/migrate/20240617091140_add_email_on_assigned_proposals_to_users.rb +7 -0
  171. data/db/migrate/20240617170052_add_parent_relation_to_decidim_proposal_notes.rb +7 -0
  172. data/db/migrate/20240828103755_add_deleted_at_to_decidim_proposals_proposals.rb +8 -0
  173. data/decidim-proposals.gemspec +1 -1
  174. data/lib/decidim/api/functions/proposal_finder_helper.rb +12 -0
  175. data/lib/decidim/api/functions/proposal_list_helper.rb +12 -0
  176. data/lib/decidim/api/proposal_type.rb +17 -25
  177. data/lib/decidim/api/proposals_type.rb +4 -19
  178. data/lib/decidim/proposals/admin_engine.rb +12 -3
  179. data/lib/decidim/proposals/admin_filter.rb +3 -6
  180. data/lib/decidim/proposals/component.rb +4 -5
  181. data/lib/decidim/proposals/download_your_data_proposal_serializer.rb +15 -0
  182. data/lib/decidim/proposals/engine.rb +5 -0
  183. data/lib/decidim/proposals/import/proposal_creator.rb +4 -4
  184. data/lib/decidim/proposals/proposal_serializer.rb +12 -29
  185. data/lib/decidim/proposals/seeds.rb +21 -17
  186. data/lib/decidim/proposals/test/factories.rb +2 -1
  187. data/lib/decidim/proposals/version.rb +1 -1
  188. data/lib/decidim/proposals.rb +4 -0
  189. metadata +65 -29
  190. data/app/commands/decidim/proposals/admin/update_proposal_category.rb +0 -70
  191. data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +0 -75
  192. data/app/events/decidim/proposals/admin/update_proposal_category_event.rb +0 -11
  193. data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +0 -11
  194. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +0 -15
  195. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +0 -21
  196. data/app/views/decidim/proposals/collaborative_drafts/_actions.html.erb +0 -7
@@ -6,7 +6,9 @@ module Decidim
6
6
  # This controller allows admins to manage proposals in a participatory process.
7
7
  class ProposalsController < Admin::ApplicationController
8
8
  include Decidim::ApplicationHelper
9
+ include Decidim::Admin::ComponentTaxonomiesHelper
9
10
  include Decidim::Proposals::Admin::Filterable
11
+ include Decidim::Admin::HasTrashableResources
10
12
 
11
13
  helper Proposals::ApplicationHelper
12
14
  helper Decidim::Proposals::Admin::ProposalRankingsHelper
@@ -47,33 +49,45 @@ module Decidim
47
49
  end
48
50
  end
49
51
 
50
- def update_category
51
- enforce_permission_to :update, :proposal_category
52
+ def update_taxonomies
53
+ enforce_permission_to :update, :proposal_taxonomy
52
54
 
53
- Admin::UpdateProposalCategory.call(params[:category][:id], proposal_ids) do
54
- on(:invalid_category) do
55
- flash.now[:error] = I18n.t(
56
- "proposals.update_category.select_a_category",
55
+ Admin::UpdateProposalTaxonomies.call(params[:taxonomies], proposal_ids, current_organization) do
56
+ on(:invalid_taxonomies) do
57
+ flash[:error] = I18n.t(
58
+ "proposals.update_taxonomies.select_a_taxonomy",
57
59
  scope: "decidim.proposals.admin"
58
60
  )
59
61
  end
60
62
 
61
- on(:invalid_proposal_ids) do
62
- flash.now[:alert] = I18n.t(
63
- "proposals.update_category.select_a_proposal",
63
+ on(:invalid_resources) do
64
+ flash[:alert] = I18n.t(
65
+ "proposals.update_taxonomies.select_a_proposal",
64
66
  scope: "decidim.proposals.admin"
65
67
  )
66
68
  end
67
69
 
68
- on(:update_proposals_category) do
69
- flash.now[:notice] = update_proposals_bulk_response_successful(@response, :category)
70
- flash.now[:alert] = update_proposals_bulk_response_errored(@response, :category)
70
+ on(:update_resources_taxonomies) do |response|
71
+ if response[:successful].any?
72
+ flash[:notice] = t(
73
+ "proposals.update_taxonomies.success",
74
+ taxonomies: response[:taxonomies].map { |taxonomy| decidim_escape_translated(taxonomy.name) }.to_sentence,
75
+ proposals: response[:successful].map { |resource| decidim_escape_translated(resource.title) }.to_sentence,
76
+ scope: "decidim.proposals.admin"
77
+ )
78
+ end
79
+ if response[:errored].any?
80
+ flash[:alert] = t(
81
+ "proposals.update_taxonomies.invalid",
82
+ taxonomies: response[:taxonomies].map { |taxonomy| decidim_escape_translated(taxonomy.name) }.to_sentence,
83
+ proposals: response[:errored].map { |resource| decidim_escape_translated(resource.title) }.to_sentence,
84
+ scope: "decidim.proposals.admin"
85
+ )
86
+ end
71
87
  end
72
88
  end
73
89
 
74
- respond_to do |format|
75
- format.js { render :update_attribute, locals: { form_selector: "#js-form-recategorize-projects", attribute_selector: "#category_id" } }
76
- end
90
+ redirect_to proposals_path
77
91
  end
78
92
 
79
93
  def publish_answers
@@ -97,34 +111,6 @@ module Decidim
97
111
  end
98
112
  end
99
113
 
100
- def update_scope
101
- enforce_permission_to :update, :proposal_scope
102
-
103
- Admin::UpdateProposalScope.call(params[:scope_id], proposal_ids) do
104
- on(:invalid_scope) do
105
- flash.now[:error] = t(
106
- "proposals.update_scope.select_a_scope",
107
- scope: "decidim.proposals.admin"
108
- )
109
- end
110
-
111
- on(:invalid_proposal_ids) do
112
- flash.now[:alert] = t(
113
- "proposals.update_scope.select_a_proposal",
114
- scope: "decidim.proposals.admin"
115
- )
116
- end
117
-
118
- on(:update_proposals_scope) do
119
- flash.now[:notice] = update_proposals_bulk_response_successful(@response, :scope)
120
- flash.now[:alert] = update_proposals_bulk_response_errored(@response, :scope)
121
- end
122
- end
123
- respond_to do |format|
124
- format.js { render :update_attribute, locals: { form_selector: "#js-form-scope-change-projects", attribute_selector: "#scope_id" } }
125
- end
126
- end
127
-
128
114
  def edit
129
115
  enforce_permission_to(:edit, :proposal, proposal:)
130
116
  @form = form(Admin::ProposalForm).from_model(proposal)
@@ -150,6 +136,18 @@ module Decidim
150
136
 
151
137
  private
152
138
 
139
+ def trashable_deleted_resource_type
140
+ :proposal
141
+ end
142
+
143
+ def trashable_deleted_resource
144
+ @trashable_deleted_resource ||= collection.with_deleted.find_by(id: params[:id])
145
+ end
146
+
147
+ def trashable_deleted_collection
148
+ @trashable_deleted_collection = filtered_collection.only_deleted.deleted_at_desc
149
+ end
150
+
153
151
  def collection
154
152
  @collection ||= Proposal.where(component: current_component).not_hidden.published
155
153
  end
@@ -166,48 +164,6 @@ module Decidim
166
164
  @proposal_ids ||= params[:proposal_ids]
167
165
  end
168
166
 
169
- def update_proposals_bulk_response_successful(response, subject)
170
- return if response[:successful].blank?
171
-
172
- case subject
173
- when :category
174
- t(
175
- "proposals.update_category.success",
176
- subject_name: response[:subject_name],
177
- proposals: response[:successful].to_sentence,
178
- scope: "decidim.proposals.admin"
179
- )
180
- when :scope
181
- t(
182
- "proposals.update_scope.success",
183
- subject_name: response[:subject_name],
184
- proposals: response[:successful].to_sentence,
185
- scope: "decidim.proposals.admin"
186
- )
187
- end
188
- end
189
-
190
- def update_proposals_bulk_response_errored(response, subject)
191
- return if response[:errored].blank?
192
-
193
- case subject
194
- when :category
195
- t(
196
- "proposals.update_category.invalid",
197
- subject_name: response[:subject_name],
198
- proposals: response[:errored].to_sentence,
199
- scope: "decidim.proposals.admin"
200
- )
201
- when :scope
202
- t(
203
- "proposals.update_scope.invalid",
204
- subject_name: response[:subject_name],
205
- proposals: response[:errored].to_sentence,
206
- scope: "decidim.proposals.admin"
207
- )
208
- end
209
- end
210
-
211
167
  def form_presenter
212
168
  @form_presenter ||= present(@form, presenter_class: Decidim::Proposals::ProposalPresenter)
213
169
  end
@@ -27,8 +27,7 @@ module Decidim
27
27
  @collaborative_drafts = search
28
28
  .result
29
29
  .not_hidden
30
- .includes(:category)
31
- .includes(:scope)
30
+ .includes(:taxonomies)
32
31
 
33
32
  @collaborative_drafts = reorder(@collaborative_drafts)
34
33
  @collaborative_drafts = paginate(@collaborative_drafts)
@@ -144,9 +143,8 @@ module Decidim
144
143
  def default_filter_params
145
144
  {
146
145
  search_text_cont: "",
147
- with_any_category: nil,
146
+ with_any_taxonomies: nil,
148
147
  with_any_state: %w(open),
149
- with_any_scope: nil,
150
148
  related_to: ""
151
149
  }
152
150
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class InviteCoauthorsController < Decidim::Proposals::ApplicationController
6
+ include Decidim::ControllerHelpers
7
+
8
+ helper_method :proposal
9
+
10
+ before_action :authenticate_user!
11
+
12
+ # author invites coauthor
13
+ def create
14
+ enforce_permission_to :invite, :proposal_coauthor_invites, { proposal:, coauthor: }
15
+
16
+ InviteCoauthor.call(proposal, coauthor) do
17
+ on(:ok) do
18
+ flash[:notice] = I18n.t("create.success", scope: "decidim.proposals.invite_coauthors", author_name: coauthor.name)
19
+ end
20
+
21
+ on(:invalid) do
22
+ flash[:alert] = I18n.t("create.error", scope: "decidim.proposals.invite_coauthors")
23
+ end
24
+ end
25
+
26
+ redirect_to Decidim::ResourceLocatorPresenter.new(proposal).path
27
+ end
28
+
29
+ # author cancels invitation
30
+ def cancel
31
+ enforce_permission_to :cancel, :proposal_coauthor_invites, { proposal:, coauthor: }
32
+
33
+ CancelCoauthorship.call(proposal, coauthor) do
34
+ on(:ok) do
35
+ flash[:notice] = I18n.t("cancel.success", scope: "decidim.proposals.invite_coauthors", author_name: coauthor.name)
36
+ end
37
+
38
+ on(:invalid) do
39
+ flash[:alert] = I18n.t("cancel.error", scope: "decidim.proposals.invite_coauthors")
40
+ end
41
+ end
42
+
43
+ redirect_to Decidim::ResourceLocatorPresenter.new(proposal).path
44
+ end
45
+
46
+ # coauthor accepts invitation
47
+ def update
48
+ enforce_permission_to :accept, :proposal_coauthor_invites, { proposal:, coauthor: }
49
+
50
+ AcceptCoauthorship.call(proposal, current_user) do
51
+ on(:ok) do
52
+ render json: { message: I18n.t("update.success", scope: "decidim.proposals.invite_coauthors") }
53
+ end
54
+
55
+ on(:invalid) do
56
+ render json: { message: I18n.t("update.error", scope: "decidim.proposals.invite_coauthors") }, status: :unprocessable_entity
57
+ end
58
+ end
59
+ end
60
+
61
+ # coauthor declines invitation
62
+ def destroy
63
+ enforce_permission_to :decline, :proposal_coauthor_invites, { proposal:, coauthor: }
64
+
65
+ RejectCoauthorship.call(proposal, current_user) do
66
+ on(:ok) do
67
+ render json: { message: I18n.t("destroy.success", scope: "decidim.proposals.invite_coauthors") }
68
+ end
69
+
70
+ on(:invalid) do
71
+ render json: { message: I18n.t("destroy.error", scope: "decidim.proposals.invite_coauthors") }, status: :unprocessable_entity
72
+ end
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def coauthor
79
+ @coauthor ||= Decidim::User.find(params[:id])
80
+ end
81
+
82
+ def proposal
83
+ @proposal ||= Proposal.where(component: current_component).find(params[:proposal_id])
84
+ end
85
+ end
86
+ end
87
+ end
@@ -39,7 +39,7 @@ module Decidim
39
39
  .published
40
40
  .not_hidden
41
41
  .only_amendables
42
- .includes(:category, :scope, :attachments, :coauthorships)
42
+ .includes(:taxonomies, :attachments, :coauthorships)
43
43
  .order(position: :asc)
44
44
  render "decidim/proposals/proposals/participatory_texts/participatory_text"
45
45
  else
@@ -202,9 +202,8 @@ module Decidim
202
202
  search_text_cont: "",
203
203
  with_any_origin: nil,
204
204
  activity: "all",
205
- with_any_category: nil,
205
+ with_any_taxonomies: nil,
206
206
  with_any_state: default_states,
207
- with_any_scope: nil,
208
207
  related_to: "",
209
208
  type: "all"
210
209
  }
@@ -282,36 +281,12 @@ module Decidim
282
281
  def tab_panel_items
283
282
  @tab_panel_items ||= [
284
283
  {
285
- enabled: @proposal.linked_resources(:projects, "included_proposals").present?,
286
- id: "included_projects",
287
- text: t("decidim/budgets/project", scope: "activerecord.models", count: 2),
288
- icon: resource_type_icon_key("Decidim::Budgets::Project"),
284
+ enabled: ProposalHistoryCell.new(@proposal).render?,
285
+ id: "included_history",
286
+ text: t("decidim.history", scope: "activerecord.models", count: 2),
287
+ icon: resource_type_icon_key("history"),
289
288
  method: :cell,
290
- args: ["decidim/linked_resources_for", @proposal, { type: :projects, link_name: "included_proposals" }]
291
- },
292
- {
293
- enabled: @proposal.linked_resources(:results, "included_proposals").present?,
294
- id: "included_results",
295
- text: t("decidim/accountability/result", scope: "activerecord.models", count: 2),
296
- icon: resource_type_icon_key("Decidim::Accountability::Result"),
297
- method: :cell,
298
- args: ["decidim/linked_resources_for", @proposal, { type: :results, link_name: "included_proposals" }]
299
- },
300
- {
301
- enabled: @proposal.linked_resources(:meetings, "proposals_from_meeting").present?,
302
- id: "included_meetings",
303
- text: t("decidim/meetings/meeting", scope: "activerecord.models", count: 2),
304
- icon: resource_type_icon_key("Decidim::Meetings::Meeting"),
305
- method: :cell,
306
- args: ["decidim/linked_resources_for", @proposal, { type: :meetings, link_name: "proposals_from_meeting" }]
307
- },
308
- {
309
- enabled: @proposal.linked_resources(:proposals, "copied_from_component").present?,
310
- id: "included_proposals",
311
- text: t("decidim/proposals/proposal", scope: "activerecord.models", count: 2),
312
- icon: resource_type_icon_key("Decidim::Proposals::Proposal"),
313
- method: :cell,
314
- args: ["decidim/linked_resources_for", @proposal, { type: :proposals, link_name: "copied_from_component" }]
289
+ args: ["decidim/proposals/proposal_history", @proposal]
315
290
  }
316
291
  ] + attachments_tab_panel_items(@proposal)
317
292
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class AcceptedCoauthorshipEvent < CoauthorAcceptedInviteEvent
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class ProposalAssignedToValuatorEvent < Decidim::Events::SimpleEvent
7
+ include Rails.application.routes.mounted_helpers
8
+
9
+ i18n_attributes :admin_proposal_info_url, :admin_proposal_info_path
10
+
11
+ def admin_proposal_info_path
12
+ ResourceLocatorPresenter.new(resource).show
13
+ end
14
+
15
+ def admin_proposal_info_url
16
+ send(resource.component.mounted_admin_engine).proposal_url(resource, resource.component.mounted_params)
17
+ end
18
+
19
+ private
20
+
21
+ def organization
22
+ @organization ||= component.participatory_space.organization
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -4,6 +4,7 @@ module Decidim
4
4
  module Proposals
5
5
  module Admin
6
6
  class ProposalNoteCreatedEvent < Decidim::Events::SimpleEvent
7
+ include Decidim::Events::AuthorEvent
7
8
  include Rails.application.routes.mounted_helpers
8
9
 
9
10
  i18n_attributes :admin_proposal_info_url, :admin_proposal_info_path
@@ -18,6 +19,10 @@ module Decidim
18
19
 
19
20
  private
20
21
 
22
+ def author
23
+ Decidim::User.find(extra[:note_author_id])
24
+ end
25
+
21
26
  def organization
22
27
  @organization ||= component.participatory_space.organization
23
28
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class CoauthorAcceptedInviteEvent < Decidim::Events::BaseEvent
6
+ include Decidim::Events::NotificationEvent
7
+ include Decidim::Core::Engine.routes.url_helpers
8
+
9
+ def notification_title
10
+ I18n.t("notification_title", **i18n_options).html_safe
11
+ end
12
+
13
+ delegate :name, to: :author, prefix: true
14
+ delegate :name, to: :coauthor, prefix: true, allow_nil: true
15
+
16
+ def author_path
17
+ profile_path(author.nickname)
18
+ end
19
+
20
+ def coauthor_path
21
+ profile_path(coauthor.nickname) if coauthor
22
+ end
23
+
24
+ def author
25
+ resource.creator_author
26
+ end
27
+
28
+ def coauthor
29
+ @coauthor ||= Decidim::User.find_by(id: extra["coauthor_id"])
30
+ end
31
+
32
+ def i18n_scope
33
+ event_name
34
+ end
35
+
36
+ def i18n_options
37
+ {
38
+ scope: i18n_scope,
39
+ coauthor_name:,
40
+ coauthor_path:,
41
+ author_name:,
42
+ author_path:,
43
+ resource_path:,
44
+ resource_title:
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class CoauthorInvitedEvent < Decidim::Events::SimpleEvent
6
+ include Decidim::Events::CoauthorEvent
7
+ include Decidim::Core::Engine.routes.url_helpers
8
+
9
+ def action_cell
10
+ "decidim/notification_actions/buttons" unless user_is_coauthor?
11
+ end
12
+
13
+ def action_data
14
+ [
15
+ {
16
+ i18n_label: "decidim.events.proposals.coauthor_invited.actions.accept",
17
+ url: invite_path,
18
+ icon: "check-line",
19
+ method: "patch"
20
+ },
21
+ {
22
+ i18n_label: "decidim.events.proposals.coauthor_invited.actions.decline",
23
+ url: invite_path,
24
+ icon: "close-circle-line",
25
+ method: "delete"
26
+ }
27
+ ]
28
+ end
29
+
30
+ def resource_url
31
+ notifications_url(host: component.organization.host)
32
+ end
33
+
34
+ private
35
+
36
+ def invite_path
37
+ @invite_path ||= EngineRouter.main_proxy(component).proposal_invite_coauthor_path(proposal_id: resource, id: user.id)
38
+ end
39
+
40
+ def user_is_coauthor?
41
+ resource.authors.include?(user)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class CoauthorRejectedInviteEvent < CoauthorAcceptedInviteEvent
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class RejectedCoauthorshipEvent < AcceptedCoauthorshipEvent
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class UpdateProposalTaxonomiesEvent < Decidim::Events::SimpleEvent
6
+ include Decidim::Events::AuthorEvent
7
+ end
8
+ end
9
+ end
@@ -6,6 +6,7 @@ module Decidim
6
6
  # A form object to be used when admin users want to answer a proposal.
7
7
  class ProposalAnswerForm < Decidim::Form
8
8
  include TranslatableAttributes
9
+
9
10
  mimic :proposal_answer
10
11
 
11
12
  translatable_attribute :answer, Decidim::Attributes::RichText
@@ -8,14 +8,13 @@ module Decidim
8
8
  include Decidim::TranslatableAttributes
9
9
  include Decidim::AttachmentAttributes
10
10
  include Decidim::ApplicationHelper
11
+ include Decidim::HasTaxonomyFormAttributes
11
12
 
12
13
  mimic :proposal
13
14
 
14
15
  attribute :address, String
15
16
  attribute :latitude, Float
16
17
  attribute :longitude, Float
17
- attribute :category_id, Integer
18
- attribute :scope_id, Integer
19
18
  attribute :attachment, AttachmentForm
20
19
  attribute :position, Integer
21
20
  attribute :created_in_meeting, Boolean
@@ -25,46 +24,19 @@ module Decidim
25
24
  attachments_attribute :photos
26
25
 
27
26
  validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? }
28
- validates :category, presence: true, if: ->(form) { form.category_id.present? }
29
- validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
30
- validates :scope_id, scope_belongs_to_component: true, if: ->(form) { form.scope_id.present? }
31
27
  validates :meeting_as_author, presence: true, if: ->(form) { form.created_in_meeting? }
32
28
 
33
29
  validate :notify_missing_attachment_if_errored
34
30
 
35
- delegate :categories, to: :current_component
36
-
37
31
  def map_model(model)
38
32
  body = translated_attribute(model.body)
39
33
  @suggested_hashtags = Decidim::ContentRenderers::HashtagRenderer.new(body).extra_hashtags.map(&:name).map(&:downcase)
40
-
41
- return unless model.categorization
42
-
43
- self.category_id = model.categorization.decidim_category_id
44
- self.scope_id = model.decidim_scope_id
45
34
  end
46
35
 
47
36
  alias component current_component
48
37
 
49
- # Finds the Category from the category_id.
50
- #
51
- # Returns a Decidim::Category
52
- def category
53
- @category ||= categories.find_by(id: category_id)
54
- end
55
-
56
- # Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
57
- #
58
- # Returns a Decidim::Scope
59
- def scope
60
- @scope ||= @attributes["scope_id"].value ? current_component.scopes.find_by(id: @attributes["scope_id"].value) : current_component.scope
61
- end
62
-
63
- # Scope identifier
64
- #
65
- # Returns the scope identifier related to the proposal
66
- def scope_id
67
- super || scope&.id
38
+ def participatory_space_manifest
39
+ @participatory_space_manifest ||= current_component.participatory_space.manifest.name
68
40
  end
69
41
 
70
42
  def geocoding_enabled?
@@ -6,27 +6,32 @@ module Decidim
6
6
  # A form object to be used when admin users want to create a proposal.
7
7
  class ProposalForm < Decidim::Proposals::Admin::ProposalBaseForm
8
8
  include Decidim::HasUploadValidations
9
+ include Decidim::AttachmentAttributes
9
10
 
10
11
  translatable_attribute :title, String do |field, _locale|
11
12
  validates field, length: { in: 15..150 }, if: proc { |resource| resource.send(field).present? }
12
13
  end
13
14
  translatable_attribute :body, Decidim::Attributes::RichText
15
+ attribute :attachment, AttachmentForm
16
+
17
+ attachments_attribute :documents
14
18
 
15
19
  validates :title, :body, translatable_presence: true
20
+ validates :title, :body, translated_etiquette: true
16
21
 
17
22
  validate :notify_missing_attachment_if_errored
18
23
 
19
24
  def map_model(model)
20
- super(model)
25
+ super
21
26
  presenter = ProposalPresenter.new(model)
22
27
 
23
28
  self.title = presenter.title(all_locales: title.is_a?(Hash))
24
29
  self.body = presenter.editor_body(all_locales: body.is_a?(Hash))
25
- self.attachment = if model.documents.first.present?
26
- { file: model.documents.first.file, title: translated_attribute(model.documents.first.title) }
27
- else
28
- {}
29
- end
30
+ self.documents = model.attachments
31
+ end
32
+
33
+ def notify_missing_attachment_if_errored
34
+ errors.add(:add_documents, :needs_to_be_reattached) if errors.any? && add_documents.present?
30
35
  end
31
36
  end
32
37
  end
@@ -14,7 +14,6 @@ module Decidim
14
14
  attribute :keep_answers, Boolean
15
15
  attribute :keep_authors, Boolean
16
16
  attribute :states, Array
17
- attribute :scope_ids, Array
18
17
 
19
18
  validates :origin_component_id, :origin_component, :states, :current_component, presence: true
20
19
  validates :import_proposals, allow_nil: false, acceptance: true
@@ -31,10 +30,6 @@ module Decidim
31
30
  super.compact_blank
32
31
  end
33
32
 
34
- def scopes
35
- Decidim::Scope.where(organization: current_organization, id: scope_ids)
36
- end
37
-
38
33
  def origin_component
39
34
  @origin_component ||= origin_components.find_by(id: origin_component_id)
40
35
  end
@@ -4,14 +4,6 @@ module Decidim
4
4
  module Proposals
5
5
  # A form object to be used when public users want to create a Collaborative Draft.
6
6
  class CollaborativeDraftForm < Decidim::Proposals::ProposalForm
7
- def map_model(model)
8
- super
9
-
10
- return unless model.categorization
11
-
12
- self.category_id = model.categorization.decidim_category_id
13
- end
14
-
15
7
  def user_group
16
8
  @user_group ||= Decidim::UserGroup.find user_group_id if user_group_id.present?
17
9
  end