decidim-proposals 0.20.1 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/proposals/admin/proposals.es6 +24 -11
  3. data/app/cells/decidim/proposals/cost_report/show.erb +35 -0
  4. data/app/cells/decidim/proposals/cost_report_cell.rb +42 -0
  5. data/app/cells/decidim/proposals/proposal_m_cell.rb +9 -1
  6. data/app/cells/decidim/proposals/proposal_tags/show.erb +12 -10
  7. data/app/cells/decidim/proposals/proposal_tags_cell.rb +5 -0
  8. data/app/commands/decidim/proposals/admin/answer_proposal.rb +24 -46
  9. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +61 -0
  10. data/app/commands/decidim/proposals/admin/create_proposal.rb +5 -0
  11. data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +85 -0
  12. data/app/commands/decidim/proposals/admin/publish_answers.rb +67 -0
  13. data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +62 -0
  14. data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +75 -0
  15. data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +82 -0
  16. data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +16 -6
  17. data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +8 -9
  18. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +105 -29
  19. data/app/controllers/decidim/proposals/admin/valuation_assignments_controller.rb +58 -0
  20. data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +19 -3
  21. data/app/controllers/decidim/proposals/proposals_controller.rb +42 -7
  22. data/app/controllers/decidim/proposals/versions_controller.rb +4 -1
  23. data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +11 -0
  24. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +27 -2
  25. data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +37 -0
  26. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +8 -0
  27. data/app/helpers/decidim/proposals/admin/filterable_helper.rb +17 -0
  28. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +35 -0
  29. data/app/helpers/decidim/proposals/admin/proposal_rankings_helper.rb +63 -0
  30. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +122 -0
  31. data/app/helpers/decidim/proposals/application_helper.rb +36 -25
  32. data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +9 -9
  33. data/app/helpers/decidim/proposals/proposal_cells_helper.rb +1 -1
  34. data/app/helpers/decidim/proposals/proposals_helper.rb +18 -0
  35. data/app/models/decidim/proposals/proposal.rb +163 -16
  36. data/app/models/decidim/proposals/valuation_assignment.rb +24 -0
  37. data/app/permissions/decidim/proposals/admin/permissions.rb +77 -11
  38. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
  39. data/app/presenters/decidim/proposals/admin_log/valuation_assignment_presenter.rb +51 -0
  40. data/app/presenters/decidim/proposals/admin_log/value_types/valuator_role_user_presenter.rb +19 -0
  41. data/app/presenters/decidim/proposals/collaborative_draft_presenter.rb +2 -28
  42. data/app/presenters/decidim/proposals/log/valuation_assignment_presenter.rb +22 -0
  43. data/app/presenters/decidim/proposals/proposal_presenter.rb +26 -1
  44. data/app/services/decidim/proposals/collaborative_draft_search.rb +18 -10
  45. data/app/services/decidim/proposals/proposal_search.rb +33 -40
  46. data/app/types/decidim/proposals/proposal_input_filter.rb +29 -0
  47. data/app/types/decidim/proposals/proposal_input_sort.rb +28 -0
  48. data/app/types/decidim/proposals/proposal_type.rb +35 -4
  49. data/app/types/decidim/proposals/proposals_type.rb +14 -17
  50. data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +35 -0
  51. data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +1 -1
  52. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +1 -1
  53. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +8 -2
  54. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
  55. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +25 -17
  56. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +15 -0
  57. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +21 -1
  58. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +14 -0
  59. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +25 -0
  60. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +15 -0
  61. data/app/views/decidim/proposals/admin/proposals/index.html.erb +16 -7
  62. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +12 -0
  63. data/app/views/decidim/proposals/admin/proposals/show.html.erb +186 -0
  64. data/app/views/decidim/proposals/admin/proposals/update_category.js.erb +3 -2
  65. data/app/views/decidim/proposals/admin/proposals/update_scope.js.erb +27 -0
  66. data/app/views/decidim/proposals/collaborative_drafts/_filters.html.erb +3 -3
  67. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +1 -1
  68. data/app/views/decidim/proposals/proposals/_filters.html.erb +12 -12
  69. data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +1 -4
  70. data/app/views/decidim/proposals/proposals/_proposal_preview.html.erb +1 -1
  71. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +1 -1
  72. data/app/views/decidim/proposals/proposals/index.html.erb +1 -1
  73. data/app/views/decidim/proposals/proposals/new.html.erb +1 -1
  74. data/app/views/decidim/proposals/proposals/show.html.erb +17 -23
  75. data/config/locales/ar.yml +69 -17
  76. data/config/locales/ca.yml +113 -18
  77. data/config/locales/cs.yml +123 -31
  78. data/config/locales/de.yml +38 -18
  79. data/config/locales/el.yml +1 -0
  80. data/config/locales/en.yml +112 -17
  81. data/config/locales/es-MX.yml +112 -17
  82. data/config/locales/es-PY.yml +112 -17
  83. data/config/locales/es.yml +113 -18
  84. data/config/locales/eu.yml +38 -18
  85. data/config/locales/fi-plain.yml +113 -18
  86. data/config/locales/fi.yml +113 -18
  87. data/config/locales/fr.yml +38 -18
  88. data/config/locales/gl.yml +38 -18
  89. data/config/locales/hu.yml +112 -17
  90. data/config/locales/id-ID.yml +38 -18
  91. data/config/locales/is-IS.yml +25 -15
  92. data/config/locales/it.yml +38 -18
  93. data/config/locales/nl.yml +43 -18
  94. data/config/locales/no.yml +66 -18
  95. data/config/locales/pl.yml +38 -18
  96. data/config/locales/pt-BR.yml +39 -19
  97. data/config/locales/pt.yml +39 -19
  98. data/config/locales/ru.yml +25 -17
  99. data/config/locales/sv.yml +39 -18
  100. data/config/locales/tr-TR.yml +38 -18
  101. data/config/locales/uk.yml +25 -17
  102. data/db/migrate/20200203111239_add_proposal_valuation_assignments.rb +12 -0
  103. data/db/migrate/20200210135152_add_costs_to_proposals.rb +9 -0
  104. data/db/migrate/20200212120110_sync_proposals_state_with_amendments_state.rb +28 -0
  105. data/db/migrate/20200227175922_add_state_published_at_to_proposals.rb +7 -0
  106. data/db/migrate/20200306123652_publish_existing_proposals_state.rb +15 -0
  107. data/lib/decidim/proposals.rb +1 -0
  108. data/lib/decidim/proposals/admin_engine.rb +7 -3
  109. data/lib/decidim/proposals/component.rb +39 -19
  110. data/lib/decidim/proposals/engine.rb +1 -1
  111. data/lib/decidim/proposals/test/factories.rb +55 -0
  112. data/lib/decidim/proposals/valuatable.rb +21 -0
  113. data/lib/decidim/proposals/version.rb +1 -1
  114. metadata +53 -36
  115. data/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +0 -22
  116. data/app/views/decidim/proposals/admin/proposal_notes/index.html.erb +0 -3
  117. data/app/views/decidim/proposals/admin/shared/_info_proposal.html.erb +0 -20
  118. data/app/views/decidim/proposals/proposal_widgets/show.html.erb +0 -4
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class ValuationAssignmentsController < Admin::ApplicationController
7
+ def create
8
+ enforce_permission_to :assign_to_valuator, :proposals
9
+
10
+ @form = form(Admin::ValuationAssignmentForm).from_params(params)
11
+
12
+ Admin::AssignProposalsToValuator.call(@form) do
13
+ on(:ok) do |_proposal|
14
+ flash[:notice] = I18n.t("valuation_assignments.create.success", scope: "decidim.proposals.admin")
15
+ redirect_to EngineRouter.admin_proxy(current_component).root_path
16
+ end
17
+
18
+ on(:invalid) do
19
+ flash.now[:alert] = I18n.t("valuation_assignments.create.invalid", scope: "decidim.proposals.admin")
20
+ redirect_to EngineRouter.admin_proxy(current_component).root_path
21
+ end
22
+ end
23
+ end
24
+
25
+ def destroy
26
+ @form = form(Admin::ValuationAssignmentForm).from_params(destroy_params)
27
+
28
+ enforce_permission_to :unassign_from_valuator, :proposals, valuator: @form.valuator_user
29
+
30
+ Admin::UnassignProposalsFromValuator.call(@form) do
31
+ on(:ok) do |_proposal|
32
+ flash.keep[:notice] = I18n.t("valuation_assignments.delete.success", scope: "decidim.proposals.admin")
33
+ redirect_back fallback_location: EngineRouter.admin_proxy(current_component).root_path
34
+ end
35
+
36
+ on(:invalid) do
37
+ flash.keep[:alert] = I18n.t("valuation_assignments.delete.invalid", scope: "decidim.proposals.admin")
38
+ redirect_back fallback_location: EngineRouter.admin_proxy(current_component).root_path
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
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
+ def skip_manage_component_permission
53
+ true
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -171,12 +171,28 @@ module Decidim
171
171
  def default_filter_params
