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,490 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/components/namer"
4
+ require "decidim/meetings"
5
+
6
+ Decidim.register_component(:reporting_proposals) do |component|
7
+ # reuses the same public/admin controllers as proposals, custom engines from this module are used for custom actions
8
+ component.engine = Decidim::Proposals::Engine
9
+ component.admin_engine = Decidim::Proposals::AdminEngine
10
+ component.stylesheet = "decidim/proposals/proposals"
11
+ component.icon = "media/images/decidim_proposals.svg"
12
+
13
+ component.on(:before_destroy) do |instance|
14
+ raise "Can't destroy this component when there are proposals" if Decidim::Proposals::Proposal.where(component: instance).any?
15
+ end
16
+
17
+ component.data_portable_entities = ["Decidim::Proposals::Proposal"]
18
+
19
+ component.newsletter_participant_entities = ["Decidim::Proposals::Proposal"]
20
+
21
+ component.actions = %w(endorse vote create withdraw amend comment vote_comment)
22
+
23
+ component.query_type = "Decidim::ReportingProposals::ReportingProposalsType"
24
+
25
+ component.permissions_class_name = "Decidim::Proposals::Permissions"
26
+
27
+ REP_POSSIBLE_SORT_ORDERS = %w(default random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
28
+
29
+ component.settings(:global) do |settings|
30
+ settings.attribute :scopes_enabled, type: :boolean, default: false
31
+ settings.attribute :scope_id, type: :scope
32
+ settings.attribute :vote_limit, type: :integer, default: 0
33
+ settings.attribute :minimum_votes_per_user, type: :integer, default: 0
34
+ settings.attribute :proposal_limit, type: :integer, default: 0
35
+ settings.attribute :proposal_length, type: :integer, default: 500
36
+ settings.attribute :proposal_edit_time, type: :enum, default: "limited", choices: -> { %w(limited infinite) }
37
+ settings.attribute :proposal_edit_before_minutes, type: :integer, default: 5
38
+ settings.attribute :threshold_per_proposal, type: :integer, default: 0
39
+ settings.attribute :can_accumulate_supports_beyond_threshold, type: :boolean, default: false
40
+ settings.attribute :proposal_answering_enabled, type: :boolean, default: true
41
+ settings.attribute :default_sort_order, type: :select, default: "default", choices: -> { REP_POSSIBLE_SORT_ORDERS }
42
+ settings.attribute :official_proposals_enabled, type: :boolean, default: true
43
+ settings.attribute :comments_enabled, type: :boolean, default: true
44
+ settings.attribute :comments_max_length, type: :integer, required: false
45
+ settings.attribute :geocoding_enabled, type: :boolean, default: true
46
+ settings.attribute :geocoding_comparison_enabled, type: :boolean, default: true
47
+ settings.attribute :geocoding_comparison_radius, type: :integer, default: 30
48
+ settings.attribute :geocoding_comparison_newer_than, type: :integer, default: 60
49
+ settings.attribute :attachments_allowed, type: :boolean, default: true
50
+ settings.attribute :only_photo_attachments, type: :boolean, default: true
51
+ settings.attribute :resources_permissions_enabled, type: :boolean, default: true
52
+ settings.attribute :collaborative_drafts_enabled, type: :boolean, default: false, readonly: ->(_) { true }
53
+ settings.attribute :participatory_texts_enabled,
54
+ type: :boolean, default: false,
55
+ readonly: ->(_) { true }
56
+ settings.attribute :amendments_enabled, type: :boolean, default: false
57
+ settings.attribute :amendments_wizard_help_text, type: :text, translated: true, editor: true, required: false
58
+ settings.attribute :announcement, type: :text, translated: true, editor: true
59
+ settings.attribute :new_proposal_body_template, type: :text, translated: true, editor: true, required: false
60
+ settings.attribute :new_proposal_help_text, type: :text, translated: true, editor: true
61
+ settings.attribute :proposal_wizard_step_1_help_text, type: :text, translated: true, editor: true
62
+ settings.attribute :proposal_wizard_step_2_help_text, type: :text, translated: true, editor: true
63
+ settings.attribute :proposal_wizard_step_3_help_text, type: :text, translated: true, editor: true, readonly: ->(_) { true }
64
+ settings.attribute :proposal_wizard_step_4_help_text, type: :text, translated: true, editor: true
65
+ settings.attribute :unanswered_proposals_overdue, type: :integer, default: Decidim::ReportingProposals.unanswered_proposals_overdue
66
+ settings.attribute :evaluating_proposals_overdue, type: :integer, default: Decidim::ReportingProposals.evaluating_proposals_overdue
67
+ settings.attribute :proposal_photo_editing_enabled, type: :boolean, default: true
68
+ settings.attribute :additional_buttons_show, type: :boolean, default: false
69
+ settings.attribute :additional_button_text, type: :string, translated: true, editor: true
70
+ settings.attribute :additional_button_link, type: :string, editor: true
71
+ settings.attribute :additional_buttons_for_show_proposal_show, type: :boolean, default: false
72
+ settings.attribute :additional_button_for_show_proposal_text, type: :string, translated: true, editor: true
73
+ settings.attribute :additional_button_for_show_proposal_link, type: :string, editor: true
74
+ end
75
+
76
+ component.settings(:step) do |settings|
77
+ settings.attribute :endorsements_enabled, type: :boolean, default: true
78
+ settings.attribute :endorsements_blocked, type: :boolean
79
+ settings.attribute :votes_enabled, type: :boolean
80
+ settings.attribute :votes_blocked, type: :boolean
81
+ settings.attribute :votes_hidden, type: :boolean, default: false
82
+ settings.attribute :comments_blocked, type: :boolean, default: false
83
+ settings.attribute :creation_enabled, type: :boolean, default: true
84
+ settings.attribute :proposal_answering_enabled, type: :boolean, default: true
85
+ settings.attribute :publish_answers_immediately, type: :boolean, default: true
86
+ settings.attribute :answers_with_costs, type: :boolean, default: false
87
+ settings.attribute :default_sort_order, type: :select, include_blank: true, choices: -> { REP_POSSIBLE_SORT_ORDERS }
88
+ settings.attribute :amendment_creation_enabled, type: :boolean, default: true
89
+ settings.attribute :amendment_reaction_enabled, type: :boolean, default: true
90
+ settings.attribute :amendment_promotion_enabled, type: :boolean, default: true
91
+ settings.attribute :amendments_visibility,
92
+ type: :enum, default: "all",
93
+ choices: -> { Decidim.config.amendments_visibility_options }
94
+ settings.attribute :announcement, type: :text, translated: true, editor: true
95
+ settings.attribute :automatic_hashtags, type: :text, editor: false, required: false
96
+ settings.attribute :suggested_hashtags, type: :text, editor: false, required: false
97
+ end
98
+
99
+ component.register_resource(:proposal) do |resource|
100
+ resource.model_class_name = "Decidim::Proposals::Proposal"
101
+ resource.template = "decidim/proposals/proposals/linked_proposals"
102
+ resource.card = "decidim/proposals/proposal"
103
+ resource.reported_content_cell = "decidim/proposals/reported_content"
104
+ resource.actions = %w(endorse vote amend comment vote_comment)
105
+ resource.searchable = true
106
+ end
107
+
108
+ component.register_resource(:collaborative_draft) do |resource|
109
+ resource.model_class_name = "Decidim::Proposals::CollaborativeDraft"
110
+ resource.card = "decidim/proposals/collaborative_draft"
111
+ resource.reported_content_cell = "decidim/proposals/collaborative_drafts/reported_content"
112
+ end
113
+
114
+ component.register_stat :proposals_count, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
115
+ Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.except_withdrawn.not_hidden.count
116
+ end
117
+
118
+ component.register_stat :proposals_accepted, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
119
+ Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).accepted.not_hidden.count
120
+ end
121
+
122
+ component.register_stat :supports_count, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
123
+ proposals = Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.not_hidden
124
+ Decidim::Proposals::ProposalVote.where(proposal: proposals).count
125
+ end
126
+
127
+ component.register_stat :endorsements_count, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
128
+ proposals = Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).not_hidden
129
+ proposals.sum(:endorsements_count)
130
+ end
131
+
132
+ component.register_stat :comments_count, tag: :comments do |components, start_at, end_at|
133
+ proposals = Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.not_hidden
134
+ proposals.sum(:comments_count)
135
+ end
136
+
137
+ component.register_stat :followers_count, tag: :followers, priority: Decidim::StatsRegistry::LOW_PRIORITY do |components, start_at, end_at|
138
+ proposals_ids = Decidim::Proposals::FilteredProposals.for(components, start_at, end_at).published.not_hidden.pluck(:id)
139
+ Decidim::Follow.where(decidim_followable_type: "Decidim::Proposals::Proposal", decidim_followable_id: proposals_ids).count
140
+ end
141
+
142
+ component.exports :proposals do |exports|
143
+ exports.collection do |component_instance, user|
144
+ space = component_instance.participatory_space
145
+
146
+ collection = Decidim::Proposals::Proposal
147
+ .published
148
+ .where(component: component_instance)
149
+ .includes(:scope, :category, :component)
150
+
151
+ if space.user_roles(:valuator).where(user: user).any?
152
+ collection.with_valuation_assigned_to(user, space)
153
+ else
154
+ collection
155
+ end
156
+ end
157
+
158
+ exports.include_in_open_data = true
159
+
160
+ exports.serializer Decidim::Proposals::ProposalSerializer
161
+ end
162
+
163
+ component.exports :proposal_comments do |exports|
164
+ exports.collection do |component_instance|
165
+ Decidim::Comments::Export.comments_for_resource(
166
+ Decidim::Proposals::Proposal, component_instance
167
+ ).includes(:author, :user_group, root_commentable: { component: { participatory_space: :organization } })
168
+ end
169
+
170
+ exports.include_in_open_data = true
171
+
172
+ exports.serializer Decidim::Comments::CommentSerializer
173
+ end
174
+
175
+ component.imports :proposals do |imports|
176
+ imports.form_view = "decidim/proposals/admin/imports/proposals_fields"
177
+ imports.form_class_name = "Decidim::Proposals::Admin::ProposalsFileImportForm"
178
+
179
+ imports.messages do |msg|
180
+ msg.set(:resource_name) { |count: 1| I18n.t("decidim.proposals.admin.imports.resources.proposals", count: count) }
181
+ msg.set(:title) { I18n.t("decidim.proposals.admin.imports.title.proposals") }
182
+ msg.set(:label) { I18n.t("decidim.proposals.admin.imports.label.proposals") }
183
+ msg.set(:help) { I18n.t("decidim.proposals.admin.imports.help.proposals") }
184
+ end
185
+
186
+ imports.creator Decidim::Proposals::Import::ProposalCreator
187
+ end
188
+
189
+ component.imports :answers do |imports|
190
+ imports.messages do |msg|
191
+ msg.set(:resource_name) { |count: 1| I18n.t("decidim.proposals.admin.imports.resources.answers", count: count) }
192
+ msg.set(:title) { I18n.t("decidim.proposals.admin.imports.title.answers") }
193
+ msg.set(:label) { I18n.t("decidim.proposals.admin.imports.label.answers") }
194
+ msg.set(:help) { I18n.t("decidim.proposals.admin.imports.help.answers") }
195
+ end
196
+
197
+ imports.creator Decidim::Proposals::Import::ProposalAnswerCreator
198
+ imports.example do |import_component|
199
+ organization = import_component.organization
200
+ [
201
+ %w(id state) + organization.available_locales.map { |l| "answer/#{l}" },
202
+ [1, "accepted"] + organization.available_locales.map { "Example answer" },
203
+ [2, "rejected"] + organization.available_locales.map { "Example answer" },
204
+ [3, "evaluating"] + organization.available_locales.map { "Example answer" }
205
+ ]
206
+ end
207
+ end
208
+
209
+ component.seeds do |participatory_space|
210
+ admin_user = Decidim::User.find_by(
211
+ organization: participatory_space.organization,
212
+ email: "admin@example.org"
213
+ )
214
+
215
+ step_settings = if participatory_space.allows_steps?
216
+ { participatory_space.active_step.id => { votes_enabled: true, votes_blocked: false, creation_enabled: true } }
217
+ else
218
+ {}
219
+ end
220
+
221
+ params = {
222
+ name: Decidim::Components::Namer.new(participatory_space.organization.available_locales, :reporting_proposals).i18n_name,
223
+ manifest_name: :reporting_proposals,
224
+ published_at: Time.current,
225
+ participatory_space: participatory_space,
226
+ settings: {
227
+ vote_limit: 0,
228
+ collaborative_drafts_enabled: true
229
+ },
230
+ step_settings: step_settings
231
+ }
232
+
233
+ component = Decidim.traceability.perform_action!(
234
+ "publish",
235
+ Decidim::Component,
236
+ admin_user,
237
+ visibility: "all"
238
+ ) do
239
+ Decidim::Component.create!(params)
240
+ end
241
+
242
+ if participatory_space.scope
243
+ scopes = participatory_space.scope.descendants
244
+ global = participatory_space.scope
245
+ else
246
+ scopes = participatory_space.organization.scopes
247
+ global = nil
248
+ end
249
+
250
+ 5.times do |n|
251
+ state, answer, state_published_at = if n > 3
252
+ ["accepted", Decidim::Faker::Localized.sentence(word_count: 10), Time.current]
253
+ elsif n > 2
254
+ ["rejected", nil, Time.current]
255
+ elsif n > 1
256
+ ["evaluating", nil, Time.current]
257
+ elsif n.positive?
258
+ ["accepted", Decidim::Faker::Localized.sentence(word_count: 10), nil]
259
+ else
260
+ [nil, nil, nil]
261
+ end
262
+
263
+ params = {
264
+ component: component,
265
+ category: participatory_space.categories.sample,
266
+ scope: Faker::Boolean.boolean(true_ratio: 0.5) ? global : scopes.sample,
267
+ title: { en: Faker::Lorem.sentence(word_count: 2) },
268
+ body: { en: Faker::Lorem.paragraphs(number: 2).join("\n") },
269
+ state: state,
270
+ answer: answer,
271
+ answered_at: state.present? ? Time.current : nil,
272
+ state_published_at: state_published_at,
273
+ published_at: Time.current
274
+ }
275
+
276
+ proposal = Decidim.traceability.perform_action!(
277
+ "publish",
278
+ Decidim::Proposals::Proposal,
279
+ admin_user,
280
+ visibility: "all"
281
+ ) do
282
+ proposal = Decidim::Proposals::Proposal.new(params)
283
+ proposal.add_coauthor(participatory_space.organization)
284
+ proposal.save!
285
+ proposal
286
+ end
287
+
288
+ if n.positive?
289
+ Decidim::User.where(decidim_organization_id: participatory_space.decidim_organization_id).all.sample(n).each do |author|
290
+ user_group = [true, false].sample ? Decidim::UserGroups::ManageableUserGroups.for(author).verified.sample : nil
291
+ proposal.add_coauthor(author, user_group: user_group)
292
+ end
293
+ end
294
+
295
+ if proposal.state.nil?
296
+ email = "amendment-author-#{participatory_space.underscored_name}-#{participatory_space.id}-#{n}-amend#{n}@example.org"
297
+ name = "#{Faker::Name.name} #{participatory_space.id} #{n} amend#{n}"
298
+
299
+ author = Decidim::User.find_or_initialize_by(email: email)
300
+ author.update!(
301
+ password: "decidim123456",
302
+ password_confirmation: "decidim123456",
303
+ name: name,
304
+ nickname: Faker::Twitter.unique.screen_name,
305
+ organization: component.organization,
306
+ tos_agreement: "1",
307
+ confirmed_at: Time.current
308
+ )
309
+
310
+ group = Decidim::UserGroup.create!(
311
+ name: Faker::Name.name,
312
+ nickname: Faker::Twitter.unique.screen_name,
313
+ email: Faker::Internet.email,
314
+ extended_data: {
315
+ document_number: Faker::Code.isbn,
316
+ phone: Faker::PhoneNumber.phone_number,
317
+ verified_at: Time.current
318
+ },
319
+ decidim_organization_id: component.organization.id,
320
+ confirmed_at: Time.current
321
+ )
322
+
323
+ Decidim::UserGroupMembership.create!(
324
+ user: author,
325
+ role: "creator",
326
+ user_group: group
327
+ )
328
+
329
+ params = {
330
+ component: component,
331
+ category: participatory_space.categories.sample,
332
+ scope: Faker::Boolean.boolean(true_ratio: 0.5) ? global : scopes.sample,
333
+ title: { en: "#{proposal.title["en"]} #{Faker::Lorem.sentence(word_count: 1)}" },
334
+ body: { en: "#{proposal.body["en"]} #{Faker::Lorem.sentence(word_count: 3)}" },
335
+ state: "evaluating",
336
+ answer: nil,
337
+ answered_at: Time.current,
338
+ published_at: Time.current
339
+ }
340
+
341
+ emendation = Decidim.traceability.perform_action!(
342
+ "create",
343
+ Decidim::Proposals::Proposal,
344
+ author,
345
+ visibility: "public-only"
346
+ ) do
347
+ emendation = Decidim::Proposals::Proposal.new(params)
348
+ emendation.add_coauthor(author, user_group: author.user_groups.first)
349
+ emendation.save!
350
+ emendation
351
+ end
352
+
353
+ Decidim::Amendment.create!(
354
+ amender: author,
355
+ amendable: proposal,
356
+ emendation: emendation,
357
+ state: "evaluating"
358
+ )
359
+ end
360
+
361
+ (n % 3).times do |m|
362
+ email = "vote-author-#{participatory_space.underscored_name}-#{participatory_space.id}-#{n}-#{m}@example.org"
363
+ name = "#{Faker::Name.name} #{participatory_space.id} #{n} #{m}"
364
+
365
+ author = Decidim::User.find_or_initialize_by(email: email)
366
+ author.update!(
367
+ password: "decidim123456",
368
+ password_confirmation: "decidim123456",
369
+ name: name,
370
+ nickname: Faker::Twitter.unique.screen_name,
371
+ organization: component.organization,
372
+ tos_agreement: "1",
373
+ confirmed_at: Time.current,
374
+ personal_url: Faker::Internet.url,
375
+ about: Faker::Lorem.paragraph(sentence_count: 2)
376
+ )
377
+
378
+ Decidim::Proposals::ProposalVote.create!(proposal: proposal, author: author) unless proposal.published_state? && proposal.rejected?
379
+ Decidim::Proposals::ProposalVote.create!(proposal: emendation, author: author) if emendation
380
+ end
381
+
382
+ unless proposal.published_state? && proposal.rejected?
383
+ (n * 2).times do |index|
384
+ email = "endorsement-author-#{participatory_space.underscored_name}-#{participatory_space.id}-#{n}-endr#{index}@example.org"
385
+ name = "#{Faker::Name.name} #{participatory_space.id} #{n} endr#{index}"
386
+
387
+ author = Decidim::User.find_or_initialize_by(email: email)
388
+ author.update!(
389
+ password: "decidim123456",
390
+ password_confirmation: "decidim123456",
391
+ name: name,
392
+ nickname: Faker::Twitter.unique.screen_name,
393
+ organization: component.organization,
394
+ tos_agreement: "1",
395
+ confirmed_at: Time.current
396
+ )
397
+ if index.even?
398
+ group = Decidim::UserGroup.create!(
399
+ name: Faker::Name.name,
400
+ nickname: Faker::Twitter.unique.screen_name,
401
+ email: Faker::Internet.email,
402
+ extended_data: {
403
+ document_number: Faker::Code.isbn,
404
+ phone: Faker::PhoneNumber.phone_number,
405
+ verified_at: Time.current
406
+ },
407
+ decidim_organization_id: component.organization.id,
408
+ confirmed_at: Time.current
409
+ )
410
+
411
+ Decidim::UserGroupMembership.create!(
412
+ user: author,
413
+ role: "creator",
414
+ user_group: group
415
+ )
416
+ end
417
+ Decidim::Endorsement.create!(resource: proposal, author: author, user_group: author.user_groups.first)
418
+ end
419
+ end
420
+
421
+ (n % 3).times do
422
+ author_admin = Decidim::User.where(organization: component.organization, admin: true).all.sample
423
+
424
+ Decidim::Proposals::ProposalNote.create!(
425
+ proposal: proposal,
426
+ author: author_admin,
427
+ body: Faker::Lorem.paragraphs(number: 2).join("\n")
428
+ )
429
+ end
430
+
431
+ Decidim::Comments::Seed.comments_for(proposal)
432
+
433
+ #
434
+ # Collaborative drafts
435
+ #
436
+ state = if n > 3
437
+ "published"
438
+ elsif n > 2
439
+ "withdrawn"
440
+ else
441
+ "open"
442
+ end
443
+ author = Decidim::User.where(organization: component.organization).all.sample
444
+
445
+ draft = Decidim.traceability.perform_action!("create", Decidim::Proposals::CollaborativeDraft, author) do
446
+ draft = Decidim::Proposals::CollaborativeDraft.new(
447
+ component: component,
448
+ category: participatory_space.categories.sample,
449
+ scope: Faker::Boolean.boolean(true_ratio: 0.5) ? global : scopes.sample,
450
+ title: Faker::Lorem.sentence(word_count: 2),
451
+ body: Faker::Lorem.paragraphs(number: 2).join("\n"),
452
+ state: state,
453
+ published_at: Time.current
454
+ )
455
+ draft.coauthorships.build(author: participatory_space.organization)
456
+ draft.save!
457
+ draft
458
+ end
459
+
460
+ case n
461
+ when 2
462
+ author2 = Decidim::User.where(organization: component.organization).all.sample
463
+ Decidim::Coauthorship.create(coauthorable: draft, author: author2)
464
+ author3 = Decidim::User.where(organization: component.organization).all.sample
465
+ Decidim::Coauthorship.create(coauthorable: draft, author: author3)
466
+ author4 = Decidim::User.where(organization: component.organization).all.sample
467
+ Decidim::Coauthorship.create(coauthorable: draft, author: author4)
468
+ author5 = Decidim::User.where(organization: component.organization).all.sample
469
+ Decidim::Coauthorship.create(coauthorable: draft, author: author5)
470
+ author6 = Decidim::User.where(organization: component.organization).all.sample
471
+ Decidim::Coauthorship.create(coauthorable: draft, author: author6)
472
+ when 3
473
+ author2 = Decidim::User.where(organization: component.organization).all.sample
474
+ Decidim::Coauthorship.create(coauthorable: draft, author: author2)
475
+ end
476
+
477
+ Decidim::Comments::Seed.comments_for(draft)
478
+ end
479
+
480
+ Decidim.traceability.update!(
481
+ Decidim::Proposals::CollaborativeDraft.all.sample,
482
+ Decidim::User.where(organization: component.organization).all.sample,
483
+ component: component,
484
+ category: participatory_space.categories.sample,
485
+ scope: Faker::Boolean.boolean(true_ratio: 0.5) ? global : scopes.sample,
486
+ title: Faker::Lorem.sentence(word_count: 2),
487
+ body: Faker::Lorem.paragraphs(number: 2).join("\n")
488
+ )
489
+ end
490
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This namespace holds the logic of the `decidim-reporting_proposals` module.
5
+ module ReportingProposals
6
+ include ActiveSupport::Configurable
7
+
8
+ # Public Setting that defines after how many days a not-answered proposal is overdue
9
+ # Set it to 0 (zero) if you don't want to use this feature
10
+ config_accessor :unanswered_proposals_overdue do
11
+ 7
12
+ end
13
+
14
+ # Public Setting that defines after how many days an evaluating-state proposal is overdue
15
+ # Set it to 0 (zero) if you don't want to use this feature
16
+ config_accessor :evaluating_proposals_overdue do
17
+ 3
18
+ end
19
+
20
+ # Public Setting that defines whether the administrator is allowed to hide the proposals.
21
+ # Set to false if you do not want to use this feature
22
+ config_accessor :allow_admins_to_hide_proposals do
23
+ true
24
+ end
25
+
26
+ # Public Setting that allows to configure which component will have "Use my location" button
27
+ # in a geocoded address field. Accepts an array of component manifest names
28
+ config_accessor :show_my_location_button do
29
+ [:proposals, :meetings, :reporting_proposals]
30
+ end
31
+
32
+ # Public Setting that adds a button next to the "add image" input[type=file] to open the camera directly
33
+ config_accessor :use_camera_button do
34
+ [:proposals, :reporting_proposals]
35
+ end
36
+
37
+ # Public Setting to prevent adding the camera button on not photo/image input[type=file]
38
+ config_accessor :camera_button_on_attachments do
39
+ false
40
+ end
41
+
42
+ # Public setting to prevent valuators or admins to modify the photos attached to a proposal
43
+ # otherwise can be configured at the component level
44
+ config_accessor :allow_proposal_photo_editing do
45
+ true
46
+ end
47
+
48
+ # Public setting to allow to assign other valuators
49
+ config_accessor :valuators_assign_other_valuators do
50
+ true
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "deface"
4
+
5
+ module Decidim
6
+ module ReportingProposals
7
+ # This is the engine that runs on the public interface of decidim-ReportingProposals.
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace Decidim::ReportingProposals
10
+
11
+ routes do
12
+ post :locate, to: "geolocation#locate"
13
+ end
14
+
15
+ # generic overrides
16
+ config.to_prepare do
17
+ ComponentValidator.include(Decidim::ReportingProposals::ComponentValidatorOverride)
18
+ Decidim::Category.include(Decidim::ReportingProposals::CategoryOverride)
19
+ Decidim::FormBuilder.include(Decidim::ReportingProposals::FormBuilderOverride)
20
+ Decidim::Map::Autocomplete::Builder.include(Decidim::ReportingProposals::MapBuilderOverride)
21
+ Decidim::CreateReport.include(Decidim::ReportingProposals::CreateReportOverride)
22
+ Decidim::Proposals::ProposalSerializer.include(Decidim::ReportingProposals::ProposalSerializerOverride)
23
+ Decidim::Admin::CategoryForm.include(Decidim::ReportingProposals::Admin::CategoryFormOverride)
24
+ Decidim::Admin::CreateCategory.include(Decidim::ReportingProposals::Admin::CreateCategoryOverride)
25
+ Decidim::Admin::UpdateCategory.include(Decidim::ReportingProposals::Admin::UpdateCategoryOverride)
26
+ Decidim::Proposals::Admin::Permissions.include(Decidim::ReportingProposals::Admin::PermissionsOverride)
27
+ Decidim::ParticipatorySpaceRoleConfig::Valuator.include(Decidim::ReportingProposals::ParticipatorySpaceRoleConfig::ValuatorOverride)
28
+
29
+ # Search user roles for different participatory spaces and apply override to all of them
30
+ # We'll make sure this does not break rails in situations where database is not installed (ie, creating the test or development apps)
31
+ begin
32
+ Decidim.participatory_space_manifests.each do |manifest|
33
+ manifest.model_class_name.constantize.new.user_roles.model.include(Decidim::ReportingProposals::ParticipatorySpaceUserRoleOverride)
34
+ end
35
+ rescue StandardError => e
36
+ Rails.logger.error("Error while trying to include Decidim::ReportingProposals::ParticipatorySpaceUserRoleOverride: #{e.message}")
37
+ end
38
+ end
39
+
40
+ # controllers and helpers overrides
41
+ initializer "decidim_reporting_proposals.overrides", after: "decidim.action_controller" do
42
+ config.to_prepare do
43
+ Decidim::Admin::ComponentsController.include(Decidim::ReportingProposals::Admin::NeedsHeaderSnippets)
44
+ Decidim::Proposals::ProposalsController.include(Decidim::ReportingProposals::ProposalsControllerOverride)
45
+ Decidim::Proposals::ProposalWizardHelper.include(Decidim::ReportingProposals::ProposalWizardHelperOverride)
46
+ Decidim::Proposals::Admin::ProposalsController.include(Decidim::ReportingProposals::Admin::NeedsHeaderSnippets)
47
+ Decidim::Proposals::Admin::ProposalsController.include(Decidim::ReportingProposals::Admin::ProposalsControllerOverride)
48
+ Decidim::Proposals::Admin::ProposalAnswersController.include(Decidim::ReportingProposals::Admin::ProposalAnswersControllerOverride)
49
+ Decidim::Proposals::Admin::ProposalsHelper.include(Decidim::ReportingProposals::Admin::ProposalsHelperOverride)
50
+ Decidim::Admin::CategoriesController.include(Decidim::ReportingProposals::Admin::CategoriesControllerOverride)
51
+ begin
52
+ Decidim::Templates::Admin::ProposalAnswerTemplatesController.include(Decidim::ReportingProposals::Admin::ProposalAnswerTemplatesControllerOverride)
53
+ rescue StandardError => e
54
+ Rails.logger.error("Error while trying to include Decidim::Templates::Admin::ProposalAnswerTemplatesControllerOverride: #{e.message}")
55
+ end
56
+ end
57
+ end
58
+
59
+ initializer "decidim_reporting_proposals.mount_routes" do
60
+ Decidim::Core::Engine.routes do
61
+ mount Decidim::ReportingProposals::Engine, at: "/reporting_proposals", as: "decidim_reporting_proposals"
62
+ end
63
+ end
64
+
65
+ initializer "decidim_reporting_proposals.component_overdue_options" do
66
+ Decidim.component_registry.find(:proposals).tap do |component|
67
+ component.settings(:global) do |settings|
68
+ settings.attribute :geocoding_comparison_enabled, type: :boolean, default: false
69
+ settings.attribute :geocoding_comparison_radius, type: :integer, default: 30
70
+ settings.attribute :geocoding_comparison_newer_than, type: :integer, default: 60
71
+ settings.attribute(:unanswered_proposals_overdue, type: :integer, default: Decidim::ReportingProposals.unanswered_proposals_overdue)
72
+ settings.attribute(:evaluating_proposals_overdue, type: :integer, default: Decidim::ReportingProposals.evaluating_proposals_overdue)
73
+ settings.attribute(:proposal_photo_editing_enabled, type: :boolean, default: false)
74
+ end
75
+ end
76
+ end
77
+
78
+ initializer "decidim_reporting_proposals.on_publish_proposals" do
79
+ config.to_prepare do
80
+ Decidim::EventsManager.subscribe(/decidim.events\.proposals\.(proposal_published|proposal_update_category)/) do |_event_name, data|
81
+ Decidim::ReportingProposals::AssignProposalValuatorsJob.perform_later(data)
82
+ end
83
+ end
84
+ end
85
+
86
+ initializer "decidim_reporting_proposals.webpacker.assets_path" do
87
+ Decidim.register_assets_path File.expand_path("app/packs", root)
88
+ end
89
+
90
+ initializer "decidim_reporting_proposals.add_cells_view_paths" do
91
+ Cell::ViewModel.view_paths << File.expand_path("#{Decidim::ReportingProposals::Engine.root}/app/cells")
92
+ Cell::ViewModel.view_paths << File.expand_path("#{Decidim::ReportingProposals::Engine.root}/app/views")
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :reporting_proposals_component, parent: :proposal_component do
5
+ name { Decidim::Components::Namer.new(participatory_space.organization.available_locales, :reporting_proposals).i18n_name }
6
+ manifest_name { :reporting_proposals }
7
+ end
8
+
9
+ factory :category_valuator, class: "Decidim::ReportingProposals::CategoryValuator" do
10
+ category { create :category, participatory_space: valuator_role.participatory_space }
11
+ valuator_role { create :participatory_process_user_role, role: "valuator" }
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ReportingProposals
5
+ VERSION = "0.2.0"
6
+ # DECIDIM_VERSION = "0.26.5"
7
+ # Provisional until
8
+ # https://github.com/openpoke/decidim/pull/23
9
+ # https://github.com/openpoke/decidim/pull/37
10
+ # are merged on upstream
11
+ DECIDIM_VERSION = { github: "openpoke/decidim", branch: "0.26-lucerne" }.freeze
12
+
13
+ COMPAT_DECIDIM_VERSION = [">= 0.25.0", "< 0.27"].freeze
14
+ end
15
+ end