decidim-core 0.29.2 → 0.30.0.rc2

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 (484) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/amendable/amend_button_card/show.erb +6 -4
  3. data/app/cells/decidim/amendable/amend_button_card_cell.rb +8 -0
  4. data/app/cells/decidim/amendable/amendments/show.erb +1 -1
  5. data/app/cells/decidim/amendable/promote_button_card/show.erb +2 -0
  6. data/app/cells/decidim/author_cell.rb +1 -1
  7. data/app/cells/decidim/card_l/extra_data.erb +1 -0
  8. data/app/cells/decidim/card_metadata_cell.rb +9 -16
  9. data/app/cells/decidim/comments_button_cell.rb +14 -2
  10. data/app/cells/decidim/content_blocks/highlighted_elements_cell.rb +2 -0
  11. data/app/cells/decidim/content_blocks/highlighted_elements_with_cell_for_list_cell.rb +1 -1
  12. data/app/cells/decidim/endorsement_block/show.erb +0 -1
  13. data/app/cells/decidim/endorsement_block_cell.rb +6 -0
  14. data/app/cells/decidim/endorsement_buttons/button_content.erb +1 -1
  15. data/app/cells/decidim/endorsement_buttons/select_identity_button.erb +1 -1
  16. data/app/cells/decidim/endorsement_buttons/show.erb +7 -7
  17. data/app/cells/decidim/endorsement_buttons/verification_modal.erb +4 -2
  18. data/app/cells/decidim/endorsers_list/empty.erb +3 -0
  19. data/app/cells/decidim/endorsers_list/full.erb +17 -5
  20. data/app/cells/decidim/endorsers_list/show.erb +19 -10
  21. data/app/cells/decidim/endorsers_list_cell.rb +19 -6
  22. data/app/cells/decidim/follow_button/show.erb +1 -2
  23. data/app/cells/decidim/follow_button_cell.rb +5 -0
  24. data/app/cells/decidim/map/show.erb +3 -0
  25. data/app/cells/decidim/map/template.erb +14 -0
  26. data/app/cells/decidim/map_cell.rb +39 -0
  27. data/app/cells/decidim/notification_actions/buttons_cell.rb +1 -1
  28. data/app/cells/decidim/onboarding_action_message/show.erb +15 -0
  29. data/app/cells/decidim/onboarding_action_message_cell.rb +81 -0
  30. data/app/cells/decidim/participatory_space_private_user/show.erb +23 -0
  31. data/app/cells/decidim/participatory_space_private_user_cell.rb +21 -0
  32. data/app/cells/decidim/progress_bar/show.erb +2 -2
  33. data/app/cells/decidim/report_button_cell.rb +1 -0
  34. data/app/cells/decidim/resource_history/show.erb +20 -0
  35. data/app/cells/decidim/resource_history_cell.rb +66 -0
  36. data/app/cells/decidim/share_button_cell.rb +0 -4
  37. data/app/cells/decidim/share_widget/modal.erb +26 -0
  38. data/app/cells/decidim/share_widget/show.erb +2 -0
  39. data/app/cells/decidim/share_widget_cell.rb +20 -0
  40. data/app/cells/decidim/tags/show.erb +7 -4
  41. data/app/cells/decidim/tags_cell.rb +11 -72
  42. data/app/commands/decidim/amendable/create_draft.rb +2 -2
  43. data/app/commands/decidim/create_ephemeral_user.rb +52 -0
  44. data/app/commands/decidim/create_omniauth_registration.rb +13 -3
  45. data/app/commands/decidim/create_report.rb +1 -5
  46. data/app/commands/decidim/create_user_group.rb +6 -2
  47. data/app/commands/decidim/destroy_ephemeral_user.rb +47 -0
  48. data/app/commands/decidim/update_account.rb +11 -3
  49. data/app/commands/decidim/update_notifications_settings.rb +1 -0
  50. data/app/commands/decidim/update_resources_taxonomies.rb +67 -0
  51. data/app/commands/decidim/update_user_group.rb +7 -1
  52. data/app/constraints/decidim/current_component.rb +1 -1
  53. data/app/controllers/concerns/decidim/ajax_permission_handler.rb +21 -0
  54. data/app/controllers/concerns/decidim/devise_authentication_methods.rb +3 -6
  55. data/app/controllers/concerns/decidim/devise_controllers.rb +2 -0
  56. data/app/controllers/concerns/decidim/disable_redirection_to_external_host.rb +1 -1
  57. data/app/controllers/concerns/decidim/ephemeral_session_checker.rb +87 -0
  58. data/app/controllers/concerns/decidim/filter_resource.rb +4 -2
  59. data/app/controllers/concerns/decidim/has_members_page.rb +25 -0
  60. data/app/controllers/concerns/decidim/headers/browser_feature_permissions.rb +50 -0
  61. data/app/controllers/concerns/decidim/locale_switcher.rb +2 -2
  62. data/app/controllers/concerns/decidim/needs_password_change.rb +0 -1
  63. data/app/controllers/concerns/decidim/needs_permission.rb +2 -1
  64. data/app/controllers/concerns/decidim/needs_tos_accepted.rb +4 -5
  65. data/app/controllers/concerns/decidim/onboarding_action_methods.rb +52 -0
  66. data/app/controllers/decidim/application_controller.rb +10 -0
  67. data/app/controllers/decidim/authorization_modals_controller.rb +8 -2
  68. data/app/controllers/decidim/components/base_controller.rb +3 -7
  69. data/app/controllers/decidim/devise/confirmations_controller.rb +4 -0
  70. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +8 -1
  71. data/app/controllers/decidim/devise/registrations_controller.rb +1 -1
  72. data/app/controllers/decidim/devise/sessions_controller.rb +2 -0
  73. data/app/controllers/decidim/download_your_data_controller.rb +33 -2
  74. data/app/controllers/decidim/editor_images_controller.rb +1 -11
  75. data/app/controllers/decidim/follows_controller.rb +1 -1
  76. data/app/controllers/decidim/geolocation_controller.rb +19 -0
  77. data/app/controllers/decidim/homepage_controller.rb +0 -1
  78. data/app/controllers/decidim/open_data_controller.rb +33 -8
  79. data/app/controllers/decidim/user_conversations_controller.rb +1 -11
  80. data/app/events/decidim/soft_delete_resource_event.rb +15 -0
  81. data/app/forms/decidim/amendable/form.rb +4 -2
  82. data/app/forms/decidim/ephemeral_user_form.rb +22 -0
  83. data/app/forms/decidim/notifications_settings_form.rb +1 -0
  84. data/app/forms/decidim/omniauth_registration_form.rb +14 -1
  85. data/app/helpers/concerns/decidim/flash_helper_extensions.rb +17 -2
  86. data/app/helpers/decidim/action_authorization_helper.rb +75 -14
  87. data/app/helpers/decidim/amendments_helper.rb +0 -9
  88. data/app/helpers/decidim/application_helper.rb +6 -2
  89. data/app/helpers/decidim/breadcrumb_helper.rb +6 -0
  90. data/app/helpers/decidim/cache_helper.rb +1 -1
  91. data/app/helpers/decidim/check_boxes_tree_helper.rb +25 -53
  92. data/app/helpers/decidim/decidim_form_helper.rb +0 -44
  93. data/app/helpers/decidim/endorsable_helper.rb +0 -10
  94. data/app/helpers/decidim/layout_helper.rb +6 -0
  95. data/app/helpers/decidim/messaging/conversation_helper.rb +2 -3
  96. data/app/helpers/decidim/meta_tags_helper.rb +32 -23
  97. data/app/helpers/decidim/modal_helper.rb +23 -0
  98. data/app/helpers/decidim/omniauth_helper.rb +5 -5
  99. data/app/helpers/decidim/onboarding_action_helper.rb +13 -0
  100. data/app/helpers/decidim/orders_helper.rb +2 -1
  101. data/app/helpers/decidim/participatory_space_helpers.rb +1 -1
  102. data/app/helpers/decidim/passwords_helper.rb +2 -2
  103. data/app/helpers/decidim/taxonomies_helper.rb +40 -0
  104. data/app/jobs/decidim/download_your_data_export_job.rb +2 -19
  105. data/app/jobs/decidim/event_publisher_job.rb +18 -9
  106. data/app/jobs/decidim/export_job.rb +5 -1
  107. data/app/jobs/decidim/export_participatory_space_job.rb +4 -1
  108. data/app/jobs/decidim/hide_child_resources_job.rb +24 -0
  109. data/app/jobs/decidim/open_data_job.rb +4 -4
  110. data/app/mailers/decidim/export_mailer.rb +7 -12
  111. data/app/models/decidim/action_log.rb +11 -0
  112. data/app/models/decidim/category.rb +4 -0
  113. data/app/models/decidim/component.rb +25 -3
  114. data/app/models/decidim/metric.rb +2 -1
  115. data/app/models/decidim/moderation.rb +16 -0
  116. data/app/models/decidim/newsletter.rb +12 -8
  117. data/app/models/decidim/organization.rb +7 -3
  118. data/app/models/decidim/participatory_space_private_user.rb +20 -0
  119. data/app/models/decidim/private_export.rb +24 -0
  120. data/app/models/decidim/push_notification_message.rb +1 -1
  121. data/app/models/decidim/report.rb +1 -1
  122. data/app/models/decidim/scope.rb +4 -0
  123. data/app/models/decidim/share_token.rb +36 -10
  124. data/app/models/decidim/taxonomization.rb +23 -0
  125. data/app/models/decidim/taxonomy.rb +139 -0
  126. data/app/models/decidim/taxonomy_filter.rb +130 -0
  127. data/app/models/decidim/taxonomy_filter_item.rb +33 -0
  128. data/app/models/decidim/user.rb +9 -23
  129. data/app/models/decidim/user_base_entity.rb +12 -0
  130. data/app/models/decidim/user_moderation.rb +9 -0
  131. data/app/models/decidim/user_report.rb +4 -0
  132. data/app/packs/entrypoints/decidim_core.js +1 -0
  133. data/app/packs/entrypoints/decidim_geocoding.js +2 -0
  134. data/app/packs/images/decidim/brands/facebook.svg +10 -0
  135. data/app/packs/images/decidim/brands/google.svg +7 -1
  136. data/app/packs/images/decidim/brands/twitter-x.svg +3 -0
  137. data/app/packs/src/decidim/check_boxes_tree.js +0 -1
  138. data/app/packs/src/decidim/clipboard.js +18 -9
  139. data/app/packs/src/decidim/confirm.js +79 -59
  140. data/app/packs/src/decidim/decidim_application.js +1 -0
  141. data/app/packs/src/decidim/form_remote.js +1 -1
  142. data/app/packs/src/decidim/geocoding/reverse_geocoding.js +60 -0
  143. data/app/packs/src/decidim/impersonation.js +1 -1
  144. data/app/packs/src/decidim/index.js +11 -1
  145. data/app/packs/src/decidim/map/provider/here.js +1 -1
  146. data/app/packs/src/decidim/onboarding_pending_action.js +24 -0
  147. data/app/packs/src/decidim/session_timeouter.js +1 -1
  148. data/app/packs/src/decidim/sticky_footer.js +29 -0
  149. data/app/packs/src/decidim/sticky_header.js +6 -31
  150. data/app/packs/src/decidim/user_registrations.js +13 -0
  151. data/app/packs/src/decidim/utilities/dom.js +148 -0
  152. data/app/packs/stylesheets/decidim/_accordion.scss +30 -0
  153. data/app/packs/stylesheets/decidim/_buttons.scss +19 -0
  154. data/app/packs/stylesheets/decidim/_dropdown.scss +1 -1
  155. data/app/packs/stylesheets/decidim/_endorsers_list.scss +26 -20
  156. data/app/packs/stylesheets/decidim/_forms.scss +2 -2
  157. data/app/packs/stylesheets/decidim/_hashtags.scss +5 -0
  158. data/app/packs/stylesheets/decidim/_header.scss +5 -3
  159. data/app/packs/stylesheets/decidim/_layout.scss +32 -2
  160. data/app/packs/stylesheets/decidim/_login.scss +53 -6
  161. data/app/packs/stylesheets/decidim/_modal.scss +42 -0
  162. data/app/packs/stylesheets/decidim/_modal_tos_refuse.scss +4 -0
  163. data/app/packs/stylesheets/decidim/_participatory_spaces.scss +46 -0
  164. data/app/packs/stylesheets/decidim/_profile.scss +1 -1
  165. data/app/packs/stylesheets/decidim/_success_image.scss +64 -0
  166. data/app/packs/stylesheets/decidim/_tribute.scss +36 -0
  167. data/app/packs/stylesheets/decidim/application.scss +2 -0
  168. data/app/packs/stylesheets/decidim/geocoding_addons.scss +5 -0
  169. data/app/packs/stylesheets/decidim/map.scss +7 -10
  170. data/app/packs/stylesheets/decidim/resource_history.scss +31 -0
  171. data/app/permissions/decidim/permissions.rb +8 -2
  172. data/app/presenters/decidim/admin_log/base_user_presenter.rb +67 -0
  173. data/app/presenters/decidim/admin_log/component_presenter.rb +32 -3
  174. data/app/presenters/decidim/admin_log/moderation_presenter.rb +30 -3
  175. data/app/presenters/decidim/admin_log/share_token_presenter.rb +39 -0
  176. data/app/presenters/decidim/admin_log/taxonomy_filter_presenter.rb +57 -0
  177. data/app/presenters/decidim/admin_log/taxonomy_presenter.rb +48 -0
  178. data/app/presenters/decidim/admin_log/user_group_presenter.rb +6 -6
  179. data/app/presenters/decidim/admin_log/user_presenter.rb +4 -18
  180. data/app/presenters/decidim/log/value_types/date_presenter.rb +1 -0
  181. data/app/presenters/decidim/log/value_types/taxonomy_presenter.rb +29 -0
  182. data/app/presenters/decidim/participatory_space_private_user_presenter.rb +50 -0
  183. data/app/presenters/decidim/taxonomy_filter_presenter.rb +19 -0
  184. data/app/presenters/decidim/taxonomy_presenter.rb +14 -0
  185. data/app/queries/decidim/metrics/blocked_users_metric_manage.rb +2 -2
  186. data/app/queries/decidim/metrics/users_metric_manage.rb +2 -2
  187. data/app/queries/decidim/public_activities.rb +1 -12
  188. data/app/queries/decidim/stats_users_count.rb +2 -2
  189. data/app/resolvers/decidim/meta_image_url_resolver.rb +128 -0
  190. data/app/serializers/decidim/exporters/open_data_blocked_user_serializer.rb +25 -0
  191. data/app/serializers/decidim/exporters/open_data_metric_serializer.rb +22 -0
  192. data/app/serializers/decidim/exporters/open_data_moderation_serializer.rb +30 -0
  193. data/app/serializers/decidim/exporters/open_data_taxonomy_serializer.rb +30 -0
  194. data/app/serializers/decidim/exporters/open_data_user_group_serializer.rb +38 -0
  195. data/app/serializers/decidim/exporters/open_data_user_serializer.rb +39 -0
  196. data/app/serializers/decidim/exporters/participatory_space_serializer.rb +104 -0
  197. data/app/serializers/decidim/exporters/serializer.rb +25 -0
  198. data/app/serializers/decidim/schema_org_breadcrumb_list_serializer.rb +52 -0
  199. data/app/services/decidim/action_authorizer.rb +32 -3
  200. data/app/services/decidim/base_diff_renderer.rb +1 -1
  201. data/app/services/decidim/download_your_data_exporter.rb +58 -24
  202. data/app/services/decidim/onboarding_manager.rb +272 -0
  203. data/app/services/decidim/open_data_exporter.rb +139 -13
  204. data/app/services/decidim/traceability.rb +13 -0
  205. data/app/uploaders/decidim/application_uploader.rb +1 -1
  206. data/app/validators/etiquette_validator.rb +9 -2
  207. data/app/validators/passthru_validator.rb +1 -1
  208. data/app/validators/translated_etiquette_validator.rb +32 -0
  209. data/app/views/decidim/account/_password_fields.html.erb +2 -2
  210. data/app/views/decidim/application/_accordion_section.html.erb +14 -0
  211. data/app/views/decidim/application/_collection.html.erb +30 -18
  212. data/app/views/decidim/devise/omniauth_registrations/new.html.erb +5 -1
  213. data/app/views/decidim/devise/omniauth_registrations/new_tos_fields.html.erb +29 -0
  214. data/app/views/decidim/devise/registrations/new.html.erb +10 -22
  215. data/app/views/decidim/devise/sessions/new.html.erb +24 -29
  216. data/app/views/decidim/devise/shared/_omniauth_buttons.html.erb +2 -4
  217. data/app/views/decidim/devise/shared/_tos_fields.html.erb +16 -0
  218. data/app/views/decidim/download_your_data/_export.html.erb +15 -0
  219. data/app/views/decidim/download_your_data/show.html.erb +51 -5
  220. data/app/views/decidim/endorsements/update_buttons_and_counters.js.erb +16 -19
  221. data/app/views/decidim/export_mailer/download_your_data_export.html.erb +2 -2
  222. data/app/views/decidim/export_mailer/export.html.erb +5 -1
  223. data/app/views/decidim/notifications_settings/show.html.erb +22 -0
  224. data/app/views/decidim/open_data/_how_to_open_accordion_section.html.erb +15 -0
  225. data/app/views/decidim/open_data/index.html.erb +108 -0
  226. data/app/views/decidim/pages/show.html.erb +2 -1
  227. data/app/views/decidim/participatory_space_private_users/_participatory_space_private_user.html.erb +1 -0
  228. data/app/views/decidim/shared/_login_modal.html.erb +26 -20
  229. data/app/views/decidim/shared/_orders.html.erb +2 -2
  230. data/app/views/decidim/shared/_resource_actions.html.erb +21 -0
  231. data/app/views/decidim/shared/filters/_check_boxes_tree.html.erb +1 -2
  232. data/app/views/decidim/shared/filters/_collection.html.erb +1 -1
  233. data/app/views/decidim/shared/filters/_dropdown_label.html.erb +7 -4
  234. data/app/views/layouts/decidim/_application.html.erb +0 -1
  235. data/app/views/layouts/decidim/_js_configuration.html.erb +1 -0
  236. data/app/views/layouts/decidim/_logo.html.erb +1 -1
  237. data/app/views/layouts/decidim/_logo_mobile.html.erb +1 -1
  238. data/app/views/layouts/decidim/_meta_tags_config.html.erb +6 -11
  239. data/app/views/layouts/decidim/_schema_org_breadcrumb_list.html.erb +3 -0
  240. data/app/views/layouts/decidim/_wrapper.html.erb +15 -9
  241. data/app/views/layouts/decidim/footer/_main_links.html.erb +1 -1
  242. data/app/views/layouts/decidim/header/_close_ephemeral_session.html.erb +25 -0
  243. data/app/views/layouts/decidim/header/_main.html.erb +14 -10
  244. data/app/views/layouts/decidim/header/_menu_breadcrumb_items.html.erb +2 -0
  245. data/app/views/layouts/decidim/shared/_layout_item.html.erb +1 -1
  246. data/config/assets.rb +2 -1
  247. data/config/locales/ar.yml +27 -59
  248. data/config/locales/bg.yml +3 -64
  249. data/config/locales/ca.yml +370 -61
  250. data/config/locales/cs.yml +371 -54
  251. data/config/locales/de.yml +365 -54
  252. data/config/locales/el.yml +1 -48
  253. data/config/locales/en.yml +363 -54
  254. data/config/locales/eo.yml +0 -2
  255. data/config/locales/es-MX.yml +363 -57
  256. data/config/locales/es-PY.yml +363 -57
  257. data/config/locales/es.yml +367 -58
  258. data/config/locales/eu.yml +373 -65
  259. data/config/locales/fi-plain.yml +365 -57
  260. data/config/locales/fi.yml +370 -59
  261. data/config/locales/fr-CA.yml +178 -54
  262. data/config/locales/fr.yml +179 -52
  263. data/config/locales/ga-IE.yml +0 -21
  264. data/config/locales/gl.yml +1 -34
  265. data/config/locales/hu.yml +1 -53
  266. data/config/locales/id-ID.yml +1 -34
  267. data/config/locales/is-IS.yml +0 -16
  268. data/config/locales/it.yml +5 -60
  269. data/config/locales/ja.yml +215 -56
  270. data/config/locales/lb.yml +1 -52
  271. data/config/locales/lt.yml +1 -59
  272. data/config/locales/lv.yml +1 -40
  273. data/config/locales/nl.yml +1 -53
  274. data/config/locales/no.yml +2 -52
  275. data/config/locales/pl.yml +4 -66
  276. data/config/locales/pt-BR.yml +11 -52
  277. data/config/locales/pt.yml +2 -52
  278. data/config/locales/ro-RO.yml +219 -117
  279. data/config/locales/ru.yml +0 -20
  280. data/config/locales/sk.yml +1 -41
  281. data/config/locales/sv.yml +138 -59
  282. data/config/locales/tr-TR.yml +1 -50
  283. data/config/locales/uk.yml +0 -18
  284. data/config/locales/zh-CN.yml +1 -50
  285. data/config/locales/zh-TW.yml +1 -54
  286. data/config/routes.rb +4 -10
  287. data/db/migrate/20181025082245_add_timestamps_to_components.rb +2 -0
  288. data/db/migrate/20240628111905_add_visible_to_components.rb +7 -0
  289. data/db/migrate/20240704115429_create_decidim_taxonomies.rb +23 -0
  290. data/db/migrate/20240717093514_add_registered_only_to_decidim_share_tokens.rb +7 -0
  291. data/db/migrate/20240722215500_change_object_changes_on_versions.rb +39 -0
  292. data/db/migrate/20240806065417_create_decidim_taxonomy_filters.rb +23 -0
  293. data/db/migrate/20240822161348_add_deleted_at_to_decidim_components.rb +8 -0
  294. data/db/migrate/20240906162524_add_part_of_to_taxonomies.rb +26 -0
  295. data/db/migrate/20241001135825_add_role_to_participatory_space_private_users.rb +7 -0
  296. data/db/migrate/20241001140408_add_published_to_participatory_space_private_users.rb +7 -0
  297. data/db/migrate/20241022002600_create_private_exports.rb +18 -0
  298. data/db/migrate/20241028114430_add_component_counter_to_taxonomy_filters.rb +7 -0
  299. data/db/migrate/20241111104357_add_names_and_space_boolean_to_taxonomy_filters.rb +9 -0
  300. data/db/migrate/20241127093708_add_taxonomy_to_metrics.rb +7 -0
  301. data/db/migrate/20241204121445_add_participatory_space_manifests_from_taxonomy_filters.rb +9 -0
  302. data/decidim-core.gemspec +14 -17
  303. data/lib/decidim/action_authorization.rb +3 -2
  304. data/lib/decidim/amendable.rb +1 -1
  305. data/lib/decidim/api/input_filters/category_input_filter.rb +5 -3
  306. data/lib/decidim/api/input_filters/component_input_filter.rb +33 -20
  307. data/lib/decidim/api/input_filters/user_entity_input_filter.rb +53 -44
  308. data/lib/decidim/api/input_sorts/component_input_sort.rb +15 -13
  309. data/lib/decidim/api/input_sorts/user_entity_input_sort.rb +4 -4
  310. data/lib/decidim/api/interfaces/author_interface.rb +3 -2
  311. data/lib/decidim/api/interfaces/coauthorable_interface.rb +5 -5
  312. data/lib/decidim/api/interfaces/endorsable_interface.rb +2 -2
  313. data/lib/decidim/api/interfaces/participatory_space_interface.rb +5 -10
  314. data/lib/decidim/api/interfaces/taxonomizable_interface.rb +13 -0
  315. data/lib/decidim/api/types/amendment_type.rb +5 -7
  316. data/lib/decidim/api/types/area_api_type.rb +3 -3
  317. data/lib/decidim/api/types/attachment_type.rb +3 -3
  318. data/lib/decidim/api/types/category_type.rb +2 -2
  319. data/lib/decidim/api/types/decidim_type.rb +5 -1
  320. data/lib/decidim/api/types/fingerprint_type.rb +1 -1
  321. data/lib/decidim/api/types/localized_string_type.rb +1 -1
  322. data/lib/decidim/api/types/metric_history_type.rb +2 -0
  323. data/lib/decidim/api/types/metric_type.rb +1 -1
  324. data/lib/decidim/api/types/organization_type.rb +5 -0
  325. data/lib/decidim/api/types/participatory_space_link_type.rb +2 -2
  326. data/lib/decidim/api/types/participatory_space_manifest_type.rb +1 -1
  327. data/lib/decidim/api/types/quantifiable_translated_field_type.rb +1 -1
  328. data/lib/decidim/api/types/scope_api_type.rb +2 -3
  329. data/lib/decidim/api/types/session_type.rb +1 -2
  330. data/lib/decidim/api/types/taxonomy_type.rb +15 -0
  331. data/lib/decidim/api/types/trace_version_type.rb +2 -2
  332. data/lib/decidim/api/types/translated_field_type.rb +3 -5
  333. data/lib/decidim/api/types/user_group_type.rb +7 -20
  334. data/lib/decidim/api/types/user_type.rb +7 -20
  335. data/lib/decidim/asset_router/storage.rb +4 -4
  336. data/lib/decidim/assets/tailwind/tailwind.config.js.erb +2 -1
  337. data/lib/decidim/attachment_attributes.rb +1 -1
  338. data/lib/decidim/attribute_encryptor.rb +1 -1
  339. data/lib/decidim/attribute_object/nested_validator.rb +1 -1
  340. data/lib/decidim/attributes/integer_with_units.rb +27 -0
  341. data/lib/decidim/attributes.rb +2 -0
  342. data/lib/decidim/authorization_form_builder.rb +0 -7
  343. data/lib/decidim/coauthorable.rb +1 -1
  344. data/lib/decidim/command.rb +3 -3
  345. data/lib/decidim/commands/destroy_resource.rb +5 -1
  346. data/lib/decidim/commands/resource_handler.rb +2 -2
  347. data/lib/decidim/commands/restore_resource.rb +45 -0
  348. data/lib/decidim/commands/soft_delete_resource.rb +63 -0
  349. data/lib/decidim/component_manifest.rb +7 -0
  350. data/lib/decidim/core/api.rb +2 -0
  351. data/lib/decidim/core/engine.rb +21 -7
  352. data/lib/decidim/core/menu.rb +0 -5
  353. data/lib/decidim/core/seeds.rb +42 -2
  354. data/lib/decidim/core/test/factories.rb +105 -2
  355. data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +85 -0
  356. data/lib/decidim/core/test/shared_examples/comments_examples.rb +393 -38
  357. data/lib/decidim/core/test/shared_examples/components_controller_hide_shared_examples.rb +11 -0
  358. data/lib/decidim/core/test/shared_examples/components_controller_reorder_shared_examples.rb +21 -0
  359. data/lib/decidim/core/test/shared_examples/download_open_data_shared_context.rb +36 -0
  360. data/lib/decidim/core/test/shared_examples/download_open_data_shared_examples.rb +45 -0
  361. data/lib/decidim/core/test/shared_examples/download_your_data_shared_examples.rb +17 -0
  362. data/lib/decidim/core/test/shared_examples/etiquette_validator_examples.rb +25 -0
  363. data/lib/decidim/core/test/shared_examples/follows_examples.rb +43 -0
  364. data/lib/decidim/core/test/shared_examples/has_taxonomies.rb +94 -0
  365. data/lib/decidim/core/test/shared_examples/manage_share_tokens_examples.rb +237 -0
  366. data/lib/decidim/core/test/shared_examples/map_examples.rb +2 -0
  367. data/lib/decidim/core/test/shared_examples/open_data_exporter_examples.rb +102 -0
  368. data/lib/decidim/core/test/shared_examples/participatory_space_members_page_examples.rb +46 -0
  369. data/lib/decidim/core/test/shared_examples/participatory_space_search_examples.rb +98 -0
  370. data/lib/decidim/core/test/shared_examples/permissions.rb +7 -7
  371. data/lib/decidim/core/test/shared_examples/preview_with_share_token_examples.rb +94 -0
  372. data/lib/decidim/core/test/shared_examples/reports_examples.rb +60 -5
  373. data/lib/decidim/core/test/shared_examples/resource_endorsed_event_examples.rb +2 -2
  374. data/lib/decidim/core/test/shared_examples/resource_search_examples.rb +75 -69
  375. data/lib/decidim/core/test/shared_examples/searchable_resources_shared_context.rb +1 -1
  376. data/lib/decidim/core/test/shared_examples/simple_event.rb +33 -19
  377. data/lib/decidim/core/test/shared_examples/social_share_examples.rb +46 -0
  378. data/lib/decidim/core/test/shared_examples/softdeleteable_components_examples.rb +173 -0
  379. data/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb +54 -8
  380. data/lib/decidim/core/test/shared_examples/taxonomizable_interface_examples.rb +20 -0
  381. data/lib/decidim/core/test/shared_examples/taxonomizable_resource_examples.rb +37 -0
  382. data/lib/decidim/core/test/shared_examples/taxonomy_settings.rb +49 -0
  383. data/lib/decidim/core/test/shared_examples/translated_event_examples.rb +1 -1
  384. data/lib/decidim/core/test/shared_examples/versions_controller_examples.rb +2 -2
  385. data/lib/decidim/core/test/shared_examples/with_endorsable_permissions_examples.rb +1 -1
  386. data/lib/decidim/core/test.rb +9 -5
  387. data/lib/decidim/core/version.rb +1 -1
  388. data/lib/decidim/core.rb +58 -0
  389. data/lib/decidim/download_your_data_serializers/download_your_data_identity_serializer.rb +0 -4
  390. data/lib/decidim/download_your_data_serializers/download_your_data_participatory_space_private_user_serializer.rb +3 -1
  391. data/lib/decidim/download_your_data_serializers/download_your_data_report_serializer.rb +2 -1
  392. data/lib/decidim/download_your_data_serializers/download_your_data_user_serializer.rb +6 -7
  393. data/lib/decidim/download_your_data_serializers.rb +19 -0
  394. data/lib/decidim/exporters/csv.rb +6 -0
  395. data/lib/decidim/exporters/export_manifest.rb +14 -0
  396. data/lib/decidim/exporters/pdf.rb +26 -22
  397. data/lib/decidim/filter_form_builder.rb +12 -15
  398. data/lib/decidim/form_builder.rb +6 -129
  399. data/lib/decidim/has_category.rb +5 -1
  400. data/lib/decidim/has_private_users.rb +4 -0
  401. data/lib/decidim/has_taxonomy_settings.rb +47 -0
  402. data/lib/decidim/has_upload_validations.rb +1 -1
  403. data/lib/decidim/legacy_form_builder.rb +118 -0
  404. data/lib/decidim/maintenance/import_models/application_record.rb +93 -0
  405. data/lib/decidim/maintenance/import_models/area.rb +82 -0
  406. data/lib/decidim/maintenance/import_models/area_type.rb +12 -0
  407. data/lib/decidim/maintenance/import_models/assembly_type.rb +49 -0
  408. data/lib/decidim/maintenance/import_models/categorization.rb +14 -0
  409. data/lib/decidim/maintenance/import_models/category.rb +139 -0
  410. data/lib/decidim/maintenance/import_models/participatory_process_type.rb +21 -0
  411. data/lib/decidim/maintenance/import_models/scope.rb +163 -0
  412. data/lib/decidim/maintenance/import_models.rb +19 -0
  413. data/lib/decidim/maintenance/taxonomy_importer.rb +142 -0
  414. data/lib/decidim/maintenance/taxonomy_plan.rb +78 -0
  415. data/lib/decidim/maintenance.rb +12 -0
  416. data/lib/decidim/map/autocomplete.rb +52 -8
  417. data/lib/decidim/map/dynamic_map.rb +1 -1
  418. data/lib/decidim/map/provider/dynamic_map/here.rb +1 -40
  419. data/lib/decidim/map/provider/geocoding/here.rb +9 -3
  420. data/lib/decidim/map.rb +3 -6
  421. data/lib/decidim/moderation_tools.rb +29 -5
  422. data/lib/decidim/newsletter_encryptor.rb +1 -1
  423. data/lib/decidim/paddable.rb +1 -1
  424. data/lib/decidim/participable.rb +10 -10
  425. data/lib/decidim/participatory_space_user.rb +10 -0
  426. data/lib/decidim/private_download_helper.rb +15 -0
  427. data/lib/decidim/record_encryptor.rb +51 -46
  428. data/lib/decidim/resourceable.rb +16 -0
  429. data/lib/decidim/seeds.rb +18 -20
  430. data/lib/decidim/settings_manifest.rb +34 -1
  431. data/lib/decidim/soft_deletable.rb +17 -0
  432. data/lib/decidim/taxonomizable.rb +72 -0
  433. data/lib/decidim/view_model.rb +0 -1
  434. data/lib/tasks/decidim_download_your_data_tasks.rake +2 -4
  435. data/lib/tasks/decidim_procfile.rake +6 -0
  436. data/lib/tasks/decidim_tasks.rake +1 -0
  437. data/lib/tasks/decidim_taxonomies.rake +160 -0
  438. data/lib/tasks/upgrade/{decidim_fix_categorization.rake → clean.rake} +25 -18
  439. data/lib/tasks/upgrade/clean_hidden_resources.rake +33 -0
  440. data/lib/tasks/upgrade/decidim_active_storage_migration_tasks.rake +1 -1
  441. data/lib/tasks/upgrade/migrations.rake +94 -0
  442. metadata +198 -103
  443. data/app/cells/decidim/amendable/amenders_list/show.erb +0 -0
  444. data/app/cells/decidim/content_blocks/base/content.erb +0 -0
  445. data/app/cells/decidim/endorsers_list/full_endorsers_list.erb +0 -3
  446. data/app/cells/decidim/endorsers_list_button/content.erb +0 -1
  447. data/app/cells/decidim/endorsers_list_button_cell.rb +0 -15
  448. data/app/cells/decidim/scopes_picker/scope_picker_prompt.erb +0 -3
  449. data/app/cells/decidim/scopes_picker/scope_picker_values.erb +0 -5
  450. data/app/cells/decidim/scopes_picker/show.erb +0 -14
  451. data/app/cells/decidim/scopes_picker_cell.rb +0 -92
  452. data/app/commands/decidim/update_user_interests.rb +0 -41
  453. data/app/controllers/decidim/scopes_controller.rb +0 -84
  454. data/app/controllers/decidim/user_interests_controller.rb +0 -30
  455. data/app/forms/decidim/user_interest_scope_form.rb +0 -25
  456. data/app/forms/decidim/user_interests_form.rb +0 -17
  457. data/app/helpers/decidim/categories_helper.rb +0 -26
  458. data/app/helpers/decidim/scopes_helper.rb +0 -105
  459. data/app/packs/images/decidim/vendor/leaflet/layers-2x.png +0 -0
  460. data/app/packs/images/decidim/vendor/leaflet/layers.png +0 -0
  461. data/app/packs/images/decidim/vendor/leaflet/marker-icon-2x.png +0 -0
  462. data/app/packs/images/decidim/vendor/leaflet/marker-icon.png +0 -0
  463. data/app/packs/images/decidim/vendor/leaflet/marker-shadow.png +0 -0
  464. data/app/packs/src/decidim/vendor/leaflet-tilelayer-here.js +0 -212
  465. data/app/packs/stylesheets/decidim/legacy/MarkerCluster.Default.scss +0 -67
  466. data/app/packs/stylesheets/decidim/legacy/MarkerCluster.scss +0 -18
  467. data/app/packs/stylesheets/decidim/legacy/leaflet.scss +0 -705
  468. data/app/validators/scope_belongs_to_component_validator.rb +0 -16
  469. data/app/views/decidim/account/_user_groups.html.erb +0 -0
  470. data/app/views/decidim/scopes/_scopes_picker_input.html.erb +0 -19
  471. data/app/views/decidim/scopes/picker.html.erb +0 -53
  472. data/app/views/decidim/shared/_comments.html.erb +0 -0
  473. data/app/views/decidim/shared/_share_modal.html.erb +0 -26
  474. data/app/views/decidim/shared/_tags.html.erb +0 -1
  475. data/app/views/decidim/user_interests/_areas.html.erb +0 -14
  476. data/app/views/decidim/user_interests/_scopes.html.erb +0 -22
  477. data/app/views/decidim/user_interests/show.html.erb +0 -22
  478. data/config/initializers/foundation_rails_helper.rb +0 -4
  479. data/lib/decidim/core/test/shared_examples/has_category.rb +0 -38
  480. data/lib/decidim/core/test/shared_examples/has_scope.rb +0 -11
  481. data/lib/decidim/core/test/shared_examples/manage_component_share_tokens.rb +0 -83
  482. data/lib/decidim/core/test/shared_examples/preview_component_with_share_token_examples.rb +0 -49
  483. data/lib/decidim/core/test/shared_examples/scope_helper_examples.rb +0 -93
  484. data/lib/decidim/seven_zip_wrapper.rb +0 -29
