decidim-proposals 0.28.5 → 0.29.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 (179) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -18
  3. data/app/cells/decidim/proposals/cost_report_cell.rb +0 -3
  4. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +11 -11
  5. data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
  6. data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +1 -1
  7. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +2 -3
  8. data/app/cells/decidim/proposals/proposal_cell.rb +2 -0
  9. data/app/cells/decidim/proposals/proposal_g/show.erb +23 -0
  10. data/app/cells/decidim/proposals/proposal_g_cell.rb +48 -0
  11. data/app/cells/decidim/proposals/proposal_l_cell.rb +18 -19
  12. data/app/cells/decidim/proposals/proposal_metadata_cell.rb +23 -15
  13. data/app/commands/decidim/proposals/admin/answer_proposal.rb +2 -1
  14. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +7 -5
  15. data/app/commands/decidim/proposals/admin/create_proposal.rb +4 -6
  16. data/app/commands/decidim/proposals/admin/create_proposal_state.rb +15 -0
  17. data/app/commands/decidim/proposals/admin/destroy_proposal_state.rb +10 -0
  18. data/app/commands/decidim/proposals/admin/import_proposals.rb +10 -2
  19. data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +4 -21
  20. data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +6 -4
  21. data/app/commands/decidim/proposals/admin/update_proposal_state.rb +13 -0
  22. data/app/commands/decidim/proposals/create_proposal.rb +21 -2
  23. data/app/commands/decidim/proposals/update_proposal.rb +2 -2
  24. data/app/commands/decidim/proposals/vote_proposal.rb +1 -1
  25. data/app/commands/decidim/proposals/withdraw_proposal.rb +3 -7
  26. data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +10 -22
  27. data/app/controllers/decidim/proposals/admin/proposal_states_controller.rb +86 -0
  28. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +4 -0
  29. data/app/controllers/decidim/proposals/admin/valuation_assignments_controller.rb +8 -11
  30. data/app/controllers/decidim/proposals/proposals_controller.rb +38 -39
  31. data/app/events/decidim/proposals/proposal_state_changed_event.rb +37 -0
  32. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +8 -4
  33. data/app/forms/decidim/proposals/admin/proposal_form.rb +1 -1
  34. data/app/forms/decidim/proposals/admin/proposal_state_form.rb +22 -0
  35. data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +1 -1
  36. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +1 -1
  37. data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +12 -14
  38. data/app/forms/decidim/proposals/proposal_form.rb +25 -4
  39. data/app/forms/decidim/proposals/reject_access_to_collaborative_draft_form.rb +1 -1
  40. data/app/forms/decidim/proposals/request_access_to_collaborative_draft_form.rb +1 -1
  41. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +7 -17
  42. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +13 -89
  43. data/app/helpers/decidim/proposals/application_helper.rb +16 -10
  44. data/app/helpers/decidim/proposals/map_helper.rb +1 -1
  45. data/app/helpers/decidim/proposals/proposal_cells_helper.rb +6 -2
  46. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +3 -3
  47. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +5 -8
  48. data/app/helpers/decidim/proposals/proposals_helper.rb +18 -24
  49. data/app/models/decidim/proposals/proposal.rb +83 -29
  50. data/app/models/decidim/proposals/proposal_state.rb +58 -0
  51. data/app/packs/documents/decidim/proposals/participatory_texts/participatory_text.md +1 -3
  52. data/app/packs/images/decidim/proposals/proposal-placeholder-card-g.svg +15 -0
  53. data/app/packs/src/decidim/proposals/add_proposal.js +2 -0
  54. data/app/packs/src/decidim/proposals/admin/proposals.js +43 -8
  55. data/app/packs/stylesheets/decidim/proposals/proposals.scss +39 -1
  56. data/app/permissions/decidim/proposals/admin/permissions.rb +16 -4
  57. data/app/permissions/decidim/proposals/permissions.rb +3 -4
  58. data/app/presenters/decidim/proposals/admin_log/proposal_state_presenter.rb +21 -0
  59. data/app/presenters/decidim/proposals/proposal_presenter.rb +12 -3
  60. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +1 -1
  61. data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +1 -1
  62. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +4 -4
  63. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +1 -1
  64. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +1 -1
  65. data/app/services/decidim/proposals/diff_renderer.rb +1 -3
  66. data/app/services/decidim/proposals/proposal_builder.rb +1 -1
  67. data/app/views/decidim/proposals/admin/imports/_proposals_fields.html.erb +1 -1
  68. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +3 -2
  69. data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +2 -2
  70. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +3 -3
  71. data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +67 -0
  72. data/app/views/decidim/proposals/admin/proposal_states/edit.html.erb +18 -0
  73. data/app/views/decidim/proposals/admin/proposal_states/index.html.erb +50 -0
  74. data/app/views/decidim/proposals/admin/proposal_states/new.html.erb +18 -0
  75. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +6 -12
  76. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +6 -6
  77. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +2 -2
  78. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +11 -7
  79. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +7 -5
  80. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_merge.html.erb +2 -2
  81. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +2 -2
  82. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +2 -2
  83. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +2 -2
  84. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_split.html.erb +2 -2
  85. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +11 -7
  86. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_valuators_picker.html.erb +12 -0
  87. data/app/views/decidim/proposals/admin/proposals/index.html.erb +11 -7
  88. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
  89. data/app/views/decidim/proposals/admin/proposals/show.html.erb +3 -2
  90. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +3 -3
  91. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +12 -5
  92. data/app/views/decidim/proposals/proposals/_proposal.html.erb +1 -1
  93. data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +1 -1
  94. data/app/views/decidim/proposals/proposals/_proposals.html.erb +9 -3
  95. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +1 -1
  96. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +3 -3
  97. data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +0 -1
  98. data/app/views/decidim/proposals/proposals/index.html.erb +1 -1
  99. data/app/views/decidim/proposals/proposals/index.js.erb +0 -12
  100. data/app/views/decidim/proposals/proposals/new.html.erb +2 -7
  101. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +4 -4
  102. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_votes_count.html.erb +8 -8
  103. data/app/views/decidim/proposals/proposals/preview.html.erb +1 -1
  104. data/app/views/decidim/proposals/proposals/show.html.erb +3 -3
  105. data/config/locales/ar.yml +9 -115
  106. data/config/locales/bg.yml +117 -111
  107. data/config/locales/ca.yml +86 -83
  108. data/config/locales/cs.yml +65 -127
  109. data/config/locales/de.yml +128 -125
  110. data/config/locales/el.yml +8 -121
  111. data/config/locales/en.yml +110 -107
  112. data/config/locales/es-MX.yml +85 -82
  113. data/config/locales/es-PY.yml +88 -85
  114. data/config/locales/es.yml +83 -80
  115. data/config/locales/eu.yml +166 -170
  116. data/config/locales/fi-plain.yml +90 -87
  117. data/config/locales/fi.yml +139 -136
  118. data/config/locales/fr-CA.yml +92 -87
  119. data/config/locales/fr.yml +83 -78
  120. data/config/locales/ga-IE.yml +1 -27
  121. data/config/locales/gl.yml +11 -107
  122. data/config/locales/he-IL.yml +0 -17
  123. data/config/locales/hu.yml +18 -90
  124. data/config/locales/id-ID.yml +6 -106
  125. data/config/locales/is-IS.yml +8 -33
  126. data/config/locales/it.yml +14 -103
  127. data/config/locales/ja.yml +117 -112
  128. data/config/locales/lb.yml +1 -0
  129. data/config/locales/lt.yml +6 -123
  130. data/config/locales/lv.yml +4 -101
  131. data/config/locales/nl.yml +11 -102
  132. data/config/locales/no.yml +7 -108
  133. data/config/locales/pl.yml +110 -115
  134. data/config/locales/pt-BR.yml +10 -84
  135. data/config/locales/pt.yml +9 -109
  136. data/config/locales/ro-RO.yml +15 -117
  137. data/config/locales/ru.yml +9 -53
  138. data/config/locales/sk.yml +5 -109
  139. data/config/locales/sl.yml +4 -0
  140. data/config/locales/sv.yml +142 -337
  141. data/config/locales/tr-TR.yml +64 -108
  142. data/config/locales/uk.yml +11 -55
  143. data/config/locales/zh-CN.yml +5 -100
  144. data/config/locales/zh-TW.yml +8 -119
  145. data/db/migrate/20240110203500_add_withdrawn_at_field_to_proposals.rb +27 -0
  146. data/db/migrate/20240110203501_create_decidim_proposals_proposal_state.rb +14 -0
  147. data/db/migrate/20240110203502_add_state_id_to_decidim_proposals_proposals.rb +13 -0
  148. data/db/migrate/20240110203503_remove_state_from_decidim_proposals_proposals.rb +11 -0
  149. data/db/migrate/20240110203504_create_default_proposal_states.rb +31 -0
  150. data/db/migrate/20240209092404_change_color_fields_on_proposals_states.rb +54 -0
  151. data/decidim-proposals.gemspec +3 -3
  152. data/lib/decidim/api/proposal_type.rb +4 -13
  153. data/lib/decidim/api/proposals_type.rb +3 -1
  154. data/lib/decidim/proposals/admin_engine.rb +8 -0
  155. data/lib/decidim/proposals/admin_filter.rb +37 -0
  156. data/lib/decidim/proposals/component.rb +8 -5
  157. data/lib/decidim/proposals/engine.rb +1 -15
  158. data/lib/decidim/proposals/import/proposal_answer_creator.rb +6 -6
  159. data/lib/decidim/proposals/import/proposal_creator.rb +1 -1
  160. data/lib/decidim/proposals/markdown_to_proposals.rb +2 -8
  161. data/lib/decidim/proposals/proposal_serializer.rb +8 -9
  162. data/lib/decidim/proposals/seeds.rb +60 -51
  163. data/lib/decidim/proposals/test/factories.rb +69 -14
  164. data/lib/decidim/proposals/version.rb +1 -1
  165. data/lib/decidim/proposals.rb +84 -12
  166. data/lib/tasks/proposals/upgrade/{decdim_proposals_upgrade_tasks.rake → decidim_proposals_upgrade_tasks.rake} +0 -13
  167. metadata +51 -40
  168. data/app/events/decidim/proposals/accepted_proposal_event.rb +0 -17
  169. data/app/events/decidim/proposals/evaluating_proposal_event.rb +0 -11
  170. data/app/events/decidim/proposals/rejected_proposal_event.rb +0 -17
  171. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +0 -44
  172. data/app/queries/decidim/proposals/similar_proposals.rb +0 -67
  173. data/app/services/decidim/proposals/collaborative_draft_diff_renderer.rb +0 -22
  174. data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +0 -0
  175. data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +0 -3
  176. data/app/views/decidim/proposals/proposals/compare.html.erb +0 -24
  177. data/app/views/decidim/proposals/proposals/complete.html.erb +0 -31
  178. data/config/locales/bn-BD.yml +0 -1
  179. data/config/locales/bs-BA.yml +0 -100
