decidim-core 0.26.2 → 0.27.0.rc1

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 (543) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/cells/decidim/amendable/announcement_cell.rb +3 -8
  4. data/app/cells/decidim/announcement_cell.rb +1 -1
  5. data/app/cells/decidim/author_cell.rb +1 -1
  6. data/app/cells/decidim/content_blocks/footer_sub_hero/show.erb +1 -1
  7. data/app/cells/decidim/content_blocks/hero/show.erb +1 -1
  8. data/app/cells/decidim/content_blocks/highlighted_content_banner/show.erb +3 -3
  9. data/app/cells/decidim/content_blocks/highlighted_elements_cell.rb +1 -1
  10. data/app/cells/decidim/content_blocks/how_to_participate/show.erb +1 -1
  11. data/app/cells/decidim/content_blocks/last_activity/show.erb +1 -1
  12. data/app/cells/decidim/content_blocks/last_activity_cell.rb +3 -3
  13. data/app/cells/decidim/content_blocks/stats_cell.rb +4 -4
  14. data/app/cells/decidim/content_blocks/sub_hero/show.erb +1 -1
  15. data/app/cells/decidim/data_consent/category.erb +64 -0
  16. data/app/cells/decidim/data_consent/dialog.erb +29 -0
  17. data/app/cells/decidim/data_consent/modal.erb +39 -0
  18. data/app/cells/decidim/data_consent/show.erb +3 -0
  19. data/app/cells/decidim/data_consent_cell.rb +21 -0
  20. data/app/cells/decidim/follow_button_cell.rb +5 -7
  21. data/app/cells/decidim/profile_sidebar_cell.rb +1 -1
  22. data/app/cells/decidim/search_results_cell.rb +1 -1
  23. data/app/cells/decidim/tos_page/refuse_btn_modal.erb +1 -1
  24. data/app/cells/decidim/upload_modal/files.erb +52 -0
  25. data/app/cells/decidim/upload_modal/modal.erb +49 -0
  26. data/app/cells/decidim/upload_modal/show.erb +3 -0
  27. data/app/cells/decidim/upload_modal_cell.rb +205 -0
  28. data/app/cells/decidim/user_conversation/reply.erb +1 -1
  29. data/app/cells/decidim/user_conversations/add_conversation_users.erb +5 -3
  30. data/app/cells/decidim/version_cell.rb +2 -13
  31. data/app/cells/decidim/versions_list_cell.rb +2 -13
  32. data/app/cells/decidim/versions_list_item_cell.rb +2 -13
  33. data/app/commands/decidim/accept_group_invitation.rb +1 -1
  34. data/app/commands/decidim/accept_user_group_join_request.rb +1 -1
  35. data/app/commands/decidim/amendable/accept.rb +1 -1
  36. data/app/commands/decidim/amendable/create_draft.rb +1 -1
  37. data/app/commands/decidim/amendable/destroy_draft.rb +1 -1
  38. data/app/commands/decidim/amendable/promote.rb +1 -1
  39. data/app/commands/decidim/amendable/publish_draft.rb +1 -1
  40. data/app/commands/decidim/amendable/reject.rb +1 -1
  41. data/app/commands/decidim/amendable/update_draft.rb +1 -1
  42. data/app/commands/decidim/amendable/withdraw.rb +1 -1
  43. data/app/commands/decidim/attachment_methods.rb +3 -2
  44. data/app/commands/decidim/create_editor_image.rb +1 -1
  45. data/app/commands/decidim/create_follow.rb +1 -1
  46. data/app/commands/decidim/create_omniauth_registration.rb +2 -3
  47. data/app/commands/decidim/create_registration.rb +1 -2
  48. data/app/commands/decidim/create_report.rb +1 -1
  49. data/app/commands/decidim/create_user_group.rb +2 -2
  50. data/app/commands/decidim/create_user_report.rb +5 -3
  51. data/app/commands/decidim/delete_follow.rb +1 -1
  52. data/app/commands/decidim/demote_membership.rb +1 -1
  53. data/app/commands/decidim/destroy_account.rb +1 -1
  54. data/app/commands/decidim/endorse_resource.rb +1 -1
  55. data/app/commands/decidim/gallery_methods.rb +45 -10
  56. data/app/commands/decidim/invite_user.rb +1 -3
  57. data/app/commands/decidim/invite_user_again.rb +1 -1
  58. data/app/commands/decidim/invite_user_to_group.rb +1 -1
  59. data/app/commands/decidim/join_user_group.rb +1 -1
  60. data/app/commands/decidim/leave_user_group.rb +8 -2
  61. data/app/commands/decidim/messaging/reply_to_conversation.rb +2 -2
  62. data/app/commands/decidim/messaging/start_conversation.rb +2 -2
  63. data/app/commands/decidim/multiple_attachments_methods.rb +44 -5
  64. data/app/commands/decidim/promote_membership.rb +1 -1
  65. data/app/commands/decidim/reject_group_invitation.rb +1 -1
  66. data/app/commands/decidim/reject_user_group_join_request.rb +1 -1
  67. data/app/commands/decidim/remove_user_from_group.rb +1 -1
  68. data/app/commands/decidim/resend_confirmation_instructions.rb +18 -0
  69. data/app/commands/decidim/search.rb +10 -9
  70. data/app/commands/decidim/unendorse_resource.rb +1 -1
  71. data/app/commands/decidim/unsubscribe_settings.rb +1 -1
  72. data/app/commands/decidim/update_account.rb +2 -2
  73. data/app/commands/decidim/update_notifications_settings.rb +3 -2
  74. data/app/commands/decidim/update_password.rb +31 -0
  75. data/app/commands/decidim/update_user_group.rb +2 -2
  76. data/app/commands/decidim/update_user_interests.rb +1 -1
  77. data/app/commands/decidim/validate_upload.rb +15 -0
  78. data/app/controllers/concerns/decidim/devise_controllers.rb +0 -1
  79. data/app/controllers/concerns/decidim/filter_resource.rb +23 -24
  80. data/app/controllers/concerns/decidim/force_authentication.rb +1 -2
  81. data/app/controllers/concerns/decidim/http_caching_disabler.rb +1 -1
  82. data/app/controllers/concerns/decidim/needs_password_change.rb +43 -0
  83. data/app/controllers/concerns/decidim/needs_tos_accepted.rb +3 -3
  84. data/app/controllers/concerns/decidim/orderable.rb +1 -1
  85. data/app/controllers/concerns/decidim/participatory_space_context.rb +2 -2
  86. data/app/controllers/decidim/account_controller.rb +45 -0
  87. data/app/controllers/decidim/application_controller.rb +1 -1
  88. data/app/controllers/decidim/devise/invitations_controller.rb +2 -0
  89. data/app/controllers/decidim/devise/passwords_controller.rb +33 -0
  90. data/app/controllers/decidim/devise/registrations_controller.rb +2 -0
  91. data/app/controllers/decidim/devise/sessions_controller.rb +2 -0
  92. data/app/controllers/decidim/{data_portability_controller.rb → download_your_data_controller.rb} +8 -8
  93. data/app/controllers/decidim/groups_controller.rb +5 -0
  94. data/app/controllers/decidim/last_activities_controller.rb +7 -9
  95. data/app/controllers/decidim/manifests_controller.rb +11 -0
  96. data/app/controllers/decidim/newsletters_controller.rb +1 -1
  97. data/app/controllers/decidim/notifications_subscriptions_controller.rb +16 -0
  98. data/app/controllers/decidim/offline_controller.rb +7 -0
  99. data/app/controllers/decidim/profiles_controller.rb +1 -1
  100. data/app/controllers/decidim/searches_controller.rb +4 -3
  101. data/app/controllers/decidim/short_links_controller.rb +35 -0
  102. data/app/controllers/decidim/upload_validations_controller.rb +44 -0
  103. data/app/controllers/decidim/user_activities_controller.rb +32 -16
  104. data/app/controllers/decidim/user_interests_controller.rb +0 -2
  105. data/app/controllers/decidim/user_timeline_controller.rb +6 -8
  106. data/app/events/decidim/change_nickname_event.rb +21 -0
  107. data/app/forms/decidim/account_form.rb +7 -6
  108. data/app/forms/decidim/form.rb +1 -1
  109. data/app/forms/decidim/messaging/conversation_form.rb +1 -1
  110. data/app/forms/decidim/notifications_settings_form.rb +8 -2
  111. data/app/forms/decidim/password_form.rb +10 -0
  112. data/app/forms/decidim/registration_form.rb +10 -4
  113. data/app/forms/decidim/upload_validation_form.rb +54 -0
  114. data/app/forms/decidim/user_group_form.rb +1 -1
  115. data/app/forms/decidim/user_interest_scope_form.rb +1 -1
  116. data/app/forms/decidim/user_interests_form.rb +1 -1
  117. data/app/forms/translatable_presence_validator.rb +2 -2
  118. data/app/forms/url_validator.rb +1 -1
  119. data/app/helpers/decidim/application_helper.rb +2 -2
  120. data/app/helpers/decidim/attachments_helper.rb +1 -1
  121. data/app/helpers/decidim/cells_paginate_helper.rb +1 -1
  122. data/app/helpers/decidim/datalist_select_helper.rb +40 -0
  123. data/app/helpers/decidim/decidim_form_helper.rb +1 -1
  124. data/app/helpers/decidim/followable_helper.rb +1 -1
  125. data/app/helpers/decidim/icon_helper.rb +1 -1
  126. data/app/helpers/decidim/layout_helper.rb +15 -1
  127. data/app/helpers/decidim/map_helper.rb +12 -8
  128. data/app/helpers/decidim/meta_tags_helper.rb +0 -2
  129. data/app/helpers/decidim/notifications_settings_helper.rb +15 -0
  130. data/app/helpers/decidim/passwords_helper.rb +41 -0
  131. data/app/helpers/decidim/sanitize_helper.rb +1 -0
  132. data/app/helpers/decidim/searches_helper.rb +6 -6
  133. data/app/helpers/decidim/short_link_helper.rb +28 -0
  134. data/app/helpers/decidim/translations_helper.rb +2 -2
  135. data/app/jobs/decidim/{data_portability_export_job.rb → download_your_data_export_job.rb} +5 -5
  136. data/app/jobs/decidim/email_notifications_digest_generator_job.rb +21 -0
  137. data/app/jobs/decidim/notification_generator_for_recipient_job.rb +5 -3
  138. data/app/jobs/decidim/reminder_generator_job.rb +12 -0
  139. data/app/jobs/decidim/resend_confirmation_instructions_job.rb +11 -0
  140. data/app/jobs/decidim/user_report_job.rb +2 -2
  141. data/app/mailers/concerns/decidim/multitenant_asset_host.rb +11 -3
  142. data/app/mailers/decidim/application_mailer.rb +1 -1
  143. data/app/mailers/decidim/block_user_mailer.rb +8 -7
  144. data/app/mailers/decidim/export_mailer.rb +2 -2
  145. data/app/mailers/decidim/newsletter_mailer.rb +2 -2
  146. data/app/mailers/decidim/newsletters_opt_in_mailer.rb +7 -5
  147. data/app/mailers/decidim/notifications_digest_mailer.rb +23 -0
  148. data/app/mailers/decidim/user_report_mailer.rb +10 -11
  149. data/app/models/decidim/action_log.rb +86 -2
  150. data/app/models/decidim/amendment.rb +0 -1
  151. data/app/models/decidim/area.rb +1 -2
  152. data/app/models/decidim/area_type.rb +5 -0
  153. data/app/models/decidim/attachment.rb +6 -0
  154. data/app/models/decidim/attachment_collection.rb +5 -0
  155. data/app/models/decidim/category.rb +9 -4
  156. data/app/models/decidim/coauthorship.rb +0 -2
  157. data/app/models/decidim/contextual_help_section.rb +5 -1
  158. data/app/models/decidim/follow.rb +2 -2
  159. data/app/models/decidim/gamification/badge_score.rb +0 -1
  160. data/app/models/decidim/identity.rb +2 -2
  161. data/app/models/decidim/messaging/conversation.rb +4 -4
  162. data/app/models/decidim/messaging/message.rb +1 -2
  163. data/app/models/decidim/messaging/participation.rb +0 -2
  164. data/app/models/decidim/messaging/receipt.rb +0 -2
  165. data/app/models/decidim/notification.rb +5 -2
  166. data/app/models/decidim/oauth_application.rb +12 -1
  167. data/app/models/decidim/organization.rb +7 -7
  168. data/app/models/decidim/participatory_space_private_user.rb +4 -2
  169. data/app/models/decidim/permission_action.rb +1 -0
  170. data/app/models/decidim/reminder.rb +10 -0
  171. data/app/models/decidim/reminder_delivery.rb +7 -0
  172. data/app/models/decidim/reminder_record.rb +29 -0
  173. data/app/models/decidim/report.rb +2 -2
  174. data/app/models/decidim/scope.rb +3 -4
  175. data/app/models/decidim/scope_type.rb +5 -0
  176. data/app/models/decidim/share_token.rb +0 -2
  177. data/app/models/decidim/short_link.rb +162 -0
  178. data/app/models/decidim/user.rb +34 -6
  179. data/app/models/decidim/user_base_entity.rb +1 -1
  180. data/app/models/decidim/user_group.rb +3 -3
  181. data/app/models/decidim/user_moderation.rb +0 -2
  182. data/app/models/decidim/user_report.rb +2 -2
  183. data/app/packs/entrypoints/decidim_core.js +2 -2
  184. data/app/packs/entrypoints/decidim_sw.js +1 -0
  185. data/app/packs/src/decidim/autocomplete.js +298 -0
  186. data/app/packs/src/decidim/cookie_consent/consent_manager.js +133 -0
  187. data/app/packs/src/decidim/cookie_consent/cookie_consent.js +103 -0
  188. data/app/packs/src/decidim/datalist_select.js +36 -0
  189. data/app/packs/src/decidim/direct_uploads/upload_field.js +117 -0
  190. data/app/packs/src/decidim/direct_uploads/upload_modal.js +264 -0
  191. data/app/packs/src/decidim/direct_uploads/upload_utility.js +49 -0
  192. data/app/packs/src/decidim/direct_uploads/uploader.js +89 -0
  193. data/app/packs/src/decidim/external_domain_warning.js +1 -1
  194. data/app/packs/src/decidim/external_link.js +14 -3
  195. data/app/packs/src/decidim/external_link.test.js +64 -0
  196. data/app/packs/src/decidim/geocoding.js +10 -35
  197. data/app/packs/src/decidim/identity_selector_dialog.js +1 -1
  198. data/app/packs/src/decidim/impersonation.js +3 -3
  199. data/app/packs/src/decidim/input_character_counter.js +177 -7
  200. data/app/packs/src/decidim/input_multiple_mentions.js +87 -187
  201. data/app/packs/src/decidim/map/factory.js +3 -1
  202. data/app/packs/src/decidim/map/icon.js +2 -1
  203. data/app/packs/src/decidim/map/legacy.js +2 -2
  204. data/app/packs/src/decidim/map/svg-icon.js +248 -0
  205. data/app/packs/src/decidim/map.js +2 -2
  206. data/app/packs/src/decidim/security/selfxss_warning.js +5 -1
  207. data/app/packs/src/decidim/session_timeouter.js +15 -15
  208. data/app/packs/src/decidim/sw/a2hs.js +28 -0
  209. data/app/packs/src/decidim/sw/index.js +3 -0
  210. data/app/packs/src/decidim/sw/loader.js +18 -0
  211. data/app/packs/src/decidim/sw/push-permissions.js +87 -0
  212. data/app/packs/src/decidim/sw/sw.js +80 -0
  213. data/app/packs/src/decidim/vizzs/areachart.js +33 -28
  214. data/app/packs/src/decidim/vizzs/{metrics.js → index.js} +0 -0
  215. data/app/packs/src/decidim/vizzs/linechart.js +23 -18
  216. data/app/packs/src/decidim/vizzs/rowchart.js +15 -11
  217. data/app/packs/stylesheets/decidim/email.scss +66 -0
  218. data/app/packs/stylesheets/decidim/layouts/_user.scss +8 -0
  219. data/app/packs/stylesheets/decidim/modules/_autocomplete.scss +99 -0
  220. data/app/packs/stylesheets/decidim/modules/_cookie-consent.scss +115 -0
  221. data/app/packs/stylesheets/decidim/modules/_footer.scss +4 -0
  222. data/app/packs/stylesheets/decidim/modules/_input-multiple-mentions.scss +29 -2
  223. data/app/packs/stylesheets/decidim/modules/_modules.scss +3 -1
  224. data/app/packs/stylesheets/decidim/modules/_reveal.scss +15 -0
  225. data/app/packs/stylesheets/decidim/modules/_upload_modal.scss +143 -0
  226. data/app/packs/stylesheets/decidim/utils/_fontface.scss +3 -0
  227. data/app/permissions/decidim/permissions.rb +2 -4
  228. data/app/presenters/decidim/admin_log/area_type_presenter.rb +34 -0
  229. data/app/presenters/decidim/admin_log/attachment_collection_presenter.rb +35 -0
  230. data/app/presenters/decidim/admin_log/attachment_presenter.rb +27 -0
  231. data/app/presenters/decidim/admin_log/category_presenter.rb +36 -0
  232. data/app/presenters/decidim/admin_log/component_presenter.rb +35 -3
  233. data/app/presenters/decidim/admin_log/contextual_help_section_presenter.rb +33 -0
  234. data/app/presenters/decidim/admin_log/organization_presenter.rb +9 -4
  235. data/app/presenters/decidim/admin_log/scope_type_presenter.rb +34 -0
  236. data/app/presenters/decidim/home_stats_presenter.rb +4 -2
  237. data/app/presenters/decidim/log/base_presenter.rb +1 -1
  238. data/app/presenters/decidim/menu_item_presenter.rb +1 -1
  239. data/app/presenters/decidim/metric_charts_presenter.rb +7 -1
  240. data/app/presenters/decidim/nil_presenter.rb +3 -1
  241. data/app/presenters/decidim/notification_to_mailer_presenter.rb +39 -0
  242. data/app/presenters/decidim/notifications_digest_presenter.rb +42 -0
  243. data/app/presenters/decidim/official_author_presenter.rb +4 -0
  244. data/app/presenters/decidim/organization_presenter.rb +22 -0
  245. data/app/presenters/decidim/push_notification_presenter.rb +24 -0
  246. data/app/presenters/decidim/stats_presenter.rb +1 -1
  247. data/app/presenters/decidim/validation_errors_presenter.rb +1 -1
  248. data/app/queries/decidim/messaging/user_conversations.rb +1 -1
  249. data/app/queries/decidim/metric_manage.rb +1 -1
  250. data/app/queries/decidim/own_activities.rb +11 -0
  251. data/app/queries/decidim/participatory_processes_with_user_role.rb +1 -1
  252. data/app/{services/decidim/activity_search.rb → queries/decidim/public_activities.rb} +35 -77
  253. data/app/queries/decidim/public_components.rb +1 -1
  254. data/app/queries/decidim/similar_emendations.rb +1 -1
  255. data/app/queries/decidim/stats_users_count.rb +1 -1
  256. data/app/queries/decidim/user_groups/accepted_memberships.rb +1 -1
  257. data/app/queries/decidim/user_groups/accepted_user_groups.rb +1 -1
  258. data/app/queries/decidim/user_groups/accepted_users.rb +1 -1
  259. data/app/queries/decidim/user_groups/admin_memberships.rb +1 -1
  260. data/app/queries/decidim/user_groups/invited_memberships.rb +1 -1
  261. data/app/queries/decidim/user_groups/manageable_user_groups.rb +1 -1
  262. data/app/queries/decidim/user_groups/member_memberships.rb +1 -1
  263. data/app/services/decidim/{data_portability_exporter.rb → download_your_data_exporter.rb} +6 -6
  264. data/app/services/decidim/email_notification_generator.rb +2 -2
  265. data/app/services/decidim/engine_resolver.rb +66 -0
  266. data/app/services/decidim/events_manager.rb +1 -1
  267. data/app/services/decidim/iframe_disabler.rb +31 -0
  268. data/app/services/decidim/notification_generator_for_recipient.rb +1 -1
  269. data/app/services/decidim/notifications_digest_sending_decider.rb +18 -0
  270. data/app/services/decidim/notifications_subscriptions_persistor.rb +37 -0
  271. data/app/services/decidim/open_data_exporter.rb +1 -1
  272. data/app/services/decidim/resource_search.rb +17 -164
  273. data/app/services/decidim/send_push_notification.rb +59 -0
  274. data/app/services/decidim/traceability.rb +1 -1
  275. data/app/uploaders/decidim/application_uploader.rb +2 -12
  276. data/app/uploaders/decidim/cw/attachment_uploader.rb +1 -1
  277. data/app/uploaders/decidim/cw/{data_portability_uploader.rb → download_your_data_uploader.rb} +3 -3
  278. data/app/uploaders/decidim/cw/image_uploader.rb +2 -2
  279. data/app/uploaders/decidim/organization_favicon_uploader.rb +3 -2
  280. data/app/validators/passthru_validator.rb +29 -21
  281. data/app/validators/password_validator.rb +25 -4
  282. data/app/validators/uploader_image_dimensions_validator.rb +1 -1
  283. data/app/views/decidim/account/_account_notification.js.erb +20 -0
  284. data/app/views/decidim/account/_password_fields.html.erb +2 -2
  285. data/app/views/decidim/account/cancel_email_change.js.erb +6 -0
  286. data/app/views/decidim/account/resend_confirmation_instructions.erb +1 -0
  287. data/app/views/decidim/account/show.html.erb +19 -6
  288. data/app/views/decidim/application/{_attachments.html.erb → _attachments.erb} +2 -2
  289. data/app/views/decidim/application/_collection.html.erb +1 -1
  290. data/app/views/decidim/application/{_document.html.erb → _document.erb} +0 -0
  291. data/app/views/decidim/application/{_documents.html.erb → _documents.erb} +2 -2
  292. data/app/views/decidim/application/{_photos.html.erb → _photos.erb} +0 -0
  293. data/app/views/decidim/devise/confirmations/new.html.erb +1 -1
  294. data/app/views/decidim/devise/invitations/edit.html.erb +3 -4
  295. data/app/views/decidim/devise/omniauth_registrations/new.html.erb +3 -3
  296. data/app/views/decidim/devise/passwords/edit.html.erb +7 -5
  297. data/app/views/decidim/devise/passwords/new.html.erb +1 -1
  298. data/app/views/decidim/devise/registrations/edit.html.erb +5 -5
  299. data/app/views/decidim/devise/registrations/new.html.erb +5 -5
  300. data/app/views/decidim/devise/sessions/new.html.erb +2 -2
  301. data/app/views/decidim/{data_portability → download_your_data}/export.html.erb +0 -0
  302. data/app/views/decidim/{data_portability → download_your_data}/show.html.erb +2 -2
  303. data/app/views/decidim/export_mailer/download_your_data_export.html.erb +7 -0
  304. data/app/views/decidim/last_activities/index.html.erb +1 -1
  305. data/app/views/decidim/manifests/show.json.erb +31 -0
  306. data/app/views/decidim/messaging/conversations/_add_conversation_users.html.erb +5 -3
  307. data/app/views/decidim/messaging/conversations/_reply.html.erb +1 -1
  308. data/app/views/decidim/messaging/conversations/_start.html.erb +1 -3
  309. data/app/views/decidim/messaging/conversations/create.js.erb +1 -0
  310. data/app/views/decidim/notifications_digest_mailer/_email_content.html.erb +11 -0
  311. data/app/views/decidim/notifications_digest_mailer/digest_mail.erb +27 -0
  312. data/app/views/decidim/notifications_settings/show.html.erb +34 -9
  313. data/app/views/decidim/offline/show.html.erb +9 -0
  314. data/app/views/decidim/pages/_standalone.html.erb +1 -1
  315. data/app/views/decidim/reported_mailer/hide.html.erb +1 -1
  316. data/app/views/decidim/reported_mailer/report.html.erb +1 -1
  317. data/app/views/decidim/scopes/picker.html.erb +1 -1
  318. data/app/views/decidim/searches/_filters.html.erb +2 -2
  319. data/app/views/decidim/shared/participatory_space_filters/_filters.html.erb +3 -3
  320. data/app/views/decidim/user_report_mailer/notify.html.erb +10 -3
  321. data/app/views/devise/mailer/confirmation_instructions.html.erb +3 -1
  322. data/app/views/layouts/decidim/_application.html.erb +6 -3
  323. data/app/views/layouts/decidim/_cookie_warning.html.erb +8 -8
  324. data/app/views/layouts/decidim/_decidim_javascript.html.erb +12 -0
  325. data/app/views/layouts/decidim/_head.html.erb +4 -2
  326. data/app/views/layouts/decidim/_js_configuration.html.erb +5 -1
  327. data/app/views/layouts/decidim/_main_footer.html.erb +1 -0
  328. data/app/views/layouts/decidim/_offline_banner.html.erb +11 -0
  329. data/app/views/layouts/decidim/widget.html.erb +2 -2
  330. data/config/assets.rb +1 -0
  331. data/config/locales/am-ET.yml +1 -0
  332. data/config/locales/ar.yml +48 -36
  333. data/config/locales/bg.yml +15 -30
  334. data/config/locales/ca.yml +186 -35
  335. data/config/locales/cs.yml +186 -33
  336. data/config/locales/da.yml +1 -0
  337. data/config/locales/de.yml +25 -36
  338. data/config/locales/el.yml +15 -30
  339. data/config/locales/en.yml +183 -32
  340. data/config/locales/eo.yml +4 -6
  341. data/config/locales/es-MX.yml +185 -34
  342. data/config/locales/es-PY.yml +185 -34
  343. data/config/locales/es.yml +185 -34
  344. data/config/locales/et.yml +1 -0
  345. data/config/locales/eu.yml +15 -36
  346. data/config/locales/fi-plain.yml +184 -33
  347. data/config/locales/fi.yml +184 -33
  348. data/config/locales/fr-CA.yml +170 -33
  349. data/config/locales/fr.yml +170 -33
  350. data/config/locales/ga-IE.yml +5 -7
  351. data/config/locales/gl.yml +47 -30
  352. data/config/locales/hr.yml +1 -0
  353. data/config/locales/hu.yml +65 -35
  354. data/config/locales/id-ID.yml +15 -29
  355. data/config/locales/is-IS.yml +6 -15
  356. data/config/locales/it.yml +22 -38
  357. data/config/locales/ja.yml +173 -34
  358. data/config/locales/ko.yml +1 -0
  359. data/config/locales/lb.yml +16 -32
  360. data/config/locales/lt.yml +1 -0
  361. data/config/locales/lv.yml +15 -28
  362. data/config/locales/mt.yml +1 -0
  363. data/config/locales/nl.yml +25 -33
  364. data/config/locales/no.yml +24 -35
  365. data/config/locales/om-ET.yml +1 -0
  366. data/config/locales/pl.yml +113 -32
  367. data/config/locales/pt-BR.yml +17 -32
  368. data/config/locales/pt.yml +16 -32
  369. data/config/locales/ro-RO.yml +20 -36
  370. data/config/locales/ru.yml +15 -27
  371. data/config/locales/si-LK.yml +1 -0
  372. data/config/locales/sk.yml +15 -30
  373. data/config/locales/sl.yml +1 -0
  374. data/config/locales/so-SO.yml +1 -0
  375. data/config/locales/sr-CS.yml +1 -0
  376. data/config/locales/sv.yml +69 -34
  377. data/config/locales/sw-KE.yml +1 -0
  378. data/config/locales/ti-ER.yml +1 -0
  379. data/config/locales/tr-TR.yml +15 -29
  380. data/config/locales/uk.yml +15 -26
  381. data/config/locales/val-ES.yml +1 -0
  382. data/config/locales/vi.yml +1 -0
  383. data/config/locales/zh-CN.yml +15 -28
  384. data/config/locales/zh-TW.yml +1 -0
  385. data/config/routes.rb +17 -2
  386. data/db/migrate/20180508111710_add_accepted_tos_version_field_to_users.rb +1 -0
  387. data/db/migrate/20181030090144_destroy_deleted_users_follows.rb +1 -0
  388. data/db/migrate/20190412131728_fix_user_names.rb +4 -4
  389. data/db/migrate/20210831181634_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
  390. data/db/migrate/20210831181635_create_active_storage_variant_records.active_storage.rb +14 -0
  391. data/db/migrate/20211208155453_create_decidim_reminders.rb +11 -0
  392. data/db/migrate/20211209121025_create_decidim_reminder_records.rb +11 -0
  393. data/db/migrate/20211209121040_create_decidim_reminder_deliveries.rb +10 -0
  394. data/db/migrate/20220118121921_change_required_description_categories.rb +7 -0
  395. data/db/migrate/20220127113419_add_notification_settings_to_users.rb +7 -0
  396. data/db/migrate/20220203121137_add_notifications_sending_frequency_to_users.rb +7 -0
  397. data/db/migrate/20220215172439_add_digest_sent_at_to_users.rb +7 -0
  398. data/db/migrate/20220323195258_add_index_to_decidim_users_notifications_sending_frequency.rb +7 -0
  399. data/db/migrate/20220427142214_drop_emails_on_notifications_flag_from_user.rb +15 -0
  400. data/db/migrate/20220518094535_add_previous_passwords_to_users.rb +20 -0
  401. data/db/migrate/20220524195530_create_decidim_short_links.rb +23 -0
  402. data/db/seeds.rb +17 -18
  403. data/lib/decidim/acts_as_tree.rb +47 -0
  404. data/lib/decidim/api/input_sorts/component_input_sort.rb +2 -1
  405. data/lib/decidim/api/interfaces/authorable_interface.rb +6 -5
  406. data/lib/decidim/asset_router.rb +80 -0
  407. data/lib/decidim/attachment_attributes.rb +8 -8
  408. data/lib/decidim/attribute_object/form.rb +174 -0
  409. data/lib/decidim/attribute_object/model.rb +148 -0
  410. data/lib/decidim/attribute_object/nested_validator.rb +22 -0
  411. data/lib/decidim/attribute_object/type_map.rb +14 -0
  412. data/lib/decidim/attribute_object/type_resolver.rb +91 -0
  413. data/lib/decidim/attribute_object.rb +26 -0
  414. data/lib/decidim/attributes/array.rb +62 -0
  415. data/lib/decidim/attributes/blob.rb +20 -0
  416. data/lib/decidim/attributes/clean_string.rb +12 -6
  417. data/lib/decidim/attributes/hash.rb +36 -0
  418. data/lib/decidim/attributes/integer.rb +17 -0
  419. data/lib/decidim/attributes/localized_date.rb +9 -7
  420. data/lib/decidim/attributes/model.rb +24 -0
  421. data/lib/decidim/attributes/object.rb +28 -0
  422. data/lib/decidim/attributes/symbol.rb +23 -0
  423. data/lib/decidim/attributes/time_with_zone.rb +9 -7
  424. data/lib/decidim/attributes.rb +29 -0
  425. data/lib/decidim/authorable.rb +21 -6
  426. data/lib/decidim/authorization_form_builder.rb +9 -11
  427. data/lib/decidim/carrier_wave_migrator_service.rb +15 -15
  428. data/lib/decidim/coauthorable.rb +22 -4
  429. data/lib/decidim/command.rb +44 -0
  430. data/lib/decidim/common_passwords.rb +1 -1
  431. data/lib/decidim/component_manifest.rb +5 -5
  432. data/lib/decidim/component_validator.rb +2 -2
  433. data/lib/decidim/content_block_manifest.rb +1 -1
  434. data/lib/decidim/content_parsers/hashtag_parser.rb +2 -2
  435. data/lib/decidim/content_parsers/resource_parser.rb +1 -1
  436. data/lib/decidim/content_parsers/user_group_parser.rb +1 -1
  437. data/lib/decidim/content_parsers/user_parser.rb +4 -4
  438. data/lib/decidim/content_renderers/hashtag_renderer.rb +1 -1
  439. data/lib/decidim/content_renderers/link_renderer.rb +2 -2
  440. data/lib/decidim/content_renderers/user_group_renderer.rb +1 -1
  441. data/lib/decidim/content_renderers/user_renderer.rb +1 -1
  442. data/lib/decidim/controller_helpers.rb +41 -0
  443. data/lib/decidim/core/engine.rb +29 -18
  444. data/lib/decidim/core/test/factories.rb +48 -8
  445. data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +16 -4
  446. data/lib/decidim/core/test/shared_examples/amendable/amendment_form_examples.rb +1 -1
  447. data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +15 -2
  448. data/lib/decidim/core/test/shared_examples/coauthorable_interface_examples.rb +2 -2
  449. data/lib/decidim/core/test/shared_examples/comments_examples.rb +203 -0
  450. data/lib/decidim/core/test/shared_examples/localised_email.rb +1 -1
  451. data/lib/decidim/core/test/shared_examples/logo_email.rb +1 -1
  452. data/lib/decidim/core/test/shared_examples/map_examples.rb +2 -2
  453. data/lib/decidim/core/test/shared_examples/preview_component_with_share_token_examples.rb +0 -6
  454. data/lib/decidim/core/test/shared_examples/resource_search_examples.rb +120 -40
  455. data/lib/decidim/core/test/shared_examples/resourceable.rb +0 -6
  456. data/lib/decidim/core/test/shared_examples/searchable_participatory_space_examples.rb +1 -1
  457. data/lib/decidim/core/test/shared_examples/searchable_resources_shared_context.rb +1 -1
  458. data/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb +3 -3
  459. data/lib/decidim/core/test/shared_examples/translated_event_examples.rb +2 -2
  460. data/lib/decidim/core/version.rb +1 -1
  461. data/lib/decidim/core.rb +108 -14
  462. data/lib/decidim/{data_portability.rb → download_your_data.rb} +3 -3
  463. data/lib/decidim/{data_portability_serializers/data_portability_conversation_serializer.rb → download_your_data_serializers/download_your_data_conversation_serializer.rb} +2 -2
  464. data/lib/decidim/{data_portability_serializers/data_portability_follow_serializer.rb → download_your_data_serializers/download_your_data_follow_serializer.rb} +2 -2
  465. data/lib/decidim/{data_portability_serializers/data_portability_identity_serializer.rb → download_your_data_serializers/download_your_data_identity_serializer.rb} +2 -2
  466. data/lib/decidim/{data_portability_serializers/data_portability_notification_serializer.rb → download_your_data_serializers/download_your_data_notification_serializer.rb} +2 -2
  467. data/lib/decidim/{data_portability_serializers/data_portability_participatory_space_private_user_serializer.rb → download_your_data_serializers/download_your_data_participatory_space_private_user_serializer.rb} +2 -2
  468. data/lib/decidim/{data_portability_serializers/data_portability_report_serializer.rb → download_your_data_serializers/download_your_data_report_serializer.rb} +2 -2
  469. data/lib/decidim/{data_portability_serializers/data_portability_user_group_serializer.rb → download_your_data_serializers/download_your_data_user_group_serializer.rb} +2 -2
  470. data/lib/decidim/{data_portability_serializers/data_portability_user_serializer.rb → download_your_data_serializers/download_your_data_user_serializer.rb} +3 -3
  471. data/lib/decidim/download_your_data_serializers.rb +23 -0
  472. data/lib/decidim/engine_router.rb +7 -1
  473. data/lib/decidim/env.rb +56 -0
  474. data/lib/decidim/event_recorder.rb +25 -0
  475. data/lib/decidim/events/base_event.rb +10 -14
  476. data/lib/decidim/events/simple_event.rb +12 -4
  477. data/lib/decidim/exporters/export_data.rb +1 -1
  478. data/lib/decidim/exporters/export_manifest.rb +2 -1
  479. data/lib/decidim/file_validator_humanizer.rb +24 -1
  480. data/lib/decidim/filterable_resource.rb +55 -0
  481. data/lib/decidim/fingerprint_calculator.rb +2 -4
  482. data/lib/decidim/form_builder.rb +158 -130
  483. data/lib/decidim/gamification/badge.rb +2 -3
  484. data/lib/decidim/gamification/badge_scorer.rb +1 -0
  485. data/lib/decidim/has_area.rb +27 -0
  486. data/lib/decidim/has_attachments.rb +2 -2
  487. data/lib/decidim/has_category.rb +36 -0
  488. data/lib/decidim/importers/import_manifest.rb +3 -21
  489. data/lib/decidim/jsonb_attributes.rb +6 -4
  490. data/lib/decidim/manifest_messages.rb +23 -0
  491. data/lib/decidim/map/autocomplete.rb +6 -4
  492. data/lib/decidim/map/frontend.rb +2 -2
  493. data/lib/decidim/map/provider/geocoding/here.rb +5 -5
  494. data/lib/decidim/map/provider/here.rb +3 -0
  495. data/lib/decidim/map/provider/osm.rb +3 -0
  496. data/lib/decidim/map.rb +4 -4
  497. data/lib/decidim/menu.rb +1 -1
  498. data/lib/decidim/metric_manifest.rb +2 -2
  499. data/lib/decidim/nicknamizable.rb +1 -1
  500. data/lib/decidim/notification_setting_manifest.rb +19 -0
  501. data/lib/decidim/organization_settings.rb +8 -7
  502. data/lib/decidim/participatory_space_context_manifest.rb +2 -2
  503. data/lib/decidim/participatory_space_manifest.rb +2 -2
  504. data/lib/decidim/processes_file_locally.rb +31 -0
  505. data/lib/decidim/query.rb +84 -0
  506. data/lib/decidim/record_encryptor.rb +29 -8
  507. data/lib/decidim/reminder_manifest.rb +75 -0
  508. data/lib/decidim/reminder_registry.rb +67 -0
  509. data/lib/decidim/resource_manifest.rb +1 -1
  510. data/lib/decidim/resourceable.rb +11 -0
  511. data/lib/decidim/scopable.rb +15 -0
  512. data/lib/decidim/settings_manifest.rb +4 -4
  513. data/lib/decidim/spring.rb +16 -0
  514. data/lib/decidim/translatable_attributes.rb +12 -8
  515. data/lib/decidim/url_option_resolver.rb +50 -0
  516. data/lib/decidim/view_hooks.rb +1 -1
  517. data/lib/decidim/view_model.rb +5 -1
  518. data/lib/decidim/webpacker/thread_safe_compiler.rb +30 -0
  519. data/lib/decidim/webpacker/webpack/custom.js +15 -3
  520. data/lib/decidim/webpacker/webpacker.yml +1 -1
  521. data/lib/decidim/webpacker.rb +2 -0
  522. data/lib/devise/models/decidim_validatable.rb +1 -1
  523. data/lib/tasks/decidim_active_storage_migration_tasks.rake +1 -3
  524. data/lib/tasks/{decidim_data_portability_tasks.rake → decidim_download_your_data_tasks.rake} +9 -9
  525. data/lib/tasks/decidim_mailers_tasks.rake +23 -0
  526. data/lib/tasks/decidim_metrics_tasks.rake +1 -1
  527. data/lib/tasks/decidim_pwa_tasks.rake +18 -0
  528. data/lib/tasks/decidim_reminders_tasks.rake +17 -0
  529. data/lib/tasks/decidim_tasks.rake +6 -1
  530. data/lib/tasks/decidim_webpacker_tasks.rake +1 -1
  531. data/lib/tasks/upgrade/decidim_fix_nickname_uniqueness.rake +51 -0
  532. metadata +184 -83
  533. data/app/controllers/decidim/cookie_policy_controller.rb +0 -24
  534. data/app/helpers/decidim/cookies_helper.rb +0 -11
  535. data/app/packs/src/decidim/vizzs/renders.js +0 -9
  536. data/app/packs/src/decidim/vizzs.js +0 -1
  537. data/app/packs/stylesheets/decidim/modules/_cookie-bar.scss +0 -26
  538. data/app/services/decidim/home_activity_search.rb +0 -80
  539. data/app/services/decidim/participatory_space_search.rb +0 -54
  540. data/app/views/decidim/cookie_policy/accept.js.erb +0 -3
  541. data/app/views/decidim/export_mailer/data_portability_export.html.erb +0 -7
  542. data/lib/decidim/data_portability_serializers.rb +0 -23
  543. data/lib/decidim/middleware/rails_cookies.rb +0 -23
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Decidim
4
4
  class ShareToken < ApplicationRecord
