decidim-proposals 0.28.5 → 0.29.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -18
  3. data/app/cells/decidim/proposals/cost_report_cell.rb +0 -3
  4. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +11 -11
  5. data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
  6. data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +1 -1
  7. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +2 -3
  8. data/app/cells/decidim/proposals/proposal_cell.rb +2 -0
  9. data/app/cells/decidim/proposals/proposal_g/show.erb +23 -0
  10. data/app/cells/decidim/proposals/proposal_g_cell.rb +48 -0
  11. data/app/cells/decidim/proposals/proposal_l_cell.rb +18 -19
  12. data/app/cells/decidim/proposals/proposal_metadata_cell.rb +23 -15
  13. data/app/commands/decidim/proposals/admin/answer_proposal.rb +2 -1
  14. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +7 -5
  15. data/app/commands/decidim/proposals/admin/create_proposal.rb +4 -6
  16. data/app/commands/decidim/proposals/admin/create_proposal_state.rb +15 -0
  17. data/app/commands/decidim/proposals/admin/destroy_proposal_state.rb +10 -0
  18. data/app/commands/decidim/proposals/admin/import_proposals.rb +10 -2
  19. data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +4 -21
  20. data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +6 -4
  21. data/app/commands/decidim/proposals/admin/update_proposal_state.rb +13 -0
  22. data/app/commands/decidim/proposals/create_proposal.rb +21 -2
  23. data/app/commands/decidim/proposals/update_proposal.rb +2 -2
  24. data/app/commands/decidim/proposals/vote_proposal.rb +1 -1
  25. data/app/commands/decidim/proposals/withdraw_proposal.rb +3 -7
  26. data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +10 -22
  27. data/app/controllers/decidim/proposals/admin/proposal_states_controller.rb +86 -0
  28. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +4 -0
  29. data/app/controllers/decidim/proposals/admin/valuation_assignments_controller.rb +8 -11
  30. data/app/controllers/decidim/proposals/proposals_controller.rb +38 -39
  31. data/app/events/decidim/proposals/proposal_state_changed_event.rb +37 -0
  32. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +8 -4
  33. data/app/forms/decidim/proposals/admin/proposal_form.rb +1 -1
  34. data/app/forms/decidim/proposals/admin/proposal_state_form.rb +22 -0
  35. data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +1 -1
  36. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +1 -1
  37. data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +12 -14
  38. data/app/forms/decidim/proposals/proposal_form.rb +25 -4
  39. data/app/forms/decidim/proposals/reject_access_to_collaborative_draft_form.rb +1 -1
  40. data/app/forms/decidim/proposals/request_access_to_collaborative_draft_form.rb +1 -1
  41. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +7 -17
  42. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +13 -89
  43. data/app/helpers/decidim/proposals/application_helper.rb +16 -10
  44. data/app/helpers/decidim/proposals/map_helper.rb +1 -1
  45. data/app/helpers/decidim/proposals/proposal_cells_helper.rb +6 -2
  46. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +3 -3
  47. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +5 -8
  48. data/app/helpers/decidim/proposals/proposals_helper.rb +18 -24
  49. data/app/models/decidim/proposals/proposal.rb +83 -29
  50. data/app/models/decidim/proposals/proposal_state.rb +58 -0
  51. data/app/packs/documents/decidim/proposals/participatory_texts/participatory_text.md +1 -3
  52. data/app/packs/images/decidim/proposals/proposal-placeholder-card-g.svg +15 -0
  53. data/app/packs/src/decidim/proposals/add_proposal.js +2 -0
  54. data/app/packs/src/decidim/proposals/admin/proposals.js +43 -8
  55. data/app/packs/stylesheets/decidim/proposals/proposals.scss +39 -1
  56. data/app/permissions/decidim/proposals/admin/permissions.rb +16 -4
  57. data/app/permissions/decidim/proposals/permissions.rb +3 -4
  58. data/app/presenters/decidim/proposals/admin_log/proposal_state_presenter.rb +21 -0
  59. data/app/presenters/decidim/proposals/proposal_presenter.rb +12 -3
  60. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +1 -1
  61. data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +1 -1
  62. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +4 -4
  63. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +1 -1
  64. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +1 -1
  65. data/app/services/decidim/proposals/diff_renderer.rb +1 -3
  66. data/app/services/decidim/proposals/proposal_builder.rb +1 -1
  67. data/app/views/decidim/proposals/admin/imports/_proposals_fields.html.erb +1 -1
  68. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +3 -2
  69. data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +2 -2
  70. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +3 -3
  71. data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +67 -0
  72. data/app/views/decidim/proposals/admin/proposal_states/edit.html.erb +18 -0
  73. data/app/views/decidim/proposals/admin/proposal_states/index.html.erb +50 -0
  74. data/app/views/decidim/proposals/admin/proposal_states/new.html.erb +18 -0
  75. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +6 -12
  76. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +6 -6
  77. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +2 -2
  78. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +11 -7
  79. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +7 -5
  80. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_merge.html.erb +2 -2
  81. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +2 -2
  82. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +2 -2
  83. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +2 -2
  84. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_split.html.erb +2 -2
  85. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +11 -7
  86. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_valuators_picker.html.erb +12 -0
  87. data/app/views/decidim/proposals/admin/proposals/index.html.erb +11 -7
  88. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
  89. data/app/views/decidim/proposals/admin/proposals/show.html.erb +3 -2
  90. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +3 -3
  91. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +12 -5
  92. data/app/views/decidim/proposals/proposals/_proposal.html.erb +1 -1
  93. data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +1 -1
  94. data/app/views/decidim/proposals/proposals/_proposals.html.erb +9 -3
  95. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +1 -1
  96. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +3 -3
  97. data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +0 -1
  98. data/app/views/decidim/proposals/proposals/index.html.erb +1 -1
  99. data/app/views/decidim/proposals/proposals/index.js.erb +0 -12
  100. data/app/views/decidim/proposals/proposals/new.html.erb +2 -7
  101. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +4 -4
  102. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_votes_count.html.erb +8 -8
  103. data/app/views/decidim/proposals/proposals/preview.html.erb +1 -1
  104. data/app/views/decidim/proposals/proposals/show.html.erb +3 -3
  105. data/config/locales/ar.yml +9 -115
  106. data/config/locales/bg.yml +117 -111
  107. data/config/locales/ca.yml +86 -83
  108. data/config/locales/cs.yml +65 -127
  109. data/config/locales/de.yml +128 -125
  110. data/config/locales/el.yml +8 -121
  111. data/config/locales/en.yml +110 -107
  112. data/config/locales/es-MX.yml +85 -82
  113. data/config/locales/es-PY.yml +88 -85
  114. data/config/locales/es.yml +83 -80
  115. data/config/locales/eu.yml +166 -170
  116. data/config/locales/fi-plain.yml +90 -87
  117. data/config/locales/fi.yml +139 -136
  118. data/config/locales/fr-CA.yml +92 -87
  119. data/config/locales/fr.yml +83 -78
  120. data/config/locales/ga-IE.yml +1 -27
  121. data/config/locales/gl.yml +11 -107
  122. data/config/locales/he-IL.yml +0 -17
  123. data/config/locales/hu.yml +18 -90
  124. data/config/locales/id-ID.yml +6 -106
  125. data/config/locales/is-IS.yml +8 -33
  126. data/config/locales/it.yml +14 -103
  127. data/config/locales/ja.yml +117 -112
  128. data/config/locales/lb.yml +1 -0
  129. data/config/locales/lt.yml +6 -123
  130. data/config/locales/lv.yml +4 -101
  131. data/config/locales/nl.yml +11 -102
  132. data/config/locales/no.yml +7 -108
  133. data/config/locales/pl.yml +110 -115
  134. data/config/locales/pt-BR.yml +10 -84
  135. data/config/locales/pt.yml +9 -109
  136. data/config/locales/ro-RO.yml +15 -117
  137. data/config/locales/ru.yml +9 -53
  138. data/config/locales/sk.yml +5 -109
  139. data/config/locales/sl.yml +4 -0
  140. data/config/locales/sv.yml +142 -337
  141. data/config/locales/tr-TR.yml +64 -108
  142. data/config/locales/uk.yml +11 -55
  143. data/config/locales/zh-CN.yml +5 -100
  144. data/config/locales/zh-TW.yml +8 -119
  145. data/db/migrate/20240110203500_add_withdrawn_at_field_to_proposals.rb +27 -0
  146. data/db/migrate/20240110203501_create_decidim_proposals_proposal_state.rb +14 -0
  147. data/db/migrate/20240110203502_add_state_id_to_decidim_proposals_proposals.rb +13 -0
  148. data/db/migrate/20240110203503_remove_state_from_decidim_proposals_proposals.rb +11 -0
  149. data/db/migrate/20240110203504_create_default_proposal_states.rb +31 -0
  150. data/db/migrate/20240209092404_change_color_fields_on_proposals_states.rb +54 -0
  151. data/decidim-proposals.gemspec +3 -3
  152. data/lib/decidim/api/proposal_type.rb +4 -13
  153. data/lib/decidim/api/proposals_type.rb +3 -1
  154. data/lib/decidim/proposals/admin_engine.rb +8 -0
  155. data/lib/decidim/proposals/admin_filter.rb +37 -0
  156. data/lib/decidim/proposals/component.rb +8 -5
  157. data/lib/decidim/proposals/engine.rb +1 -15
  158. data/lib/decidim/proposals/import/proposal_answer_creator.rb +6 -6
  159. data/lib/decidim/proposals/import/proposal_creator.rb +1 -1
  160. data/lib/decidim/proposals/markdown_to_proposals.rb +2 -8
  161. data/lib/decidim/proposals/proposal_serializer.rb +8 -9
  162. data/lib/decidim/proposals/seeds.rb +60 -51
  163. data/lib/decidim/proposals/test/factories.rb +69 -14
  164. data/lib/decidim/proposals/version.rb +1 -1
  165. data/lib/decidim/proposals.rb +84 -12
  166. data/lib/tasks/proposals/upgrade/{decdim_proposals_upgrade_tasks.rake → decidim_proposals_upgrade_tasks.rake} +0 -13
  167. metadata +51 -40
  168. data/app/events/decidim/proposals/accepted_proposal_event.rb +0 -17
  169. data/app/events/decidim/proposals/evaluating_proposal_event.rb +0 -11
  170. data/app/events/decidim/proposals/rejected_proposal_event.rb +0 -17
  171. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +0 -44
  172. data/app/queries/decidim/proposals/similar_proposals.rb +0 -67
  173. data/app/services/decidim/proposals/collaborative_draft_diff_renderer.rb +0 -22
  174. data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +0 -0
  175. data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +0 -3
  176. data/app/views/decidim/proposals/proposals/compare.html.erb +0 -24
  177. data/app/views/decidim/proposals/proposals/complete.html.erb +0 -31
  178. data/config/locales/bn-BD.yml +0 -1
  179. data/config/locales/bs-BA.yml +0 -100
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDefaultProposalStates < ActiveRecord::Migration[6.1]
4
+ class Proposal < ApplicationRecord
5
+ belongs_to :proposal_state,
6
+ class_name: "Decidim::Proposals::ProposalState",
7
+ foreign_key: "decidim_proposals_proposal_state_id",
8
+ inverse_of: :proposals,
9
+ optional: true
10
+
11
+ self.table_name = :decidim_proposals_proposals
12
+ STATES = { not_answered: 0, evaluating: 10, accepted: 20, rejected: -10 }.freeze
13
+ enum old_state: STATES, _default: "not_answered"
14
+ end
15
+
16
+ def up
17
+ Decidim::Component.where(manifest_name: "proposals").find_each do |component|
18
+ admin_user = component.organization.admins.first
19
+
20
+ default_states = Decidim::Proposals.create_default_states!(component, admin_user)
21
+
22
+ Proposal.where(decidim_component_id: component.id).find_each do |proposal|
23
+ proposal.update!(proposal_state: default_states.dig(proposal.old_state.to_sym, :object))
24
+ end
25
+ end
26
+ end
27
+
28
+ def down
29
+ raise ActiveRecord::IrreversibleMigration
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChangeColorFieldsOnProposalsStates < ActiveRecord::Migration[6.1]
4
+ class ProposalState < ApplicationRecord
5
+ self.table_name = :decidim_proposals_proposal_states
6
+
7
+ def self.colors
8
+ {
9
+ gray: {
10
+ background: "#F6F8FA",
11
+ foreground: "#4B5058"
12
+ },
13
+ green: {
14
+ background: "#E3FCE9",
15
+ foreground: "#15602C"
16
+ },
17
+ orange: {
18
+ background: "#FFF1E5",
19
+ foreground: "#BC4C00"
20
+ },
21
+ red: {
22
+ background: "#FFEBE9",
23
+ foreground: "#D1242F"
24
+ }
25
+ }
26
+ end
27
+ end
28
+
29
+ def up
30
+ colors = ProposalState.colors
31
+
32
+ add_column :decidim_proposals_proposal_states, :bg_color, :string, default: colors[:gray][:background], null: false
33
+ add_column :decidim_proposals_proposal_states, :text_color, :string, default: colors[:gray][:foreground], null: false
34
+ remove_column :decidim_proposals_proposal_states, :css_class
35
+
36
+ # rubocop:disable Rails/SkipsModelValidations
37
+ ProposalState.where(token: :accepted).update_all(
38
+ bg_color: colors[:green][:background], text_color: colors[:green][:foreground]
39
+ )
40
+ ProposalState.where(token: :evaluating).update_all(
41
+ bg_color: colors[:orange][:background], text_color: colors[:orange][:foreground]
42
+ )
43
+ ProposalState.where(token: :rejected).update_all(
44
+ bg_color: colors[:red][:background], text_color: colors[:red][:foreground]
45
+ )
46
+ # rubocop:enable Rails/SkipsModelValidations
47
+ end
48
+
49
+ def down
50
+ remove_column :decidim_proposals_proposal_states, :bg_color
51
+ remove_column :decidim_proposals_proposal_states, :text_color
52
+ add_column :decidim_proposals_proposal_states, :css_class, :string
53
+ end
54
+ end
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.version = Decidim::Proposals.version
11
11
  s.authors = ["Josep Jaume Rey Peroy", "Marc Riera Casals", "Oriol Gual Oliva"]
