decidim-proposals 0.26.0 → 0.27.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/collaborative_draft_cell.rb +1 -1
  3. data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +9 -1
  4. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +1 -1
  5. data/app/cells/decidim/proposals/proposal_cell.rb +1 -1
  6. data/app/cells/decidim/proposals/proposal_m_cell.rb +8 -8
  7. data/app/commands/decidim/proposals/accept_access_to_collaborative_draft.rb +1 -1
  8. data/app/commands/decidim/proposals/admin/answer_proposal.rb +8 -3
  9. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +1 -1
  10. data/app/commands/decidim/proposals/admin/create_proposal.rb +9 -3
  11. data/app/commands/decidim/proposals/admin/create_proposal_note.rb +2 -2
  12. data/app/commands/decidim/proposals/admin/discard_participatory_text.rb +1 -1
  13. data/app/commands/decidim/proposals/admin/import_participatory_text.rb +1 -1
  14. data/app/commands/decidim/proposals/admin/import_proposals.rb +1 -1
  15. data/app/commands/decidim/proposals/admin/merge_proposals.rb +1 -1
  16. data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +1 -1
  17. data/app/commands/decidim/proposals/admin/publish_answers.rb +1 -1
  18. data/app/commands/decidim/proposals/admin/split_proposals.rb +1 -1
  19. data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +1 -1
  20. data/app/commands/decidim/proposals/admin/update_participatory_text.rb +1 -1
  21. data/app/commands/decidim/proposals/admin/update_proposal.rb +9 -3
  22. data/app/commands/decidim/proposals/admin/update_proposal_category.rb +5 -3
  23. data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +3 -3
  24. data/app/commands/decidim/proposals/create_collaborative_draft.rb +5 -5
  25. data/app/commands/decidim/proposals/create_proposal.rb +1 -1
  26. data/app/commands/decidim/proposals/destroy_proposal.rb +1 -1
  27. data/app/commands/decidim/proposals/hashtags_methods.rb +1 -1
  28. data/app/commands/decidim/proposals/publish_collaborative_draft.rb +1 -1
  29. data/app/commands/decidim/proposals/publish_proposal.rb +1 -1
  30. data/app/commands/decidim/proposals/reject_access_to_collaborative_draft.rb +1 -1
  31. data/app/commands/decidim/proposals/request_access_to_collaborative_draft.rb +1 -1
  32. data/app/commands/decidim/proposals/unvote_proposal.rb +1 -1
  33. data/app/commands/decidim/proposals/update_collaborative_draft.rb +1 -1
  34. data/app/commands/decidim/proposals/update_proposal.rb +8 -2
  35. data/app/commands/decidim/proposals/vote_proposal.rb +1 -1
  36. data/app/commands/decidim/proposals/withdraw_collaborative_draft.rb +1 -1
  37. data/app/commands/decidim/proposals/withdraw_proposal.rb +1 -1
  38. data/app/controllers/concerns/decidim/proposals/orderable.rb +7 -5
  39. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +7 -7
  40. data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +8 -8
  41. data/app/controllers/decidim/proposals/proposal_votes_controller.rb +1 -1
  42. data/app/controllers/decidim/proposals/proposals_controller.rb +24 -17
  43. data/app/events/decidim/proposals/collaborative_draft_withdrawn_event.rb +5 -1
  44. data/app/forms/decidim/proposals/admin/import_participatory_text_form.rb +4 -4
  45. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +1 -1
  46. data/app/forms/decidim/proposals/admin/proposal_base_form.rb +5 -5
  47. data/app/forms/decidim/proposals/admin/proposal_form.rb +2 -0
  48. data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +2 -2
  49. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +1 -1
  50. data/app/forms/decidim/proposals/proposal_form.rb +11 -4
  51. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +3 -0
  52. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +4 -0
  53. data/app/helpers/decidim/proposals/application_helper.rb +5 -7
  54. data/app/helpers/decidim/proposals/proposal_cells_helper.rb +1 -3
  55. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +7 -7
  56. data/app/models/decidim/proposals/collaborative_draft.rb +10 -0
  57. data/app/models/decidim/proposals/collaborative_draft_collaborator_request.rb +0 -2
  58. data/app/models/decidim/proposals/proposal.rb +53 -8
  59. data/app/permissions/decidim/proposals/admin/permissions.rb +3 -0
  60. data/app/queries/decidim/proposals/filtered_proposals.rb +1 -1
  61. data/app/queries/decidim/proposals/similar_proposals.rb +1 -1
  62. data/app/services/decidim/proposals/proposal_search.rb +16 -71
  63. data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +1 -1
  64. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
  65. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
  66. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +26 -0
  67. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +8 -16
  68. data/app/views/decidim/proposals/collaborative_drafts/_filters.html.erb +4 -4
  69. data/app/views/decidim/proposals/collaborative_drafts/edit.html.erb +3 -1
  70. data/app/views/decidim/proposals/collaborative_drafts/new.html.erb +3 -1
  71. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +13 -36
  72. data/app/views/decidim/proposals/proposals/_filters.html.erb +5 -5
  73. data/app/views/decidim/proposals/proposals/_proposal_similar.html.erb +1 -1
  74. data/app/views/decidim/proposals/proposals/_proposals.html.erb +5 -5
  75. data/app/views/decidim/proposals/proposals/complete.html.erb +3 -1
  76. data/app/views/decidim/proposals/proposals/edit.html.erb +3 -1
  77. data/app/views/decidim/proposals/proposals/edit_draft.html.erb +3 -1
  78. data/app/views/decidim/proposals/proposals/index.html.erb +2 -2
  79. data/app/views/decidim/proposals/proposals/new.html.erb +3 -1
  80. data/app/views/decidim/proposals/proposals/show.html.erb +3 -3
  81. data/config/locales/am-ET.yml +1 -0
  82. data/config/locales/ar.yml +3 -18
  83. data/config/locales/bg.yml +1 -16
  84. data/config/locales/ca.yml +23 -18
  85. data/config/locales/cs.yml +23 -18
  86. data/config/locales/da.yml +1 -0
  87. data/config/locales/de.yml +1 -22
  88. data/config/locales/el.yml +1 -18
  89. data/config/locales/en.yml +23 -19
  90. data/config/locales/eo.yml +1 -0
  91. data/config/locales/es-MX.yml +23 -18
  92. data/config/locales/es-PY.yml +23 -18
  93. data/config/locales/es.yml +24 -19
  94. data/config/locales/et.yml +1 -0
  95. data/config/locales/eu.yml +1 -22
  96. data/config/locales/fi-plain.yml +23 -18
  97. data/config/locales/fi.yml +25 -20
  98. data/config/locales/fr-CA.yml +22 -18
  99. data/config/locales/fr.yml +37 -33
  100. data/config/locales/ga-IE.yml +1 -2
  101. data/config/locales/gl.yml +8 -22
  102. data/config/locales/hr.yml +1 -0
  103. data/config/locales/hu.yml +9 -18
  104. data/config/locales/id-ID.yml +1 -18
  105. data/config/locales/is-IS.yml +2 -3
  106. data/config/locales/it.yml +2 -22
  107. data/config/locales/ja.yml +24 -19
  108. data/config/locales/ko.yml +1 -0
  109. data/config/locales/lb.yml +1 -0
  110. data/config/locales/lt.yml +1 -0
  111. data/config/locales/lv.yml +1 -18
  112. data/config/locales/mt.yml +1 -0
  113. data/config/locales/nl.yml +2 -22
  114. data/config/locales/no.yml +1 -18
  115. data/config/locales/om-ET.yml +1 -0
  116. data/config/locales/pl.yml +1 -22
  117. data/config/locales/pt-BR.yml +2 -23
  118. data/config/locales/pt.yml +1 -22
  119. data/config/locales/ro-RO.yml +2 -22
  120. data/config/locales/ru.yml +1 -2
  121. data/config/locales/si-LK.yml +1 -0
  122. data/config/locales/sk.yml +1 -18
  123. data/config/locales/sl.yml +1 -0
  124. data/config/locales/so-SO.yml +1 -0
  125. data/config/locales/sr-CS.yml +1 -0
  126. data/config/locales/sv.yml +10 -27
  127. data/config/locales/sw-KE.yml +1 -0
  128. data/config/locales/ti-ER.yml +1 -0
  129. data/config/locales/tr-TR.yml +1 -22
  130. data/config/locales/uk.yml +1 -2
  131. data/config/locales/val-ES.yml +1 -0
  132. data/config/locales/vi.yml +1 -0
  133. data/config/locales/zh-CN.yml +1 -21
  134. data/config/locales/zh-TW.yml +1 -0
  135. data/db/migrate/20180529110230_move_authorships_to_coauthorships.rb +1 -0
  136. data/lib/decidim/content_parsers/proposal_parser.rb +7 -63
  137. data/lib/decidim/content_renderers/proposal_renderer.rb +3 -19
  138. data/lib/decidim/proposals/component.rb +22 -16
  139. data/lib/decidim/proposals/import/proposal_answer_creator.rb +21 -19
  140. data/lib/decidim/proposals/import/proposal_creator.rb +4 -1
  141. data/lib/decidim/proposals/test/factories.rb +1 -1
  142. data/lib/decidim/proposals/version.rb +1 -1
  143. metadata +25 -28
  144. data/app/services/decidim/proposals/collaborative_draft_search.rb +0 -59
  145. data/app/views/decidim/proposals/admin/proposals/_js-callout.html.erb +0 -6
  146. data/app/views/decidim/proposals/admin/proposals/update_category.js.erb +0 -26
  147. data/app/views/decidim/proposals/admin/proposals/update_scope.js.erb +0 -27
