decidim-proposals 0.29.3 → 0.30.0.rc1

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +13 -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 +60 -7
  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 +46 -5
  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/admin/proposals_imports_controller.rb +2 -2
  38. data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +2 -4
  39. data/app/controllers/decidim/proposals/invite_coauthors_controller.rb +87 -0
  40. data/app/controllers/decidim/proposals/proposals_controller.rb +7 -32
  41. data/app/controllers/decidim/proposals/versions_controller.rb +1 -1
  42. data/app/events/decidim/proposals/accepted_coauthorship_event.rb +8 -0
  43. data/app/events/decidim/proposals/admin/proposal_assigned_to_valuator_event.rb +27 -0
  44. data/app/events/decidim/proposals/admin/proposal_note_created_event.rb +5 -0
  45. data/app/events/decidim/proposals/coauthor_accepted_invite_event.rb +49 -0
  46. data/app/events/decidim/proposals/coauthor_invited_event.rb +45 -0
  47. data/app/events/decidim/proposals/coauthor_rejected_invite_event.rb +8 -0
  48. data/app/events/decidim/proposals/rejected_coauthorship_event.rb +8 -0
  49. data/app/events/decidim/proposals/update_proposal_taxonomies_event.rb +9 -0
  50. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +16 -0
  51. data/app/forms/decidim/proposals/admin/proposal_base_form.rb +3 -31
  52. data/app/forms/decidim/proposals/admin/proposal_form.rb +11 -6
  53. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +0 -5
  54. data/app/forms/decidim/proposals/collaborative_draft_form.rb +0 -8
  55. data/app/forms/decidim/proposals/proposal_form.rb +5 -32
  56. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +25 -0
  57. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +0 -1
  58. data/app/helpers/decidim/proposals/application_helper.rb +23 -15
  59. data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +7 -7
  60. data/app/helpers/decidim/proposals/map_helper.rb +0 -18
  61. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +15 -2
  62. data/app/helpers/decidim/proposals/proposals_helper.rb +3 -1
  63. data/app/jobs/decidim/proposals/admin/proposal_answer_job.rb +20 -0
  64. data/app/models/decidim/proposals/collaborative_draft.rb +13 -3
  65. data/app/models/decidim/proposals/proposal.rb +71 -5
  66. data/app/models/decidim/proposals/proposal_note.rb +11 -0
  67. data/app/models/decidim/proposals/proposal_state.rb +1 -1
  68. data/app/packs/entrypoints/decidim_proposals.js +1 -0
  69. data/app/packs/entrypoints/decidim_proposals_geocoding.js +2 -0
  70. data/app/packs/src/decidim/proposals/admin/proposals.js +16 -1
  71. data/app/packs/src/decidim/proposals/exit_handler.js +73 -0
  72. data/app/packs/stylesheets/decidim/proposals/proposals.scss +248 -3
  73. data/app/permissions/decidim/proposals/admin/permissions.rb +2 -5
  74. data/app/permissions/decidim/proposals/permissions.rb +42 -0
  75. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
  76. data/app/presenters/decidim/proposals/proposal_presenter.rb +1 -1
  77. data/app/queries/decidim/proposals/filtered_proposals.rb +2 -2
  78. data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +2 -2
  79. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +10 -10
  80. data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +4 -4
  81. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +6 -6
  82. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +6 -6
  83. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +6 -6
  84. data/app/services/decidim/proposals/proposal_builder.rb +1 -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 +17 -24
  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 +14 -26
  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 +0 -3
  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/_proposals.html.erb +1 -1
  118. data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +2 -2
  119. data/app/views/decidim/proposals/proposals/_remaining_votes_notification.html.erb +12 -0
  120. data/app/views/decidim/proposals/proposals/_update_proposal_voting_rules.html.erb +6 -0
  121. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +12 -8
  122. data/app/views/decidim/proposals/proposals/_votes_count.html.erb +2 -1
  123. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +1 -7
  124. data/app/views/decidim/proposals/proposals/index.html.erb +3 -18
  125. data/app/views/decidim/proposals/proposals/index.js.erb +1 -1
  126. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +3 -1
  127. data/app/views/decidim/proposals/proposals/show.html.erb +35 -15
  128. data/config/locales/ar.yml +15 -73
  129. data/config/locales/bg.yml +12 -89
  130. data/config/locales/bs-BA.yml +2 -13
  131. data/config/locales/ca.yml +209 -84
  132. data/config/locales/cs.yml +210 -81
  133. data/config/locales/de.yml +213 -89
  134. data/config/locales/el.yml +12 -84
  135. data/config/locales/en.yml +206 -81
  136. data/config/locales/es-MX.yml +213 -88
  137. data/config/locales/es-PY.yml +213 -88
  138. data/config/locales/es.yml +214 -89
  139. data/config/locales/eu.yml +288 -164
  140. data/config/locales/fi-plain.yml +213 -87
  141. data/config/locales/fi.yml +213 -87
  142. data/config/locales/fr-CA.yml +116 -97
  143. data/config/locales/fr.yml +116 -97
  144. data/config/locales/ga-IE.yml +1 -21
  145. data/config/locales/gl.yml +8 -45
  146. data/config/locales/hu.yml +12 -68
  147. data/config/locales/id-ID.yml +9 -43
  148. data/config/locales/is-IS.yml +0 -19
  149. data/config/locales/it.yml +11 -77
  150. data/config/locales/ja.yml +165 -106
  151. data/config/locales/lt.yml +13 -85
  152. data/config/locales/lv.yml +10 -52
  153. data/config/locales/nl.yml +10 -59
  154. data/config/locales/no.yml +10 -44
  155. data/config/locales/pl.yml +11 -88
  156. data/config/locales/pt-BR.yml +9 -74
  157. data/config/locales/pt.yml +10 -56
  158. data/config/locales/ro-RO.yml +12 -72
  159. data/config/locales/ru.yml +0 -23
  160. data/config/locales/sk.yml +10 -52
  161. data/config/locales/sr-CS.yml +2 -14
  162. data/config/locales/sv.yml +127 -86
  163. data/config/locales/tr-TR.yml +10 -53
  164. data/config/locales/uk.yml +0 -23
  165. data/config/locales/zh-CN.yml +10 -53
  166. data/config/locales/zh-TW.yml +12 -86
  167. data/db/migrate/20171220084719_add_published_at_to_proposals.rb +1 -1
  168. data/db/migrate/20181016132225_add_organization_as_author.rb +1 -1
  169. data/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb +1 -1
  170. data/db/migrate/20200827154156_add_commentable_counter_cache_to_proposals.rb +3 -3
  171. data/db/migrate/20210310102839_add_followable_counter_cache_to_proposals.rb +1 -1
  172. data/db/migrate/20240110203504_create_default_proposal_states.rb +1 -1
  173. data/db/migrate/20240404202756_add_valuation_assignments_count_to_decidim_proposals_proposals.rb +1 -1
  174. data/db/migrate/20240617091140_add_email_on_assigned_proposals_to_users.rb +7 -0
  175. data/db/migrate/20240617170052_add_parent_relation_to_decidim_proposal_notes.rb +7 -0
  176. data/db/migrate/20240828103755_add_deleted_at_to_decidim_proposals_proposals.rb +8 -0
  177. data/decidim-proposals.gemspec +1 -1
  178. data/lib/decidim/api/functions/proposal_finder_helper.rb +12 -0
  179. data/lib/decidim/api/functions/proposal_list_helper.rb +12 -0
  180. data/lib/decidim/api/proposal_type.rb +17 -25
  181. data/lib/decidim/api/proposals_type.rb +4 -19
  182. data/lib/decidim/proposals/admin_engine.rb +12 -3
  183. data/lib/decidim/proposals/admin_filter.rb +3 -6
  184. data/lib/decidim/proposals/component.rb +4 -5
  185. data/lib/decidim/proposals/download_your_data_proposal_serializer.rb +15 -0
  186. data/lib/decidim/proposals/engine.rb +5 -0
  187. data/lib/decidim/proposals/import/proposal_creator.rb +4 -4
  188. data/lib/decidim/proposals/proposal_serializer.rb +12 -29
  189. data/lib/decidim/proposals/seeds.rb +21 -17
  190. data/lib/decidim/proposals/test/factories.rb +2 -1
  191. data/lib/decidim/proposals/version.rb +1 -1
  192. data/lib/decidim/proposals.rb +4 -0
  193. data/lib/tasks/proposals/upgrade/decidim_proposals_upgrade_tasks.rake +0 -22
  194. metadata +65 -34
  195. data/app/commands/decidim/proposals/admin/update_proposal_category.rb +0 -70
  196. data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +0 -75
  197. data/app/events/decidim/proposals/admin/update_proposal_category_event.rb +0 -11
  198. data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +0 -11
  199. data/app/jobs/decidim/proposals/admin/import_proposals_job.rb +0 -91
  200. data/app/mailers/decidim/proposals/admin/import_proposals_mailer.rb +0 -30
  201. data/app/views/decidim/proposals/admin/import_proposals_mailer/notify_failure.html.erb +0 -1
  202. data/app/views/decidim/proposals/admin/import_proposals_mailer/notify_success.html.erb +0 -2
  203. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +0 -15
  204. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +0 -21
  205. data/app/views/decidim/proposals/collaborative_drafts/_actions.html.erb +0 -7
  206. data/config/locales/ca-IT.yml +0 -945