12
12
  s.email = ["josepjaume@gmail.com", "mrc2407@gmail.com", "oriolgual@gmail.com"]
13
- s.license = "AGPL-3.0-or-later"
13
+ s.license = "AGPL-3.0"
14
14
  s.homepage = "https://decidim.org"
15
15
  s.metadata = {
16
16
  "bug_tracker_uri" => "https://github.com/decidim/decidim/issues",
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  "homepage_uri" => "https://decidim.org",
20
20
  "source_code_uri" => "https://github.com/decidim/decidim"
21
21
  }
22
- s.required_ruby_version = "~> 3.1.0"
22
+ s.required_ruby_version = "~> 3.2.0"
23
23
 
24
24
  s.name = "decidim-proposals"
25
25
  s.summary = "Decidim proposals module"
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
 
35
35
  s.add_dependency "decidim-comments", Decidim::Proposals.version
36
36
  s.add_dependency "decidim-core", Decidim::Proposals.version
37
- s.add_dependency "doc2text", "~> 0.4.6"
37
+ s.add_dependency "doc2text", "~> 0.4.7"
38
38
  s.add_dependency "redcarpet", "~> 3.5", ">= 3.5.1"
39
39
 
40
40
  s.add_development_dependency "decidim-admin", Decidim::Proposals.version