@@ -1,41 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "decidim/seven_zip_wrapper"
4
-
5
3
  module Decidim
6
- # Public: Generates a 7z(seven zip) file with data files ready to be persisted
4
+ # Public: Generates a zip archive with data files ready to be persisted
7
5
  # somewhere so users can download their data.
8
6
  #
9
- # In fact, the 7z file wraps a ZIP file which finally contains the data files.
10
7
  class DownloadYourDataExporter
11
8
  DEFAULT_EXPORT_FORMAT = "CSV"
12
9
  ZIP_FILE_NAME = "download-your-data.zip"
13
10
 
11
+ include Decidim::TranslatableAttributes
12
+
14
13
  # Public: Initializes the class.
15
14
  #
16
15
  # user - The user to export the data from.
17
- # path - The String path where to write the zip file.
18
- # password - The password to protect the zip file.
16
+ # name - The name of the export in private area
19
17
  # export_format - The format of the data files inside the zip file. (CSV by default)
20
- def initialize(user, path, password, export_format = DEFAULT_EXPORT_FORMAT)
18
+ def initialize(user, name, export_format = DEFAULT_EXPORT_FORMAT)
21
19
  @user = user
22
- @path = File.expand_path path
23
20
  @export_format = export_format
24
- @password = password
21
+ @name = name
25
22
  end
