decidim-reporting_proposals 0.2.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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +32 -0
  3. data/.erb-lint.yml +30 -0
  4. data/.eslintignore +3 -0
  5. data/.eslintrc.json +239 -0
  6. data/.github/workflows/codeql-analysis.yml +74 -0
  7. data/.github/workflows/lint.yml +45 -0
  8. data/.github/workflows/test_integration.yml +125 -0
  9. data/.github/workflows/test_unit.yml +51 -0
  10. data/.gitignore +27 -0
  11. data/.rspec +3 -0
  12. data/.rubocop.yml +3 -0
  13. data/.rubocop_rails.yml +87 -0
  14. data/.rubocop_ruby.yml +1754 -0
  15. data/.ruby-version +1 -0
  16. data/.simplecov +13 -0
  17. data/CODE_OF_CONDUCT.md +74 -0
  18. data/Gemfile +40 -0
  19. data/Gemfile.lock +815 -0
  20. data/LICENSE-AGPLv3.txt +661 -0
  21. data/README.md +166 -0
  22. data/Rakefile +40 -0
  23. data/app/cells/decidim/reporting_proposals/edit_note_modal/show.erb +20 -0
  24. data/app/cells/decidim/reporting_proposals/edit_note_modal_cell.rb +37 -0
  25. data/app/commands/concerns/decidim/reporting_proposals/admin/create_category_override.rb +32 -0
  26. data/app/commands/concerns/decidim/reporting_proposals/admin/update_category_override.rb +32 -0
  27. data/app/commands/concerns/decidim/reporting_proposals/create_report_override.rb +19 -0
  28. data/app/commands/decidim/reporting_proposals/admin/update_proposal.rb +46 -0
  29. data/app/commands/decidim/reporting_proposals/admin/update_proposal_note.rb +36 -0
  30. data/app/commands/decidim/reporting_proposals/create_reporting_proposal.rb +68 -0
  31. data/app/controllers/concerns/decidim/reporting_proposals/admin/categories_controller_override.rb +16 -0
  32. data/app/controllers/concerns/decidim/reporting_proposals/admin/needs_header_snippets.rb +50 -0
  33. data/app/controllers/concerns/decidim/reporting_proposals/admin/proposal_answer_templates_controller_override.rb +18 -0
  34. data/app/controllers/concerns/decidim/reporting_proposals/admin/proposal_answers_controller_override.rb +35 -0
  35. data/app/controllers/concerns/decidim/reporting_proposals/admin/proposals_controller_override.rb +28 -0
  36. data/app/controllers/concerns/decidim/reporting_proposals/needs_proposal_extra_validations_snippets.rb +51 -0
  37. data/app/controllers/concerns/decidim/reporting_proposals/proposals_controller_override.rb +113 -0
  38. data/app/controllers/decidim/reporting_proposals/admin/application_controller.rb +18 -0
  39. data/app/controllers/decidim/reporting_proposals/admin/proposal_notes_controller.rb +38 -0
  40. data/app/controllers/decidim/reporting_proposals/admin/proposals_controller.rb +61 -0
  41. data/app/controllers/decidim/reporting_proposals/application_controller.rb +11 -0
  42. data/app/controllers/decidim/reporting_proposals/geolocation_controller.rb +32 -0
  43. data/app/forms/concerns/decidim/reporting_proposals/admin/category_form_override.rb +19 -0
  44. data/app/forms/concerns/decidim/reporting_proposals/form_builder_override.rb +48 -0
  45. data/app/forms/concerns/decidim/reporting_proposals/map_builder_override.rb +60 -0
  46. data/app/forms/decidim/reporting_proposals/admin/proposal_photo_form.rb +23 -0
  47. data/app/forms/decidim/reporting_proposals/proposal_form.rb +33 -0
  48. data/app/helpers/concerns/decidim/reporting_proposals/admin/proposals_helper_override.rb +63 -0
  49. data/app/helpers/concerns/decidim/reporting_proposals/proposal_wizard_helper_override.rb +61 -0
  50. data/app/jobs/decidim/reporting_proposals/assign_proposal_valuators_job.rb +51 -0
  51. data/app/models/concerns/decidim/reporting_proposals/category_override.rb +28 -0
  52. data/app/models/concerns/decidim/reporting_proposals/participatory_space_role_config/valuator_override.rb +17 -0
  53. data/app/models/concerns/decidim/reporting_proposals/participatory_space_user_role_override.rb +25 -0
  54. data/app/models/decidim/reporting_proposals/category_valuator.rb +28 -0
  55. data/app/overrides/decidim/admin/categories/_form/add_valuators_field.html.erb.deface +3 -0
  56. data/app/overrides/decidim/admin/categories/index/add_table_column_name.html.erb.deface +3 -0
  57. data/app/overrides/decidim/admin/categories/index/add_valuators.html.erb.deface +3 -0
  58. data/app/overrides/decidim/proposals/admin/proposal_notes/_proposal_notes/add_edit_link.html.erb.deface +3 -0
  59. data/app/overrides/decidim/proposals/admin/proposal_notes/_proposal_notes/add_links_to_note.html.erb.deface +3 -0
  60. data/app/overrides/decidim/proposals/admin/proposals/_proposal-tr/add_td_hide_action.html.erb.deface +3 -0
  61. data/app/overrides/decidim/proposals/admin/proposals/_proposal-tr/add_valuators_name.html.erb.deface +9 -0
  62. data/app/overrides/decidim/proposals/admin/proposals/_proposal-tr/replace_td_title.html.erb.deface +3 -0
  63. data/app/overrides/decidim/proposals/admin/proposals/show/add_photo_management.html.erb.deface +3 -0
  64. data/app/overrides/decidim/proposals/admin/proposals/show/add_send_email_btn.html.erb.deface +3 -0
  65. data/app/overrides/decidim/proposals/admin/proposals/show/remove_photos.html.erb.deface +4 -0
  66. data/app/overrides/decidim/proposals/admin/proposals/show/replace_valuators.html.erb.deface +3 -0
  67. data/app/overrides/decidim/proposals/proposals/_proposal_similar/add_distance_badge.html.erb.deface +5 -0
  68. data/app/overrides/decidim/proposals/proposals/_wizard_header/replace_title.html.erb.deface +14 -0
  69. data/app/overrides/decidim/proposals/proposals/edit/add_user_group.html.erb.deface +3 -0
  70. data/app/overrides/decidim/proposals/proposals/edit/replace_javascript.html.erb.deface +8 -0
  71. data/app/overrides/decidim/proposals/proposals/edit/replace_partial_edit_form_fields.html.erb.deface +3 -0
  72. data/app/overrides/decidim/proposals/proposals/edit_draft/add_user_group.html.erb.deface +3 -0
  73. data/app/overrides/decidim/proposals/proposals/edit_draft/replace_javascript.html.erb.deface +8 -0
  74. data/app/overrides/decidim/proposals/proposals/edit_draft/replace_partial_edit_form_fields.html.erb.deface +3 -0
  75. data/app/overrides/decidim/proposals/proposals/index/add_additional_button.html.erb.deface +3 -0
  76. data/app/overrides/decidim/proposals/proposals/new/remove_title.html.erb.deface +1 -0
  77. data/app/overrides/decidim/proposals/proposals/new/replace_body.html.erb.deface +3 -0
  78. data/app/overrides/decidim/proposals/proposals/new/replace_javascript.html.erb.deface +8 -0
  79. data/app/overrides/decidim/proposals/proposals/show/add_additional_button.html.erb.deface +3 -0
  80. data/app/overrides/layouts/decidim/_process_header_steps/always_show_new_proposals.html.erb.deface +3 -0
  81. data/app/packs/entrypoints/decidim_reporting_proposals.js +6 -0
  82. data/app/packs/entrypoints/decidim_reporting_proposals_camera.js +2 -0
  83. data/app/packs/entrypoints/decidim_reporting_proposals_geocoding.js +2 -0
  84. data/app/packs/entrypoints/decidim_reporting_proposals_js_validations.js +1 -0
  85. data/app/packs/entrypoints/decidim_reporting_proposals_list_component_admin.js +1 -0
  86. data/app/packs/entrypoints/decidim_reporting_proposals_manage_component_admin.js +1 -0
  87. data/app/packs/images/.keep +0 -0
  88. data/app/packs/src/decidim/reporting_proposals/proposal_extra_validations.js +89 -0
  89. data/app/packs/src/decidim/reporting_proposals/proposals/add_proposal.js +66 -0
  90. data/app/packs/src/decidim/reporting_proposals/reverse_geocoding.js +54 -0
  91. data/app/packs/src/decidim/reporting_proposals/user_camera_inputs.js +49 -0
  92. data/app/packs/stylesheets/decidim/reporting_proposals/geocoding_addons.scss +34 -0
  93. data/app/packs/stylesheets/decidim/reporting_proposals/list_component_admin.scss +27 -0
  94. data/app/packs/stylesheets/decidim/reporting_proposals/manage_component_admin.scss +31 -0
  95. data/app/packs/stylesheets/decidim/reporting_proposals/proposals/add_proposal.scss +12 -0
  96. data/app/packs/stylesheets/decidim/reporting_proposals/user_camera_inputs.scss +19 -0
  97. data/app/permissions/concerns/decidim/reporting_proposals/admin/permissions_override.rb +27 -0
  98. data/app/permissions/decidim/reporting_proposals/admin/permissions.rb +79 -0
  99. data/app/permissions/decidim/reporting_proposals/permissions.rb +17 -0
  100. data/app/queries/decidim/reporting_proposals/nearby_proposals.rb +57 -0
  101. data/app/serializers/decidim/reporting_proposals/proposal_serializer_override.rb +77 -0
  102. data/app/validators/concerns/decidim/reporting_proposals/component_validator_override.rb +25 -0
  103. data/app/views/decidim/proposals/admin/proposal_notes/_editing_note.html.erb +18 -0
  104. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes_body.html.erb +6 -0
  105. data/app/views/decidim/proposals/admin/proposals/_send_email_button.html.erb +4 -0
  106. data/app/views/decidim/proposals/proposals/_additional_button.html.erb +6 -0
  107. data/app/views/decidim/reporting_proposals/admin/categories/_column_valuators.html.erb +1 -0
  108. data/app/views/decidim/reporting_proposals/admin/categories/_valuators.html.erb +1 -0
  109. data/app/views/decidim/reporting_proposals/admin/categories/_valuators_field.html.erb +7 -0
  110. data/app/views/decidim/reporting_proposals/admin/proposals/_photo_form.html.erb +24 -0
  111. data/app/views/decidim/reporting_proposals/admin/proposals/_photo_gallery.html.erb +21 -0
  112. data/app/views/decidim/reporting_proposals/admin/proposals/_photos.html.erb +14 -0
  113. data/app/views/decidim/reporting_proposals/admin/proposals/_proposal_td_hide.html.erb +20 -0
  114. data/app/views/decidim/reporting_proposals/admin/proposals/_proposal_td_title.html.erb +41 -0
  115. data/app/views/decidim/reporting_proposals/proposals/_additional_button_for_show.html.erb +6 -0
  116. data/app/views/decidim/reporting_proposals/proposals/_new_proposal_fields.html.erb +7 -0
  117. data/app/views/decidim/reporting_proposals/proposals/_reporting_proposal_fields.html.erb +113 -0
  118. data/app/views/decidim/reporting_proposals/proposals/_user_group.html.erb +5 -0
  119. data/bin/rails +6 -0
  120. data/bin/webpack-dev-server +6 -0
  121. data/codecov.yml +11 -0
  122. data/config/assets.rb +13 -0
  123. data/config/i18n-tasks.yml +13 -0
  124. data/config/locales/ca.yml +366 -0
  125. data/config/locales/de.yml +366 -0
  126. data/config/locales/en.yml +426 -0
  127. data/config/locales/es.yml +366 -0
  128. data/crowdin.yml +45 -0
  129. data/db/migrate/20221219151846_create_decidim_categories_valuators.rb +17 -0
  130. data/decidim-reporting_proposals.gemspec +34 -0
  131. data/lib/decidim/api/reporting_proposals_type.rb +10 -0
  132. data/lib/decidim/reporting_proposals/admin.rb +8 -0
  133. data/lib/decidim/reporting_proposals/admin_engine.rb +31 -0
  134. data/lib/decidim/reporting_proposals/component.rb +490 -0
  135. data/lib/decidim/reporting_proposals/config.rb +53 -0
  136. data/lib/decidim/reporting_proposals/engine.rb +96 -0
  137. data/lib/decidim/reporting_proposals/test/factories.rb +13 -0
  138. data/lib/decidim/reporting_proposals/version.rb +15 -0
  139. data/lib/decidim/reporting_proposals.rb +13 -0
  140. data/package-lock.json +7844 -0
  141. data/package.json +195 -0
  142. metadata +319 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ # Exposes the proposal resource so users can view and create them.