@@ -26,6 +26,7 @@ module Decidim
26
26
  def coordinates
27
27
  [object.latitude, object.longitude]
28
28
  end
29
+
29
30
  field :reference, GraphQL::Types::String, "This proposal's unique reference", null: true
30
31
  field :state, GraphQL::Types::String, "The answer status in which proposal is in", null: true
31
32
  field :answer, Decidim::Core::TranslatedFieldType, "The answer feedback for the status for this proposal", null: true
@@ -41,6 +42,9 @@ module Decidim
41
42
  field :created_in_meeting, GraphQL::Types::Boolean, "Whether this proposal comes from a meeting or not", method: :official_meeting?, null: true
42
43
  field :meeting, Decidim::Meetings::MeetingType, description: "If the proposal comes from a meeting, the related meeting", null: true
43
44
 
45
+ field :withdrawn_at, Decidim::Core::DateTimeType, description: "The date and time this proposal was withdrawn", null: true
46
+ field :withdrawn, GraphQL::Types::Boolean, "Whether this proposal has been withdrawn or not", method: :withdrawn?, null: true
47
+
44
48
  def meeting
45
49
  object.authors.first if object.official_meeting?
46
50
  end
@@ -51,19 +55,6 @@ module Decidim
51
55
  current_component = object.component