26
23
 
24
+ # i18n-tasks-use t("decidim.download_your_data.show.download_your_data")
27
25
  def export
28
- tmpdir = Dir.mktmpdir("temporary-download-your-data-dir")
29
- user_data, user_attachments = data_and_attachments_for_user
30
- save_user_data(tmpdir, user_data)
31
- save_user_attachments(tmpdir, user_attachments)
32
-
33
- SevenZipWrapper.compress_and_encrypt(filename: @path, password: @password, input_directory: tmpdir)
26
+ user_export = user.private_exports.build
27
+ user_export.export_type = name
28
+ user_export.file.attach(io: data, filename: "#{name}.zip", content_type: "application/zip")
29
+ user_export.expires_at = Decidim.download_your_data_expiry_time.from_now
30
+ user_export.metadata = {}
31
+ user_export.save!
32
+ user_export
34
33
  end
35
34
 
36
35
  private
37
36
 
38
- attr_reader :user, :export_format
37
+ attr_reader :user, :export_format, :name
38
+
39
+ def data
40
+ user_data, user_attachments = data_and_attachments_for_user
41
+ buffer = Zip::OutputStream.write_buffer do |out|
42
+ save_user_data(out, user_data)
43
+ save_user_attachments(out, user_attachments)
44
+ save_readme(out)
45
+ end
46
+
47
+ buffer.rewind
48
+ buffer
49
+ end
39
50
 