@@ -6,7 +6,7 @@ module Decidim
6
6
  # This controller allows admins to answer proposals in a participatory process.
7
7
  class ProposalAnswersController < Admin::ApplicationController
8
8
  include ActionView::Helpers::SanitizeHelper
9
- include Decidim::Proposals::Admin::Filterable
9
+ include Decidim::Proposals::Admin::NeedsInterpolations
10
10
 
11
11
  helper_method :proposal
12
12
 
@@ -17,13 +17,13 @@ module Decidim
17
17
 
18
18
  def edit
19
19
  enforce_permission_to(:create, :proposal_answer, proposal:)
20
- @form = form(Admin::ProposalAnswerForm).from_model(proposal)
20
+ @form = form(ProposalAnswerForm).from_model(proposal)
21
21
  end
22
22
 
23
23
  def update
24
24
  enforce_permission_to(:create, :proposal_answer, proposal:)
25
25
  @notes_form = form(ProposalNoteForm).instance
26
- @answer_form = form(Admin::ProposalAnswerForm).from_params(params)
26
+ @answer_form = form(ProposalAnswerForm).from_params(params)
27
27
 
28
28
  Admin::AnswerProposal.call(@answer_form, proposal) do
29
29
  on(:ok) do
@@ -38,6 +38,33 @@ module Decidim
38
38
  end