@@ -25,7 +25,7 @@ module Decidim
25
25
 
26
26
  def index
27
27
  @collaborative_drafts = search
28
- .results
28
+ .result
29
29
  .not_hidden
30
30
  .includes(:category)
31
31
  .includes(:scope)
@@ -134,19 +134,19 @@ module Decidim
134
134
  end
135
135
 
136
136
  def geocoded_collaborative_draft
137
- @geocoded_collaborative_draft ||= search.results.not_hidden.select(&:geocoded_and_valid?)
137
+ @geocoded_collaborative_draft ||= search.result.not_hidden.select(&:geocoded_and_valid?)
138
138
  end
139
139
 
140
- def search_klass
141
- CollaborativeDraftSearch
140
+ def search_collection
141
+ CollaborativeDraft.where(component: current_component).not_hidden
142
142
  end
143
143
 
144
144
  def default_filter_params
145
145
  {
146
- search_text: "",
147
- category_id: default_filter_category_params,
148
- state: %w(open),
149
- scope_id: default_filter_scope_params,
146
+ search_text_cont: "",
147
+ with_any_category: default_filter_category_params,
148
+ with_any_state: %w(open),
149
+ with_any_scope: default_filter_scope_params,
150
150
  related_to: ""
151
151
  }
152
152
  end