5
- validates :organization, presence: true
6
- validates :user, presence: true
7
5
  validates :token, presence: true, uniqueness: { scope: [:decidim_organization_id, :token_for_type, :token_for_id] }
8
6
 
9
7
  belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization"
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # Short links are a way to reference specific locations within Decidim with
5
+ # shorted URLs, similar to the popular link shortening services. The original
6
+ # reason for creating the feature was to reference long calendar URLs in a
7
+ # more compact way for the URLs to be compatible with the calendar programs.
8
+ # When the URL is long with lots of filtering parameters included in it, it
9
+ # may be too long for specific 3rd party programs.
10
+ #
11
+ # This feature can be used to link to any URLs or resources in Decidim with a
12
+ # short reference.
13
+ class ShortLink < ApplicationRecord
14
+ belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization"
15
+ belongs_to :target, polymorphic: true
16
+
17
+ validates :identifier, presence: true, uniqueness: true
18
+
19
+ before_validation :generate_identifier
20
+
21
+ # Finds a matching short link to the same target with exactly the same
22
+ # parameters if it already exists or creates a new one if it doesn't exist.
23
+ #
24
+ # @param target [ActiveRecord::Base] The target where this short link should
25
+ # link to. Most of the times [Decidim::Organization], [Decidim::Component]
26
+ # or some other top-level record.
27
+ # @param mounted_engine [String] The mounted engine helper name that will be
28
+ # used to generate the link.
29
+ # @param route_name [String, nil] The route name to be linked to. If not
30
+ # defined, the short link will be generated for the root URL for the
31
+ # mounted engine.
32
+ # @param params [Hash] The query parameters that should be included in the
33
+ # URL where this short link will redirect to.
34
+ # @return [Decidim::ShortLink] The short link instance to link to.
35
+ def self.to(target, mounted_engine, route_name: nil, params: {})
36
+ organization =
37
+ if target.is_a?(Decidim::Organization)
38
+ target
39
+ else
40
+ target.try(:organization)
41
+ end
42
+
43
+ values = {
44
+ organization: organization,
45
+ target: target,
46
+ mounted_engine_name: mounted_engine,
47
+ route_name: route_name
48
+ }
49
+ existing =
50
+ if params
51
+ where(values).find_by("params = ?::jsonb", params.to_json)
52
+ else
53
+ find_by(values.merge(params: nil))
54
+ end
55
+
56
+ existing || create!(values.merge(params: params))
57
+ end
58
+
59
+ # Creates a random unique identifier for any new links. Raises an
60
+ # OutOfCandidatesError if a free candidate cannot be found with 20 tries.
61
+ # In this situation the older records should be removed from the database.
62
+ #
63
+ # @return [String] A new unique identifier.
64
+ def self.unique_identifier_within(organization)
65
+ 1.step do |n|
66
+ raise OutOfCandidatesError if n > 20
67
+
68
+ # A-Z, a-z and 0-9
69
+ # 26 + 26 + 10 = 62 possibilities per character
70
+ # 62^10 ≈ 8×10¹⁷ total possibilities
71
+ candidate = SecureRandom.alphanumeric(10)
72
+ next if where(organization: organization, identifier: candidate).any?
73
+
74
+ return candidate
75
+ end
76
+ end
77
+
78
+ # Overrides the route_name method to add a default route name for the "root"
79
+ # path in case the route name is not defined for the record.
80
+ #
81
+ # @return [String] The route name to link to.
82
+ def route_name
83
+ super || "root"
84
+ end
85
+
86
+ # Generates the short URL referencing this link.
87
+ #
88
+ # @return [String] The short URL that can be used to link to the target.
89
+ def short_url
90
+ EngineRouter.new("decidim", default_url_options).short_link_url(id: identifier)
91
+ end
92
+
93
+ # Generates the full long URL to the resource matching this short link.
94
+ #
95
+ # @return [String] The target full URL that the short link should link to.
96
+ def target_url
97
+ url_helpers.send("#{route_name}_url", **params)
98
+ end
99
+
100
+ private
101
+
102
+ # Generates a new identifier for new records.
103
+ #
104
+ # @return [void]
105
+ def generate_identifier
106
+ return if identifier.present?
107
+
108
+ self.identifier = self.class.unique_identifier_within(organization)
109
+ end
110
+
111
+ # Returns the default URL options for the organization. The participatory
112
+ # space and component related parameters are excluded from the
113
+ # default_url_options because these are also needed for the short URL
114
+ # generation.
115
+ #
116
+ # @return [Hash] A hash of the default URL options for the links.
117
+ def default_url_options
118
+ { host: organization&.host }.compact
119
+ end
120
+
121
+ # Returns the options for the URL helper call against the routes proxy.
122
+ # The options include the mounted params for the target, such as
123
+ # participatory space slug and component ID, depending what the target is.
124
+ #
125
+ # @return [Hash] The mounted options for the target record that will be used
126
+ # to generate the final link.
127
+ def mounted_options
128
+ if target.is_a?(Decidim::Participable)
129
+ # The mounted_params method returns always e.g. participatory_space_slug
130
+ # assembly_slug, etc. But when we want to link to the space itself, we
131
+ # only need the `slug` parameter.
132
+ { host: target.organization.host, slug: target.slug }
133
+ elsif target.respond_to?(:mounted_params)
134
+ target.mounted_params
135
+ elsif target.respond_to?(:component)
136
+ target.component.mounted_params.merge(id: target.id)
137
+ elsif target.respond_to?(:participatory_space)
138
+ target.participatory_space.mounted_params
139
+ elsif target.respond_to?(:slug)
140
+ # E.g. Decidim::StaticPage uses the slug as the `:id` parameter.
141
+ default_url_options.merge(id: target.slug)
142
+ elsif !target.is_a?(Decidim::Organization)
143
+ default_url_options.merge(id: target.id)
144
+ else
145
+ default_url_options
146
+ end
147
+ end
148
+
149
+ # Returns the routes helpers for the target engine.
150
+ #
151
+ # @return [Decidim::EngineRouter] An instance of the engine router that will
152
+ # be used to generate the links to the correct context.
153
+ def url_helpers
154
+ @url_helpers ||= EngineRouter.new(mounted_engine_name, mounted_options)
155
+ end
156
+
157
+ # The OutOfCandidatesError is an error class that will be raised when the
158
+ # short link identifiers are running out and the short link cannot be
159
+ # generated.
160
+ class OutOfCandidatesError < StandardError; end
161
+ end
162
+ end
@@ -5,15 +5,15 @@ require "devise/models/decidim_newsletterable"
5
5
  require "valid_email2"
