decidim-proposals 0.28.1 → 0.29.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) 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_cell.rb +1 -1
  5. data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +1 -1
  6. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +2 -3
  7. data/app/cells/decidim/proposals/proposal_cell.rb +2 -0
  8. data/app/cells/decidim/proposals/proposal_g/show.erb +23 -0
  9. data/app/cells/decidim/proposals/proposal_g_cell.rb +48 -0
  10. data/app/cells/decidim/proposals/proposal_l_cell.rb +0 -2
  11. data/app/cells/decidim/proposals/proposal_metadata_cell.rb +23 -15
  12. data/app/commands/decidim/proposals/admin/answer_proposal.rb +2 -1
  13. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +7 -5
  14. data/app/commands/decidim/proposals/admin/create_proposal.rb +2 -2
  15. data/app/commands/decidim/proposals/admin/create_proposal_state.rb +15 -0
  16. data/app/commands/decidim/proposals/admin/destroy_proposal_state.rb +10 -0
  17. data/app/commands/decidim/proposals/admin/import_proposals.rb +10 -2
  18. data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +4 -21
  19. data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +6 -4
  20. data/app/commands/decidim/proposals/admin/update_proposal_state.rb +13 -0
  21. data/app/commands/decidim/proposals/create_proposal.rb +21 -2
  22. data/app/commands/decidim/proposals/update_proposal.rb +2 -2
  23. data/app/commands/decidim/proposals/vote_proposal.rb +1 -1
  24. data/app/commands/decidim/proposals/withdraw_proposal.rb +3 -7
  25. data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +10 -22
  26. data/app/controllers/concerns/decidim/proposals/orderable.rb +1 -1
  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 +30 -35
  31. data/app/events/decidim/proposals/proposal_state_changed_event.rb +37 -0
  32. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +5 -1
  33. data/app/forms/decidim/proposals/admin/proposal_state_form.rb +22 -0
  34. data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +1 -1
  35. data/app/forms/decidim/proposals/admin/proposals_import_form.rb +1 -1
  36. data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +12 -14
  37. data/app/forms/decidim/proposals/proposal_form.rb +25 -4
  38. data/app/forms/decidim/proposals/reject_access_to_collaborative_draft_form.rb +1 -1
  39. data/app/forms/decidim/proposals/request_access_to_collaborative_draft_form.rb +1 -1
  40. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +7 -17
  41. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +13 -89
  42. data/app/helpers/decidim/proposals/application_helper.rb +17 -11
  43. data/app/helpers/decidim/proposals/proposal_cells_helper.rb +6 -2
  44. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +3 -3
  45. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +5 -8
  46. data/app/helpers/decidim/proposals/proposals_helper.rb +18 -24
  47. data/app/models/decidim/proposals/proposal.rb +78 -28
  48. data/app/models/decidim/proposals/proposal_state.rb +58 -0
  49. data/app/packs/documents/decidim/proposals/participatory_texts/participatory_text.md +1 -3
  50. data/app/packs/entrypoints/participatory_texts_admin.js +2 -0
  51. data/app/packs/images/decidim/proposals/proposal-placeholder-card-g.svg +15 -0
  52. data/app/packs/src/decidim/proposals/add_proposal.js +2 -0
  53. data/app/packs/src/decidim/proposals/admin/proposals.js +43 -8
  54. data/app/packs/stylesheets/decidim/proposals/admin/participatory_texts.scss +23 -0
  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/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -5
  58. data/app/presenters/decidim/proposals/admin_log/proposal_state_presenter.rb +21 -0
  59. data/app/presenters/decidim/proposals/admin_log/value_types/proposal_title_body_presenter.rb +1 -3
  60. data/app/presenters/decidim/proposals/log/valuation_assignment_presenter.rb +1 -1
  61. data/app/presenters/decidim/proposals/proposal_presenter.rb +12 -3
  62. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +1 -1
  63. data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +1 -1
  64. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +4 -4
  65. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +1 -1
  66. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +1 -1
  67. data/app/services/decidim/proposals/diff_renderer.rb +1 -1
  68. data/app/views/decidim/proposals/admin/imports/_proposals_fields.html.erb +1 -1
  69. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +9 -5
  70. data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +1 -1
  71. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +3 -3
  72. data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +67 -0
  73. data/app/views/decidim/proposals/admin/proposal_states/edit.html.erb +18 -0
  74. data/app/views/decidim/proposals/admin/proposal_states/index.html.erb +50 -0
  75. data/app/views/decidim/proposals/admin/proposal_states/new.html.erb +18 -0
  76. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +0 -2
  77. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +6 -6
  78. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +2 -2
  79. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +11 -7
  80. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +7 -5
  81. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_merge.html.erb +2 -2
  82. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +2 -2
  83. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +2 -2
  84. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +2 -2
  85. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_split.html.erb +2 -2
  86. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +11 -7
  87. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_valuators_picker.html.erb +12 -0
  88. data/app/views/decidim/proposals/admin/proposals/index.html.erb +11 -5
  89. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
  90. data/app/views/decidim/proposals/admin/proposals/show.html.erb +3 -2
  91. data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +3 -3
  92. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +12 -5
  93. data/app/views/decidim/proposals/proposals/_proposal.html.erb +1 -1
  94. data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +5 -3
  95. data/app/views/decidim/proposals/proposals/_proposals.html.erb +9 -3
  96. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +1 -1
  97. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +3 -3
  98. data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +0 -1
  99. data/app/views/decidim/proposals/proposals/new.html.erb +2 -7
  100. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +4 -4
  101. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_votes_count.html.erb +8 -8
  102. data/app/views/decidim/proposals/proposals/show.html.erb +1 -1
  103. data/config/assets.rb +1 -0
  104. data/config/locales/ar.yml +5 -119
  105. data/config/locales/bg.yml +741 -42
  106. data/config/locales/ca.yml +88 -85
  107. data/config/locales/cs.yml +62 -124
  108. data/config/locales/de.yml +111 -108
  109. data/config/locales/el.yml +2 -123
  110. data/config/locales/en.yml +113 -110
  111. data/config/locales/es-MX.yml +86 -83
  112. data/config/locales/es-PY.yml +88 -85
  113. data/config/locales/es.yml +87 -84
  114. data/config/locales/eu.yml +104 -108
  115. data/config/locales/fi-plain.yml +91 -88
  116. data/config/locales/fi.yml +113 -110
  117. data/config/locales/fr-CA.yml +89 -86
  118. data/config/locales/fr.yml +81 -78
  119. data/config/locales/ga-IE.yml +1 -21
  120. data/config/locales/gl.yml +4 -109
  121. data/config/locales/he-IL.yml +0 -13
  122. data/config/locales/hu.yml +14 -91
  123. data/config/locales/id-ID.yml +1 -98
  124. data/config/locales/is-IS.yml +0 -35
  125. data/config/locales/it.yml +5 -97
  126. data/config/locales/ja.yml +112 -109
  127. data/config/locales/lb.yml +1 -0
  128. data/config/locales/lt.yml +2 -127
  129. data/config/locales/lv.yml +1 -97
  130. data/config/locales/nl.yml +5 -100
  131. data/config/locales/no.yml +2 -112
  132. data/config/locales/pl.yml +201 -107
  133. data/config/locales/pt-BR.yml +4 -82
  134. data/config/locales/pt.yml +4 -112
  135. data/config/locales/ro-RO.yml +5 -114
  136. data/config/locales/ru.yml +1 -55
  137. data/config/locales/sk.yml +1 -104
  138. data/config/locales/sv.yml +20 -108
  139. data/config/locales/tr-TR.yml +57 -103
  140. data/config/locales/uk.yml +2 -56
  141. data/config/locales/zh-CN.yml +1 -100
  142. data/config/locales/zh-TW.yml +2 -121
  143. data/db/migrate/20240110203500_add_withdrawn_at_field_to_proposals.rb +27 -0
  144. data/db/migrate/20240110203501_create_decidim_proposals_proposal_state.rb +14 -0
  145. data/db/migrate/20240110203502_add_state_id_to_decidim_proposals_proposals.rb +13 -0
  146. data/db/migrate/20240110203503_remove_state_from_decidim_proposals_proposals.rb +11 -0
  147. data/db/migrate/20240110203504_create_default_proposal_states.rb +31 -0
  148. data/db/migrate/20240209092404_change_color_fields_on_proposals_states.rb +54 -0
  149. data/decidim-proposals.gemspec +2 -2
  150. data/lib/decidim/api/proposal_type.rb +4 -0
  151. data/lib/decidim/proposals/admin_engine.rb +8 -0
  152. data/lib/decidim/proposals/admin_filter.rb +37 -0
  153. data/lib/decidim/proposals/component.rb +10 -9
  154. data/lib/decidim/proposals/engine.rb +1 -15
  155. data/lib/decidim/proposals/import/proposal_answer_creator.rb +6 -6
  156. data/lib/decidim/proposals/import/proposal_creator.rb +1 -1
  157. data/lib/decidim/proposals/markdown_to_proposals.rb +2 -8
  158. data/lib/decidim/proposals/proposal_serializer.rb +56 -3
  159. data/lib/decidim/proposals/seeds.rb +60 -47
  160. data/lib/decidim/proposals/test/factories.rb +77 -9
  161. data/lib/decidim/proposals/version.rb +1 -1
  162. data/lib/decidim/proposals.rb +84 -12
  163. data/lib/tasks/proposals/upgrade/decidim_proposals_upgrade_tasks.rake +32 -0
  164. metadata +55 -40
  165. data/app/events/decidim/proposals/accepted_proposal_event.rb +0 -17
  166. data/app/events/decidim/proposals/evaluating_proposal_event.rb +0 -11
  167. data/app/events/decidim/proposals/rejected_proposal_event.rb +0 -17
  168. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +0 -44
  169. data/app/presenters/decidim/proposals/log/resource_presenter.rb +0 -22
  170. data/app/queries/decidim/proposals/similar_proposals.rb +0 -67
  171. data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +0 -0
  172. data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +0 -3
  173. data/app/views/decidim/proposals/proposals/compare.html.erb +0 -24
  174. data/app/views/decidim/proposals/proposals/complete.html.erb +0 -31
  175. data/lib/tasks/proposals/upgrade/decdim_proposal_upgrade_tasks.rake +0 -34