@@ -5,7 +5,7 @@ module Decidim
5
5
  # Exposes the proposal vote resource so users can vote proposals.
6
6
  class ProposalVotesController < Decidim::Proposals::ApplicationController
7
7
  include ProposalVotesHelper
8
- include Rectify::ControllerHelpers
8
+ include Decidim::ControllerHelpers
9
9
 
10
10
  helper_method :proposal
11
11
 
@@ -25,6 +25,13 @@ module Decidim
25
25
 
26
26
  before_action :set_participatory_text
27
27
 
28
+ # rubocop:disable Naming/VariableNumber
29
+ STEP1 = :step_1
30
+ STEP2 = :step_2
31
+ STEP3 = :step_3
32
+ STEP4 = :step_4
33
+ # rubocop:enable Naming/VariableNumber
34
+
28
35
  def index
29
36
  if component_settings.participatory_texts_enabled?
30
37
  @proposals = Decidim::Proposals::Proposal
@@ -37,7 +44,7 @@ module Decidim
37
44
  render "decidim/proposals/proposals/participatory_texts/participatory_text"
38
45
  else
39
46
  @base_query = search
40
- .results
47
+ .result
41
48
  .published
42
49
  .not_hidden
43
50
 
@@ -63,7 +70,7 @@ module Decidim
63
70
 
64
71
  def new
65
72
  enforce_permission_to :create, :proposal
66
- @step = :step_1
73
+ @step = STEP1
67
74
  if proposal_draft.present?
68
75
  redirect_to edit_draft_proposal_path(proposal_draft, component_id: proposal_draft.component.id, question_slug: proposal_draft.component.participatory_space.slug)
69
76
  else
@@ -73,7 +80,7 @@ module Decidim
73
80
 
74
81
  def create
75
82
  enforce_permission_to :create, :proposal
76
- @step = :step_1
83
+ @step = STEP1
77
84
  @form = form(ProposalWizardCreateStepForm).from_params(proposal_creation_params)
78
85
 
79
86
  CreateProposal.call(@form, current_user) do
@@ -92,7 +99,7 @@ module Decidim
92
99
 
93
100
  def compare
94
101
  enforce_permission_to :edit, :proposal, proposal: @proposal
95
- @step = :step_2
102
+ @step = STEP2
96
103
  @similar_proposals ||= Decidim::Proposals::SimilarProposals
97
104
  .for(current_component, @proposal)
98
105
  .all
@@ -105,7 +112,7 @@ module Decidim
105
112
 
106
113
  def complete
107
114
  enforce_permission_to :edit, :proposal, proposal: @proposal
108
- @step = :step_3
115
+ @step = STEP3
109
116
 
110
117
  @form = form_proposal_model
111
118
 
@@ -114,13 +121,13 @@ module Decidim
114
121
 
115
122
  def preview
116
123
  enforce_permission_to :edit, :proposal, proposal: @proposal
117
- @step = :step_4
124
+ @step = STEP4
118
125
  @form = form(ProposalForm).from_model(@proposal)
119
126
  end
120
127
 
121
128
  def publish
122
129
  enforce_permission_to :edit, :proposal, proposal: @proposal