172
172
  {
173
173
  search_text: "",
174
- category_id: "",
175
- state: "open",
176
- scope_id: nil,
174
+ category_id: default_filter_category_params,
175
+ state: %w(open),
176
+ scope_id: default_filter_scope_params,
177
177
  related_to: ""
178
178
  }
179
179
  end
180
+
181
+ def default_filter_category_params
182
+ return unless current_component.participatory_space.categories.any?
183
+
184
+ ["without"] + current_component.participatory_space.categories.map { |category| category.id.to_s }
185
+ end
186
+
187
+ def default_filter_scope_params
188
+ return unless current_component.participatory_space.scopes.any?
189
+
190
+ if current_component.participatory_space.scope
191
+ [current_component.participatory_space.scope.id] + current_component.participatory_space.scope.children.map { |scope| scope.id.to_s }
192
+ else
193
+ ["global"] + current_component.participatory_space.scopes.map { |scope| scope.id.to_s }
194
+ end
195
+ end
180
196
  end
181
197
  end
182
198
  end
@@ -13,7 +13,7 @@ module Decidim
13
13
  include Decidim::Proposals::Orderable
14
14
  include Paginable
15
15
 
16
- helper_method :form_presenter
16
+ helper_method :proposal_presenter, :form_presenter
17
17
 