@@ -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
@@ -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
@@ -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
@@ -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
@@ -13,6 +11,11 @@ Decidim.register_component(:proposals) do |component|
13
11
  raise "Cannot destroy this component when there are proposals" if Decidim::Proposals::Proposal.where(component: instance).any?
14
12
  end
15
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
+
16
19
  component.data_portable_entities = ["Decidim::Proposals::Proposal"]
17
20
 
18
21
  component.newsletter_participant_entities = ["Decidim::Proposals::Proposal"]
@@ -23,7 +26,7 @@ Decidim.register_component(:proposals) do |component|
23
26
 
24
27
  component.permissions_class_name = "Decidim::Proposals::Permissions"
25
28
 
26
- POSSIBLE_SORT_ORDERS = %w(default random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
29
+ POSSIBLE_SORT_ORDERS = %w(automatic random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
27
30
 
28
31
  component.settings(:global) do |settings|
29
32
  settings.attribute :scopes_enabled, type: :boolean, default: false
@@ -35,9 +38,9 @@ Decidim.register_component(:proposals) do |component|
35
38
  settings.attribute :proposal_edit_time, type: :enum, default: "limited", choices: -> { %w(limited infinite) }
36
39
  settings.attribute :proposal_edit_before_minutes, type: :integer, default: 5, required: true
37
40
  settings.attribute :threshold_per_proposal, type: :integer, default: 0, required: true
38
- settings.attribute :can_accumulate_supports_beyond_threshold, type: :boolean, default: false
41
+ settings.attribute :can_accumulate_votes_beyond_threshold, type: :boolean, default: false
39
42
  settings.attribute :proposal_answering_enabled, type: :boolean, default: true
40
- settings.attribute :default_sort_order, type: :select, default: "default", choices: -> { POSSIBLE_SORT_ORDERS }
43
+ settings.attribute :default_sort_order, type: :select, default: "automatic", choices: -> { POSSIBLE_SORT_ORDERS }
41
44
  settings.attribute :official_proposals_enabled, type: :boolean, default: true
42
45
  settings.attribute :comments_enabled, type: :boolean, default: true
43
46
  settings.attribute :comments_max_length, type: :integer, required: true
@@ -58,8 +61,6 @@ Decidim.register_component(:proposals) do |component|
58
61
  settings.attribute :new_proposal_help_text, type: :text, translated: true, editor: true
59
62
  settings.attribute :proposal_wizard_step_1_help_text, type: :text, translated: true, editor: true
60
63
  settings.attribute :proposal_wizard_step_2_help_text, type: :text, translated: true, editor: true
61
- settings.attribute :proposal_wizard_step_3_help_text, type: :text, translated: true, editor: true
62
- settings.attribute :proposal_wizard_step_4_help_text, type: :text, translated: true, editor: true
63
64
  end
64
65
 
65
66
  component.settings(:step) do |settings|
@@ -101,14 +102,14 @@ Decidim.register_component(:proposals) do |component|
101
102
  end
102
103
 
103
104
  component.register_stat :proposals_count, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
104
- 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
105
106
  end
106
107
 
107
108
  component.register_stat :proposals_accepted, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
108
109
  Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).accepted.not_hidden.count
109
110
  end
110
111
 
111
- 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|
112
113
  proposals = Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.not_hidden
113
114
  Decidim::Proposals::ProposalVote.where(proposal: proposals).count
114
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
 
@@ -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
@@ -41,13 +44,13 @@ module Decidim
41
44
  reference: proposal.reference,
42
45
  answer: ensure_translatable(proposal.answer),
43
46
  answered_at: proposal.answered_at,
44
- supports: proposal.proposal_votes_count,
47
+ votes: proposal.proposal_votes_count,
45
48
  endorsements: {
46
49
  total_count: proposal.endorsements.size,
47
50
  user_endorsements:
48
51
  },
49
52
  comments: proposal.comments_count,
50
- attachments: proposal.attachments.count,
53
+ attachments: proposal.attachments.size,
51
54
  followers: proposal.follows.size,
52
55
  published_at: proposal.published_at,
53
56
  url:,
@@ -57,7 +60,9 @@ module Decidim
57
60
  original_proposal: {
58
61
  title: proposal&.amendable&.title,
59
62
  url: original_proposal_url
60
- }
63
+ },
64
+ withdrawn: proposal.withdrawn?,
65
+ withdrawn_at: proposal.withdrawn_at
61
66
  }