52
56
  object.proposal_votes_count unless current_component.current_settings.votes_hidden?
53
57
  end
54
-
55
- def self.authorized?(object, context)
56
- context[:proposal] = object
57
-
58
- chain = [
59
- allowed_to?(:read, :proposal, object, context),
60
- object.published?
61
- ].all?
62
-
63
- super && chain
64
- rescue Decidim::PermissionAction::PermissionNotSetError
65
- false
66
- end
67
58
  end
68
59
  end
69
60
  end
@@ -16,7 +16,9 @@ module Decidim
16
16
  end
17
17
  end
18
18
 
19
- class ProposalsType < Decidim::Core::ComponentType
19
+ class ProposalsType < Decidim::Api::Types::BaseObject
20
+ implements Decidim::Core::ComponentInterface
21
+
20
22
  graphql_name "Proposals"
21
23
  description "A proposals component of a participatory space."
22
24
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "decidim/proposals/admin_filter"
4
+
3
5
  module Decidim
4
6
  module Proposals
5
7
  # This is the engine that runs on the public interface of `decidim-proposals`.
@@ -25,6 +27,8 @@ module Decidim
25
27
  resources :proposal_notes, only: [:create]
26
28
  end
27
29
 
30
+ resources :proposal_states
31
+
28
32
  resources :participatory_texts, only: [:index] do
29
33
  collection do
30
34
  get :new_import
@@ -38,6 +42,10 @@ module Decidim
38
42
  root to: "proposals#index"