18
18
  before_action :authenticate_user!, only: [:new, :create, :complete]
19
19
  before_action :ensure_is_draft, only: [:compare, :complete, :preview, :publish, :edit_draft, :update_draft, :destroy_draft]
@@ -65,14 +65,14 @@ module Decidim
65
65
  if proposal_draft.present?
66
66
  redirect_to edit_draft_proposal_path(proposal_draft, component_id: proposal_draft.component.id, question_slug: proposal_draft.component.participatory_space.slug)
67
67
  else
68
- @form = form(ProposalWizardCreateStepForm).from_params({})
68
+ @form = form(ProposalWizardCreateStepForm).from_params(body: translated_proposal_body_template)
69
69
  end
70
70
  end
71
71
 
72
72
  def create
73
73
  enforce_permission_to :create, :proposal
74
74
  @step = :step_1
75
- @form = form(ProposalWizardCreateStepForm).from_params(params)
75
+ @form = form(ProposalWizardCreateStepForm).from_params(proposal_creation_params)
76
76
 
77
77
  CreateProposal.call(@form, current_user) do
78
78
  on(:ok) do |proposal|
@@ -212,16 +212,39 @@ module Decidim
212
212
  def default_filter_params
213
213
  {
214
214
  search_text: "",
215
- origin: "all",
215
+ origin: default_filter_origin_params,
216
216
  activity: "all",
217
- category_id: "",
218
- state: "except_rejected",
219
- scope_id: nil,
217
+ category_id: default_filter_category_params,
218
+ state: %w(accepted evaluating not_answered),
219
+ scope_id: default_filter_scope_params,
220
220
  related_to: "",
221
221
  type: "all"
222
222
  }
223
223
  end
224
224
 