123
- @step = :step_4
130
+ @step = STEP4
124
131
  PublishProposal.call(@proposal, current_user) do
125
132
  on(:ok) do
126
133
  flash[:notice] = I18n.t("proposals.publish.success", scope: "decidim")
@@ -135,12 +142,12 @@ module Decidim
135
142
  end
136
143
 
137
144
  def edit_draft
138
- @step = :step_3
145
+ @step = STEP3
139
146
  enforce_permission_to :edit, :proposal, proposal: @proposal
140
147
  end
141
148
 
142
149
  def update_draft
143
- @step = :step_1
150
+ @step = STEP1
144
151
  enforce_permission_to :edit, :proposal, proposal: @proposal
145
152
 
146
153
  @form = form_proposal_params
@@ -211,25 +218,25 @@ module Decidim
211
218
 
212
219
  private
213
220
 
214
- def search_klass
215
- ProposalSearch
221
+ def search_collection
222
+ Proposal.where(component: current_component).published.not_hidden.with_availability(params[:filter].try(:[], :with_availability))
216
223
  end
217
224
 
218
225
  def default_filter_params
219
226
  {
220
- search_text: "",
221
- origin: default_filter_origin_params,
227
+ search_text_cont: "",
228
+ with_any_origin: default_filter_origin_params,
222
229
  activity: "all",
223
- category_id: default_filter_category_params,
224
- state: %w(accepted evaluating state_not_published),
225
- scope_id: default_filter_scope_params,
230
+ with_any_category: default_filter_category_params,
231
+ with_any_state: %w(accepted evaluating state_not_published),
232
+ with_any_scope: default_filter_scope_params,
226
233
  related_to: "",
227
234
  type: "all"
228
235
  }
229
236
  end
230
237
 
231
238
  def default_filter_origin_params
232
- filter_origin_params = %w(citizens meeting)
239
+ filter_origin_params = %w(participants meeting)
233
240
  filter_origin_params << "official" if component_settings.official_proposals_enabled
234
241
  filter_origin_params << "user_group" if current_organization.user_groups_enabled?
235
242
  filter_origin_params
@@ -3,7 +3,7 @@
3
3
  module Decidim
4
4
  module Proposals
5
5
  class CollaborativeDraftWithdrawnEvent < Decidim::Events::SimpleEvent
6
- i18n_attributes :author_nickname, :author_name, :author_path
6
+ i18n_attributes :author_nickname, :author_name, :author_path, :author_url
7
7
 
8
8
  delegate :nickname, :name, to: :author, prefix: true
9
9
 
@@ -15,6 +15,10 @@ module Decidim
15
15
  author.profile_path
16
16
  end
17
17
 
18
+ def author_url
19
+ author.profile_url
20
+ end
21
+
18
22
  private
19
23
 
20
24
  def author
@@ -15,7 +15,7 @@ module Decidim
15
15
 
16
16
  translatable_attribute :title, String
17
17
  translatable_attribute :description, String
18
- attribute :document
18
+ attribute :document, Decidim::Attributes::Blob
19
19
 
20
20
  validates :title, translatable_presence: true
21
21
  validates :document, presence: true, if: :new_participatory_text?
@@ -44,9 +44,9 @@ module Decidim
44
44
  end
45
45
 
46
46
  def i18n_invalid_document_type_text
47
- I18n.t("invalid_document_type",
47
+ I18n.t("allowed_file_content_types",
48
48
  scope: "activemodel.errors.models.participatory_text.attributes.document",
49
- valid_mime_types: i18n_valid_mime_types_text)
49
+ types: i18n_valid_mime_types_text)
50
50
  end
51
51
 
52
52
  def i18n_valid_mime_types_text
@@ -60,7 +60,7 @@ module Decidim
60
60
  end
61
61
 
62
62
  def document_text
63
- @document_text ||= document&.read
63
+ @document_text ||= document&.download
64
64
  end
65
65
  end
66
66
  end
@@ -14,7 +14,7 @@ module Decidim
14
14
  attribute :cost, Float
15
15
  attribute :internal_state, String
16
16
 
17
- validates :internal_state, presence: true, inclusion: { in: %w(accepted rejected evaluating) }
17
+ validates :internal_state, presence: true, inclusion: { in: %w(not_answered accepted rejected evaluating) }
18
18
  validates :answer, translatable_presence: true, if: ->(form) { form.state == "rejected" }
19
19
 
20
20
  with_options if: :costs_required? do
@@ -57,14 +57,14 @@ module Decidim
57
57
  #
58
58
  # Returns a Decidim::Scope
59
59
  def scope
60
- @scope ||= @scope_id ? current_component.scopes.find_by(id: @scope_id) : current_component.scope
60
+ @scope ||= @attributes["scope_id"].value ? current_component.scopes.find_by(id: @attributes["scope_id"].value) : current_component.scope
61
61
  end