6
6
 
7
7
  module Decidim
8
- # A User is a citizen that wants to join the platform to participate.
8
+ # A User is a participant that wants to join the platform to engage.
9
9
  class User < UserBaseEntity
10
- include Decidim::DataPortability
10
+ include Decidim::DownloadYourData
11
11
  include Decidim::Searchable
12
12
  include Decidim::ActsAsAuthor
13
13
  include Decidim::UserReportable
14
14
  include Decidim::Traceable
15
15
 
16
- REGEXP_NICKNAME = /\A[\w\-]+\z/.freeze
16
+ REGEXP_NICKNAME = /\A[\w\-]+\z/
17
17
 
18
18
  class Roles
19
19
  def self.all
@@ -34,6 +34,7 @@ module Decidim
34
34
  has_many :user_groups, through: :memberships, class_name: "Decidim::UserGroup", foreign_key: :decidim_user_group_id
35
35
  has_many :access_grants, class_name: "Doorkeeper::AccessGrant", foreign_key: :resource_owner_id, dependent: :destroy
36
36
  has_many :access_tokens, class_name: "Doorkeeper::AccessToken", foreign_key: :resource_owner_id, dependent: :destroy
37
+ has_many :reminders, foreign_key: "decidim_user_id", class_name: "Decidim::Reminder", dependent: :destroy
37
38
 