225
+ def default_filter_origin_params
226
+ filter_origin_params = %w(citizens meeting)
227
+ filter_origin_params << "official" if component_settings.official_proposals_enabled
228
+ filter_origin_params << "user_group" if current_organization.user_groups_enabled?
229
+ filter_origin_params
230
+ end
231
+
232
+ def default_filter_category_params
233
+ return "all" unless current_component.participatory_space.categories.any?
234
+
235
+ ["all"] + current_component.participatory_space.categories.map { |category| category.id.to_s }
236
+ end
237
+
238
+ def default_filter_scope_params
239
+ return "all" unless current_component.participatory_space.scopes.any?
240
+
241
+ if current_component.participatory_space.scope
242
+ ["all", current_component.participatory_space.scope.id] + current_component.participatory_space.scope.children.map { |scope| scope.id.to_s }
243
+ else
244
+ %w(all global) + current_component.participatory_space.scopes.map { |scope| scope.id.to_s }
245
+ end
246
+ end
247
+
225
248
  def proposal_draft
226
249
  Proposal.from_all_author_identities(current_user).not_hidden.only_amendables
227
250
  .where(component: current_component).find_by(published_at: nil)
@@ -245,6 +268,10 @@ module Decidim
245
268
  Proposal.only_visible_emendations_for(current_user, current_component).published.include?(@proposal)
246
269
  end
247
270
 
271
+ def proposal_presenter
272
+ @proposal_presenter ||= present(@proposal)
273
+ end
274
+
248
275
  def form_proposal_params
249
276
  form(ProposalForm).from_params(params)
250
277
  end
@@ -271,6 +298,14 @@ module Decidim
271
298
  def set_participatory_text
272
299
  @participatory_text = Decidim::Proposals::ParticipatoryText.find_by(component: current_component)
273
300
  end
301
+
302
+ def translated_proposal_body_template
303
+ translated_attribute component_settings.new_proposal_body_template
304
+ end
305
+
306
+ def proposal_creation_params
307
+ params[:proposal].merge(body_template: translated_proposal_body_template)
308
+ end
274
309
  end
275
310
  end
276
311
  end
@@ -6,13 +6,16 @@ module Decidim
6
6
  # has been updated through time.
7
7
  class VersionsController < Decidim::Proposals::ApplicationController
8
8
  helper Decidim::TraceabilityHelper
9
+
10
+ include Decidim::ApplicationHelper
11
+
9
12
  helper_method :current_version, :item
10
13
 
11
14
  private
12
15
 
13
16
  def item
14
17
  @item ||= if params[:proposal_id]
15
- Proposal.where(component: current_component).find(params[:proposal_id])
18
+ present(Proposal.where(component: current_component).find(params[:proposal_id]))
16
19
  else
17
20
  CollaborativeDraft.where(component: current_component).find(params[:collaborative_draft_id])
18
21
  end
@@ -0,0 +1,11 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class UpdateProposalScopeEvent < Decidim::Events::SimpleEvent
7
+ include Decidim::Events::AuthorEvent
8
+ end
9
+ end
10
+ end
11
+ end
@@ -9,10 +9,35 @@ module Decidim
9
9
  mimic :proposal_answer
10
10
 
11
11
  translatable_attribute :answer, String
12
- attribute :state, String
12
+ translatable_attribute :cost_report, String
13
+ translatable_attribute :execution_period, String
14
+ attribute :cost, Float
15
+ attribute :internal_state, String
13
16
 
14
- validates :state, presence: true, inclusion: { in: %w(accepted rejected evaluating) }
17
+ validates :internal_state, presence: true, inclusion: { in: %w(accepted rejected evaluating) }
15
18
  validates :answer, translatable_presence: true, if: ->(form) { form.state == "rejected" }
19
+
20
+ with_options if: :costs_required? do
21
+ validates :cost, numericality: true, presence: true
22
+ validates :cost_report, translatable_presence: true
23
+ validates :execution_period, translatable_presence: true
24
+ end
25
+
26
+ alias state internal_state
27
+
28
+ def costs_required?
29
+ costs_enabled? && state == "accepted"
30
+ end
31
+
32
+ def publish_answer?
33
+ current_component.current_settings.publish_answers_immediately?
34
+ end
35
+
36
+ private
37
+
38
+ def costs_enabled?
39
+ current_component.current_settings.answers_with_costs?
40
+ end
16
41
  end
17
42
  end
