decidim-proposals 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/admin/decidim_proposals_manifest.js +1 -0
  3. data/app/assets/images/decidim/gamification/badges/accepted_proposals.svg +1 -234
  4. data/app/assets/images/decidim/gamification/badges/proposal_votes.svg +1 -95
  5. data/app/assets/images/decidim/gamification/badges/proposals.svg +1 -229
  6. data/app/assets/images/decidim/proposals/icon.svg +1 -3
  7. data/app/assets/javascripts/decidim/proposals/admin/proposals_form.js.es6 +0 -5
  8. data/app/assets/javascripts/decidim/proposals/admin/proposals_picker.js.es6 +35 -0
  9. data/app/cells/decidim/proposals/collaborative_draft_link_to_proposal_cell.rb +1 -1
  10. data/app/cells/decidim/proposals/collaborative_draft_m/footer.erb +1 -1
  11. data/app/cells/decidim/proposals/collaborative_draft_m_cell.rb +1 -1
  12. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +3 -3
  13. data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
  14. data/app/cells/decidim/proposals/irreversible_action_modal/show.erb +2 -2
  15. data/app/cells/decidim/proposals/irreversible_action_modal_cell.rb +1 -1
  16. data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +1 -1
  17. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +1 -1
  18. data/app/cells/decidim/proposals/proposal_m/footer.erb +4 -1
  19. data/app/cells/decidim/proposals/proposal_m_cell.rb +28 -8
  20. data/app/cells/decidim/proposals/proposal_tags/show.erb +8 -2
  21. data/app/cells/decidim/proposals/proposals_picker/proposals.erb +12 -0
  22. data/app/cells/decidim/proposals/proposals_picker/show.erb +14 -0
  23. data/app/cells/decidim/proposals/proposals_picker_cell.rb +72 -0
  24. data/app/commands/decidim/proposals/admin/create_proposal.rb +1 -1
  25. data/app/commands/decidim/proposals/admin/create_proposal_note.rb +15 -0
  26. data/app/commands/decidim/proposals/admin/update_proposal.rb +1 -1
  27. data/app/commands/decidim/proposals/create_collaborative_draft.rb +1 -1
  28. data/app/commands/decidim/proposals/create_proposal.rb +1 -1
  29. data/app/commands/decidim/proposals/gallery_methods.rb +2 -51
  30. data/app/commands/decidim/proposals/update_proposal.rb +1 -1
  31. data/app/controllers/concerns/decidim/proposals/admin/picker.rb +21 -0
  32. data/app/controllers/concerns/decidim/proposals/orderable.rb +1 -1
  33. data/app/controllers/decidim/proposals/proposals_controller.rb +4 -5
  34. data/app/controllers/decidim/proposals/versions_controller.rb +8 -18
  35. data/app/events/decidim/proposals/admin/proposal_note_created_event.rb +27 -0
  36. data/app/forms/decidim/proposals/admin/participatory_text_proposal_form.rb +13 -0
  37. data/app/forms/decidim/proposals/admin/preview_participatory_text_form.rb +2 -2
  38. data/app/forms/decidim/proposals/admin/proposal_base_form.rb +129 -0
  39. data/app/forms/decidim/proposals/admin/proposal_form.rb +2 -120
  40. data/app/forms/decidim/proposals/proposal_form.rb +4 -0
  41. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +5 -1
  42. data/app/helpers/decidim/proposals/admin/filterable_helper.rb +2 -2
  43. data/app/helpers/decidim/proposals/admin/proposal_rankings_helper.rb +2 -2
  44. data/app/helpers/decidim/proposals/admin/proposals_picker_helper.rb +30 -0
  45. data/app/helpers/decidim/proposals/application_helper.rb +6 -6
  46. data/app/helpers/decidim/proposals/control_version_helper.rb +1 -37
  47. data/app/helpers/decidim/proposals/proposal_endorsements_helper.rb +0 -145
  48. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +2 -2
  49. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +24 -7
  50. data/app/helpers/decidim/proposals/proposals_helper.rb +6 -0
  51. data/app/models/decidim/proposals/proposal.rb +4 -14
  52. data/app/permissions/decidim/proposals/permissions.rb +1 -22
  53. data/app/presenters/decidim/proposals/proposal_presenter.rb +14 -2
  54. data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +1 -2
  55. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +15 -12
  56. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +5 -4
  57. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +2 -8
  58. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +3 -9
  59. data/app/services/decidim/proposals/diff_renderer.rb +2 -0
  60. data/app/services/decidim/proposals/proposal_builder.rb +1 -1
  61. data/app/services/decidim/proposals/proposal_search.rb +2 -2
  62. data/app/types/decidim/proposals/proposal_input_sort.rb +1 -7
  63. data/app/types/decidim/proposals/proposal_type.rb +1 -11
  64. data/app/types/decidim/proposals/proposals_type.rb +10 -0
  65. data/app/validators/proposal_length_validator.rb +38 -0
  66. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +9 -1
  67. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +2 -2
  68. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +3 -23
  69. data/app/views/decidim/proposals/admin/proposals/index.html.erb +1 -1
  70. data/app/views/decidim/proposals/admin/proposals/show.html.erb +1 -1
  71. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +6 -4
  72. data/app/views/decidim/proposals/collaborative_drafts/_filters.html.erb +9 -7
  73. data/app/views/decidim/proposals/collaborative_drafts/_new_collaborative_draft_button.html.erb +4 -4
  74. data/app/views/decidim/proposals/collaborative_drafts/_reject_request_access_form.html.erb +1 -1
  75. data/app/views/decidim/proposals/collaborative_drafts/_wizard_aside.html.erb +4 -2
  76. data/app/views/decidim/proposals/collaborative_drafts/compare.html.erb +2 -0
  77. data/app/views/decidim/proposals/collaborative_drafts/complete.html.erb +2 -0
  78. data/app/views/decidim/proposals/collaborative_drafts/edit.html.erb +3 -1
  79. data/app/views/decidim/proposals/collaborative_drafts/index.html.erb +4 -2
  80. data/app/views/decidim/proposals/collaborative_drafts/new.html.erb +4 -0
  81. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +29 -30
  82. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +5 -3
  83. data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +0 -16
  84. data/app/views/decidim/proposals/proposals/_filters.html.erb +12 -10
  85. data/app/views/decidim/proposals/proposals/_proposal_preview.html.erb +1 -11
  86. data/app/views/decidim/proposals/proposals/_proposal_similar.html.erb +2 -2
  87. data/app/views/decidim/proposals/proposals/_proposals.html.erb +14 -0
  88. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +13 -6
  89. data/app/views/decidim/proposals/proposals/_wizard_aside.html.erb +4 -2
  90. data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +4 -3
  91. data/app/views/decidim/proposals/proposals/compare.html.erb +2 -0
  92. data/app/views/decidim/proposals/proposals/complete.html.erb +2 -0
  93. data/app/views/decidim/proposals/proposals/edit.html.erb +3 -1
  94. data/app/views/decidim/proposals/proposals/edit_draft.html.erb +2 -0
  95. data/app/views/decidim/proposals/proposals/index.html.erb +5 -10
  96. data/app/views/decidim/proposals/proposals/new.html.erb +5 -1
  97. data/app/views/decidim/proposals/proposals/participatory_texts/_index.html.erb +1 -1
  98. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +10 -3
  99. data/app/views/decidim/proposals/proposals/participatory_texts/_view_index.html.erb +1 -1
  100. data/app/views/decidim/proposals/proposals/preview.html.erb +7 -8
  101. data/app/views/decidim/proposals/proposals/show.html.erb +52 -34
  102. data/app/views/decidim/proposals/versions/index.html.erb +14 -32
  103. data/app/views/decidim/proposals/versions/show.html.erb +16 -34
  104. data/config/locales/ar.yml +8 -64
  105. data/config/locales/bg-BG.yml +237 -0
  106. data/config/locales/ca.yml +68 -61
  107. data/config/locales/cs.yml +78 -68
  108. data/config/locales/da-DK.yml +1 -0
  109. data/config/locales/de.yml +142 -58
  110. data/config/locales/el.yml +875 -0
  111. data/config/locales/en.yml +74 -67
  112. data/config/locales/es-MX.yml +68 -61
  113. data/config/locales/es-PY.yml +68 -61
  114. data/config/locales/es.yml +68 -61
  115. data/config/locales/et-EE.yml +1 -0
  116. data/config/locales/eu.yml +5 -43
  117. data/config/locales/fi-plain.yml +68 -61
  118. data/config/locales/fi.yml +109 -102
  119. data/config/locales/fr-CA.yml +876 -0
  120. data/config/locales/fr.yml +128 -60
  121. data/config/locales/ga-IE.yml +1 -0
  122. data/config/locales/gl.yml +5 -43
  123. data/config/locales/hr-HR.yml +1 -0
  124. data/config/locales/hu.yml +38 -61
  125. data/config/locales/id-ID.yml +5 -42
  126. data/config/locales/is-IS.yml +1 -27
  127. data/config/locales/it.yml +136 -76
  128. data/config/locales/ja-JP.yml +886 -0
  129. data/config/locales/lt-LT.yml +1 -0
  130. data/config/locales/lv-LV.yml +858 -0
  131. data/config/locales/mt-MT.yml +1 -0
  132. data/config/locales/nl.yml +114 -59
  133. data/config/locales/no.yml +11 -64
  134. data/config/locales/pl.yml +170 -60
  135. data/config/locales/pt-BR.yml +6 -47
  136. data/config/locales/pt.yml +419 -331
  137. data/config/locales/ro-RO.yml +840 -0
  138. data/config/locales/ru.yml +1 -40
  139. data/config/locales/sk-SK.yml +896 -0
  140. data/config/locales/sk.yml +869 -0
  141. data/config/locales/sl.yml +26 -0
  142. data/config/locales/sr-CS.yml +126 -0
  143. data/config/locales/sv.yml +228 -156
  144. data/config/locales/tr-TR.yml +5 -43
  145. data/config/locales/uk.yml +1 -40
  146. data/db/migrate/20181003074440_fix_user_groups_ids_in_proposals_endorsements.rb +4 -0
  147. data/db/migrate/20191206154128_add_endorsements_counter_cache_to_proposals.rb +7 -0
  148. data/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb +52 -0
  149. data/db/migrate/20200730131631_move_proposal_endorsed_event_notifications_to_resource_endorsed_event.rb +20 -0
  150. data/lib/decidim/proposals/component.rb +8 -4
  151. data/lib/decidim/proposals/engine.rb +1 -5
  152. data/lib/decidim/proposals/test/capybara_proposals_picker.rb +49 -0
  153. data/lib/decidim/proposals/test/factories.rb +3 -12
  154. data/lib/decidim/proposals/version.rb +1 -1
  155. metadata +55 -34
  156. data/app/assets/javascripts/decidim/proposals/identity_selector_dialog.js.es6 +0 -56
  157. data/app/cells/decidim/proposals/endorsers_list/show.erb +0 -17
  158. data/app/cells/decidim/proposals/endorsers_list_cell.rb +0 -31
  159. data/app/commands/decidim/proposals/attachment_methods.rb +0 -43
  160. data/app/commands/decidim/proposals/endorse_proposal.rb +0 -59
  161. data/app/commands/decidim/proposals/unendorse_proposal.rb +0 -40
  162. data/app/controllers/decidim/proposals/proposal_endorsements_controller.rb +0 -60
  163. data/app/models/decidim/proposals/proposal_endorsement.rb +0 -37
  164. data/app/views/decidim/proposals/proposal_endorsements/_identity.html.erb +0 -9
  165. data/app/views/decidim/proposals/proposal_endorsements/identities.html.erb +0 -12
  166. data/app/views/decidim/proposals/proposal_endorsements/update_buttons_and_counters.js.erb +0 -20
  167. data/app/views/decidim/proposals/proposals/_endorsement_button.html.erb +0 -11
  168. data/app/views/decidim/proposals/proposals/_endorsement_identities_cabin.html.erb +0 -13
  169. data/app/views/decidim/proposals/versions/_version.html.erb +0 -20
