decidim-proposals 0.28.0 → 0.28.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +1 -1
  3. data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +2 -2
  4. data/app/controllers/concerns/decidim/proposals/orderable.rb +1 -1
  5. data/app/controllers/decidim/proposals/admin/participatory_texts_controller.rb +1 -1
  6. data/app/helpers/decidim/proposals/application_helper.rb +1 -1
  7. data/app/models/decidim/proposals/proposal.rb +2 -14
  8. data/app/models/decidim/proposals/valuation_assignment.rb +2 -1
  9. data/app/packs/entrypoints/participatory_texts_admin.js +2 -0
  10. data/app/packs/src/decidim/proposals/admin/proposals.js +7 -0
  11. data/app/packs/stylesheets/decidim/proposals/admin/participatory_texts.scss +23 -0
  12. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -5
  13. data/app/presenters/decidim/proposals/admin_log/value_types/proposal_title_body_presenter.rb +1 -3
  14. data/app/presenters/decidim/proposals/log/valuation_assignment_presenter.rb +1 -1
  15. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +8 -5
  16. data/app/views/decidim/proposals/admin/participatory_texts/new_import.html.erb +3 -3
  17. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +1 -1
  18. data/app/views/decidim/proposals/admin/proposals/edit.html.erb +2 -2
  19. data/app/views/decidim/proposals/admin/proposals/index.html.erb +4 -2
  20. data/app/views/decidim/proposals/admin/proposals/new.html.erb +2 -2
  21. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +2 -1
  22. data/app/views/decidim/proposals/admin/proposals_imports/new.html.erb +2 -2
  23. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +1 -1
  24. data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +4 -2
  25. data/app/views/decidim/proposals/proposals/compare.html.erb +1 -1
  26. data/app/views/decidim/proposals/proposals/preview.html.erb +1 -1
  27. data/app/views/decidim/proposals/proposals/show.html.erb +2 -2
  28. data/config/assets.rb +1 -0
  29. data/config/locales/ar.yml +2 -10
  30. data/config/locales/bg.yml +718 -1
  31. data/config/locales/ca.yml +34 -34
  32. data/config/locales/cs.yml +5 -8
  33. data/config/locales/de.yml +10 -10
  34. data/config/locales/el.yml +0 -8
  35. data/config/locales/en.yml +4 -4
  36. data/config/locales/es-MX.yml +11 -11
  37. data/config/locales/es-PY.yml +11 -11
  38. data/config/locales/es.yml +23 -23
  39. data/config/locales/eu.yml +9 -9
  40. data/config/locales/fi-plain.yml +9 -9
  41. data/config/locales/fi.yml +8 -8
  42. data/config/locales/fr-CA.yml +11 -11
  43. data/config/locales/fr.yml +11 -11
  44. data/config/locales/ga-IE.yml +6 -0
  45. data/config/locales/gl.yml +2 -9
  46. data/config/locales/he-IL.yml +14 -0
  47. data/config/locales/hu.yml +11 -12
  48. data/config/locales/id-ID.yml +1 -4
  49. data/config/locales/is-IS.yml +4 -5
  50. data/config/locales/it.yml +4 -7
  51. data/config/locales/ja.yml +27 -21
  52. data/config/locales/lt.yml +0 -8
  53. data/config/locales/lv.yml +1 -4
  54. data/config/locales/nl.yml +5 -12
  55. data/config/locales/no.yml +1 -8
  56. data/config/locales/pl.yml +168 -5
  57. data/config/locales/pt-BR.yml +41 -5
  58. data/config/locales/pt.yml +1 -8
  59. data/config/locales/ro-RO.yml +1 -7
  60. data/config/locales/ru.yml +6 -5
  61. data/config/locales/sk.yml +5 -8
  62. data/config/locales/sv.yml +3 -11
  63. data/config/locales/tr-TR.yml +10 -8
  64. data/config/locales/uk.yml +6 -5
  65. data/config/locales/zh-CN.yml +1 -4
  66. data/config/locales/zh-TW.yml +0 -8
  67. data/db/migrate/20240404202756_add_valuation_assignments_count_to_decidim_proposals_proposals.rb +16 -0
  68. data/decidim-proposals.gemspec +47 -0
  69. data/lib/decidim/proposals/component.rb +2 -4
  70. data/lib/decidim/proposals/engine.rb +2 -2
  71. data/lib/decidim/proposals/proposal_serializer.rb +52 -0
  72. data/lib/decidim/proposals/seeds.rb +5 -1
  73. data/lib/decidim/proposals/test/factories.rb +75 -85
  74. data/lib/decidim/proposals/valuatable.rb +2 -1
  75. data/lib/decidim/proposals/version.rb +1 -1
  76. metadata +28 -26
  77. data/app/presenters/decidim/proposals/log/resource_presenter.rb +0 -22
  78. data/config/brakeman.ignore +0 -63
  79. data/config/environment.rb +0 -3