40
51
  def data_and_attachments_for_user
41
52
  export_data = []
@@ -43,7 +54,8 @@ module Decidim
43
54
 
44
55
  download_your_data_entities.each do |object|
45
56
  klass = Object.const_get(object)
46
- export_data << [klass.model_name.name.parameterize.pluralize, Exporters.find_exporter(export_format).new(klass.user_collection(user), klass.export_serializer).export]
57
+ exporter = Exporters.find_exporter(export_format).new(klass.user_collection(user), klass.export_serializer)
58
+ export_data << [klass.model_name.name.parameterize.pluralize, exporter.export]
47
59
  attachments = klass.download_your_data_images(user)
48
60
  export_attachments << [klass.model_name.name.parameterize.pluralize, attachments.flatten] unless attachments.nil?
49
61
  end
@@ -55,30 +67,52 @@ module Decidim
55
67
  @download_your_data_entities ||= DownloadYourDataSerializers.data_entities
56
68
  end
57
69
 
58
- def save_user_data(tmpdir, user_data)
70
+ def save_user_data(output, user_data)
59
71
  user_data.each do |entity, exporter_data|
60
72
  next if exporter_data.read == "\n"
61
73
 
62
- file_name = File.join(tmpdir, "#{entity}-#{exporter_data.filename}")
63
- File.write(file_name, exporter_data.read)
74
+ output.put_next_entry("#{entity}-#{exporter_data.filename}")
75
+ output.write exporter_data.read
64
76
  end
