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,27 @@
1
+ .table__title-block {
2
+ display: flex;
3
+ align-items: flex-start;
4
+ }
5
+
6
+ .table__title-icon {
7
+ width: 20px;
8
+ padding: 3px;
9
+ margin-right: 5px;
10
+ float: left;
11
+ }
12
+
13
+ .table__list-title {
14
+ display: flex;
15
+ flex-direction: column;
16
+ align-items: start;
17
+ }
18
+
19
+ .table__list-title a {
20
+ text-align: start;
21
+ }
22
+
23
+ .help-text-overdue {
24
+ font-style: italic;
25
+ font-size: .8125rem;
26
+ text-align: start;
27
+ }
@@ -0,0 +1,31 @@
1
+ .global-settings,
2
+ .step-settings {
3
+ .readonly_container {
4
+ display: none;
5
+ }
6
+ }
7
+
8
+ .proposal-photo.gallery__item {
9
+ position: relative;
10
+ margin: 0 0 1rem;
11
+ border: 1px solid rgba(0, 0, 0, .2);
12
+ border-radius: 0;
13
+ background-color: white;
14
+ color: #202734;
15
+ }
16
+
17
+ .proposal-photo .thumbnail {
18
+ margin-bottom: 0;
19
+ }
20
+
21
+ .delete-proposal__button {
22
+ position: absolute;
23
+ right: 0;
24
+ top: 7px;
25
+ width: 2rem;
26
+ height: 2rem;
27
+ background: rgba(var(--primary-rgb), .8);
28
+ color: #fff;
29
+ border-radius: 50%;
30
+ text-align: center;
31
+ }
@@ -0,0 +1,12 @@
1
+ .has_no_address {
2
+ text-align: right;
3
+ }
4
+
5
+ .editor {
6
+ .form-error {
7
+ margin: -1rem 0 1rem;
8
+ font-size: .75rem;
9
+ font-weight: 600;
10
+ color: var(--alert);
11
+ }
12
+ }
@@ -0,0 +1,19 @@
1
+ button.user-device-camera {
2
+ border-radius: 4px !important;
3
+ max-height: 3rem;
4
+ }
5
+
6
+ .has_no_image {
7
+ margin-top: -1rem;
8
+ text-align: right;
9
+ }
10
+
11
+ @media only screen and (max-width: 596px) {
12
+ .gallery__container .input-group .input-group-button {
13
+ display: block;
14
+ }
15
+
16
+ .has_no_image {
17
+ margin-top: -.5rem;
18
+ }
19
+ }
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ module PermissionsOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ private
11
+
12
+ def valuator_can_unassign_valuator_from_proposals?
13
+ can_unassign_valuator_from_proposals? if user == context.fetch(:valuator, nil)
14
+
15
+ can_add_valuators?
16
+ end
17
+
18
+ def can_add_valuators?
19
+ return unless permission_action.action == :assign_to_valuator && permission_action.subject == :proposals
20
+
21
+ toggle_allow(Decidim::ReportingProposals.valuators_assign_other_valuators)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module Admin
6
+ class Permissions < Decidim::Proposals::Admin::Permissions
7
+ def permissions
8
+ return permission_action if permission_action.scope != :admin
9
+ return permission_action unless user
10
+ return permission_action if current_organization != user.organization
11
+
12
+ hide_content_action?
13
+ edit_photos_action?
14
+ edit_proposal_note?
15
+ super
16
+ end
17
+
18
+ private
19
+
20
+ def current_organization
21
+ context[:proposal].try(:organization) || context[:current_organization]
22
+ end
23
+
24
+ def component_settings
25
+ context[:component_settings] || component.try(:settings)
26
+ end
27
+
28
+ def component
29
+ context[:proposal].try(:component) || context[:current_component]
30
+ end
31
+
32
+ def user_author_note?
33
+ context[:proposal_note].try(:author) == user
34
+ end
35
+
36
+ def hide_content_action?
37
+ return unless permission_action.action == :hide_proposal && permission_action.subject == :proposals
38
+
39
+ toggle_allow((admin_hide_proposals_enabled? && user_allowed_or_assigned?) || user_administrator?)
40
+ end
41
+
42
+ def edit_photos_action?
43
+ return unless permission_action.action == :edit_photos && permission_action.subject == :proposals
44
+
45
+ toggle_allow(admin_proposal_photo_editing_enabled? && (user_allowed_or_assigned? || user_administrator?))
46
+ end
47
+
48
+ def edit_proposal_note?
49
+ return unless permission_action.action == :edit_note && permission_action.subject == :proposal_note
50
+
51
+ toggle_allow(user_author_note?)
52
+ end
53
+
54
+ def admin_proposal_photo_editing_enabled?
55
+ Decidim::ReportingProposals.allow_proposal_photo_editing.present? &&
56
+ component_settings.try(:proposal_photo_editing_enabled)
57
+ end
58
+
59
+ def admin_hide_proposals_enabled?
60
+ Decidim::ReportingProposals.allow_admins_to_hide_proposals.present?
61
+ end
62
+
63
+ def user_allowed_or_assigned?
64
+ user.admin? || (user_is_valuator? && valuator_assigned_to_proposal?)
65
+ end
66
+
67
+ def user_administrator?
68
+ process = Decidim::ParticipatoryProcess.where(organization: context[:proposal].try(:organization))
69
+
70
+ admin = Decidim::User.where(id: Decidim::ParticipatoryProcessUserRole
71
+ .where(participatory_process: process, role: :admin)
72
+ .select(user.id))
73
+
74
+ admin.exists? ? user : nil
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ class Permissions < Decidim::DefaultPermissions
6
+ def permissions
7
+ return permission_action unless user
8
+
9
+ return Decidim::ReportingProposals::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
10
+
11
+ allow! if permission_action.action == :locate && permission_action.subject == :geolocation
12
+
13
+ permission_action
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ # Class used to retrieve similar proposals.
6
+ class NearbyProposals < Rectify::Query
7
+ # Syntactic sugar to initialize the class and return the queried objects.
8
+ #
9
+ # components - Decidim::CurrentComponent
10
+ # proposal - Decidim::Proposals::Proposal
11
+ def self.for(components, proposal)
12
+ new(components, proposal).query
13
+ end
14
+
15
+ # Initializes the class.
16
+ #
17
+ # components - Decidim::CurrentComponent
18
+ # proposal - Decidim::Proposals::Proposal
19
+ def initialize(components, proposal)
20
+ @components = components
21
+ @proposal = proposal
22
+ @radius = proposal.component.settings.geocoding_comparison_radius
23
+ @newer_than = proposal.component.settings.geocoding_comparison_newer_than
24
+ end
25
+
26
+ # Retrieves similar proposals by distance
27
+ def query
28
+ return Decidim::Proposals::Proposal.none if query_ids.blank?
29
+
30
+ Decidim::Proposals::Proposal
31
+ .where(id: query_ids)
32
+ .order([Arel.sql("array_position(ARRAY[?], id)"), query_ids])
33
+ end
34
+
35
+ private
36
+
37
+ # we won't return directly this query due a problem with the method "count" in the geocoder gem
38
+ # see https://github.com/alexreisner/geocoder#note-on-rails-41-and-greater
39
+ def query_ids
40
+ base_query
41
+ .near([@proposal.latitude, @proposal.longitude], @radius.to_f / 1000, units: :km)
42
+ .limit(Decidim::Proposals.similarity_limit).map(&:id)
43
+ end
44
+
45
+ def base_query
46
+ @base_query = Decidim::Proposals::Proposal
47
+ .where(component: @components)
48
+ .published
49
+ .not_hidden
50
+
51
+ return @base_query if @newer_than.zero?
52
+
53
+ @base_query.where("published_at > ?", @newer_than.days.ago)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ module ProposalSerializerOverride
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActionView::Helpers::DateHelper
10
+ include Decidim::Proposals::Admin::ProposalsHelper
11
+
12
+ def serialize
13
+ {
14
+ id: proposal.id,
15
+ category: {
16
+ id: proposal.category.try(:id),
17
+ name: proposal.category.try(:name) || empty_translatable
18
+ },
19
+ scope: {
20
+ id: proposal.scope.try(:id),
21
+ name: proposal.scope.try(:name) || empty_translatable
22
+ },
23
+ participatory_space: {
24
+ id: proposal.participatory_space.id,
25
+ url: Decidim::ResourceLocatorPresenter.new(proposal.participatory_space).url
26
+ },
27
+ component: { id: component.id },
28
+ title: proposal.title,
29
+ body: proposal.body,
30
+ address: proposal.address,
31
+ latitude: proposal.latitude,
32
+ longitude: proposal.longitude,
33
+ state: proposal.state.to_s,
34
+ reference: proposal.reference,
35
+ answer: ensure_translatable(proposal.answer),
36
+ answer_time: answer_time,
37
+ supports: proposal.proposal_votes_count,
38
+ endorsements: {
39
+ total_count: proposal.endorsements.size,
40
+ user_endorsements: user_endorsements
41
+ },
42
+ comments: proposal.comments_count,
43
+ attachments: proposal.attachments.size,
44
+ followers: proposal.follows.size,
45
+ published_at: proposal.published_at,
46
+ url: url,
47
+ meeting_urls: meetings,
48
+ related_proposals: related_proposals,
49
+ is_amend: proposal.emendation?,
50
+ original_proposal: {
51
+ title: proposal&.amendable&.title,
52
+ url: original_proposal_url
53
+ }
54
+ }
55
+ end
56
+
57
+ def answer_time
58
+ if unanswered_proposals_overdue?(proposal)
59
+ time_ago_in_words(last_day_to_answer(proposal),
60
+ scope: "decidim.reporting_proposals.admin.answer_overdue.datetime.distance_in_words")
61
+ elsif evaluating_proposals_overdue?(proposal)
62
+ time_ago_in_words(last_day_to_evaluate(proposal),
63
+ scope: "decidim.reporting_proposals.admin.answer_overdue.datetime.distance_in_words")
64
+ elsif grace_period_unanswered?(proposal)
65
+ time_ago_in_words(last_day_to_answer(proposal),
66
+ scope: "decidim.reporting_proposals.admin.answer_pending.datetime.distance_in_words")
67
+ elsif grace_period_evaluating?(proposal)
68
+ time_ago_in_words(last_day_to_evaluate(proposal),
69
+ scope: "decidim.reporting_proposals.admin.evaluate_pending.datetime.distance_in_words")
70
+ elsif proposal.accepted? || proposal.rejected?
71
+ "#{I18n.t("decidim.reporting_proposals.admin.resolution_time")}: #{time_elapsed_to_answer(proposal)}"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,25 @@
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 ComponentValidatorOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # The actual validator method. It is called when ActiveRecord iterates
11
+ # over all the validators.
12
+ def validate_each(record, attribute, component)
13
+ unless component
14
+ record.errors[attribute] << :blank
15
+ return
16
+ end
17
+ manifests = [options[:manifest].to_s]
18
+ manifests << "reporting_proposals" if manifests.first == "proposals"
19
+
20
+ record.errors[attribute] << :invalid unless component.manifest_name.to_s.in?(manifests)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ <% if note.author == current_user %>
2
+ <%= cell("decidim/reporting_proposals/edit_note_modal", note, modal_id: "editNoteModal#{note.id}", note: note, proposal: proposal) %>
3
+ <button type="button" class="link-alt m-xs"
4
+ data-open="<%= current_user.present? ? "editNoteModal#{note.id}" : "loginModal" %>"
5
+ title="<%= t("modal.title", scope: "decidim.reporting_proposals.admin.proposal_notes") %>"
6
+ aria-controls="<%= current_user.present? ? "editNoteModal#{note.id}" : "loginModal" %>"
7
+ aria-haspopup="true"
8
+ tabindex="0"
9
+ style="color:#97a2b2; cursor: pointer">
10
+ <%= icon "pencil", aria_hidden: true, class: "action-icon--preview", role: "img", "aria-hidden": true %>
11
+ </button>
12
+ <% end %>
13
+ <% if note.created_at != note.updated_at %>
14
+ <span class="text-muted">
15
+ <%= t("update.edited", scope: "decidim.reporting_proposals.admin.proposal_notes") %>
16
+ <%= l note.updated_at, format: :short %>
17
+ </span>
18
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <% note.body.split("\n").each_with_index do |paragraph, index| %>
2
+ <%= Decidim::ContentRenderers::LinkRenderer.new(paragraph.gsub(URI.regexp, '<a href="\0" target="_blank">\0</a>')).render.html_safe %>
3
+ <% unless index == note.body.split("\n").size - 1 %>
4
+ <br>
5
+ <% end %>
6
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <p>
2
+ <%= link_to t(".send_mail"), "mailto:#{presented_author.email}?subject=#{translated_attribute(proposal.title)}&body=#{resource_locator(proposal).url}",
3
+ class: "button tiny", target: "_blank" %>
4
+ </p>
@@ -0,0 +1,6 @@
1
+ <% if current_component.settings.try(:additional_buttons_show) &&
2
+ current_component.settings.additional_button_text.present? &&
3
+ current_component.settings.additional_button_link.present? %>
4
+ <%= link_to translated_attribute(component_settings.additional_button_text),
5
+ component_settings.additional_button_link, class: "title-action__action button small ml-s" %>
6
+ <% end %>
@@ -0,0 +1 @@
1
+ <th><%= t("valuators_column", scope: "decidim.reporting_proposals.admin.categories.index") %></th>
@@ -0,0 +1 @@
1
+ <td><%= category.valuator_names.join(", ") %></td>
@@ -0,0 +1,7 @@
1
+
2
+ <div class="row column">
3
+ <%= form.select :valuator_ids,
4
+ find_valuators_for_select(current_participatory_space),
5
+ { help_text: t("form.valuators_help", scope: "decidim.reporting_proposals.admin.categories") },
6
+ { multiple: true } %>
7
+ </div>
@@ -0,0 +1,24 @@
1
+ <%= decidim_form_for(@photo_form, url: decidim_admin_reporting_proposals.add_photos_path(id: proposal.id),
2
+ method: :post,
3
+ html: { multipart: true, class: "form edit_proposal proposal_form_admin" }) do |f| %>
4
+ <div class="row column gallery__container">
5
+ <fieldset>
6
+ <legend><%= t(".gallery_legend") %></legend>
7
+ <% if proposal.photos.any? %>
8
+ <div class="gallery row">
9
+ <% proposal.photos.each do |photo| %>
10
+ <div class="callout gallery__item">
11
+ <%= f.hidden_field :photos, multiple: true, value: photo.id, id: "photo-#{photo.id}" %>
12
+ </div>
13
+ <% end %>
14
+ </div>
15
+ <% end %>
16
+ <div class="row column">
17
+ <%= f.file_field :add_photos, multiple: true %>
18
+ </div>
19
+ <div class="actions">
20
+ <%= f.submit t(".action") %>
21
+ </div>
22
+ </fieldset>
23
+ </div>
24
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <% if proposal.photos.any? %>
2
+ <div class="row column">
3
+ <strong><%= t(".photos") %>:</strong>
4
+ <div id="photos" class="gallery row flex--fsc proposal-<%= proposal.id %>">
5
+ <% proposal.photos.each do |photo| %>
6
+ <div class="proposal-photo gallery__item" id="photo-<%= photo.id %>">
7
+ <%= link_to photo.big_url, target: "_blank", rel: "noopener" do %>
8
+ <%= image_tag photo.thumbnail_url, class: "thumbnail", alt: strip_tags(translated_attribute(photo.title)) %>
9
+ <% if admin_allowed_to? :edit_photos, :proposals, extra_context = { proposal: proposal }, chain = [::Decidim::ReportingProposals::Admin::Permissions] %>
10
+ <%= icon_link_to "x",
11
+ decidim_admin_reporting_proposals.remove_photo_path(id: proposal.id, photo_id: photo.id),
12
+ t(".delete_image"),
13
+ method: :delete,
14
+ class: "delete-proposal__button", style: "color:white;" %>
15
+ <% end %>
16
+ <% end %>
17
+ </div>
18
+ <% end %>
19
+ </div>
20
+ </div>
21
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <% if proposal.photos.any? || component_settings.attachments_allowed? %>
2
+ <div class="card">
3
+ <div class="card-divider">
4
+ <h2 class="card-title"><%= t(".title") %></h2>
5
+ </div>
6
+ <div class="card-section">
7
+ <%= render partial: "decidim/reporting_proposals/admin/proposals/photo_gallery" %>
8
+
9
+ <% if admin_allowed_to? :edit_photos, :proposals, extra_context = { proposal: proposal }, chain = [::Decidim::ReportingProposals::Admin::Permissions] %>
10
+ <%= render partial: "decidim/reporting_proposals/admin/proposals/photo_form" %>
11
+ <% end %>
12
+ </div>
13
+ </div>
14
+ <% end %>
@@ -0,0 +1,20 @@
1
+ <% if admin_allowed_to?(:hide_proposal, :proposals, extra_context = { proposal: proposal }, chain = [::Decidim::ReportingProposals::Admin::Permissions]) %>
2
+ <% if proposal.reported? %>
3
+ <%= icon_link_to "trash", decidim_admin_reporting_proposals.hide_proposal_path(id: proposal),
4
+ t("actions.hide", scope: "decidim.moderations"), method: :put, class: "action-icon" %>
5
+ <% else %>
6
+ <%= cell("decidim/flag_modal", proposal, modal_id: "flagModal#{proposal.id}") %>
7
+ <button type="button" class="link-alt"
8
+ data-open="<%= current_user.present? ? "flagModal#{proposal.id}" : "loginModal" %>"
9
+ title="<%= t("report", scope: "decidim.proposals.proposals.show") %>"
10
+ aria-controls="<%= current_user.present? ? "flagModal#{proposal.id}" : "loginModal" %>"
11
+ aria-haspopup="true"
12
+ tabindex="0"
13
+ style="color:#97a2b2; cursor: pointer">
14
+ <%= icon "flag", aria_hidden: true, class: "action-icon--preview", role: "img", "aria-hidden": true %>
15
+ <span class="show-for-sr">
16
+ <%= t("report", scope: "decidim.proposals.proposals.show") %>
17
+ </span>
18
+ </button>
19
+ <% end %>
20
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <div class="table__title-block">
2
+ <span class="table__title-icon">
3
+ <% if unanswered_proposals_overdue?(proposal) || evaluating_proposals_overdue?(proposal) %>
4
+ <%= icon "datetime", class: "text-alert" %>
5
+ <% elsif grace_period_unanswered?(proposal) || grace_period_evaluating?(proposal) %>
6
+ <%= icon "timer", class: "text-warning" %>
7
+ <% end %>
8
+ </span>
9
+ <div class="table__list-title">
10
+ <%= link_to(
11
+ decidim_html_escape(present(proposal).title).html_safe,
12
+ proposal_path(proposal)
13
+ ) %>
14
+ <% if unanswered_proposals_overdue?(proposal) %>
15
+ <div class="help-text-overdue text-alert">
16
+ <%= time_ago_in_words(last_day_to_answer(proposal),
17
+ scope: "decidim.reporting_proposals.admin.answer_overdue.datetime.distance_in_words") %>
18
+ </div>
19
+ <% elsif evaluating_proposals_overdue?(proposal) %>
20
+ <div class="help-text-overdue text-alert">
21
+ <%= time_ago_in_words(last_day_to_evaluate(proposal),
22
+ scope: "decidim.reporting_proposals.admin.answer_overdue.datetime.distance_in_words") %>
23
+ </div>
24
+ <% elsif grace_period_unanswered?(proposal) %>
25
+ <div class="help-text-overdue text-warning">
26
+ <%= time_ago_in_words(last_day_to_answer(proposal),
27
+ scope: "decidim.reporting_proposals.admin.answer_pending.datetime.distance_in_words") %>
28
+ </div>
29
+ <% elsif grace_period_evaluating?(proposal) %>
30
+ <div class="help-text-overdue text-warning">
31
+ <%= time_ago_in_words(last_day_to_evaluate(proposal),
32
+ scope: "decidim.reporting_proposals.admin.evaluate_pending.datetime.distance_in_words") %>
33
+ </div>
34
+ <% elsif proposal.accepted? || proposal.rejected? %>
35
+ <div class="help-text-overdue text-success">
36
+ <%= t("decidim.reporting_proposals.admin.resolution_time") %>:
37
+ <%= time_elapsed_to_answer(proposal) %>
38
+ </div>
39
+ <% end %>
40
+ </div>
41
+ </div>
@@ -0,0 +1,6 @@
1
+ <% if current_component.settings.try(:additional_buttons_for_show_proposal_show) &&
2
+ current_component.settings.additional_button_for_show_proposal_text.present? &&
3
+ current_component.settings.additional_button_for_show_proposal_link.present? %>
4
+ <%= link_to translated_attribute(component_settings.additional_button_for_show_proposal_text),
5
+ component_settings.additional_button_for_show_proposal_link, class: "title-action__action button small ml-s" %>
6
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <div class="field hashtags__container">
2
+ <%= form.text_field :title, class: "js-hashtags" %>
3
+ </div>
4
+
5
+ <div class="field hashtags__container">
6
+ <%= text_editor_for_proposal_body(form) %>
7
+ </div>