decidim-proposals 0.19.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/admin/decidim_proposals_manifest.js +1 -0
  3. data/app/assets/images/decidim/gamification/badges/accepted_proposals.svg +1 -234
  4. data/app/assets/images/decidim/gamification/badges/proposal_votes.svg +1 -95
  5. data/app/assets/images/decidim/gamification/badges/proposals.svg +1 -229
  6. data/app/assets/images/decidim/proposals/icon.svg +1 -3
  7. data/app/assets/javascripts/decidim/proposals/add_proposal.js.es6 +6 -0
  8. data/app/assets/javascripts/decidim/proposals/admin/proposals.es6 +24 -11
  9. data/app/assets/javascripts/decidim/proposals/admin/proposals_form.js.es6 +3 -5
  10. data/app/assets/javascripts/decidim/proposals/admin/proposals_picker.js.es6 +35 -0
  11. data/app/cells/decidim/proposals/collaborative_draft_cell.rb +1 -1
  12. data/app/cells/decidim/proposals/collaborative_draft_link_to_proposal_cell.rb +1 -1
  13. data/app/cells/decidim/proposals/collaborative_draft_m/footer.erb +1 -1
  14. data/app/cells/decidim/proposals/collaborative_draft_m_cell.rb +1 -1
  15. data/app/cells/decidim/proposals/cost_report/show.erb +35 -0
  16. data/app/cells/decidim/proposals/cost_report_cell.rb +42 -0
  17. data/app/cells/decidim/proposals/highlighted_proposals_for_component/show.erb +3 -3
  18. data/app/cells/decidim/proposals/highlighted_proposals_for_component_cell.rb +1 -1
  19. data/app/cells/decidim/proposals/irreversible_action_modal/show.erb +2 -2
  20. data/app/cells/decidim/proposals/irreversible_action_modal_cell.rb +1 -1
  21. data/app/cells/decidim/proposals/participatory_text_proposal/buttons.erb +3 -3
  22. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +2 -2
  23. data/app/cells/decidim/proposals/proposal_cell.rb +1 -1
  24. data/app/cells/decidim/proposals/proposal_m/footer.erb +4 -1
  25. data/app/cells/decidim/proposals/proposal_m_cell.rb +37 -9
  26. data/app/cells/decidim/proposals/proposal_tags/show.erb +18 -10
  27. data/app/cells/decidim/proposals/proposal_tags_cell.rb +5 -0
  28. data/app/cells/decidim/proposals/proposals_picker/proposals.erb +12 -0
  29. data/app/cells/decidim/proposals/proposals_picker/show.erb +14 -0
  30. data/app/cells/decidim/proposals/proposals_picker_cell.rb +72 -0
  31. data/app/commands/decidim/proposals/admin/answer_proposal.rb +24 -46
  32. data/app/commands/decidim/proposals/admin/assign_proposals_to_valuator.rb +61 -0
  33. data/app/commands/decidim/proposals/admin/create_proposal.rb +10 -3
  34. data/app/commands/decidim/proposals/admin/create_proposal_note.rb +15 -0
  35. data/app/commands/decidim/proposals/admin/notify_proposal_answer.rb +85 -0
  36. data/app/commands/decidim/proposals/admin/publish_answers.rb +67 -0
  37. data/app/commands/decidim/proposals/admin/publish_participatory_text.rb +6 -1
  38. data/app/commands/decidim/proposals/admin/unassign_proposals_from_valuator.rb +62 -0
  39. data/app/commands/decidim/proposals/admin/update_participatory_text.rb +10 -2
  40. data/app/commands/decidim/proposals/admin/update_proposal.rb +5 -3
  41. data/app/commands/decidim/proposals/admin/update_proposal_scope.rb +75 -0
  42. data/app/commands/decidim/proposals/create_collaborative_draft.rb +1 -1
  43. data/app/commands/decidim/proposals/create_proposal.rb +7 -3
  44. data/app/commands/decidim/proposals/gallery_methods.rb +2 -51
  45. data/app/commands/decidim/proposals/publish_collaborative_draft.rb +2 -2
  46. data/app/commands/decidim/proposals/update_proposal.rb +25 -9
  47. data/app/controllers/concerns/decidim/proposals/admin/filterable.rb +82 -0
  48. data/app/controllers/concerns/decidim/proposals/admin/picker.rb +21 -0
  49. data/app/controllers/concerns/decidim/proposals/orderable.rb +13 -2
  50. data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +16 -6
  51. data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +8 -9
  52. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +107 -31
  53. data/app/controllers/decidim/proposals/admin/valuation_assignments_controller.rb +58 -0
  54. data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +10 -3
  55. data/app/controllers/decidim/proposals/proposals_controller.rb +31 -11
  56. data/app/controllers/decidim/proposals/versions_controller.rb +9 -16
  57. data/app/controllers/decidim/proposals/{proposal_widgets_controller.rb → widgets_controller.rb} +2 -2
  58. data/app/events/decidim/proposals/admin/proposal_note_created_event.rb +27 -0
  59. data/app/events/decidim/proposals/admin/update_proposal_scope_event.rb +11 -0
  60. data/app/forms/decidim/proposals/admin/import_participatory_text_form.rb +3 -1
  61. data/app/forms/decidim/proposals/admin/participatory_text_proposal_form.rb +20 -0
  62. data/app/forms/decidim/proposals/admin/preview_participatory_text_form.rb +2 -2
  63. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +27 -2
  64. data/app/forms/decidim/proposals/admin/proposal_base_form.rb +136 -0
  65. data/app/forms/decidim/proposals/admin/proposal_form.rb +6 -117
  66. data/app/forms/decidim/proposals/admin/valuation_assignment_form.rb +37 -0
  67. data/app/forms/decidim/proposals/proposal_form.rb +25 -12
  68. data/app/forms/decidim/proposals/proposal_wizard_create_step_form.rb +13 -1
  69. data/app/helpers/decidim/proposals/admin/filterable_helper.rb +17 -0
  70. data/app/helpers/decidim/proposals/admin/proposal_bulk_actions_helper.rb +35 -0
  71. data/app/helpers/decidim/proposals/admin/proposal_rankings_helper.rb +63 -0
  72. data/app/helpers/decidim/proposals/admin/proposals_helper.rb +124 -0
  73. data/app/helpers/decidim/proposals/admin/proposals_picker_helper.rb +30 -0
  74. data/app/helpers/decidim/proposals/application_helper.rb +64 -38
  75. data/app/helpers/decidim/proposals/collaborative_draft_helper.rb +9 -9
  76. data/app/helpers/decidim/proposals/control_version_helper.rb +1 -37
  77. data/app/helpers/decidim/proposals/proposal_cells_helper.rb +1 -1
  78. data/app/helpers/decidim/proposals/proposal_endorsements_helper.rb +0 -145
  79. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +2 -2
  80. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +24 -7
  81. data/app/helpers/decidim/proposals/proposals_helper.rb +66 -0
  82. data/app/models/decidim/proposals/collaborative_draft.rb +2 -2
  83. data/app/models/decidim/proposals/participatory_text.rb +3 -0
  84. data/app/models/decidim/proposals/proposal.rb +153 -42
  85. data/app/models/decidim/proposals/valuation_assignment.rb +24 -0
  86. data/app/permissions/decidim/proposals/admin/permissions.rb +77 -11
  87. data/app/permissions/decidim/proposals/permissions.rb +1 -22
  88. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +1 -1
  89. data/app/presenters/decidim/proposals/admin_log/valuation_assignment_presenter.rb +51 -0
  90. data/app/presenters/decidim/proposals/admin_log/value_types/proposal_title_body_presenter.rb +6 -1
  91. data/app/presenters/decidim/proposals/admin_log/value_types/valuator_role_user_presenter.rb +19 -0
  92. data/app/presenters/decidim/proposals/collaborative_draft_presenter.rb +2 -28
  93. data/app/presenters/decidim/proposals/log/valuation_assignment_presenter.rb +22 -0
  94. data/app/presenters/decidim/proposals/official_author_presenter.rb +1 -29
  95. data/app/presenters/decidim/proposals/proposal_presenter.rb +80 -12
  96. data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +1 -2
  97. data/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb +15 -12
  98. data/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb +5 -4
  99. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +2 -8
  100. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +3 -9
  101. data/app/queries/decidim/proposals/similar_proposals.rb +4 -4
  102. data/app/services/decidim/proposals/collaborative_draft_search.rb +8 -10
  103. data/app/services/decidim/proposals/diff_renderer.rb +17 -5
  104. data/app/services/decidim/proposals/proposal_builder.rb +9 -3
  105. data/app/services/decidim/proposals/proposal_search.rb +17 -70
  106. data/app/types/decidim/proposals/proposal_input_filter.rb +29 -0
  107. data/app/types/decidim/proposals/proposal_input_sort.rb +22 -0
  108. data/app/types/decidim/proposals/proposal_type.rb +34 -13
  109. data/app/types/decidim/proposals/proposals_type.rb +22 -15
  110. data/app/validators/proposal_length_validator.rb +38 -0
  111. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +9 -1
  112. data/app/views/decidim/proposals/admin/proposal_answers/_form.html.erb +35 -0
  113. data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +1 -1
  114. data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +3 -3
  115. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +8 -2
  116. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +8 -28
  117. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +26 -27
  118. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_assign_to_valuator.html.erb +15 -0
  119. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +21 -1
  120. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_publish_answers.html.erb +14 -0
  121. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_scope-change.html.erb +25 -0
  122. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_unassign_from_valuator.html.erb +15 -0
  123. data/app/views/decidim/proposals/admin/proposals/index.html.erb +18 -9
  124. data/app/views/decidim/proposals/admin/proposals/publish_answers.js.erb +12 -0
  125. data/app/views/decidim/proposals/admin/proposals/show.html.erb +186 -0
  126. data/app/views/decidim/proposals/admin/proposals/update_category.js.erb +3 -2
  127. data/app/views/decidim/proposals/admin/proposals/update_scope.js.erb +27 -0
  128. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +11 -9
  129. data/app/views/decidim/proposals/collaborative_drafts/_filters.html.erb +10 -8
  130. data/app/views/decidim/proposals/collaborative_drafts/_new_collaborative_draft_button.html.erb +4 -4
  131. data/app/views/decidim/proposals/collaborative_drafts/_reject_request_access_form.html.erb +1 -1
  132. data/app/views/decidim/proposals/collaborative_drafts/_wizard_aside.html.erb +4 -2
  133. data/app/views/decidim/proposals/collaborative_drafts/compare.html.erb +2 -0
  134. data/app/views/decidim/proposals/collaborative_drafts/complete.html.erb +2 -0
  135. data/app/views/decidim/proposals/collaborative_drafts/edit.html.erb +3 -1
  136. data/app/views/decidim/proposals/collaborative_drafts/index.html.erb +4 -2
  137. data/app/views/decidim/proposals/collaborative_drafts/new.html.erb +4 -0
  138. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +29 -30
  139. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +52 -22
  140. data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +0 -16
  141. data/app/views/decidim/proposals/proposals/_filters.html.erb +19 -17
  142. data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +1 -4
  143. data/app/views/decidim/proposals/proposals/_proposal_preview.html.erb +1 -11
  144. data/app/views/decidim/proposals/proposals/_proposal_similar.html.erb +2 -2
  145. data/app/views/decidim/proposals/proposals/_proposals.html.erb +14 -0
  146. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +14 -7
  147. data/app/views/decidim/proposals/proposals/_wizard_aside.html.erb +4 -2
  148. data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +4 -3
  149. data/app/views/decidim/proposals/proposals/compare.html.erb +2 -0
  150. data/app/views/decidim/proposals/proposals/complete.html.erb +2 -0
  151. data/app/views/decidim/proposals/proposals/edit.html.erb +3 -1
  152. data/app/views/decidim/proposals/proposals/edit_draft.html.erb +2 -0
  153. data/app/views/decidim/proposals/proposals/index.html.erb +7 -14
  154. data/app/views/decidim/proposals/proposals/new.html.erb +6 -2
  155. data/app/views/decidim/proposals/proposals/participatory_texts/_index.html.erb +1 -1
  156. data/app/views/decidim/proposals/proposals/participatory_texts/_proposal_vote_button.html.erb +10 -3
  157. data/app/views/decidim/proposals/proposals/participatory_texts/_view_index.html.erb +1 -1
  158. data/app/views/decidim/proposals/proposals/preview.html.erb +7 -8
  159. data/app/views/decidim/proposals/proposals/show.html.erb +62 -71
  160. data/app/views/decidim/proposals/versions/index.html.erb +14 -32
  161. data/app/views/decidim/proposals/versions/show.html.erb +16 -34
  162. data/config/locales/am-ET.yml +1 -0
  163. data/config/locales/ar.yml +73 -67
  164. data/config/locales/bg-BG.yml +237 -0
  165. data/config/locales/bg.yml +237 -0
  166. data/config/locales/ca.yml +183 -67
  167. data/config/locales/cs.yml +196 -80
  168. data/config/locales/da-DK.yml +1 -0
  169. data/config/locales/da.yml +1 -0
  170. data/config/locales/de.yml +269 -162
  171. data/config/locales/el-GR.yml +1 -0
  172. data/config/locales/el.yml +876 -0
  173. data/config/locales/en.yml +187 -71
  174. data/config/locales/eo.yml +1 -0
  175. data/config/locales/es-MX.yml +181 -65
  176. data/config/locales/es-PY.yml +181 -65
  177. data/config/locales/es.yml +186 -70
  178. data/config/locales/et-EE.yml +1 -0
  179. data/config/locales/et.yml +1 -0
  180. data/config/locales/eu.yml +28 -63
  181. data/config/locales/fi-plain.yml +181 -65
  182. data/config/locales/fi.yml +219 -103
  183. data/config/locales/fr-CA.yml +906 -0
  184. data/config/locales/fr.yml +184 -68
  185. data/config/locales/ga-IE.yml +1 -0
  186. data/config/locales/gl.yml +28 -63
  187. data/config/locales/hr-HR.yml +1 -0
  188. data/config/locales/hr.yml +1 -0
  189. data/config/locales/hu.yml +148 -70
  190. data/config/locales/id-ID.yml +28 -62
  191. data/config/locales/is-IS.yml +274 -0
  192. data/config/locales/is.yml +274 -0
  193. data/config/locales/it.yml +177 -85
  194. data/config/locales/ja-JP.yml +865 -0
  195. data/config/locales/ja.yml +889 -0
  196. data/config/locales/ko-KR.yml +1 -0
  197. data/config/locales/ko.yml +1 -0
  198. data/config/locales/lt-LT.yml +1 -0
  199. data/config/locales/lt.yml +1 -0
  200. data/config/locales/lv.yml +858 -0
  201. data/config/locales/mt-MT.yml +1 -0
  202. data/config/locales/mt.yml +1 -0
  203. data/config/locales/nl.yml +282 -188
  204. data/config/locales/no.yml +586 -19
  205. data/config/locales/om-ET.yml +1 -0
  206. data/config/locales/pl.yml +524 -393
  207. data/config/locales/pt-BR.yml +29 -67
  208. data/config/locales/pt.yml +438 -331
  209. data/config/locales/ro-RO.yml +841 -0
  210. data/config/locales/ru.yml +14 -47
  211. data/config/locales/sk-SK.yml +896 -0
  212. data/config/locales/sk.yml +869 -0
  213. data/config/locales/sl.yml +26 -0
  214. data/config/locales/so-SO.yml +1 -0
  215. data/config/locales/sr-CS.yml +126 -0
  216. data/config/locales/sv.yml +285 -171
  217. data/config/locales/ti-ER.yml +1 -0
  218. data/config/locales/tr-TR.yml +28 -63
  219. data/config/locales/uk.yml +14 -47
  220. data/config/locales/vi-VN.yml +1 -0
  221. data/config/locales/vi.yml +1 -0
  222. data/config/locales/zh-CN.yml +885 -0
  223. data/config/locales/zh-TW.yml +1 -0
  224. data/db/migrate/20181003074440_fix_user_groups_ids_in_proposals_endorsements.rb +4 -0
  225. data/db/migrate/20191206154128_add_endorsements_counter_cache_to_proposals.rb +7 -0
  226. data/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb +54 -0
  227. data/db/migrate/20200120230130_drop_proposal_endorsements.rb +8 -0
  228. data/db/migrate/20200203111239_add_proposal_valuation_assignments.rb +12 -0
  229. data/db/migrate/20200210135152_add_costs_to_proposals.rb +9 -0
  230. data/db/migrate/20200212120110_sync_proposals_state_with_amendments_state.rb +28 -0
  231. data/db/migrate/20200227175922_add_state_published_at_to_proposals.rb +7 -0
  232. data/db/migrate/20200306123652_publish_existing_proposals_state.rb +15 -0
  233. data/db/migrate/20200708091228_move_proposals_fields_to_i18n.rb +80 -0
  234. data/db/migrate/20200730131631_move_proposal_endorsed_event_notifications_to_resource_endorsed_event.rb +20 -0
  235. data/db/migrate/20200827154156_add_commentable_counter_cache_to_proposals.rb +12 -0
  236. data/db/migrate/20200915151348_fix_proposals_data_to_ensure_title_and_body_are_hashes.rb +37 -0
  237. data/db/migrate/20201002085508_fix_proposals_data.rb +37 -0
  238. data/lib/decidim/content_renderers/proposal_renderer.rb +3 -1
  239. data/lib/decidim/proposals.rb +1 -0
  240. data/lib/decidim/proposals/admin_engine.rb +7 -3
  241. data/lib/decidim/proposals/component.rb +55 -28
  242. data/lib/decidim/proposals/engine.rb +3 -7
  243. data/lib/decidim/proposals/markdown_to_proposals.rb +2 -2
  244. data/lib/decidim/proposals/proposal_serializer.rb +3 -3
  245. data/lib/decidim/proposals/test/capybara_proposals_picker.rb +49 -0
  246. data/lib/decidim/proposals/test/factories.rb +100 -28
  247. data/lib/decidim/proposals/valuatable.rb +21 -0
  248. data/lib/decidim/proposals/version.rb +1 -1
  249. metadata +119 -54
  250. data/app/assets/javascripts/decidim/proposals/identity_selector_dialog.js.es6 +0 -56
  251. data/app/cells/decidim/proposals/endorsers_list/show.erb +0 -17
  252. data/app/cells/decidim/proposals/endorsers_list_cell.rb +0 -31
  253. data/app/commands/decidim/proposals/attachment_methods.rb +0 -43
  254. data/app/commands/decidim/proposals/endorse_proposal.rb +0 -59
  255. data/app/commands/decidim/proposals/unendorse_proposal.rb +0 -40
  256. data/app/controllers/decidim/proposals/proposal_endorsements_controller.rb +0 -60
  257. data/app/models/decidim/proposals/proposal_endorsement.rb +0 -37
  258. data/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +0 -22
  259. data/app/views/decidim/proposals/admin/proposal_notes/index.html.erb +0 -3
  260. data/app/views/decidim/proposals/admin/shared/_info_proposal.html.erb +0 -20
  261. data/app/views/decidim/proposals/proposal_endorsements/_identity.html.erb +0 -9
  262. data/app/views/decidim/proposals/proposal_endorsements/identities.html.erb +0 -12
  263. data/app/views/decidim/proposals/proposal_endorsements/update_buttons_and_counters.js.erb +0 -20
  264. data/app/views/decidim/proposals/proposal_widgets/show.html.erb +0 -4
  265. data/app/views/decidim/proposals/proposals/_endorsement_button.html.erb +0 -11
  266. data/app/views/decidim/proposals/proposals/_endorsement_identities_cabin.html.erb +0 -13
  267. data/app/views/decidim/proposals/versions/_version.html.erb +0 -20
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ # Simple helpers to handle markup variations for proposals
6
+ module ProposalsHelper
7
+ def proposal_reason_callout_args
8
+ {
9
+ announcement: {
10
+ title: proposal_reason_callout_title,
11
+ body: decidim_sanitize(translated_attribute(@proposal.answer))
12
+ },
13
+ callout_class: proposal_reason_callout_class
14
+ }
15
+ end
16
+
17
+ def proposal_reason_callout_class
18
+ case @proposal.state
19
+ when "accepted"
20
+ "success"
21
+ when "evaluating"
22
+ "warning"
23
+ when "rejected"
24
+ "alert"
25
+ else
26
+ ""
27
+ end
28
+ end
29
+
30
+ def proposal_reason_callout_title
31
+ i18n_key = case @proposal.state
32
+ when "evaluating"
33
+ "proposal_in_evaluation_reason"
34
+ else
35
+ "proposal_#{@proposal.state}_reason"
36
+ end
37
+
38
+ t(i18n_key, scope: "decidim.proposals.proposals.show")
39
+ end
40
+
41
+ def filter_proposals_state_values
42
+ Decidim::CheckBoxesTreeHelper::TreeNode.new(
43
+ Decidim::CheckBoxesTreeHelper::TreePoint.new("", t("decidim.proposals.application_helper.filter_state_values.all")),
44
+ [
45
+ Decidim::CheckBoxesTreeHelper::TreePoint.new("accepted", t("decidim.proposals.application_helper.filter_state_values.accepted")),
46
+ Decidim::CheckBoxesTreeHelper::TreePoint.new("evaluating", t("decidim.proposals.application_helper.filter_state_values.evaluating")),
47
+ Decidim::CheckBoxesTreeHelper::TreePoint.new("state_not_published", t("decidim.proposals.application_helper.filter_state_values.not_answered")),
48
+ Decidim::CheckBoxesTreeHelper::TreePoint.new("rejected", t("decidim.proposals.application_helper.filter_state_values.rejected"))
49
+ ]
50
+ )
51
+ end
52
+
53
+ def proposal_has_costs?
54
+ @proposal.cost.present? &&
55
+ translated_attribute(@proposal.cost_report).present? &&
56
+ translated_attribute(@proposal.execution_period).present?
57
+ end
58
+
59
+ def resource_version(resource, options = {})
60
+ return unless resource.respond_to?(:amendable?) && resource.amendable?
61
+
62
+ super
63
+ end
64
+ end
65
+ end
66
+ end
@@ -6,7 +6,7 @@ module Decidim
6
6
  include Decidim::Resourceable