38
39
  has_one :blocking, class_name: "Decidim::UserBlock", foreign_key: :id, primary_key: :block_id, dependent: :destroy
39
40
 
@@ -50,7 +51,7 @@ module Decidim
50
51
 
51
52
  validate :all_roles_are_valid
52
53
 
53
- has_one_attached :data_portability_file
54
+ has_one_attached :download_your_data_file
54
55
 
55
56
  scope :not_deleted, -> { where(deleted_at: nil) }
56
57
 
@@ -81,12 +82,14 @@ module Decidim
81
82
  # scope_id: :decidim_scope_id,
82
83
  organization_id: :decidim_organization_id,
83
84
  A: :name,
85
+ B: :nickname,
84
86
  datetime: :created_at
85
87
  },
86
88
  index_on_create: ->(user) { !(user.deleted? || user.blocked?) },
87
89
  index_on_update: ->(user) { !(user.deleted? || user.blocked?) })
88
90
 
89
91
  before_save :ensure_encrypted_password
92
+ before_save :save_password_change
90
93
 
91
94
  def user_invited?
92
95
  invitation_token_changed? && invitation_accepted_at_changed?
@@ -186,10 +189,10 @@ module Decidim
186
189
  end
187
190
 
188
191
  def self.export_serializer
189
- Decidim::DataPortabilitySerializers::DataPortabilityUserSerializer
192
+ Decidim::DownloadYourDataSerializers::DownloadYourDataUserSerializer
190
193
  end