@@ -138,9 +138,7 @@ zh-TW:
138
138
  comments_enabled: 已啟用留言
139
139
  comments_max_length: 評論字數最大長度(若要使用預設值請輸入0)
140
140
  default_sort_order: 預設提案排序
141
- default_sort_order_help: 默認意味著如果啟用了支持,則建議將提案按隨機順序排序,如果封鎖了支持,則將按最受支持的順序排序。
142
141
  default_sort_order_options:
143
- default: 預設值
144
142
  most_commented: 最多評論
145
143
  most_endorsed: 最多已連署
146
144
  most_followed: 最多追蹤者
@@ -167,7 +165,6 @@ zh-TW:
167
165
  proposal_wizard_step_1_help_text: 提案向導「創建」步驟說明文字
168
166
  proposal_wizard_step_2_help_text: 提案向導「比較」步驟說明文字
169
167
  proposal_wizard_step_3_help_text: 提案向導「完成」步驟說明文字
170
- proposal_wizard_step_4_help_text: 提案向導「發佈」步驟說明文字
171
168
  resources_permissions_enabled: 每個提案都可以設定行動權限
172
169
  scope_id: 範圍
173
170
  scopes_enabled: 啟用範圍
@@ -192,9 +189,7 @@ zh-TW:
192
189
  creation_enabled: 參與者可以建立提案
193
190
  creation_enabled_readonly: 啟用參與式文本功能後,此設置將被禁用。要將提案上傳為參與式文本,請點擊“參與式文本”按鈕,然後按照說明進行操作。
194
191
  default_sort_order: 預設提案排序
195
- default_sort_order_help: 默認意味著如果啟用了支持,則建議將提案按隨機順序排序,如果封鎖了支持,則將按最受支持的順序排序。
196
192
  default_sort_order_options:
197
- default: 預設值
198
193
  most_commented: 最多評論
199
194
  most_endorsed: 最多已連署
200
195
  most_followed: 最多追蹤者
@@ -267,7 +262,6 @@ zh-TW:
267
262
  notification_title: 您的提案 <a href="%{resource_path}">%{resource_title}</a> 已被接受。
268
263
  follower:
269
264
  email_intro: '該提案「%{resource_title}」已被接受。您可以在此頁面上閱讀回應:'
270
- email_outro: 您收到了此通知,是因為您正在關注 "%{resource_title}"。您可以通過前面的鏈接取消關注它。
271
265
  email_subject: 您追蹤的提案已被接受
272
266
  notification_title: <a href="%{resource_path}">%{resource_title}</a>" 提案已經被接受。
273
267
  proposal_evaluating:
@@ -278,7 +272,6 @@ zh-TW:
278
272
  notification_title: 您的提案<a href="%{resource_path}">%{resource_title}</a>正在評估中。
279
273
  follower:
280
274
  email_intro: '該提案“%{resource_title}”正在進行評估。您可以在此頁面上查看答案:'
281
- email_outro: 您收到了此通知,是因為您正在關注 "%{resource_title}"。您可以通過前面的鏈接取消關注它。
282
275
  email_subject: 你追蹤的提案正在評估中
283
276
  notification_title: <a href="%{resource_path}">%{resource_title}</a> 提案正在評估中。
284
277
  proposal_mentioned:
@@ -305,7 +298,6 @@ zh-TW:
305
298
  notification_title: 您的提案<a href="%{resource_path}">%{resource_title}</a>已被拒絕。
306
299
  follower:
307
300
  email_intro: '該提案「%{resource_title}」已被駁回。您可以在此頁面中閱讀回覆:'
308
- email_outro: 您收到了此通知,是因為您正在關注 "%{resource_title}"。您可以通過前面的鏈接取消關注它。
309
301
  email_subject: 您追蹤的提案已被拒絕
310
302
  notification_title: <a href="%{resource_path}">%{resource_title}</a>" 提案已經被拒絕。
311
303
  proposal_update_category:
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddValuationAssignmentsCountToDecidimProposalsProposals < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :decidim_proposals_proposals, :valuation_assignments_count, :integer, default: 0
6
+
7
+ reversible do |dir|
8
+ dir.up do
9
+ Decidim::Proposals::Proposal.reset_column_information
10
+ Decidim::Proposals::Proposal.find_each do |record|
11
+ Decidim::Proposals::Proposal.reset_counters(record.id, :valuation_assignments)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path("lib", __dir__)
4
+
5
+ # Maintain your gem's version:
6
+ require "decidim/proposals/version"
7
+
8
+ # Describe your gem and declare its dependencies:
9
+ Gem::Specification.new do |s|
10
+ s.version = Decidim::Proposals.version
11
+ s.authors = ["Josep Jaume Rey Peroy", "Marc Riera Casals", "Oriol Gual Oliva"]
12
+ s.email = ["josepjaume@gmail.com", "mrc2407@gmail.com", "oriolgual@gmail.com"]
13
+ s.license = "AGPL-3.0"
14
+ s.homepage = "https://decidim.org"
15
+ s.metadata = {
16
+ "bug_tracker_uri" => "https://github.com/decidim/decidim/issues",
17
+ "documentation_uri" => "https://docs.decidim.org/",
18
+ "funding_uri" => "https://opencollective.com/decidim",
19
+ "homepage_uri" => "https://decidim.org",
20
+ "source_code_uri" => "https://github.com/decidim/decidim"
21
+ }
22
+ s.required_ruby_version = "~> 3.1.0"
23
+
24
+ s.name = "decidim-proposals"
25
+ s.summary = "Decidim proposals module"
26
+ s.description = "A proposals component for decidim's participatory spaces."
27
+
28
+ s.files = Dir.chdir(__dir__) do
29
+ `git ls-files -z`.split("\x0").select do |f|
30
+ (File.expand_path(f) == __FILE__) ||
31
+ f.start_with?(*%w(app/ config/ db/ lib/ Rakefile README.md))
32
+ end
33
+ end
34
+
35
+ s.add_dependency "decidim-comments", Decidim::Proposals.version
36
+ s.add_dependency "decidim-core", Decidim::Proposals.version
37
+ s.add_dependency "doc2text", "~> 0.4.6"
38
+ s.add_dependency "redcarpet", "~> 3.5", ">= 3.5.1"
39
+
40
+ s.add_development_dependency "decidim-admin", Decidim::Proposals.version
41
+ s.add_development_dependency "decidim-assemblies", Decidim::Proposals.version
42
+ s.add_development_dependency "decidim-budgets", Decidim::Proposals.version
43
+ s.add_development_dependency "decidim-conference", Decidim::Proposals.version
44
+ s.add_development_dependency "decidim-dev", Decidim::Proposals.version
45
+ s.add_development_dependency "decidim-meetings", Decidim::Proposals.version
46
+ s.add_development_dependency "decidim-participatory_processes", Decidim::Proposals.version
47
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "decidim/meetings"
4
-
5
3
  Decidim.register_component(:proposals) do |component|
6
4
  component.engine = Decidim::Proposals::Engine
7
5
  component.admin_engine = Decidim::Proposals::AdminEngine
@@ -23,7 +21,7 @@ Decidim.register_component(:proposals) do |component|
23
21
 
24
22
  component.permissions_class_name = "Decidim::Proposals::Permissions"
25
23
 