39
43
  end
40
44
 
45
+ initializer "decidim_proposals.admin_filters" do
46
+ Decidim::Proposals::AdminFilter.register_filter!
47
+ end
48
+
41
49
  def load_seed
42
50
  nil
43
51
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ class AdminFilter
6
+ def self.register_filter!
7
+ Decidim.admin_filter(:proposals) do |configuration|
8
+ configuration.add_filters(
9
+ :is_emendation_true,
10
+ :state_eq,
11
+ :with_any_state,
12
+ :scope_id_eq,
13
+ :category_id_eq,
14
+ :valuator_role_ids_has
15
+ )
16
+
17
+ configuration.add_filters_with_values(
18
+ is_emendation_true: %w(true false),
19
+ state_eq: state_eq_values,
20
+ with_any_state: %w(state_published state_not_published),
21
+ scope_id_eq: scope_ids_hash(scopes.top_level),
22
+ category_id_eq: category_ids_hash(categories.first_class),
23
+ valuator_role_ids_has: valuator_role_ids
24
+ )
25
+
26
+ configuration.add_dynamically_translated_filters(
27
+ :scope_id_eq,
28
+ :category_id_eq,
29
+ :valuator_role_ids_has,
30
+ :proposal_state_id_eq,
31
+ :state_eq
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -11,6 +11,11 @@ Decidim.register_component(:proposals) do |component|
11
11
  raise "Cannot destroy this component when there are proposals" if Decidim::Proposals::Proposal.where(component: instance).any?
12
12
  end
13
13
 
14
+ component.on(:create) do |instance|
15
+ admin_user = GlobalID::Locator.locate(instance.versions.first.whodunnit)
16
+ Decidim::Proposals.create_default_states!(instance, admin_user)
17
+ end
18
+
14
19
  component.data_portable_entities = ["Decidim::Proposals::Proposal"]
15
20
 
16
21
  component.newsletter_participant_entities = ["Decidim::Proposals::Proposal"]
@@ -33,7 +38,7 @@ Decidim.register_component(:proposals) do |component|
33
38
  settings.attribute :proposal_edit_time, type: :enum, default: "limited", choices: -> { %w(limited infinite) }
34
39
  settings.attribute :proposal_edit_before_minutes, type: :integer, default: 5, required: true
35
40
  settings.attribute :threshold_per_proposal, type: :integer, default: 0, required: true
36
- settings.attribute :can_accumulate_supports_beyond_threshold, type: :boolean, default: false
41
+ settings.attribute :can_accumulate_votes_beyond_threshold, type: :boolean, default: false
37
42
  settings.attribute :proposal_answering_enabled, type: :boolean, default: true
38
43
  settings.attribute :default_sort_order, type: :select, default: "automatic", choices: -> { POSSIBLE_SORT_ORDERS }
39
44
  settings.attribute :official_proposals_enabled, type: :boolean, default: true
@@ -56,8 +61,6 @@ Decidim.register_component(:proposals) do |component|
56
61
  settings.attribute :new_proposal_help_text, type: :text, translated: true, editor: true
57
62
  settings.attribute :proposal_wizard_step_1_help_text, type: :text, translated: true, editor: true
58
63
  settings.attribute :proposal_wizard_step_2_help_text, type: :text, translated: true, editor: true
59
- settings.attribute :proposal_wizard_step_3_help_text, type: :text, translated: true, editor: true
60
- settings.attribute :proposal_wizard_step_4_help_text, type: :text, translated: true, editor: true
61
64
  end
62
65
 
63
66
  component.settings(:step) do |settings|
@@ -99,14 +102,14 @@ Decidim.register_component(:proposals) do |component|
99
102
  end
100
103
 
101
104
  component.register_stat :proposals_count, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
102
- Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.except_withdrawn.not_hidden.count
105
+ Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.not_withdrawn.not_hidden.count
103
106
  end
104
107
 
105
108
  component.register_stat :proposals_accepted, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
106
109
  Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).accepted.not_hidden.count
107
110
  end
108
111
 
109
- component.register_stat :supports_count, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
112
+ component.register_stat :votes_count, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
110
113
  proposals = Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.not_hidden
111
114
  Decidim::Proposals::ProposalVote.where(proposal: proposals).count
112
115
  end
@@ -13,8 +13,6 @@ module Decidim
13
13
  routes do
14
14
  resources :proposals, except: [:destroy] do
15
15
  member do
16
- get :compare
17
- get :complete
18
16
  get :edit_draft
19
17
  patch :update_draft
20
18
  get :preview
@@ -57,6 +55,7 @@ module Decidim
57
55
  Decidim.icons.register(name: "arrow-right-s-fill", icon: "arrow-right-s-fill", category: "system", description: "", engine: :proposals)
