decidim-proposals 0.28.2 → 0.29.0.rc1
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 +14 -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 +54 -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
@@ -8,6 +8,16 @@ module Decidim
|
|
8
8
|
#
|
9
9
|
module ProposalsHelper
|
10
10
|
include Decidim::Admin::ResourceScopeHelper
|
11
|
+
include Decidim::TranslatableAttributes
|
12
|
+
|
13
|
+
def available_states
|
14
|
+
[
|
15
|
+
Decidim::Proposals::ProposalState.where(component: current_component).new(
|
16
|
+
token: "not_answered",
|
17
|
+
title: t("decidim.proposals.answers.not_answered")
|
18
|
+
)
|
19
|
+
] + Decidim::Proposals::ProposalState.where(component: current_component).all
|
20
|
+
end
|
11
21
|
|
12
22
|
# Public: A formatted collection of Meetings to be used
|
13
23
|
# in forms.
|
@@ -32,96 +42,10 @@ module Decidim
|
|
32
42
|
end
|
33
43
|
|
34
44
|
def proposal_complete_state(proposal)
|
35
|
-
return humanize_proposal_state(
|
36
|
-
|
37
|
-
humanize_proposal_state(proposal.state).html_safe
|
38
|
-
end
|
39
|
-
|
40
|
-
def proposals_admin_filter_tree
|
41
|
-
{
|
42
|
-
t("proposals.filters.type", scope: "decidim.proposals") => {
|
43
|
-
link_to(t("proposals", scope: "decidim.proposals.application_helper.filter_type_values"), q: ransak_params_for_query(is_emendation_true: "0"),
|
44
|
-
per_page:) => nil,
|
45
|
-
link_to(t("amendments", scope: "decidim.proposals.application_helper.filter_type_values"), q: ransak_params_for_query(is_emendation_true: "1"),
|
46
|
-
per_page:) => nil
|
47
|
-
},
|
48
|
-
t("models.proposal.fields.state", scope: "decidim.proposals") =>
|
49
|
-
Decidim::Proposals::Proposal::STATES.each_pair do |state, hash|
|
50
|
-
if state == "not_answered"
|
51
|
-
hash[link_to((humanize_proposal_state state), q: ransak_params_for_query(state_null: 1), per_page:)] = nil
|
52
|
-
else
|
53
|
-
hash[link_to((humanize_proposal_state state), q: ransak_params_for_query(state_eq: state), per_page:)] = nil
|
54
|
-
end
|
55
|
-
end,
|
56
|
-
t("models.proposal.fields.category", scope: "decidim.proposals") => admin_filter_categories_tree(categories.first_class),
|
57
|
-
t("proposals.filters.scope", scope: "decidim.proposals") => admin_filter_scopes_tree(current_component.organization.id)
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
def proposals_admin_search_hidden_params
|
62
|
-
return unless params[:q]
|
45
|
+
return humanize_proposal_state(:withdrawn).html_safe if proposal.withdrawn?
|
46
|
+
return humanize_proposal_state("not_answered").html_safe if proposal.proposal_state.nil?
|
63
47
|
|
64
|
-
|
65
|
-
tags += hidden_field_tag "per_page", params[:per_page] if params[:per_page]
|
66
|
-
tags += hidden_field_tag "q[is_emendation_true]", params[:q][:is_emendation_true] if params[:q][:is_emendation_true]
|
67
|
-
tags += hidden_field_tag "q[state_eq]", params[:q][:state_eq] if params[:q][:state_eq]
|
68
|
-
tags += hidden_field_tag "q[category_id_eq]", params[:q][:category_id_eq] if params[:q][:category_id_eq]
|
69
|
-
tags += hidden_field_tag "q[scope_id_eq]", params[:q][:scope_id_eq] if params[:q][:scope_id_eq]
|
70
|
-
tags.html_safe
|
71
|
-
end
|
72
|
-
|
73
|
-
def proposals_admin_filter_applied_filters
|
74
|
-
html = []
|
75
|
-
if params[:q][:is_emendation_true].present?
|
76
|
-
html << tag.span(class: "label secondary") do
|
77
|
-
tag = "#{t("filters.type", scope: "decidim.proposals.proposals")}: "
|
78
|
-
tag += if params[:q][:is_emendation_true].to_s == "1"
|
79
|
-
t("amendments", scope: "decidim.proposals.application_helper.filter_type_values")
|
80
|
-
else
|
81
|
-
t("proposals", scope: "decidim.proposals.application_helper.filter_type_values")
|
82
|
-
end
|
83
|
-
tag += icon_link_to("delete-bin-line", url_for(q: ransak_params_for_query_without(:is_emendation_true), per_page:), t("decidim.admin.actions.cancel"),
|
84
|
-
class: "action-icon--remove")
|
85
|
-
tag.html_safe
|
86
|
-
end
|
87
|
-
end
|
88
|
-
if params[:q][:state_null]
|
89
|
-
html << tag.span(class: "label secondary") do
|
90
|
-
tag = "#{t("models.proposal.fields.state", scope: "decidim.proposals")}: "
|
91
|
-
tag += humanize_proposal_state "not_answered"
|
92
|
-
tag += icon_link_to("delete-bin-line", url_for(q: ransak_params_for_query_without(:state_null), per_page:), t("decidim.admin.actions.cancel"),
|
93
|
-
class: "action-icon--remove")
|
94
|
-
tag.html_safe
|
95
|
-
end
|
96
|
-
end
|
97
|
-
if params[:q][:state_eq]
|
98
|
-
html << tag.span(class: "label secondary") do
|
99
|
-
tag = "#{t("models.proposal.fields.state", scope: "decidim.proposals")}: "
|
100
|
-
tag += humanize_proposal_state params[:q][:state_eq]
|
101
|
-
tag += icon_link_to("delete-bin-line", url_for(q: ransak_params_for_query_without(:state_eq), per_page:), t("decidim.admin.actions.cancel"),
|
102
|
-
class: "action-icon--remove")
|
103
|
-
tag.html_safe
|
104
|
-
end
|
105
|
-
end
|
106
|
-
if params[:q][:category_id_eq]
|
107
|
-
html << tag.span(class: "label secondary") do
|
108
|
-
tag = "#{t("models.proposal.fields.category", scope: "decidim.proposals")}: "
|
109
|
-
tag += translated_attribute categories.find(params[:q][:category_id_eq]).name
|
110
|
-
tag += icon_link_to("delete-bin-line", url_for(q: ransak_params_for_query_without(:category_id_eq), per_page:), t("decidim.admin.actions.cancel"),
|
111
|
-
class: "action-icon--remove")
|
112
|
-
tag.html_safe
|
113
|
-
end
|
114
|
-
end
|
115
|
-
if params[:q][:scope_id_eq]
|
116
|
-
html << tag.span(class: "label secondary") do
|
117
|
-
tag = "#{t("models.proposal.fields.scope", scope: "decidim.proposals")}: "
|
118
|
-
tag += translated_attribute Decidim::Scope.where(decidim_organization_id: current_component.organization.id).find(params[:q][:scope_id_eq]).name
|
119
|
-
tag += icon_link_to("delete-bin-line", url_for(q: ransak_params_for_query_without(:scope_id_eq), per_page:), t("decidim.admin.actions.cancel"),
|
120
|
-
class: "action-icon--remove")
|
121
|
-
tag.html_safe
|
122
|
-
end
|
123
|
-
end
|
124
|
-
html.join(" ").html_safe
|
48
|
+
translated_attribute(proposal&.proposal_state&.title)
|
125
49
|
end
|
126
50
|
|
127
51
|
def icon_with_link_to_proposal(proposal)
|
@@ -28,16 +28,22 @@ module Decidim
|
|
28
28
|
I18n.t(state, scope: "decidim.proposals.answers", default: :not_answered)
|
29
29
|
end
|
30
30
|
|
31
|
+
def proposal_state_css_style(proposal)
|
32
|
+
return "" if proposal.emendation?
|
33
|
+
|
34
|
+
proposal.proposal_state&.css_style
|
35
|
+
end
|
36
|
+
|
31
37
|
# Public: The css class applied based on the proposal state.
|
32
38
|
#
|
33
39
|
# proposal - The proposal to evaluate.
|
34
40
|
#
|
35
41
|
# Returns a String.
|
36
42
|
def proposal_state_css_class(proposal)
|
37
|
-
|
38
|
-
|
43
|
+
return "alert" if proposal.withdrawn?
|
44
|
+
return if proposal.state.blank?
|
39
45
|
|
40
|
-
case state
|
46
|
+
case proposal.state
|
41
47
|
when "accepted"
|
42
48
|
"success"
|
43
49
|
when "rejected", "withdrawn"
|
@@ -144,7 +150,7 @@ module Decidim
|
|
144
150
|
return true if vote_limit_enabled?
|
145
151
|
return true if threshold_per_proposal_enabled?
|
146
152
|
return true if proposal_limit_enabled?
|
147
|
-
return true if
|
153
|
+
return true if can_accumulate_votes_beyond_threshold?
|
148
154
|
return true if minimum_votes_per_user_enabled?
|
149
155
|
end
|
150
156
|
|
@@ -166,7 +172,7 @@ module Decidim
|
|
166
172
|
base
|
167
173
|
end
|
168
174
|
|
169
|
-
#
|
175
|
+
# Explicitly commenting the used I18n keys so their are not flagged as unused
|
170
176
|
# i18n-tasks-use t('decidim.proposals.application_helper.filter_origin_values.official')
|
171
177
|
# i18n-tasks-use t('decidim.proposals.application_helper.filter_origin_values.participants')
|
172
178
|
# i18n-tasks-use t('decidim.proposals.application_helper.filter_origin_values.user_groups')
|
@@ -191,11 +197,11 @@ module Decidim
|
|
191
197
|
Decidim::CheckBoxesTreeHelper::TreeNode.new(
|
192
198
|
Decidim::CheckBoxesTreeHelper::TreePoint.new("", t("decidim.proposals.application_helper.filter_state_values.all")),
|
193
199
|
[
|
194
|
-
Decidim::CheckBoxesTreeHelper::TreePoint.new("
|
195
|
-
|
196
|
-
Decidim::
|
197
|
-
|
198
|
-
|
200
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("state_not_published", t("decidim.proposals.application_helper.filter_state_values.not_answered"))
|
201
|
+
] +
|
202
|
+
Decidim::Proposals::ProposalState.where(component: current_component).where.not(token: "not_answered").map do |state|
|
203
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new(state.token, translated_attribute(state.title))
|
204
|
+
end
|
199
205
|
)
|
200
206
|
end
|
201
207
|
|
@@ -14,7 +14,7 @@ module Decidim
|
|
14
14
|
include Decidim::TranslatableAttributes
|
15
15
|
include Decidim::CardHelper
|
16
16
|
|
17
|
-
delegate :title, :state, :published_state?, :withdrawn?, :amendable?, :emendation?, to: :model
|
17
|
+
delegate :title, :proposal_state, :state, :published_state?, :withdrawn?, :amendable?, :emendation?, to: :model
|
18
18
|
|
19
19
|
def has_actions?
|
20
20
|
return context[:has_actions] if context[:has_actions].present?
|
@@ -53,14 +53,18 @@ module Decidim
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def badge_name
|
56
|
+
return humanize_proposal_state(:withdrawn) if withdrawn?
|
57
|
+
|
56
58
|
humanize_proposal_state state
|
57
59
|
end
|
58
60
|
|
59
61
|
def state_classes
|
62
|
+
return ["alert"] if withdrawn?
|
63
|
+
|
60
64
|
case state
|
61
65
|
when "accepted"
|
62
66
|
["success"]
|
63
|
-
when "rejected"
|
67
|
+
when "rejected"
|
64
68
|
["alert"]
|
65
69
|
when "evaluating"
|
66
70
|
["warning"]
|
@@ -36,11 +36,11 @@ module Decidim
|
|
36
36
|
component_settings.threshold_per_proposal
|
37
37
|
end
|
38
38
|
|
39
|
-
# Public: Checks if can accumulate more than
|
39
|
+
# Public: Checks if can accumulate more than maximum is enabled
|
40
40
|
#
|
41
41
|
# Returns true if enabled, false otherwise.
|
42
|
-
def
|
43
|
-
component_settings.
|
42
|
+
def can_accumulate_votes_beyond_threshold?
|
43
|
+
component_settings.can_accumulate_votes_beyond_threshold
|
44
44
|
end
|
45
45
|
|
46
46
|
# Public: Checks if voting is enabled in this step.
|
@@ -12,7 +12,7 @@ module Decidim
|
|
12
12
|
active = current_step == wizard_step
|
13
13
|
inactive_data = {} if active
|
14
14
|
attributes = active ? { "aria-current": "step", "aria-label": "#{t("current_step", scope:)}: #{t(wizard_step, scope:)}", data: { active: "" } } : inactive_data
|
15
|
-
content_tag(:li, t(wizard_step, scope:), attributes)
|
15
|
+
content_tag(:li, t(wizard_step, scope:), attributes.merge(class: "w-1/2"))
|
16
16
|
end.join.html_safe
|
17
17
|
end
|
18
18
|
end
|
@@ -41,24 +41,21 @@ module Decidim
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def proposal_wizard_steps
|
44
|
-
[ProposalsController::STEP1, ProposalsController::STEP2
|
44
|
+
[ProposalsController::STEP1, ProposalsController::STEP2]
|
45
45
|
end
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
49
|
def total_steps
|
50
|
-
|
50
|
+
2
|
51
51
|
end
|
52
52
|
|
53
|
-
# Renders the back link except for step_2: compare
|
54
53
|
def proposal_wizard_aside_link_to_back(step)
|
55
54
|
case step
|
56
55
|
when ProposalsController::STEP1
|
57
56
|
proposals_path
|
58
|
-
when ProposalsController::
|
59
|
-
|
60
|
-
when ProposalsController::STEP4
|
61
|
-
edit_draft_proposal_path
|
57
|
+
when ProposalsController::STEP2
|
58
|
+
complete_proposal_path
|
62
59
|
end
|
63
60
|
end
|
64
61
|
|
@@ -6,39 +6,33 @@ module Decidim
|
|
6
6
|
module ProposalsHelper
|
7
7
|
def proposal_reason_callout_announcement
|
8
8
|
{
|
9
|
-
title:
|
9
|
+
title: translated_attribute(@proposal.proposal_state&.announcement_title),
|
10
10
|
body: decidim_sanitize_editor_admin(translated_attribute(@proposal.answer))
|
11
11
|
}
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
when "evaluating"
|
19
|
-
"warning"
|
20
|
-
when "rejected"
|
21
|
-
"alert"
|
22
|
-
else
|
23
|
-
""
|
24
|
-
end
|
14
|
+
def proposal_has_costs?
|
15
|
+
@proposal.cost.present? &&
|
16
|
+
translated_attribute(@proposal.cost_report).present? &&
|
17
|
+
translated_attribute(@proposal.execution_period).present?
|
25
18
|
end
|
26
19
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
20
|
+
def toggle_view_mode_link(current_mode, target_mode, title)
|
21
|
+
path = proposals_path(view_mode: target_mode)
|
22
|
+
icon_name = target_mode == "grid" ? "layout-grid-fill" : "list-check"
|
23
|
+
icon_class = "view-icon--disabled" unless current_mode == target_mode
|
24
|
+
|
25
|
+
link_to path, remote: true, title: do
|
26
|
+
icon(icon_name, class: icon_class, role: "img", "aria-hidden": true)
|
27
|
+
end
|
28
|
+
end
|
34
29
|
|
35
|
-
|
30
|
+
def proposals_container_class(view_mode)
|
31
|
+
view_mode == "grid" ? "card__grid-grid" : "card__list-list"
|
36
32
|
end
|
37
33
|
|
38
|
-
def
|
39
|
-
|
40
|
-
translated_attribute(@proposal.cost_report).present? &&
|
41
|
-
translated_attribute(@proposal.execution_period).present?
|
34
|
+
def card_size_for_view_mode(view_mode)
|
35
|
+
view_mode == "grid" ? :g : nil
|
42
36
|
end
|
43
37
|
|
44
38
|
def resource_version(resource, options = {})
|
@@ -29,9 +29,13 @@ module Decidim
|
|
29
29
|
include Decidim::TranslatableAttributes
|
30
30
|
include Decidim::FilterableResource
|
31
31
|
|
32
|
-
|
32
|
+
def assign_state(token)
|
33
|
+
proposal_state = Decidim::Proposals::ProposalState.where(component:, token:).first
|
34
|
+
|
35
|
+
self.proposal_state = proposal_state
|
36
|
+
end
|
33
37
|
|
34
|
-
|
38
|
+
translatable_fields :title, :body
|
35
39
|
|
36
40
|
fingerprint fields: [:title, :body]
|
37
41
|
|
@@ -42,6 +46,13 @@ module Decidim
|
|
42
46
|
|
43
47
|
component_manifest_name "proposals"
|
44
48
|
|
49
|
+
belongs_to :proposal_state,
|
50
|
+
class_name: "Decidim::Proposals::ProposalState",
|
51
|
+
foreign_key: "decidim_proposals_proposal_state_id",
|
52
|
+
inverse_of: :proposals,
|
53
|
+
optional: true,
|
54
|
+
counter_cache: true
|
55
|
+
|
45
56
|
has_many :votes,
|
46
57
|
-> { final },
|
47
58
|
foreign_key: "decidim_proposal_id",
|
@@ -55,19 +66,31 @@ module Decidim
|
|
55
66
|
|
56
67
|
geocoded_by :address
|
57
68
|
|
58
|
-
|
69
|
+
scope :not_status, lambda { |status|
|
70
|
+
joins(:proposal_state).where.not(decidim_proposals_proposal_states: { token: status })
|
71
|
+
}
|
72
|
+
|
73
|
+
scope :only_status, lambda { |status|
|
74
|
+
joins(:proposal_state).where(decidim_proposals_proposal_states: { token: status })
|
75
|
+
}
|
76
|
+
|
77
|
+
scope :accepted, -> { state_published.only_status(:accepted) }
|
78
|
+
scope :rejected, -> { state_published.only_status(:rejected) }
|
79
|
+
scope :evaluating, -> { state_published.only_status(:evaluating) }
|
59
80
|
|
60
|
-
scope :
|
61
|
-
scope :rejected, -> { state_published.where(state: "rejected") }
|
62
|
-
scope :evaluating, -> { state_published.where(state: "evaluating") }
|
81
|
+
scope :gamified, -> { only_status(:accepted).where(decidim_proposals_proposal_states: { gamified: true }) }
|
63
82
|
|
64
83
|
scope :answered, -> { where.not(answered_at: nil) }
|
65
84
|
scope :not_answered, -> { where(answered_at: nil) }
|
66
85
|
|
67
86
|
scope :state_not_published, -> { where(state_published_at: nil) }
|
68
87
|
scope :state_published, -> { where.not(state_published_at: nil) }
|
69
|
-
|
70
|
-
scope :
|
88
|
+
|
89
|
+
scope :except_rejected, -> { state_published.not_status(:rejected).or(state_not_published) }
|
90
|
+
|
91
|
+
scope :withdrawn, -> { where.not(withdrawn_at: nil) }
|
92
|
+
scope :not_withdrawn, -> { where(withdrawn_at: nil) }
|
93
|
+
|
71
94
|
scope :drafts, -> { where(published_at: nil) }
|
72
95
|
scope :published, -> { where.not(published_at: nil) }
|
73
96
|
scope :order_by_most_recent, -> { order(created_at: :desc) }
|
@@ -77,7 +100,7 @@ module Decidim
|
|
77
100
|
when "withdrawn"
|
78
101
|
withdrawn
|
79
102
|
else
|
80
|
-
|
103
|
+
not_withdrawn
|
81
104
|
end
|
82
105
|
}
|
83
106
|
|
@@ -104,7 +127,34 @@ module Decidim
|
|
104
127
|
order(valuation_assignments_count: :desc)
|
105
128
|
}
|
106
129
|
|
107
|
-
|
130
|
+
scope :state_eq, lambda { |state|
|
131
|
+
return withdrawn if state == "withdrawn"
|
132
|
+
|
133
|
+
only_status(state)
|
134
|
+
}
|
135
|
+
|
136
|
+
scope :with_any_state, lambda { |*value_keys|
|
137
|
+
possible_scopes = [:state_not_published, :state_published]
|
138
|
+
custom_states = Decidim::Proposals::ProposalState.distinct.pluck(:token)
|
139
|
+
|
140
|
+
search_values = value_keys.compact.compact_blank
|
141
|
+
|
142
|
+
conditions = possible_scopes.map do |scope|
|
143
|
+
search_values.member?(scope.to_s) ? try(scope) : nil
|
144
|
+
end.compact
|
145
|
+
|
146
|
+
additional_conditions = search_values & custom_states
|
147
|
+
conditions.push(state_published.only_status(additional_conditions)) if additional_conditions.any?
|
148
|
+
|
149
|
+
return self unless conditions.any?
|
150
|
+
|
151
|
+
scoped_query = where(id: conditions.shift)
|
152
|
+
conditions.each do |condition|
|
153
|
+
scoped_query = scoped_query.or(where(id: condition))
|
154
|
+
end
|
155
|
+
|
156
|
+
scoped_query
|
157
|
+
}
|
108
158
|
|
109
159
|
def self.with_valuation_assigned_to(user, space)
|
110
160
|
valuator_roles = space.user_roles(:valuator).where(user:)
|
@@ -145,7 +195,7 @@ module Decidim
|
|
145
195
|
.where(decidim_coauthorships: { decidim_author_type: "Decidim::UserBaseEntity" })
|
146
196
|
.not_hidden
|
147
197
|
.published
|
148
|
-
.
|
198
|
+
.not_withdrawn
|
149
199
|
end
|
150
200
|
|
151
201
|
def self.newsletter_participant_ids(component)
|
@@ -194,10 +244,10 @@ module Decidim
|
|
194
244
|
return amendment.state if emendation?
|
195
245
|
return nil unless published_state? || withdrawn?
|
196
246
|
|
197
|
-
|
247
|
+
proposal_state&.token || "not_answered"
|
198
248
|
end
|
199
249
|
|
200
|
-
# This is only used to define the setter, as the getter will be
|
250
|
+
# This is only used to define the setter, as the getter will be overridden below.
|
201
251
|
alias_attribute :internal_state, :state
|
202
252
|
|
203
253
|
# Public: Returns the internal state of the proposal.
|
@@ -206,7 +256,7 @@ module Decidim
|
|
206
256
|
def internal_state
|
207
257
|
return amendment.state if emendation?
|
208
258
|
|
209
|
-
|
259
|
+
proposal_state&.token || "not_answered"
|
210
260
|
end
|
211
261
|
|
212
262
|
# Public: Checks if the organization has published the state for the proposal.
|
@@ -227,7 +277,7 @@ module Decidim
|
|
227
277
|
#
|
228
278
|
# Returns Boolean.
|
229
279
|
def withdrawn?
|
230
|
-
|
280
|
+
withdrawn_at.present?
|
231
281
|
end
|
232
282
|
|
233
283
|
# Public: Checks if the organization has accepted a proposal.
|
@@ -302,11 +352,11 @@ module Decidim
|
|
302
352
|
votes.count >= maximum_votes
|
303
353
|
end
|
304
354
|
|
305
|
-
# Public: Can accumulate more
|
355
|
+
# Public: Can accumulate more votes than maximum for this proposal.
|
306
356
|
#
|
307
357
|
# Returns true if can accumulate, false otherwise
|
308
|
-
def
|
309
|
-
component.settings.
|
358
|
+
def can_accumulate_votes_beyond_threshold
|
359
|
+
component.settings.can_accumulate_votes_beyond_threshold
|
310
360
|
end
|
311
361
|
|
312
362
|
# Checks whether the user can edit the given proposal.
|
@@ -325,6 +375,11 @@ module Decidim
|
|
325
375
|
user && !withdrawn? && authored_by?(user) && !copied_from_other_component?
|
326
376
|
end
|
327
377
|
|
378
|
+
def withdraw!
|
379
|
+
self.withdrawn_at = Time.zone.now
|
380
|
+
save
|
381
|
+
end
|
382
|
+
|
328
383
|
# Public: Whether the proposal is a draft or not.
|
329
384
|
def draft?
|
330
385
|
published_at.nil?
|
@@ -348,7 +403,7 @@ module Decidim
|
|
348
403
|
end
|
349
404
|
|
350
405
|
def self.ransackable_scopes(auth_object = nil)
|
351
|
-
base = [:with_any_origin, :with_any_state, :voted_by, :coauthored_by, :related_to, :with_any_scope, :with_any_category]
|
406
|
+
base = [:with_any_origin, :with_any_state, :state_eq, :voted_by, :coauthored_by, :related_to, :with_any_scope, :with_any_category]
|
352
407
|
return base unless auth_object&.admin?
|
353
408
|
|
354
409
|
# Add extra scopes for admins for the admin panel searches
|
@@ -390,10 +445,6 @@ module Decidim
|
|
390
445
|
Arel.sql(%{cast("decidim_proposals_proposals"."id" as text)})
|
391
446
|
end
|
392
447
|
|
393
|
-
ransacker :state, formatter: proc { |v| STATES[v.to_sym] } do |parent|
|
394
|
-
parent.table[:state]
|
395
|
-
end
|
396
|
-
|
397
448
|
ransacker :is_emendation do |_parent|
|
398
449
|
query = <<-SQL.squish
|
399
450
|
(
|
@@ -430,13 +481,12 @@ module Decidim
|
|
430
481
|
end
|
431
482
|
|
432
483
|
def process_amendment_state_change!
|
433
|
-
return
|
484
|
+
return withdraw! if amendment.withdrawn?
|
485
|
+
return unless %w(accepted rejected evaluating).member?(amendment.state)
|
434
486
|
|
435
487
|
PaperTrail.request(enabled: false) do
|
436
|
-
|
437
|
-
|
438
|
-
state_published_at: Time.current
|
439
|
-
)
|
488
|
+
assign_state(amendment.state)
|
489
|
+
update!(state_published_at: Time.current)
|
440
490
|
end
|
441
491
|
end
|
442
492
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
class ProposalState < Proposals::ApplicationRecord
|
6
|
+
include Decidim::HasComponent
|
7
|
+
include Decidim::Traceable
|
8
|
+
include Decidim::Loggable
|
9
|
+
|
10
|
+
include Decidim::TranslatableResource
|
11
|
+
include Decidim::TranslatableAttributes
|
12
|
+
|
13
|
+
before_validation :generate_token, on: :create
|
14
|
+
|
15
|
+
translatable_fields :title
|
16
|
+
|
17
|
+
validates :token, presence: true, uniqueness: { scope: :component }
|
18
|
+
|
19
|
+
has_many :proposals,
|
20
|
+
class_name: "Decidim::Proposals::Proposal",
|
21
|
+
foreign_key: "decidim_proposals_proposal_state_id",
|
22
|
+
inverse_of: :proposal_state,
|
23
|
+
dependent: :restrict_with_error,
|
24
|
+
counter_cache: :proposals_count
|
25
|
+
|
26
|
+
def self.log_presenter_class_for(_log)
|
27
|
+
Decidim::Proposals::AdminLog::ProposalStatePresenter
|
28
|
+
end
|
29
|
+
|
30
|
+
def css_style
|
31
|
+
"background-color: #{bg_color}; color: #{text_color}; border-color: #{text_color};"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.colors
|
35
|
+
Decidim::Proposals.proposal_states_colors
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def generate_token
|
41
|
+
self.token = ensure_unique_token(translated_attribute(title).parameterize(separator: "_"))
|
42
|
+
end
|
43
|
+
|
44
|
+
def ensure_unique_token(token)
|
45
|
+
step = 0
|
46
|
+
code = token
|
47
|
+
loop do
|
48
|
+
break if Decidim::Proposals::ProposalState.where(component:, token: code).empty?
|
49
|
+
|
50
|
+
code = "#{token}_#{step}"
|
51
|
+
step += 1
|
52
|
+
end
|
53
|
+
|
54
|
+
code
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
<!-- markdownlint-disable-file single-h1 strong-style emphasis-style -->
|
2
|
-
|
3
1
|
# Section title 1: grouping content
|
4
2
|
|
5
3
|
Participatory texts relay on the parsing of Markdown texts to produce a structured document.
|
@@ -54,4 +52,4 @@ Ordered lists will be parsed too:
|
|
54
52
|
|
55
53
|
A link to Decidim's web site uses [this format](https://decidim.org).
|
56
54
|
|
57
|
-
![Important image for Decidim](https://
|
55
|
+
![Important image for Decidim](https://raw.githubusercontent.com/decidim/decidim/develop/logo.svg)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<svg viewBox="0 0 434 160" preserveAspectRatio="xMidYMid slice"
|
2
|
+
id="ri-proposal-placeholder-card-g">
|
3
|
+
<rect opacity="0.106" fill="fill-current" width="434" height="160"></rect>
|
4
|
+
<g opacity="0.2" clip-path="url(#clip0_256_5589)">
|
5
|
+
<path
|
6
|
+
d="M331.544 115.752C331.543 114.67 331.753 113.598 332.163 112.597C332.574 111.596 333.175 110.687 333.935 109.921C334.694 109.155 335.596 108.547 336.588 108.132C337.581 107.718 338.645 107.504 339.719 107.504H346.262C346.696 107.504 347.111 107.678 347.417 107.987C347.724 108.295 347.896 108.714 347.897 109.15V142.096C347.897 142.312 347.855 142.526 347.773 142.726C347.69 142.926 347.57 143.107 347.418 143.26C347.266 143.413 347.086 143.535 346.888 143.617C346.69 143.7 346.477 143.743 346.262 143.743H339.719C337.552 143.742 335.473 142.874 333.94 141.331C332.407 139.787 331.545 137.693 331.544 135.509V115.752Z"
|
7
|
+
fill="fill-current"/>
|
8
|
+
<path
|
9
|
+
d="M365.884 96H352.801C351.898 96 351.166 96.7372 351.166 97.6466V142.096C351.166 143.005 351.898 143.743 352.801 143.743H365.884C366.787 143.743 367.519 143.005 367.519 142.096V97.6466C367.519 96.7372 366.787 96 365.884 96Z"
|
10
|
+
fill="fill-current"/>
|
11
|
+
<path
|
12
|
+
d="M388.776 135.509C388.775 137.138 388.295 138.73 387.397 140.084C386.498 141.438 385.221 142.493 383.727 143.116C382.233 143.739 380.59 143.902 379.004 143.584C377.418 143.266 375.962 142.481 374.819 141.329C373.676 140.178 372.897 138.71 372.582 137.113C372.267 135.515 372.429 133.859 373.048 132.354C373.667 130.85 374.715 129.564 376.059 128.659C377.403 127.754 378.984 127.271 380.601 127.271C382.769 127.272 384.848 128.14 386.381 129.685C387.915 131.23 388.776 133.325 388.776 135.509Z"
|
13
|
+
fill="fill-current"/>
|
14
|
+
</g>
|
15
|
+
</svg>
|
@@ -11,6 +11,7 @@ $(() => {
|
|
11
11
|
}
|
12
12
|
$addressInputField.on("geocoder-suggest-coordinates.decidim", () => $map.show());
|
13
13
|
|
14
|
+
const markerAddedAnnouncement = $addressInputField.data("screen-reader-announcement");
|
14
15
|
let latFieldName = "latitude";
|
15
16
|
let longFieldName = "longitude";
|
16
17
|
|
@@ -35,6 +36,7 @@ $(() => {
|
|
35
36
|
longitude: coordinates[1],
|
36
37
|
address: $addressInputField.val()
|
37
38
|
});
|
39
|
+
window.Decidim.announceForScreenReader(markerAddedAnnouncement);
|
38
40
|
});
|
39
41
|
});
|
40
42
|
}
|