@@ -4,7 +4,7 @@ module Decidim
4
4
  module Proposals
5
5
  # A command with all the business logic when a user updates a proposal.
6
6
  class UpdateProposal < Rectify::Command
7
- include AttachmentMethods
7
+ include ::Decidim::AttachmentMethods
8
8
  include HashtagsMethods
9
9
 
10
10
  # Public: Initializes the command.
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Proposals
7
+ module Admin
8
+ module Picker
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ helper Decidim::Proposals::Admin::ProposalsPickerHelper
13
+ end
14
+
15
+ def proposals_picker
16
+ render :proposals_picker, layout: false
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -46,7 +46,7 @@ module Decidim
46
46
  when "most_commented"
47
47
  proposals.left_joins(:comments).group(:id).order(Arel.sql("COUNT(decidim_comments_comments.id) DESC"))
48
48
  when "most_endorsed"
49
- proposals.order(proposal_endorsements_count: :desc)
49
+ proposals.order(endorsements_count: :desc)
50
50
  when "most_followed"
51
51
  proposals.left_joins(:follows).group(:id).order(Arel.sql("COUNT(decidim_follows.id) DESC"))
52
52
  when "most_voted"
@@ -37,8 +37,7 @@ module Decidim
37
37
  .results
38
38
  .published