26
- POSSIBLE_SORT_ORDERS = %w(default random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
24
+ POSSIBLE_SORT_ORDERS = %w(automatic random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
27
25
 
28
26
  component.settings(:global) do |settings|
29
27
  settings.attribute :scopes_enabled, type: :boolean, default: false
@@ -37,7 +35,7 @@ Decidim.register_component(:proposals) do |component|
37
35
  settings.attribute :threshold_per_proposal, type: :integer, default: 0, required: true
38
36
  settings.attribute :can_accumulate_supports_beyond_threshold, type: :boolean, default: false
39
37
  settings.attribute :proposal_answering_enabled, type: :boolean, default: true
40
- settings.attribute :default_sort_order, type: :select, default: "default", choices: -> { POSSIBLE_SORT_ORDERS }
38
+ settings.attribute :default_sort_order, type: :select, default: "automatic", choices: -> { POSSIBLE_SORT_ORDERS }
41
39
  settings.attribute :official_proposals_enabled, type: :boolean, default: true
42
40
  settings.attribute :comments_enabled, type: :boolean, default: true
43
41
  settings.attribute :comments_max_length, type: :integer, required: true
@@ -111,8 +111,8 @@ module Decidim
111
111
  end
112
112
 
113
113
  initializer "decidim_proposals.remove_space_admins" do
114
- ActiveSupport::Notifications.subscribe("decidim.admin.participatorty_space.destroy_admin:after") do |_event_name, klass, id|
115
- Decidim::Proposals::ValuationAssignment.where(valuator_role_type: klass, valuator_role_id: id).destroy_all
114
+ ActiveSupport::Notifications.subscribe("decidim.admin.participatory_space.destroy_admin:after") do |_event_name, data|
115
+ Decidim::Proposals::ValuationAssignment.where(valuator_role_type: data.fetch(:class_name), valuator_role_id: data.fetch(:role)).destroy_all
116
116
  end
117
117
  end
118
118
 
@@ -19,6 +19,9 @@ module Decidim
19
19
  def serialize
20
20
  {
21
21
  id: proposal.id,
22
+ author: {
23
+ **author_fields
24
+ },
22
25
  category: {
23
26
  id: proposal.category.try(:id),
24
27
  name: proposal.category.try(:name) || empty_translatable
@@ -40,6 +43,7 @@ module Decidim
40
43
  state: proposal.state.to_s,
41
44
  reference: proposal.reference,
42
45
  answer: ensure_translatable(proposal.answer),
46
+ answered_at: proposal.answered_at,
43
47
  supports: proposal.proposal_votes_count,
44
48
  endorsements: {
45
49
  total_count: proposal.endorsements.size,
@@ -101,6 +105,54 @@ module Decidim
101
105
 
102
106
  convert_to_text(value)
103
107
  end
108
+
109
+ def author_fields
110
+ is_author_user_group = resource.coauthorships.map(&:decidim_user_group_id).any?
111
+
112
+ {
113
+ id: resource.authors.map(&:id),
114
+ name: resource.authors.map do |author|
115
+ author_name(is_author_user_group ? resource.coauthorships.first.user_group : author)
116
+ end,
117
+ url: resource.authors.map do |author|
118
+ author_url(is_author_user_group ? resource.coauthorships.first.user_group : author)
119
+ end
120
+ }
121
+ end
122
+
123
+ def author_name(author)
124
+ if author.respond_to?(:name)
125
+ translated_attribute(author.name) # is a Decidim::User or Decidim::Organization or Decidim::UserGroup
126
+ elsif author.respond_to?(:title)
127
+ translated_attribute(author.title) # is a Decidim::Meetings::Meeting
128
+ end
129
+ end
130
+
131
+ def author_url(author)
132
+ if author.respond_to?(:nickname)
133
+ profile_url(author.nickname) # is a Decidim::User or Decidim::UserGroup
134
+ elsif author.respond_to?(:title)
135
+ meeting_url(author) # is a Decidim::Meetings::Meeting
136
+ else
137
+ root_url # is a Decidim::Organization
138
+ end
139
+ end
140
+
141
+ def profile_url(nickname)
142
+ Decidim::Core::Engine.routes.url_helpers.profile_url(nickname, host:)
143
+ end
144
+
145
+ def meeting_url(meeting)
146
+ Decidim::EngineRouter.main_proxy(meeting.component).meeting_url(id: meeting.id, host:)
147
+ end
148
+
149
+ def root_url
150
+ Decidim::Core::Engine.routes.url_helpers.root_url(host:)
151
+ end
152
+
153
+ def host
154
+ resource.organization.host
155
+ end
104
156
  end
105
157
  end
106
158
  end
@@ -69,6 +69,7 @@ module Decidim
69
69
  end
70
70
  end
71
71
 
72
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
72
73
  def create_proposal!(component:)
73
74
  n = rand(5)
74
75
  state, answer, state_published_at = if n > 3
@@ -103,7 +104,7 @@ module Decidim
103
104
  visibility: "all"
104
105
  ) do
105
106
  proposal = Decidim::Proposals::Proposal.new(params)
106
- meeting_component = participatory_space.components.find_by(manifest_name: "meetings")
107
+ n = 3 if n == 2 && !Decidim.module_installed?(:meetings)
107
108
 
108
109
  coauthor = case n
109
110
  when 0
@@ -111,6 +112,8 @@ module Decidim
111
112
  when 1
112
113
  Decidim::UserGroup.where(organization:).sample
113
114
  when 2
115
+ meeting_component = participatory_space.components.find_by(manifest_name: "meetings")
116
+
114
117
  Decidim::Meetings::Meeting.where(component: meeting_component).sample
115
118
  else
116
119
  organization
@@ -120,6 +123,7 @@ module Decidim
120
123
  proposal
121
124
  end
122
125
  end
126
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
123
127
 
124
128
  def random_nickname
125
129
  "#{::Faker::Twitter.unique.screen_name}-#{SecureRandom.hex(4)}"[0, 20]
@@ -7,9 +7,12 @@ require "decidim/meetings/test/factories"
7
7
 
8
8
  FactoryBot.define do
9
9
  factory :proposal_component, parent: :component do
10
- name { Decidim::Components::Namer.new(participatory_space.organization.available_locales, :proposals).i18n_name }
10
+ transient do
11
+ skip_injection { false }
12
+ end
13
+ name { generate_component_name(participatory_space.organization.available_locales, :proposals) }
11
14
  manifest_name { :proposals }
12
- participatory_space { create(:participatory_process, :with_steps, organization:) }
15
+ participatory_space { create(:participatory_process, :with_steps, organization:, skip_injection:) }
13
16
 
14
17
  trait :with_endorsements_enabled do
15
18
  step_settings do
@@ -251,40 +254,16 @@ FactoryBot.define do
251
254
  skip_injection { false }
252
255
  end
253
256
 
254
- title do
255
- if skip_injection
256
- Decidim::Faker::Localized.localized { generate(:title) }
257
- else
258
- Decidim::Faker::Localized.localized { "<script>alert(\"Proposal TITLE\");</script> #{generate(:title)}" }
259
- end
260
- end
261
- body do
262
- if skip_injection
263
- Decidim::Faker::Localized.localized { Faker::Lorem.sentences(number: 3).join("\n") }
264
- else
265
- Decidim::Faker::Localized.localized { "<script>alert(\"Proposal BODY\");</script> #{Faker::Lorem.sentences(number: 3).join("\n")}" }
266
- end
267
- end
268
- component { create(:proposal_component) }
257
+ title { generate_localized_title(:proposal_title, skip_injection:) }
258
+ body { generate_localized_description(:proposal_body, skip_injection:) }
259
+ component { create(:proposal_component, skip_injection:) }
269
260
  published_at { Time.current }
270
261
  address { "#{Faker::Address.street_name}, #{Faker::Address.city}" }
271
262
  latitude { Faker::Address.latitude }
272
263
  longitude { Faker::Address.longitude }
273
264
  cost { 20_000 }
274
- cost_report do
275
- if skip_injection
276
- Decidim::Faker::Localized.localized { generate(:title) }
277
- else
278
- Decidim::Faker::Localized.localized { "<script>alert(\"Proposal cost report\")</script> #{generate(:title)}" }
279
- end
280
- end
281
- execution_period do
282
- if skip_injection
283
- Decidim::Faker::Localized.localized { generate(:title) }
284
- else
285
- Decidim::Faker::Localized.localized { "<script>alert(\"Proposal execution period\")</script> #{generate(:title)}" }
286
- end
287
- end
265
+ cost_report { generate_localized_title(:proposal_cost_report, skip_injection:) }
266
+ execution_period { generate_localized_title(:proposal_execution_period, skip_injection:) }
288
267
 
289
268
  after(:build) do |proposal, evaluator|
290
269
  proposal.title = if evaluator.title.is_a?(String)
@@ -302,7 +281,7 @@ FactoryBot.define do
302
281
  proposal.body = Decidim::ContentProcessor.parse_with_processor(:hashtag, proposal.body, current_organization: proposal.organization).rewrite
303
282
 
304
283
  if proposal.component
305
- users = evaluator.users || [create(:user, :confirmed, organization: proposal.component.participatory_space.organization)]
284
+ users = evaluator.users || [create(:user, :confirmed, organization: proposal.component.participatory_space.organization, skip_injection: evaluator.skip_injection)]
306
285
  users.each_with_index do |user, idx|
307
286
  user_group = evaluator.user_groups[idx]
308
287
  proposal.coauthorships.build(author: user, user_group:)
@@ -319,18 +298,18 @@ FactoryBot.define do
319
298
  end
320
299
 
321
300
  trait :participant_author do
322
- after :build do |proposal|
301
+ after :build do |proposal, evaluator|
323
302
  proposal.coauthorships.clear
324
- user = build(:user, organization: proposal.component.participatory_space.organization)
303
+ user = build(:user, organization: proposal.component.participatory_space.organization, skip_injection: evaluator.skip_injection)
325
304
  proposal.coauthorships.build(author: user)
326
305
  end
327
306
  end
328
307
 
329
308
  trait :user_group_author do
330
- after :build do |proposal|
309
+ after :build do |proposal, evaluator|
331
310
  proposal.coauthorships.clear
332
- user = create(:user, organization: proposal.component.participatory_space.organization)
333
- user_group = create(:user_group, :verified, organization: user.organization, users: [user])
311
+ user = create(:user, organization: proposal.component.participatory_space.organization, skip_injection: evaluator.skip_injection)
312
+ user_group = create(:user_group, :verified, organization: user.organization, users: [user], skip_injection: evaluator.skip_injection)
334
313
  proposal.coauthorships.build(author: user, user_group:)
335
314
  end
336
315
  end
@@ -343,10 +322,10 @@ FactoryBot.define do
343
322
  end
344
323
 
345
324
  trait :official_meeting do
346
- after :build do |proposal|
325
+ after :build do |proposal, evaluator|
347
326
  proposal.coauthorships.clear
348
- component = build(:meeting_component, participatory_space: proposal.component.participatory_space)
349
- proposal.coauthorships.build(author: build(:meeting, component:))
327
+ component = build(:meeting_component, participatory_space: proposal.component.participatory_space, skip_injection: evaluator.skip_injection)
328
+ proposal.coauthorships.build(author: build(:meeting, component:, skip_injection: evaluator.skip_injection))
350
329
  end
351
330
  end
352
331
 
@@ -395,78 +374,101 @@ FactoryBot.define do
395
374
  end
396
375
 
397
376
  trait :hidden do
398
- after :create do |proposal|
399
- create(:moderation, hidden_at: Time.current, reportable: proposal)
377
+ after :create do |proposal, evaluator|
378
+ create(:moderation, hidden_at: Time.current, reportable: proposal, skip_injection: evaluator.skip_injection)
400
379
  end
401
380
  end
402
381
 
403
382
  trait :with_votes do
404
- after :create do |proposal|
405
- create_list(:proposal_vote, 5, proposal:)
383
+ after :create do |proposal, evaluator|
384
+ create_list(:proposal_vote, 5, proposal:, skip_injection: evaluator.skip_injection)
406
385
  end
407
386
  end
408
387
 
409
388
  trait :with_endorsements do
410
- after :create do |proposal|
389
+ after :create do |proposal, evaluator|
411
390
  5.times.collect do
412
- create(:endorsement, resource: proposal, author: build(:user, organization: proposal.participatory_space.organization))
391
+ create(:endorsement, resource: proposal, author: build(:user, organization: proposal.participatory_space.organization, skip_injection: evaluator.skip_injection),
392
+ skip_injection: evaluator.skip_injection)
413
393
  end
414
394
  end
415
395
  end
416
396
 
417
397
  trait :with_amendments do
418
- after :create do |proposal|
419
- create_list(:proposal_amendment, 5, amendable: proposal)
398
+ after :create do |proposal, evaluator|
399
+ create_list(:proposal_amendment, 5, amendable: proposal, skip_injection: evaluator.skip_injection)
420
400
  end
421
401
  end
422
402
 
423
403
  trait :with_photo do
424
- after :create do |proposal|
425
- proposal.attachments << create(:attachment, :with_image, attached_to: proposal)
404
+ after :create do |proposal, evaluator|
405
+ proposal.attachments << create(:attachment, :with_image, attached_to: proposal, skip_injection: evaluator.skip_injection)
426
406
  end
427
407
  end
428
408
 
429
409
  trait :with_document do
430
- after :create do |proposal|
431
- proposal.attachments << create(:attachment, :with_pdf, attached_to: proposal)
410
+ after :create do |proposal, evaluator|
411
+ proposal.attachments << create(:attachment, :with_pdf, attached_to: proposal, skip_injection: evaluator.skip_injection)
412
+ end
413
+ end
414
+
415
+ trait :moderated do
416
+ after(:create) do |proposal, evaluator|
417
+ create(:moderation, reportable: proposal, hidden_at: 2.days.ago, skip_injection: evaluator.skip_injection)
432
418
  end
433
419
  end
434
420
  end
435
421
 
436
422
  factory :proposal_vote, class: "Decidim::Proposals::ProposalVote" do
437
- proposal { build(:proposal) }
438
- author { build(:user, organization: proposal.organization) }
423
+ transient do
424
+ skip_injection { false }
425
+ end
426
+ proposal { build(:proposal, skip_injection:) }
427
+ author { build(:user, organization: proposal.organization, skip_injection:) }
439
428
  end
440
429
 
441
430
  factory :proposal_amendment, class: "Decidim::Amendment" do
442
- amendable { build(:proposal) }
443
- emendation { build(:proposal, component: amendable.component) }
444
- amender { build(:user, organization: amendable.component.participatory_space.organization) }
431
+ transient do
432
+ skip_injection { false }
433
+ end
434
+ amendable { build(:proposal, skip_injection:) }
435
+ emendation { build(:proposal, component: amendable.component, skip_injection:) }
436
+ amender { build(:user, organization: amendable.component.participatory_space.organization, skip_injection:) }
445
437
  state { Decidim::Amendment::STATES.keys.sample }
446
438
  end
447
439
 
448
440
  factory :proposal_note, class: "Decidim::Proposals::ProposalNote" do
449
- body { Faker::Lorem.sentences(number: 3).join("\n") }
450
- proposal { build(:proposal) }
451
- author { build(:user, organization: proposal.organization) }
441
+ transient do
442
+ skip_injection { false }
443
+ end
444
+ body do
445
+ if skip_injection
446
+ generate(:title)
447
+ else
448
+ "<script>alert(\"proposal_note_body\");</script> #{generate(:title)}"
449
+ end
450
+ end
451
+ proposal { build(:proposal, skip_injection:) }
452
+ author { build(:user, organization: proposal.organization, skip_injection:) }
452
453
  end
453
454
 
454
455
  factory :collaborative_draft, class: "Decidim::Proposals::CollaborativeDraft" do
455
456
  transient do
457
+ skip_injection { false }
456
458
  users { nil }
457
459
  # user_groups correspondence to users is by sorting order
458
460
  user_groups { [] }
459
461
  end
460
462
 
461
- title { "<script>alert(\"TITLE\");</script> #{generate(:title)}" }
462
- body { "<script>alert(\"BODY\");</script>\n#{Faker::Lorem.sentences(number: 3).join("\n")}" }
463
- component { create(:proposal_component) }
463
+ title { generate_localized_title(:collaborative_draft_title, skip_injection:)["en"] }
464
+ body { generate_localized_description(:collaborative_draft_body, skip_injection:)["en"] }
465
+ component { create(:proposal_component, skip_injection:) }
464
466
  address { "#{Faker::Address.street_name}, #{Faker::Address.city}" }
465
467
  state { "open" }
466
468
 
467
469
  after(:build) do |collaborative_draft, evaluator|
468
470
  if collaborative_draft.component
469
- users = evaluator.users || [create(:user, organization: collaborative_draft.component.participatory_space.organization)]
471
+ users = evaluator.users || [create(:user, organization: collaborative_draft.component.participatory_space.organization, skip_injection: evaluator.skip_injection)]
470
472
  users.each_with_index do |user, idx|
471
473
  user_group = evaluator.user_groups[idx]
472
474
  collaborative_draft.coauthorships.build(author: user, user_group:)
@@ -475,9 +477,9 @@ FactoryBot.define do
475
477
  end
476
478
 
477
479
  trait :participant_author do
478
- after :build do |draft|
480
+ after :build do |draft, evaluator|
479
481
  draft.coauthorships.clear
480
- user = build(:user, organization: draft.component.participatory_space.organization)
482
+ user = build(:user, organization: draft.component.participatory_space.organization, skip_injection: evaluator.skip_injection)
481
483
  draft.coauthorships.build(author: user)
482
484
  end
483
485
  end
@@ -501,32 +503,20 @@ FactoryBot.define do
501
503
  skip_injection { false }
502
504
  end
503
505
 
504
- title do
505
- if skip_injection
506
- Decidim::Faker::Localized.localized { generate(:title) }
507
- else
508
- Decidim::Faker::Localized.localized { "<script>alert(\"Meetings TITLE\")</script> #{generate(:title)}" }
509
- end
510
- end
511
-
512
- description do
513
- Decidim::Faker::Localized.wrapped("<p>", "</p>") do
514
- if skip_injection
515
- Decidim::Faker::Localized.localized { Faker::Lorem.sentences(number: 3).join("\n") }
516
- else
517
- Decidim::Faker::Localized.localized { "<script>alert(\"Meetings description\");</script> #{Faker::Lorem.sentences(number: 3).join("\n")}" }
518
- end
519
- end
520
- end
521
- component { create(:proposal_component) }
506
+ title { generate_localized_title(:participatory_text_title, skip_injection:) }
507
+ description { generate_localized_description(:participatory_text_description, skip_injection:) }
508
+ component { create(:proposal_component, skip_injection:) }
522
509
  end
523
510
 
524
511
  factory :valuation_assignment, class: "Decidim::Proposals::ValuationAssignment" do
512
+ transient do
513
+ skip_injection { false }
514
+ end
525
515
  proposal
526
516
  valuator_role do
527
517
  space = proposal.component.participatory_space
528
518
  organization = space.organization
529
- build :participatory_process_user_role, role: :valuator, user: build(:user, organization:)
519
+ build :participatory_process_user_role, role: :valuator, skip_injection:, user: build(:user, organization:, skip_injection:)
530
520
  end
531
521
  end
532
522
  end
@@ -8,7 +8,8 @@ module Decidim
8
8
  include Decidim::Comments::Commentable
9
9
 
10
10
  included do
11
- has_many :valuation_assignments, foreign_key: "decidim_proposal_id", dependent: :destroy
11
+ has_many :valuation_assignments, foreign_key: "decidim_proposal_id", dependent: :destroy,
12
+ counter_cache: :valuation_assignments_count, class_name: "Decidim::Proposals::ValuationAssignment"
12
13
 
13
14
  def valuators
14
15
  valuator_role_ids = valuation_assignments.where(proposal: self).pluck(:valuator_role_id)
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds decidim-proposals version.
5
5
  module Proposals
6
6
  def self.version
7
- "0.28.0"
7
+ "0.28.2"
8
8
  end
9
9
  end
10
10
  end