58
56
  Decidim.icons.register(name: "bar-chart-2-line", icon: "bar-chart-2-line", category: "system", description: "", engine: :proposals)
59
57
  Decidim.icons.register(name: "scales-line", icon: "scales-line", category: "system", description: "", engine: :proposals)
58
+ Decidim.icons.register(name: "layout-grid-fill", icon: "layout-grid-fill", category: "system", description: "", engine: :proposals)
60
59
  end
61
60
 
62
61
  initializer "decidim_proposals.content_processors" do |_app|
@@ -92,19 +91,6 @@ module Decidim
92
91
  end
93
92
  end
94
93
 
95
- # Subscribes to ActiveSupport::Notifications that may affect a Proposal.
96
- initializer "decidim_proposals.subscribe_to_events" do
97
- # when a proposal is linked from a result
98
- event_name = "decidim.resourceable.included_proposals.created"
99
- ActiveSupport::Notifications.subscribe event_name do |_name, _started, _finished, _unique_id, data|
100
- payload = data[:this]
101
- if payload[:from_type] == Decidim::Accountability::Result.name && payload[:to_type] == Proposal.name
102
- proposal = Proposal.find(payload[:to_id])
103
- proposal.update(state: "accepted", state_published_at: Time.current)
104
- end
105
- end
106
- end
107
-
108
94
  initializer "decidim_proposals.add_cells_view_paths" do
109
95
  Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Proposals::Engine.root}/app/cells")
110
96
  Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Proposals::Engine.root}/app/views") # for proposal partials
@@ -6,9 +6,7 @@ module Decidim
6
6
  # This class is responsible for creating the imported proposal answers
7
7
  # and must be included in proposals component's import manifest.
8
8
  class ProposalAnswerCreator < Decidim::Admin::Import::Creator
9
- POSSIBLE_ANSWER_STATES = %w(evaluating accepted rejected).freeze
10
-
11
- # Retuns the resource class to be created with the provided data.
9
+ # Returns the resource class to be created with the provided data.
12
10
  def self.resource_klass
13
11
  Decidim::Proposals::Proposal
14
12
  end
@@ -58,10 +56,12 @@ module Decidim
58
56
 
59
57
  proposal.answer = answer
60
58
  proposal.answered_at = Time.current
61
- @initial_state = proposal.state
59
+ @initial_state = proposal.proposal_state
60
+
61
+ proposal_state = Decidim::Proposals::ProposalState.where(component:, token: state).first
62
62
 
63
- if POSSIBLE_ANSWER_STATES.include?(state)
64
- proposal.state = state
63
+ if proposal_state.present?
64
+ proposal.proposal_state = proposal_state
65
65
  proposal.state_published_at = Time.current if component.current_settings.publish_answers_immediately?
66
66
  else
67
67
  proposal.errors.add(:state, :invalid)
@@ -6,7 +6,7 @@ module Decidim
6
6
  # This class is responsible for creating the imported proposals
7
7
  # and must be included in proposals component's import manifest.
8
8
  class ProposalCreator < Decidim::Admin::Import::Creator
9
- # Retuns the resource class to be created with the provided data.
9
+ # Returns the resource class to be created with the provided data.
10
10
  def self.resource_klass
11
11
  Decidim::Proposals::Proposal
12
12
  end
@@ -41,14 +41,8 @@ module Decidim
41
41
 
42
42
  # Block-level calls ######################
43
43
 
44
- # Recarpet callback to preprocess the document.
45
- # Removes the HTML comment from the markdown file
46
- def preprocess(document)
47
- document.gsub(/<!--.*-->/, "")
48
- end
49
-
50
44
  # Recarpet callback to process headers.
51
- # Creates Paricipatory Text Proposals at Section and Subsection levels.
45
+ # Creates Participatory Text Proposals at Section and Subsection levels.
52
46
  def header(title, level)
53
47
  participatory_text_level = if level > 1
54
48
  Decidim::Proposals::ParticipatoryTextSection::LEVELS[:sub_section]
@@ -63,7 +57,7 @@ module Decidim
63
57
  end
64
58
 
65
59
  # Recarpet callback to process paragraphs.
66
- # Creates Paricipatory Text Proposals at Article level.
60
+ # Creates Participatory Text Proposals at Article level.
67
61
  def paragraph(text)
68
62
  return if text.blank?
69
63
 
@@ -44,14 +44,13 @@ module Decidim
44
44
  reference: proposal.reference,
45
45
  answer: ensure_translatable(proposal.answer),
46
46
  answered_at: proposal.answered_at,