39
39
  .not_hidden
40
- .includes(:category)
41
- .includes(:scope)
40
+ .includes(:amendable, :category, :component, :resource_permission, :scope)
42
41
 
43
42
  @voted_proposals = if current_user
44
43
  ProposalVote.where(
@@ -232,7 +231,7 @@ module Decidim
232
231
  def default_filter_category_params
233
232
  return "all" unless current_component.participatory_space.categories.any?
234
233
 
235
- ["all"] + current_component.participatory_space.categories.map { |category| category.id.to_s }
234
+ ["all"] + current_component.participatory_space.categories.pluck(:id).map(&:to_s)
236
235
  end
237
236
 
238
237
  def default_filter_scope_params
@@ -241,7 +240,7 @@ module Decidim
241
240
  if current_component.participatory_space.scope
242
241
  ["all", current_component.participatory_space.scope.id] + current_component.participatory_space.scope.children.map { |scope| scope.id.to_s }
243
242
  else
244
- %w(all global) + current_component.participatory_space.scopes.map { |scope| scope.id.to_s }
243
+ %w(all global) + current_component.participatory_space.scopes.pluck(:id).map(&:to_s)
245
244
  end
246
245
  end
247
246
 
@@ -285,7 +284,7 @@ module Decidim
285
284
  end
286
285
 
287
286
  def form_attachment_new
288
- form(AttachmentForm).from_params({})
287
+ form(AttachmentForm).from_model(Attachment.new)
289
288
  end
290
289
 
291
290
  def edit_form
@@ -5,26 +5,16 @@ module Decidim
5
5
  # Exposes Proposals versions so users can see how a Proposal/CollaborativeDraft
6
6
  # has been updated through time.
7
7
  class VersionsController < Decidim::Proposals::ApplicationController
8
- helper Decidim::TraceabilityHelper
9
-
10
8
  include Decidim::ApplicationHelper
9
+ include Decidim::ResourceVersionsConcern
11
10
 
12
- helper_method :current_version, :item
13
-
14
- private
15
-
16
- def item
17
- @item ||= if params[:proposal_id]
18
- present(Proposal.where(component: current_component).find(params[:proposal_id]))
19
- else
20
- CollaborativeDraft.where(component: current_component).find(params[:collaborative_draft_id])
21
- end
22
- end
23
-
24
- def current_version
25
- return nil unless params[:id].to_i.positive?
26
-
27
- @current_version ||= item.versions[params[:id].to_i - 1]
11
+ def versioned_resource
12
+ @versioned_resource ||=
13
+ if params[:proposal_id]
14
+ present(Proposal.where(component: current_component).find(params[:proposal_id]))
15
+ else
16
+ CollaborativeDraft.where(component: current_component).find(params[:collaborative_draft_id])
17
+ end
28
18
  end
29
19
  end
30
20
  end
@@ -0,0 +1,27 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ class ProposalNoteCreatedEvent < 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
+ decidim_admin_participatory_process_proposals.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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # A form object to be used when admin users want to create a proposal
7
+ # through the participatory texts.
8
+ class ParticipatoryTextProposalForm < Admin::ProposalBaseForm
9
+ validates :title, length: { maximum: 150 }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,11 +6,11 @@ module Decidim
6
6
  # A form object to be used when admin users want to review a collection of proposals
7
7
  # from a participatory text.
8
8
  class PreviewParticipatoryTextForm < Decidim::Form
9
- attribute :proposals, Array[Decidim::Proposals::Admin::ProposalForm]
9
+ attribute :proposals, Array[Decidim::Proposals::Admin::ParticipatoryTextProposalForm]
10
10
 
11
11
  def from_models(proposals)
12
12
  self.proposals = proposals.collect do |proposal|
13
- ProposalForm.from_model(proposal)
13
+ Admin::ParticipatoryTextProposalForm.from_model(proposal)
14
14
  end
15
15
  end
16
16
 
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # A form object to be used when admin users want to create a proposal.
7
+ class ProposalBaseForm < Decidim::Form
8
+ include Decidim::ApplicationHelper
9
+ mimic :proposal
10
+
11
+ attribute :title, String
12
+ attribute :body, String
13
+ attribute :address, String
14
+ attribute :latitude, Float
15
+ attribute :longitude, Float
16
+ attribute :category_id, Integer
17
+ attribute :scope_id, Integer
18
+ attribute :attachment, AttachmentForm
19
+ attribute :position, Integer
20
+ attribute :created_in_meeting, Boolean
21
+ attribute :meeting_id, Integer
22
+ attribute :suggested_hashtags, Array[String]
23
+ attribute :photos, Array[String]
24
+ attribute :add_photos, Array
25
+
26
+ validates :title, :body, presence: true
27
+ validates :address, geocoding: true, if: -> { current_component.settings.geocoding_enabled? }
28
+ validates :category, presence: true, if: ->(form) { form.category_id.present? }
29
+ validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
30
+ validates :meeting_as_author, presence: true, if: ->(form) { form.created_in_meeting? }
31
+
32
+ validate :scope_belongs_to_participatory_space_scope
33
+
34
+ validate :notify_missing_attachment_if_errored
35
+
36
+ delegate :categories, to: :current_component
37
+
38
+ def map_model(model)
39
+ return unless model.categorization
40
+
41
+ self.category_id = model.categorization.decidim_category_id
42
+ self.scope_id = model.decidim_scope_id
43
+
44
+ @suggested_hashtags = Decidim::ContentRenderers::HashtagRenderer.new(model.body).extra_hashtags.map(&:name).map(&:downcase)
45
+ end
46
+
47
+ alias component current_component
48
+
49
+ # Finds the Category from the category_id.
50
+ #
51
+ # Returns a Decidim::Category
52
+ def category
53
+ @category ||= categories.find_by(id: category_id)
54
+ end
55
+
56
+ # Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
57
+ #
58
+ # Returns a Decidim::Scope
59
+ def scope
60
+ @scope ||= @scope_id ? current_participatory_space.scopes.find_by(id: @scope_id) : current_participatory_space.scope
61
+ end
62
+
63
+ # Scope identifier
64
+ #
65
+ # Returns the scope identifier related to the proposal
66
+ def scope_id
67
+ @scope_id || scope&.id
68
+ end
69
+
70
+ # Finds the Meetings of the current participatory space
71
+ def meetings
72
+ @meetings ||= Decidim.find_resource_manifest(:meetings).try(:resource_scope, current_component)
73
+ &.order(title: :asc)
74
+ end
75
+
76
+ # Return the meeting as author
77
+ def meeting_as_author
78
+ @meeting_as_author ||= meetings.find_by(id: meeting_id)
79
+ end
80
+
81
+ def author
82
+ return current_organization unless created_in_meeting?
83
+
84
+ meeting_as_author
85
+ end
86
+
87
+ def extra_hashtags
88
+ @extra_hashtags ||= (component_automatic_hashtags + suggested_hashtags).uniq
89
+ end
90
+
91
+ def suggested_hashtags
92
+ downcased_suggested_hashtags = Array(@suggested_hashtags&.map(&:downcase)).to_set
93
+ component_suggested_hashtags.select { |hashtag| downcased_suggested_hashtags.member?(hashtag.downcase) }
94
+ end
95
+
96
+ def suggested_hashtag_checked?(hashtag)
97
+ suggested_hashtags.member?(hashtag)
98
+ end
99
+
100
+ def component_automatic_hashtags
101
+ @component_automatic_hashtags ||= ordered_hashtag_list(current_component.current_settings.automatic_hashtags)
102
+ end
103
+
104
+ def component_suggested_hashtags
105
+ @component_suggested_hashtags ||= ordered_hashtag_list(current_component.current_settings.suggested_hashtags)
106
+ end
107
+
108
+ private
109
+
110
+ def scope_belongs_to_participatory_space_scope
111
+ errors.add(:scope_id, :invalid) if current_participatory_space.out_of_scope?(scope)
112
+ end
113
+
114
+ # This method will add an error to the `attachment` field only if there's
115
+ # any error in any other field. This is needed because when the form has
116
+ # an error, the attachment is lost, so we need a way to inform the user of
117
+ # this problem.
118
+ def notify_missing_attachment_if_errored
119
+ errors.add(:attachment, :needs_to_be_reattached) if errors.any? && attachment.present?
120
+ errors.add(:add_photos, :needs_to_be_reattached) if errors.any? && add_photos.present?
121
+ end
122
+
123
+ def ordered_hashtag_list(string)
124
+ string.to_s.split.reject(&:blank?).uniq.sort_by(&:parameterize)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -4,126 +4,8 @@ module Decidim
4
4
  module Proposals
5
5
  module Admin
6
6
  # A form object to be used when admin users want to create a proposal.
7
- class ProposalForm < Decidim::Form
8
- include Decidim::ApplicationHelper
9
- mimic :proposal
10
-
11
- attribute :title, String
12
- attribute :body, String
13
- attribute :address, String
14
- attribute :latitude, Float
15
- attribute :longitude, Float
16
- attribute :category_id, Integer
17
- attribute :scope_id, Integer
18
- attribute :attachment, AttachmentForm
19
- attribute :position, Integer
20
- attribute :created_in_meeting, Boolean
21
- attribute :meeting_id, Integer
22
- attribute :suggested_hashtags, Array[String]
23
- attribute :photos, Array[String]
24
- attribute :add_photos, Array
25
-
26
- validates :title, :body, presence: true
27
- validates :title, length: { maximum: 150 }
28
- validates :address, geocoding: true, if: -> { current_component.settings.geocoding_enabled? }
29
- validates :category, presence: true, if: ->(form) { form.category_id.present? }
30
- validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
31
- validates :meeting_as_author, presence: true, if: ->(form) { form.created_in_meeting? }
32
-
33
- validate :scope_belongs_to_participatory_space_scope
34
-
35
- validate :notify_missing_attachment_if_errored
36
-
37
- delegate :categories, to: :current_component
38
-
39
- def map_model(model)
40
- return unless model.categorization
41
-
42
- self.category_id = model.categorization.decidim_category_id
43
- self.scope_id = model.decidim_scope_id
44
-
45
- @suggested_hashtags = Decidim::ContentRenderers::HashtagRenderer.new(model.body).extra_hashtags.map(&:name).map(&:downcase)
46
- end
47
-
48
- alias component current_component
49
-
50
- # Finds the Category from the category_id.
51
- #
52
- # Returns a Decidim::Category
53
- def category
54
- @category ||= categories.find_by(id: category_id)
55
- end
56
-
57
- # Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
58
- #
59
- # Returns a Decidim::Scope
60
- def scope
61
- @scope ||= @scope_id ? current_participatory_space.scopes.find_by(id: @scope_id) : current_participatory_space.scope
62
- end
63
-
64
- # Scope identifier
65
- #
66
- # Returns the scope identifier related to the proposal
67
- def scope_id
68
- @scope_id || scope&.id
69
- end
70
-
71
- # Finds the Meetings of the current participatory space
72
- def meetings
73
- @meetings ||= Decidim.find_resource_manifest(:meetings).try(:resource_scope, current_component)
74
- &.order(title: :asc)
75
- end
76
-
77
- # Return the meeting as author
78
- def meeting_as_author
79
- @meeting_as_author ||= meetings.find_by(id: meeting_id)
80
- end
81
-
82
- def author
83
- return current_organization unless created_in_meeting?
84
-
85
- meeting_as_author
86
- end
87
-
88
- def extra_hashtags
89
- @extra_hashtags ||= (component_automatic_hashtags + suggested_hashtags).uniq
90
- end
91
-
92
- def suggested_hashtags
93
- downcased_suggested_hashtags = Array(@suggested_hashtags&.map(&:downcase)).to_set
94
- component_suggested_hashtags.select { |hashtag| downcased_suggested_hashtags.member?(hashtag.downcase) }
95
- end
96
-
97
- def suggested_hashtag_checked?(hashtag)
98
- suggested_hashtags.member?(hashtag)
99
- end
100
-
101
- def component_automatic_hashtags
102
- @component_automatic_hashtags ||= ordered_hashtag_list(current_component.current_settings.automatic_hashtags)
103
- end
104
-
105
- def component_suggested_hashtags
106
- @component_suggested_hashtags ||= ordered_hashtag_list(current_component.current_settings.suggested_hashtags)
107
- end
108
-
109
- private
110
-
111
- def scope_belongs_to_participatory_space_scope
112
- errors.add(:scope_id, :invalid) if current_participatory_space.out_of_scope?(scope)
113
- end
114
-
115
- # This method will add an error to the `attachment` field only if there's
116
- # any error in any other field. This is needed because when the form has
117
- # an error, the attachment is lost, so we need a way to inform the user of
118
- # this problem.
119
- def notify_missing_attachment_if_errored
120
- errors.add(:attachment, :needs_to_be_reattached) if errors.any? && attachment.present?
121
- errors.add(:add_photos, :needs_to_be_reattached) if errors.any? && add_photos.present?
122
- end
123
-
124
- def ordered_hashtag_list(string)
125
- string.to_s.split.reject(&:blank?).uniq.sort_by(&:parameterize)
126
- end
7
+ class ProposalForm < Admin::ProposalBaseForm
8
+ validates :title, length: { in: 15..150 }
127
9
  end
128
10
  end
129
11
  end
@@ -30,6 +30,10 @@ module Decidim
30
30
  super
31
31
 
32
32
  @suggested_hashtags = Decidim::ContentRenderers::HashtagRenderer.new(model.body).extra_hashtags.map(&:name).map(&:downcase)
33
+
34
+ # The scope attribute is with different key (decidim_scope_id), so it
35
+ # has to be manually mapped.
36
+ self.scope_id = model.scope.id if model.scope
33
37
  end
34
38
 
35
39
  # Finds the Category from the category_id.
@@ -12,7 +12,11 @@ module Decidim
12
12
  attribute :user_group_id, Integer
13
13
 
14
14
  validates :title, :body, presence: true, etiquette: true
15
- validates :title, length: { maximum: 150 }
15
+ validates :title, length: { in: 15..150 }
16
+ validates :body, proposal_length: {
17
+ minimum: 15,
18
+ maximum: ->(record) { record.component.settings.proposal_length }
19
+ }
16
20
 
17
21
  validate :proposal_length
18
22
  validate :body_is_not_bare_template