39
39
  end
40
40
 
41
+ def update_multiple_answers
42
+ valid_proposals = []
43
+ failed_proposals = []
44
+ proposals.each do |proposal|
45
+ proposal_answer_form = answer_form(proposal)
46
+ if allowed_to?(:create, :proposal_answer, proposal:) && proposal_answer_form.valid? && !proposal.emendation?
47
+ valid_proposals << proposal.id
48
+ ProposalAnswerJob.perform_later(proposal, proposal_answer_form.attributes, { current_organization:, current_component:, current_user: })
49
+ else
50
+ failed_proposals << proposal.id
51
+ end
52
+ end
53
+
54
+ if failed_proposals.any?
55
+ flash[:alert] =
56
+ t("proposals.answer.bulk_answer_error", scope: "decidim.proposals.admin", template: strip_tags(translated_attribute(template&.name)),
57
+ proposals: failed_proposals.join(", "))
58
+ end
59
+ if valid_proposals.any?
60
+ flash[:notice] =
61
+ I18n.t("proposals.answer.bulk_answer_success", scope: "decidim.proposals.admin", template: strip_tags(translated_attribute(template&.name)),
62
+ count: valid_proposals.count)
63
+ end
64
+
65
+ redirect_to EngineRouter.admin_proxy(current_component).root_path
66
+ end
67
+
41
68
  private