65
77
  end
66
78
 
67
- def save_user_attachments(tmpdir, user_attachments)
79
+ def save_user_attachments(output, user_attachments)
68
80
  user_attachments.each do |entity, attachment_block|
69
81
  attachment_block.each do |attachment|
70
82
  next unless attachment.attached?
71
83
 
72
84
  blobs = attachment.is_a?(ActiveStorage::Attached::One) ? [attachment.blob] : attachment.blobs
73
85
  blobs.each do |blob|
74
- Dir.mkdir(File.join(tmpdir, entity.parameterize))
75
- file_name = File.join(tmpdir, entity.parameterize, blob.filename.to_s)
76
86
  blob.open do |blob_file|
77
- File.write(file_name, blob_file.read.force_encoding("UTF-8"))
87
+ output.put_next_entry("#{entity.parameterize}/#{blob.filename}")
88
+ output.write blob_file.read.force_encoding("UTF-8")
78
89
  end
79
90
  end
80
91
  end
81
92
  end
82
93
  end
94
+
95
+ def save_readme(output)
96
+ output.put_next_entry("README.md")
97
+ output.write readme
98
+ end
99
+
100
+ def readme
101
+ readme_file = "# #{I18n.t("decidim.download_your_data.help.core.title", organization: translated_attribute(user.organization.name))}\n\n"
102
+ readme_file << "#{I18n.t("decidim.download_your_data.help.core.description", user_name: "#{user.name} (#{user.nickname})")}\n\n"
103
+ help_definition = DownloadYourDataSerializers.help_definitions_for(user)
104
+
105
+ help_definition.each do |entity, headers|
106
+ next if headers.empty?
107
+
108
+ readme_file << "\n\n## #{entity}\n\n"
109
+
110
+ headers.each do |header, help_value|
111
+ readme_file << "* #{header}: #{help_value}\n"
112
+ end
113
+ end
114
+
115
+ readme_file
116
+ end
83
117
  end