62
67
  end
63
68
 
@@ -102,6 +107,54 @@ module Decidim
102
107
 
103
108
  convert_to_text(value)
104
109
  end
110
+
111
+ def author_fields
112
+ is_author_user_group = resource.coauthorships.map(&:decidim_user_group_id).any?
113
+
114
+ {
115
+ id: resource.authors.map(&:id),
116
+ name: resource.authors.map do |author|
117
+ author_name(is_author_user_group ? resource.coauthorships.first.user_group : author)
118
+ end,
119
+ url: resource.authors.map do |author|
120
+ author_url(is_author_user_group ? resource.coauthorships.first.user_group : author)
121
+ end
122
+ }
123
+ end
124
+
125
+ def author_name(author)
126
+ if author.respond_to?(:name)
127
+ translated_attribute(author.name) # is a Decidim::User or Decidim::Organization or Decidim::UserGroup
128
+ elsif author.respond_to?(:title)
129
+ translated_attribute(author.title) # is a Decidim::Meetings::Meeting
130
+ end
131
+ end
132
+
133
+ def author_url(author)
134
+ if author.respond_to?(:nickname)
135
+ profile_url(author.nickname) # is a Decidim::User or Decidim::UserGroup
136
+ elsif author.respond_to?(:title)
137
+ meeting_url(author) # is a Decidim::Meetings::Meeting
138
+ else
139
+ root_url # is a Decidim::Organization
140
+ end
141
+ end
142
+
143
+ def profile_url(nickname)
144
+ Decidim::Core::Engine.routes.url_helpers.profile_url(nickname, host:)
145
+ end
146
+
147
+ def meeting_url(meeting)
148
+ Decidim::EngineRouter.main_proxy(meeting.component).meeting_url(id: meeting.id, host:)
149
+ end
150
+
151
+ def root_url
152
+ Decidim::Core::Engine.routes.url_helpers.root_url(host:)
153
+ end
154
+
155
+ def host
156
+ resource.organization.host
157
+ end
105
158
  end