7
7
  include Decidim::Coauthorable
8
8
  include Decidim::HasComponent
9
- include Decidim::ScopableComponent
9
+ include Decidim::ScopableResource
10
10
  include Decidim::HasReference
11
11
  include Decidim::HasCategory
12
12
  include Decidim::Reportable
@@ -28,7 +28,7 @@ module Decidim
28
28
  class_name: "Decidim::User",
29
29
  foreign_key: :decidim_user_id
30
30
 
31
- geocoded_by :address, http_headers: ->(collaborative_draft) { { "Referer" => collaborative_draft.component.organization.host } }
31
+ geocoded_by :address
32
32
 
33
33
  scope :open, -> { where(state: "open") }
34
34
  scope :withdrawn, -> { where(state: "withdrawn") }
@@ -8,6 +8,9 @@ module Decidim
8
8
  include Decidim::HasComponent
9
9
  include Decidim::Traceable
10
10
  include Decidim::Loggable
11
+ include Decidim::TranslatableResource
12
+
13
+ translatable_fields :title, :description
11
14
  end
12
15
  end
13
16
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
  include Decidim::Resourceable
8
8
  include Decidim::Coauthorable
9
9
  include Decidim::HasComponent
10
- include Decidim::ScopableComponent
10
+ include Decidim::ScopableResource
11
11
  include Decidim::HasReference