84
118
  end
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This class is used to manage the onboarding funnel for users that
5
+ # intended to perform an action in the platform but it requires some
6
+ # verification steps to be completed.
7
+ #
8
+ # The flow is as follows:
9
+ #
10
+ # 1. A visitor (logged out) tries to perform an action that requires to be logged in
11
+ # 2. The intended action is stored in a cookie (onboarding key)
12
+ # 3. The visitor logs in and the onboarding cookie data is merged into the User#extended_data column.
13
+ # 4. After logging in, the user is redirected to the onboarding pending authorizations page
14
+ # if there is any pending action stored in the User#extended_data column
15
+ # 5. The user completes the required authorizations
16
+ # 6. When the onboarding authorizations page detects that all the verification steps have been
17
+ # completed, it will remove the pending action from the User#extended_data column and redirect
18
+ # the user to the intended action.
19
+ #
20
+ class OnboardingManager
21
+ # The same key is set in onboarding_pending_action js file to manage the
22
+ # onboarding data in the cookie
23
+ DATA_KEY = "onboarding"
24
+
25
+ attr_reader :user
26
+
27
+ def initialize(user)
28
+ @user = user
29
+ end
30
+
31
+ delegate :ephemeral?, to: :user
32
+
33
+ # Checks if the onboarding data has an action and a model.
34
+ #
35
+ # Returns a boolean
36
+ def valid?
37
+ return if action.blank?
38
+
39
+ permissions_holder.present?
40
+ end
41
+
42
+ # Returns the action to be performed.
43
+ #
44
+ # Returns a string
45
+ def action
46
+ onboarding_action
47
+ end
48
+
49
+ # Returns the translation of the action to be performed if translation
50
+ # is found, otherwise the untranslated literal action key
51
+ #
52
+ # Returns a string
53
+ def action_text
54
+ @action_text ||= if component && I18n.exists?("#{component.manifest.name}.actions.#{action}", scope: "decidim.components")
55
+ I18n.t("#{component.manifest.name}.actions.#{action}", scope: "decidim.components")
56
+ elsif permissions_holder.respond_to?(:resource_manifest) &&
57
+ I18n.exists?("#{permissions_holder.resource_manifest.name}.actions.#{action}", scope: "decidim.resources")
58
+ I18n.t("#{permissions_holder.resource_manifest.name}.actions.#{action}", scope: "decidim.resources")
59
+ else
60
+ action
61
+ end
62
+ end
63
+
64
+ # Checks if there is any pending action for the user.
65
+ #
66
+ # Returns a boolean
67
+ def pending_action?
68
+ onboarding_data.present?
69
+ end
70
+
71
+ # Checks if the user has completed all the required authorizations.
72
+ #
73
+ # active_authorization_methods - A list of the active authorization methods for the user.
74
+ #
75
+ # Returns a boolean
76
+ def finished_verifications?(authorization_methods = active_authorization_methods)
77
+ (authorization_handlers - authorization_methods).empty?
78
+ end
79
+
80
+ # Returns the model related to the action in the onboarding process.
81
+ #
82
+ # Returns an ActiveRecord model
83
+ def model
84
+ return if onboarding_model.blank?
85
+
86
+ @model ||= GlobalID::Locator.locate(onboarding_model)
87
+ end
88
+
89
+ # Returns the permissions_holder if present related to the action in the
90
+ # onboarding process.
91
+ #
92
+ # Returns an ActiveRecord model
93
+ def permissions_holder
94
+ return model if onboarding_permissions_holder.blank?
95
+
96
+ @permissions_holder ||= GlobalID::Locator.locate(onboarding_permissions_holder)
97
+ end
98
+
99
+ # Returns the model name related to the action in the onboarding process.
100
+ #
101
+ # Returns a string
102
+ def model_name
103
+ return unless valid?
104
+
105
+ @model_name ||= (model.presence || permissions_holder).class.model_name
106
+ end
107
+
108
+ # Returns the resource title associated to the action. If the model is defined
109
+ # its title is used, if not the permissions holder title
110
+ #
111
+ # Returns a translations Hash or a String
112
+ def model_title
113
+ return unless valid?
114
+
115
+ @model_title ||= begin
116
+ resource = model.presence || permissions_holder
117
+
118
+ method = [:title, :name].find { |m| resource.respond_to?(m) }
119
+
120
+ resource.send(method) if method
121
+ end
122
+ end
123
+
124
+ # Filters the given authorizations that are required for the onboarding process.
125
+ #
126
+ # authorizations - An array of Decidim::Authorization objects
127
+ #
128
+ # Returns an array of Decidim::Authorization objects
129
+ def filter_authorizations(authorizations)
130
+ filtered_authorizations = authorizations.select { |authorization| authorization_handlers.include?(authorization.name) }
131
+
132
+ # By calling authorize on each authorization the path generated for each
133
+ # one will include the specific options of the action if available
134
+ filtered_authorizations.each do |authorization|
135
+ next unless authorization.is_a? Decidim::Verifications::Adapter
136
+
137
+ authorization.authorize(nil, permissions.dig("authorization_handlers", authorization.name, "options") || {}, model, permissions_holder)
138
+ end
139
+ filtered_authorizations
140
+ end
141
+
142
+ # Returns a hash which can be passed to action_authorized_to helper method
143
+ # to determine the permissions status of the action
144
+ #
145
+ # Returns a Hash
146
+ def action_authorized_resources
147
+ return {} unless valid?
148
+
149
+ {
150
+ resource: model,
151
+ permissions_holder: onboarding_permissions_holder.presence && permissions_holder
152
+ }
153
+ end
154
+
155
+ # Returns the path to redirect after finishing the verification process. The path
156
+ # can be obtained from the user onboarding redirect_path data or if a resource is
157
+ # present using a ResourceLocatorPresenter
158
+ #
159
+ # Returns a String
160
+ def finished_redirect_path
161
+ @finished_redirect_path ||= onboarding_data["redirect_path"].presence || model_path
162
+ end
163
+
164
+ def root_path
165
+ component_path || Decidim::Core::Engine.routes.url_helpers.root_path
166
+ end
167
+
168
+ def component_path
169
+ return if component.blank?
170
+
171
+ EngineRouter.main_proxy(component).root_path
172
+ end
173
+
174
+ def expired?
175
+ return unless ephemeral?
176
+
177
+ session_duration > Decidim.config.expire_session_after.to_i
178
+ end
179
+
180
+ # Time in seconds since the last login or creation of the user
181
+ #
182
+ # Returns an Integer
183
+ def session_duration
184
+ Time.current.to_i - (user.last_sign_in_at || user.created_at).to_i
185
+ end
186
+
187
+ # This method is used to determine if an ephemeral user has an onboarding
188
+ # page to be redirected or only an authorization is required to complete the
189
+ # verification
190
+ #
191
+ # Returns a Boolean
192
+ def available_authorization_selection_page?
193
+ return true unless valid? && ephemeral?
194
+
195
+ authorization_handlers.count > 1
196
+ end
197
+
198
+ private
199
+
200
+ def active_authorization_methods
201
+ @active_authorization_methods ||= Verifications::Authorizations.new(organization: user.organization, user:).pluck(:name)
202
+ end
203
+
204
+ # Returns the permissions for the action and model in the onboarding process.
205
+ #
206
+ # Returns a hash
207
+ def permissions
208
+ @permissions ||= permissions_holder&.permissions&.fetch(action, nil) || component&.permissions&.fetch(action, nil)
209
+ end
210
+
211
+ # Returns the authorization handlers for the action and model in the onboarding process.
212
+ #
213
+ # Returns an array of strings
214
+ def authorization_handlers
215
+ return [] unless permissions
216
+
217
+ permissions["authorization_handlers"]&.keys&.map(&:to_s) || []
218
+ end
219
+
220
+ # Returns the model component or the permissions holder when it is a component.
221
+ #
222
+ # Returns a Decidim::Component
223
+ def component
224
+ return permissions_holder if permissions_holder.is_a?(Decidim::Component)
225
+ return if model.blank?
226
+ return unless model.respond_to?(:component)
227
+
228
+ model.component
229
+ end
230
+
231
+ # Returns the onboarding data from the user's extended_data related to the model in gid format.
232
+ #
233
+ # Returns a string
234
+ def onboarding_model
235
+ onboarding_data["model"]
236
+ end
237
+
238
+ # Returns the onboarding data from the user's extended_data related to the permissions_holder
239
+ # in gid format. This attribute is optional and may not be present
240
+ #
241
+ # Returns a string
242
+ def onboarding_permissions_holder
243
+ onboarding_data["permissions_holder"]
244
+ end
245
+
246
+ # Returns the onboarding data from the user's extended_data related to the action.
247
+ #
248
+ # Returns a string
249
+ def onboarding_action
250
+ onboarding_data["action"]
251
+ end
252
+
253
+ # Returns the onboarding data from the user's extended_data.
254
+ #
255
+ # Returns a hash
256
+ def onboarding_data
257
+ user.extended_data[DATA_KEY] || {}
258
+ end
259
+
260
+ # Returns the path associated to the model using ResourceLocatorPresenter. If model is not
261
+ # present returns the root path
262
+ #
263
+ # Returns a String
264
+ def model_path
265
+ return Decidim::Core::Engine.routes.url_helpers.root_path if model.blank?
266
+
267
+ ResourceLocatorPresenter.new(model).url
268
+ rescue NoMethodError
269
+ Decidim::Core::Engine.routes.url_helpers.root_path
270
+ end
271
+ end
272
+ end
@@ -9,48 +9,86 @@ module Decidim
9
9
  class OpenDataExporter