62
62
 
63
63
  # Scope identifier
64
64
  #
65
65
  # Returns the scope identifier related to the proposal
66
66
  def scope_id
67
- @scope_id || scope&.id
67
+ super || scope&.id
68
68
  end
69
69
 
70
70
  def geocoding_enabled?
@@ -82,7 +82,7 @@ module Decidim
82
82
  # Finds the Meetings of the current participatory space
83
83
  def meetings
84
84
  @meetings ||= Decidim.find_resource_manifest(:meetings).try(:resource_scope, current_component)
85
- &.order(title: :asc)
85
+ &.published&.order(title: :asc)
86
86
  end
87
87
 
88
88
  # Return the meeting as author
@@ -101,7 +101,7 @@ module Decidim
101
101
  end
102
102
 
103
103
  def suggested_hashtags
104
- downcased_suggested_hashtags = Array(@suggested_hashtags&.map(&:downcase)).to_set
104
+ downcased_suggested_hashtags = super.map(&:downcase).to_set
105
105
  component_suggested_hashtags.select { |hashtag| downcased_suggested_hashtags.member?(hashtag.downcase) }
106
106
  end
107
107
 
@@ -129,7 +129,7 @@ module Decidim
129
129
  end
130
130
 
131
131
  def ordered_hashtag_list(string)
132
- string.to_s.split.reject(&:blank?).uniq.sort_by(&:parameterize)
132
+ string.to_s.split.compact_blank.uniq.sort_by(&:parameterize)
133
133
  end
134
134
  end
135
135
  end
@@ -5,6 +5,8 @@ module Decidim
5
5
  module Admin
6
6
  # A form object to be used when admin users want to create a proposal.
7
7
  class ProposalForm < Decidim::Proposals::Admin::ProposalBaseForm
8
+ include Decidim::HasUploadValidations
9
+
8
10
  translatable_attribute :title, String do |field, _locale|
9
11
  validates field, length: { in: 15..150 }, if: proc { |resource| resource.send(field).present? }
10
12
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
  class ProposalsForkForm < Decidim::Form
8
8
  mimic :proposals_import
9
9
 
10
- attribute :target_component_id, Integer
10
+ attribute :target_component_id, Array[Integer]
11
11
  attribute :proposal_ids, Array
12
12
 
13
13
  validates :target_component, :proposals, :current_component, presence: true
@@ -55,7 +55,7 @@ module Decidim
55
55
  #
56
56
  # We receive this as ["id"] since it's from a select in a form.
57
57
  def clean_target_component_id
58
- target_component_id.first.to_i
58
+ target_component_id.first
59
59
  end
60
60
  end
61
61
  end
@@ -31,7 +31,7 @@ module Decidim
31
31
  end
32
32
 
33
33
  def states
34
- super.reject(&:blank?)
34
+ super.compact_blank
35
35
  end
36
36
 
37
37
  def scopes
@@ -6,6 +6,7 @@ module Decidim
6
6
  class ProposalForm < Decidim::Proposals::ProposalWizardCreateStepForm
7
7
  include Decidim::TranslatableAttributes
8
8
  include Decidim::AttachmentAttributes
9
+ include Decidim::HasUploadValidations
9
10
 
10
11
  mimic :proposal
11
12
 
@@ -41,6 +42,12 @@ module Decidim
41
42
  self.scope_id = model.scope.id if model.scope
42
43
 
43
44
  self.has_address = true if model.address.present?
45
+
46
+ # Proposals have the "photos" field reserved for the proposal card image
47
+ # so we don't want to show all photos there. Instead, only show the
48
+ # first photo.
49
+ self.photos = [model.photo].compact.select { |p| p.weight.zero? }
50
+ self.documents = model.attachments - photos
44
51
  end
45
52
 
46
53
  # Finds the Category from the category_id.
@@ -54,14 +61,14 @@ module Decidim
54
61
  #
55
62
  # Returns a Decidim::Scope
56
63
  def scope
57
- @scope ||= @scope_id ? current_component.scopes.find_by(id: @scope_id) : current_component.scope
64
+ @scope ||= @attributes["scope_id"].value ? current_component.scopes.find_by(id: @attributes["scope_id"].value) : current_component.scope
58
65
  end
59
66
 
60
67
  # Scope identifier
61
68
  #
62
69
  # Returns the scope identifier related to the proposal
63
70
  def scope_id
64
- @scope_id || scope&.id
71
+ super || scope&.id
65
72
  end
66
73
 
67
74
  def geocoding_enabled?
@@ -89,7 +96,7 @@ module Decidim
89
96
  end
90
97
 
91
98
  def suggested_hashtags