47
- supports: (proposal.proposal_votes_count unless
48
- proposal.component.current_settings.votes_hidden?),
47
+ votes: proposal.proposal_votes_count,
49
48
  endorsements: {
50
49
  total_count: proposal.endorsements.size,
51
50
  user_endorsements:
52
51
  },
53
52
  comments: proposal.comments_count,
54
- attachments: proposal.attachments.count,
53
+ attachments: proposal.attachments.size,
55
54
  followers: proposal.follows.size,
56
55
  published_at: proposal.published_at,
57
56
  url:,
@@ -61,7 +60,9 @@ module Decidim
61
60
  original_proposal: {
62
61
  title: proposal&.amendable&.title,
63
62
  url: original_proposal_url
64
- }
63
+ },
64
+ withdrawn: proposal.withdrawn?,
65
+ withdrawn_at: proposal.withdrawn_at
65
66
  }
66
67
  end
67
68
 
@@ -131,7 +132,7 @@ module Decidim
131
132
 
132
133
  def author_url(author)
133
134
  if author.respond_to?(:nickname)
134
- profile_url(author) # is a Decidim::User or Decidim::UserGroup
135
+ profile_url(author.nickname) # is a Decidim::User or Decidim::UserGroup
135
136
  elsif author.respond_to?(:title)
136
137
  meeting_url(author) # is a Decidim::Meetings::Meeting
137
138
  else
@@ -139,10 +140,8 @@ module Decidim
139
140
  end
140
141
  end
141
142
 
142
- def profile_url(author)
143
- return "" if author.respond_to?(:deleted?) && author.deleted?
144
-
145
- Decidim::Core::Engine.routes.url_helpers.profile_url(author.nickname, host:)
143
+ def profile_url(nickname)
144
+ Decidim::Core::Engine.routes.url_helpers.profile_url(nickname, host:)
146
145
  end
147
146
 
148
147
  def meeting_url(meeting)
@@ -15,6 +15,8 @@ module Decidim
15
15
  def call
16
16
  component = create_component!
17
17
 
18
+ Decidim::Proposals.create_default_states!(component, admin_user)
19
+
18
20
  5.times do |n|
19
21
  proposal = create_proposal!(component:)
20
22
 
@@ -34,6 +36,9 @@ module Decidim
34
36
  end
35
37
 
36
38
  update_traceability!(component:)
39
+
40
+ create_report!(reportable: Decidim::Proposals::Proposal.take, current_user: Decidim::User.take)
41
+ hide_report!(reportable: Decidim::Proposals::Proposal.take)
37
42
  end
38
43
 
39
44
  def organization
@@ -54,6 +59,7 @@ module Decidim
54
59
  participatory_space:,
55
60
  settings: {
56
61
  vote_limit: 0,
62
+ attachments_allowed: [true, false].sample,
57
63
  collaborative_drafts_enabled: true
58
64
  },
59
65
  step_settings:
@@ -69,20 +75,9 @@ module Decidim
69
75
  end
70
76
  end
71
77
 
72
- # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
73
78
  def create_proposal!(component:)
74
- n = rand(5)
75
- state, answer, state_published_at = if n > 3
76
- ["accepted", Decidim::Faker::Localized.sentence(word_count: 10), Time.current]
77
- elsif n > 2
78
- ["rejected", nil, Time.current]
79
- elsif n > 1
80
- ["evaluating", nil, Time.current]
81
- elsif n.positive?
82
- ["accepted", Decidim::Faker::Localized.sentence(word_count: 10), nil]
83
- else
84
- ["not_answered", nil, nil]
85
- end
79
+ proposal_state, answer, state_published_at = random_state_answer
80
+ proposal_state = Decidim::Proposals::ProposalState.where(component:, token: proposal_state).first
86
81
 
87
82
  params = {
88
83
  component:,
@@ -90,9 +85,9 @@ module Decidim
90
85
  scope: random_scope(participatory_space:),
91
86
  title: { en: ::Faker::Lorem.sentence(word_count: 2) },
92
87
  body: { en: ::Faker::Lorem.paragraphs(number: 2).join("\n") },
93
- state:,
88
+ proposal_state:,
94
89
  answer:,
95
- answered_at: state.present? ? Time.current : nil,
90
+ answered_at: proposal_state.present? ? Time.current : nil,
96
91
  state_published_at:,
97
92
  published_at: Time.current
98
93
  }
@@ -104,26 +99,58 @@ module Decidim
104
99
  visibility: "all"
105
100
  ) do
106
101
  proposal = Decidim::Proposals::Proposal.new(params)
