decidim-proposals 0.28.2 → 0.29.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -18
- data/app/cells/decidim/proposals/cost_report_cell.rb +0 -3
- data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
- data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +1 -1
- data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +2 -3
- data/app/cells/decidim/proposals/proposal_cell.rb +2 -0
- data/app/cells/decidim/proposals/proposal_g/show.erb +23 -0
- data/app/cells/decidim/proposals/proposal_g_cell.rb +48 -0
- data/app/cells/decidim/proposals/proposal_l_cell.rb +0 -2
- data/app/cells/decidim/proposals/proposal_metadata_cell.rb +23 -15
- data/app/commands/decidim/proposals/admin/answer_proposal.rb +2 -1
- data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +7 -5
- data/app/commands/decidim/proposals/admin/create_proposal.rb +2 -2
- data/app/commands/decidim/proposals/admin/create_proposal_state.rb +15 -0
- data/app/commands/decidim/proposals/admin/destroy_proposal_state.rb +10 -0
- data/app/commands/decidim/proposals/admin/import_proposals.rb +10 -2
- data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +4 -21
- data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +6 -4
- data/app/commands/decidim/proposals/admin/update_proposal_state.rb +13 -0
- data/app/commands/decidim/proposals/create_proposal.rb +21 -2
- data/app/commands/decidim/proposals/update_proposal.rb +2 -2
- data/app/commands/decidim/proposals/vote_proposal.rb +1 -1
- data/app/commands/decidim/proposals/withdraw_proposal.rb +3 -7
- data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +10 -22
- data/app/controllers/decidim/proposals/admin/proposal_states_controller.rb +86 -0
- data/app/controllers/decidim/proposals/admin/proposals_controller.rb +4 -0
- data/app/controllers/decidim/proposals/admin/valuation_assignments_controller.rb +8 -11
- data/app/controllers/decidim/proposals/proposals_controller.rb +30 -35
- data/app/events/decidim/proposals/proposal_state_changed_event.rb +37 -0
- data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +5 -1
- data/app/forms/decidim/proposals/admin/proposal_state_form.rb +22 -0
- data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +1 -1
- data/app/forms/decidim/proposals/admin/proposals_import_form.rb +1 -1
- data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +12 -14
- data/app/forms/decidim/proposals/proposal_form.rb +25 -4
- data/app/forms/decidim/proposals/reject_access_to_collaborative_draft_form.rb +1 -1
- data/app/forms/decidim/proposals/request_access_to_collaborative_draft_form.rb +1 -1
- data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +7 -17
- data/app/helpers/decidim/proposals/admin/proposals_helper.rb +13 -89
- data/app/helpers/decidim/proposals/application_helper.rb +16 -10
- data/app/helpers/decidim/proposals/proposal_cells_helper.rb +6 -2
- data/app/helpers/decidim/proposals/proposal_votes_helper.rb +3 -3
- data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +5 -8
- data/app/helpers/decidim/proposals/proposals_helper.rb +18 -24
- data/app/models/decidim/proposals/proposal.rb +78 -28
- data/app/models/decidim/proposals/proposal_state.rb +58 -0
- data/app/packs/documents/decidim/proposals/participatory_texts/participatory_text.md +1 -3
- data/app/packs/images/decidim/proposals/proposal-placeholder-card-g.svg +15 -0
- data/app/packs/src/decidim/proposals/add_proposal.js +2 -0
- data/app/packs/src/decidim/proposals/admin/proposals.js +43 -8
- data/app/packs/stylesheets/decidim/proposals/proposals.scss +39 -1
- data/app/permissions/decidim/proposals/admin/permissions.rb +16 -4
- data/app/presenters/decidim/proposals/admin_log/proposal_state_presenter.rb +21 -0
- data/app/presenters/decidim/proposals/proposal_presenter.rb +12 -3
- data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +1 -1
- data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +1 -1
- data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +4 -4
- data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +1 -1
- data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +1 -1
- data/app/services/decidim/proposals/diff_renderer.rb +1 -1
- data/app/views/decidim/proposals/admin/imports/_proposals_fields.html.erb +1 -1
- data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +3 -2
- data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +3 -3
- data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +67 -0
- data/app/views/decidim/proposals/admin/proposal_states/edit.html.erb +18 -0
- data/app/views/decidim/proposals/admin/proposal_states/index.html.erb +50 -0
- data/app/views/decidim/proposals/admin/proposal_states/new.html.erb +18 -0
- data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +0 -2
- data/app/views/decidim/proposals/admin/proposals/_form.html.erb +6 -6
- data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +11 -7
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +7 -5
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_merge.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_split.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +11 -7
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_valuators_picker.html.erb +12 -0
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +9 -5
- data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/show.html.erb +3 -2
- data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +3 -3
- data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +12 -5
- data/app/views/decidim/proposals/proposals/_proposal.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_proposals.html.erb +9 -3
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +3 -3
- data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +0 -1
- data/app/views/decidim/proposals/proposals/new.html.erb +2 -7
- data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +4 -4
- data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_votes_count.html.erb +8 -8
- data/app/views/decidim/proposals/proposals/show.html.erb +1 -1
- data/config/locales/ar.yml +5 -114
- data/config/locales/bg.yml +109 -106
- data/config/locales/ca.yml +80 -77
- data/config/locales/cs.yml +60 -119
- data/config/locales/de.yml +107 -104
- data/config/locales/el.yml +2 -118
- data/config/locales/en.yml +109 -106
- data/config/locales/es-MX.yml +82 -79
- data/config/locales/es-PY.yml +84 -81
- data/config/locales/es.yml +81 -78
- data/config/locales/eu.yml +100 -104
- data/config/locales/fi-plain.yml +87 -84
- data/config/locales/fi.yml +109 -106
- data/config/locales/fr-CA.yml +85 -82
- data/config/locales/fr.yml +77 -74
- data/config/locales/ga-IE.yml +1 -27
- data/config/locales/gl.yml +4 -104
- data/config/locales/he-IL.yml +0 -13
- data/config/locales/hu.yml +14 -88
- data/config/locales/id-ID.yml +1 -97
- data/config/locales/is-IS.yml +0 -33
- data/config/locales/it.yml +5 -96
- data/config/locales/ja.yml +108 -105
- data/config/locales/lb.yml +1 -0
- data/config/locales/lt.yml +2 -122
- data/config/locales/lv.yml +1 -96
- data/config/locales/nl.yml +5 -95
- data/config/locales/no.yml +2 -107
- data/config/locales/pl.yml +105 -113
- data/config/locales/pt-BR.yml +4 -81
- data/config/locales/pt.yml +4 -107
- data/config/locales/ro-RO.yml +5 -110
- data/config/locales/ru.yml +1 -53
- data/config/locales/sk.yml +1 -103
- data/config/locales/sv.yml +20 -103
- data/config/locales/tr-TR.yml +56 -103
- data/config/locales/uk.yml +2 -54
- data/config/locales/zh-CN.yml +1 -99
- data/config/locales/zh-TW.yml +2 -116
- data/db/migrate/20240110203500_add_withdrawn_at_field_to_proposals.rb +27 -0
- data/db/migrate/20240110203501_create_decidim_proposals_proposal_state.rb +15 -0
- data/db/migrate/20240110203502_add_state_id_to_decidim_proposals_proposals.rb +13 -0
- data/db/migrate/20240110203503_remove_state_from_decidim_proposals_proposals.rb +11 -0
- data/db/migrate/20240110203504_create_default_proposal_states.rb +31 -0
- data/db/migrate/20240209092404_change_color_fields_on_proposals_states.rb +48 -0
- data/decidim-proposals.gemspec +2 -2
- data/lib/decidim/api/proposal_type.rb +4 -0
- data/lib/decidim/proposals/admin_engine.rb +8 -0
- data/lib/decidim/proposals/admin_filter.rb +37 -0
- data/lib/decidim/proposals/component.rb +8 -5
- data/lib/decidim/proposals/engine.rb +1 -15
- data/lib/decidim/proposals/import/proposal_answer_creator.rb +6 -6
- data/lib/decidim/proposals/import/proposal_creator.rb +1 -1
- data/lib/decidim/proposals/markdown_to_proposals.rb +2 -8
- data/lib/decidim/proposals/proposal_serializer.rb +5 -3
- data/lib/decidim/proposals/seeds.rb +60 -51
- data/lib/decidim/proposals/test/factories.rb +64 -8
- data/lib/decidim/proposals/version.rb +1 -1
- data/lib/decidim/proposals.rb +84 -12
- data/lib/tasks/proposals/upgrade/decidim_proposals_upgrade_tasks.rake +32 -0
- metadata +49 -35
- data/app/events/decidim/proposals/accepted_proposal_event.rb +0 -17
- data/app/events/decidim/proposals/evaluating_proposal_event.rb +0 -11
- data/app/events/decidim/proposals/rejected_proposal_event.rb +0 -17
- data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +0 -44
- data/app/queries/decidim/proposals/similar_proposals.rb +0 -67
- data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +0 -0
- data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +0 -3
- data/app/views/decidim/proposals/proposals/compare.html.erb +0 -24
- data/app/views/decidim/proposals/proposals/complete.html.erb +0 -31
- data/lib/tasks/proposals/upgrade/decdim_proposal_upgrade_tasks.rake +0 -34
@@ -0,0 +1,48 @@
|
|
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
|
+
# rubocop:disable Rails/SkipsModelValidations
|
33
|
+
ProposalState.where(token: :accepted).update_all(
|
34
|
+
bg_color: colors[:green][:background], text_color: colors[:green][:foreground]
|
35
|
+
)
|
36
|
+
ProposalState.where(token: :evaluating).update_all(
|
37
|
+
bg_color: colors[:orange][:background], text_color: colors[:orange][:foreground]
|
38
|
+
)
|
39
|
+
ProposalState.where(token: :rejected).update_all(
|
40
|
+
bg_color: colors[:red][:background], text_color: colors[:red][:foreground]
|
41
|
+
)
|
42
|
+
# rubocop:enable Rails/SkipsModelValidations
|
43
|
+
end
|
44
|
+
|
45
|
+
def down
|
46
|
+
raise ActiveRecord::IrreversibleMigration
|
47
|
+
end
|
48
|
+
end
|
data/decidim-proposals.gemspec
CHANGED
@@ -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.
|
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.
|
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
|
@@ -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 :
|
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.
|
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 :
|
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
|
-
|
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.
|
59
|
+
@initial_state = proposal.proposal_state
|
60
|
+
|
61
|
+
proposal_state = Decidim::Proposals::ProposalState.where(component:, token: state).first
|
62
62
|
|
63
|
-
if
|
64
|
-
proposal.
|
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
|
-
#
|
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
|
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
|
60
|
+
# Creates Participatory Text Proposals at Article level.
|
67
61
|
def paragraph(text)
|
68
62
|
return if text.blank?
|
69
63
|
|
@@ -44,13 +44,13 @@ module Decidim
|
|
44
44
|
reference: proposal.reference,
|
45
45
|
answer: ensure_translatable(proposal.answer),
|
46
46
|
answered_at: proposal.answered_at,
|
47
|
-
|
47
|
+
votes: proposal.proposal_votes_count,
|
48
48
|
endorsements: {
|
49
49
|
total_count: proposal.endorsements.size,
|
50
50
|
user_endorsements:
|
51
51
|
},
|
52
52
|
comments: proposal.comments_count,
|
53
|
-
attachments: proposal.attachments.
|
53
|
+
attachments: proposal.attachments.size,
|
54
54
|
followers: proposal.follows.size,
|
55
55
|
published_at: proposal.published_at,
|
56
56
|
url:,
|
@@ -60,7 +60,9 @@ module Decidim
|
|
60
60
|
original_proposal: {
|
61
61
|
title: proposal&.amendable&.title,
|
62
62
|
url: original_proposal_url
|
63
|
-
}
|
63
|
+
},
|
64
|
+
withdrawn: proposal.withdrawn?,
|
65
|
+
withdrawn_at: proposal.withdrawn_at
|
64
66
|
}
|
65
67
|
end
|
66
68
|
|
@@ -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
|
-
|
75
|
-
|
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
|
-
|
88
|
+
proposal_state:,
|
94
89
|
answer:,
|
95
|
-
answered_at:
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
@@ -5,6 +5,17 @@ require "decidim/core/test/factories"
|
|
5
5
|
require "decidim/participatory_processes/test/factories"
|
6
6
|
require "decidim/meetings/test/factories"
|
7
7
|
|
8
|
+
def generate_state_title(token, skip_injection: false)
|
9
|
+
value = I18n.t(token, scope: "decidim.proposals.answers")
|
10
|
+
Decidim::Faker::Localized.localized do
|
11
|
+
if skip_injection
|
12
|
+
value
|
13
|
+
else
|
14
|
+
"<script>alert(\"proposal_state_title\");</script> #{value}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
8
19
|
FactoryBot.define do
|
9
20
|
factory :proposal_component, parent: :component do
|
10
21
|
transient do
|
@@ -14,6 +25,10 @@ FactoryBot.define do
|
|
14
25
|
manifest_name { :proposals }
|
15
26
|
participatory_space { create(:participatory_process, :with_steps, organization:, skip_injection:) }
|
16
27
|
|
28
|
+
after :create do |proposal_component|
|
29
|
+
Decidim::Proposals.create_default_states!(proposal_component, nil, with_traceability: false)
|
30
|
+
end
|
31
|
+
|
17
32
|
trait :with_endorsements_enabled do
|
18
33
|
step_settings do
|
19
34
|
{
|
@@ -148,10 +163,10 @@ FactoryBot.define do
|
|
148
163
|
end
|
149
164
|
end
|
150
165
|
|
151
|
-
trait :
|
166
|
+
trait :with_can_accumulate_votes_beyond_threshold do
|
152
167
|
settings do
|
153
168
|
{
|
154
|
-
|
169
|
+
can_accumulate_votes_beyond_threshold: true
|
155
170
|
}
|
156
171
|
end
|
157
172
|
end
|
@@ -246,12 +261,45 @@ FactoryBot.define do
|
|
246
261
|
end
|
247
262
|
end
|
248
263
|
|
264
|
+
factory :proposal_state, class: "Decidim::Proposals::ProposalState" do
|
265
|
+
transient do
|
266
|
+
skip_injection { false }
|
267
|
+
end
|
268
|
+
token { :not_answered }
|
269
|
+
title { generate_state_title(:not_answered, skip_injection:) }
|
270
|
+
announcement_title { generate_localized_title(:announcement_title, skip_injection:) }
|
271
|
+
component { build(:proposal_component) }
|
272
|
+
bg_color { Faker::Color.hex_color(:light) }
|
273
|
+
text_color { Faker::Color.hex_color(:dark) }
|
274
|
+
|
275
|
+
trait :evaluating do
|
276
|
+
title { generate_state_title(:evaluating, skip_injection:) }
|
277
|
+
token { :evaluating }
|
278
|
+
end
|
279
|
+
|
280
|
+
trait :accepted do
|
281
|
+
title { generate_state_title(:accepted, skip_injection:) }
|
282
|
+
token { :accepted }
|
283
|
+
end
|
284
|
+
|
285
|
+
trait :rejected do
|
286
|
+
title { generate_state_title(:rejected, skip_injection:) }
|
287
|
+
token { :rejected }
|
288
|
+
end
|
289
|
+
|
290
|
+
trait :withdrawn do
|
291
|
+
title { generate_state_title(:withdrawn, skip_injection:) }
|
292
|
+
token { :withdrawn }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
249
296
|
factory :proposal, class: "Decidim::Proposals::Proposal" do
|
250
297
|
transient do
|
251
298
|
users { nil }
|
252
299
|
# user_groups correspondence to users is by sorting order
|
253
300
|
user_groups { [] }
|
254
301
|
skip_injection { false }
|
302
|
+
state { :not_answered }
|
255
303
|
end
|
256
304
|
|
257
305
|
title { generate_localized_title(:proposal_title, skip_injection:) }
|
@@ -266,6 +314,14 @@ FactoryBot.define do
|
|
266
314
|
execution_period { generate_localized_title(:proposal_execution_period, skip_injection:) }
|
267
315
|
|
268
316
|
after(:build) do |proposal, evaluator|
|
317
|
+
if proposal.component
|
318
|
+
existing_states = Decidim::Proposals::ProposalState.where(component: proposal.component)
|
319
|
+
|
320
|
+
Decidim::Proposals.create_default_states!(proposal.component, nil, with_traceability: false) unless existing_states.any?
|
321
|
+
end
|
322
|
+
|
323
|
+
proposal.assign_state(evaluator.state)
|
324
|
+
|
269
325
|
proposal.title = if evaluator.title.is_a?(String)
|
270
326
|
{ proposal.try(:organization).try(:default_locale) || "en" => evaluator.title }
|
271
327
|
else
|
@@ -330,36 +386,36 @@ FactoryBot.define do
|
|
330
386
|
end
|
331
387
|
|
332
388
|
trait :evaluating do
|
333
|
-
state {
|
389
|
+
state { :evaluating }
|
334
390
|
answered_at { Time.current }
|
335
391
|
state_published_at { Time.current }
|
336
392
|
end
|
337
393
|
|
338
394
|
trait :accepted do
|
339
|
-
state {
|
395
|
+
state { :accepted }
|
340
396
|
answered_at { Time.current }
|
341
397
|
state_published_at { Time.current }
|
342
398
|
end
|
343
399
|
|
344
400
|
trait :rejected do
|
345
|
-
state {
|
401
|
+
state { :rejected }
|
346
402
|
answered_at { Time.current }
|
347
403
|
state_published_at { Time.current }
|
348
404
|
end
|
349
405
|
|
350
406
|
trait :withdrawn do
|
351
|
-
|
407
|
+
withdrawn_at { Time.current }
|
352
408
|
end
|
353
409
|
|
354
410
|
trait :accepted_not_published do
|
355
|
-
state {
|
411
|
+
state { :accepted }
|
356
412
|
answered_at { Time.current }
|
357
413
|
state_published_at { nil }
|
358
414
|
answer { generate_localized_title }
|
359
415
|
end
|
360
416
|
|
361
417
|
trait :with_answer do
|
362
|
-
state {
|
418
|
+
state { :accepted }
|
363
419
|
answer { generate_localized_title }
|
364
420
|
answered_at { Time.current }
|
365
421
|
state_published_at { Time.current }
|