@@ -13,6 +13,8 @@ module Decidim
13
13
 
14
14
  private
15
15
 
16
+ delegate :filters, :dynamically_translated_filters, :filters_with_values, to: :filter_config
17
+
16
18
  # Comment about participatory_texts_enabled.
17
19
  def base_query
18
20
  return collection.order(:position) if current_component.settings.participatory_texts_enabled?
@@ -30,32 +32,18 @@ module Decidim
30
32
  :id_string_or_title_cont
31
33
  end
32
34
 
33
- def filters
34
- [
35
- :is_emendation_true,
36
- :with_any_state,
37
- :state_eq,
38
- :scope_id_eq,
39
- :category_id_eq,
40
- :valuator_role_ids_has
41
- ]
35
+ def filter_config
36
+ @filter_config ||= Decidim::AdminFilter.new(:proposals).build_for(self)
42
37
  end
43
38
 
44
- def filters_with_values
45
- {
46
- is_emendation_true: %w(true false),
47
- state_eq: Proposal::STATES,
48
- with_any_state: %w(state_published state_not_published),
49
- scope_id_eq: scope_ids_hash(scopes.top_level),
50
- category_id_eq: category_ids_hash(categories.first_class),
51
- valuator_role_ids_has: valuator_role_ids
52
- }
39
+ def translated_state_eq(state)
40
+ return t("decidim.admin.filters.proposals.state_eq.values.withdrawn") if state == "withdrawn"
41
+
42
+ translated_attribute(ProposalState.where(component: current_component, token: state).first&.title)
53
43
  end