12
12
  include Decidim::HasCategory
13
13
  include Decidim::Reportable
@@ -19,11 +19,18 @@ module Decidim
19
19
  include Decidim::Loggable
20
20
  include Decidim::Fingerprintable
21
21
  include Decidim::DataPortability
22
- include Decidim::Hashtaggable
23
22
  include Decidim::Proposals::ParticipatoryTextSection
24
23
  include Decidim::Amendable
25
24
  include Decidim::NewsletterParticipant
26
25
  include Decidim::Randomable
26
+ include Decidim::Endorsable
27
+ include Decidim::Proposals::Valuatable
28
+ include Decidim::TranslatableResource
29
+ include Decidim::TranslatableAttributes
30
+
31
+ translatable_fields :title, :body
32
+
33
+ POSSIBLE_STATES = %w(not_answered evaluating accepted rejected withdrawn).freeze
27
34
 
28
35
  fingerprint fields: [:title, :body]
29
36
 
@@ -34,8 +41,6 @@ module Decidim
34
41
 
35
42
  component_manifest_name "proposals"
36
43
 
37
- has_many :endorsements, foreign_key: "decidim_proposal_id", class_name: "ProposalEndorsement", dependent: :destroy, counter_cache: "proposal_endorsements_count"
38
-
39
44
  has_many :votes,