191
194
 
192
- def self.data_portability_images(user)
195
+ def self.download_your_data_images(user)
193
196
  user_collection(user).map(&:avatar)
194
197
  end
195
198
 
@@ -255,6 +258,18 @@ module Decidim
255
258
  Arel.sql(%{("decidim_users"."last_sign_in_at")::text})
256
259
  end
257
260
 
261
+ def notifications_subscriptions
262
+ notification_settings.fetch("subscriptions", {})
263
+ end
264
+
265
+ def needs_password_update?
266
+ return false unless admin?
267
+ return false unless Decidim.config.admin_password_strong
268
+ return true if password_updated_at.blank?
269
+
270
+ password_updated_at < Decidim.config.admin_password_expiration_days.days.ago
271
+ end
272
+
258
273
  protected
259
274
 
260
275
  # Overrides devise email required validation.
@@ -302,5 +317,18 @@ module Decidim
302
317
  def ensure_encrypted_password
303
318
  restore_encrypted_password! if will_save_change_to_encrypted_password? && encrypted_password.blank?
304
319
  end
320
+
321
+ def save_password_change
322
+ return unless persisted?
323
+ return unless encrypted_password_changed?
324
+ return unless admin?
325
+ return unless Decidim.config.admin_password_strong
326
+
327
+ # We don't want to run validations here because that could lead to an endless validation loop.
328
+ # rubocop:disable Rails/SkipsModelValidations
329
+ update_column(:password_updated_at, Time.current)
330
+ update_column(:previous_passwords, [encrypted_password_was, *previous_passwords].first(Decidim.config.admin_password_repetition_times))
331
+ # rubocop:enable Rails/SkipsModelValidations
332
+ end
305
333
  end