54
44
 
55
- # Cannot user `super` here, because it does not belong to a superclass
56
- # but to a concern.
57
- def dynamically_translated_filters
58
- [:scope_id_eq, :category_id_eq, :valuator_role_ids_has]
45
+ def state_eq_values
46
+ ProposalState.where(component: current_component).pluck(:token) + ["withdrawn"]
59
47
  end
60
48
 
61
49
  def valuator_role_ids
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class ProposalStatesController < Admin::ApplicationController
7
+ include Decidim::Admin::Paginable
8
+
9
+ helper_method :proposal_states, :proposal_state
10
+ def index
11
+ enforce_permission_to :read, :proposal_state
12
+ end
13
+
14
+ def new
15
+ enforce_permission_to :create, :proposal_state
16
+ @form = form(Decidim::Proposals::Admin::ProposalStateForm).instance
17
+ end
18
+
19
+ def create
20
+ enforce_permission_to :create, :proposal_state
21
+
22
+ @form = form(ProposalStateForm).from_params(params)
23
+
24
+ CreateProposalState.call(@form) do
25
+ on(:ok) do
26
+ flash[:notice] = I18n.t("proposal_states.create.success", scope: "decidim.proposals.admin")
27
+ redirect_to proposal_states_path
28
+ end
29
+
30
+ on(:invalid) do
31
+ flash.keep[:alert] = I18n.t("proposal_states.create.error", scope: "decidim.proposals.admin")
32
+
33
+ render action: :new
34
+ end
35
+ end
36
+ end
37
+
38
+ def edit
39
+ enforce_permission_to(:update, :proposal_state, proposal_state:)
40
+ @form = form(Decidim::Proposals::Admin::ProposalStateForm).from_model(proposal_state)
41
+ end
42
+
43
+ def update
44
+ enforce_permission_to(:update, :proposal_state, proposal_state:)
45
+ @form = form(ProposalStateForm).from_params(params)
46
+
47
+ UpdateProposalState.call(@form, proposal_state) do
48
+ on(:ok) do
49
+ flash[:notice] = I18n.t("proposal_states.update.success", scope: "decidim.proposals.admin")
50
+
51
+ redirect_to proposal_states_path
52
+ end
53
+
54
+ on(:invalid) do
55
+ flash.now[:alert] = I18n.t("proposal_states.update.error", scope: "decidim.proposals.admin")
56
+
57
+ render action: :edit
58
+ end
59
+ end
60
+ end
61
+
62
+ def destroy
63
+ enforce_permission_to(:destroy, :proposal_state, proposal_state:)
64
+
65
+ DestroyProposalState.call(proposal_state, current_user) do
66
+ on(:ok) do
67
+ flash[:notice] = I18n.t("proposal_states.destroy.success", scope: "decidim.proposals.admin")
68
+
69
+ redirect_to proposal_states_path
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def proposal_state
77
+ @proposal_state ||= proposal_states.find(params[:id])
78
+ end
79
+
80
+ def proposal_states
81
+ @proposal_states ||= paginate(ProposalState.where(component: current_component))
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -14,6 +14,10 @@ module Decidim
14
14
  helper_method :proposals, :query, :form_presenter, :proposal, :proposal_ids