92
- downcased_suggested_hashtags = Array(@suggested_hashtags&.map(&:downcase)).to_set
99
+ downcased_suggested_hashtags = super.map(&:downcase).to_set
93
100
  component_suggested_hashtags.select { |hashtag| downcased_suggested_hashtags.member?(hashtag.downcase) }
94
101
  end
95
102
 
@@ -119,7 +126,7 @@ module Decidim
119
126
  end
120
127
 
121
128
  def ordered_hashtag_list(string)
122
- string.to_s.split.reject(&:blank?).uniq.sort_by(&:parameterize)
129
+ string.to_s.split.compact_blank.uniq.sort_by(&:parameterize)
123
130
  end
124
131
  end
125
132
  end
@@ -23,6 +23,9 @@ module Decidim
23
23
  alias component current_component
24
24
 
25
25
  def map_model(model)
26
+ self.title = translated_attribute(model.title)
27
+ self.body = translated_attribute(model.body)
28
+
26
29
  self.user_group_id = model.user_groups.first&.id
27
30
  return unless model.categorization
28
31
 
@@ -4,6 +4,10 @@ module Decidim
4
4
  module Proposals
5
5
  module Admin
6
6
  module ProposalBulkActionsHelper
7
+ def proposal_find(id)
8
+ Decidim::Proposals::Proposal.find(id)
9
+ end
10
+
7
11
  # Public: Generates a select field with the valuators of the given participatory space.
8
12
  #
9
13
  # participatory_space - A participatory space instance.
@@ -40,12 +40,10 @@ module Decidim
40
40
  case state
41
41
  when "accepted"
42
42
  "text-success"
43
- when "rejected"
43
+ when "rejected", "withdrawn"
44
44
  "text-alert"
45
45
  when "evaluating"
46
46
  "text-warning"
47
- when "withdrawn"
48
- "text-alert"
49
47
  else
50
48
  "text-info"
51
49
  end
@@ -96,13 +94,13 @@ module Decidim
96
94
  # frontend, the proposal body is considered as safe content; that's unless
97
95
  # the proposal comes from a collaborative_draft or a participatory_text.
98
96
  def safe_content?
99
- rich_text_editor_in_public_views? && not_from_collaborative_draft(@proposal) ||
100
- (@proposal.official? || @proposal.official_meeting?) && not_from_participatory_text(@proposal)
97
+ (rich_text_editor_in_public_views? && not_from_collaborative_draft(@proposal)) ||
98
+ ((@proposal.official? || @proposal.official_meeting?) && not_from_participatory_text(@proposal))
101
99
  end
102
100
 
103
101
  # If the content is safe, HTML tags are sanitized, otherwise, they are stripped.
104
102
  def render_proposal_body(proposal)
105
- render_sanitized_content(proposal, :body)
103
+ Decidim::ContentProcessor.render(render_sanitized_content(proposal, :body), "div")
106
104
  end
107
105
 
108
106
  # Returns :text_area or :editor based on the organization' settings.
@@ -172,7 +170,7 @@ module Decidim
172
170
  def filter_origin_values
173
171
  origin_values = []
174
172
  origin_values << TreePoint.new("official", t("decidim.proposals.application_helper.filter_origin_values.official")) if component_settings.official_proposals_enabled
175
- origin_values << TreePoint.new("citizens", t("decidim.proposals.application_helper.filter_origin_values.citizens"))
173
+ origin_values << TreePoint.new("participants", t("decidim.proposals.application_helper.filter_origin_values.participants"))
176
174
  origin_values << TreePoint.new("user_group", t("decidim.proposals.application_helper.filter_origin_values.user_groups")) if current_organization.user_groups_enabled?
177
175
  origin_values << TreePoint.new("meeting", t("decidim.proposals.application_helper.filter_origin_values.meetings"))
178
176
 
@@ -60,12 +60,10 @@ module Decidim
60
60
  case state
61
61
  when "accepted"
62
62
  ["success"]
63
- when "rejected"
63
+ when "rejected", "withdrawn"
64
64
  ["alert"]
65
65
  when "evaluating"
66
66
  ["warning"]
67
- when "withdrawn"
68
- ["alert"]
69
67
  else
70
68
  ["muted"]
71
69
  end
@@ -73,10 +73,10 @@ module Decidim
73
73
  def proposal_wizard_stepper(current_step)
74
74
  content_tag :ol, class: "wizard__steps" do
75
75
  %(
76
- #{proposal_wizard_stepper_step(:step_1, current_step)}
77
- #{proposal_wizard_stepper_step(:step_2, current_step)}
78
- #{proposal_wizard_stepper_step(:step_3, current_step)}
79
- #{proposal_wizard_stepper_step(:step_4, current_step)}
76
+ #{proposal_wizard_stepper_step(ProposalsController::STEP1, current_step)}
77
+ #{proposal_wizard_stepper_step(ProposalsController::STEP2, current_step)}
78
+ #{proposal_wizard_stepper_step(ProposalsController::STEP3, current_step)}
79
+ #{proposal_wizard_stepper_step(ProposalsController::STEP4, current_step)}
80
80
  ).html_safe
