decidim-plans 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +157 -0
- data/Rakefile +48 -0
- data/app/assets/config/admin/decidim_plans_manifest.js +3 -0
- data/app/assets/config/decidim_plans_manifest.css +4 -0
- data/app/assets/config/decidim_plans_manifest.js +2 -0
- data/app/assets/images/decidim/plans/icon.svg +3 -0
- data/app/assets/javascripts/decidim/plans/admin/plans.js.es6 +3 -0
- data/app/assets/javascripts/decidim/plans/admin/sections.js.es6 +78 -0
- data/app/assets/javascripts/decidim/plans/multifield/component.js.es6 +101 -0
- data/app/assets/javascripts/decidim/plans/multifield.js.es6 +5 -0
- data/app/assets/javascripts/decidim/plans/plans_form.js.es6 +9 -0
- data/app/assets/javascripts/decidim/plans/proposal_picker.js.es6 +41 -0
- data/app/assets/javascripts/decidim/plans/remaining_characters.es6 +42 -0
- data/app/assets/javascripts/decidim/plans/social_share.js +2 -0
- data/app/assets/javascripts/decidim/plans/tab_focus.es6 +20 -0
- data/app/assets/stylesheets/decidim/plans/plans_form.scss +26 -0
- data/app/assets/stylesheets/decidim/plans/proposal_picker.scss +3 -0
- data/app/assets/stylesheets/decidim/plans/social_share.css.scss +22 -0
- data/app/cells/decidim/plans/author/flag.erb +5 -0
- data/app/cells/decidim/plans/author/withdraw.erb +6 -0
- data/app/cells/decidim/plans/author_cell.rb +9 -0
- data/app/cells/decidim/plans/coauthorships_cell.rb +23 -0
- data/app/cells/decidim/plans/collapsible_authors/show.erb +17 -0
- data/app/cells/decidim/plans/collapsible_authors_cell.rb +9 -0
- data/app/cells/decidim/plans/plan_activity_cell.rb +19 -0
- data/app/cells/decidim/plans/plan_cell.rb +59 -0
- data/app/cells/decidim/plans/plan_m/footer.erb +8 -0
- data/app/cells/decidim/plans/plan_m_cell.rb +62 -0
- data/app/commands/decidim/plans/accept_access_to_plan.rb +53 -0
- data/app/commands/decidim/plans/admin/answer_plan.rb +82 -0
- data/app/commands/decidim/plans/admin/create_plan.rb +102 -0
- data/app/commands/decidim/plans/admin/update_plan.rb +98 -0
- data/app/commands/decidim/plans/admin/update_sections.rb +54 -0
- data/app/commands/decidim/plans/attachment_methods.rb +78 -0
- data/app/commands/decidim/plans/create_plan.rb +90 -0
- data/app/commands/decidim/plans/destroy_plan.rb +33 -0
- data/app/commands/decidim/plans/nested_updater.rb +26 -0
- data/app/commands/decidim/plans/publish_plan.rb +79 -0
- data/app/commands/decidim/plans/reject_access_to_plan.rb +46 -0
- data/app/commands/decidim/plans/request_access_to_plan.rb +50 -0
- data/app/commands/decidim/plans/respond_to_access_request.rb +76 -0
- data/app/commands/decidim/plans/update_plan.rb +90 -0
- data/app/commands/decidim/plans/withdraw_plan.rb +50 -0
- data/app/controllers/concerns/decidim/plans/orderable.rb +53 -0
- data/app/controllers/decidim/plans/admin/application_controller.rb +16 -0
- data/app/controllers/decidim/plans/admin/plan_answers_controller.rb +40 -0
- data/app/controllers/decidim/plans/admin/plans_controller.rb +80 -0
- data/app/controllers/decidim/plans/admin/sections_controller.rb +75 -0
- data/app/controllers/decidim/plans/application_controller.rb +30 -0
- data/app/controllers/decidim/plans/plan_collaborator_requests_controller.rb +53 -0
- data/app/controllers/decidim/plans/plan_widgets_controller.rb +19 -0
- data/app/controllers/decidim/plans/plans_controller.rb +192 -0
- data/app/controllers/decidim/plans/versions_controller.rb +51 -0
- data/app/events/decidim/plans/accepted_plan_event.rb +9 -0
- data/app/events/decidim/plans/evaluating_plan_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_accepted_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_rejected_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_request_event.rb +27 -0
- data/app/events/decidim/plans/plan_access_requested_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_requester_accepted_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_requester_rejected_event.rb +8 -0
- data/app/events/decidim/plans/publish_plan_event.rb +25 -0
- data/app/events/decidim/plans/rejected_plan_event.rb +9 -0
- data/app/forms/decidim/plans/accept_access_to_plan_form.rb +10 -0
- data/app/forms/decidim/plans/access_to_plan_form.rb +35 -0
- data/app/forms/decidim/plans/admin/plan_answer_form.rb +19 -0
- data/app/forms/decidim/plans/admin/plan_form.rb +88 -0
- data/app/forms/decidim/plans/admin/plan_sections_form.rb +20 -0
- data/app/forms/decidim/plans/admin/section_form.rb +34 -0
- data/app/forms/decidim/plans/attachment_form.rb +20 -0
- data/app/forms/decidim/plans/content_form.rb +58 -0
- data/app/forms/decidim/plans/plan_form.rb +85 -0
- data/app/forms/decidim/plans/reject_access_to_plan_form.rb +9 -0
- data/app/forms/decidim/plans/request_access_to_plan_form.rb +20 -0
- data/app/helpers/decidim/plans/admin/application_helper.rb +19 -0
- data/app/helpers/decidim/plans/application_helper.rb +78 -0
- data/app/helpers/decidim/plans/attached_proposals_helper.rb +44 -0
- data/app/helpers/decidim/plans/attachments_helper.rb +59 -0
- data/app/helpers/decidim/plans/cells_helper.rb +26 -0
- data/app/helpers/decidim/plans/plan_cells_helper.rb +67 -0
- data/app/helpers/decidim/plans/remaining_characters_helper.rb +32 -0
- data/app/helpers/decidim/plans/traceability_helper.rb +79 -0
- data/app/helpers/decidim/plans/user_group_helper.rb +23 -0
- data/app/models/concerns/decidim/plans/traceable.rb +48 -0
- data/app/models/decidim/plans/application_record.rb +10 -0
- data/app/models/decidim/plans/attached_proposal.rb +12 -0
- data/app/models/decidim/plans/content.rb +21 -0
- data/app/models/decidim/plans/paper_trail/version.rb +26 -0
- data/app/models/decidim/plans/paper_trail/version_association.rb +14 -0
- data/app/models/decidim/plans/plan.rb +206 -0
- data/app/models/decidim/plans/plan_collaborator_request.rb +13 -0
- data/app/models/decidim/plans/section.rb +16 -0
- data/app/permissions/decidim/plans/admin/permissions.rb +44 -0
- data/app/permissions/decidim/plans/permissions.rb +67 -0
- data/app/presenters/decidim/plans/admin_log/plan_presenter.rb +49 -0
- data/app/presenters/decidim/plans/admin_log/value_types/plan_state_presenter.rb +16 -0
- data/app/presenters/decidim/plans/content_presenter.rb +25 -0
- data/app/presenters/decidim/plans/log/resource_presenter.rb +18 -0
- data/app/presenters/decidim/plans/official_author_presenter.rb +38 -0
- data/app/presenters/decidim/plans/plan_presenter.rb +48 -0
- data/app/queries/decidim/plans/filtered_plans.rb +37 -0
- data/app/services/decidim/plans/diff_renderer/base.rb +108 -0
- data/app/services/decidim/plans/diff_renderer/categorization.rb +23 -0
- data/app/services/decidim/plans/diff_renderer/content.rb +33 -0
- data/app/services/decidim/plans/diff_renderer/plan.rb +25 -0
- data/app/services/decidim/plans/loggability.rb +19 -0
- data/app/services/decidim/plans/plan_builder.rb +68 -0
- data/app/services/decidim/plans/plan_search.rb +95 -0
- data/app/services/decidim/plans/tracer.rb +29 -0
- data/app/views/decidim/plans/admin/plan_answers/edit.html.erb +22 -0
- data/app/views/decidim/plans/admin/plans/_bulk-actions.html.erb +9 -0
- data/app/views/decidim/plans/admin/plans/_form.html.erb +62 -0
- data/app/views/decidim/plans/admin/plans/_js-callout.html.erb +6 -0
- data/app/views/decidim/plans/admin/plans/_plan-tr.html.erb +53 -0
- data/app/views/decidim/plans/admin/plans/edit.html.erb +7 -0
- data/app/views/decidim/plans/admin/plans/index.html.erb +64 -0
- data/app/views/decidim/plans/admin/plans/new.html.erb +7 -0
- data/app/views/decidim/plans/admin/sections/_bulk-actions.html.erb +7 -0
- data/app/views/decidim/plans/admin/sections/_form.html.erb +27 -0
- data/app/views/decidim/plans/admin/sections/_section.html.erb +66 -0
- data/app/views/decidim/plans/admin/sections/index.html.erb +7 -0
- data/app/views/decidim/plans/admin/shared/_info_plan.html.erb +17 -0
- data/app/views/decidim/plans/attached_proposals/_proposals.html.erb +12 -0
- data/app/views/decidim/plans/plan_widgets/show.html.erb +3 -0
- data/app/views/decidim/plans/plans/_accept_request_access_form.html.erb +8 -0
- data/app/views/decidim/plans/plans/_attached_proposals.html.erb +10 -0
- data/app/views/decidim/plans/plans/_collaborator_requests.html.erb +20 -0
- data/app/views/decidim/plans/plans/_content.html.erb +6 -0
- data/app/views/decidim/plans/plans/_contents.html.erb +8 -0
- data/app/views/decidim/plans/plans/_count.html.erb +1 -0
- data/app/views/decidim/plans/plans/_filters.html.erb +32 -0
- data/app/views/decidim/plans/plans/_filters_small_view.html.erb +18 -0
- data/app/views/decidim/plans/plans/_form.html.erb +57 -0
- data/app/views/decidim/plans/plans/_new_plan_button.html.erb +11 -0
- data/app/views/decidim/plans/plans/_plan.html.erb +1 -0
- data/app/views/decidim/plans/plans/_plan_preview.html.erb +1 -0
- data/app/views/decidim/plans/plans/_plans.html.erb +14 -0
- data/app/views/decidim/plans/plans/_reject_request_access_form.html.erb +10 -0
- data/app/views/decidim/plans/plans/_request_access_form.html.erb +7 -0
- data/app/views/decidim/plans/plans/edit.html.erb +21 -0
- data/app/views/decidim/plans/plans/index.html.erb +28 -0
- data/app/views/decidim/plans/plans/index.js.erb +10 -0
- data/app/views/decidim/plans/plans/new.html.erb +15 -0
- data/app/views/decidim/plans/plans/preview.html.erb +15 -0
- data/app/views/decidim/plans/plans/show.html.erb +133 -0
- data/app/views/decidim/plans/shared/_attachment_fields.html.erb +45 -0
- data/app/views/decidim/plans/shared/_attachments.html.erb +23 -0
- data/app/views/decidim/plans/shared/_remaining_characters_container.html.erb +12 -0
- data/app/views/decidim/plans/shared/_section_content_field.html.erb +39 -0
- data/app/views/decidim/plans/versions/_version.html.erb +20 -0
- data/app/views/decidim/plans/versions/index.html.erb +34 -0
- data/app/views/decidim/plans/versions/show.html.erb +41 -0
- data/config/locales/en.yml +341 -0
- data/config/locales/fi.yml +341 -0
- data/config/locales/sv.yml +341 -0
- data/db/migrate/20181230110225_enable_pg_trgm_extension_for_plans.rb +18 -0
- data/db/migrate/20181230110848_create_decidim_plans.rb +20 -0
- data/db/migrate/20181230111731_create_decidim_plans_sections.rb +13 -0
- data/db/migrate/20181230111931_create_decidim_plans_plan_contents.rb +14 -0
- data/db/migrate/20181230122035_create_decidim_plans_plan_collaborator_requests.rb +12 -0
- data/db/migrate/20190110125855_create_decidim_plans_attached_proposals.rb +12 -0
- data/db/migrate/20190129140441_add_help_to_decidim_plans_sections.rb +7 -0
- data/db/migrate/20190129143607_add_mandatory_to_decidim_plans_sections.rb +7 -0
- data/db/migrate/20190129172040_add_section_type_to_decidim_plans_sections.rb +12 -0
- data/db/migrate/20190129231439_add_answer_length_to_decidim_plans_sections.rb +11 -0
- data/db/migrate/20190202123044_create_version_associations.rb +25 -0
- data/db/migrate/20190202123045_add_transaction_id_column_to_versions.rb +15 -0
- data/db/migrate/20190202200716_add_update_token_to_decidim_plans.rb +7 -0
- data/lib/decidim/plans/admin.rb +10 -0
- data/lib/decidim/plans/admin_engine.rb +29 -0
- data/lib/decidim/plans/component.rb +163 -0
- data/lib/decidim/plans/engine.rb +43 -0
- data/lib/decidim/plans/locale_aware.rb +32 -0
- data/lib/decidim/plans/optionally_translatable_attributes.rb +85 -0
- data/lib/decidim/plans/paper_trail/record_trail.rb +24 -0
- data/lib/decidim/plans/paper_trail.rb +10 -0
- data/lib/decidim/plans/test/factories.rb +130 -0
- data/lib/decidim/plans/version.rb +8 -0
- data/lib/decidim/plans.rb +24 -0
- metadata +323 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module Admin
|
6
|
+
# A form object to be used when admin users want to create a plan.
|
7
|
+
class PlanForm < Decidim::Form
|
8
|
+
include OptionallyTranslatableAttributes
|
9
|
+
include Decidim::ApplicationHelper
|
10
|
+
mimic :plan
|
11
|
+
|
12
|
+
optionally_translatable_attribute :title, String
|
13
|
+
|
14
|
+
attribute :category_id, Integer
|
15
|
+
attribute :scope_id, Integer
|
16
|
+
attribute :attachments, Array[Plans::AttachmentForm]
|
17
|
+
attribute :contents, Array[Decidim::Plans::ContentForm]
|
18
|
+
attribute :proposal_ids, Array[Integer]
|
19
|
+
|
20
|
+
optionally_translatable_validate_presence :title
|
21
|
+
|
22
|
+
validates :proposals, presence: true
|
23
|
+
validates :category, presence: true, if: ->(form) { form.category_id.present? }
|
24
|
+
validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
|
25
|
+
|
26
|
+
validate :scope_belongs_to_participatory_space_scope
|
27
|
+
|
28
|
+
delegate :categories, to: :current_component
|
29
|
+
|
30
|
+
def map_model(model)
|
31
|
+
self.contents = model.sections.map do |section|
|
32
|
+
ContentForm.from_model(
|
33
|
+
Content
|
34
|
+
.where(plan: model, section: section)
|
35
|
+
.first_or_initialize(plan: model, section: section)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
return unless model.categorization
|
40
|
+
|
41
|
+
self.category_id = model.categorization.decidim_category_id
|
42
|
+
self.scope_id = model.decidim_scope_id
|
43
|
+
end
|
44
|
+
|
45
|
+
alias component current_component
|
46
|
+
|
47
|
+
# Finds the Category from the category_id.
|
48
|
+
#
|
49
|
+
# Returns a Decidim::Category
|
50
|
+
def category
|
51
|
+
@category ||= categories.find_by(id: category_id)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
|
55
|
+
#
|
56
|
+
# Returns a Decidim::Scope
|
57
|
+
def scope
|
58
|
+
@scope ||= @scope_id ? current_participatory_space.scopes.find_by(id: @scope_id) : current_participatory_space.scope
|
59
|
+
end
|
60
|
+
|
61
|
+
# Scope identifier
|
62
|
+
#
|
63
|
+
# Returns the scope identifier related to the plan
|
64
|
+
def scope_id
|
65
|
+
@scope_id || scope&.id
|
66
|
+
end
|
67
|
+
|
68
|
+
def author
|
69
|
+
current_organization
|
70
|
+
end
|
71
|
+
|
72
|
+
def proposals
|
73
|
+
@proposals ||= Decidim
|
74
|
+
.find_resource_manifest(:proposals)
|
75
|
+
.try(:resource_scope, current_component)
|
76
|
+
&.where(id: proposal_ids)
|
77
|
+
&.order(title: :asc)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def scope_belongs_to_participatory_space_scope
|
83
|
+
errors.add(:scope_id, :invalid) if current_participatory_space.out_of_scope?(scope)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module Admin
|
6
|
+
# A form object to be used when admin users want to create a plan.
|
7
|
+
class PlanSectionsForm < Decidim::Form
|
8
|
+
include Decidim::ApplicationHelper
|
9
|
+
|
10
|
+
attribute :sections, Array[SectionForm]
|
11
|
+
|
12
|
+
def map_model(sections)
|
13
|
+
self.sections = sections.map do |section|
|
14
|
+
SectionForm.from_model(section)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module Admin
|
6
|
+
# A form object to be used when admin users want to create a plan.
|
7
|
+
class SectionForm < Decidim::Form
|
8
|
+
include TranslatableAttributes
|
9
|
+
include Decidim::ApplicationHelper
|
10
|
+
|
11
|
+
mimic :section
|
12
|
+
|
13
|
+
translatable_attribute :body, String
|
14
|
+
translatable_attribute :help, String
|
15
|
+
attribute :mandatory, Boolean, default: false
|
16
|
+
attribute :answer_length, Integer, default: 0
|
17
|
+
attribute :section_type, String
|
18
|
+
attribute :position, Integer
|
19
|
+
attribute :deleted, Boolean, default: false
|
20
|
+
|
21
|
+
validates :position, numericality: { greater_than_or_equal_to: 0 }
|
22
|
+
validates :body, translatable_presence: true, unless: :deleted
|
23
|
+
validates :answer_length, numericality: {
|
24
|
+
greater_than_or_equal_to: 0,
|
25
|
+
only_integer: true
|
26
|
+
}
|
27
|
+
|
28
|
+
def to_param
|
29
|
+
id || "section-id"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
class AttachmentForm < Decidim::Form
|
6
|
+
attribute :title, String
|
7
|
+
attribute :file
|
8
|
+
attribute :weight, Integer
|
9
|
+
attribute :deleted, Boolean, default: false
|
10
|
+
|
11
|
+
mimic :attachment
|
12
|
+
|
13
|
+
validates :title, presence: true, if: ->(form) { form.file.present? || form.id.present? }
|
14
|
+
|
15
|
+
def to_param
|
16
|
+
id || "attachment-id"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
# A form object to be used when admin users want to create or edit
|
6
|
+
# a plan content field.
|
7
|
+
class ContentForm < Decidim::Form
|
8
|
+
include OptionallyTranslatableAttributes
|
9
|
+
include Decidim::TranslationsHelper
|
10
|
+
|
11
|
+
mimic :content
|
12
|
+
|
13
|
+
alias component current_component
|
14
|
+
|
15
|
+
optionally_translatable_attribute :body, String
|
16
|
+
attribute :section_id, Integer
|
17
|
+
attribute :plan_id, Integer
|
18
|
+
|
19
|
+
optionally_translatable_validate_presence :body, if: :mandatory
|
20
|
+
|
21
|
+
attr_writer :section
|
22
|
+
attr_writer :plan
|
23
|
+
|
24
|
+
delegate :mandatory, to: :section
|
25
|
+
|
26
|
+
def section
|
27
|
+
@section ||= Decidim::Plans::Section.find(section_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def plan
|
31
|
+
@plan ||= Decidim::Plans::Plan.find(plan_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def label
|
35
|
+
translated_attribute(section.body)
|
36
|
+
end
|
37
|
+
|
38
|
+
def help
|
39
|
+
translated_attribute(section.help)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Map the correct fields.
|
43
|
+
#
|
44
|
+
# Returns nothing.
|
45
|
+
def map_model(model)
|
46
|
+
self.section_id = model.decidim_section_id
|
47
|
+
self.section = model.section
|
48
|
+
|
49
|
+
self.plan_id = model.decidim_plan_id
|
50
|
+
self.plan = model.plan
|
51
|
+
end
|
52
|
+
|
53
|
+
def deleted?
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
# A form object to be used when public users want to create a Plan.
|
6
|
+
class PlanForm < Decidim::Form
|
7
|
+
include OptionallyTranslatableAttributes
|
8
|
+
mimic :plan
|
9
|
+
|
10
|
+
alias component current_component
|
11
|
+
|
12
|
+
optionally_translatable_attribute :title, String
|
13
|
+
|
14
|
+
attribute :user_group_id, Integer
|
15
|
+
attribute :category_id, Integer
|
16
|
+
attribute :scope_id, Integer
|
17
|
+
attribute :attachments, Array[Plans::AttachmentForm]
|
18
|
+
attribute :contents, Array[ContentForm]
|
19
|
+
attribute :proposal_ids, Array[Integer]
|
20
|
+
|
21
|
+
validates :proposals, presence: true
|
22
|
+
validates :category, presence: true, if: ->(form) { form.category_id.present? }
|
23
|
+
validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
|
24
|
+
optionally_translatable_validate_presence :title
|
25
|
+
|
26
|
+
validate :scope_belongs_to_participatory_space_scope
|
27
|
+
|
28
|
+
delegate :categories, to: :current_component
|
29
|
+
|
30
|
+
# Finds the Category from the category_id.
|
31
|
+
#
|
32
|
+
# Returns a Decidim::Category
|
33
|
+
def category
|
34
|
+
@category ||= categories.find_by(id: category_id)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
|
38
|
+
#
|
39
|
+
# Returns a Decidim::Scope
|
40
|
+
def scope
|
41
|
+
@scope ||= @scope_id ? current_participatory_space.scopes.find_by(id: @scope_id) : current_participatory_space.scope
|
42
|
+
end
|
43
|
+
|
44
|
+
# Scope identifier
|
45
|
+
#
|
46
|
+
# Returns the scope identifier related to the plan
|
47
|
+
def scope_id
|
48
|
+
@scope_id || scope&.id
|
49
|
+
end
|
50
|
+
|
51
|
+
def map_model(model)
|
52
|
+
self.contents = model.sections.map do |section|
|
53
|
+
ContentForm.from_model(
|
54
|
+
Content
|
55
|
+
.where(plan: model, section: section)
|
56
|
+
.first_or_initialize(plan: model, section: section)
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
self.user_group_id = model.user_groups.first&.id
|
61
|
+
return unless model.categorization
|
62
|
+
|
63
|
+
self.category_id = model.categorization.decidim_category_id
|
64
|
+
end
|
65
|
+
|
66
|
+
def user_group
|
67
|
+
@user_group ||= Decidim::UserGroup.find user_group_id if user_group_id.present?
|
68
|
+
end
|
69
|
+
|
70
|
+
def proposals
|
71
|
+
@proposals ||= Decidim
|
72
|
+
.find_resource_manifest(:proposals)
|
73
|
+
.try(:resource_scope, current_component)
|
74
|
+
&.where(id: proposal_ids)
|
75
|
+
&.order(title: :asc)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def scope_belongs_to_participatory_space_scope
|
81
|
+
errors.add(:scope_id, :invalid) if current_participatory_space.out_of_scope?(scope)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
# A form object to be used when public users want to request acces to a Plan.
|
6
|
+
class RequestAccessToPlanForm < Decidim::Form
|
7
|
+
mimic :plan
|
8
|
+
|
9
|
+
attribute :id, String
|
10
|
+
attribute :state, String
|
11
|
+
|
12
|
+
validates :id, presence: true
|
13
|
+
validates :state, presence: true, inclusion: { in: %w(open) }
|
14
|
+
|
15
|
+
def plan
|
16
|
+
@plan ||= Decidim::Plans::Plan.find id
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module Admin
|
6
|
+
# Custom helpers, scoped to the forms engine.
|
7
|
+
#
|
8
|
+
module ApplicationHelper
|
9
|
+
def tabs_id_for_section(section)
|
10
|
+
"section_#{section.to_param}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def tabs_id_for_content(idx)
|
14
|
+
"content_#{idx}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
# Custom helpers, scoped to the plans engine.
|
6
|
+
#
|
7
|
+
module ApplicationHelper
|
8
|
+
include Decidim::Comments::CommentsHelper
|
9
|
+
include PaginateHelper
|
10
|
+
include Decidim::MapHelper
|
11
|
+
|
12
|
+
# Public: The state of a plan in a way a human can understand.
|
13
|
+
#
|
14
|
+
# state - The String state of the plan.
|
15
|
+
#
|
16
|
+
# Returns a String.
|
17
|
+
def humanize_plan_state(state)
|
18
|
+
I18n.t(state, scope: "decidim.plans.answers", default: :not_answered)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: The css class applied based on the plan state.
|
22
|
+
#
|
23
|
+
# state - The String state of the plan.
|
24
|
+
#
|
25
|
+
# Returns a String.
|
26
|
+
def plan_state_css_class(state)
|
27
|
+
case state
|
28
|
+
when "accepted"
|
29
|
+
"text-success"
|
30
|
+
when "rejected"
|
31
|
+
"text-alert"
|
32
|
+
when "evaluating"
|
33
|
+
"text-warning"
|
34
|
+
when "withdrawn"
|
35
|
+
"text-alert"
|
36
|
+
else
|
37
|
+
"text-info"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_user_plans
|
42
|
+
Plan.joins(:coauthorships).where(
|
43
|
+
component: current_component,
|
44
|
+
decidim_coauthorships: {
|
45
|
+
decidim_author_id: current_user.id,
|
46
|
+
decidim_author_type: "Decidim::UserBaseEntity"
|
47
|
+
}
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def authors_for(plan)
|
52
|
+
plan.identities.map { |identity| present(identity) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def filter_state_values
|
56
|
+
[
|
57
|
+
["except_rejected", t("decidim.plans.application_helper.filter_state_values.except_rejected")],
|
58
|
+
["accepted", t("decidim.plans.application_helper.filter_state_values.accepted")],
|
59
|
+
["evaluating", t("decidim.plans.application_helper.filter_state_values.evaluating")],
|
60
|
+
["rejected", t("decidim.plans.application_helper.filter_state_values.rejected")],
|
61
|
+
["all", t("decidim.plans.application_helper.filter_state_values.all")]
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
def filter_type_values
|
66
|
+
[
|
67
|
+
["all", t("decidim.plans.application_helper.filter_type_values.all")],
|
68
|
+
["plans", t("decidim.plans.application_helper.filter_type_values.plans")],
|
69
|
+
["amendments", t("decidim.plans.application_helper.filter_type_values.amendments")]
|
70
|
+
]
|
71
|
+
end
|
72
|
+
|
73
|
+
def tabs_id_for_content(idx)
|
74
|
+
"content_#{idx}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module AttachedProposalsHelper
|
6
|
+
include Decidim::ApplicationHelper
|
7
|
+
|
8
|
+
def attached_proposals_picker_field(form, name)
|
9
|
+
picker_options = {
|
10
|
+
id: "attached_proposals",
|
11
|
+
"class": "picker-multiple",
|
12
|
+
name: "proposal_ids",
|
13
|
+
multiple: true
|
14
|
+
}
|
15
|
+
|
16
|
+
prompt_params = {
|
17
|
+
url: plan_search_proposals_path(current_component, format: :html),
|
18
|
+
text: t("decidim.plans.attached_proposals_helper.attach_proposal")
|
19
|
+
}
|
20
|
+
|
21
|
+
form.data_picker(name, picker_options, prompt_params) do |item|
|
22
|
+
{ url: plan_search_proposals_path(current_component, format: :json),
|
23
|
+
text: item.title }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def search_proposals
|
28
|
+
respond_to do |format|
|
29
|
+
format.html do
|
30
|
+
render partial: "decidim/plans/attached_proposals/proposals"
|
31
|
+
end
|
32
|
+
format.json do
|
33
|
+
query = Decidim
|
34
|
+
.find_resource_manifest(:proposals)
|
35
|
+
.try(:resource_scope, current_component)
|
36
|
+
&.order(title: :asc)
|
37
|
+
&.where("title ilike ?", "%#{params[:term]}%")
|
38
|
+
render json: query.all.collect { |p| [present(p).title, p.id] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module AttachmentsHelper
|
6
|
+
def tabs_id_for_attachment(attachment)
|
7
|
+
"attachment_#{attachment.to_param}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def blank_attachment
|
11
|
+
@blank_attachment ||= Plans::AttachmentForm.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# A modified version of the Decidim's own form builder `upload` method
|
15
|
+
# which is buggy for multiple file fields.
|
16
|
+
#
|
17
|
+
# See:
|
18
|
+
# Decidim::FormBuilder#upload
|
19
|
+
#
|
20
|
+
# form - The form object for which to create the field.
|
21
|
+
# attribute - The String/Symbol name of the attribute to build the
|
22
|
+
# field.
|
23
|
+
def upload_field(form, attribute)
|
24
|
+
file = form.object.send attribute
|
25
|
+
required = file.nil?
|
26
|
+
|
27
|
+
label_content = form.label_for(attribute)
|
28
|
+
label_content += required_tag if required
|
29
|
+
template = ""
|
30
|
+
template += form.label(attribute, label_content)
|
31
|
+
template += form.file_field attribute, label: false
|
32
|
+
|
33
|
+
if form.send(:file_is_image?, file)
|
34
|
+
template += if file.present?
|
35
|
+
content_tag :label, I18n.t("current_image", scope: "decidim.forms")
|
36
|
+
else
|
37
|
+
content_tag :label, I18n.t("default_image", scope: "decidim.forms")
|
38
|
+
end
|
39
|
+
template += link_to image_tag(file.url), file.url, target: "_blank"
|
40
|
+
elsif form.send(:file_is_present?, file)
|
41
|
+
template += label_tag I18n.t("current_file", scope: "decidim.forms")
|
42
|
+
template += link_to file.file.filename, file.url, target: "_blank"
|
43
|
+
end
|
44
|
+
|
45
|
+
template.html_safe
|
46
|
+
end
|
47
|
+
|
48
|
+
def required_tag
|
49
|
+
content_tag(
|
50
|
+
:abbr,
|
51
|
+
"*",
|
52
|
+
title: I18n.t("required", scope: "forms"),
|
53
|
+
data: { tooltip: true, disable_hover: false }, 'aria-haspopup': true,
|
54
|
+
class: "label-required"
|
55
|
+
).html_safe
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module CellsHelper
|
6
|
+
def plans_controller?
|
7
|
+
context[:controller].class.to_s == "Decidim::Plans::PlansController"
|
8
|
+
end
|
9
|
+
|
10
|
+
def withdrawable?
|
11
|
+
return unless from_context
|
12
|
+
return unless plans_controller?
|
13
|
+
return if index_action?
|
14
|
+
from_context.withdrawable_by?(current_user)
|
15
|
+
end
|
16
|
+
|
17
|
+
def flagable?
|
18
|
+
return unless from_context
|
19
|
+
return unless plans_controller?
|
20
|
+
return if index_action?
|
21
|
+
return if from_context.try(:official?)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
# Custom helpers, scoped to the plans engine.
|
6
|
+
#
|
7
|
+
module PlanCellsHelper
|
8
|
+
include Decidim::Plans::ApplicationHelper
|
9
|
+
include Decidim::Plans::Engine.routes.url_helpers
|
10
|
+
include Decidim::LayoutHelper
|
11
|
+
include Decidim::ApplicationHelper
|
12
|
+
include Decidim::TranslationsHelper
|
13
|
+
include Decidim::ResourceReferenceHelper
|
14
|
+
include Decidim::TranslatableAttributes
|
15
|
+
include Decidim::CardHelper
|
16
|
+
|
17
|
+
delegate :title, :state, :answered?, :withdrawn?, to: :model
|
18
|
+
|
19
|
+
def has_actions?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def plans_controller?
|
24
|
+
context[:controller].class.to_s == "Decidim::Plans::PlansController"
|
25
|
+
end
|
26
|
+
|
27
|
+
def index_action?
|
28
|
+
context[:controller].action_name == "index"
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_settings
|
32
|
+
model.component.current_settings
|
33
|
+
end
|
34
|
+
|
35
|
+
def component_settings
|
36
|
+
model.component.settings
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_component
|
40
|
+
model.component
|
41
|
+
end
|
42
|
+
|
43
|
+
def from_context
|
44
|
+
@options[:from]
|
45
|
+
end
|
46
|
+
|
47
|
+
def badge_name
|
48
|
+
humanize_plan_state state
|
49
|
+
end
|
50
|
+
|
51
|
+
def state_classes
|
52
|
+
case state
|
53
|
+
when "accepted"
|
54
|
+
["success"]
|
55
|
+
when "rejected"
|
56
|
+
["alert"]
|
57
|
+
when "evaluating"
|
58
|
+
["warning"]
|
59
|
+
when "withdrawn"
|
60
|
+
["alert"]
|
61
|
+
else
|
62
|
+
["muted"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
module RemainingCharactersHelper
|
6
|
+
def remaining_characters(attribute, num_characters)
|
7
|
+
return unless block_given?
|
8
|
+
|
9
|
+
field_opts = {}
|
10
|
+
if num_characters.positive?
|
11
|
+
remaining_characters_id = "#{attribute}_remaining_characters"
|
12
|
+
field_opts = {
|
13
|
+
maxlength: num_characters,
|
14
|
+
data: {
|
15
|
+
remaining_characters: "##{remaining_characters_id}"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
field = capture do
|
21
|
+
yield field_opts
|
22
|
+
end
|
23
|
+
chars_elem = render(
|
24
|
+
"decidim/plans/shared/remaining_characters_container",
|
25
|
+
remaining_characters_id: remaining_characters_id
|
26
|
+
)
|
27
|
+
|
28
|
+
field + chars_elem
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|