15
15
  helper Proposals::Admin::ProposalBulkActionsHelper
16
16
 
17
+ before_action :check_admin_session_filters, only: [:index]
18
+
19
+ def index; end
20
+
17
21
  def show
18
22
  @notes_form = form(ProposalNoteForm).instance
19
23
  @answer_form = form(Admin::ProposalAnswerForm).from_model(proposal)
@@ -5,10 +5,12 @@ module Decidim
5
5
  module Admin
6
6
  class ValuationAssignmentsController < Admin::ApplicationController
7
7
  def create
8
- enforce_permission_to :assign_to_valuator, :proposals
9
-
10
8
  @form = form(Admin::ValuationAssignmentForm).from_params(params)
11
9
 
10
+ @form.proposals.each do |proposal|
11
+ enforce_permission_to :assign_to_valuator, :proposals, proposal:
12
+ end
13
+
12
14
  Admin::AssignProposalsToValuator.call(@form) do
13
15
  on(:ok) do |_proposal|
14
16
  flash[:notice] = I18n.t("valuation_assignments.create.success", scope: "decidim.proposals.admin")
@@ -23,9 +25,11 @@ module Decidim
23
25
  end
24
26
 
25
27
  def destroy
26
- @form = form(Admin::ValuationAssignmentForm).from_params(destroy_params)
28
+ @form = form(Admin::ValuationAssignmentForm).from_params(params)
27
29
 
28
- enforce_permission_to :unassign_from_valuator, :proposals, valuator: @form.valuator_user
30
+ @form.valuator_roles.each do |valuator_role|
31
+ enforce_permission_to :unassign_from_valuator, :proposals, valuator: valuator_role.user
32
+ end
29
33
 
30
34
  Admin::UnassignProposalsFromValuator.call(@form) do
31
35
  on(:ok) do |_proposal|
@@ -42,13 +46,6 @@ module Decidim
42
46
 
43
47
  private
44
48
 
45
- def destroy_params
46
- {
47
- id: params.dig(:valuator_role, :id) || params[:id],
48
- proposal_ids: params[:proposal_ids] || [params[:proposal_id]]
49
- }
50
- end
51
-
52
49
  def skip_manage_component_permission
53
50
  true
54
51
  end
@@ -7,6 +7,7 @@ module Decidim
7
7
  helper ProposalWizardHelper
8
8
  helper ParticipatoryTextsHelper
9
9
  helper UserGroupHelper
10
+ helper Decidim::Admin::IconLinkHelper
10
11
  include Decidim::ApplicationHelper
11
12
  include Flaggable