81
81
  end
82
82
  end
@@ -120,11 +120,11 @@ module Decidim
120
120
  # Renders the back link except for step_2: compare
121
121
  def proposal_wizard_aside_link_to_back(step)
122
122
  case step
123
- when :step_1
123
+ when ProposalsController::STEP1
124
124
  proposals_path
125
- when :step_3
125
+ when ProposalsController::STEP3
126
126
  compare_proposal_path
127
- when :step_4
127
+ when ProposalsController::STEP4
128
128
  edit_draft_proposal_path
129
129
  end
130
130
  end
@@ -16,6 +16,7 @@ module Decidim
16
16
  include Decidim::Traceable
17
17
  include Decidim::Loggable
18
18
  include Decidim::Randomable
19
+ include Decidim::FilterableResource
19
20
 
20
21
  has_many :collaborator_requests,
21
22
  class_name: "Decidim::Proposals::CollaborativeDraftCollaboratorRequest",
@@ -35,6 +36,8 @@ module Decidim
35
36
  scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
36
37
  scope :published, -> { where(state: "published") }
37
38
 
39
+ scope_search_multi :with_any_state, [:open, :published, :withdrawn]
40
+
38
41
  # Checks whether the user can edit the given proposal.
39
42
  #
40
43
  # user - the user to check for authorship
@@ -68,6 +71,13 @@ module Decidim
68
71
  def reported_searchable_content_extras
69
72
  [authors.map(&:name).join("\n")]
70
73
  end
74
+
75
+ # Create the :search_text ransacker alias for searching from both :title or :body.
76
+ ransacker_text_multi :search_text, [:title, :body]
77
+
78
+ def self.ransackable_scopes(_auth_object = nil)
79
+ [:with_any_state, :related_to, :with_any_scope, :with_any_category]
80
+ end
71
81
  end
72
82
  end
73
83
  end
@@ -4,8 +4,6 @@ module Decidim
4
4
  module Proposals
5
5
  # A collaborative_draft can accept requests to coauthor and contribute
6
6
  class CollaborativeDraftCollaboratorRequest < Proposals::ApplicationRecord
7
- validates :collaborative_draft, :user, presence: true
8
-
9
7
  belongs_to :collaborative_draft, class_name: "Decidim::Proposals::CollaborativeDraft", foreign_key: :decidim_proposals_collaborative_draft_id
10
8
  belongs_to :user, class_name: "Decidim::User", foreign_key: :decidim_user_id
11
9
  end
@@ -18,7 +18,7 @@ module Decidim
18
18
  include Decidim::Traceable
19
19
  include Decidim::Loggable
20
20
  include Decidim::Fingerprintable
21
- include Decidim::DataPortability
21
+ include Decidim::DownloadYourData
22
22
  include Decidim::Proposals::ParticipatoryTextSection
23
23
  include Decidim::Amendable
24
24
  include Decidim::NewsletterParticipant
@@ -27,6 +27,7 @@ module Decidim
27
27
  include Decidim::Proposals::Valuatable
28
28
  include Decidim::TranslatableResource
29
29
  include Decidim::TranslatableAttributes
30
+ include Decidim::FilterableResource
30
31
 
31
32
  translatable_fields :title, :body
32
33
 
@@ -67,10 +68,33 @@ module Decidim
67
68
  scope :except_rejected, -> { where.not(state: "rejected").or(state_not_published) }
68
69
  scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
69
70
  scope :drafts, -> { where(published_at: nil) }
70
- scope :except_drafts, -> { where.not(published_at: nil) }
71
71
  scope :published, -> { where.not(published_at: nil) }
72
72
  scope :order_by_most_recent, -> { order(created_at: :desc) }
73
73
 
74
+ scope :with_availability, lambda { |state_key|
75
+ case state_key
76
+ when "withdrawn"
77
+ withdrawn
78
+ else
79
+ except_withdrawn
80
+ end
81
+ }
82
+
83
+ scope :with_type, lambda { |type_key, user, component|
84
+ case type_key
85
+ when "proposals"
86
+ only_amendables
87
+ when "amendments"
88
+ only_visible_emendations_for(user, component)
89
+ else # Assume 'all'
90
+ amendables_and_visible_emendations_for(user, component)
91
+ end
92
+ }
93
+
94
+ scope :voted_by, lambda { |user|
95
+ includes(:votes).where(decidim_proposals_proposal_votes: { decidim_author_id: user })
96
+ }
97
+
74
98
  scope :sort_by_valuation_assignments_count_asc, lambda {
75
99
  order(Arel.sql("#{sort_by_valuation_assignments_count_nulls_last_query} ASC NULLS FIRST").to_s)
76
100
  }