107
- n = 3 if n == 2 && !Decidim.module_installed?(:meetings)
108
-
109
- coauthor = case n
110
- when 0
111
- Decidim::User.where(organization:).sample
112
- when 1
113
- Decidim::UserGroup.where(organization:).sample
114
- when 2
115
- meeting_component = participatory_space.components.find_by(manifest_name: "meetings")
116
-
117
- Decidim::Meetings::Meeting.where(component: meeting_component).sample
118
- else
119
- organization
120
- end
102
+ coauthor = random_coauthor
121
103
  proposal.add_coauthor(coauthor)
122
104
  proposal.save!
105
+
106
+ Decidim::EventsManager.publish(
107
+ event: "decidim.events.proposals.proposal_published_for_space",
108
+ event_class: Decidim::Proposals::PublishProposalEvent,
109
+ resource: proposal,
110
+ followers: proposal.participatory_space.followers
111
+ )
112
+
123
113
  proposal
124
114
  end
115
+
116
+ create_attachment(attached_to: proposal, filename: "city.jpeg") if component.settings.attachments_allowed?
117
+
118
+ proposal
119
+ end
120
+
121
+ def random_state_answer
122
+ n = rand(5)
123
+
124
+ if n > 3
125
+ [:accepted, Decidim::Faker::Localized.sentence(word_count: 10), Time.current]
126
+ elsif n > 2
127
+ [:rejected, nil, Time.current]
128
+ elsif n > 1
129
+ [:evaluating, nil, Time.current]
130
+ elsif n.positive?
131
+ [:accepted, Decidim::Faker::Localized.sentence(word_count: 10), nil]
132
+ else
133
+ [:not_answered, nil, nil]
134
+ end
135
+ end
136
+
137
+ def random_coauthor
138
+ n = rand(5)
139
+ n = 3 if n == 2 && !Decidim.module_installed?(:meetings)
140
+
141
+ case n
142
+ when 0
143
+ Decidim::User.where(organization:).sample
144
+ when 1
145
+ Decidim::UserGroup.where(organization:).sample
146
+ when 2
147
+ meeting_component = participatory_space.components.find_by(manifest_name: "meetings")
148
+
149
+ Decidim::Meetings::Meeting.where(component: meeting_component).sample
150
+ else
151
+ organization
152
+ end
125
153
  end
126
- # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
127
154
 
128
155
  def random_nickname
129
156
  "#{::Faker::Twitter.unique.screen_name}-#{SecureRandom.hex(4)}"[0, 20]
@@ -136,15 +163,7 @@ module Decidim
136
163
  end
137
164
 
138
165
  def create_emendation!(proposal:)
139
- author = Decidim::User.find_or_initialize_by(email: random_email(suffix: "amendment"))
140
- author.update!(
141
- password: "decidim123456789",
142
- name: "#{::Faker::Name.name} #{participatory_space.id}",
143
- nickname: random_nickname,
144
- organization:,
145
- tos_agreement: "1",
146
- confirmed_at: Time.current
147
- )
166
+ author = find_or_initialize_user_by(email: random_email(suffix: "amendment"))
148
167
 
149
168
  group = Decidim::UserGroup.create!(
150
169
  name: ::Faker::Name.name,
@@ -171,7 +190,7 @@ module Decidim
171
190
  scope: random_scope(participatory_space:),
172
191
  title: { en: "#{proposal.title["en"]} #{::Faker::Lorem.sentence(word_count: 1)}" },
173
192
  body: { en: "#{proposal.body["en"]} #{::Faker::Lorem.sentence(word_count: 3)}" },
174
- state: "evaluating",
193
+ proposal_state: Decidim::Proposals::ProposalState.where(component: proposal.component, token: :evaluating).first,
175
194
  answer: nil,
176
195
  answered_at: Time.current,
177
196
  published_at: Time.current
@@ -200,17 +219,7 @@ module Decidim
200
219
  end
201
220
 
202
221
  def create_proposal_votes!(proposal:, emendation: nil)
203
- author = Decidim::User.find_or_initialize_by(email: random_email(suffix: "vote"))
204
- author.update!(
205
- password: "decidim123456789",
206
- name: "#{::Faker::Name.name} #{participatory_space.id}",
207
- nickname: random_nickname,
208
- organization:,
209
- tos_agreement: "1",
210
- confirmed_at: Time.current,
211
- personal_url: ::Faker::Internet.url,
212
- about: ::Faker::Lorem.paragraph(sentence_count: 2)
213
- )
222
+ author = find_or_initialize_user_by(email: random_email(suffix: "vote"))
214
223
 
215
224
  Decidim::Proposals::ProposalVote.create!(proposal:, author:) unless proposal.published_state? && proposal.rejected?
216
225
  Decidim::Proposals::ProposalVote.create!(proposal: emendation, author:) if emendation