decidim-proposals 0.29.1 → 0.29.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +0 -12
- data/app/cells/decidim/proposals/proposal_g_cell.rb +21 -0
- data/app/cells/decidim/proposals/proposal_l_cell.rb +17 -18
- data/app/commands/decidim/proposals/admin/import_proposals.rb +7 -65
- data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +1 -1
- data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +7 -0
- data/app/controllers/decidim/proposals/admin/proposals_imports_controller.rb +2 -2
- data/app/controllers/decidim/proposals/proposals_controller.rb +4 -8
- data/app/controllers/decidim/proposals/versions_controller.rb +1 -1
- data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +3 -18
- data/app/forms/decidim/proposals/admin/proposal_form.rb +1 -1
- data/app/forms/decidim/proposals/admin/proposals_import_form.rb +6 -9
- data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +4 -1
- data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +1 -1
- data/app/helpers/decidim/proposals/application_helper.rb +1 -0
- data/app/helpers/decidim/proposals/map_helper.rb +1 -1
- data/app/helpers/decidim/proposals/proposals_helper.rb +1 -3
- data/app/jobs/decidim/proposals/admin/import_proposals_job.rb +91 -0
- data/app/mailers/decidim/proposals/admin/import_proposals_mailer.rb +30 -0
- data/app/models/decidim/proposals/collaborative_draft.rb +2 -3
- data/app/models/decidim/proposals/proposal.rb +2 -11
- data/app/packs/stylesheets/decidim/proposals/proposals.scss +1 -5
- data/app/permissions/decidim/proposals/permissions.rb +4 -3
- data/app/services/decidim/proposals/collaborative_draft_diff_renderer.rb +22 -0
- data/app/services/decidim/proposals/diff_renderer.rb +2 -0
- data/app/services/decidim/proposals/proposal_builder.rb +2 -1
- data/app/views/decidim/proposals/admin/import_proposals_mailer/notify_failure.html.erb +1 -0
- data/app/views/decidim/proposals/admin/import_proposals_mailer/notify_success.html.erb +2 -0
- data/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +2 -2
- data/app/views/decidim/proposals/admin/proposals/show.html.erb +4 -4
- data/app/views/decidim/proposals/admin/proposals_imports/new.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/_proposals.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/index.html.erb +9 -2
- data/app/views/decidim/proposals/proposals/index.js.erb +12 -0
- data/app/views/decidim/proposals/proposals/show.html.erb +1 -1
- data/config/locales/ar.yml +8 -6
- data/config/locales/bg.yml +5 -12
- data/config/locales/bn-BD.yml +1 -0
- data/config/locales/bs-BA.yml +98 -0
- data/config/locales/ca-IT.yml +945 -0
- data/config/locales/ca.yml +27 -12
- data/config/locales/cs.yml +33 -8
- data/config/locales/de.yml +27 -12
- data/config/locales/el.yml +5 -9
- data/config/locales/en.yml +18 -3
- data/config/locales/es-MX.yml +29 -14
- data/config/locales/es-PY.yml +24 -9
- data/config/locales/es.yml +32 -17
- data/config/locales/eu.yml +185 -163
- data/config/locales/fi-plain.yml +25 -10
- data/config/locales/fi.yml +42 -27
- data/config/locales/fr-CA.yml +20 -10
- data/config/locales/fr.yml +19 -9
- data/config/locales/ga-IE.yml +2 -1
- data/config/locales/gl.yml +5 -6
- data/config/locales/he-IL.yml +7 -0
- data/config/locales/hu.yml +4 -9
- data/config/locales/id-ID.yml +12 -5
- data/config/locales/is-IS.yml +5 -7
- data/config/locales/it.yml +31 -11
- data/config/locales/ja.yml +22 -8
- data/config/locales/lt.yml +3 -8
- data/config/locales/lv.yml +7 -4
- data/config/locales/nl.yml +11 -9
- data/config/locales/no.yml +3 -6
- data/config/locales/pl.yml +2 -9
- data/config/locales/pt-BR.yml +3 -8
- data/config/locales/pt.yml +4 -6
- data/config/locales/ro-RO.yml +25 -11
- data/config/locales/ru.yml +5 -7
- data/config/locales/sk.yml +8 -5
- data/config/locales/sl.yml +0 -4
- data/config/locales/sr-CS.yml +0 -2
- data/config/locales/sv.yml +11 -8
- data/config/locales/tr-TR.yml +7 -9
- data/config/locales/uk.yml +6 -8
- data/config/locales/zh-CN.yml +3 -5
- data/config/locales/zh-TW.yml +5 -9
- data/db/migrate/20240110203504_create_default_proposal_states.rb +3 -2
- data/decidim-proposals.gemspec +1 -1
- data/lib/decidim/api/proposal_type.rb +13 -0
- data/lib/decidim/api/proposals_type.rb +1 -3
- data/lib/decidim/proposals/proposal_serializer.rb +7 -4
- data/lib/decidim/proposals/test/factories.rb +6 -5
- data/lib/decidim/proposals/version.rb +1 -1
- data/lib/tasks/proposals/upgrade/decidim_proposals_upgrade_tasks.rake +22 -0
- metadata +29 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f68f4460c5148fc90b2ff8d92c25b7c1c5afae4347c4ce8bdcdfe2eec7e533cb
|
4
|
+
data.tar.gz: 06c7f3d44d8534c22b816d9ba8e216d1fa854337ada6158056a06bcb70a46598
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e10e7ef879ce34a18a5b9e101ca6fef2cc8d4c7434b6f0599fa1d5cf830e34e00fbd8fd9cb571b280e2c2113fc375f8233d6bfa4b32fea9c864958c0ab62c342
|
7
|
+
data.tar.gz: 5363d5e2789296bb4f969919776c406a2ede64f6e1651a27c56c9fab3b55c82e35f00b60c4a8c564f5192ce7b4d03a581f557b217c2185810c37f237e961d740
|
@@ -10,22 +10,10 @@
|
|
10
10
|
<% end %>
|
11
11
|
<% end %>
|
12
12
|
</div>
|
13
|
-
|
14
13
|
<div class="flex items-start justify-between">
|
15
14
|
<div class="grow space-y-6">
|
16
|
-
<span class="content-block__span">
|
17
|
-
<%= t("decidim.participatory_spaces.highlighted_proposals.last") %>
|
18
|
-
</span>
|
19
|
-
|
20
15
|
<% proposals_to_render.each do |p| %>
|
21
16
|
<%= card_for p, link_whole_card: true, title_tag: :h3, **options.slice(:show_space) %>
|
22
17
|
<% end %>
|
23
18
|
</div>
|
24
|
-
|
25
|
-
<% if single_component? %>
|
26
|
-
<%= link_to decidim_proposals.new_proposal_path, class: "button button__sm md:button__lg button__secondary" do %>
|
27
|
-
<span><%= t("decidim.proposals.actions.new") %></span>
|
28
|
-
<%= icon "add-line" %>
|
29
|
-
<% end %>
|
30
|
-
<% end %>
|
31
19
|
</div>
|
@@ -34,6 +34,27 @@ module Decidim
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
+
def cache_hash
|
38
|
+
@cache_hash ||= begin
|
39
|
+
hash = []
|
40
|
+
hash << I18n.locale.to_s
|
41
|
+
hash << self.class.name.demodulize.underscore
|
42
|
+
hash << model.cache_key_with_version
|
43
|
+
hash << model.proposal_votes_count
|
44
|
+
hash << model.endorsements_count
|
45
|
+
hash << model.comments_count
|
46
|
+
hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
|
47
|
+
hash << Digest::MD5.hexdigest(resource_image_url) if resource_image_url
|
48
|
+
hash << 0 # render space
|
49
|
+
hash << model.follows_count
|
50
|
+
hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
|
51
|
+
hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
|
52
|
+
hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
|
53
|
+
|
54
|
+
hash.join(Decidim.cache_key_separator)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
37
58
|
def classes
|
38
59
|
super.merge(metadata: "card__list-metadata")
|
39
60
|
end
|
@@ -20,25 +20,24 @@ module Decidim
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def cache_hash
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
hash <<
|
34
|
-
hash <<
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
|
39
|
-
hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
|
23
|
+
@cache_hash ||= begin
|
24
|
+
hash = []
|
25
|
+
hash << I18n.locale.to_s
|
26
|
+
hash << self.class.name.demodulize.underscore
|
27
|
+
hash << model.cache_key_with_version
|
28
|
+
hash << model.proposal_votes_count
|
29
|
+
hash << model.endorsements_count
|
30
|
+
hash << model.comments_count
|
31
|
+
hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
|
32
|
+
hash << Digest::MD5.hexdigest(resource_image_url) if resource_image_url
|
33
|
+
hash << render_space? ? 1 : 0
|
34
|
+
hash << model.follows_count
|
35
|
+
hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
|
36
|
+
hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
|
37
|
+
hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
|
40
38
|
|
41
|
-
|
39
|
+
hash.join(Decidim.cache_key_separator)
|
40
|
+
end
|
42
41
|
end
|
43
42
|
end
|
44
43
|
end
|
@@ -22,7 +22,8 @@ module Decidim
|
|
22
22
|
def call
|
23
23
|
return broadcast(:invalid) unless form.valid?
|
24
24
|
|
25
|
-
|
25
|
+
import_proposals
|
26
|
+
broadcast(:ok)
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
@@ -30,70 +31,11 @@ module Decidim
|
|
30
31
|
attr_reader :form
|
31
32
|
|
32
33
|
def import_proposals
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
author: proposal_author,
|
39
|
-
action_user: form.current_user,
|
40
|
-
extra_attributes: {
|
41
|
-
"component" => target_component
|
42
|
-
}.merge(proposal_answer_attributes(original_proposal))
|
43
|
-
)
|
44
|
-
end.compact
|
45
|
-
end
|
46
|
-
|
47
|
-
def proposals
|
48
|
-
@proposals = Decidim::Proposals::Proposal
|
49
|
-
.where(component: origin_component)
|
50
|
-
@proposals = @proposals.where(scope: proposal_scopes) unless proposal_scopes.empty?
|
51
|
-
|
52
|
-
@proposals = if @form.states.include?("not_answered")
|
53
|
-
@proposals.not_answered.or(@proposals.where(id: @proposals.only_status(@form.states).pluck(:id)))
|
54
|
-
else
|
55
|
-
@proposals.only_status(@form.states)
|
56
|
-
end
|
57
|
-
|
58
|
-
@proposals
|
59
|
-
end
|
60
|
-
|
61
|
-
def proposal_scopes
|
62
|
-
@form.scopes
|
63
|
-
end
|
64
|
-
|
65
|
-
def origin_component
|
66
|
-
@form.origin_component
|
67
|
-
end
|
68
|
-
|
69
|
-
def target_component
|
70
|
-
@form.current_component
|
71
|
-
end
|
72
|
-
|
73
|
-
def proposal_already_copied?(original_proposal, target_component)
|
74
|
-
# Note: we are including also proposals from unpublished components
|
75
|
-
# because otherwise duplicates could be created until the component is
|
76
|
-
# published.
|
77
|
-
original_proposal.linked_resources(:proposals, "copied_from_component", component_published: false).any? do |proposal|
|
78
|
-
proposal.component == target_component
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def proposal_author
|
83
|
-
form.keep_authors ? nil : @form.current_organization
|
84
|
-
end
|
85
|
-
|
86
|
-
def proposal_answer_attributes(original_proposal)
|
87
|
-
return {} unless form.keep_answers
|
88
|
-
|
89
|
-
state = Decidim::Proposals::ProposalState.where(component: target_component, token: original_proposal.state).first
|
90
|
-
|
91
|
-
{
|
92
|
-
answer: original_proposal.answer,
|
93
|
-
answered_at: original_proposal.answered_at,
|
94
|
-
proposal_state: state,
|
95
|
-
state_published_at: original_proposal.state_published_at
|
96
|
-
}
|
34
|
+
ImportProposalsJob.perform_later(form.as_json.merge({
|
35
|
+
"current_user_id" => form.current_user.id,
|
36
|
+
"current_organization_id" => form.current_organization.id,
|
37
|
+
"current_component_id" => form.current_component.id
|
38
|
+
}))
|
97
39
|
end
|
98
40
|
end
|
99
41
|
end
|
@@ -5,6 +5,9 @@ module Decidim
|
|
5
5
|
module Admin
|
6
6
|
# This controller allows admins to answer proposals in a participatory process.
|
7
7
|
class ProposalAnswersController < Admin::ApplicationController
|
8
|
+
include ActionView::Helpers::SanitizeHelper
|
9
|
+
include Decidim::Proposals::Admin::Filterable
|
10
|
+
|
8
11
|
helper_method :proposal
|
9
12
|
|
10
13
|
helper Proposals::ApplicationHelper
|
@@ -44,6 +47,10 @@ module Decidim
|
|
44
47
|
def proposal
|
45
48
|
@proposal ||= Proposal.where(component: current_component).find(params[:id])
|
46
49
|
end
|
50
|
+
|
51
|
+
def collection
|
52
|
+
@collection ||= Proposal.where(component: current_component).not_hidden.published
|
53
|
+
end
|
47
54
|
end
|
48
55
|
end
|
49
56
|
end
|
@@ -16,8 +16,8 @@ module Decidim
|
|
16
16
|
@form = form(Admin::ProposalsImportForm).from_params(params)
|
17
17
|
|
18
18
|
Admin::ImportProposals.call(@form) do
|
19
|
-
on(:ok) do
|
20
|
-
flash[:notice] = I18n.t("proposals_imports.create.success", scope: "decidim.proposals.admin"
|
19
|
+
on(:ok) do
|
20
|
+
flash[:notice] = I18n.t("proposals_imports.create.success", scope: "decidim.proposals.admin")
|
21
21
|
redirect_to EngineRouter.admin_proxy(current_component).root_path
|
22
22
|
end
|
23
23
|
|
@@ -43,13 +43,11 @@ module Decidim
|
|
43
43
|
.order(position: :asc)
|
44
44
|
render "decidim/proposals/proposals/participatory_texts/participatory_text"
|
45
45
|
else
|
46
|
-
@
|
47
|
-
.result
|
48
|
-
.published
|
49
|
-
.not_hidden
|
46
|
+
@proposals = search.result
|
50
47
|
|
51
|
-
@proposals = @
|
52
|
-
@
|
48
|
+
@proposals = reorder(@proposals)
|
49
|
+
@proposals = paginate(@proposals)
|
50
|
+
@proposals = @proposals.includes(:component, :coauthorships, :attachments)
|
53
51
|
|
54
52
|
@voted_proposals = if current_user
|
55
53
|
ProposalVote.where(
|
@@ -59,8 +57,6 @@ module Decidim
|
|
59
57
|
else
|
60
58
|
[]
|
61
59
|
end
|
62
|
-
@proposals = reorder(@proposals)
|
63
|
-
@proposals = paginate(@proposals)
|
64
60
|
end
|
65
61
|
end
|
66
62
|
|
@@ -11,7 +11,7 @@ module Decidim
|
|
11
11
|
def versioned_resource
|
12
12
|
@versioned_resource ||=
|
13
13
|
if params[:proposal_id]
|
14
|
-
present(Proposal.where(component: current_component).find(params[:proposal_id]))
|
14
|
+
present(Proposal.not_hidden.published.where(component: current_component).find(params[:proposal_id]))
|
15
15
|
else
|
16
16
|
CollaborativeDraft.where(component: current_component).find(params[:collaborative_draft_id])
|
17
17
|
end
|
@@ -8,27 +8,16 @@ module Decidim
|
|
8
8
|
include TranslatableAttributes
|
9
9
|
mimic :proposal_answer
|
10
10
|
|
11
|
-
translatable_attribute :answer,
|
12
|
-
translatable_attribute :cost_report,
|
13
|
-
translatable_attribute :execution_period,
|
11
|
+
translatable_attribute :answer, Decidim::Attributes::RichText
|
12
|
+
translatable_attribute :cost_report, Decidim::Attributes::RichText
|
13
|
+
translatable_attribute :execution_period, Decidim::Attributes::RichText
|
14
14
|
attribute :cost, Float
|
15
15
|
attribute :internal_state, String
|
16
16
|
|
17
17
|
validates :internal_state, presence: true, inclusion: { in: :proposal_states }
|
18
|
-
validates :answer, translatable_presence: true, if: ->(form) { form.state == "rejected" }
|
19
|
-
|
20
|
-
with_options if: :costs_required? do
|
21
|
-
validates :cost, numericality: true, presence: true
|
22
|
-
validates :cost_report, translatable_presence: true
|
23
|
-
validates :execution_period, translatable_presence: true
|
24
|
-
end
|
25
18
|
|
26
19
|
alias state internal_state
|
27
20
|
|
28
|
-
def costs_required?
|
29
|
-
costs_enabled? && state == "accepted"
|
30
|
-
end
|
31
|
-
|
32
21
|
def publish_answer?
|
33
22
|
current_component.current_settings.publish_answers_immediately?
|
34
23
|
end
|
@@ -38,10 +27,6 @@ module Decidim
|
|
38
27
|
def proposal_states
|
39
28
|
Decidim::Proposals::ProposalState.where(component: current_component).pluck(:token).map(&:to_s) + ["not_answered"]
|
40
29
|
end
|
41
|
-
|
42
|
-
def costs_enabled?
|
43
|
-
current_component.current_settings.answers_with_costs?
|
44
|
-
end
|
45
30
|
end
|
46
31
|
end
|
47
32
|
end
|
@@ -10,7 +10,7 @@ module Decidim
|
|
10
10
|
translatable_attribute :title, String do |field, _locale|
|
11
11
|
validates field, length: { in: 15..150 }, if: proc { |resource| resource.send(field).present? }
|
12
12
|
end
|
13
|
-
translatable_attribute :body,
|
13
|
+
translatable_attribute :body, Decidim::Attributes::RichText
|
14
14
|
|
15
15
|
validates :title, :body, translatable_presence: true
|
16
16
|
|
@@ -6,6 +6,7 @@ module Decidim
|
|
6
6
|
# A form object to be used when admin users want to import a collection of proposals
|
7
7
|
# from another component.
|
8
8
|
class ProposalsImportForm < Decidim::Form
|
9
|
+
include TranslatableAttributes
|
9
10
|
mimic :proposals_import
|
10
11
|
|
11
12
|
attribute :origin_component_id, Integer
|
@@ -19,15 +20,11 @@ module Decidim
|
|
19
20
|
validates :import_proposals, allow_nil: false, acceptance: true
|
20
21
|
validate :valid_states
|
21
22
|
|
22
|
-
VALID_STATES = %w(accepted not_answered evaluating rejected).freeze
|
23
|
-
|
24
23
|
def states_collection
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
)
|
30
|
-
end
|
24
|
+
@states_collection ||= ProposalState.where(component: current_component) + [ProposalState.new(token: "not_answered",
|
25
|
+
title: I18n.t(
|
26
|
+
:not_answered, scope: "decidim.proposals.answers"
|
27
|
+
))]
|
31
28
|
end
|
32
29
|
|
33
30
|
def states
|
@@ -56,7 +53,7 @@ module Decidim
|
|
56
53
|
|
57
54
|
def valid_states
|
58
55
|
return if states.all? do |state|
|
59
|
-
|
56
|
+
states_collection.pluck(:token).include?(state)
|
60
57
|
end
|
61
58
|
|
62
59
|
errors.add(:states, :invalid)
|
@@ -16,7 +16,10 @@ module Decidim
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def valuator_roles
|
19
|
-
@valuator_roles ||= current_component.participatory_space
|
19
|
+
@valuator_roles ||= current_component.participatory_space
|
20
|
+
.user_roles(:valuator)
|
21
|
+
.order_by_name
|
22
|
+
.where(id: valuator_role_ids)
|
20
23
|
end
|
21
24
|
|
22
25
|
def same_participatory_space
|
@@ -10,7 +10,7 @@ module Decidim
|
|
10
10
|
|
11
11
|
# find the valuators for the current space.
|
12
12
|
def find_valuators_for_select(participatory_space, current_user)
|
13
|
-
valuator_roles = participatory_space.user_roles(:valuator)
|
13
|
+
valuator_roles = participatory_space.user_roles(:valuator).order_by_name
|
14
14
|
valuators = Decidim::User.where(id: valuator_roles.pluck(:decidim_user_id)).to_a
|
15
15
|
|
16
16
|
filtered_valuator_roles = valuator_roles.filter do |role|
|
@@ -9,7 +9,7 @@ module Decidim
|
|
9
9
|
#
|
10
10
|
# geocoded_proposals - A collection of geocoded proposals
|
11
11
|
def proposals_data_for_map(geocoded_proposals)
|
12
|
-
geocoded_proposals.map do |proposal|
|
12
|
+
geocoded_proposals.select(&:geocoded_and_valid?).map do |proposal|
|
13
13
|
proposal_data_for_map(proposal)
|
14
14
|
end
|
15
15
|
end
|
@@ -12,9 +12,7 @@ module Decidim
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def proposal_has_costs?
|
15
|
-
@proposal.cost.present?
|
16
|
-
translated_attribute(@proposal.cost_report).present? &&
|
17
|
-
translated_attribute(@proposal.execution_period).present?
|
15
|
+
@proposal.cost.present?
|
18
16
|
end
|
19
17
|
|
20
18
|
def toggle_view_mode_link(current_mode, target_mode, title, params)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
module Admin
|
6
|
+
class ImportProposalsJob < ApplicationJob
|
7
|
+
queue_as :default
|
8
|
+
|
9
|
+
def perform(form)
|
10
|
+
@form = form
|
11
|
+
ActiveRecord::Base.transaction do
|
12
|
+
proposals.map do |original_proposal|
|
13
|
+
next if proposal_already_copied?(original_proposal, target_component)
|
14
|
+
|
15
|
+
Decidim::Proposals::ProposalBuilder.copy(
|
16
|
+
original_proposal,
|
17
|
+
author: proposal_author,
|
18
|
+
action_user: current_user,
|
19
|
+
extra_attributes: {
|
20
|
+
"component" => target_component
|
21
|
+
}.merge(proposal_answer_attributes(original_proposal))
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
ImportProposalsMailer.notify_success(current_user, origin_component, target_component, proposals.count).deliver_later
|
26
|
+
rescue ActiveRecord::RecordNotFound, NoMethodError
|
27
|
+
ImportProposalsMailer.notify_failure(current_user, origin_component, target_component).deliver_later
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def proposals
|
33
|
+
proposals = Decidim::Proposals::Proposal.where(component: origin_component)
|
34
|
+
proposals = proposals.where(scope: proposal_scopes) unless proposal_scopes.empty?
|
35
|
+
|
36
|
+
if @form["states"].include?("not_answered")
|
37
|
+
proposals.not_answered.or(proposals.where(id: proposals.only_status(@form["states"]).pluck(:id)))
|
38
|
+
else
|
39
|
+
proposals.only_status(@form["states"])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def origin_component
|
44
|
+
@origin_component ||= Decidim::Component.find(@form["origin_component_id"])
|
45
|
+
end
|
46
|
+
|
47
|
+
def target_component
|
48
|
+
@target_component ||= Decidim::Component.find(@form["current_component_id"])
|
49
|
+
end
|
50
|
+
|
51
|
+
def current_user
|
52
|
+
@current_user ||= Decidim::User.find(@form["current_user_id"])
|
53
|
+
end
|
54
|
+
|
55
|
+
def current_organization
|
56
|
+
@current_organization ||= Decidim::Organization.find(@form["current_organization_id"])
|
57
|
+
end
|
58
|
+
|
59
|
+
def proposal_already_copied?(original_proposal, target_component)
|
60
|
+
# Note: we are including also proposals from unpublished components
|
61
|
+
# because otherwise duplicates could be created until the component is
|
62
|
+
# published.
|
63
|
+
original_proposal.linked_resources(:proposals, "copied_from_component", component_published: false).any? do |proposal|
|
64
|
+
proposal.component == target_component
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def proposal_author
|
69
|
+
@form["keep_authors"] ? nil : current_organization
|
70
|
+
end
|
71
|
+
|
72
|
+
def proposal_scopes
|
73
|
+
@form["scopes"] || []
|
74
|
+
end
|
75
|
+
|
76
|
+
def proposal_answer_attributes(original_proposal)
|
77
|
+
return {} unless @form["keep_answers"]
|
78
|
+
|
79
|
+
state = Decidim::Proposals::ProposalState.where(component: target_component, token: original_proposal.proposal_state&.token).first
|
80
|
+
|
81
|
+
{
|
82
|
+
answer: original_proposal.answer,
|
83
|
+
answered_at: original_proposal.answered_at,
|
84
|
+
proposal_state: state,
|
85
|
+
state_published_at: original_proposal.state_published_at
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
module Admin
|
6
|
+
class ImportProposalsMailer < Decidim::ApplicationMailer
|
7
|
+
def notify_success(user, origin_component, target_component, count)
|
8
|
+
@organization = user.organization
|
9
|
+
@origin_component = origin_component
|
10
|
+
@target_component = target_component
|
11
|
+
@count = count
|
12
|
+
|
13
|
+
with_user(user) do
|
14
|
+
mail(to: user.email, subject: t(".subject"))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def notify_failure(user, origin_component, target_component)
|
19
|
+
@organization = user.organization
|
20
|
+
@origin_component = origin_component
|
21
|
+
@target_component = target_component
|
22
|
+
|
23
|
+
with_user(user) do
|
24
|
+
mail(to: user.email, subject: t(".subject"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -45,9 +45,8 @@ module Decidim
|
|
45
45
|
authored_by?(user)
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
ResourceLocatorPresenter.new(self).url
|
48
|
+
def presenter
|
49
|
+
Decidim::Proposals::CollaborativeDraftPresenter.new(self)
|
51
50
|
end
|
52
51
|
|
53
52
|
# Public: Overrides the `reported_attributes` Reportable concern method.
|
@@ -301,11 +301,6 @@ module Decidim
|
|
301
301
|
state == "evaluating"
|
302
302
|
end
|
303
303
|
|
304
|
-
# Public: Overrides the `reported_content_url` Reportable concern method.
|
305
|
-
def reported_content_url
|
306
|
-
ResourceLocatorPresenter.new(self).url
|
307
|
-
end
|
308
|
-
|
309
304
|
# Returns the presenter for this author, to be used in the views.
|
310
305
|
# Required by ResourceRenderer.
|
311
306
|
def presenter
|
@@ -402,12 +397,8 @@ module Decidim
|
|
402
397
|
where(query, value:)
|
403
398
|
end
|
404
399
|
|
405
|
-
def self.ransackable_scopes(
|
406
|
-
|
407
|
-
return base unless auth_object&.admin?
|
408
|
-
|
409
|
-
# Add extra scopes for admins for the admin panel searches
|
410
|
-
base + [:valuator_role_ids_has]
|
400
|
+
def self.ransackable_scopes(_auth_object = nil)
|
401
|
+
[:with_any_origin, :with_any_state, :state_eq, :voted_by, :coauthored_by, :related_to, :with_any_scope, :with_any_category, :valuator_role_ids_has]
|
411
402
|
end
|
412
403
|
|
413
404
|
# Create i18n ransackers for :title and :body.
|
@@ -103,11 +103,7 @@
|
|
103
103
|
}
|
104
104
|
|
105
105
|
&__grid-text-title {
|
106
|
-
@apply flex justify-between flex-row md:flex-col
|
107
|
-
|
108
|
-
.label {
|
109
|
-
@apply md:mt-2;
|
110
|
-
}
|
106
|
+
@apply flex justify-between flex-row md:flex-col items-start lg:flex-row;
|
111
107
|
}
|
112
108
|
|
113
109
|
&__list-metadata {
|
@@ -4,15 +4,16 @@ module Decidim
|
|
4
4
|
module Proposals
|
5
5
|
class Permissions < Decidim::DefaultPermissions
|
6
6
|
def permissions
|
7
|
-
return permission_action unless user
|
8
|
-
|
9
7
|
# Delegate the admin permission checks to the admin permissions class
|
10
8
|
return Decidim::Proposals::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
|
11
9
|
return permission_action if permission_action.scope != :public
|
12
10
|
|
11
|
+
toggle_allow(!proposal.hidden?) if permission_action.subject == :proposal && permission_action.action == :read
|
12
|
+
return permission_action unless user
|
13
|
+
|
13
14
|
case permission_action.subject
|
14
15
|
when :proposal
|
15
|
-
apply_proposal_permissions(permission_action)
|
16
|
+
apply_proposal_permissions(permission_action) unless permission_action.action == :read
|
16
17
|
when :collaborative_draft
|
17
18
|
apply_collaborative_draft_permissions(permission_action)
|
18
19
|
else
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
class CollaborativeDraftDiffRenderer < DiffRenderer
|
6
|
+
private
|
7
|
+
|
8
|
+
def attribute_types
|
9
|
+
{
|
10
|
+
title: :string,
|
11
|
+
body: :string,
|
12
|
+
decidim_category_id: :category,
|
13
|
+
decidim_scope_id: :scope,
|
14
|
+
address: :string,
|
15
|
+
latitude: :string,
|
16
|
+
longitude: :string,
|
17
|
+
decidim_proposals_proposal_state_id: :string
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -21,8 +21,10 @@ module Decidim
|
|
21
21
|
|
22
22
|
# Parses the values before parsing the changeset.
|
23
23
|
def parse_changeset(attribute, values, type, diff)
|
24
|
+
return parse_i18n_changeset(attribute, values, type, diff) if [:i18n, :i18n_html].include?(type)
|
24
25
|
return parse_scope_changeset(attribute, values, type, diff) if type == :scope
|
25
26
|
return parse_state_changeset(attribute, values, type, diff) if type == :state
|
27
|
+
return parse_user_group_changeset(attribute, values, type, diff) if type == :user_group
|
26
28
|
|
27
29
|
values = parse_values(attribute, values)
|
28
30
|
old_value = values[0]
|