@@ -79,6 +103,8 @@ module Decidim
79
103
  order(Arel.sql("#{sort_by_valuation_assignments_count_nulls_last_query} DESC NULLS LAST").to_s)
80
104
  }
81
105
 
106
+ scope_search_multi :with_any_state, [:accepted, :rejected, :evaluating, :state_not_published]
107
+
82
108
  def self.with_valuation_assigned_to(user, space)
83
109
  valuator_roles = space.user_roles(:valuator).where(user: user)
84
110
 
@@ -103,7 +129,7 @@ module Decidim
103
129
  end
104
130
 
105
131
  # Returns a collection scoped by an author.
106
- # Overrides this method in DataPortability to support Coauthorable.
132
+ # Overrides this method in DownloadYourData to support Coauthorable.
107
133
  def self.user_collection(author)
108
134
  return unless author.is_a?(Decidim::User)
109
135
 
@@ -229,14 +255,21 @@ module Decidim
229
255
  ResourceLocatorPresenter.new(self).url
230
256
  end
231
257
 
258
+ # Returns the presenter for this author, to be used in the views.
259
+ # Required by ResourceRenderer.
260
+ def presenter
261
+ Decidim::Proposals::ProposalPresenter.new(self)
262
+ end
263
+
232
264
  # Public: Overrides the `reported_attributes` Reportable concern method.
233
265
  def reported_attributes
234
266
  [:title, :body]
235
267
  end
236
268
 
237
269
  # Public: Overrides the `reported_searchable_content_extras` Reportable concern method.
270
+ # Returns authors name or title in case it's a meeting
238
271
  def reported_searchable_content_extras
239
- [authors.map(&:name).join("\n")]
272
+ [authors.map { |p| p.respond_to?(:name) ? p.name : p.title }.join("\n")]
240
273
  end
241
274
 
242
275
  # Public: Whether the proposal is official or not.
@@ -246,7 +279,7 @@ module Decidim
246
279
 
247
280
  # Public: Whether the proposal is created in a meeting or not.
248
281
  def official_meeting?
249
- authors.first.class.name == "Decidim::Meetings::Meeting"
282
+ authors.first.instance_of?(Decidim::Meetings::Meeting)
250
283
  end
251
284
 
252
285
  # Public: The maximum amount of votes allowed for this proposal.
@@ -296,6 +329,10 @@ module Decidim
296
329
  published_at.nil?
297
330
  end
298
331
 
332
+ def self.ransack(params = {}, options = {})
333
+ ProposalSearch.new(self, params, options)
334
+ end
335
+
299
336
  # Defines the base query so that ransack can actually sort by this value
300
337
  def self.sort_by_valuation_assignments_count_nulls_last_query
301
338
  <<-SQL.squish
@@ -321,10 +358,18 @@ module Decidim
321
358
  where(query, value: value)
322
359
  end
323
360
 
324
- def self.ransackable_scopes(_auth = nil)
325
- [:valuator_role_ids_has]
361
+ def self.ransackable_scopes(auth_object = nil)
362
+ base = [:with_any_origin, :with_any_state, :voted_by, :coauthored_by, :related_to, :with_any_scope, :with_any_category]
363
+ return base unless auth_object&.admin?
364
+
365
+ # Add extra scopes for admins for the admin panel searches
366
+ base + [:valuator_role_ids_has]
326
367
  end
327
368
 
369
+ # Create i18n ransackers for :title and :body.
370
+ # Create the :search_text ransacker alias for searching from both of these.
371
+ ransacker_i18n_multi :search_text, [:title, :body]
372
+
328
373
  ransacker :state_published do
329
374
  Arel.sql("CASE
330
375
  WHEN EXISTS (
@@ -373,7 +418,7 @@ module Decidim
373
418
  Decidim::Proposals::ProposalSerializer
374
419
  end
375
420
 
376
- def self.data_portability_images(user)
421
+ def self.download_your_data_images(user)
377
422
  user_collection(user).map { |p| p.attachments.collect(&:file) }
378
423
  end
379
424
 
@@ -120,7 +120,10 @@ module Decidim
120
120
 
121
121
  # Proposals can only be created from the admin when the
122
122
  # corresponding setting is enabled.
123
+ # This setting is incompatible with participatory texts.
123
124
  def can_create_proposal_from_admin?
125
+ return disallow! if participatory_texts_are_enabled? && permission_action.subject == :proposal
126
+
124
127
  toggle_allow(admin_creation_is_enabled?) if permission_action.subject == :proposal
125
128
  end
126
129