40
45
  -> { final },
41
46
  foreign_key: "decidim_proposal_id",
@@ -47,24 +52,45 @@ module Decidim
47
52
 
48
53
  validates :title, :body, presence: true
49
54
 
50
- geocoded_by :address, http_headers: ->(proposal) { { "Referer" => proposal.component.organization.host } }
55
+ geocoded_by :address
56
+
57
+ scope :answered, -> { where.not(answered_at: nil) }
58
+ scope :not_answered, -> { where(answered_at: nil) }
51
59
 
52
- scope :accepted, -> { where(state: "accepted") }
53
- scope :rejected, -> { where(state: "rejected") }
54
- scope :evaluating, -> { where(state: "evaluating") }
60
+ scope :state_not_published, -> { where(state_published_at: nil) }
61
+ scope :state_published, -> { where.not(state_published_at: nil).where.not(state: nil) }
62
+
63
+ scope :accepted, -> { state_published.where(state: "accepted") }
64
+ scope :rejected, -> { state_published.where(state: "rejected") }
65
+ scope :evaluating, -> { state_published.where(state: "evaluating") }
55
66
  scope :withdrawn, -> { where(state: "withdrawn") }
56
- scope :except_rejected, -> { where.not(state: "rejected").or(where(state: nil)) }
67
+ scope :except_rejected, -> { where.not(state: "rejected").or(state_not_published) }
57
68
  scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