18
43
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class ValuationAssignmentForm < Decidim::Form
7
+ mimic :valuator_role
8
+
9
+ attribute :id, Integer
10
+ attribute :proposal_ids, Array
11
+
12
+ validates :valuator_role, :proposals, :current_component, presence: true
13
+ validate :same_participatory_space
14
+
15
+ def proposals
16
+ @proposals ||= Decidim::Proposals::Proposal.where(component: current_component, id: proposal_ids).uniq
17
+ end
18
+
19
+ def valuator_role
20
+ @valuator_role ||= current_component.participatory_space.user_roles(:valuator).find_by(id: id)
21
+ end
22
+
23
+ def valuator_user
24
+ return unless valuator_role
25
+
26
+ @valuator_user ||= valuator_role.user
27
+ end
28
+
29
+ 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
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -8,12 +8,14 @@ module Decidim
8
8
 
9
9
  attribute :title, String
10
10
  attribute :body, String
11
+ attribute :body_template, String
11
12
  attribute :user_group_id, Integer
12
13
 
13
14
  validates :title, :body, presence: true, etiquette: true
14
15
  validates :title, length: { maximum: 150 }
15
16
 
16
17
  validate :proposal_length
18
+ validate :body_is_not_bare_template
17
19
 
18
20
  alias component current_component
19
21
 
@@ -32,6 +34,12 @@ module Decidim
32
34
  length = current_component.settings.proposal_length
33
35
  errors.add(:body, :too_long, count: length) if body.length > length
34
36
  end
37
+
38
+ def body_is_not_bare_template
39
+ return if body_template.blank?
40
+
41
+ errors.add(:body, :cant_be_equal_to_template) if body.presence == body_template.presence
42
+ end
35
43
  end
36
44
  end