306
334
  end
@@ -18,7 +18,7 @@ module Decidim
18
18
  has_many :following_follows, foreign_key: "decidim_user_id", class_name: "Decidim::Follow", dependent: :destroy
19
19
 
20
20
  # Regex for name & nickname format validations
21
- REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}\\|])/.freeze
21
+ REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}\\|])/
22
22
 
23
23
  has_one_attached :avatar
24
24
  validates_avatar :avatar, uploader: Decidim::AvatarUploader
@@ -4,10 +4,10 @@ require "devise/models/decidim_validatable"
4
4
  require "valid_email2"
5
5
 
6
6
  module Decidim
7
- # A UserGroup is an organization of citizens
7
+ # A UserGroup is an organization of participants
8
8
  class UserGroup < UserBaseEntity
9
9
  include Decidim::Traceable
10
- include Decidim::DataPortability
10
+ include Decidim::DownloadYourData
11
11
  include Decidim::ActsAsAuthor
12
12
  include Decidim::UserReportable
13
13
  include Decidim::Searchable
@@ -87,7 +87,7 @@ module Decidim
87
87
  end
88
88
 
89
89
  def self.export_serializer
90
- Decidim::DataPortabilitySerializers::DataPortabilityUserGroupSerializer
90
+ Decidim::DownloadYourDataSerializers::DownloadYourDataUserGroupSerializer
91
91
  end