12
13
  include Withdrawable
@@ -18,18 +19,17 @@ module Decidim
18
19
 
19
20
  helper_method :proposal_presenter, :form_presenter, :tab_panel_items
20
21
 
21
- before_action :authenticate_user!, only: [:new, :create, :complete]
22
- before_action :ensure_is_draft, only: [:compare, :complete, :preview, :publish, :edit_draft, :update_draft, :destroy_draft]
22
+ before_action :authenticate_user!, only: [:new, :create]
23
+ before_action :ensure_is_draft, only: [:preview, :publish, :edit_draft, :update_draft, :destroy_draft]
23
24
  before_action :set_proposal, only: [:show, :edit, :update, :withdraw]
24
25
  before_action :edit_form, only: [:edit_draft, :edit]
26
+ before_action :set_view_mode, only: [:index]
25
27
 
26
28
  before_action :set_participatory_text
27
29
 
28
30
  # rubocop:disable Naming/VariableNumber
29
31
  STEP1 = :step_1
30
32
  STEP2 = :step_2
31
- STEP3 = :step_3
32
- STEP4 = :step_4
33
33
  # rubocop:enable Naming/VariableNumber
34
34
 
35
35
  def index
@@ -43,11 +43,13 @@ module Decidim
43
43
  .order(position: :asc)
44
44
  render "decidim/proposals/proposals/participatory_texts/participatory_text"
45
45
  else
46
- @proposals = search.result
46
+ @base_query = search
47
+ .result
48
+ .published
49
+ .not_hidden
47
50
 
48
- @proposals = reorder(@proposals)
49
- @proposals = paginate(@proposals)
50
- @proposals = @proposals.includes(:component, :coauthorships, :attachments)
51
+ @proposals = @base_query.includes(:component, :coauthorships, :attachments)
52
+ @all_geocoded_proposals = @base_query.geocoded
51
53
 
52
54
  @voted_proposals = if current_user
53
55
  ProposalVote.where(
@@ -57,6 +59,8 @@ module Decidim
57
59
  else
58
60
  []
59
61
  end
62
+ @proposals = reorder(@proposals)
63
+ @proposals = paginate(@proposals)
60
64
  end
61
65
  end
62
66
 
@@ -70,20 +74,21 @@ module Decidim
70
74
  if proposal_draft.present?
71
75
  redirect_to edit_draft_proposal_path(proposal_draft, component_id: proposal_draft.component.id, question_slug: proposal_draft.component.participatory_space.slug)
72
76
  else
73
- @form = form(ProposalWizardCreateStepForm).from_params(body: translated_proposal_body_template)
77
+ @form = form(ProposalForm).from_params(body: translated_proposal_body_template)
74
78
  end
75
79
  end
76
80
 
77
81
  def create
78
82
  enforce_permission_to :create, :proposal
79
83
  @step = STEP1
80
- @form = form(ProposalWizardCreateStepForm).from_params(proposal_creation_params)
84
+ @form = form(ProposalForm).from_params(proposal_creation_params)
81
85
 
82
86
  CreateProposal.call(@form, current_user) do
83
87
  on(:ok) do |proposal|
84
88
  flash[:notice] = I18n.t("proposals.create.success", scope: "decidim")
85
89
 
86
- redirect_to "#{Decidim::ResourceLocatorPresenter.new(proposal).path}/compare"
90
+ @proposal = proposal
91
+ redirect_to "#{Decidim::ResourceLocatorPresenter.new(proposal).path}/preview"
87
92
  end
88
93
 
89
94
  on(:invalid) do
@@ -93,37 +98,15 @@ module Decidim
93
98
  end
94
99
  end
95
100
 
96
- def compare
97
- enforce_permission_to :edit, :proposal, proposal: @proposal
98
- @step = STEP2
99
- @similar_proposals ||= Decidim::Proposals::SimilarProposals
100
- .for(current_component, @proposal)
101
- .all
102
-
103
- if @similar_proposals.blank?
104
- flash[:notice] = I18n.t("proposals.proposals.compare.no_similars_found", scope: "decidim")
105
- redirect_to "#{Decidim::ResourceLocatorPresenter.new(@proposal).path}/complete"
106
- end
107
- end
108
-
109
- def complete
110
- enforce_permission_to :edit, :proposal, proposal: @proposal
111
- @step = STEP3
112
-
113
- @form = form_proposal_model
114
-
115
- @form.attachment = form_attachment_new
116
- end
117
-
118
101
  def preview
119
102
  enforce_permission_to :edit, :proposal, proposal: @proposal
120
- @step = STEP4
103
+ @step = STEP2
121
104
  @form = form(ProposalForm).from_model(@proposal)
122
105
  end
123
106
 
124
107
  def publish
125
108
  enforce_permission_to :edit, :proposal, proposal: @proposal
126
- @step = STEP4
109
+ @step = STEP2
127
110
  PublishProposal.call(@proposal, current_user) do
128
111
  on(:ok) do
129
112
  flash[:notice] = I18n.t("proposals.publish.success", scope: "decidim")
@@ -138,7 +121,7 @@ module Decidim
138
121
  end
139
122
 
140
123
  def edit_draft
141
- @step = STEP3
124
+ @step = STEP1
142
125
  enforce_permission_to :edit, :proposal, proposal: @proposal
143
126
  end
144
127
 
@@ -205,8 +188,8 @@ module Decidim
205
188
  flash[:notice] = I18n.t("proposals.update.success", scope: "decidim")
206
189
  redirect_to Decidim::ResourceLocatorPresenter.new(@proposal).path
207
190
  end
208
- on(:has_supports) do
209
- flash[:alert] = I18n.t("proposals.withdraw.errors.has_supports", scope: "decidim")
191
+ on(:has_votes) do
192
+ flash[:alert] = I18n.t("proposals.withdraw.errors.has_votes", scope: "decidim")
210
193
  redirect_to Decidim::ResourceLocatorPresenter.new(@proposal).path
211
194
  end
212
195
  end
@@ -224,13 +207,20 @@ module Decidim
224
207
  with_any_origin: nil,
225
208
  activity: "all",
226
209
  with_any_category: nil,
227
- with_any_state: %w(accepted evaluating state_not_published),
210
+ with_any_state: default_states,
228
211
  with_any_scope: nil,
229
212
  related_to: "",
230
213
  type: "all"
231
214
  }
