decidim-proposals 0.30.0.rc2 → 0.30.0
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 +1 -13
- data/app/cells/decidim/proposals/proposal_g_cell.rb +4 -2
- data/app/cells/decidim/proposals/proposal_l/show.erb +1 -1
- data/app/cells/decidim/proposals/proposal_l_cell.rb +6 -1
- data/app/cells/decidim/proposals/proposal_vote/show.erb +12 -8
- data/app/commands/decidim/proposals/admin/import_proposals.rb +7 -60
- data/app/controllers/decidim/proposals/admin/proposals_imports_controller.rb +2 -2
- data/app/controllers/decidim/proposals/proposals_controller.rb +0 -9
- data/app/controllers/decidim/proposals/versions_controller.rb +1 -1
- data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +0 -15
- data/app/helpers/decidim/proposals/application_helper.rb +0 -13
- data/app/helpers/decidim/proposals/proposal_votes_helper.rb +39 -16
- data/app/helpers/decidim/proposals/proposals_helper.rb +1 -3
- data/app/jobs/decidim/proposals/admin/import_proposals_job.rb +86 -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 +0 -5
- data/app/models/decidim/proposals/proposal_vote.rb +1 -0
- data/app/packs/stylesheets/decidim/proposals/proposals.scss +34 -2
- data/app/services/decidim/proposals/proposal_builder.rb +1 -0
- 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/proposal_votes/update_buttons_and_counters.js.erb +3 -6
- data/app/views/decidim/proposals/proposals/_exit_modal.html.erb +3 -3
- data/app/views/decidim/proposals/proposals/_proposal.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_proposals.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_remaining_votes_notification.html.erb +3 -3
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/_votes_count.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +27 -24
- data/app/views/decidim/proposals/proposals/index.html.erb +1 -6
- data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_votes_count.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/show.html.erb +1 -1
- data/config/locales/ar.yml +3 -3
- data/config/locales/bg.yml +2 -6
- data/config/locales/bs-BA.yml +2 -2
- data/config/locales/ca-IT.yml +1079 -0
- data/config/locales/ca.yml +18 -9
- data/config/locales/cs.yml +10 -6
- data/config/locales/de.yml +15 -6
- data/config/locales/el.yml +4 -4
- data/config/locales/en.yml +14 -5
- data/config/locales/es-MX.yml +17 -8
- data/config/locales/es-PY.yml +17 -8
- data/config/locales/es.yml +18 -9
- data/config/locales/eu.yml +110 -101
- data/config/locales/fi-plain.yml +13 -5
- data/config/locales/fi.yml +12 -4
- data/config/locales/fr-CA.yml +34 -6
- data/config/locales/fr.yml +35 -7
- data/config/locales/ga-IE.yml +2 -1
- data/config/locales/gl.yml +2 -0
- data/config/locales/hu.yml +4 -7
- data/config/locales/id-ID.yml +3 -1
- data/config/locales/is-IS.yml +5 -0
- data/config/locales/it.yml +5 -4
- data/config/locales/ja.yml +81 -6
- data/config/locales/lt.yml +4 -5
- data/config/locales/lv.yml +4 -2
- data/config/locales/nl.yml +6 -5
- data/config/locales/no.yml +2 -2
- data/config/locales/pl.yml +2 -6
- data/config/locales/pt-BR.yml +2 -4
- data/config/locales/pt.yml +4 -2
- data/config/locales/ro-RO.yml +26 -2
- data/config/locales/ru.yml +5 -1
- data/config/locales/sk.yml +2 -2
- data/config/locales/sr-CS.yml +2 -2
- data/config/locales/sv.yml +2 -7
- data/config/locales/tr-TR.yml +4 -2
- data/config/locales/uk.yml +5 -1
- data/config/locales/zh-CN.yml +4 -2
- data/config/locales/zh-TW.yml +4 -4
- data/lib/decidim/proposals/version.rb +1 -1
- data/lib/tasks/proposals/upgrade/decidim_proposals_upgrade_tasks.rake +22 -0
- metadata +25 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81bba03dc800ce062b503bcaad10e09f1b3bc016dd02c647dc08f1e893a64f68
|
4
|
+
data.tar.gz: 4770709bececf57eef406cf4e879f7be42132a81782911c79c3dc8f16d690035
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc1bf3583428e26ca578f26d84a24cb0b5cb36da2938e143d9fbb23d009b8487a8270e1dc40ab3df9e370cd79bf636a974f22c1b238581aa6e770a995bbe6d2c
|
7
|
+
data.tar.gz: 551a0f369ace9f5881edb71d6fc4bc9b4c212e597e5df2cd67b01bbf0300f321db1729ac5fa132d02ebd55763fa18b7247c9522cf22ac8a7ac034738ec958cc9
|
@@ -10,22 +10,10 @@
|
|
10
10
|
<% end %>
|
11
11
|
<% end %>
|
12
12
|
</div>
|
13
|
-
|
14
|
-
<div class="flex items-center justify-between space-x-6">
|
15
|
-
<span class="content-block__span flex-shrink-0">
|
16
|
-
<%= t("decidim.participatory_spaces.highlighted_proposals.last") %>
|
17
|
-
</span>
|
18
|
-
<% if single_component? %>
|
19
|
-
<%= link_to decidim_proposals.new_proposal_path, class: "button button__xs md:button__lg button__secondary" do %>
|
20
|
-
<span class="text-center"><%= t("decidim.proposals.actions.new") %></span>
|
21
|
-
<%= icon "add-line" %>
|
22
|
-
<% end %>
|
23
|
-
<% end %>
|
24
|
-
</div>
|
25
13
|
<div class="flex items-start justify-between">
|
26
14
|
<div class="grow space-y-6">
|
27
15
|
<% proposals_to_render.each do |p| %>
|
28
|
-
<%= card_for p, link_whole_card: true,
|
16
|
+
<%= card_for p, link_whole_card: true, title_tag: :h3, **options.slice(:show_space) %>
|
29
17
|
<% end %>
|
30
18
|
</div>
|
31
19
|
</div>
|
@@ -46,6 +46,7 @@ module Decidim
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
49
50
|
def cache_hash
|
50
51
|
@cache_hash ||= begin
|
51
52
|
hash = []
|
@@ -53,7 +54,7 @@ module Decidim
|
|
53
54
|
hash << self.class.name.demodulize.underscore
|
54
55
|
hash << model.cache_key_with_version
|
55
56
|
hash << model.proposal_votes_count
|
56
|
-
hash << options[:
|
57
|
+
hash << options[:show_voting] ? 0 : 1
|
57
58
|
hash << model.endorsements_count
|
58
59
|
hash << model.comments_count
|
59
60
|
hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
|
@@ -63,10 +64,11 @@ module Decidim
|
|
63
64
|
hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
|
64
65
|
hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
|
65
66
|
hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
|
66
|
-
|
67
|
+
hash << (current_user&.id || 0)
|
67
68
|
hash.join(Decidim.cache_key_separator)
|
68
69
|
end
|
69
70
|
end
|
71
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
70
72
|
|
71
73
|
def classes
|
72
74
|
super.merge(metadata: "card__list-metadata")
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<%= render :content %>
|
5
5
|
<%= render :extra_data if render_extra_data? %>
|
6
6
|
<% end %>
|
7
|
-
<% if has_actions? && options.fetch(:
|
7
|
+
<% if has_actions? && options.fetch(:show_voting, false) == true %>
|
8
8
|
<% if current_settings.votes_hidden? %>
|
9
9
|
<div class="card__proposals-votes-hidden">
|
10
10
|
<%= cell proposal_vote_cell, resource, **options %>
|
@@ -27,6 +27,8 @@ module Decidim
|
|
27
27
|
"decidim/proposals/proposal_vote"
|
28
28
|
end
|
29
29
|
|
30
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
31
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
30
32
|
def cache_hash
|
31
33
|
@cache_hash ||= begin
|
32
34
|
hash = []
|
@@ -34,7 +36,7 @@ module Decidim
|
|
34
36
|
hash << self.class.name.demodulize.underscore
|
35
37
|
hash << model.cache_key_with_version
|
36
38
|
hash << model.proposal_votes_count
|
37
|
-
hash << options[:
|
39
|
+
hash << options[:show_voting] ? 0 : 1
|
38
40
|
hash << model.endorsements_count
|
39
41
|
hash << model.comments_count
|
40
42
|
hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
|
@@ -44,9 +46,12 @@ module Decidim
|
|
44
46
|
hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
|
45
47
|
hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
|
46
48
|
hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
|
49
|
+
hash << (current_user&.id || 0)
|
47
50
|
|
48
51
|
hash.join(Decidim.cache_key_separator)
|
49
52
|
end
|
53
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
54
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
50
55
|
end
|
51
56
|
end
|
52
57
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% if !current_settings.votes_hidden? && current_component.participatory_space.can_participate?(current_user) %>
|
1
|
+
<% if !current_settings.votes_hidden? && (current_component.participatory_space.can_participate?(current_user) || current_user.admin?) %>
|
2
2
|
<% if component_settings.participatory_texts_enabled? && from_proposals_list %>
|
3
3
|
<%= render partial: "decidim/proposals/proposals/participatory_texts/proposal_votes_count", locals: { proposal: resource, from_proposals_list: true } %>
|
4
4
|
<% else %>
|
@@ -24,7 +24,7 @@
|
|
24
24
|
<div id="proposal-<%= resource.id %>-vote-button" class="card__proposals-votes-container">
|
25
25
|
<% if !current_user %>
|
26
26
|
<% if current_settings.votes_blocked? %>
|
27
|
-
<%= action_authorized_button_to :vote, t("decidim.proposals.proposals.vote_button.votes_blocked"), proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , class: button_classes, disabled: true %>
|
27
|
+
<%= action_authorized_button_to :vote, t("decidim.proposals.proposals.vote_button.votes_blocked"), proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , class: button_classes, disabled: true, data: { "proposal-vote-button": true } %>
|
28
28
|
<% else %>
|
29
29
|
<%= action_authorized_button_to :vote, proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , class: button_classes, data: { "proposal-vote-button": true, disable: true, "redirect-url": proposal_path(resource) } do %>
|
30
30
|
<%= t("decidim.proposals.proposals.vote_button.vote") %>
|
@@ -32,7 +32,7 @@
|
|
32
32
|
<% end %>
|
33
33
|
<% end %>
|
34
34
|
<% else %>
|
35
|
-
<% if
|
35
|
+
<% if proposal_voted_by_user?(resource) %>
|
36
36
|
<%= action_authorized_button_to(
|
37
37
|
:vote,
|
38
38
|
proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:),
|
@@ -40,6 +40,7 @@
|
|
40
40
|
method: :delete,
|
41
41
|
remote: true,
|
42
42
|
data: {
|
43
|
+
"proposal-vote-button": true,
|
43
44
|
disable: true,
|
44
45
|
original: t("decidim.proposals.proposals.vote_button.already_voted"),
|
45
46
|
replace: t("decidim.proposals.proposals.vote_button.already_voted_hover"),
|
@@ -55,14 +56,17 @@
|
|
55
56
|
<% end %>
|
56
57
|
<% else %>
|
57
58
|
<% if resource.maximum_votes_reached? && !resource.can_accumulate_votes_beyond_threshold && current_component.participatory_space.can_participate?(current_user) %>
|
58
|
-
<%= content_tag :button, t("decidim.proposals.proposals.vote_button.maximum_votes_reached"), class: button_classes, disabled: true %>
|
59
|
+
<%= content_tag :button, t("decidim.proposals.proposals.vote_button.maximum_votes_reached"), class: button_classes, disabled: true, data: { "proposal-vote-button": true } %>
|
59
60
|
<% else %>
|
60
|
-
<% if vote_limit_enabled? &&
|
61
|
-
<%=
|
61
|
+
<% if vote_limit_enabled? && remaining_votes_count_for_user == 0 %>
|
62
|
+
<%= action_authorized_button_to :vote, proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , remote: true, disabled: true, data: { "proposal-vote-button": true, disable: true, "redirect-url": proposal_path(resource) }, class: button_classes do %>
|
63
|
+
<%= t("decidim.proposals.proposals.vote_button.vote") %>
|
64
|
+
<span class="sr-only"><%= decidim_html_escape(present(resource).title) %></span>
|
65
|
+
<% end %>
|
62
66
|
<% elsif current_settings.votes_blocked? || !current_component.participatory_space.can_participate?(current_user) %>
|
63
|
-
<%= content_tag :button, t("decidim.proposals.proposals.vote_button.votes_blocked"), class: button_classes, disabled: true %>
|
67
|
+
<%= content_tag :button, t("decidim.proposals.proposals.vote_button.votes_blocked"), class: button_classes, disabled: true, data: { "proposal-vote-button": true } %>
|
64
68
|
<% else %>
|
65
|
-
<%= action_authorized_button_to :vote, proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , remote: true, data: {
|
69
|
+
<%= action_authorized_button_to :vote, proposal_proposal_vote_path(proposal_id: resource, from_proposals_list:), resource: , remote: true, data: { "proposal-vote-button": true, "redirect-url": proposal_path(resource) }, class: button_classes do %>
|
66
70
|
<%= t("decidim.proposals.proposals.vote_button.vote") %>
|
67
71
|
<span class="sr-only"><%= decidim_html_escape(present(resource).title) %></span>
|
68
72
|
<% 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,65 +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
|
-
|
51
|
-
@proposals = if @form.states.include?("not_answered")
|
52
|
-
@proposals.not_answered.or(@proposals.where(id: @proposals.only_status(@form.states).pluck(:id)))
|
53
|
-
else
|
54
|
-
@proposals.only_status(@form.states)
|
55
|
-
end
|
56
|
-
|
57
|
-
@proposals
|
58
|
-
end
|
59
|
-
|
60
|
-
def origin_component
|
61
|
-
@form.origin_component
|
62
|
-
end
|
63
|
-
|
64
|
-
def target_component
|
65
|
-
@form.current_component
|
66
|
-
end
|
67
|
-
|
68
|
-
def proposal_already_copied?(original_proposal, target_component)
|
69
|
-
# Note: we are including also proposals from unpublished components
|
70
|
-
# because otherwise duplicates could be created until the component is
|
71
|
-
# published.
|
72
|
-
original_proposal.linked_resources(:proposals, "copied_from_component", component_published: false).any? do |proposal|
|
73
|
-
proposal.component == target_component
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def proposal_author
|
78
|
-
form.keep_authors ? nil : @form.current_organization
|
79
|
-
end
|
80
|
-
|
81
|
-
def proposal_answer_attributes(original_proposal)
|
82
|
-
return {} unless form.keep_answers
|
83
|
-
|
84
|
-
state = Decidim::Proposals::ProposalState.where(component: target_component, token: original_proposal.state).first
|
85
|
-
|
86
|
-
{
|
87
|
-
answer: original_proposal.answer,
|
88
|
-
answered_at: original_proposal.answered_at,
|
89
|
-
proposal_state: state,
|
90
|
-
state_published_at: original_proposal.state_published_at
|
91
|
-
}
|
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
|
+
}))
|
92
39
|
end
|
93
40
|
end
|
94
41
|
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
|
|
@@ -48,15 +48,6 @@ module Decidim
|
|
48
48
|
@proposals = reorder(@proposals)
|
49
49
|
@proposals = paginate(@proposals)
|
50
50
|
@proposals = @proposals.includes(:component, :coauthorships, :attachments)
|
51
|
-
|
52
|
-
@voted_proposals = if current_user
|
53
|
-
ProposalVote.where(
|
54
|
-
author: current_user,
|
55
|
-
proposal: @proposals.pluck(:id)
|
56
|
-
).pluck(:decidim_proposal_id)
|
57
|
-
else
|
58
|
-
[]
|
59
|
-
end
|
60
51
|
end
|
61
52
|
end
|
62
53
|
|
@@ -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
|
@@ -16,20 +16,9 @@ module Decidim
|
|
16
16
|
attribute :internal_state, String
|
17
17
|
|
18
18
|
validates :internal_state, presence: true, inclusion: { in: :proposal_states }
|
19
|
-
validates :answer, translatable_presence: true, if: ->(form) { form.state == "rejected" }
|
20
|
-
|
21
|
-
with_options if: :costs_required? do
|
22
|
-
validates :cost, numericality: true, presence: true
|
23
|
-
validates :cost_report, translatable_presence: true
|
24
|
-
validates :execution_period, translatable_presence: true
|
25
|
-
end
|
26
19
|
|
27
20
|
alias state internal_state
|
28
21
|
|
29
|
-
def costs_required?
|
30
|
-
costs_enabled? && state == "accepted"
|
31
|
-
end
|
32
|
-
|
33
22
|
def publish_answer?
|
34
23
|
current_component.current_settings.publish_answers_immediately?
|
35
24
|
end
|
@@ -39,10 +28,6 @@ module Decidim
|
|
39
28
|
def proposal_states
|
40
29
|
Decidim::Proposals::ProposalState.where(component: current_component).pluck(:token).map(&:to_s) + ["not_answered"]
|
41
30
|
end
|
42
|
-
|
43
|
-
def costs_enabled?
|
44
|
-
current_component.current_settings.answers_with_costs?
|
45
|
-
end
|
46
31
|
end
|
47
32
|
end
|
48
33
|
end
|
@@ -17,8 +17,6 @@ module Decidim
|
|
17
17
|
include Decidim::RichTextEditorHelper
|
18
18
|
include Decidim::CheckBoxesTreeHelper
|
19
19
|
|
20
|
-
delegate :minimum_votes_per_user, to: :component_settings
|
21
|
-
|
22
20
|
# Public: The state of a proposal in a way a human can understand.
|
23
21
|
#
|
24
22
|
# state - The String state of the proposal.
|
@@ -85,10 +83,6 @@ module Decidim
|
|
85
83
|
proposal_limit.present?
|
86
84
|
end
|
87
85
|
|
88
|
-
def minimum_votes_per_user_enabled?
|
89
|
-
minimum_votes_per_user.positive?
|
90
|
-
end
|
91
|
-
|
92
86
|
def not_from_collaborative_draft(proposal)
|
93
87
|
proposal.linked_resources(:proposals, "created_from_collaborative_draft").empty?
|
94
88
|
end
|
@@ -138,13 +132,6 @@ module Decidim
|
|
138
132
|
component_settings.proposal_limit
|
139
133
|
end
|
140
134
|
|
141
|
-
def votes_given
|
142
|
-
@votes_given ||= ProposalVote.where(
|
143
|
-
proposal: Proposal.where(component: current_component),
|
144
|
-
author: current_user
|
145
|
-
).count
|
146
|
-
end
|
147
|
-
|
148
135
|
def layout_item_classes
|
149
136
|
if show_voting_rules?
|
150
137
|
"layout-item lg:pt-4"
|
@@ -4,14 +4,7 @@ module Decidim
|
|
4
4
|
module Proposals
|
5
5
|
# Simple helpers to handle markup variations for proposal votes partials
|
6
6
|
module ProposalVotesHelper
|
7
|
-
|
8
|
-
#
|
9
|
-
# Returns an Integer if set, nil otherwise.
|
10
|
-
def vote_limit
|
11
|
-
return nil if component_settings.vote_limit&.zero?
|
12
|
-
|
13
|
-
component_settings.vote_limit
|
14
|
-
end
|
7
|
+
delegate :minimum_votes_per_user, to: :component_settings
|
15
8
|
|
16
9
|
# Check if the vote limit is enabled for the current component
|
17
10
|
#
|
@@ -20,6 +13,10 @@ module Decidim
|
|
20
13
|
vote_limit.present?
|
21
14
|
end
|
22
15
|
|
16
|
+
def minimum_votes_per_user_enabled?
|
17
|
+
minimum_votes_per_user.positive?
|
18
|
+
end
|
19
|
+
|
23
20
|
# Public: Checks if threshold per proposal are set.
|
24
21
|
#
|
25
22
|
# Returns true if set, false otherwise.
|
@@ -64,17 +61,21 @@ module Decidim
|
|
64
61
|
current_user && votes_enabled? && vote_limit_enabled? && !votes_blocked?
|
65
62
|
end
|
66
63
|
|
64
|
+
def proposal_voted_by_user?(proposal)
|
65
|
+
return false if current_user.blank? || proposal.blank?
|
66
|
+
|
67
|
+
all_voted_proposals_by_user.include?(proposal.id)
|
68
|
+
end
|
69
|
+
|
67
70
|
# Return the remaining votes for a user if the current component has a vote limit
|
68
71
|
#
|
69
72
|
# user - A User object
|
70
73
|
#
|
71
74
|
# Returns a number with the remaining votes for that user
|
72
|
-
def
|
75
|
+
def remaining_votes_count_for_user
|
73
76
|
return 0 unless vote_limit_enabled?
|
74
77
|
|
75
|
-
|
76
|
-
votes_count = ProposalVote.where(author: user, proposal: proposals).size
|
77
|
-
component_settings.vote_limit - votes_count
|
78
|
+
component_settings.vote_limit - votes_given
|
78
79
|
end
|
79
80
|
|
80
81
|
# Return the remaining minimum votes for a user if the current component has a vote limit
|
@@ -82,12 +83,34 @@ module Decidim
|
|
82
83
|
# user - A User object
|
83
84
|
#
|
84
85
|
# Returns a number with the remaining minimum votes for that user
|
85
|
-
def
|
86
|
-
return 0 unless
|
86
|
+
def remaining_minimum_votes_count_for_user
|
87
|
+
return 0 unless minimum_votes_per_user_enabled?
|
88
|
+
|
89
|
+
component_settings.minimum_votes_per_user - votes_given
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def votes_given
|
95
|
+
@votes_given ||= all_voted_proposals_by_user.length
|
96
|
+
end
|
97
|
+
|
98
|
+
# Gets the vote limit for each user, if set.
|
99
|
+
#
|
100
|
+
# Returns an Integer if set, nil otherwise.
|
101
|
+
def vote_limit
|
102
|
+
return nil if component_settings.vote_limit&.zero?
|
103
|
+
|
104
|
+
component_settings.vote_limit
|
105
|
+
end
|
87
106
|
|
88
|
-
|
107
|
+
def all_voted_proposals_by_user
|
108
|
+
return [] if current_user.blank?
|
89
109
|
|
90
|
-
|
110
|
+
@all_voted_proposals ||= ProposalVote.where(
|
111
|
+
proposal: Proposal.where(component: current_component),
|
112
|
+
author: current_user
|
113
|
+
).pluck(:decidim_proposal_id)
|
91
114
|
end
|
92
115
|
end
|
93
116
|
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,86 @@
|
|
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
|
+
|
35
|
+
if @form["states"].include?("not_answered")
|
36
|
+
proposals.not_answered.or(proposals.where(id: proposals.only_status(@form["states"]).pluck(:id)))
|
37
|
+
else
|
38
|
+
proposals.only_status(@form["states"])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def origin_component
|
43
|
+
@origin_component ||= Decidim::Component.find(@form["origin_component_id"])
|
44
|
+
end
|
45
|
+
|
46
|
+
def target_component
|
47
|
+
@target_component ||= Decidim::Component.find(@form["current_component_id"])
|
48
|
+
end
|
49
|
+
|
50
|
+
def current_user
|
51
|
+
@current_user ||= Decidim::User.find(@form["current_user_id"])
|
52
|
+
end
|
53
|
+
|
54
|
+
def current_organization
|
55
|
+
@current_organization ||= Decidim::Organization.find(@form["current_organization_id"])
|
56
|
+
end
|
57
|
+
|
58
|
+
def proposal_already_copied?(original_proposal, target_component)
|
59
|
+
# Note: we are including also proposals from unpublished components
|
60
|
+
# because otherwise duplicates could be created until the component is
|
61
|
+
# published.
|
62
|
+
original_proposal.linked_resources(:proposals, "copied_from_component", component_published: false).any? do |proposal|
|
63
|
+
proposal.component == target_component
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def proposal_author
|
68
|
+
@form["keep_authors"] ? nil : current_organization
|
69
|
+
end
|
70
|
+
|
71
|
+
def proposal_answer_attributes(original_proposal)
|
72
|
+
return {} unless @form["keep_answers"]
|
73
|
+
|
74
|
+
state = Decidim::Proposals::ProposalState.where(component: target_component, token: original_proposal.proposal_state&.token).first
|
75
|
+
|
76
|
+
{
|
77
|
+
answer: original_proposal.answer,
|
78
|
+
answered_at: original_proposal.answered_at,
|
79
|
+
proposal_state: state,
|
80
|
+
state_published_at: original_proposal.state_published_at
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
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
|
@@ -46,9 +46,8 @@ module Decidim
|
|
46
46
|
authored_by?(user)
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
ResourceLocatorPresenter.new(self).url
|
49
|
+
def presenter
|
50
|
+
Decidim::Proposals::CollaborativeDraftPresenter.new(self)
|
52
51
|
end
|
53
52
|
|
54
53
|
# Public: Overrides the `reported_attributes` Reportable concern method.
|
@@ -303,11 +303,6 @@ module Decidim
|
|
303
303
|
state == "evaluating"
|
304
304
|
end
|
305
305
|
|
306
|
-
# Public: Overrides the `reported_content_url` Reportable concern method.
|
307
|
-
def reported_content_url
|
308
|
-
ResourceLocatorPresenter.new(self).url
|
309
|
-
end
|
310
|
-
|
311
306
|
# Returns the presenter for this author, to be used in the views.
|
312
307
|
# Required by ResourceRenderer.
|
313
308
|
def presenter
|