92
92
 
93
93
  def document_number
@@ -12,8 +12,6 @@ module Decidim
12
12
  scope :unblocked, -> { joins(:user).where(decidim_users: { blocked: false }) }
13
13
 
14
14
  delegate :organization, to: :user
15
- scope :blocked, -> { joins(:user).where(decidim_users: { blocked: true }) }
16
- scope :unblocked, -> { joins(:user).where(decidim_users: { blocked: false }) }
17
15
 
18
16
  def self.log_presenter_class_for(_log)
19
17
  Decidim::AdminLog::UserModerationPresenter
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  class UserReport < ApplicationRecord
5
- include Decidim::DataPortability
5
+ include Decidim::DownloadYourData
6
6
 
7
7
  REASONS = %w(spam offensive does_not_belong).freeze
8
8
 
@@ -16,7 +16,7 @@ module Decidim
16
16
 
17
17
  def self.export_serializer
18
18
  raise NotImplementedError
19
- # Decidim::DataPortabilitySerializers::DataPortabilityReportSerializer
19
+ # Decidim::DownloadYourDataSerializers::DownloadYourDataReportSerializer
20
20
  end
21
21
 
22
22
  private
@@ -14,7 +14,6 @@ import "src/decidim/vendor/foundation-datepicker"
14
14
  import "src/decidim/foundation_datepicker_locales"
15
15
  import "src/decidim/vendor/modernizr"
16
16
  import "src/decidim/vendor/social-share-button"
17
- import "social-share-button"
18
17
 
19
18
  import "src/decidim/input_tags"
20
19
  import "src/decidim/input_hashtags"
@@ -57,7 +56,9 @@ import "src/decidim/start_conversation_dialog"
57
56
  import "src/decidim/notifications"
58
57
  import "src/decidim/identity_selector_dialog"
59
58
  import "src/decidim/gallery"
59
+ import "src/decidim/direct_uploads/upload_field"
60
60
  import "src/decidim/back_to_list"
61
+ import "src/decidim/cookie_consent/cookie_consent"
61
62
 
62
63
  // CSS
63
64
  import "entrypoints/decidim_core.scss"
@@ -70,4 +71,3 @@ require.context("../images", true)
70
71
 
71
72
  // This needs to be loaded after confirm dialog to bind properly
72
73
  Rails.start()