232
215
  end
233
216
 
217
+ def default_states
218
+ [
219
+ Decidim::Proposals::ProposalState.where(component: current_component).pluck(:token).map(&:to_s),
220
+ %w(state_not_published)
221
+ ].flatten - ["rejected"]
222
+ end
223
+
234
224
  def proposal_draft
235
225
  Proposal.from_all_author_identities(current_user).not_hidden.only_amendables
236
226
  .where(component: current_component).find_by(published_at: nil)
@@ -329,6 +319,15 @@ module Decidim
329
319
  }
330
320
  ] + attachments_tab_panel_items(@proposal)
331
321
  end
322
+
323
+ def set_view_mode
324
+ @view_mode ||= params[:view_mode] || session[:view_mode] || default_view_mode
325
+ session[:view_mode] = @view_mode
326
+ end
327
+
328
+ def default_view_mode
329
+ @default_view_mode ||= current_component.settings.attachments_allowed? ? "grid" : "list"
330
+ end
332
331
  end
333
332
  end
334
333
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.affected_user.notification_title')
4
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.affected_user.email_subject')
5
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.affected_user.email_outro')
6
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.affected_user.email_intro')
7
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.follower.notification_title')
8
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.follower.email_subject')
9
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.follower.email_outro')
10
+ # i18n-tasks-use t('decidim.events.proposals.proposal_state_changed.follower.email_intro')
11
+ module Decidim
12
+ module Proposals
13
+ class ProposalStateChangedEvent < Decidim::Events::SimpleEvent
14
+ include Decidim::Events::AuthorEvent
15
+
16
+ def resource_text
17
+ translated_attribute(resource.answer)
18
+ end
19
+
20
+ def event_has_roles?
21
+ true
22
+ end
23
+
24
+ def default_i18n_options
25
+ super.merge({ state: })
26
+ end
27
+
28
+ def state
29
+ if resource.emendation?
30
+ humanize_proposal_state(model.state)
31
+ else
32
+ translated_attribute(resource.proposal_state&.title)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -8,13 +8,13 @@ module Decidim
8
8
  include TranslatableAttributes
9
9
  mimic :proposal_answer
10
10
 
11
- translatable_attribute :answer, Decidim::Attributes::RichText
12
- translatable_attribute :cost_report, Decidim::Attributes::RichText
13
- translatable_attribute :execution_period, Decidim::Attributes::RichText
11
+ translatable_attribute :answer, String
12
+ translatable_attribute :cost_report, String
13
+ translatable_attribute :execution_period, String
14
14
  attribute :cost, Float
15
15
  attribute :internal_state, String
16
16
 
17
- validates :internal_state, presence: true, inclusion: { in: %w(not_answered accepted rejected evaluating) }
17
+ validates :internal_state, presence: true, inclusion: { in: :proposal_states }
18
18
  validates :answer, translatable_presence: true, if: ->(form) { form.state == "rejected" }