58
69
  scope :drafts, -> { where(published_at: nil) }
70
+ scope :except_drafts, -> { where.not(published_at: nil) }
59
71
  scope :published, -> { where.not(published_at: nil) }
72
+ scope :sort_by_valuation_assignments_count_asc, lambda {
73
+ order(sort_by_valuation_assignments_count_nulls_last_query + "ASC NULLS FIRST")
74
+ }
75
+
76
+ scope :sort_by_valuation_assignments_count_desc, lambda {
77
+ order(sort_by_valuation_assignments_count_nulls_last_query + "DESC NULLS LAST")
78
+ }
79
+
80
+ def self.with_valuation_assigned_to(user, space)
81
+ valuator_roles = space.user_roles(:valuator).where(user: user)
82
+
83
+ includes(:valuation_assignments)
84
+ .where(decidim_proposals_valuation_assignments: { valuator_role_id: valuator_roles })
85
+ end
60
86
 
61
87
  acts_as_list scope: :decidim_component_id
62
88
 
63
89
  searchable_fields({
64
90
  scope_id: :decidim_scope_id,
65
91
  participatory_space: { component: :participatory_space },
66
- D: :search_body,
67
- A: :search_title,
92
+ D: :body,
93
+ A: :title,
68
94
  datetime: :published_at
69
95
  },
70
96
  index_on_create: ->(proposal) { proposal.official? },
@@ -100,10 +126,9 @@ module Decidim
100
126
 
101
127
  participants_has_voted_ids = Decidim::Proposals::ProposalVote.joins(:proposal).where(proposal: proposals).joins(:author).map(&:decidim_author_id).flatten.compact.uniq
102
128
 
103
- endorsements_participants_ids = Decidim::Proposals::ProposalEndorsement.joins(:proposal)
104
- .where(proposal: proposals)
105
- .where(decidim_author_type: "Decidim::UserBaseEntity")
106
- .map(&:decidim_author_id).flatten.compact.uniq
129
+ endorsements_participants_ids = Decidim::Endorsement.where(resource: proposals)
130
+ .where(decidim_author_type: "Decidim::UserBaseEntity")
131
+ .pluck(:decidim_author_id).to_a.compact.uniq
107
132
 
108
133
  (endorsements_participants_ids + participants_has_voted_ids + coauthors_recipients_ids).flatten.compact.uniq
109
134
  end
@@ -124,54 +149,75 @@ module Decidim
124
149
  ProposalVote.where(proposal: self, author: user).any?
125
150
  end
126
151
 
127
- # Public: Check if the user has endorsed the proposal.
128
- # - user_group: may be nil if user is not representing any user_group.
152
+ # Public: Checks if the proposal has been published or not.
129
153
  #
130
154
  # Returns Boolean.
131
- def endorsed_by?(user, user_group = nil)
132
- endorsements.where(author: user, user_group: user_group).any?
155
+ def published?
156
+ published_at.present?
133
157
  end
134
158
 
135
- # Public: Checks if the proposal has been published or not.
159
+ # Public: Returns the published state of the proposal.
136
160
  #
137
161
  # Returns Boolean.
138
- def published?
139
- published_at.present?
162
+ def state
163
+ return amendment.state if emendation?
164
+ return nil unless published_state? || withdrawn?
165
+
166
+ super
167
+ end
168
+
169
+ # This is only used to define the setter, as the getter will be overriden below.
170
+ alias_attribute :internal_state, :state
171
+
172
+ # Public: Returns the internal state of the proposal.
173
+ #
174
+ # Returns Boolean.
175
+ def internal_state
176
+ return amendment.state if emendation?
177
+
178
+ self[:state]
179
+ end
180
+
181
+ # Public: Checks if the organization has published the state for the proposal.
182
+ #
183
+ # Returns Boolean.
184
+ def published_state?
185
+ emendation? || state_published_at.present?
140
186
  end
141
187
 
142
188
  # Public: Checks if the organization has given an answer for the proposal.
143
189
  #
144
190
  # Returns Boolean.
145
191
  def answered?
146
- answered_at.present? && state.present?
192
+ answered_at.present?
193
+ end
194
+
195
+ # Public: Checks if the author has withdrawn the proposal.
196
+ #
197
+ # Returns Boolean.
198
+ def withdrawn?
199
+ internal_state == "withdrawn"
147
200
  end
148
201
 
149
202
  # Public: Checks if the organization has accepted a proposal.
150
203
  #
151
204
  # Returns Boolean.
152
205
  def accepted?
153
- answered? && state == "accepted"
206
+ state == "accepted"
154
207
  end
155
208
 
156
209
  # Public: Checks if the organization has rejected a proposal.
157
210
  #
158
211
  # Returns Boolean.
159
212
  def rejected?
160
- answered? && state == "rejected"
213
+ state == "rejected"
161
214
  end
162
215
 
163
216
  # Public: Checks if the organization has marked the proposal as evaluating it.
164
217
  #
165
218
  # Returns Boolean.
166
219
  def evaluating?
167
- answered? && state == "evaluating"
168
- end
169
-
170
- # Public: Checks if the author has withdrawn the proposal.
171
- #
172
- # Returns Boolean.
173
- def withdrawn?
174
- state == "withdrawn"
220
+ state == "evaluating"
175
221
  end
176
222
 
177
223
  # Public: Overrides the `reported_content_url` Reportable concern method.
@@ -221,7 +267,7 @@ module Decidim
221
267
  def editable_by?(user)
222
268
  return true if draft?
223
269
 
224
- !answered? && within_edit_time_limit? && !copied_from_other_component? && created_by?(user)
270
+ !published_state? && within_edit_time_limit? && !copied_from_other_component? && created_by?(user)
225
271
  end
226
272
 
227
273
  # Checks whether the user can withdraw the given proposal.
@@ -236,15 +282,69 @@ module Decidim
236
282
  published_at.nil?
237
283
  end
238
284
 
239
- # method for sort_link by number of comments
240
- ransacker :commentable_comments_count do
285
+ # Defines the base query so that ransack can actually sort by this value
286
+ def self.sort_by_valuation_assignments_count_nulls_last_query
287
+ <<-SQL
288
+ (
289
+ SELECT COUNT(decidim_proposals_valuation_assignments.id)
290
+ FROM decidim_proposals_valuation_assignments
291
+ WHERE decidim_proposals_valuation_assignments.decidim_proposal_id = decidim_proposals_proposals.id
292
+ GROUP BY decidim_proposals_valuation_assignments.decidim_proposal_id
293
+ )
294
+ SQL
295
+ end
296
+
297
+ # method to filter by assigned valuator role ID
298
+ def self.valuator_role_ids_has(value)
241
299
  query = <<-SQL
242
- (SELECT COUNT(decidim_comments_comments.id)
243
- FROM decidim_comments_comments
244
- WHERE decidim_comments_comments.decidim_commentable_id = decidim_proposals_proposals.id
245
- AND decidim_comments_comments.decidim_commentable_type = 'Decidim::Proposals::Proposal'
246
- GROUP BY decidim_comments_comments.decidim_commentable_id
247
- )
300
+ :value = any(
301
+ (SELECT decidim_proposals_valuation_assignments.valuator_role_id
302
+ FROM decidim_proposals_valuation_assignments
303
+ WHERE decidim_proposals_valuation_assignments.decidim_proposal_id = decidim_proposals_proposals.id
304
+ )
305
+ )
306
+ SQL
307
+ where(query, value: value)
308
+ end
309
+
310
+ def self.ransackable_scopes(_auth = nil)
311
+ [:valuator_role_ids_has]
312
+ end
313
+
314
+ ransacker :state_published do
315
+ Arel.sql("CASE
316
+ WHEN EXISTS (
317
+ SELECT 1 FROM decidim_amendments
318
+ WHERE decidim_amendments.decidim_emendation_type = 'Decidim::Proposals::Proposal'
319
+ AND decidim_amendments.decidim_emendation_id = decidim_proposals_proposals.id
320
+ ) THEN 0
321
+ WHEN state_published_at IS NULL AND answered_at IS NOT NULL THEN 2
322
+ WHEN state_published_at IS NOT NULL THEN 1
323
+ ELSE 0 END
324
+ ")
325
+ end
326
+
327
+ ransacker :state do
328
+ Arel.sql("CASE WHEN state = 'withdrawn' THEN 'withdrawn' WHEN state_published_at IS NULL THEN NULL ELSE state END")
329
+ end
330
+
331
+ ransacker :title do
332
+ Arel.sql(%{("decidim_proposals_proposals"."title")::text})
333
+ end
334
+
335
+ ransacker :id_string do
336
+ Arel.sql(%{cast("decidim_proposals_proposals"."id" as text)})
337
+ end
338
+
339
+ ransacker :is_emendation do |_parent|
340
+ query = <<-SQL
341
+ (
342
+ SELECT EXISTS (
343
+ SELECT 1 FROM decidim_amendments
344
+ WHERE decidim_amendments.decidim_emendation_type = 'Decidim::Proposals::Proposal'
345
+ AND decidim_amendments.decidim_emendation_id = decidim_proposals_proposals.id
346
+ )
347
+ )
248
348
  SQL
249
349
  Arel.sql(query)
250
350
  end
@@ -270,6 +370,17 @@ module Decidim
270
370
  Time.current < limit
271
371
  end
272
372
 
373
+ def process_amendment_state_change!
374
+ return unless %w(accepted rejected evaluating withdrawn).member?(amendment.state)
375
+
376
+ PaperTrail.request(enabled: false) do
377
+ update!(
378
+ state: amendment.state,
379
+ state_published_at: Time.current
380
+ )
381
+ end
382
+ end
383
+
273
384
  private
274
385
 
275
386
  def copied_from_other_component?
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ # A valuation assignment links a proposal and a Valuator user role.
6
+ # Valuators will be users in charge of defining the monetary cost of a
7
+ # proposal.
8
+ class ValuationAssignment < ApplicationRecord
9
+ include Decidim::Traceable
10
+ include Decidim::Loggable
11
+
12
+ belongs_to :proposal, foreign_key: "decidim_proposal_id", class_name: "Decidim::Proposals::Proposal"
13
+ belongs_to :valuator_role, polymorphic: true
14
+
15
+ def self.log_presenter_class_for(_log)
16
+ Decidim::Proposals::AdminLog::ValuationAssignmentPresenter
17
+ end
18
+
19
+ def valuator
20
+ valuator_role.user
21
+ end
22
+ end
23
+ end
24
+ end
@@ -8,18 +8,22 @@ module Decidim
8
8
  # The public part needs to be implemented yet
9
9
  return permission_action if permission_action.scope != :admin
10
10
 
11
- if create_permission_action?
12
- # There's no special condition to create proposal notes, only
13
- # users with access to the admin section can do it.
14
- allow! if permission_action.subject == :proposal_note
15
-
16
- # Proposals can only be created from the admin when the
17
- # corresponding setting is enabled.
18
- toggle_allow(admin_creation_is_enabled?) if permission_action.subject == :proposal
11
+ # Valuators can only perform these actions
12
+ if user_is_valuator?
13
+ if valuator_assigned_to_proposal?
14
+ can_create_proposal_note?
15
+ can_create_proposal_answer?
16
+ end
17
+ can_export_proposals?
18
+ valuator_can_unassign_valuator_from_proposals?
19
+
20
+ return permission_action
21
+ end
19
22
 
20
- # Proposals can only be answered from the admin when the
21
- # corresponding setting is enabled.
22
- toggle_allow(admin_proposal_answering_is_enabled?) if permission_action.subject == :proposal_answer
23
+ if create_permission_action?
24
+ can_create_proposal_note?
25
+ can_create_proposal_from_admin?
26
+ can_create_proposal_answer?
23
27
  end
24
28
 
25
29
  # Admins can only edit official proposals if they are within the
@@ -29,15 +33,30 @@ module Decidim
29
33
  # Every user allowed by the space can update the category of the proposal
30
34
  allow! if permission_action.subject == :proposal_category && permission_action.action == :update
31
35
 
36
+ # Every user allowed by the space can update the scope of the proposal
37
+ allow! if permission_action.subject == :proposal_scope && permission_action.action == :update
38
+
32
39
  # Every user allowed by the space can import proposals from another_component
33
40
  allow! if permission_action.subject == :proposals && permission_action.action == :import
34
41
 
42
+ # Every user allowed by the space can export proposals
43
+ can_export_proposals?
44
+
35
45
  # Every user allowed by the space can merge proposals to another component
36
46
  allow! if permission_action.subject == :proposals && permission_action.action == :merge
37
47
 
38
48
  # Every user allowed by the space can split proposals to another component
39
49
  allow! if permission_action.subject == :proposals && permission_action.action == :split
40
50
 
51
+ # Every user allowed by the space can assign proposals to a valuator
52
+ allow! if permission_action.subject == :proposals && permission_action.action == :assign_to_valuator
53
+
54
+ # Every user allowed by the space can unassign a valuator from proposals
55
+ can_unassign_valuator_from_proposals?
56
+
57
+ # Only admin users can publish many answers at once
58
+ toggle_allow(user.admin?) if permission_action.subject == :proposals && permission_action.action == :publish_answers
59
+
41
60
  if permission_action.subject == :participatory_texts && participatory_texts_are_enabled?
42
61
  # Every user allowed by the space can manage (import, update and publish) participatory texts to proposals
43
62
  allow! if permission_action.action == :manage
@@ -52,6 +71,23 @@ module Decidim
52
71
  @proposal ||= context.fetch(:proposal, nil)
53
72
  end
54
73
 
74
+ def user_valuator_role
75
+ @user_valuator_role ||= space.user_roles(:valuator).find_by(user: user)
76
+ end
77
+
78
+ def user_is_valuator?
79
+ return if user.admin?
80
+
81
+ user_valuator_role.present?
82
+ end
83
+
84
+ def valuator_assigned_to_proposal?
85
+ @valuator_assigned_to_proposal ||=
86
+ Decidim::Proposals::ValuationAssignment
87
+ .where(proposal: proposal, valuator_role: user_valuator_role)
88
+ .any?
89
+ end
90
+
55
91
  def admin_creation_is_enabled?
56
92
  current_settings.try(:creation_enabled?) &&
57
93
  component_settings.try(:official_proposals_enabled)
@@ -75,6 +111,36 @@ module Decidim
75
111
  def participatory_texts_are_enabled?
76
112
  component_settings.participatory_texts_enabled?
77
113
  end
114
+
115
+ # There's no special condition to create proposal notes, only
116
+ # users with access to the admin section can do it.
117
+ def can_create_proposal_note?
118
+ allow! if permission_action.subject == :proposal_note
119
+ end
120
+
121
+ # Proposals can only be created from the admin when the
122
+ # corresponding setting is enabled.
123
+ def can_create_proposal_from_admin?
124
+ toggle_allow(admin_creation_is_enabled?) if permission_action.subject == :proposal
125
+ end
126
+
127
+ # Proposals can only be answered from the admin when the
128
+ # corresponding setting is enabled.
129
+ def can_create_proposal_answer?
130
+ toggle_allow(admin_proposal_answering_is_enabled?) if permission_action.subject == :proposal_answer
131
+ end
132
+
133
+ def can_unassign_valuator_from_proposals?
134
+ allow! if permission_action.subject == :proposals && permission_action.action == :unassign_from_valuator
135
+ end
136
+
137
+ def valuator_can_unassign_valuator_from_proposals?
138
+ can_unassign_valuator_from_proposals? if user == context.fetch(:valuator, nil)
139
+ end
140
+
141
+ def can_export_proposals?
142
+ allow! if permission_action.subject == :proposals && permission_action.action == :export
143
+ end
78
144
  end
79
145
  end
80
146
  end