37
45
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ module FilterableHelper
7
+ def extra_dropdown_submenu_options_items(filter)
8
+ options = case filter
9
+ when :state_eq
10
+ content_tag(:li, filter_link_value(:state_null, true))
11
+ end
12
+ [options].compact
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ module ProposalBulkActionsHelper
7
+ # Public: Generates a select field with the valuators of the given participatory space.
8
+ #
9
+ # participatory_space - A participatory space instance.
10
+ # prompt - An i18n string to show as prompt
11
+ #
12
+ # Returns a String.
13
+ def bulk_valuators_select(participatory_space, prompt)
14
+ options_for_select = find_valuators_for_select(participatory_space)
15
+ select(:valuator_role, :id, options_for_select, prompt: prompt)
16
+ end
17
+
18
+ # Internal: A method to cache to queries to find the valuators for the
19
+ # current space.
20
+ def find_valuators_for_select(participatory_space)
21
+ return @valuators_for_select if @valuators_for_select
22
+
23
+ valuator_roles = participatory_space.user_roles(:valuator)
24
+ valuators = Decidim::User.where(id: valuator_roles.pluck(:decidim_user_id)).to_a
25
+
26
+ @valuators_for_select = valuator_roles.map do |role|
27
+ valuator = valuators.find { |user| user.id == role.decidim_user_id }
28
+
29
+ [valuator.name, role.id]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # This class contains helpers needed to get rankings for a given proposal
7
+ # ordered by some given criteria.
8
+ #
9
+ module ProposalRankingsHelper
10
+ # Public: Gets the ranking for a given proposal, ordered by some given
11
+ # criteria. Proposal is sorted amongst its own siblings.
12
+ #
13
+ # Returns a Hash with two keys:
14
+ # :ranking - an Integer representing the ranking for the given proposal.
15
+ # Ranking starts with 1.
16
+ # :total - an Integer representing the total number of ranked proposals.
17
+ #
18
+ # Examples:
19
+ # ranking_for(proposal, proposal_votes_count: :desc)
20
+ # ranking_for(proposal, proposal_endorsements_count: :desc)
21
+ def ranking_for(proposal, order = {})
22
+ siblings = Decidim::Proposals::Proposal.where(component: proposal.component)
23
+ ranked = siblings.order([order, id: :asc])
24
+ ranked_ids = ranked.pluck(:id)
25
+
26
+ { ranking: ranked_ids.index(proposal.id) + 1, total: ranked_ids.count }
27
+ end
28
+
29
+ # Public: Gets the ranking for a given proposal, ordered by endorsements.
30
+ def endorsements_ranking_for(proposal)
31
+ ranking_for(proposal, proposal_endorsements_count: :desc)
32
+ end
33
+
34
+ # Public: Gets the ranking for a given proposal, ordered by votes.
35
+ def votes_ranking_for(proposal)
36
+ ranking_for(proposal, proposal_votes_count: :desc)
37
+ end
38
+
39
+ def i18n_endorsements_ranking_for(proposal)
40
+ rankings = endorsements_ranking_for(proposal)
41
+
42
+ I18n.t(
43
+ "ranking",
44
+ scope: "decidim.proposals.admin.proposals.show",
45
+ ranking: rankings[:ranking],
46
+ total: rankings[:total]
47
+ )
48
+ end
49
+
50
+ def i18n_votes_ranking_for(proposal)
51
+ rankings = votes_ranking_for(proposal)
52
+
53
+ I18n.t(
54
+ "ranking",
55
+ scope: "decidim.proposals.admin.proposals.show",
56
+ ranking: rankings[:ranking],
57
+ total: rankings[:total]
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -14,6 +14,128 @@ module Decidim
14
14
 
15
15
  @meetings_as_authors_selected ||= @proposal.authors.pluck(:id)
16
16
  end
17
+
18
+ def coauthor_presenters_for(proposal)
19
+ proposal.authors.map do |identity|
20
+ if identity.is_a?(Decidim::Organization)
21
+ Decidim::Proposals::OfficialAuthorPresenter.new
22
+ else
23
+ present(identity)
24
+ end
25
+ end
26
+ end
27
+
28
+ def endorsers_presenters_for(proposal)
29
+ proposal.endorsements.for_listing.map { |identity| present(identity.normalized_author) }
30
+ end
31
+
32
+ def proposal_complete_state(proposal)
33
+ state = humanize_proposal_state(proposal.state)
34
+ state += " (#{humanize_proposal_state(proposal.internal_state)})" if proposal.answered? && !proposal.published_state?
35
+ state.html_safe
36
+ end
37
+
38
+ def proposals_admin_filter_tree
39
+ {
40
+ t("proposals.filters.type", scope: "decidim.proposals") => {
41
+ link_to(t("proposals", scope: "decidim.proposals.application_helper.filter_type_values"), q: ransak_params_for_query(is_emendation_true: "0"),
42
+ per_page: per_page) => nil,
43
+ link_to(t("amendments", scope: "decidim.proposals.application_helper.filter_type_values"), q: ransak_params_for_query(is_emendation_true: "1"),
44
+ per_page: per_page) => nil
45
+ },
46
+ t("models.proposal.fields.state", scope: "decidim.proposals") =>
47
+ Decidim::Proposals::Proposal::POSSIBLE_STATES.each_with_object({}) do |state, hash|
48
+ if state == "not_answered"
49
+ hash[link_to((humanize_proposal_state state), q: ransak_params_for_query(state_null: 1), per_page: per_page)] = nil
50
+ else
51
+ hash[link_to((humanize_proposal_state state), q: ransak_params_for_query(state_eq: state), per_page: per_page)] = nil
52
+ end
53
+ end,
54
+ t("models.proposal.fields.category", scope: "decidim.proposals") => admin_filter_categories_tree(categories.first_class),
55
+ t("proposals.filters.scope", scope: "decidim.proposals") => admin_filter_scopes_tree(current_component.organization.id)
56
+ }
57
+ end
58
+
59
+ def proposals_admin_search_hidden_params
60
+ return unless params[:q]
61
+
62
+ tags = ""
63
+ tags += hidden_field_tag "per_page", params[:per_page] if params[:per_page]
64
+ tags += hidden_field_tag "q[is_emendation_true]", params[:q][:is_emendation_true] if params[:q][:is_emendation_true]
65
+ tags += hidden_field_tag "q[state_eq]", params[:q][:state_eq] if params[:q][:state_eq]
66
+ tags += hidden_field_tag "q[category_id_eq]", params[:q][:category_id_eq] if params[:q][:category_id_eq]
67
+ tags += hidden_field_tag "q[scope_id_eq]", params[:q][:scope_id_eq] if params[:q][:scope_id_eq]
68
+ tags.html_safe
69
+ end
70
+
71
+ def proposals_admin_filter_applied_filters
72
+ html = []
73
+ if params[:q][:is_emendation_true].present?
74
+ html << content_tag(:span, class: "label secondary") do
75
+ tag = "#{t("filters.type", scope: "decidim.proposals.proposals")}: "
76
+ tag += if params[:q][:is_emendation_true].to_s == "1"
77
+ t("amendments", scope: "decidim.proposals.application_helper.filter_type_values")
78
+ else
79
+ t("proposals", scope: "decidim.proposals.application_helper.filter_type_values")
80
+ end
81
+ tag += icon_link_to("circle-x", url_for(q: ransak_params_for_query_without(:is_emendation_true), per_page: per_page), t("decidim.admin.actions.cancel"),
82
+ class: "action-icon--remove")
83
+ tag.html_safe
84
+ end
85
+ end
86
+ if params[:q][:state_null]
87
+ html << content_tag(:span, class: "label secondary") do
88
+ tag = "#{t("models.proposal.fields.state", scope: "decidim.proposals")}: "
89
+ tag += humanize_proposal_state "not_answered"
90
+ tag += icon_link_to("circle-x", url_for(q: ransak_params_for_query_without(:state_null), per_page: per_page), t("decidim.admin.actions.cancel"),
91
+ class: "action-icon--remove")
92
+ tag.html_safe
93
+ end
94
+ end
95
+ if params[:q][:state_eq]
96
+ html << content_tag(:span, class: "label secondary") do
97
+ tag = "#{t("models.proposal.fields.state", scope: "decidim.proposals")}: "
98
+ tag += humanize_proposal_state params[:q][:state_eq]
99
+ tag += icon_link_to("circle-x", url_for(q: ransak_params_for_query_without(:state_eq), per_page: per_page), t("decidim.admin.actions.cancel"),
100
+ class: "action-icon--remove")
101
+ tag.html_safe
102
+ end
103
+ end
104
+ if params[:q][:category_id_eq]
105
+ html << content_tag(:span, class: "label secondary") do
106
+ tag = "#{t("models.proposal.fields.category", scope: "decidim.proposals")}: "
107
+ tag += translated_attribute categories.find(params[:q][:category_id_eq]).name
108
+ tag += icon_link_to("circle-x", url_for(q: ransak_params_for_query_without(:category_id_eq), per_page: per_page), t("decidim.admin.actions.cancel"),
109
+ class: "action-icon--remove")
110
+ tag.html_safe
111
+ end
112
+ end
113
+ if params[:q][:scope_id_eq]
114
+ html << content_tag(:span, class: "label secondary") do
115
+ tag = "#{t("models.proposal.fields.scope", scope: "decidim.proposals")}: "
116
+ tag += translated_attribute Decidim::Scope.where(decidim_organization_id: current_component.organization.id).find(params[:q][:scope_id_eq]).name
117
+ tag += icon_link_to("circle-x", url_for(q: ransak_params_for_query_without(:scope_id_eq), per_page: per_page), t("decidim.admin.actions.cancel"),
118
+ class: "action-icon--remove")
119
+ tag.html_safe
120
+ end
121
+ end
122
+ html.join(" ").html_safe
123
+ end
124
+
125
+ def icon_with_link_to_proposal(proposal)
126
+ icon, tooltip = if allowed_to?(:create, :proposal_answer, proposal: proposal) && !proposal.emendation?
127
+ [
128
+ "comment-square",
129
+ t(:answer_proposal, scope: "decidim.proposals.actions")
130
+ ]
131
+ else
132
+ [
133
+ "info",
134
+ t(:show, scope: "decidim.proposals.actions")
135
+ ]
136
+ end
137
+ icon_link_to(icon, proposal_path(proposal), tooltip, class: "icon--small action-icon--show-proposal")
138
+ end
17
139
  end
18
140
  end
19
141
  end