6
+ module NeedsProposalExtraValidationsSnippets
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ helper_method :snippets
11
+ end
12
+
13
+ def snippets
14
+ @snippets ||= Decidim::Snippets.new
15
+
16
+ unless @snippets.any?(:reporting_proposals_js_validations)
17
+ @snippets.add(:reporting_proposals_js_validations, ActionController::Base.helpers.javascript_pack_tag("decidim_reporting_proposals_js_validations"))
18
+ @snippets.add(:reporting_proposals_js_validations, rules_tag)
19
+ @snippets.add(:head, @snippets.for(:reporting_proposals_js_validations))
20
+ end
21
+ @snippets
22
+ end
23
+
24
+ def rules_tag
25
+ content_tag(:script, "Decidim.ProposalRules = #{rules.to_json};".html_safe)
26
+ end
27
+
28
+ # caps rules are not explicitly used in the JS validations
29
+ def rules
30
+ model = Decidim::Proposals::Proposal.new(title: "title", body: "body")
31
+ etiquette_validator = EtiquetteValidator.new(attributes: [:title, :body])
32
+ etiquette_validator.validate(model)
33
+ {
34
+ genericError: I18n.t("decidim.forms.errors.error"),
35
+ title: {
36
+ caps: {
37
+ enabled: model.errors.details[:title].to_s.include?(":must_start_with_caps"),
38
+ error: I18n.t("errors.messages.must_start_with_caps")
39
+ }
40
+ },
41
+ body: {
42
+ caps: {
43
+ enabled: model.errors.details[:body].to_s.include?(":must_start_with_caps"),
44
+ error: I18n.t("errors.messages.must_start_with_caps")
45
+ }
46
+ }
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ # Exposes the proposal resource so users can view and create them.
6
+ module ProposalsControllerOverride
7
+ extend ActiveSupport::Concern
8
+ include NeedsProposalExtraValidationsSnippets
9
+
10
+ included do
11
+ helper_method :reporting_proposal?, :geocoding_comparison?
12
+
13
+ def new
14
+ enforce_permission_to :create, :proposal
15
+ @step = :step_1
16
+ if proposal_draft.present?
17
+ redirect_to edit_draft_proposal_path(proposal_draft, component_id: proposal_draft.component.id, question_slug: proposal_draft.component.participatory_space.slug)
18
+ else
19
+ @form = form(new_proposal_form).from_params(body: translated_proposal_body_template)
20
+ end
21
+ end
22
+
23
+ def create
24
+ enforce_permission_to :create, :proposal
25
+ @step = :step_1
26
+ @form = form(new_proposal_form).from_params(proposal_creation_params)
27
+
28
+ create_proposal_command.call(@form, current_user) do
29
+ on(:ok) do |proposal|
30
+ flash[:notice] = I18n.t("proposals.create.success", scope: "decidim")
31
+
32
+ redirect_to "#{Decidim::ResourceLocatorPresenter.new(proposal).path}/compare"
33
+ end
34
+
35
+ on(:invalid) do
36
+ flash.now[:alert] = I18n.t("proposals.create.error", scope: "decidim")
37
+ render :new
38
+ end
39
+ end
40
+ end
41
+
42
+ # change comparison class if geocoding comparison is enabled
43
+ def compare
44
+ enforce_permission_to :edit, :proposal, proposal: @proposal
45
+ @step = :step_2
46
+ klass = if geocoding_comparison?
47
+ Decidim::ReportingProposals::NearbyProposals
48
+ else
49
+ Decidim::Proposals::SimilarProposals
50
+ end
51
+ @similar_proposals ||= klass
52
+ .for(current_component, @proposal)
53
+ .all
54
+
55
+ if @similar_proposals.blank?
56
+ flash[:notice] = I18n.t("proposals.proposals.compare.no_similars_found", scope: "decidim")
57
+ redirect_to "#{Decidim::ResourceLocatorPresenter.new(@proposal).path}/complete"
58
+ end
59
+ end
60
+
61
+ # disable this step for reporting proposals
62
+ def complete
63
+ enforce_permission_to :edit, :proposal, proposal: @proposal
64
+ @step = :step_3
65
+
66
+ @form = form_proposal_model
67
+
68
+ @form.attachment = form_attachment_new
69
+
70
+ redirect_to "#{Decidim::ResourceLocatorPresenter.new(@proposal).path}/preview" if reporting_proposal?
71
+ end
72
+
73
+ def edit_draft
74
+ @step = reporting_proposal? ? :step_1 : :step_3
75
+ enforce_permission_to :edit, :proposal, proposal: @proposal
76
+ end
77
+
78
+ private
79
+
80
+ def form_proposal_params
81
+ form(edit_proposal_form).from_params(params)
82
+ end
83
+
84
+ def form_proposal_model
85
+ form(edit_proposal_form).from_model(@proposal)
86
+ end
87
+
88
+ def edit_proposal_form
89
+ reporting_proposal? ? Decidim::ReportingProposals::ProposalForm : Decidim::Proposals::ProposalForm
90
+ end
91
+
92
+ def new_proposal_form
93
+ reporting_proposal? ? Decidim::ReportingProposals::ProposalForm : Decidim::Proposals::ProposalWizardCreateStepForm
94
+ end
95
+
96
+ def create_proposal_command
97
+ reporting_proposal? ? CreateReportingProposal : Decidim::Proposals::CreateProposal
98
+ end
99
+
100
+ def reporting_proposal?
101
+ component = current_component || @form.component
102
+ component.manifest_name == "reporting_proposals"
103
+ end
104
+
105
+ def geocoding_comparison?
106
+ if Decidim::Map.configured? && component_settings.geocoding_enabled? && component_settings.geocoding_comparison_enabled?
107
+ @proposal ? @proposal.geocoded? : true
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ #
9
+ # Note that it inherits from `Decidim::Admin::Components::BaseController`, which
10
+ # override its layout and provide all kinds of useful methods.
11
+ class ApplicationController < Decidim::Admin::ApplicationController
12
+ def permission_class_chain
13
+ [::Decidim::ReportingProposals::Admin::Permissions] + super
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ class ProposalNotesController < Admin::ApplicationController
7
+ helper_method :note
8
+
9
+ def edit
10
+ enforce_permission_to :edit_note, :proposal_note, proposal_note: note
11
+ @notes_form = form(Decidim::Proposals::Admin::ProposalNoteForm).from_model(note)
12
+ end
13
+
14
+ def update
15
+ enforce_permission_to :edit_note, :proposal_note, proposal_note: note
16
+ @notes_form = form(Decidim::Proposals::Admin::ProposalNoteForm).from_params(params)
17
+
18
+ Decidim::ReportingProposals::Admin::UpdateProposalNote.call(@notes_form, note) do
19
+ on(:ok) do
20
+ flash[:notice] = I18n.t("proposal_notes.update.success", scope: "decidim.reporting_proposals.admin")
21
+ end
22
+
23
+ on(:invalid) do
24
+ flash[:alert] = I18n.t("proposal_notes.update.invalid", scope: "decidim.reporting_proposals.admin")
25
+ end
26
+ redirect_back(fallback_location: decidim_admin.root_path)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def note
33
+ @note ||= Decidim::Proposals::ProposalNote.find(params[:id])
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ class ProposalsController < Admin::ApplicationController
7
+ def hide_proposal
8
+ enforce_permission_to :hide_proposal, :proposals, proposal: proposal
9
+
10
+ Decidim::Admin::HideResource.call(proposal, current_user) do
11
+ on(:ok) do
12
+ flash[:notice] = I18n.t("reportable.hide.success", scope: "decidim.moderations.admin")
13
+ end
14
+
15
+ on(:invalid) do
16
+ flash.now[:alert] = I18n.t("reportable.hide.invalid", scope: "decidim.moderations.admin")
17
+ end
18
+ end
19
+ redirect_back(fallback_location: decidim_admin.root_path)
20
+ end
21
+
22
+ def add_photos
23
+ enforce_permission_to :edit_photos, :proposals, proposal: proposal
24
+
25
+ @photo_form = form(Decidim::ReportingProposals::Admin::ProposalPhotoForm).from_params(params)
26
+
27
+ Decidim::ReportingProposals::Admin::UpdateProposal.call(@photo_form, proposal) do
28
+ on(:ok) do |_proposal|
29
+ flash[:notice] = t("proposals.update.success", scope: "decidim")
30
+ end
31
+
32
+ on(:invalid) do
33
+ flash[:alert] = t("proposals.update.error", scope: "decidim")
34
+ end
35
+ end
36
+ redirect_to Decidim::ResourceLocatorPresenter.new(proposal).show
37
+ end
38
+
39
+ def remove_photo
40
+ enforce_permission_to :edit_photos, :proposals, proposal: proposal
41
+
42
+ attachment = proposal.attachments.find_by(id: params[:photo_id])
43
+ if attachment.try(:photo?)
44
+ attachment.destroy!
45
+ flash[:notice] = t("proposals.update.success", scope: "decidim")
46
+ else
47
+ flash[:alert] = t("proposals.update.error", scope: "decidim")
48
+ end
49
+
50
+ redirect_to Decidim::ResourceLocatorPresenter.new(proposal).show
51
+ end
52
+
53
+ private
54
+
55
+ def proposal
56
+ @proposal ||= Decidim::Proposals::Proposal.find(params[:id])
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ class ApplicationController < Decidim::ApplicationController
6
+ def permission_class_chain
7
+ [::Decidim::ReportingProposals::Permissions] + super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ class GeolocationController < Decidim::ReportingProposals::ApplicationController
6
+ # overwrite original rescue_from to ensure we print messages from ajax methods (update)
7
+ rescue_from Decidim::ActionForbidden, with: :ajax_user_has_no_permission
8
+
9
+ def locate
10
+ enforce_permission_to :locate, :geolocation
11
+
12
+ unless Decidim::Map.configured?
13
+ return render(json: { message: I18n.t("unconfigured", scope: "decidim.application.geocoding"), found: false }, status: :unprocessable_entity)
14
+ end
15
+
16
+ geocoder = Decidim::Map.utility(:geocoding, organization: current_organization)
17
+ address = geocoder.address([params[:latitude], params[:longitude]])
18
+ render json: { address: address, found: address.present? }
19
+ end
20
+
21
+ private
22
+
23
+ # Rescue ajax calls and print the update.js view which prints the info on the message ajax form
24
+ # Only if the request is AJAX, otherwise behave as Decidim standards
25
+ def ajax_user_has_no_permission
26
+ return user_has_no_permission unless request.xhr?
27
+
28
+ render json: { message: I18n.t("actions.unauthorized", scope: "decidim.core") }, status: :unprocessable_entity
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ module CategoryFormOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ attribute :valuator_ids, Array[Integer]
11
+
12
+ def map_model(model)
13
+ self.valuator_ids = model.category_valuators.pluck(:valuator_role_id)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module FormBuilderOverride
6
+ extend ActiveSupport::Concern
7
+ include Decidim::LayoutHelper
8
+
9
+ # These methods are used in deeper levels and might not be available in this context when this is called, thus the delegation
10
+ delegate :asset_pack_path, to: :@template
11
+
12
+ included do
13
+ def file_field(object_name, method, options = {})
14
+ return super(object_name, method, options) unless use_camera_button?(object_name)
15
+
16
+ unless @template.snippets.any?(:reporting_proposals_camera_addons)
17
+ @template.snippets.add(:reporting_proposals_camera_addons, @template.javascript_pack_tag("decidim_reporting_proposals_camera"))
18
+ @template.snippets.add(:reporting_proposals_camera_addons, @template.stylesheet_pack_tag("decidim_reporting_proposals_camera"))
19
+
20
+ # This will display the snippets in the <head> part of the page.
21
+ @template.snippets.add(:head, @template.snippets.for(:reporting_proposals_camera_addons))
22
+ end
23
+
24
+ content_tag(:div, class: "input-group") do
25
+ super(object_name, method, options) +
26
+ content_tag(:div, class: "input-group-button") do
27
+ content_tag(:button, class: "button secondary user-device-camera", type: "button", data: { input: "#{object_name}_#{method}" }) do
28
+ icon("camera-slr", role: "img", "aria-hidden": true) + " #{I18n.t("use_my_camera", scope: "decidim.reporting_proposals.forms")}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def use_camera_button?(object_name)
37
+ return unless @template.respond_to?(:current_component)
38
+
39
+ return unless Decidim::ReportingProposals.use_camera_button.include?(@template.current_component.manifest_name.to_sym)
40
+
41
+ return object_name == :add_photos unless Decidim::ReportingProposals.camera_button_on_attachments
42
+
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module MapBuilderOverride
6
+ extend ActiveSupport::Concern
7
+ include Decidim::LayoutHelper
8
+
9
+ # These methods are used in deeper levels and might not be available in this context when this is called, thus the delegation
10
+ delegate :content_tag, :asset_pack_path, to: :template
11
+
12
+ included do
13
+ alias_method :original_geocoding_field, :geocoding_field
14
+
15
+ def geocoding_field(object_name, method, options = {})
16
+ return original_geocoding_field(object_name, method, options) unless show_my_location_button?
17
+
18
+ unless template.snippets.any?(:reporting_proposals_geocoding_addons)
19
+ template.snippets.add(:reporting_proposals_geocoding_addons, template.javascript_pack_tag("decidim_reporting_proposals_geocoding"))
20
+ template.snippets.add(:reporting_proposals_geocoding_addons, template.stylesheet_pack_tag("decidim_reporting_proposals_geocoding"))
21
+
22
+ # This will display the snippets in the <head> part of the page.
23
+ template.snippets.add(:head, template.snippets.for(:reporting_proposals_geocoding_addons))
24
+ end
25
+
26
+ options[:autocomplete] ||= "off"
27
+ options[:class] ||= "input-group-field"
28
+
29
+ template.content_tag(:div, class: "input-group") do
30
+ template.text_field(
31
+ object_name,
32
+ method,
33
+ options.merge("data-decidim-geocoding" => view_options.to_json)
34
+ ) +
35
+ template.content_tag(:div, class: "input-group-button user-device-location") do
36
+ template.content_tag(:button, class: "button secondary", type: "button", data: {
37
+ input: "#{object_name}_#{method}",
38
+ latitude: "#{object_name}_latitude",
39
+ longitude: "#{object_name}_longitude",
40
+ error_no_location: I18n.t("errors.no_device_location", scope: "decidim.reporting_proposals.forms"),
41
+ error_unsupported: I18n.t("errors.device_not_supported", scope: "decidim.reporting_proposals.forms"),
42
+ url: Decidim::ReportingProposals::Engine.routes.url_helpers.locate_path
43
+ }) do
44
+ icon("location", role: "img", "aria-hidden": true) + " #{I18n.t("use_my_location", scope: "decidim.reporting_proposals.forms")}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def show_my_location_button?
53
+ return unless template.respond_to?(:current_component)
54
+
55
+ Decidim::ReportingProposals.show_my_location_button.include?(template.current_component.manifest_name.to_sym)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ class ProposalPhotoForm < Decidim::Form
7
+ include Decidim::AttachmentAttributes
8
+ attribute :attachment, AttachmentForm
9
+ attachments_attribute :photos
10
+
11
+ validates :add_photos, presence: true
12
+
13
+ def proposal
14
+ @proposal ||= Decidim::Proposals::Proposal.find(id)
15
+ end
16
+
17
+ def current_component
18
+ @current_component ||= proposal&.component
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ class ProposalForm < Decidim::Proposals::ProposalForm
6
+ attribute :address, String
7
+ attribute :has_no_address, Boolean
8
+ attribute :has_no_image, Boolean
9
+
10
+ validates :add_photos, presence: true, if: ->(form) { form.has_camera? && form.photos.blank? }
11
+
12
+ # Set the has no address
13
+ def map_model(model)
14
+ super(model)
15
+
16
+ self.has_no_address = true if model.address.blank?
17
+ self.has_no_image = true if model.photo.blank?
18
+ end
19
+
20
+ def has_address?
21
+ return if has_no_address
22
+
23
+ geocoding_enabled?
24
+ end
25
+
26
+ def has_camera?
27
+ return if has_no_image
28
+
29
+ current_component.settings.attachments_allowed?
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ # Exposes the proposal resource so users can view and create them.
7
+ module ProposalsHelperOverride
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ # Helpers for overdue proposals
12
+ include ActionView::Helpers::DateHelper
13
+
14
+ def unanswered_proposals_overdue?(proposal)
15
+ grace_period = days_unanswered(proposal)
16
+ !grace_period.zero? &&
17
+ proposal.state.blank? && (Time.current - grace_period.days).to_date > proposal.published_at
18
+ end
19
+
20
+ def evaluating_proposals_overdue?(proposal)
21
+ grace_period = days_evaluating(proposal)
22
+ !grace_period.zero? &&
23
+ proposal.evaluating? && proposal.answered? &&
24
+ (Time.current - grace_period.days).to_date > proposal.answered_at
25
+ end
26
+
27
+ def grace_period_unanswered?(proposal)
28
+ !proposal.answered? && Time.current < last_day_to_answer(proposal)
29
+ end
30
+
31
+ def days_unanswered(proposal)
32
+ return proposal.component.settings.unanswered_proposals_overdue.to_i if proposal.component&.settings
33
+
34
+ Decidim::ReportingProposals.unanswered_proposals_overdue.to_i
35
+ end
36
+
37
+ def days_evaluating(proposal)
38
+ return proposal.component.settings.evaluating_proposals_overdue.to_i if proposal.component&.settings
39
+
40
+ Decidim::ReportingProposals.evaluating_proposals_overdue.to_i
41
+ end
42
+
43
+ def grace_period_evaluating?(proposal)
44
+ proposal.evaluating? && Time.current < last_day_to_evaluate(proposal)
45
+ end
46
+
47
+ def last_day_to_answer(proposal)
48
+ (proposal.published_at + days_unanswered(proposal).days).to_date
49
+ end
50
+
51
+ def last_day_to_evaluate(proposal)
52
+ (proposal.answered_at + days_evaluating(proposal).days).to_date if proposal.answered?
53
+ end
54
+
55
+ def time_elapsed_to_answer(proposal)
56
+ distance_of_time_in_words(proposal.answered_at, proposal.created_at,
57
+ scope: "decidim.reporting_proposals.admin.time_elapsed.datetime.distance_in_words")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ # Exposes the proposal resource so users can view and create them.
6
+ module ProposalWizardHelperOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ def proposal_wizard_stepper(current_step)
11
+ steps = %(
12
+ #{proposal_wizard_stepper_step(:step_1, current_step)}
13
+ #{proposal_wizard_stepper_step(:step_2, current_step)}
14
+ )
15
+ steps = %(#{steps} #{proposal_wizard_stepper_step(:step_3, current_step)}) unless reporting_proposals_component?
16
+ steps = %(#{steps} #{proposal_wizard_stepper_step(:step_4, current_step)})
17
+
18
+ content_tag :ol, class: "wizard__steps" do
19
+ steps.html_safe
20
+ end
21
+ end
22
+
23
+ def proposal_wizard_current_step_of(step)
24
+ current_step_num = proposal_wizard_step_number(step)
25
+ current_step_num = 3 if current_step_num == 4 && reporting_proposals_component?
26
+ see_steps = content_tag(:span, class: "hide-for-large") do
27
+ concat " ("
28
+ concat content_tag :a, t(:"decidim.proposals.proposals.wizard_steps.see_steps"), "data-toggle": "steps"
29
+ concat ")"
30
+ end
31
+ content_tag :span, class: "text-small" do
32
+ concat t(:"decidim.proposals.proposals.wizard_steps.step_of", current_step_num: current_step_num, total_steps: total_steps)
33
+ concat see_steps
34
+ end
35
+ end
36
+
37
+ # rubocop:disable Rails/HelperInstanceVariable:
38
+ def distance(meters = nil)
39
+ meters = @proposal.component.settings.geocoding_comparison_radius.to_f if meters.nil?
40
+
41
+ return "#{meters.round}m" if meters < 1000
42
+
43
+ "#{(meters / 1000).round}Km"
44
+ end
45
+
46
+ private
47
+
48
+ def total_steps
49
+ reporting_proposals_component? ? 3 : 4
50
+ end
51
+
52
+ def reporting_proposals_component?
53
+ return unless @form&.component&.manifest_name
54
+
55
+ @form.component.manifest_name == "reporting_proposals"
56
+ end
57
+ # rubocop:enable Rails/HelperInstanceVariable:
58
+ end
59
+ end
60
+ end
61
+ end