10
10
  FILE_NAME_PATTERN = "%{host}-open-data-%{entity}.csv"
11
11
 
12
- attr_reader :organization, :path
12
+ attr_reader :organization, :path, :resource, :help_definition
13
+
14
+ include Decidim::TranslatableAttributes
13
15
 
14
16
  # Public: Initializes the class.
15
17
  #
16
18
  # organization - The Organization to export the data from.
17
19
  # path - The String path where to write the zip file.
18
- def initialize(organization, path)
20
+ # resource - The String of the component or participatory space to export. If nil, it will export all.
21
+ def initialize(organization, path, resource = nil)
19
22
  @organization = organization
20
23
  @path = File.expand_path path
24
+ @resource = resource
25
+ @help_definition = {}
21
26
  end
22
27
 
23
28
  def export
24
29
  dirname = File.dirname(path)
25
30
  FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
26
- File.binwrite(path, data)
31
+ if resource.nil?
32
+ File.binwrite(path, data_for_all_resources)
33
+ else
34
+ File.write(path, data_for_resource(resource))
35
+ end
27
36
  end
28
37
 
29
38
  private
30
39
 
31
- def data
40
+ def data_for_all_resources
32
41
  buffer = Zip::OutputStream.write_buffer do |out|
42
+ core_data_manifests.each do |manifest|
43
+ add_file_to_output(out, format(FILE_NAME_PATTERN, { host: organization.host, entity: manifest.name }), data_for_core(manifest).read)
44
+ end
33
45
  open_data_component_manifests.each do |manifest|
34
- add_file_to_output(out, format(FILE_NAME_PATTERN, { host: organization.host, entity: manifest.name }), data_for_component(manifest))
46
+ add_file_to_output(out, format(FILE_NAME_PATTERN, { host: organization.host, entity: manifest.name }), data_for_component(manifest).read)
35
47
  end
36
48
  open_data_participatory_space_manifests.each do |manifest|
37
- add_file_to_output(out, format(FILE_NAME_PATTERN, { host: organization.host, entity: manifest.name }), data_for_participatory_space(manifest))
49
+ add_file_to_output(out, format(FILE_NAME_PATTERN, { host: organization.host, entity: manifest.name }), data_for_participatory_space(manifest).read)
38
50
  end
51
+
52
+ add_file_to_output(out, "README.md", readme)
53
+ add_file_to_output(out, "LICENSE.md", license)
39
54
  end
40
55
 
41
56
  buffer.string
42
57
  end
43
58
 
59
+ def data_for_core(export_manifest)
60
+ collection = export_manifest.collection.call(organization)
61
+ exporter = Decidim::Exporters::CSV.new(collection, export_manifest.serializer)
62
+
63
+ get_help_definition(:core, exporter, export_manifest) unless collection.empty?
64
+
65
+ exporter.export
66
+ end
67
+
68
+ def data_for_resource(resource)
69
+ export_manifest = (core_data_manifests + open_data_component_manifests + open_data_participatory_space_manifests)
70
+ .select { |manifest| manifest.name == resource.to_sym }.first
71
+
72
+ case export_manifest.respond_to?(:manifest) && export_manifest.manifest
73
+ when Decidim::ComponentManifest
74
+ data_for_component(export_manifest).read
75
+ when Decidim::ParticipatorySpaceManifest
76
+ data_for_participatory_space(export_manifest).read
77
+ else
78
+ data_for_core(export_manifest).read
79
+ end
80
+ end
81
+
44
82
  def data_for_component(export_manifest, col_sep = Decidim.default_csv_col_sep)
45
83
  headers = []
46
84
  collection = []
47
85
  ActiveRecord::Base.uncached do
48
- components.where(manifest_name: export_manifest.manifest.name).find_each do |component|
86
+ components.where(manifest_name: export_manifest.manifest.name).unscope(:order).find_each do |component|
49
87
  export_manifest.collection.call(component).find_in_batches(batch_size: 100) do |batch|