19
19
 
20
20
  with_options if: :costs_required? do
@@ -35,6 +35,10 @@ module Decidim
35
35
 
36
36
  private
37
37
 
38
+ def proposal_states
39
+ Decidim::Proposals::ProposalState.where(component: current_component).pluck(:token).map(&:to_s) + ["not_answered"]
40
+ end
41
+
38
42
  def costs_enabled?
39
43
  current_component.current_settings.answers_with_costs?
40
44
  end
@@ -10,7 +10,7 @@ module Decidim
10
10
  translatable_attribute :title, String do |field, _locale|
11
11
  validates field, length: { in: 15..150 }, if: proc { |resource| resource.send(field).present? }
12
12
  end
13
- translatable_attribute :body, Decidim::Attributes::RichText
13
+ translatable_attribute :body, String
14
14
 
15
15
  validates :title, :body, translatable_presence: true
16
16
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class ProposalStateForm < Decidim::Form
7
+ include Decidim::TranslatableAttributes
8
+
9
+ mimic :proposal_state
10
+
11
+ translatable_attribute :title, String
12
+ translatable_attribute :announcement_title, String
13
+ attribute :bg_color, String, default: "#F3F4F7"
14
+ attribute :text_color, String, default: "#3E4C5C"
15
+
16
+ validates :title, translatable_presence: true
17
+ validates :bg_color, presence: true
18
+ validates :text_color, presence: true
19
+ end
20
+ end
21
+ end
22
+ end
@@ -39,7 +39,7 @@ module Decidim
39
39
 
40
40
  proposals.each do |proposal|
41
41
  errors_set << :not_official unless proposal.official?
42
- errors_set << :supported if proposal.votes.any? || proposal.endorsements.any?
42
+ errors_set << :voted if proposal.votes.any? || proposal.endorsements.any?
43
43
  end
44
44
 
45
45
  errors_set.each { |error| errors.add(:base, error) } if errors_set.any?
@@ -19,7 +19,7 @@ module Decidim
19
19
  validates :import_proposals, allow_nil: false, acceptance: true
20
20
  validate :valid_states
21
21
 
22
- VALID_STATES = %w(accepted not_answered evaluating rejected withdrawn).freeze
22
+ VALID_STATES = %w(accepted not_answered evaluating rejected).freeze
23
23
 
24
24
  def states_collection
25
25
  VALID_STATES.map do |state|
@@ -4,32 +4,30 @@ module Decidim
4
4
  module Proposals
5
5
  module Admin
6
6
  class ValuationAssignmentForm < Decidim::Form
7
- mimic :valuator_role
8
-
9
7
  attribute :id, Integer
10
8
  attribute :proposal_ids, Array
9
+ attribute :valuator_role_ids, Array
11
10
 
12
- validates :valuator_role, :proposals, :current_component, presence: true
11
+ validates :valuator_roles, :proposals, :current_component, presence: true
13
12
  validate :same_participatory_space
14
13
 
15
14
  def proposals
16
15
  @proposals ||= Decidim::Proposals::Proposal.where(component: current_component, id: proposal_ids).uniq
17
16
  end
18
17
 
19
- def valuator_role
20
- @valuator_role ||= current_component.participatory_space.user_roles(:valuator).find_by(id:)
21
- end
22
-
23
- def valuator_user
24
- return unless valuator_role
25
-
26
- @valuator_user ||= valuator_role.user
18
+ def valuator_roles
19
+ @valuator_roles ||= current_component.participatory_space.user_roles(:valuator).where(id: valuator_role_ids)
27
20
  end
28
21
 
29
22
  def same_participatory_space
30
- return if !valuator_role || !current_component
31
-
32
- errors.add(:id, :invalid) if current_component.participatory_space != valuator_role.participatory_space
23
+ return if valuator_roles.empty? || !current_component
24
+
25
+ valuator_roles.each do |valuator_role|
26
+ if current_component.participatory_space != valuator_role.participatory_space
27
+ errors.add(:id, :invalid)
28
+ break
29
+ end
30
+ end
33
31
  end
34
32
  end
35
33
  end
@@ -3,13 +3,17 @@
3
3
  module Decidim
4
4
  module Proposals
5
5
  # A form object to be used when public users want to create a proposal.
6
- class ProposalForm < Decidim::Proposals::ProposalWizardCreateStepForm
6
+ class ProposalForm < Decidim::Form
7
7
  include Decidim::TranslatableAttributes
8
8
  include Decidim::AttachmentAttributes
9
9
  include Decidim::HasUploadValidations
10
10
 
11
11
  mimic :proposal
12
12
 