106
159
  end
107
160
  end
@@ -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:
@@ -70,18 +76,8 @@ module Decidim
70
76
  end
71
77
 
72
78
  def create_proposal!(component:)
73
- n = rand(5)
74
- state, answer, state_published_at = if n > 3
75
- ["accepted", Decidim::Faker::Localized.sentence(word_count: 10), Time.current]
76
- elsif n > 2
77
- ["rejected", nil, Time.current]
78
- elsif n > 1
79
- ["evaluating", nil, Time.current]
80
- elsif n.positive?
81
- ["accepted", Decidim::Faker::Localized.sentence(word_count: 10), nil]
82
- else
83
- ["not_answered", nil, nil]
84
- end
79
+ proposal_state, answer, state_published_at = random_state_answer
80
+ proposal_state = Decidim::Proposals::ProposalState.where(component:, token: proposal_state).first
85
81
 
86
82
  params = {
87
83
  component:,
@@ -89,9 +85,9 @@ module Decidim
89
85
  scope: random_scope(participatory_space:),
90
86
  title: { en: ::Faker::Lorem.sentence(word_count: 2) },
91
87
  body: { en: ::Faker::Lorem.paragraphs(number: 2).join("\n") },
92
- state:,
88
+ proposal_state:,
93
89
  answer:,
94
- answered_at: state.present? ? Time.current : nil,
90
+ answered_at: proposal_state.present? ? Time.current : nil,
95
91
  state_published_at:,
96
92
  published_at: Time.current
97
93
  }
