decidim-reporting_proposals 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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