decidim-proposals 0.20.1 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/decidim/proposals/admin/proposals.es6 +24 -11
- data/app/cells/decidim/proposals/cost_report/show.erb +35 -0
- data/app/cells/decidim/proposals/cost_report_cell.rb +42 -0
- data/app/cells/decidim/proposals/proposal_m_cell.rb +9 -1
- data/app/cells/decidim/proposals/proposal_tags/show.erb +12 -10
- data/app/cells/decidim/proposals/proposal_tags_cell.rb +5 -0
- data/app/commands/decidim/proposals/admin/answer_proposal.rb +24 -46
- data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +61 -0
- data/app/commands/decidim/proposals/admin/create_proposal.rb +5 -0
- data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +85 -0
- data/app/commands/decidim/proposals/admin/publish_answers.rb +67 -0
- data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +62 -0
- data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +75 -0
- data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +82 -0
- data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +16 -6
- data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +8 -9
- data/app/controllers/decidim/proposals/admin/proposals_controller.rb +105 -29
- data/app/controllers/decidim/proposals/admin/valuation_assignments_controller.rb +58 -0
- data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +19 -3
- data/app/controllers/decidim/proposals/proposals_controller.rb +42 -7
- data/app/controllers/decidim/proposals/versions_controller.rb +4 -1
- data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +11 -0
- data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +27 -2
- data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +37 -0
- data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +8 -0
- data/app/helpers/decidim/proposals/admin/filterable_helper.rb +17 -0
- data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +35 -0
- data/app/helpers/decidim/proposals/admin/proposal_rankings_helper.rb +63 -0
- data/app/helpers/decidim/proposals/admin/proposals_helper.rb +122 -0
- data/app/helpers/decidim/proposals/application_helper.rb +36 -25
- data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +9 -9
- data/app/helpers/decidim/proposals/proposal_cells_helper.rb +1 -1
- data/app/helpers/decidim/proposals/proposals_helper.rb +18 -0
- data/app/models/decidim/proposals/proposal.rb +163 -16
- data/app/models/decidim/proposals/valuation_assignment.rb +24 -0
- data/app/permissions/decidim/proposals/admin/permissions.rb +77 -11
- data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
- data/app/presenters/decidim/proposals/admin_log/valuation_assignment_presenter.rb +51 -0
- data/app/presenters/decidim/proposals/admin_log/value_types/valuator_role_user_presenter.rb +19 -0
- data/app/presenters/decidim/proposals/collaborative_draft_presenter.rb +2 -28
- data/app/presenters/decidim/proposals/log/valuation_assignment_presenter.rb +22 -0
- data/app/presenters/decidim/proposals/proposal_presenter.rb +26 -1
- data/app/services/decidim/proposals/collaborative_draft_search.rb +18 -10
- data/app/services/decidim/proposals/proposal_search.rb +33 -40
- data/app/types/decidim/proposals/proposal_input_filter.rb +29 -0
- data/app/types/decidim/proposals/proposal_input_sort.rb +28 -0
- data/app/types/decidim/proposals/proposal_type.rb +35 -4
- data/app/types/decidim/proposals/proposals_type.rb +14 -17
- data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +35 -0
- data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +8 -2
- data/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +25 -17
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +15 -0
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +21 -1
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +14 -0
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +25 -0
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +15 -0
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +16 -7
- data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +12 -0
- data/app/views/decidim/proposals/admin/proposals/show.html.erb +186 -0
- data/app/views/decidim/proposals/admin/proposals/update_category.js.erb +3 -2
- data/app/views/decidim/proposals/admin/proposals/update_scope.js.erb +27 -0
- data/app/views/decidim/proposals/collaborative_drafts/_filters.html.erb +3 -3
- data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_filters.html.erb +12 -12
- data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +1 -4
- data/app/views/decidim/proposals/proposals/_proposal_preview.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/index.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/new.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/show.html.erb +17 -23
- data/config/locales/ar.yml +69 -17
- data/config/locales/ca.yml +113 -18
- data/config/locales/cs.yml +123 -31
- data/config/locales/de.yml +38 -18
- data/config/locales/el.yml +1 -0
- data/config/locales/en.yml +112 -17
- data/config/locales/es-MX.yml +112 -17
- data/config/locales/es-PY.yml +112 -17
- data/config/locales/es.yml +113 -18
- data/config/locales/eu.yml +38 -18
- data/config/locales/fi-plain.yml +113 -18
- data/config/locales/fi.yml +113 -18
- data/config/locales/fr.yml +38 -18
- data/config/locales/gl.yml +38 -18
- data/config/locales/hu.yml +112 -17
- data/config/locales/id-ID.yml +38 -18
- data/config/locales/is-IS.yml +25 -15
- data/config/locales/it.yml +38 -18
- data/config/locales/nl.yml +43 -18
- data/config/locales/no.yml +66 -18
- data/config/locales/pl.yml +38 -18
- data/config/locales/pt-BR.yml +39 -19
- data/config/locales/pt.yml +39 -19
- data/config/locales/ru.yml +25 -17
- data/config/locales/sv.yml +39 -18
- data/config/locales/tr-TR.yml +38 -18
- data/config/locales/uk.yml +25 -17
- data/db/migrate/20200203111239_add_proposal_valuation_assignments.rb +12 -0
- data/db/migrate/20200210135152_add_costs_to_proposals.rb +9 -0
- data/db/migrate/20200212120110_sync_proposals_state_with_amendments_state.rb +28 -0
- data/db/migrate/20200227175922_add_state_published_at_to_proposals.rb +7 -0
- data/db/migrate/20200306123652_publish_existing_proposals_state.rb +15 -0
- data/lib/decidim/proposals.rb +1 -0
- data/lib/decidim/proposals/admin_engine.rb +7 -3
- data/lib/decidim/proposals/component.rb +39 -19
- data/lib/decidim/proposals/engine.rb +1 -1
- data/lib/decidim/proposals/test/factories.rb +55 -0
- data/lib/decidim/proposals/valuatable.rb +21 -0
- data/lib/decidim/proposals/version.rb +1 -1
- metadata +53 -36
- data/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +0 -22
- data/app/views/decidim/proposals/admin/proposal_notes/index.html.erb +0 -3
- data/app/views/decidim/proposals/admin/shared/_info_proposal.html.erb +0 -20
- data/app/views/decidim/proposals/proposal_widgets/show.html.erb +0 -4
@@ -13,6 +13,8 @@ module Decidim
|
|
13
13
|
include Decidim::Proposals::MapHelper
|
14
14
|
include CollaborativeDraftHelper
|
15
15
|
include ControlVersionHelper
|
16
|
+
include Decidim::RichTextEditorHelper
|
17
|
+
include Decidim::CheckBoxesTreeHelper
|
16
18
|
|
17
19
|
delegate :minimum_votes_per_user, to: :component_settings
|
18
20
|
|
@@ -78,6 +80,40 @@ module Decidim
|
|
78
80
|
minimum_votes_per_user.positive?
|
79
81
|
end
|
80
82
|
|
83
|
+
def not_from_collaborative_draft(proposal)
|
84
|
+
proposal.linked_resources(:proposals, "created_from_collaborative_draft").empty?
|
85
|
+
end
|
86
|
+
|
87
|
+
def not_from_participatory_text(proposal)
|
88
|
+
proposal.participatory_text_level.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
# If the proposal is official or the rich text editor is enabled on the
|
92
|
+
# frontend, the proposal body is considered as safe content; that's unless
|
93
|
+
# the proposal comes from a collaborative_draft or a participatory_text.
|
94
|
+
def safe_content?
|
95
|
+
rich_text_editor_in_public_views? && not_from_collaborative_draft(@proposal) ||
|
96
|
+
(@proposal.official? || @proposal.official_meeting?) && not_from_participatory_text(@proposal)
|
97
|
+
end
|
98
|
+
|
99
|
+
# If the content is safe, HTML tags are sanitized, otherwise, they are stripped.
|
100
|
+
def render_proposal_body(proposal)
|
101
|
+
body = present(proposal).body(links: true, strip_tags: !safe_content?)
|
102
|
+
|
103
|
+
safe_content? ? decidim_sanitize(body) : simple_format(body, {}, sanitize: false)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns :text_area or :editor based on the organization' settings.
|
107
|
+
def text_editor_for_proposal_body(form)
|
108
|
+
options = {
|
109
|
+
class: "js-hashtags",
|
110
|
+
hashtaggable: true,
|
111
|
+
value: form_presenter.body(extras: false).strip
|
112
|
+
}
|
113
|
+
|
114
|
+
text_editor_for(form, :body, options)
|
115
|
+
end
|
116
|
+
|
81
117
|
def proposal_limit
|
82
118
|
return if component_settings.proposal_limit.zero?
|
83
119
|
|
@@ -125,31 +161,6 @@ module Decidim
|
|
125
161
|
return true if minimum_votes_per_user_enabled?
|
126
162
|
end
|
127
163
|
|
128
|
-
def filter_origin_values
|
129
|
-
base = if component_settings.official_proposals_enabled
|
130
|
-
[
|
131
|
-
["all", t("decidim.proposals.application_helper.filter_origin_values.all")],
|
132
|
-
["official", t("decidim.proposals.application_helper.filter_origin_values.official")]
|
133
|
-
]
|
134
|
-
else
|
135
|
-
[["all", t("decidim.proposals.application_helper.filter_origin_values.all")]]
|
136
|
-
end
|
137
|
-
|
138
|
-
base += [["citizens", t("decidim.proposals.application_helper.filter_origin_values.citizens")]]
|
139
|
-
base += [["user_group", t("decidim.proposals.application_helper.filter_origin_values.user_groups")]] if current_organization.user_groups_enabled?
|
140
|
-
base + [["meeting", t("decidim.proposals.application_helper.filter_origin_values.meetings")]]
|
141
|
-
end
|
142
|
-
|
143
|
-
def filter_state_values
|
144
|
-
[
|
145
|
-
["except_rejected", t("decidim.proposals.application_helper.filter_state_values.except_rejected")],
|
146
|
-
["accepted", t("decidim.proposals.application_helper.filter_state_values.accepted")],
|
147
|
-
["evaluating", t("decidim.proposals.application_helper.filter_state_values.evaluating")],
|
148
|
-
["rejected", t("decidim.proposals.application_helper.filter_state_values.rejected")],
|
149
|
-
["all", t("decidim.proposals.application_helper.filter_state_values.all")]
|
150
|
-
]
|
151
|
-
end
|
152
|
-
|
153
164
|
def filter_type_values
|
154
165
|
[
|
155
166
|
["all", t("decidim.proposals.application_helper.filter_type_values.all")],
|
@@ -5,16 +5,16 @@ module Decidim
|
|
5
5
|
# Custom helpers, scoped to the collaborative_draft resource.
|
6
6
|
#
|
7
7
|
module CollaborativeDraftHelper
|
8
|
-
def
|
8
|
+
def filter_collaborative_drafts_state_values
|
9
9
|
scope = "decidim.proposals.collaborative_drafts.filters"
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
Decidim::CheckBoxesTreeHelper::TreeNode.new(
|
11
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("", t("all", scope: scope)),
|
12
|
+
[
|
13
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("open", t("open", scope: scope)),
|
14
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("withdrawn", t("withdrawn", scope: scope)),
|
15
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("published", t("published", scope: scope))
|
16
|
+
]
|
17
|
+
)
|
18
18
|
end
|
19
19
|
|
20
20
|
def accept_request_button_label
|
@@ -14,7 +14,7 @@ module Decidim
|
|
14
14
|
include Decidim::TranslatableAttributes
|
15
15
|
include Decidim::CardHelper
|
16
16
|
|
17
|
-
delegate :title, :state, :
|
17
|
+
delegate :title, :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?
|
@@ -37,6 +37,24 @@ module Decidim
|
|
37
37
|
|
38
38
|
t(i18n_key, scope: "decidim.proposals.proposals.show")
|
39
39
|
end
|
40
|
+
|
41
|
+
def filter_proposals_state_values
|
42
|
+
Decidim::CheckBoxesTreeHelper::TreeNode.new(
|
43
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("", t("decidim.proposals.application_helper.filter_state_values.all")),
|
44
|
+
[
|
45
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("accepted", t("decidim.proposals.application_helper.filter_state_values.accepted")),
|
46
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("evaluating", t("decidim.proposals.application_helper.filter_state_values.evaluating")),
|
47
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("not_answered", t("decidim.proposals.application_helper.filter_state_values.not_answered")),
|
48
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("rejected", t("decidim.proposals.application_helper.filter_state_values.rejected"))
|
49
|
+
]
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def proposal_has_costs?
|
54
|
+
@proposal.cost.present? &&
|
55
|
+
translated_attribute(@proposal.cost_report).present? &&
|
56
|
+
translated_attribute(@proposal.execution_period).present?
|
57
|
+
end
|
40
58
|
end
|
41
59
|
end
|
42
60
|
end
|
@@ -24,6 +24,9 @@ module Decidim
|
|
24
24
|
include Decidim::Amendable
|
25
25
|
include Decidim::NewsletterParticipant
|
26
26
|
include Decidim::Randomable
|
27
|
+
include Decidim::Proposals::Valuatable
|
28
|
+
|
29
|
+
POSSIBLE_STATES = %w(not_answered evaluating accepted rejected withdrawn).freeze
|
27
30
|
|
28
31
|
fingerprint fields: [:title, :body]
|
29
32
|
|
@@ -49,15 +52,56 @@ module Decidim
|
|
49
52
|
|
50
53
|
geocoded_by :address, http_headers: ->(proposal) { { "Referer" => proposal.component.organization.host } }
|
51
54
|
|
52
|
-
scope :
|
53
|
-
scope :
|
54
|
-
|
55
|
+
scope :answered, -> { where.not(answered_at: nil) }
|
56
|
+
scope :not_answered, -> { where(answered_at: nil) }
|
57
|
+
|
58
|
+
scope :state_not_published, -> { where(state_published_at: nil) }
|
59
|
+
scope :state_published, -> { where.not(state_published_at: nil).where.not(state: nil) }
|
60
|
+
|
61
|
+
scope :accepted, -> { state_published.where(state: "accepted") }
|
62
|
+
scope :rejected, -> { state_published.where(state: "rejected") }
|
63
|
+
scope :evaluating, -> { state_published.where(state: "evaluating") }
|
55
64
|
scope :withdrawn, -> { where(state: "withdrawn") }
|
56
|
-
scope :except_rejected, -> { where.not(state: "rejected").or(
|
65
|
+
scope :except_rejected, -> { where.not(state: "rejected").or(state_not_published) }
|
57
66
|
scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
|
58
67
|
scope :drafts, -> { where(published_at: nil) }
|
59
68
|
scope :except_drafts, -> { where.not(published_at: nil) }
|
60
69
|
scope :published, -> { where.not(published_at: nil) }
|
70
|
+
scope :official_origin, lambda {
|
71
|
+
where.not(coauthorships_count: 0)
|
72
|
+
.joins(:coauthorships)
|
73
|
+
.where(decidim_coauthorships: { decidim_author_type: "Decidim::Organization" })
|
74
|
+
}
|
75
|
+
scope :citizens_origin, lambda {
|
76
|
+
where.not(coauthorships_count: 0)
|
77
|
+
.joins(:coauthorships)
|
78
|
+
.where.not(decidim_coauthorships: { decidim_author_type: "Decidim::Organization" })
|
79
|
+
}
|
80
|
+
scope :user_group_origin, lambda {
|
81
|
+
where.not(coauthorships_count: 0)
|
82
|
+
.joins(:coauthorships)
|
83
|
+
.where(decidim_coauthorships: { decidim_author_type: "Decidim::UserBaseEntity" })
|
84
|
+
.where.not(decidim_coauthorships: { decidim_user_group_id: nil })
|
85
|
+
}
|
86
|
+
scope :meeting_origin, lambda {
|
87
|
+
where.not(coauthorships_count: 0)
|
88
|
+
.joins(:coauthorships)
|
89
|
+
.where(decidim_coauthorships: { decidim_author_type: "Decidim::Meetings::Meeting" })
|
90
|
+
}
|
91
|
+
scope :sort_by_valuation_assignments_count_asc, lambda {
|
92
|
+
order(sort_by_valuation_assignments_count_nulls_last_query + "ASC NULLS FIRST")
|
93
|
+
}
|
94
|
+
|
95
|
+
scope :sort_by_valuation_assignments_count_desc, lambda {
|
96
|
+
order(sort_by_valuation_assignments_count_nulls_last_query + "DESC NULLS LAST")
|
97
|
+
}
|
98
|
+
|
99
|
+
def self.with_valuation_assigned_to(user, space)
|
100
|
+
valuator_roles = space.user_roles(:valuator).where(user: user)
|
101
|
+
|
102
|
+
includes(:valuation_assignments)
|
103
|
+
.where(decidim_proposals_valuation_assignments: { valuator_role_id: valuator_roles })
|
104
|
+
end
|
61
105
|
|
62
106
|
acts_as_list scope: :decidim_component_id
|
63
107
|
|
@@ -140,39 +184,68 @@ module Decidim
|
|
140
184
|
published_at.present?
|
141
185
|
end
|
142
186
|
|
187
|
+
# Public: Returns the published state of the proposal.
|
188
|
+
#
|
189
|
+
# Returns Boolean.
|
190
|
+
def state
|
191
|
+
return amendment.state if emendation?
|
192
|
+
return nil unless published_state? || withdrawn?
|
193
|
+
|
194
|
+
super
|
195
|
+
end
|
196
|
+
|
197
|
+
# This is only used to define the setter, as the getter will be overriden below.
|
198
|
+
alias_attribute :internal_state, :state
|
199
|
+
|
200
|
+
# Public: Returns the internal state of the proposal.
|
201
|
+
#
|
202
|
+
# Returns Boolean.
|
203
|
+
def internal_state
|
204
|
+
return amendment.state if emendation?
|
205
|
+
|
206
|
+
self[:state]
|
207
|
+
end
|
208
|
+
|
209
|
+
# Public: Checks if the organization has published the state for the proposal.
|
210
|
+
#
|
211
|
+
# Returns Boolean.
|
212
|
+
def published_state?
|
213
|
+
emendation? || state_published_at.present?
|
214
|
+
end
|
215
|
+
|
143
216
|
# Public: Checks if the organization has given an answer for the proposal.
|
144
217
|
#
|
145
218
|
# Returns Boolean.
|
146
219
|
def answered?
|
147
|
-
answered_at.present?
|
220
|
+
answered_at.present?
|
221
|
+
end
|
222
|
+
|
223
|
+
# Public: Checks if the author has withdrawn the proposal.
|
224
|
+
#
|
225
|
+
# Returns Boolean.
|
226
|
+
def withdrawn?
|
227
|
+
internal_state == "withdrawn"
|
148
228
|
end
|
149
229
|
|
150
230
|
# Public: Checks if the organization has accepted a proposal.
|
151
231
|
#
|
152
232
|
# Returns Boolean.
|
153
233
|
def accepted?
|
154
|
-
|
234
|
+
state == "accepted"
|
155
235
|
end
|
156
236
|
|
157
237
|
# Public: Checks if the organization has rejected a proposal.
|
158
238
|
#
|
159
239
|
# Returns Boolean.
|
160
240
|
def rejected?
|
161
|
-
|
241
|
+
state == "rejected"
|
162
242
|
end
|
163
243
|
|
164
244
|
# Public: Checks if the organization has marked the proposal as evaluating it.
|
165
245
|
#
|
166
246
|
# Returns Boolean.
|
167
247
|
def evaluating?
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
|
-
# Public: Checks if the author has withdrawn the proposal.
|
172
|
-
#
|
173
|
-
# Returns Boolean.
|
174
|
-
def withdrawn?
|
175
|
-
state == "withdrawn"
|
248
|
+
state == "evaluating"
|
176
249
|
end
|
177
250
|
|
178
251
|
# Public: Overrides the `reported_content_url` Reportable concern method.
|
@@ -222,7 +295,7 @@ module Decidim
|
|
222
295
|
def editable_by?(user)
|
223
296
|
return true if draft?
|
224
297
|
|
225
|
-
!
|
298
|
+
!published_state? && within_edit_time_limit? && !copied_from_other_component? && created_by?(user)
|
226
299
|
end
|
227
300
|
|
228
301
|
# Checks whether the user can withdraw the given proposal.
|
@@ -250,6 +323,69 @@ module Decidim
|
|
250
323
|
Arel.sql(query)
|
251
324
|
end
|
252
325
|
|
326
|
+
# Defines the base query so that ransack can actually sort by this value
|
327
|
+
def self.sort_by_valuation_assignments_count_nulls_last_query
|
328
|
+
<<-SQL
|
329
|
+
(
|
330
|
+
SELECT COUNT(decidim_proposals_valuation_assignments.id)
|
331
|
+
FROM decidim_proposals_valuation_assignments
|
332
|
+
WHERE decidim_proposals_valuation_assignments.decidim_proposal_id = decidim_proposals_proposals.id
|
333
|
+
GROUP BY decidim_proposals_valuation_assignments.decidim_proposal_id
|
334
|
+
)
|
335
|
+
SQL
|
336
|
+
end
|
337
|
+
|
338
|
+
# method to filter by assigned valuator role ID
|
339
|
+
def self.valuator_role_ids_has(value)
|
340
|
+
query = <<-SQL
|
341
|
+
:value = any(
|
342
|
+
(SELECT decidim_proposals_valuation_assignments.valuator_role_id
|
343
|
+
FROM decidim_proposals_valuation_assignments
|
344
|
+
WHERE decidim_proposals_valuation_assignments.decidim_proposal_id = decidim_proposals_proposals.id
|
345
|
+
)
|
346
|
+
)
|
347
|
+
SQL
|
348
|
+
where(query, value: value)
|
349
|
+
end
|
350
|
+
|
351
|
+
def self.ransackable_scopes(_auth = nil)
|
352
|
+
[:valuator_role_ids_has]
|
353
|
+
end
|
354
|
+
|
355
|
+
ransacker :state_published do
|
356
|
+
Arel.sql("CASE
|
357
|
+
WHEN EXISTS (
|
358
|
+
SELECT 1 FROM decidim_amendments
|
359
|
+
WHERE decidim_amendments.decidim_emendation_type = 'Decidim::Proposals::Proposal'
|
360
|
+
AND decidim_amendments.decidim_emendation_id = decidim_proposals_proposals.id
|
361
|
+
) THEN 0
|
362
|
+
WHEN state_published_at IS NULL AND answered_at IS NOT NULL THEN 2
|
363
|
+
WHEN state_published_at IS NOT NULL THEN 1
|
364
|
+
ELSE 0 END
|
365
|
+
")
|
366
|
+
end
|
367
|
+
|
368
|
+
ransacker :state do
|
369
|
+
Arel.sql("CASE WHEN state = 'withdrawn' THEN 'withdrawn' WHEN state_published_at IS NULL THEN NULL ELSE state END")
|
370
|
+
end
|
371
|
+
|
372
|
+
ransacker :id_string do
|
373
|
+
Arel.sql(%{cast("decidim_proposals_proposals"."id" as text)})
|
374
|
+
end
|
375
|
+
|
376
|
+
ransacker :is_emendation do |_parent|
|
377
|
+
query = <<-SQL
|
378
|
+
(
|
379
|
+
SELECT EXISTS (
|
380
|
+
SELECT 1 FROM decidim_amendments
|
381
|
+
WHERE decidim_amendments.decidim_emendation_type = 'Decidim::Proposals::Proposal'
|
382
|
+
AND decidim_amendments.decidim_emendation_id = decidim_proposals_proposals.id
|
383
|
+
)
|
384
|
+
)
|
385
|
+
SQL
|
386
|
+
Arel.sql(query)
|
387
|
+
end
|
388
|
+
|
253
389
|
def self.export_serializer
|
254
390
|
Decidim::Proposals::ProposalSerializer
|
255
391
|
end
|
@@ -271,6 +407,17 @@ module Decidim
|
|
271
407
|
Time.current < limit
|
272
408
|
end
|
273
409
|
|
410
|
+
def process_amendment_state_change!
|
411
|
+
return unless %w(accepted rejected evaluating withdrawn).member?(amendment.state)
|
412
|
+
|
413
|
+
PaperTrail.request(enabled: false) do
|
414
|
+
update!(
|
415
|
+
state: amendment.state,
|
416
|
+
state_published_at: Time.current
|
417
|
+
)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
274
421
|
private
|
275
422
|
|
276
423
|
def copied_from_other_component?
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
# A valuation assignment links a proposal and a Valuator user role.
|
6
|
+
# Valuators will be users in charge of defining the monetary cost of a
|
7
|
+
# proposal.
|
8
|
+
class ValuationAssignment < ApplicationRecord
|
9
|
+
include Decidim::Traceable
|
10
|
+
include Decidim::Loggable
|
11
|
+
|
12
|
+
belongs_to :proposal, foreign_key: "decidim_proposal_id", class_name: "Decidim::Proposals::Proposal"
|
13
|
+
belongs_to :valuator_role, polymorphic: true
|
14
|
+
|
15
|
+
def self.log_presenter_class_for(_log)
|
16
|
+
Decidim::Proposals::AdminLog::ValuationAssignmentPresenter
|
17
|
+
end
|
18
|
+
|
19
|
+
def valuator
|
20
|
+
valuator_role.user
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -8,18 +8,22 @@ module Decidim
|
|
8
8
|
# The public part needs to be implemented yet
|
9
9
|
return permission_action if permission_action.scope != :admin
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
# Valuators can only perform these actions
|
12
|
+
if user_is_valuator?
|
13
|
+
if valuator_assigned_to_proposal?
|
14
|
+
can_create_proposal_note?
|
15
|
+
can_create_proposal_answer?
|
16
|
+
end
|
17
|
+
can_export_proposals?
|
18
|
+
valuator_can_unassign_valuator_from_proposals?
|
19
|
+
|
20
|
+
return permission_action
|
21
|
+
end
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
+
if create_permission_action?
|
24
|
+
can_create_proposal_note?
|
25
|
+
can_create_proposal_from_admin?
|
26
|
+
can_create_proposal_answer?
|
23
27
|
end
|
24
28
|
|
25
29
|
# Admins can only edit official proposals if they are within the
|
@@ -29,15 +33,30 @@ module Decidim
|
|
29
33
|
# Every user allowed by the space can update the category of the proposal
|
30
34
|
allow! if permission_action.subject == :proposal_category && permission_action.action == :update
|
31
35
|
|
36
|
+
# Every user allowed by the space can update the scope of the proposal
|
37
|
+
allow! if permission_action.subject == :proposal_scope && permission_action.action == :update
|
38
|
+
|
32
39
|
# Every user allowed by the space can import proposals from another_component
|
33
40
|
allow! if permission_action.subject == :proposals && permission_action.action == :import
|
34
41
|
|
42
|
+
# Every user allowed by the space can export proposals
|
43
|
+
can_export_proposals?
|
44
|
+
|
35
45
|
# Every user allowed by the space can merge proposals to another component
|
36
46
|
allow! if permission_action.subject == :proposals && permission_action.action == :merge
|
37
47
|
|
38
48
|
# Every user allowed by the space can split proposals to another component
|
39
49
|
allow! if permission_action.subject == :proposals && permission_action.action == :split
|
40
50
|
|
51
|
+
# Every user allowed by the space can assign proposals to a valuator
|
52
|
+
allow! if permission_action.subject == :proposals && permission_action.action == :assign_to_valuator
|
53
|
+
|
54
|
+
# Every user allowed by the space can unassign a valuator from proposals
|
55
|
+
can_unassign_valuator_from_proposals?
|
56
|
+
|
57
|
+
# Only admin users can publish many answers at once
|
58
|
+
toggle_allow(user.admin?) if permission_action.subject == :proposals && permission_action.action == :publish_answers
|
59
|
+
|
41
60
|
if permission_action.subject == :participatory_texts && participatory_texts_are_enabled?
|
42
61
|
# Every user allowed by the space can manage (import, update and publish) participatory texts to proposals
|
43
62
|
allow! if permission_action.action == :manage
|
@@ -52,6 +71,23 @@ module Decidim
|
|
52
71
|
@proposal ||= context.fetch(:proposal, nil)
|
53
72
|
end
|
54
73
|
|
74
|
+
def user_valuator_role
|
75
|
+
@user_valuator_role ||= space.user_roles(:valuator).find_by(user: user)
|
76
|
+
end
|
77
|
+
|
78
|
+
def user_is_valuator?
|
79
|
+
return if user.admin?
|
80
|
+
|
81
|
+
user_valuator_role.present?
|
82
|
+
end
|
83
|
+
|
84
|
+
def valuator_assigned_to_proposal?
|
85
|
+
@valuator_assigned_to_proposal ||=
|
86
|
+
Decidim::Proposals::ValuationAssignment
|
87
|
+
.where(proposal: proposal, valuator_role: user_valuator_role)
|
88
|
+
.any?
|
89
|
+
end
|
90
|
+
|
55
91
|
def admin_creation_is_enabled?
|
56
92
|
current_settings.try(:creation_enabled?) &&
|
57
93
|
component_settings.try(:official_proposals_enabled)
|
@@ -75,6 +111,36 @@ module Decidim
|
|
75
111
|
def participatory_texts_are_enabled?
|
76
112
|
component_settings.participatory_texts_enabled?
|
77
113
|
end
|
114
|
+
|
115
|
+
# There's no special condition to create proposal notes, only
|
116
|
+
# users with access to the admin section can do it.
|
117
|
+
def can_create_proposal_note?
|
118
|
+
allow! if permission_action.subject == :proposal_note
|
119
|
+
end
|
120
|
+
|
121
|
+
# Proposals can only be created from the admin when the
|
122
|
+
# corresponding setting is enabled.
|
123
|
+
def can_create_proposal_from_admin?
|
124
|
+
toggle_allow(admin_creation_is_enabled?) if permission_action.subject == :proposal
|
125
|
+
end
|
126
|
+
|
127
|
+
# Proposals can only be answered from the admin when the
|
128
|
+
# corresponding setting is enabled.
|
129
|
+
def can_create_proposal_answer?
|
130
|
+
toggle_allow(admin_proposal_answering_is_enabled?) if permission_action.subject == :proposal_answer
|
131
|
+
end
|
132
|
+
|
133
|
+
def can_unassign_valuator_from_proposals?
|
134
|
+
allow! if permission_action.subject == :proposals && permission_action.action == :unassign_from_valuator
|
135
|
+
end
|
136
|
+
|
137
|
+
def valuator_can_unassign_valuator_from_proposals?
|
138
|
+
can_unassign_valuator_from_proposals? if user == context.fetch(:valuator, nil)
|
139
|
+
end
|
140
|
+
|
141
|
+
def can_export_proposals?
|
142
|
+
allow! if permission_action.subject == :proposals && permission_action.action == :export
|
143
|
+
end
|
78
144
|
end
|
79
145
|
end
|
80
146
|
end
|