@@ -103,22 +99,57 @@ module Decidim
103
99
  visibility: "all"
104
100
  ) do
105
101
  proposal = Decidim::Proposals::Proposal.new(params)
106
- meeting_component = participatory_space.components.find_by(manifest_name: "meetings")
107
-
108
- coauthor = case n
109
- when 0
110
- Decidim::User.where(organization:).sample
111
- when 1
112
- Decidim::UserGroup.where(organization:).sample
113
- when 2
114
- Decidim::Meetings::Meeting.where(component: meeting_component).sample
115
- else
116
- organization
117
- end
102
+ coauthor = random_coauthor
118
103
  proposal.add_coauthor(coauthor)
119
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
+
120
113
  proposal
121
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
122
153
  end
123
154
 
124
155
  def random_nickname
@@ -132,15 +163,7 @@ module Decidim
132
163
  end
133
164
 
134
165
  def create_emendation!(proposal:)
135
- author = Decidim::User.find_or_initialize_by(email: random_email(suffix: "amendment"))
136
- author.update!(
137
- password: "decidim123456789",
138
- name: "#{::Faker::Name.name} #{participatory_space.id}",
139
- nickname: random_nickname,
140
- organization:,
141
- tos_agreement: "1",
142
- confirmed_at: Time.current
143
- )
166
+ author = find_or_initialize_user_by(email: random_email(suffix: "amendment"))
144
167
 
145
168
  group = Decidim::UserGroup.create!(
146
169
  name: ::Faker::Name.name,
@@ -167,7 +190,7 @@ module Decidim
167
190
  scope: random_scope(participatory_space:),
168
191
  title: { en: "#{proposal.title["en"]} #{::Faker::Lorem.sentence(word_count: 1)}" },
169
192
  body: { en: "#{proposal.body["en"]} #{::Faker::Lorem.sentence(word_count: 3)}" },
170
- state: "evaluating",
193
+ proposal_state: Decidim::Proposals::ProposalState.where(component: proposal.component, token: :evaluating).first,
171
194
  answer: nil,
172
195
  answered_at: Time.current,
173
196
  published_at: Time.current
@@ -196,17 +219,7 @@ module Decidim
196
219
  end
197
220
 
198
221
  def create_proposal_votes!(proposal:, emendation: nil)
199
- author = Decidim::User.find_or_initialize_by(email: random_email(suffix: "vote"))
200
- author.update!(
201
- password: "decidim123456789",
202
- name: "#{::Faker::Name.name} #{participatory_space.id}",
203
- nickname: random_nickname,
204
- organization:,
205
- tos_agreement: "1",
206
- confirmed_at: Time.current,
207
- personal_url: ::Faker::Internet.url,
208
- about: ::Faker::Lorem.paragraph(sentence_count: 2)
209
- )
222
+ author = find_or_initialize_user_by(email: random_email(suffix: "vote"))
210
223
 
211
224
  Decidim::Proposals::ProposalVote.create!(proposal:, author:) unless proposal.published_state? && proposal.rejected?
212
225
  Decidim::Proposals::ProposalVote.create!(proposal: emendation, author:) if emendation