50
- exporter = Decidim::Exporters::CSV.new(batch, export_manifest.serializer)
88
+ serializer = export_manifest.open_data_serializer.nil? ? export_manifest.serializer : export_manifest.open_data_serializer
89
+ exporter = Decidim::Exporters::CSV.new(batch, serializer)
51
90
  headers.push(*exporter.headers)
52
91
  exported = exporter.export
53
-
54
92
  tmpdir = Dir::Tmpname.create(export_manifest.name.to_s) do
55
93
  # just get an empty file name
56
94
  end
@@ -59,6 +97,8 @@ module Decidim
59
97
  File.write(filename, exported.read)
60
98
 
61
99
  collection.push(filename)
100
+
101
+ get_help_definition(:components, exporter, export_manifest) unless collection.empty?
62
102
  end
63
103
  end
64
104
  end
@@ -76,16 +116,102 @@ module Decidim
76
116
  end
77
117
 
78
118
  def data_for_participatory_space(export_manifest)
79
- collection = participatory_spaces.filter { |space| space.manifest.name == export_manifest.manifest.name }.flat_map do |participatory_space|
119
+ collection = participatory_spaces.flat_map do |participatory_space|
80
120
  export_manifest.collection.call(participatory_space)
81
121
  end
122
+ serializer = export_manifest.open_data_serializer.nil? ? export_manifest.serializer : export_manifest.open_data_serializer
123
+ exporter = Decidim::Exporters::CSV.new(collection, serializer)
124
+ get_help_definition(:spaces, exporter, export_manifest) unless collection.empty?
125
+
126
+ exporter.export
127
+ end
128
+
129
+ def get_help_definition(manifest_type, exporter, export_manifest)
130
+ help_definition[manifest_type] = {} if help_definition[manifest_type].nil?
131
+ help_definition[manifest_type][export_manifest.name] = {} if help_definition[manifest_type][export_manifest.name].blank?
132
+ exporter.headers_without_locales.each do |header|
133
+ help_definition[manifest_type][export_manifest.name][header] = I18n.t("decidim.open_data.help.#{export_manifest.name}.#{header}")
134
+ end
135
+ end
136
+
137
+ def readme
138
+ "# #{I18n.t("decidim.open_data.help.core.title", organization: translated_attribute(organization.name))}\n\n
139
+ #{I18n.t("decidim.open_data.help.core.description")}\n\n
140
+ #{core_readme}
141
+ #{space_readme}
142
+ #{component_readme}
143
+ "
144
+ end
145
+
146
+ def core_readme
147
+ return unless help_definition.fetch(:core, false)
148
+
149
+ readme_file = "## #{I18n.t("decidim.open_data.help.core.main")}\n\n"
150
+ help_definition.fetch(:core, []).each do |element, headers|
151
+ readme_file << "### #{element}\n\n"
152
+
153
+ headers.each do |header, help_value|
154
+ readme_file << "* #{header}: #{help_value}\n"
155
+ end
156
+
157
+ readme_file << "\n\n"
158
+ end
159
+ readme_file
160
+ end
161
+
162
+ def space_readme
163
+ return unless help_definition.fetch(:spaces, false)
164
+
165
+ readme_file = "## #{I18n.t("decidim.open_data.help.core.spaces")}\n\n"
166
+
167
+ help_definition.fetch(:spaces, []).each do |space, headers|
168
+ readme_file << "### #{space}\n\n"
169
+
170
+ headers.each do |header, help_value|
171
+ readme_file << "* #{header}: #{help_value}\n"
172
+ end
173
+
174
+ readme_file << "\n\n"
175
+ end
176
+ readme_file
177
+ end
178
+
179
+ def component_readme
180
+ return unless help_definition.fetch(:components, false)
181
+
182
+ readme_file = "## #{I18n.t("decidim.open_data.help.core.components")}\n\n"
183
+
184
+ help_definition.fetch(:components, []).each do |component, headers|
185
+ readme_file << "### #{component}\n\n"
186
+
187
+ headers.each do |header, help_value|
188
+ readme_file << "* #{header}: #{help_value}\n"
189
+ end
190
+
191
+ readme_file << "\n\n"
192
+ end
82
193
 
83
- Decidim::Exporters::CSV.new(collection, export_manifest.serializer).export
194
+ readme_file
84
195
  end
85
196
 
86
- def add_file_to_output(output, file_name, data)
197
+ def license
198
+ link_database = "#{I18n.t("license_database_name", scope: "decidim.open_data.index.license")}: #{I18n.t("license_database_link", scope: "decidim.open_data.index.license")}"
199
+ link_contents = "#{I18n.t("license_contents_name", scope: "decidim.open_data.index.license")}: #{I18n.t("license_contents_link", scope: "decidim.open_data.index.license")}"
200
+
201
+ license_file = I18n.t("title", scope: "decidim.open_data.index.license")
202
+ license_file << "\n\n"
203
+ license_file << I18n.t("body_1_html", scope: "decidim.open_data.index.license", organization_name: translated_attribute(organization.name), link_database:, link_contents:)
204
+
205
+ license_file
206
+ end
207
+
208
+ def add_file_to_output(output, file_name, string)
87
209
  output.put_next_entry(file_name)
88
- output.write data.read
210
+ output.write string
211
+ end
212
+
213
+ def core_data_manifests
214
+ @core_data_manifests ||= Decidim.open_data_manifests.select(&:include_in_open_data)
89
215
  end
90
216
 
91
217
  def open_data_component_manifests
@@ -69,6 +69,19 @@ module Decidim
69
69
  end
70
70
  end
71
71
 
72
+ # Performs the given block and sets the author of the action without log.
73
+ #
74
+ # author - An object that implements `to_gid` or a String
75
+ #
76
+ # Returns whatever the given block returns.
77
+ def perform_action_without_log!(author)
78
+ PaperTrail.request(whodunnit: gid(author)) do
79
+ Decidim::ApplicationRecord.transaction do
80
+ block_given? ? yield : nil
81
+ end
82
+ end
83
+ end
84
+
72
85
  # Updates the `resource` with `update!` and sets the author of the version.
73
86
  #
74
87
  # resource - An ActiveRecord instance that implements `Decidim::Traceable`
@@ -60,7 +60,7 @@ module Decidim
60
60
  end
61
61
 
62
62
  def variant_path(key, options = {})
63
- variant_url(key, **options.merge(only_path: true))
63
+ variant_url(key, **options, only_path: true)
64
64
  end
65
65
 
66
66
  def remote_url=(url)
@@ -6,9 +6,11 @@ class EtiquetteValidator < ActiveModel::EachValidator
6
6
  include ActionView::Helpers::SanitizeHelper
7
7
 
8
8
  def validate_each(record, attribute, value)
9
+ return unless Decidim.enable_etiquette_validator
9
10
  return if value.blank?
10
11
 
11
- text_value = strip_tags(value)
12
+ # remove HTML tags, from WYSIWYG editor
13
+ text_value = clean_value(value)
12
14
 
13
15
  validate_caps(record, attribute, text_value)
14
16
  validate_marks(record, attribute, text_value)
@@ -17,8 +19,13 @@ class EtiquetteValidator < ActiveModel::EachValidator
17
19
 
18
20
  private
19
21
 
22
+ def clean_value(value)
23
+ ActionController::Base.helpers.strip_tags(value).to_s.strip
24
+ end
25
+
20
26
  def validate_caps(record, attribute, value)
21
- return if value.scan(/[A-Z]/).length < value.length / 4
27
+ number_of_caps = value.scan(/[A-Z]/).length
28
+ return if number_of_caps.zero? || number_of_caps < value.length / 2 # 50%
22
29
 
23
30
  record.errors.add(attribute, options[:message] || :too_much_caps)
24
31
  end
@@ -33,7 +33,7 @@ class PassthruValidator < ActiveModel::EachValidator
33
33
  value = dummy.public_send(dummy_attr)
34
34
  elsif dummy.respond_to? :file
35
35
  dummy.public_send("file=", value)
36
- value = dummy.public_send(:file)
36
+ value = dummy.file
37
37
  end
38
38
 
39
39
  target_validators(attribute).each do |validator|