13
+ attribute :title, String
14
+ attribute :body, Decidim::Attributes::CleanString
15
+ attribute :body_template, String
16
+ attribute :user_group_id, Integer
13
17
  attribute :address, String
14
18
  attribute :latitude, Float
15
19
  attribute :longitude, Float
@@ -20,23 +24,34 @@ module Decidim
20
24
 
21
25
  attachments_attribute :documents
22
26
 
27
+ validates :title, :body, presence: true, etiquette: true
28
+ validates :title, length: { in: 15..150 }
29
+ validates :body, proposal_length: {
30
+ minimum: 15,
31
+ maximum: ->(record) { record.component.settings.proposal_length }
32
+ }
23
33
  validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? }
24
34
  validates :category, presence: true, if: ->(form) { form.category_id.present? }
25
35
  validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
26
36
  validates :scope_id, scope_belongs_to_component: true, if: ->(form) { form.scope_id.present? }
37
+
38
+ validate :body_is_not_bare_template
27
39
  validate :notify_missing_attachment_if_errored
28
40
 
41
+ alias component current_component
29
42
  delegate :categories, to: :current_component
30
43
 
31
44
  def map_model(model)
32
- super
33
-
34
- body = translated_attribute(model.body)
45
+ self.title = translated_attribute(model.title)
46
+ self.body = translated_attribute(model.body)
35
47
  @suggested_hashtags = Decidim::ContentRenderers::HashtagRenderer.new(body).extra_hashtags.map(&:name).map(&:downcase)
36
48
 
37
49
  presenter = ProposalPresenter.new(model)
38
50
  self.body = presenter.editor_body(all_locales: body.is_a?(Hash))
39
51
 
52
+ self.user_group_id = model.user_groups.first&.id
53
+ self.category_id = model.categorization.decidim_category_id if model.categorization
54
+
40
55
  # The scope attribute is with different key (decidim_scope_id), so it
41
56
  # has to be manually mapped.
42
57
  self.scope_id = model.scope.id if model.scope
@@ -100,6 +115,12 @@ module Decidim
100
115
 
101
116
  private
102
117
 
118
+ def body_is_not_bare_template
119
+ return if body_template.blank?
120
+
121
+ errors.add(:body, :cant_be_equal_to_template) if body.presence == body_template.presence
122
+ end
123
+
103
124
  # This method will add an error to the "add_documents" field only if there is any error
104
125
  # in any other field. This is needed because when the form has an error, the attachment
105
126
  # is lost, so we need a way to inform the user of this problem.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Proposals
5
- # A form object to be used when Collaborative Draft editors rejects a request to acces it.
5
+ # A form object to be used when Collaborative Draft editors rejects a request to access it.
6
6
  class RejectAccessToCollaborativeDraftForm < AccessToCollaborativeDraftForm
7
7
  end
8
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Proposals
5
- # A form object to be used when public users want to request acces to a Collaborative Draft.
5
+ # A form object to be used when public users want to request access to a Collaborative Draft.
6
6
  class RequestAccessToCollaborativeDraftForm < Decidim::Form
7
7
  mimic :collaborative_draft
8
8
 
@@ -8,26 +8,16 @@ module Decidim
8
8
  Decidim::Proposals::Proposal.find(id)
9
9
  end
10
10
 
11
- # Public: Generates a select field with the valuators of the given participatory space.
12
- #
13
- # participatory_space - A participatory space instance.
14
- # prompt - An i18n string to show as prompt
15
- #
16
- # Returns a String.
17
- def bulk_valuators_select(participatory_space, prompt)
18
- options_for_select = find_valuators_for_select(participatory_space)
19
- select(:valuator_role, :id, options_for_select, prompt:)
20
- end
21
-
22
- # Internal: A method to cache to queries to find the valuators for the
23
- # current space.
24
- def find_valuators_for_select(participatory_space)
25
- return @valuators_for_select if @valuators_for_select
26
-
11
+ # find the valuators for the current space.
12
+ def find_valuators_for_select(participatory_space, current_user)
27
13
  valuator_roles = participatory_space.user_roles(:valuator)
28
14
  valuators = Decidim::User.where(id: valuator_roles.pluck(:decidim_user_id)).to_a
29
15
 
30
- @valuators_for_select = valuator_roles.map do |role|
16
+ filtered_valuator_roles = valuator_roles.filter do |role|
17
+ role.decidim_user_id != current_user.id
18
+ end
19
+
20
+ filtered_valuator_roles.map do |role|
31
21
  valuator = valuators.find { |user| user.id == role.decidim_user_id }
32
22
 
33
23
  [valuator.name, role.id]