73
-
@@ -0,0 +1 @@
1
+ import "src/decidim/sw"
@@ -0,0 +1,298 @@
1
+ import AutoCompleteJS from "@tarekraafat/autocomplete.js";
2
+
3
+ export default class AutoComplete {
4
+ constructor(el, options = {}) {
5
+ this.element = el;
6
+ this.stickySelectedSpan = null;
7
+ this.clearStickySelectionSpan = null;
8
+ this.stickyHiddenInput = null;
9
+ this.promptDiv = null;
10
+ const thresholdTemp = options.threshold || 2;
11
+ this.options = Object.assign({
12
+ // Defines name of the hidden input (e.g. assembly_member[user_id])
13
+ name: null,
14
+ // Placeholder of the visible input field
15
+ placeholder: "",
16
+ // Defines what happens after user has selected value from suggestions
17
+ // sticky - Allows selecting a single value and not editing the value after selected (e.g. as the admin autocomplete fields)
18
+ // single - Allows selecting a single value and editing the selected text after the selection (e.g. geocoding field)
19
+ // multi - Allows selecting multiple values
20
+ // null (default) - Disable selection event handling in this class
21
+ mode: null,
22
+ // Defines if we show input help (e.g. "Type at least three characters to search") or not.
23
+ searchPrompt: false,
24
+ // Defines search prompt message, only shown if showPrompt is enabled!
25
+ searchPromptText: `Type at least ${thresholdTemp} characters to search`,
26
+ // Defines items that are selected already when page is loaded before user selects them. (e.g. when form submit fails)
27
+ selected: null,
28
+ // Defines how many characters input has to have before we start searching
29
+ threshold: thresholdTemp,
30
+ // Defines how many results to show in the autocomplete selection list
31
+ // by maximum.
32
+ maxResults: 10,
33
+ // Defines the data keys against which to match the user input when
34
+ // searching through the results. For example, when the following
35
+ // data is returned by the API:
36
+ // { id: 123, name: "John", nickname: "john", __typename: "User" }
37
+ //
38
+ // You can define the data keys array as ["name", "nickname"] in
39
+ // which case the results shown to user would be only those that
40
+ // have matching text in these defined fields.
41
+ dataMatchKeys: null,
42
+ // The data source is a method that gets the callback parameter as
43
+ // its first argument which should be called with the results array
44
+ // once they are returned by the API.
45
+ // For example:
46
+ // (query, callback) => {
47
+ // (async () => {
48
+ // const results = await callAjax(`/api/url?query=${query}`);
49
+ // callback(results);
50
+ // })();
51
+ // }
52
+ //
53
+ // Signature: (callback: Function)
54
+ dataSource: () => [],
55
+ // Filters the data list returned by the data source before it is shown
56
+ // to the user. Can be used e.g. to hide already selected values from
57
+ // the list.
58
+ dataFilter: null,
59
+ // Delay in milliseconds how long to wait after user action before
60
+ // doing a backend request.
61
+ delay: 200,
62
+ // Allows modifying the suggested items before they are displayed in the list
63
+ // Signature: (element: HTMLElement, value: Object)
64
+ modifyResult: null
65
+ }, options);
66
+
67
+ this.autocomplete = new AutoCompleteJS({
68
+ selector: () => this.element,
69
+ placeHolder: options.placeholder,
70
+ // Delay (milliseconds) before autocomplete engine starts. It's preventing many queries when user is typing fast.
71
+ debounce: 200,
72
+ threshold: this.options.threshold,
73
+ data: {
74
+ keys: this.options.dataMatchKeys,
75
+ src: async (query) => {
76
+ const fetchResults = () => {
77
+ return new Promise((resolve) => {
78
+ this.options.dataSource(query, resolve);
79
+ });
80
+ }
81
+
82
+ try {
83
+ return await fetchResults();
84
+ } catch (error) {
85
+ return error;
86
+ }
87
+ },
88
+ filter: (list) => {
89
+ if (this.options.dataFilter) {
90
+ return this.options.dataFilter(list);
91
+ }
92
+
93
+ return list;
94
+ }
95
+ },
96
+ resultsList: {
97
+ maxResults: this.options.maxResults
98
+ },
99
+ resultItem: {
100
+ element: (item, data) => {
101
+ if (!this.options.modifyResult) {
102
+ return;
103
+ }
104
+
105
+ this.options.modifyResult(item, data.value);
106
+ }
107
+ },
108
+ events: {
109
+ input: {
110
+ blur: () => {
111
+ this.promptDiv.style.display = "none";
112
+ }
113
+ }
114
+ }
115
+ });
116
+
117
+ this.acWrapper = this.element.closest(".autoComplete_wrapper");
118
+ this.element.ac = this.autocomplete;
119
+
120
+ // Stop input field from bubbling open and close events to parent elements,
121
+ // because foundation closes modal from these events.
122
+ const stopPropagation = (event) => {
123
+ event.stopPropagation();
124
+ }
125
+ this.element.addEventListener("close", stopPropagation);
126
+ this.element.addEventListener("open", stopPropagation);
127
+
128
+ this.createPromptDiv();
129
+
130
+ switch (this.options.mode) {
131
+ case "sticky":
132
+ this.createStickySelect(this.options.name);
133
+ break;
134
+ case "multi":
135
+ this.createMultiSelect(this.options.name);
136
+ break;
137
+ default:
138
+ }
139
+ }
140
+
141
+ setInput(value) {
142
+ this.autocomplete.input.value = value;
143
+ }
144
+
145
+ handleEvent(event) {
146
+ switch (this.options.mode) {
147
+ case "single":
148
+ this.setInput(event.detail.selection.value[event.detail.selection.key]);
149
+ break;
150
+ case "sticky":
151
+ this.handleStickyEvents(event);
152
+ break;
153
+ case "multi":
154
+ this.handleMultiEvents(event);
155
+ break;
156
+ default:
157
+ }
158
+ }
159
+
160
+ handleMultiEvents(event) {
161
+ switch (event.type) {
162
+ case "selection":
163
+ this.addMultiSelectItem(event.detail.selection);
164
+ break;
165
+ default:
166
+ }
167
+ }
168
+
169
+ handleStickyEvents(event) {
170
+ switch (event.type) {
171
+ case "selection":
172
+ this.addStickySelectItem(event.detail.selection);
173
+ break;
174
+ case "click":
175
+ if (event.target === this.clearStickySelectionSpan) {
176
+ this.removeStickySelection();
177
+ }
178
+ break;
179
+ case "keyup":
180
+ if (this.stickyHiddenInput.value !== "" && event.target === this.element && (["Escape", "Backspace", "Delete"].includes(event.key) || (/^[a-z0-9]$/i).test(event.key))) {
181
+ this.removeStickySelection();
182
+ } else if (this.options.searchPrompt) {
183
+ if (this.stickyHiddenInput.value === "" && this.element.value.length < this.options.threshold) {
184
+ this.promptDiv.style.display = "block";
185
+ } else {
186
+ this.promptDiv.style.display = "none";
187
+ }
188
+ }
189
+ break;
190
+ default:
191
+ }
192
+ }
193
+
194
+ createHiddenInput(value) {
195
+ const hiddenInput = document.createElement("input");
196
+ hiddenInput.name = this.options.name;
197
+ hiddenInput.type = "hidden";
198
+ if (value) {
199
+ hiddenInput.value = value;
200
+ }
201
+ this.acWrapper.prepend(hiddenInput);
202
+ return hiddenInput;
203
+ }
204
+
205
+ removeStickySelection() {
206
+ this.stickyHiddenInput.value = "";
207
+ this.element.placeholder = this.options.placeholder;
208
+ this.clearStickySelectionSpan.style.display = "none";
209
+ this.stickySelectedSpan.style.display = "none";
210
+ }
211
+
212
+ addStickySelectItem(selection) {
213
+ this.stickyHiddenInput.value = selection.value.value;
214
+ this.element.placeholder = "";
215
+ this.stickySelectedSpan.innerHTML = selection.value[selection.key];
216
+ this.stickySelectedSpan.style.display = "block";
217
+ this.clearStickySelectionSpan.style.display = "block";
218
+ this.setInput("");
219
+ }
220
+
221
+ addMultiSelectItem(selection) {
222
+ this.setInput("");
223
+ const chosen = document.createElement("span");
224
+ chosen.classList.add("label", "primary", "autocomplete__selected-item", "multi");
225
+ chosen.innerHTML = selection.value[selection.key];
226
+ const clearSelection = document.createElement("span");
227
+ clearSelection.classList.add("clear-multi-selection");
228
+ clearSelection.innerHTML = "&times;";
229
+ clearSelection.setAttribute("data-remove", selection.value.value);
230
+ clearSelection.addEventListener("click", (evt) => {
231
+ const hiddenInput = this.acWrapper.querySelector(`input[type='hidden'][value='${selection.value.value}']`);
232
+ if (hiddenInput) {
233
+ hiddenInput.remove();
234
+ evt.target.parentElement.remove();
235
+ }
236
+ });
237
+ chosen.appendChild(clearSelection);
238
+
239
+ const multiSelectWrapper = this.acWrapper.querySelector(".multiselect");
240
+ const inputContainer = multiSelectWrapper.querySelector("span.input-container");
241
+ multiSelectWrapper.insertBefore(chosen, inputContainer);
242
+ this.createHiddenInput(selection.value.value);
243
+ }
244
+
245
+ createStickySelect() {
246
+ this.stickySelectedSpan = document.createElement("span");
247
+ this.stickySelectedSpan.classList.add("autocomplete__selected-item", "sticky");
248
+ this.stickySelectedSpan.style.display = "none";
249
+ this.stickySelectedSpan.addEventListener("click", () => this.element.focus());
250
+ this.stickyHiddenInput = this.createHiddenInput();
251
+
252
+ this.clearStickySelectionSpan = document.createElement("span");
253
+ this.clearStickySelectionSpan.className = "clear-sticky-selection";
254
+ this.clearStickySelectionSpan.innerHTML = "&times;";
255
+ this.clearStickySelectionSpan.style.display = "none";
256
+ this.clearStickySelectionSpan.addEventListener("click", this);
257
+
258
+ this.element.addEventListener("selection", this);
259
+ this.element.addEventListener("keyup", this);
260
+
261
+ this.acWrapper.insertBefore(this.clearStickySelectionSpan, this.element);
262
+ this.acWrapper.insertBefore(this.stickySelectedSpan, this.element);
263
+ if (this.options.selected) {
264
+ this.addStickySelectItem(this.options.selected);
265
+ }
266
+ }
267
+
268
+ createMultiSelect() {
269
+ const multiSelectWrapper = document.createElement("div");
270
+ multiSelectWrapper.classList.add("multiselect");
271
+
272
+ const inputContainer = document.createElement("span");
273
+ inputContainer.classList.add("input-container");
274
+
275
+ multiSelectWrapper.appendChild(inputContainer);
276
+ this.acWrapper.prepend(multiSelectWrapper);
277
+ inputContainer.appendChild(this.element);
278
+
279
+ this.element.addEventListener("selection", this);
280
+ multiSelectWrapper.addEventListener("click", () => {
281
+ this.element.focus();
282
+ })
283
+
284
+ if (this.options.selected) {
285
+ this.options.selected.forEach((selection) => {
286
+ this.addMultiSelectItem(selection);
287
+ })
288
+ }
289
+ }
290
+
291
+ createPromptDiv() {
292
+ this.promptDiv = document.createElement("div");
293
+ this.promptDiv.classList.add("search-prompt");
294
+ this.promptDiv.style.display = "none";
295
+ this.promptDiv.innerHTML = this.options.searchPromptText;
296
+ this.acWrapper.appendChild(this.promptDiv);
297
+ }
298
+ }