42
69
 
43
70
  def skip_manage_component_permission
@@ -48,8 +75,22 @@ module Decidim
48
75
  @proposal ||= Proposal.where(component: current_component).find(params[:id])
49
76
  end
50
77
 
51
- def collection
52
- @collection ||= Proposal.where(component: current_component).not_hidden.published
78
+ def proposals
79
+ @proposals ||= Proposal.where(component: current_component).where(id: params[:proposal_ids])
80
+ end
81
+
82
+ def template
83
+ return unless Decidim.module_installed?(:templates)
84
+
85
+ @template ||= Decidim::Templates::Template.find_by(id: params[:template][:template_id])
86
+ end
87
+
88
+ def answer_form(proposal)
89
+ form(ProposalAnswerForm).from_params(answer: populate_interpolations(template&.description, proposal), internal_state: proposal_state&.token)
90
+ end
91
+
92
+ def proposal_state
93
+ @proposal_state ||= Decidim::Proposals::ProposalState.find_by(id: template&.field_values&.dig("proposal_state_id"))
53
94
  end
54
95
  end
55
96
  end
@@ -7,6 +7,24 @@ module Decidim
7
7
  class ProposalNotesController < Admin::ApplicationController
8
8
  helper_method :proposal
9
9
 
10
+ def reply
11
+ enforce_permission_to(:create, :proposal_note, proposal:)
12
+ parent_note = proposal.notes.find(params[:id])
13
+ @form = form(ProposalNoteForm).from_params(params)
14
+
15
+ ReplyProposalNote.call(@form, parent_note) do
16
+ on(:ok) do
17
+ flash[:notice] = I18n.t("proposal_notes.reply.success", scope: "decidim.proposals.admin")
18
+ redirect_to proposal_path(id: proposal.id)
19
+ end
20
+
21
+ on(:invalid) do
22
+ flash.keep[:alert] = I18n.t("proposal_notes.reply.error", scope: "decidim.proposals.admin")
23
+ redirect_to proposal_path(id: proposal.id)
24
+ end
25
+ end
26
+ end
27
+
10
28
  def create
11
29
  enforce_permission_to(:create, :proposal_note, proposal:)
12
30
  @form = form(ProposalNoteForm).from_params(params)
@@ -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
@@ -16,8 +16,8 @@ module Decidim
16
16
  @form = form(Admin::ProposalsImportForm).from_params(params)
17
17
 
18
18
  Admin::ImportProposals.call(@form) do
19
- on(:ok) do
20
- flash[:notice] = I18n.t("proposals_imports.create.success", scope: "decidim.proposals.admin")
19
+ on(:ok) do |proposals|
20
+ flash[:notice] = I18n.t("proposals_imports.create.success", scope: "decidim.proposals.admin", number: proposals.length)
21
21
  redirect_to EngineRouter.admin_proxy(current_component).root_path
22
22
  end
23
23
 
@@ -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
@@ -11,7 +11,7 @@ module Decidim
11
11
  def versioned_resource
12
12
  @versioned_resource ||=
13
13
  if params[:proposal_id]
14
- present(Proposal.not_hidden.published.where(component: current_component).find(params[:proposal_id]))
14
+ present(Proposal.where(component: current_component).find(params[:proposal_id]))
15
15
  else
16
16
  CollaborativeDraft.where(component: current_component).find(params[:collaborative_draft_id])
17
17
  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