decidim-proposals 0.29.3 → 0.30.0.rc1
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 +13 -1
- data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
- data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +1 -1
- data/app/cells/decidim/proposals/proposal_g/show.erb +13 -0
- data/app/cells/decidim/proposals/proposal_g_cell.rb +13 -0
- data/app/cells/decidim/proposals/proposal_history_cell.rb +107 -0
- data/app/cells/decidim/proposals/proposal_l/show.erb +37 -0
- data/app/cells/decidim/proposals/proposal_l_cell.rb +9 -0
- data/app/cells/decidim/proposals/proposal_metadata_cell.rb +2 -2
- data/app/cells/decidim/proposals/proposal_vote/show.erb +75 -0
- data/app/cells/decidim/proposals/proposal_vote_cell.rb +43 -0
- data/app/commands/decidim/proposals/accept_coauthorship.rb +62 -0
- data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +14 -0
- data/app/commands/decidim/proposals/admin/create_proposal.rb +6 -14
- data/app/commands/decidim/proposals/admin/create_proposal_note.rb +20 -11
- data/app/commands/decidim/proposals/admin/import_proposals.rb +60 -7
- data/app/commands/decidim/proposals/admin/merge_proposals.rb +2 -2
- data/app/commands/decidim/proposals/admin/proposal_notes_methods.rb +48 -0
- data/app/commands/decidim/proposals/admin/reply_proposal_note.rb +92 -0
- data/app/commands/decidim/proposals/admin/split_proposals.rb +2 -2
- data/app/commands/decidim/proposals/admin/update_proposal.rb +10 -16
- data/app/commands/decidim/proposals/admin/update_proposal_taxonomies.rb +34 -0
- data/app/commands/decidim/proposals/cancel_coauthorship.rb +32 -0
- data/app/commands/decidim/proposals/create_collaborative_draft.rb +1 -2
- data/app/commands/decidim/proposals/create_proposal.rb +1 -2
- data/app/commands/decidim/proposals/invite_coauthor.rb +45 -0
- data/app/commands/decidim/proposals/publish_collaborative_draft.rb +1 -2
- data/app/commands/decidim/proposals/reject_coauthorship.rb +54 -0
- data/app/commands/decidim/proposals/update_collaborative_draft.rb +1 -2
- data/app/commands/decidim/proposals/update_proposal.rb +1 -2
- data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +5 -1
- data/app/controllers/concerns/decidim/proposals/admin/needs_interpolations.rb +40 -0
- data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +46 -5
- data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +18 -0
- data/app/controllers/decidim/proposals/admin/proposals_controller.rb +41 -85
- data/app/controllers/decidim/proposals/admin/proposals_imports_controller.rb +2 -2
- data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +2 -4
- data/app/controllers/decidim/proposals/invite_coauthors_controller.rb +87 -0
- data/app/controllers/decidim/proposals/proposals_controller.rb +7 -32
- data/app/controllers/decidim/proposals/versions_controller.rb +1 -1
- data/app/events/decidim/proposals/accepted_coauthorship_event.rb +8 -0
- data/app/events/decidim/proposals/admin/proposal_assigned_to_valuator_event.rb +27 -0
- data/app/events/decidim/proposals/admin/proposal_note_created_event.rb +5 -0
- data/app/events/decidim/proposals/coauthor_accepted_invite_event.rb +49 -0
- data/app/events/decidim/proposals/coauthor_invited_event.rb +45 -0
- data/app/events/decidim/proposals/coauthor_rejected_invite_event.rb +8 -0
- data/app/events/decidim/proposals/rejected_coauthorship_event.rb +8 -0
- data/app/events/decidim/proposals/update_proposal_taxonomies_event.rb +9 -0
- data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +16 -0
- data/app/forms/decidim/proposals/admin/proposal_base_form.rb +3 -31
- data/app/forms/decidim/proposals/admin/proposal_form.rb +11 -6
- data/app/forms/decidim/proposals/admin/proposals_import_form.rb +0 -5
- data/app/forms/decidim/proposals/collaborative_draft_form.rb +0 -8
- data/app/forms/decidim/proposals/proposal_form.rb +5 -32
- data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +25 -0
- data/app/helpers/decidim/proposals/admin/proposals_helper.rb +0 -1
- data/app/helpers/decidim/proposals/application_helper.rb +23 -15
- data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +7 -7
- data/app/helpers/decidim/proposals/map_helper.rb +0 -18
- data/app/helpers/decidim/proposals/proposal_votes_helper.rb +15 -2
- data/app/helpers/decidim/proposals/proposals_helper.rb +3 -1
- data/app/jobs/decidim/proposals/admin/proposal_answer_job.rb +20 -0
- data/app/models/decidim/proposals/collaborative_draft.rb +13 -3
- data/app/models/decidim/proposals/proposal.rb +71 -5
- data/app/models/decidim/proposals/proposal_note.rb +11 -0
- data/app/models/decidim/proposals/proposal_state.rb +1 -1
- data/app/packs/entrypoints/decidim_proposals.js +1 -0
- data/app/packs/entrypoints/decidim_proposals_geocoding.js +2 -0
- data/app/packs/src/decidim/proposals/admin/proposals.js +16 -1
- data/app/packs/src/decidim/proposals/exit_handler.js +73 -0
- data/app/packs/stylesheets/decidim/proposals/proposals.scss +248 -3
- data/app/permissions/decidim/proposals/admin/permissions.rb +2 -5
- data/app/permissions/decidim/proposals/permissions.rb +42 -0
- data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
- data/app/presenters/decidim/proposals/proposal_presenter.rb +1 -1
- data/app/queries/decidim/proposals/filtered_proposals.rb +2 -2
- data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +2 -2
- data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +10 -10
- data/app/queries/decidim/proposals/metrics/proposal_followers_metric_measure.rb +4 -4
- data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +6 -6
- data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +6 -6
- data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +6 -6
- data/app/services/decidim/proposals/proposal_builder.rb +1 -2
- data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +3 -3
- data/app/views/decidim/proposals/admin/proposal_notes/_proposal_note.html.erb +28 -0
- data/app/views/decidim/proposals/admin/proposal_notes/_proposal_note_reply.html.erb +9 -0
- data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +4 -28
- data/app/views/decidim/proposals/admin/proposal_states/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/_actions.html.erb +21 -0
- data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +3 -2
- data/app/views/decidim/proposals/admin/proposals/_form.html.erb +17 -24
- data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +12 -28
- data/app/views/decidim/proposals/admin/proposals/_proposals-thead.html.erb +45 -0
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_apply_answer_template.html.erb +22 -0
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +15 -11
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_taxonomy_change.html.erb +23 -0
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +17 -48
- data/app/views/decidim/proposals/admin/proposals/manage_trash.html.erb +18 -0
- data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/show.html.erb +14 -26
- data/app/views/decidim/proposals/admin/proposals/update_attribute.js.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals_imports/new.html.erb +0 -3
- data/app/views/decidim/proposals/collaborative_drafts/_collaborative_actions.html.erb +9 -0
- data/app/views/decidim/proposals/collaborative_drafts/_collaborative_draft_aside.html.erb +0 -15
- data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +4 -6
- data/app/views/decidim/proposals/collaborative_drafts/index.html.erb +6 -2
- data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +27 -11
- data/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +29 -9
- data/app/views/decidim/proposals/proposals/_actions.html.erb +4 -7
- data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +17 -22
- data/app/views/decidim/proposals/proposals/_exit_modal.html.erb +17 -0
- data/app/views/decidim/proposals/proposals/_notification_alert_box.html.erb +1 -0
- data/app/views/decidim/proposals/proposals/_proposal_actions.html.erb +19 -0
- data/app/views/decidim/proposals/proposals/_proposal_aside.html.erb +9 -32
- data/app/views/decidim/proposals/proposals/_proposal_voting_rules.html.erb +33 -0
- data/app/views/decidim/proposals/proposals/_proposals.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/_remaining_votes_notification.html.erb +12 -0
- data/app/views/decidim/proposals/proposals/_update_proposal_voting_rules.html.erb +6 -0
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +12 -8
- data/app/views/decidim/proposals/proposals/_votes_count.html.erb +2 -1
- data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +1 -7
- data/app/views/decidim/proposals/proposals/index.html.erb +3 -18
- data/app/views/decidim/proposals/proposals/index.js.erb +1 -1
- data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +3 -1
- data/app/views/decidim/proposals/proposals/show.html.erb +35 -15
- data/config/locales/ar.yml +15 -73
- data/config/locales/bg.yml +12 -89
- data/config/locales/bs-BA.yml +2 -13
- data/config/locales/ca.yml +209 -84
- data/config/locales/cs.yml +210 -81
- data/config/locales/de.yml +213 -89
- data/config/locales/el.yml +12 -84
- data/config/locales/en.yml +206 -81
- data/config/locales/es-MX.yml +213 -88
- data/config/locales/es-PY.yml +213 -88
- data/config/locales/es.yml +214 -89
- data/config/locales/eu.yml +288 -164
- data/config/locales/fi-plain.yml +213 -87
- data/config/locales/fi.yml +213 -87
- data/config/locales/fr-CA.yml +116 -97
- data/config/locales/fr.yml +116 -97
- data/config/locales/ga-IE.yml +1 -21
- data/config/locales/gl.yml +8 -45
- data/config/locales/hu.yml +12 -68
- data/config/locales/id-ID.yml +9 -43
- data/config/locales/is-IS.yml +0 -19
- data/config/locales/it.yml +11 -77
- data/config/locales/ja.yml +165 -106
- data/config/locales/lt.yml +13 -85
- data/config/locales/lv.yml +10 -52
- data/config/locales/nl.yml +10 -59
- data/config/locales/no.yml +10 -44
- data/config/locales/pl.yml +11 -88
- data/config/locales/pt-BR.yml +9 -74
- data/config/locales/pt.yml +10 -56
- data/config/locales/ro-RO.yml +12 -72
- data/config/locales/ru.yml +0 -23
- data/config/locales/sk.yml +10 -52
- data/config/locales/sr-CS.yml +2 -14
- data/config/locales/sv.yml +127 -86
- data/config/locales/tr-TR.yml +10 -53
- data/config/locales/uk.yml +0 -23
- data/config/locales/zh-CN.yml +10 -53
- data/config/locales/zh-TW.yml +12 -86
- data/db/migrate/20171220084719_add_published_at_to_proposals.rb +1 -1
- data/db/migrate/20181016132225_add_organization_as_author.rb +1 -1
- data/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb +1 -1
- data/db/migrate/20200827154156_add_commentable_counter_cache_to_proposals.rb +3 -3
- data/db/migrate/20210310102839_add_followable_counter_cache_to_proposals.rb +1 -1
- data/db/migrate/20240110203504_create_default_proposal_states.rb +1 -1
- data/db/migrate/20240404202756_add_valuation_assignments_count_to_decidim_proposals_proposals.rb +1 -1
- data/db/migrate/20240617091140_add_email_on_assigned_proposals_to_users.rb +7 -0
- data/db/migrate/20240617170052_add_parent_relation_to_decidim_proposal_notes.rb +7 -0
- data/db/migrate/20240828103755_add_deleted_at_to_decidim_proposals_proposals.rb +8 -0
- data/decidim-proposals.gemspec +1 -1
- data/lib/decidim/api/functions/proposal_finder_helper.rb +12 -0
- data/lib/decidim/api/functions/proposal_list_helper.rb +12 -0
- data/lib/decidim/api/proposal_type.rb +17 -25
- data/lib/decidim/api/proposals_type.rb +4 -19
- data/lib/decidim/proposals/admin_engine.rb +12 -3
- data/lib/decidim/proposals/admin_filter.rb +3 -6
- data/lib/decidim/proposals/component.rb +4 -5
- data/lib/decidim/proposals/download_your_data_proposal_serializer.rb +15 -0
- data/lib/decidim/proposals/engine.rb +5 -0
- data/lib/decidim/proposals/import/proposal_creator.rb +4 -4
- data/lib/decidim/proposals/proposal_serializer.rb +12 -29
- data/lib/decidim/proposals/seeds.rb +21 -17
- data/lib/decidim/proposals/test/factories.rb +2 -1
- data/lib/decidim/proposals/version.rb +1 -1
- data/lib/decidim/proposals.rb +4 -0
- data/lib/tasks/proposals/upgrade/decidim_proposals_upgrade_tasks.rake +0 -22
- metadata +65 -34
- data/app/commands/decidim/proposals/admin/update_proposal_category.rb +0 -70
- data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +0 -75
- data/app/events/decidim/proposals/admin/update_proposal_category_event.rb +0 -11
- data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +0 -11
- data/app/jobs/decidim/proposals/admin/import_proposals_job.rb +0 -91
- data/app/mailers/decidim/proposals/admin/import_proposals_mailer.rb +0 -30
- data/app/views/decidim/proposals/admin/import_proposals_mailer/notify_failure.html.erb +0 -1
- data/app/views/decidim/proposals/admin/import_proposals_mailer/notify_success.html.erb +0 -2
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +0 -15
- data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +0 -21
- data/app/views/decidim/proposals/collaborative_drafts/_actions.html.erb +0 -7
- data/config/locales/ca-IT.yml +0 -945
@@ -6,6 +6,7 @@ module Decidim
|
|
6
6
|
# A form object to be used when admin users want to answer a proposal.
|
7
7
|
class ProposalAnswerForm < Decidim::Form
|
8
8
|
include TranslatableAttributes
|
9
|
+
|
9
10
|
mimic :proposal_answer
|
10
11
|
|
11
12
|
translatable_attribute :answer, Decidim::Attributes::RichText
|
@@ -15,9 +16,20 @@ module Decidim
|
|
15
16
|
attribute :internal_state, String
|
16
17
|
|
17
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
|
18
26
|
|
19
27
|
alias state internal_state
|
20
28
|
|
29
|
+
def costs_required?
|
30
|
+
costs_enabled? && state == "accepted"
|
31
|
+
end
|
32
|
+
|
21
33
|
def publish_answer?
|
22
34
|
current_component.current_settings.publish_answers_immediately?
|
23
35
|
end
|
@@ -27,6 +39,10 @@ module Decidim
|
|
27
39
|
def proposal_states
|
28
40
|
Decidim::Proposals::ProposalState.where(component: current_component).pluck(:token).map(&:to_s) + ["not_answered"]
|
29
41
|
end
|
42
|
+
|
43
|
+
def costs_enabled?
|
44
|
+
current_component.current_settings.answers_with_costs?
|
45
|
+
end
|
30
46
|
end
|
31
47
|
end
|
32
48
|
end
|
@@ -8,14 +8,13 @@ module Decidim
|
|
8
8
|
include Decidim::TranslatableAttributes
|
9
9
|
include Decidim::AttachmentAttributes
|
10
10
|
include Decidim::ApplicationHelper
|
11
|
+
include Decidim::HasTaxonomyFormAttributes
|
11
12
|
|
12
13
|
mimic :proposal
|
13
14
|
|
14
15
|
attribute :address, String
|
15
16
|
attribute :latitude, Float
|
16
17
|
attribute :longitude, Float
|
17
|
-
attribute :category_id, Integer
|
18
|
-
attribute :scope_id, Integer
|
19
18
|
attribute :attachment, AttachmentForm
|
20
19
|
attribute :position, Integer
|
21
20
|
attribute :created_in_meeting, Boolean
|
@@ -25,46 +24,19 @@ module Decidim
|
|
25
24
|
attachments_attribute :photos
|
26
25
|
|
27
26
|
validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? }
|
28
|
-
validates :category, presence: true, if: ->(form) { form.category_id.present? }
|
29
|
-
validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
|
30
|
-
validates :scope_id, scope_belongs_to_component: true, if: ->(form) { form.scope_id.present? }
|
31
27
|
validates :meeting_as_author, presence: true, if: ->(form) { form.created_in_meeting? }
|
32
28
|
|
33
29
|
validate :notify_missing_attachment_if_errored
|
34
30
|
|
35
|
-
delegate :categories, to: :current_component
|
36
|
-
|
37
31
|
def map_model(model)
|
38
32
|
body = translated_attribute(model.body)
|
39
33
|
@suggested_hashtags = Decidim::ContentRenderers::HashtagRenderer.new(body).extra_hashtags.map(&:name).map(&:downcase)
|
40
|
-
|
41
|
-
return unless model.categorization
|
42
|
-
|
43
|
-
self.category_id = model.categorization.decidim_category_id
|
44
|
-
self.scope_id = model.decidim_scope_id
|
45
34
|
end
|
46
35
|
|
47
36
|
alias component current_component
|
48
37
|
|
49
|
-
|
50
|
-
|
51
|
-
# Returns a Decidim::Category
|
52
|
-
def category
|
53
|
-
@category ||= categories.find_by(id: category_id)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
|
57
|
-
#
|
58
|
-
# Returns a Decidim::Scope
|
59
|
-
def scope
|
60
|
-
@scope ||= @attributes["scope_id"].value ? current_component.scopes.find_by(id: @attributes["scope_id"].value) : current_component.scope
|
61
|
-
end
|
62
|
-
|
63
|
-
# Scope identifier
|
64
|
-
#
|
65
|
-
# Returns the scope identifier related to the proposal
|
66
|
-
def scope_id
|
67
|
-
super || scope&.id
|
38
|
+
def participatory_space_manifest
|
39
|
+
@participatory_space_manifest ||= current_component.participatory_space.manifest.name
|
68
40
|
end
|
69
41
|
|
70
42
|
def geocoding_enabled?
|
@@ -6,27 +6,32 @@ module Decidim
|
|
6
6
|
# A form object to be used when admin users want to create a proposal.
|
7
7
|
class ProposalForm < Decidim::Proposals::Admin::ProposalBaseForm
|
8
8
|
include Decidim::HasUploadValidations
|
9
|
+
include Decidim::AttachmentAttributes
|
9
10
|
|
10
11
|
translatable_attribute :title, String do |field, _locale|
|
11
12
|
validates field, length: { in: 15..150 }, if: proc { |resource| resource.send(field).present? }
|
12
13
|
end
|
13
14
|
translatable_attribute :body, Decidim::Attributes::RichText
|
15
|
+
attribute :attachment, AttachmentForm
|
16
|
+
|
17
|
+
attachments_attribute :documents
|
14
18
|
|
15
19
|
validates :title, :body, translatable_presence: true
|
20
|
+
validates :title, :body, translated_etiquette: true
|
16
21
|
|
17
22
|
validate :notify_missing_attachment_if_errored
|
18
23
|
|
19
24
|
def map_model(model)
|
20
|
-
super
|
25
|
+
super
|
21
26
|
presenter = ProposalPresenter.new(model)
|
22
27
|
|
23
28
|
self.title = presenter.title(all_locales: title.is_a?(Hash))
|
24
29
|
self.body = presenter.editor_body(all_locales: body.is_a?(Hash))
|
25
|
-
self.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
self.documents = model.attachments
|
31
|
+
end
|
32
|
+
|
33
|
+
def notify_missing_attachment_if_errored
|
34
|
+
errors.add(:add_documents, :needs_to_be_reattached) if errors.any? && add_documents.present?
|
30
35
|
end
|
31
36
|
end
|
32
37
|
end
|
@@ -14,7 +14,6 @@ module Decidim
|
|
14
14
|
attribute :keep_answers, Boolean
|
15
15
|
attribute :keep_authors, Boolean
|
16
16
|
attribute :states, Array
|
17
|
-
attribute :scope_ids, Array
|
18
17
|
|
19
18
|
validates :origin_component_id, :origin_component, :states, :current_component, presence: true
|
20
19
|
validates :import_proposals, allow_nil: false, acceptance: true
|
@@ -31,10 +30,6 @@ module Decidim
|
|
31
30
|
super.compact_blank
|
32
31
|
end
|
33
32
|
|
34
|
-
def scopes
|
35
|
-
Decidim::Scope.where(organization: current_organization, id: scope_ids)
|
36
|
-
end
|
37
|
-
|
38
33
|
def origin_component
|
39
34
|
@origin_component ||= origin_components.find_by(id: origin_component_id)
|
40
35
|
end
|
@@ -4,14 +4,6 @@ module Decidim
|
|
4
4
|
module Proposals
|
5
5
|
# A form object to be used when public users want to create a Collaborative Draft.
|
6
6
|
class CollaborativeDraftForm < Decidim::Proposals::ProposalForm
|
7
|
-
def map_model(model)
|
8
|
-
super
|
9
|
-
|
10
|
-
return unless model.categorization
|
11
|
-
|
12
|
-
self.category_id = model.categorization.decidim_category_id
|
13
|
-
end
|
14
|
-
|
15
7
|
def user_group
|
16
8
|
@user_group ||= Decidim::UserGroup.find user_group_id if user_group_id.present?
|
17
9
|
end
|
@@ -7,6 +7,7 @@ module Decidim
|
|
7
7
|
include Decidim::TranslatableAttributes
|
8
8
|
include Decidim::AttachmentAttributes
|
9
9
|
include Decidim::HasUploadValidations
|
10
|
+
include Decidim::HasTaxonomyFormAttributes
|
10
11
|
|
11
12
|
mimic :proposal
|
12
13
|
|
@@ -17,29 +18,24 @@ module Decidim
|
|
17
18
|
attribute :address, String
|
18
19
|
attribute :latitude, Float
|
19
20
|
attribute :longitude, Float
|
20
|
-
attribute :category_id, Integer
|
21
|
-
attribute :scope_id, Integer
|
22
21
|
attribute :attachment, AttachmentForm
|
23
22
|
attribute :suggested_hashtags, Array[String]
|
24
23
|
|
25
24
|
attachments_attribute :documents
|
26
25
|
|
27
|
-
validates :title, :body, presence: true
|
26
|
+
validates :title, :body, presence: true
|
27
|
+
validates :title, :body, etiquette: true
|
28
28
|
validates :title, length: { in: 15..150 }
|
29
29
|
validates :body, proposal_length: {
|
30
30
|
minimum: 15,
|
31
31
|
maximum: ->(record) { record.component.settings.proposal_length }
|
32
32
|
}
|
33
33
|
validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? }
|
34
|
-
validates :category, presence: true, if: ->(form) { form.category_id.present? }
|
35
|
-
validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
|
36
|
-
validates :scope_id, scope_belongs_to_component: true, if: ->(form) { form.scope_id.present? }
|
37
34
|
|
38
35
|
validate :body_is_not_bare_template
|
39
36
|
validate :notify_missing_attachment_if_errored
|
40
37
|
|
41
38
|
alias component current_component
|
42
|
-
delegate :categories, to: :current_component
|
43
39
|
|
44
40
|
def map_model(model)
|
45
41
|
self.title = translated_attribute(model.title)
|
@@ -50,34 +46,11 @@ module Decidim
|
|
50
46
|
self.body = presenter.editor_body(all_locales: body.is_a?(Hash))
|
51
47
|
|
52
48
|
self.user_group_id = model.user_groups.first&.id
|
53
|
-
self.category_id = model.categorization.decidim_category_id if model.categorization
|
54
|
-
|
55
|
-
# The scope attribute is with different key (decidim_scope_id), so it
|
56
|
-
# has to be manually mapped.
|
57
|
-
self.scope_id = model.scope.id if model.scope
|
58
|
-
|
59
49
|
self.documents = model.attachments
|
60
50
|
end
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
# Returns a Decidim::Category
|
65
|
-
def category
|
66
|
-
@category ||= categories.find_by(id: category_id)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Finds the Scope from the given scope_id, uses participatory space scope if missing.
|
70
|
-
#
|
71
|
-
# Returns a Decidim::Scope
|
72
|
-
def scope
|
73
|
-
@scope ||= @attributes["scope_id"].value ? current_component.scopes.find_by(id: @attributes["scope_id"].value) : current_component.scope
|
74
|
-
end
|
75
|
-
|
76
|
-
# Scope identifier
|
77
|
-
#
|
78
|
-
# Returns the scope identifier related to the proposal
|
79
|
-
def scope_id
|
80
|
-
super || scope&.id
|
52
|
+
def participatory_space_manifest
|
53
|
+
@participatory_space_manifest ||= current_component.participatory_space.manifest.name
|
81
54
|
end
|
82
55
|
|
83
56
|
def geocoding_enabled?
|
@@ -8,6 +8,31 @@ module Decidim
|
|
8
8
|
Decidim::Proposals::Proposal.find(id)
|
9
9
|
end
|
10
10
|
|
11
|
+
# Public: Generates a select field with the templates of the given component.
|
12
|
+
#
|
13
|
+
# component - A component instance.
|
14
|
+
# prompt - An i18n string to show as prompt
|
15
|
+
#
|
16
|
+
# Returns a String.
|
17
|
+
def bulk_templates_select(component, prompt, id: nil)
|
18
|
+
options_for_select = find_templates_for_select(component)
|
19
|
+
select(:template, :template_id, options_for_select, prompt:, id:)
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_templates_for_select(component)
|
23
|
+
return [] unless Decidim.module_installed? :templates
|
24
|
+
return @templates_for_select if @templates_for_select
|
25
|
+
|
26
|
+
templates = Decidim::Templates::Template.where(
|
27
|
+
target: :proposal_answer,
|
28
|
+
templatable: component
|
29
|
+
).order(:templatable_id)
|
30
|
+
|
31
|
+
@templates_for_select = templates.map do |template|
|
32
|
+
[translated_attribute(template.name), template.id]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
11
36
|
# find the valuators for the current space.
|
12
37
|
def find_valuators_for_select(participatory_space, current_user)
|
13
38
|
valuator_roles = participatory_space.user_roles(:valuator).order_by_name
|
@@ -30,7 +30,6 @@ module Decidim
|
|
30
30
|
|
31
31
|
def proposal_state_css_style(proposal)
|
32
32
|
return "" if proposal.emendation?
|
33
|
-
return "" if proposal.withdrawn?
|
34
33
|
|
35
34
|
proposal.proposal_state&.css_style
|
36
35
|
end
|
@@ -145,8 +144,16 @@ module Decidim
|
|
145
144
|
).count
|
146
145
|
end
|
147
146
|
|
147
|
+
def layout_item_classes
|
148
|
+
if show_voting_rules?
|
149
|
+
"layout-item lg:pt-4"
|
150
|
+
else
|
151
|
+
"layout-item"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
148
155
|
def show_voting_rules?
|
149
|
-
return false if !votes_enabled? ||
|
156
|
+
return false if !votes_enabled? || votes_blocked?
|
150
157
|
|
151
158
|
return true if vote_limit_enabled?
|
152
159
|
return true if threshold_per_proposal_enabled?
|
@@ -207,41 +214,38 @@ module Decidim
|
|
207
214
|
end
|
208
215
|
|
209
216
|
# rubocop:disable Metrics/CyclomaticComplexity
|
210
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
211
217
|
def filter_sections
|
212
218
|
@filter_sections ||= begin
|
213
219
|
items = []
|
214
220
|
if component_settings.proposal_answering_enabled && current_settings.proposal_answering_enabled
|
215
|
-
items.append(method: :with_any_state, collection: filter_proposals_state_values,
|
221
|
+
items.append(method: :with_any_state, collection: filter_proposals_state_values, label: t("decidim.proposals.proposals.filters.state"), id: "state")
|
216
222
|
end
|
217
|
-
|
218
|
-
items.append(method:
|
219
|
-
|
220
|
-
|
221
|
-
|
223
|
+
current_component.available_taxonomy_filters.each do |taxonomy_filter|
|
224
|
+
items.append(method: "with_any_taxonomies[#{taxonomy_filter.root_taxonomy_id}]",
|
225
|
+
collection: filter_taxonomy_values_for(taxonomy_filter),
|
226
|
+
label: decidim_sanitize_translated(taxonomy_filter.name),
|
227
|
+
id: "taxonomy-#{taxonomy_filter.root_taxonomy_id}")
|
222
228
|
end
|
223
229
|
if component_settings.official_proposals_enabled
|
224
|
-
items.append(method: :with_any_origin, collection: filter_origin_values,
|
230
|
+
items.append(method: :with_any_origin, collection: filter_origin_values, label: t("decidim.proposals.proposals.filters.origin"), id: "origin")
|
225
231
|
end
|
226
232
|
if current_user
|
227
|
-
items.append(method: :activity, collection: activity_filter_values,
|
233
|
+
items.append(method: :activity, collection: activity_filter_values, label: t("decidim.proposals.proposals.filters.activity"), id: "activity", type: :radio_buttons)
|
228
234
|
end
|
229
235
|
if @proposals.only_emendations.any?
|
230
|
-
items.append(method: :type, collection: filter_type_values,
|
236
|
+
items.append(method: :type, collection: filter_type_values, label: t("decidim.proposals.proposals.filters.amendment_type"), id: "amendment_type", type: :radio_buttons)
|
231
237
|
end
|
232
238
|
if linked_classes_for(Decidim::Proposals::Proposal).any?
|
233
239
|
items.append(
|
234
240
|
method: :related_to,
|
235
241
|
collection: linked_classes_filter_values_for(Decidim::Proposals::Proposal),
|
236
|
-
|
242
|
+
label: t("decidim.proposals.proposals.filters.related_to"),
|
237
243
|
id: "related_to",
|
238
244
|
type: :radio_buttons
|
239
245
|
)
|
240
246
|
end
|
241
247
|
end
|
242
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
243
248
|
# rubocop:enable Metrics/CyclomaticComplexity
|
244
|
-
|
245
249
|
items.reject { |item| item[:collection].blank? }
|
246
250
|
end
|
247
251
|
|
@@ -249,6 +253,10 @@ module Decidim
|
|
249
253
|
i18n_key = controller_name == "collaborative_drafts" ? "decidim.proposals.collaborative_drafts.name" : "decidim.components.proposals.name"
|
250
254
|
(defined?(current_component) && translated_attribute(current_component&.name).presence) || t(i18n_key)
|
251
255
|
end
|
256
|
+
|
257
|
+
def templates_available?
|
258
|
+
Decidim.module_installed?(:templates) && defined?(Decidim::Templates::Template) && Decidim::Templates::Template.exists?(templatable: current_component)
|
259
|
+
end
|
252
260
|
end
|
253
261
|
end
|
254
262
|
end
|
@@ -43,20 +43,20 @@ module Decidim
|
|
43
43
|
items = [{
|
44
44
|
method: :with_any_state,
|
45
45
|
collection: filter_collaborative_drafts_state_values,
|
46
|
-
|
46
|
+
label: t("decidim.proposals.collaborative_drafts.filters.state"),
|
47
47
|
id: "state"
|
48
48
|
}]
|
49
|
-
|
50
|
-
items.append(method:
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
current_component.available_taxonomy_filters.each do |taxonomy_filter|
|
50
|
+
items.append(method: "with_any_taxonomies[#{taxonomy_filter.root_taxonomy_id}]",
|
51
|
+
collection: filter_taxonomy_values_for(taxonomy_filter),
|
52
|
+
label: decidim_sanitize_translated(taxonomy_filter.name),
|
53
|
+
id: "taxonomy-#{taxonomy_filter.root_taxonomy_id}")
|
54
54
|
end
|
55
55
|
if linked_classes_for(Decidim::Proposals::CollaborativeDraft).any?
|
56
56
|
items.append(
|
57
57
|
method: :related_to,
|
58
58
|
collection: linked_classes_filter_values_for(Decidim::Proposals::CollaborativeDraft),
|
59
|
-
|
59
|
+
label: t("decidim.proposals.collaborative_drafts.filters.related_to"),
|
60
60
|
id: "related_to",
|
61
61
|
type: :radio_buttons
|
62
62
|
)
|
@@ -5,24 +5,6 @@ module Decidim
|
|
5
5
|
# This helper include some methods for rendering proposals dynamic maps.
|
6
6
|
module MapHelper
|
7
7
|
include Decidim::ApplicationHelper
|
8
|
-
# Serialize a collection of geocoded proposals to be used by the dynamic map component
|
9
|
-
#
|
10
|
-
# geocoded_proposals - A collection of geocoded proposals
|
11
|
-
def proposals_data_for_map(geocoded_proposals)
|
12
|
-
geocoded_proposals.select(&:geocoded_and_valid?).map do |proposal|
|
13
|
-
proposal_data_for_map(proposal)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def proposal_data_for_map(proposal)
|
18
|
-
proposal
|
19
|
-
.slice(:latitude, :longitude, :address)
|
20
|
-
.merge(
|
21
|
-
title: decidim_html_escape(present(proposal).title),
|
22
|
-
link: proposal_path(proposal),
|
23
|
-
items: cell("decidim/proposals/proposal_metadata", proposal).send(:proposal_items_for_map).to_json
|
24
|
-
)
|
25
|
-
end
|
26
8
|
|
27
9
|
def proposal_preview_data_for_map(proposal)
|
28
10
|
{
|
@@ -47,14 +47,14 @@ module Decidim
|
|
47
47
|
#
|
48
48
|
# Returns true if enabled, false otherwise.
|
49
49
|
def votes_enabled?
|
50
|
-
current_settings.votes_enabled
|
50
|
+
current_settings.respond_to?(:votes_enabled) && current_settings.votes_enabled
|
51
51
|
end
|
52
52
|
|
53
53
|
# Public: Checks if voting is blocked in this step.
|
54
54
|
#
|
55
55
|
# Returns true if blocked, false otherwise.
|
56
56
|
def votes_blocked?
|
57
|
-
current_settings.votes_blocked
|
57
|
+
current_settings.respond_to?(:votes_blocked) && current_settings.votes_blocked
|
58
58
|
end
|
59
59
|
|
60
60
|
# Public: Checks if the current user is allowed to vote in this step.
|
@@ -76,6 +76,19 @@ module Decidim
|
|
76
76
|
votes_count = ProposalVote.where(author: user, proposal: proposals).size
|
77
77
|
component_settings.vote_limit - votes_count
|
78
78
|
end
|
79
|
+
|
80
|
+
# Return the remaining minimum votes for a user if the current component has a vote limit
|
81
|
+
#
|
82
|
+
# user - A User object
|
83
|
+
#
|
84
|
+
# Returns a number with the remaining minimum votes for that user
|
85
|
+
def remaining_minimum_votes_count_for(user)
|
86
|
+
return 0 unless vote_limit_enabled?
|
87
|
+
|
88
|
+
votes_count = Decidim::Proposals::ProposalVote.joins(:proposal).where(decidim_proposals_proposals: { decidim_component_id: current_component.id }).where(author: user).count
|
89
|
+
|
90
|
+
component_settings.minimum_votes_per_user - votes_count
|
91
|
+
end
|
79
92
|
end
|
80
93
|
end
|
81
94
|
end
|
@@ -12,7 +12,9 @@ module Decidim
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def proposal_has_costs?
|
15
|
-
@proposal.cost.present?
|
15
|
+
@proposal.cost.present? &&
|
16
|
+
translated_attribute(@proposal.cost_report).present? &&
|
17
|
+
translated_attribute(@proposal.execution_period).present?
|
16
18
|
end
|
17
19
|
|
18
20
|
def toggle_view_mode_link(current_mode, target_mode, title, params)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
module Admin
|
6
|
+
class ProposalAnswerJob < ApplicationJob
|
7
|
+
queue_as :default
|
8
|
+
|
9
|
+
def perform(proposal, attributes, context)
|
10
|
+
answer_form = ProposalAnswerForm.from_params(attributes).with_context(**context)
|
11
|
+
|
12
|
+
Admin::AnswerProposal.call(answer_form, proposal) do
|
13
|
+
on(:ok) { Rails.logger.info "Proposal #{proposal.id} answered successfully." }
|
14
|
+
on(:invalid) { Rails.logger.error "Proposal ID #{proposal.id} could not be updated. Errors: #{answer_form.errors.full_messages}" }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -5,6 +5,7 @@ module Decidim
|
|
5
5
|
class CollaborativeDraft < Proposals::ApplicationRecord
|
6
6
|
include Decidim::Resourceable
|
7
7
|
include Decidim::Coauthorable
|
8
|
+
include Decidim::Taxonomizable
|
8
9
|
include Decidim::HasComponent
|
9
10
|
include Decidim::ScopableResource
|
10
11
|
include Decidim::HasReference
|
@@ -45,8 +46,9 @@ module Decidim
|
|
45
46
|
authored_by?(user)
|
46
47
|
end
|
47
48
|
|
48
|
-
|
49
|
-
|
49
|
+
# Public: Overrides the `reported_content_url` Reportable concern method.
|
50
|
+
def reported_content_url
|
51
|
+
ResourceLocatorPresenter.new(self).url
|
50
52
|
end
|
51
53
|
|
52
54
|
# Public: Overrides the `reported_attributes` Reportable concern method.
|
@@ -63,7 +65,15 @@ module Decidim
|
|
63
65
|
ransacker_text_multi :search_text, [:title, :body]
|
64
66
|
|
65
67
|
def self.ransackable_scopes(_auth_object = nil)
|
66
|
-
[:with_any_state, :related_to, :
|
68
|
+
[:with_any_state, :related_to, :with_any_taxonomies]
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.ransackable_attributes(_auth_object = nil)
|
72
|
+
%w(id_string search_text title body)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.ransackable_associations(_auth_object = nil)
|
76
|
+
%w(taxonomies)
|
67
77
|
end
|
68
78
|
end
|
69
79
|
end
|