decidim-core 0.31.2 → 0.32.1

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 (516) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -13
  3. data/app/cells/decidim/amendable/amend_button_card/show.erb +2 -2
  4. data/app/cells/decidim/amendable/amend_button_card_cell.rb +1 -1
  5. data/app/cells/decidim/author_cell.rb +0 -4
  6. data/app/cells/decidim/card_metadata_cell.rb +1 -1
  7. data/app/cells/decidim/comments_button_cell.rb +9 -1
  8. data/app/cells/decidim/content_blocks/announcement_settings_form/show.erb +5 -0
  9. data/app/cells/decidim/content_blocks/announcement_settings_form_cell.rb +17 -0
  10. data/app/cells/decidim/content_blocks/cta_settings_form/show.erb +1 -1
  11. data/app/cells/decidim/content_blocks/hero_settings_form/show.erb +1 -1
  12. data/app/cells/decidim/content_blocks/highlighted_content_banner_cell.rb +1 -1
  13. data/app/cells/decidim/content_blocks/highlighted_content_banner_settings_form/show.erb +1 -1
  14. data/app/cells/decidim/content_blocks/highlighted_elements_with_cell_for_list_cell.rb +5 -1
  15. data/app/cells/decidim/content_blocks/highlighted_participatory_spaces_cell.rb +1 -1
  16. data/app/cells/decidim/content_blocks/how_to_participate/show.erb +1 -1
  17. data/app/cells/decidim/content_blocks/html_cell.rb +1 -1
  18. data/app/cells/decidim/content_blocks/participatory_space_announcement_cell.rb +1 -3
  19. data/app/cells/decidim/content_blocks/participatory_space_hero_cell.rb +1 -5
  20. data/app/cells/decidim/content_blocks/participatory_space_hero_settings_form/show.erb +1 -1
  21. data/app/cells/decidim/content_blocks/static_page/section_cell.rb +1 -1
  22. data/app/cells/decidim/content_blocks/static_page/section_settings_form/show.erb +3 -1
  23. data/app/cells/decidim/content_blocks/static_page/summary_cell.rb +1 -1
  24. data/app/cells/decidim/content_blocks/static_page/summary_settings_form/show.erb +3 -1
  25. data/app/cells/decidim/content_blocks/static_page/two_pane_section_cell.rb +2 -2
  26. data/app/cells/decidim/content_blocks/static_page/two_pane_section_settings_form/show.erb +4 -2
  27. data/app/cells/decidim/data_consent/category.erb +25 -38
  28. data/app/cells/decidim/footer_topics/show.erb +5 -8
  29. data/app/cells/decidim/{participatory_space_private_user → member}/show.erb +6 -6
  30. data/app/cells/decidim/{participatory_space_private_user_cell.rb → member_cell.rb} +1 -5
  31. data/app/cells/decidim/nav_links/show.erb +3 -3
  32. data/app/cells/decidim/onboarding_action_message_cell.rb +1 -0
  33. data/app/cells/decidim/profile/details.erb +1 -1
  34. data/app/cells/decidim/profile_cell.rb +1 -0
  35. data/app/cells/decidim/profile_member_of/show.erb +13 -0
  36. data/app/cells/decidim/profile_member_of_cell.rb +35 -0
  37. data/app/cells/decidim/report_button/already_reported_modal.erb +1 -1
  38. data/app/cells/decidim/report_button/flag_modal.erb +1 -1
  39. data/app/cells/decidim/report_user_button/already_reported_modal.erb +1 -1
  40. data/app/cells/decidim/report_user_button/flag_modal.erb +1 -1
  41. data/app/cells/decidim/resource_types_filter/show.erb +2 -2
  42. data/app/cells/decidim/share_text_widget/modal.erb +1 -1
  43. data/app/cells/decidim/share_widget/qr_code_modal.erb +1 -1
  44. data/app/cells/decidim/tags_cell.rb +1 -1
  45. data/app/cells/decidim/upload_modal/files.erb +5 -1
  46. data/app/cells/decidim/upload_modal_cell.rb +15 -1
  47. data/app/cells/decidim/versions_list/show.erb +25 -0
  48. data/app/commands/decidim/create_follow.rb +3 -5
  49. data/app/commands/decidim/create_user_report.rb +1 -1
  50. data/app/commands/decidim/destroy_account.rb +15 -4
  51. data/app/commands/decidim/multiple_attachments_methods.rb +42 -24
  52. data/app/commands/decidim/search.rb +5 -5
  53. data/app/controllers/concerns/decidim/ajax_permission_handler.rb +1 -1
  54. data/app/controllers/concerns/decidim/devise_controllers.rb +9 -0
  55. data/app/controllers/concerns/decidim/direct_upload.rb +3 -2
  56. data/app/controllers/concerns/decidim/ephemeral_session_checker.rb +1 -1
  57. data/app/controllers/concerns/decidim/force_authentication.rb +10 -1
  58. data/app/controllers/concerns/decidim/impersonate_users.rb +4 -0
  59. data/app/controllers/concerns/decidim/locale_switcher.rb +52 -2
  60. data/app/controllers/concerns/decidim/needs_tos_accepted.rb +1 -1
  61. data/app/controllers/concerns/decidim/participatory_space/has_members_page.rb +27 -0
  62. data/app/controllers/concerns/decidim/participatory_space_context.rb +1 -5
  63. data/app/controllers/decidim/account_controller.rb +1 -1
  64. data/app/controllers/decidim/amendments_controller.rb +5 -4
  65. data/app/controllers/decidim/components/base_controller.rb +20 -3
  66. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -1
  67. data/app/controllers/decidim/devise/passwords_controller.rb +1 -1
  68. data/app/controllers/decidim/devise/registrations_controller.rb +1 -1
  69. data/app/controllers/decidim/devise/sessions_controller.rb +1 -1
  70. data/app/controllers/decidim/download_your_data_controller.rb +2 -2
  71. data/app/controllers/decidim/editor_images_controller.rb +1 -1
  72. data/app/controllers/decidim/follows_controller.rb +4 -3
  73. data/app/controllers/decidim/geolocation_controller.rb +1 -1
  74. data/app/controllers/decidim/likes_controller.rb +2 -1
  75. data/app/controllers/decidim/locales_controller.rb +3 -11
  76. data/app/controllers/decidim/messaging/conversations_controller.rb +3 -3
  77. data/app/controllers/decidim/newsletters_controller.rb +1 -1
  78. data/app/controllers/decidim/notifications_settings_controller.rb +1 -1
  79. data/app/controllers/decidim/notifications_subscriptions_controller.rb +8 -0
  80. data/app/controllers/decidim/open_data_controller.rb +1 -1
  81. data/app/controllers/decidim/private_downloads_controller.rb +29 -0
  82. data/app/controllers/decidim/report_users_controller.rb +2 -2
  83. data/app/controllers/decidim/reports_controller.rb +1 -1
  84. data/app/controllers/decidim/user_activities_controller.rb +1 -2
  85. data/app/events/decidim/amendable/amendment_base_event.rb +7 -1
  86. data/app/events/decidim/welcome_notification_event.rb +1 -1
  87. data/app/forms/decidim/upload_validation_form.rb +1 -1
  88. data/app/helpers/concerns/decidim/flash_helper_extensions.rb +1 -0
  89. data/app/helpers/decidim/active_link_to_helper.rb +136 -0
  90. data/app/helpers/decidim/amendments_helper.rb +3 -3
  91. data/app/helpers/decidim/check_boxes_tree_helper.rb +2 -2
  92. data/app/helpers/decidim/component_path_helper.rb +9 -4
  93. data/app/helpers/decidim/filters_helper.rb +0 -2
  94. data/app/helpers/decidim/mailer_helper.rb +36 -0
  95. data/app/helpers/decidim/menu_helper.rb +4 -5
  96. data/app/helpers/decidim/meta_tags_helper.rb +0 -2
  97. data/app/helpers/decidim/newsletters_helper.rb +4 -22
  98. data/app/helpers/decidim/paginate_helper.rb +14 -1
  99. data/app/helpers/decidim/sanitize_helper.rb +1 -0
  100. data/app/helpers/decidim/short_link_helper.rb +5 -5
  101. data/app/helpers/decidim/translations_helper.rb +4 -4
  102. data/app/jobs/decidim/delete_inactive_participants_job.rb +2 -2
  103. data/app/jobs/decidim/find_and_update_descendants_job.rb +8 -2
  104. data/app/jobs/decidim/process_inactive_participant_job.rb +0 -7
  105. data/app/jobs/decidim/update_search_indexes_job.rb +2 -2
  106. data/app/mailers/decidim/application_mailer.rb +11 -2
  107. data/app/mailers/decidim/decidim_devise_mailer.rb +1 -0
  108. data/app/mailers/decidim/delete_user_mailer.rb +14 -0
  109. data/app/mailers/decidim/participants_account_mailer.rb +0 -16
  110. data/app/mailers/decidim/reported_mailer.rb +4 -1
  111. data/app/models/decidim/action_log.rb +1 -1
  112. data/app/models/decidim/attachment.rb +20 -2
  113. data/app/models/decidim/authorization.rb +7 -0
  114. data/app/models/decidim/authorization_transfer.rb +1 -1
  115. data/app/models/decidim/component.rb +6 -9
  116. data/app/models/decidim/content_block.rb +3 -3
  117. data/app/models/decidim/moderation.rb +1 -1
  118. data/app/models/decidim/newsletter.rb +2 -2
  119. data/app/models/decidim/participatory_space/member.rb +55 -0
  120. data/app/models/decidim/private_download.rb +61 -0
  121. data/app/models/decidim/private_export.rb +6 -0
  122. data/app/models/decidim/resource_link.rb +1 -1
  123. data/app/models/decidim/short_link.rb +1 -1
  124. data/app/models/decidim/user.rb +2 -2
  125. data/app/models/decidim/user_base_entity.rb +17 -2
  126. data/app/models/decidim/user_moderation.rb +1 -1
  127. data/app/packs/src/decidim/a11y.js +29 -0
  128. data/app/packs/src/decidim/a11y.test.js +81 -0
  129. data/app/packs/src/decidim/confirm.js +8 -1
  130. data/app/packs/src/decidim/confirm.test.js +225 -0
  131. data/app/packs/src/decidim/controllers/accordion/accordion.test.js +272 -0
  132. data/app/packs/src/decidim/controllers/accordion/controller.js +117 -17
  133. data/app/packs/src/decidim/controllers/assign_role/assign_role.test.js +80 -0
  134. data/app/packs/src/decidim/controllers/assign_role/controller.js +27 -0
  135. data/app/packs/src/decidim/controllers/breadcrumb_truncate/breadcrumb_truncate.test.js +230 -0
  136. data/app/packs/src/decidim/controllers/breadcrumb_truncate/controller.js +172 -0
  137. data/app/packs/src/decidim/controllers/dropdown/controller.js +32 -0
  138. data/app/packs/src/decidim/controllers/dropdown/dropdown.test.js +215 -0
  139. data/app/packs/src/decidim/controllers/form_validator/form_validator.js +9 -8
  140. data/app/packs/src/decidim/controllers/form_validator/form_validator.test.js +28 -1
  141. data/app/packs/src/decidim/controllers/language_change/controller.js +1 -0
  142. data/app/packs/src/decidim/controllers/language_change/language_change.test.js +13 -0
  143. data/app/packs/src/decidim/controllers/main_menu/controller.js +152 -0
  144. data/app/packs/src/decidim/controllers/main_menu/main_menu.test.js +285 -0
  145. data/app/packs/src/decidim/controllers/mention/controller.js +296 -140
  146. data/app/packs/src/decidim/controllers/mention/input_mentions.test.js +120 -457
  147. data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +68 -32
  148. data/app/packs/src/decidim/controllers/multiple_mentions/input_multiple_mentions.test.js +30 -23
  149. data/app/packs/src/decidim/controllers/password_toggler/controller.js +22 -1
  150. data/app/packs/src/decidim/controllers/password_toggler/password_toggler.test.js +11 -20
  151. data/app/packs/src/decidim/datepicker/datepicker_functions.js +26 -0
  152. data/app/packs/src/decidim/datepicker/generate_datepicker.js +3 -14
  153. data/app/packs/src/decidim/datepicker/generate_timepicker.js +3 -2
  154. data/app/packs/src/decidim/datepicker/test/datepicker_functions_adjust_picker_position.test.js +234 -0
  155. data/app/packs/src/decidim/direct_uploads/upload_field.js +4 -4
  156. data/app/packs/src/decidim/direct_uploads/upload_modal.js +12 -7
  157. data/app/packs/src/decidim/editor/common/suggestion.js +4 -2
  158. data/app/packs/src/decidim/editor/extensions/decidim_kit/index.js +4 -1
  159. data/app/packs/src/decidim/editor/extensions/image/index.js +57 -16
  160. data/app/packs/src/decidim/editor/extensions/image/node_view.js +9 -1
  161. data/app/packs/src/decidim/editor/extensions/indent/index.js +9 -0
  162. data/app/packs/src/decidim/editor/extensions/link/bubble_menu.js +34 -6
  163. data/app/packs/src/decidim/editor/extensions/link/index.js +53 -12
  164. data/app/packs/src/decidim/editor/extensions/mention/index.js +18 -5
  165. data/app/packs/src/decidim/editor/extensions/mention_resource/index.js +16 -7
  166. data/app/packs/src/decidim/editor/test/extensions/decidim_kit.test.js +13 -4
  167. data/app/packs/src/decidim/editor/test/extensions/heading.test.js +1 -1
  168. data/app/packs/src/decidim/editor/test/extensions/image.test.js +26 -28
  169. data/app/packs/src/decidim/editor/test/extensions/image_links.test.js +161 -0
  170. data/app/packs/src/decidim/editor/test/extensions/mention.test.js +6 -2
  171. data/app/packs/src/decidim/editor/test/extensions/video_embed.test.js +27 -15
  172. data/app/packs/src/decidim/editor/test/helpers.js +8 -10
  173. data/app/packs/src/decidim/editor/test/toolbar/full.test.js +15 -4
  174. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_block.js +8 -4
  175. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_formatting.js +5 -1
  176. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_indent.js +9 -5
  177. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_link.js +5 -1
  178. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_list.js +8 -4
  179. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_styling.js +13 -9
  180. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_content_styling.js +6 -2
  181. data/app/packs/src/decidim/geocoding/reverse_geocoding.js +15 -5
  182. data/app/packs/src/decidim/geocoding/reverse_geocoding.test.js +197 -0
  183. data/app/packs/src/decidim/index.js +4 -4
  184. data/app/packs/src/decidim/map/controller.js +10 -1
  185. data/app/packs/src/decidim/refactor/moved/focus_guard.js +16 -16
  186. data/app/packs/src/decidim/refactor/moved/focus_guard.test.js +136 -0
  187. data/app/packs/src/decidim/sw/push-permissions.js +48 -13
  188. data/app/packs/src/decidim/sw/sw.js +1 -1
  189. data/app/packs/src/decidim/utilities/text.js +6 -6
  190. data/app/packs/stylesheets/decidim/_cards.scss +15 -6
  191. data/app/packs/stylesheets/decidim/_conversations.scss +14 -0
  192. data/app/packs/stylesheets/decidim/_cookies.scss +13 -5
  193. data/app/packs/stylesheets/decidim/_datepicker.scss +0 -8
  194. data/app/packs/stylesheets/decidim/_documents.scss +1 -1
  195. data/app/packs/stylesheets/decidim/_dropdown.scss +22 -32
  196. data/app/packs/stylesheets/decidim/_editor_suggestions.scss +49 -0
  197. data/app/packs/stylesheets/decidim/_flash.scss +1 -1
  198. data/app/packs/stylesheets/decidim/_floating_help.scss +1 -1
  199. data/app/packs/stylesheets/decidim/_footer.scss +27 -31
  200. data/app/packs/stylesheets/decidim/_forms.scss +5 -1
  201. data/app/packs/stylesheets/decidim/_header.scss +183 -48
  202. data/app/packs/stylesheets/decidim/_language_chooser.scss +79 -0
  203. data/app/packs/stylesheets/decidim/_participatory_spaces.scss +1 -1
  204. data/app/packs/stylesheets/decidim/_rich_text.scss +17 -0
  205. data/app/packs/stylesheets/decidim/_tom_select.scss +23 -0
  206. data/app/packs/stylesheets/decidim/application.scss +3 -0
  207. data/app/packs/stylesheets/decidim/editor.scss +12 -33
  208. data/app/packs/stylesheets/decidim/geocoding_addons.scss +10 -2
  209. data/app/presenters/decidim/admin_log/participatory_space/member_presenter.rb +40 -0
  210. data/app/presenters/decidim/admin_log/static_page_resource_presenter.rb +1 -1
  211. data/app/presenters/decidim/authorization_transfer_presenter.rb +1 -1
  212. data/app/presenters/decidim/breadcrumb_root_menu_item_presenter.rb +3 -3
  213. data/app/presenters/decidim/footer_menu_presenter.rb +2 -4
  214. data/app/presenters/decidim/log/user_presenter.rb +1 -0
  215. data/app/presenters/decidim/log/value_types/access_mode_presenter.rb +45 -0
  216. data/app/presenters/decidim/menu_item_presenter.rb +15 -3
  217. data/app/presenters/decidim/participatory_space/member_presenter.rb +48 -0
  218. data/app/presenters/decidim/resource_locator_presenter.rb +22 -3
  219. data/app/presenters/decidim/stats_presenter.rb +1 -1
  220. data/app/queries/decidim/last_activity.rb +1 -1
  221. data/app/resolvers/decidim/meta_image_url_resolver.rb +7 -0
  222. data/app/serializers/decidim/exporters/participatory_space_serializer.rb +15 -1
  223. data/app/services/decidim/email_notification_generator.rb +2 -0
  224. data/app/services/decidim/notification_generator.rb +4 -0
  225. data/app/services/decidim/notifications_subscriptions_persistor.rb +6 -0
  226. data/app/services/decidim/push_subscription_endpoint_validator.rb +34 -0
  227. data/app/services/decidim/send_push_notification.rb +5 -1
  228. data/app/uploaders/decidim/image_uploader.rb +1 -1
  229. data/app/uploaders/decidim/organization_favicon_uploader.rb +4 -9
  230. data/app/validators/uploader_image_dimensions_validator.rb +13 -11
  231. data/app/views/decidim/delete_user_mailer/delete.html.erb +6 -0
  232. data/app/views/decidim/devise/invitations/edit.html.erb +3 -3
  233. data/app/views/decidim/devise/registrations/new.html.erb +1 -0
  234. data/app/views/decidim/devise/shared/_tos_fields.html.erb +3 -3
  235. data/app/views/decidim/gamification/badges/index.html.erb +1 -1
  236. data/app/views/decidim/homepage/show.html.erb +2 -2
  237. data/app/views/decidim/last_activities/index.html.erb +1 -1
  238. data/app/views/decidim/messaging/conversations/_error_modal.html.erb +11 -19
  239. data/app/views/decidim/messaging/conversations/_new_conversation_button.html.erb +1 -1
  240. data/app/views/decidim/messaging/conversations/_reply.html.erb +1 -1
  241. data/app/views/decidim/messaging/conversations/error.js.erb +12 -7
  242. data/app/views/decidim/messaging/conversations/new.html.erb +1 -1
  243. data/app/views/decidim/messaging/conversations/show.html.erb +2 -2
  244. data/app/views/decidim/newsletters/unsubscribe.html.erb +1 -1
  245. data/app/views/decidim/notification_mailer/event_received.html.erb +3 -3
  246. data/app/views/decidim/notifications_settings/show.html.erb +5 -5
  247. data/app/views/decidim/offline/show.html.erb +1 -1
  248. data/app/views/decidim/pages/_tabbed.html.erb +3 -3
  249. data/app/views/decidim/pages/index.html.erb +1 -1
  250. data/app/views/decidim/participatory_space/members/_member.html.erb +1 -0
  251. data/app/views/decidim/profiles/show.html.erb +1 -1
  252. data/app/views/decidim/searches/_filters.html.erb +1 -1
  253. data/app/views/decidim/shared/_filters.html.erb +5 -5
  254. data/app/views/decidim/shared/_resource_actions.html.erb +4 -4
  255. data/app/views/decidim/shared/_results_per_page.html.erb +1 -1
  256. data/app/views/decidim/shared/filters/_check_boxes_tree.html.erb +2 -2
  257. data/app/views/decidim/shared/filters/_collection.html.erb +1 -1
  258. data/app/views/decidim/shared/filters/_dropdown_label.html.erb +4 -3
  259. data/app/views/decidim/user_activities/index.html.erb +1 -1
  260. data/app/views/devise/mailer/{invite_private_user.html.erb → invite_member.html.erb} +2 -2
  261. data/app/views/devise/mailer/{invite_private_user.text.erb → invite_member.text.erb} +2 -2
  262. data/app/views/layouts/decidim/_head.html.erb +2 -2
  263. data/app/views/layouts/decidim/_wrapper.html.erb +3 -3
  264. data/app/views/layouts/decidim/footer/_focus_mode_main.html.erb +0 -1
  265. data/app/views/layouts/decidim/footer/_main.html.erb +1 -2
  266. data/app/views/layouts/decidim/footer/_main_links.html.erb +56 -28
  267. data/app/views/layouts/decidim/header/_focus_mode_back_button.html.erb +4 -1
  268. data/app/views/layouts/decidim/header/_main.html.erb +17 -8
  269. data/app/views/layouts/decidim/header/_main_language_chooser.html.erb +24 -0
  270. data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +56 -56
  271. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +2 -2
  272. data/app/views/layouts/decidim/header/_main_links_mobile_item_account.html.erb +3 -1
  273. data/app/views/layouts/decidim/header/_main_menu_mobile.html.erb +18 -5
  274. data/app/views/layouts/decidim/header/_main_search.html.erb +3 -3
  275. data/app/views/layouts/decidim/header/_menu.html.erb +5 -5
  276. data/app/views/layouts/decidim/header/_menu_breadcrumb_desktop.html.erb +40 -12
  277. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile.html.erb +33 -0
  278. data/app/views/layouts/decidim/shared/_layout_center.html.erb +1 -1
  279. data/app/views/layouts/decidim/shared/_layout_item.html.erb +1 -1
  280. data/app/views/layouts/decidim/shared/_layout_two_col.html.erb +1 -1
  281. data/config/initializers/decidim_locale_aware_named_route_helper.rb +16 -0
  282. data/config/initializers/devise.rb +6 -0
  283. data/config/initializers/invisible_captcha.rb +1 -0
  284. data/config/locales/ar.yml +6 -38
  285. data/config/locales/bg.yml +3 -39
  286. data/config/locales/ca-IT.yml +91 -58
  287. data/config/locales/ca.yml +91 -58
  288. data/config/locales/cs.yml +54 -58
  289. data/config/locales/de.yml +45 -68
  290. data/config/locales/el.yml +3 -31
  291. data/config/locales/en.yml +88 -55
  292. data/config/locales/es-MX.yml +93 -60
  293. data/config/locales/es-PY.yml +93 -60
  294. data/config/locales/es.yml +95 -62
  295. data/config/locales/eu.yml +124 -92
  296. data/config/locales/fi-plain.yml +93 -55
  297. data/config/locales/fi.yml +96 -58
  298. data/config/locales/fr-CA.yml +70 -52
  299. data/config/locales/fr.yml +71 -54
  300. data/config/locales/ga-IE.yml +3 -1
  301. data/config/locales/gl.yml +3 -31
  302. data/config/locales/hu.yml +7 -40
  303. data/config/locales/id-ID.yml +3 -30
  304. data/config/locales/is-IS.yml +0 -6
  305. data/config/locales/it.yml +15 -33
  306. data/config/locales/ja.yml +105 -71
  307. data/config/locales/lb.yml +3 -33
  308. data/config/locales/lt.yml +4 -37
  309. data/config/locales/lv.yml +3 -30
  310. data/config/locales/nl.yml +3 -33
  311. data/config/locales/no.yml +3 -33
  312. data/config/locales/pl.yml +7 -41
  313. data/config/locales/pt-BR.yml +24 -65
  314. data/config/locales/pt.yml +3 -33
  315. data/config/locales/ro-RO.yml +12 -55
  316. data/config/locales/ru.yml +0 -16
  317. data/config/locales/sk.yml +1417 -32
  318. data/config/locales/sv.yml +44 -62
  319. data/config/locales/tr-TR.yml +20 -37
  320. data/config/locales/uk.yml +0 -9
  321. data/config/locales/zh-CN.yml +3 -30
  322. data/config/locales/zh-TW.yml +4 -35
  323. data/config/routes.rb +140 -76
  324. data/db/data/20251213075429_rename_members_in_action_log.rb +22 -0
  325. data/db/data/20260319145808_rename_send_to_members_in_newsletter.rb +27 -0
  326. data/db/migrate/20200730142511_add_file_upload_settings_to_decidim_organizations.rb +13 -15
  327. data/db/migrate/20250217192438_convert_user_groups_into_users.rb +3 -2
  328. data/db/migrate/20250523104311_move_cta_to_hero_content_block.rb +1 -1
  329. data/db/migrate/20250527122040_move_highlighted_content_banner_settings_to_content_block.rb +1 -1
  330. data/db/migrate/20250819110800_convert_private_exports_id_to_uuid.rb +2 -2
  331. data/db/migrate/20251112132305_remove_legacy_images_from_core_module.rb +27 -0
  332. data/db/migrate/20251205122428_rename_participatory_space_private_users_to_members.rb +9 -0
  333. data/db/migrate/20251216185133_rename_privatable_to_to_participatory_space_in_members.rb +10 -0
  334. data/db/migrate/20260208201405_remove_user_group_core.rb +21 -0
  335. data/db/migrate/20260208201406_remove_user_group_memberships.rb +30 -0
  336. data/db/migrate/20260208201407_remove_user_group_organizations.rb +11 -0
  337. data/db/migrate/20260217152425_remove_enable_participatory_space_filters_from_decidim_organizations.rb +7 -0
  338. data/db/migrate/20260314081619_add_index_on_decidim_members.rb +25 -0
  339. data/decidim-core.gemspec +28 -28
  340. data/lib/decidim/acts_as_tree.rb +12 -12
  341. data/lib/decidim/admin_filters_registry.rb +1 -1
  342. data/lib/decidim/amendable.rb +5 -1
  343. data/lib/decidim/api/functions/category_list.rb +1 -0
  344. data/lib/decidim/api/functions/component_finder_base.rb +1 -1
  345. data/lib/decidim/api/functions/component_list.rb +3 -1
  346. data/lib/decidim/api/functions/component_list_base.rb +1 -0
  347. data/lib/decidim/api/functions/participatory_space_finder_base.rb +1 -1
  348. data/lib/decidim/api/functions/participatory_space_list_base.rb +1 -0
  349. data/lib/decidim/api/functions/user_entity_list.rb +2 -1
  350. data/lib/decidim/api/interfaces/amendable_entity_interface.rb +1 -0
  351. data/lib/decidim/api/interfaces/amendable_interface.rb +1 -0
  352. data/lib/decidim/api/interfaces/attachable_collection_interface.rb +1 -0
  353. data/lib/decidim/api/interfaces/attachable_interface.rb +1 -0
  354. data/lib/decidim/api/interfaces/author_interface.rb +2 -1
  355. data/lib/decidim/api/interfaces/authorable_interface.rb +1 -0
  356. data/lib/decidim/api/interfaces/categories_container_interface.rb +1 -0
  357. data/lib/decidim/api/interfaces/categorizable_interface.rb +1 -0
  358. data/lib/decidim/api/interfaces/coauthorable_interface.rb +1 -0
  359. data/lib/decidim/api/interfaces/component_interface.rb +9 -8
  360. data/lib/decidim/api/interfaces/fingerprint_interface.rb +1 -0
  361. data/lib/decidim/api/interfaces/followable_interface.rb +2 -0
  362. data/lib/decidim/api/interfaces/likeable_interface.rb +2 -2
  363. data/lib/decidim/api/interfaces/localizable_interface.rb +1 -0
  364. data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -0
  365. data/lib/decidim/api/interfaces/referable_interface.rb +1 -0
  366. data/lib/decidim/api/interfaces/scopable_interface.rb +1 -0
  367. data/lib/decidim/api/interfaces/taxonomizable_interface.rb +1 -0
  368. data/lib/decidim/api/interfaces/timestamps_interface.rb +1 -0
  369. data/lib/decidim/api/interfaces/traceable_interface.rb +1 -0
  370. data/lib/decidim/api/types/attachment_collection_type.rb +5 -3
  371. data/lib/decidim/api/types/attachment_type.rb +11 -4
  372. data/lib/decidim/api/types/badge_score_type.rb +28 -0
  373. data/lib/decidim/api/types/moderation_type.rb +25 -0
  374. data/lib/decidim/api/types/participant_details_type.rb +17 -0
  375. data/lib/decidim/api/types/reportable_type.rb +18 -0
  376. data/lib/decidim/api/types/reportable_user_type.rb +17 -0
  377. data/lib/decidim/api/types/static_page_topic_type.rb +16 -0
  378. data/lib/decidim/api/types/static_page_type.rb +22 -0
  379. data/lib/decidim/api/types/taxonomy_type.rb +2 -0
  380. data/lib/decidim/api/types/user_moderation_type.rb +36 -0
  381. data/lib/decidim/api/types/user_type.rb +13 -0
  382. data/lib/decidim/asset_router/pipeline.rb +1 -1
  383. data/lib/decidim/asset_router/storage.rb +9 -9
  384. data/lib/decidim/assets/tailwind/tailwind.config.js.erb +4 -1
  385. data/lib/decidim/attachment_attributes.rb +58 -9
  386. data/lib/decidim/attribute_encryptor.rb +51 -10
  387. data/lib/decidim/attribute_object/model.rb +0 -5
  388. data/lib/decidim/block_registry.rb +1 -1
  389. data/lib/decidim/coauthorable.rb +2 -1
  390. data/lib/decidim/command.rb +2 -1
  391. data/lib/decidim/content_parsers/blob_parser.rb +3 -3
  392. data/lib/decidim/content_processor.rb +1 -1
  393. data/lib/decidim/content_renderers/base_renderer.rb +112 -0
  394. data/lib/decidim/content_renderers/blob_renderer.rb +6 -9
  395. data/lib/decidim/content_renderers/mention_resource_renderer.rb +10 -6
  396. data/lib/decidim/content_renderers/resource_renderer.rb +16 -7
  397. data/lib/decidim/content_renderers/user_renderer.rb +11 -9
  398. data/lib/decidim/core/api.rb +8 -0
  399. data/lib/decidim/core/content_blocks/registry_manager.rb +4 -10
  400. data/lib/decidim/core/engine.rb +10 -34
  401. data/lib/decidim/core/menu.rb +0 -26
  402. data/lib/decidim/core/test/factories.rb +32 -18
  403. data/lib/decidim/core/test/shared_examples/access_mode_restricted_participatory_spaces.rb +175 -0
  404. data/lib/decidim/core/test/shared_examples/access_mode_transparent_participatory_spaces.rb +244 -0
  405. data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +10 -10
  406. data/lib/decidim/core/test/shared_examples/amendable/amendment_event_examples.rb +2 -2
  407. data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +3 -3
  408. data/lib/decidim/core/test/shared_examples/comments_examples.rb +147 -117
  409. data/lib/decidim/core/test/shared_examples/fingerprint_examples.rb +2 -0
  410. data/lib/decidim/core/test/shared_examples/has_members.rb +63 -0
  411. data/lib/decidim/core/test/shared_examples/has_space_in_mcell_examples.rb +2 -1
  412. data/lib/decidim/core/test/shared_examples/manage_share_tokens_examples.rb +1 -1
  413. data/lib/decidim/core/test/shared_examples/map_examples.rb +1 -1
  414. data/lib/decidim/core/test/shared_examples/participant_details_examples.rb +16 -0
  415. data/lib/decidim/core/test/shared_examples/participatory_space_members_page_examples.rb +10 -10
  416. data/lib/decidim/core/test/shared_examples/participatory_space_members_shared_examples.rb +121 -0
  417. data/lib/decidim/core/test/shared_examples/resource_liked_event_examples.rb +3 -3
  418. data/lib/decidim/core/test/shared_examples/resource_locator_presenter_examples.rb +7 -7
  419. data/lib/decidim/core/test/shared_examples/searchable_participatory_space_examples.rb +3 -3
  420. data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +2 -2
  421. data/lib/decidim/core/test/shared_examples/simple_event.rb +1 -0
  422. data/lib/decidim/core/test/shared_examples/translated_event_examples.rb +2 -2
  423. data/lib/decidim/core/test/shared_examples/versions_controller_examples.rb +1 -1
  424. data/lib/decidim/core/test.rb +2 -1
  425. data/lib/decidim/core/version.rb +1 -1
  426. data/lib/decidim/core.rb +222 -335
  427. data/lib/decidim/download_your_data_serializers/{download_your_data_participatory_space_private_user_serializer.rb → download_your_data_member_serializer.rb} +6 -6
  428. data/lib/decidim/download_your_data_serializers.rb +2 -2
  429. data/lib/decidim/engine_router.rb +31 -6
  430. data/lib/decidim/exporters/csv.rb +1 -1
  431. data/lib/decidim/filter_form_builder.rb +1 -1
  432. data/lib/decidim/form_builder.rb +58 -36
  433. data/{app/controllers/concerns → lib}/decidim/form_factory.rb +11 -1
  434. data/lib/decidim/has_settings.rb +2 -2
  435. data/lib/decidim/legacy_form_builder.rb +1 -0
  436. data/lib/decidim/locale_router_detector.rb +41 -0
  437. data/lib/decidim/maintenance/import_models/scope.rb +1 -1
  438. data/lib/decidim/maintenance/import_models.rb +0 -2
  439. data/lib/decidim/maintenance/taxonomy_importer.rb +1 -1
  440. data/lib/decidim/map/autocomplete.rb +4 -3
  441. data/lib/decidim/map/dynamic_map.rb +3 -1
  442. data/lib/decidim/map/frontend.rb +1 -1
  443. data/lib/decidim/map/geocoding.rb +2 -2
  444. data/lib/decidim/map/static_map.rb +10 -22
  445. data/lib/decidim/map/utility.rb +3 -6
  446. data/lib/decidim/map.rb +2 -2
  447. data/lib/decidim/menu.rb +1 -1
  448. data/lib/decidim/menu_item.rb +1 -0
  449. data/lib/decidim/menu_registry.rb +1 -1
  450. data/lib/decidim/moderation_tools.rb +1 -1
  451. data/lib/decidim/newsletter_encryptor.rb +5 -8
  452. data/lib/decidim/organization_settings.rb +2 -2
  453. data/lib/decidim/participable.rb +1 -0
  454. data/lib/decidim/participatory_space/has_members.rb +61 -0
  455. data/lib/decidim/participatory_space_manifest.rb +0 -4
  456. data/lib/decidim/participatory_space_resourceable.rb +10 -1
  457. data/lib/decidim/participatory_space_user.rb +1 -1
  458. data/lib/decidim/routes/locale_redirects.rb +47 -0
  459. data/lib/decidim/routes.rb +7 -0
  460. data/lib/decidim/search_resource_fields_mapper.rb +3 -4
  461. data/lib/decidim/searchable.rb +9 -4
  462. data/lib/decidim/seeds.rb +17 -0
  463. data/lib/decidim/shakapacker/runner.rb +4 -4
  464. data/lib/decidim/shakapacker/shakapacker.yml +28 -3
  465. data/lib/decidim/taxonomizable.rb +1 -1
  466. data/lib/decidim/translatable_attributes.rb +9 -3
  467. data/lib/decidim/view_model.rb +5 -1
  468. data/lib/devise/models/decidim_newsletterable.rb +1 -1
  469. data/lib/tasks/decidim_mailers_tasks.rake +31 -9
  470. data/lib/tasks/decidim_procfile.rake +1 -1
  471. data/lib/tasks/decidim_tasks.rake +0 -1
  472. data/lib/tasks/decidim_taxonomies.rake +1 -1
  473. data/lib/tasks/upgrade/decidim_active_storage_migration_tasks.rake +0 -1
  474. data/lib/tasks/upgrade/decidim_encryption.rake +12 -0
  475. data/lib/tasks/upgrade/decidim_migrate_wysiwyg_content.rake +0 -1
  476. data/lib/tasks/upgrade/decidim_remove_deleted_users_left_data_tasks.rake +1 -1
  477. data/lib/tasks/upgrade/{fix_deleted_private_follows.rake → fix_deleted_members_follows.rake} +5 -5
  478. data/lib/tasks/upgrade/migrations.rake +0 -1
  479. data/lib/tasks/upgrade/user_groups_migration.rake +0 -1
  480. metadata +244 -132
  481. data/app/cells/decidim/content_blocks/global_menu/show.erb +0 -11
  482. data/app/cells/decidim/content_blocks/global_menu_cell.rb +0 -35
  483. data/app/cells/decidim/content_blocks/menu_breadcrumb_last_activity/show.erb +0 -10
  484. data/app/cells/decidim/content_blocks/menu_breadcrumb_last_activity_cell.rb +0 -49
  485. data/app/cells/decidim/participatory_space_dropdown_metadata/links.erb +0 -9
  486. data/app/cells/decidim/participatory_space_dropdown_metadata/metadata.erb +0 -1
  487. data/app/cells/decidim/participatory_space_dropdown_metadata/show.erb +0 -11
  488. data/app/cells/decidim/participatory_space_dropdown_metadata_cell.rb +0 -26
  489. data/app/commands/decidim/gallery_methods.rb +0 -107
  490. data/app/controllers/concerns/decidim/has_members_page.rb +0 -25
  491. data/app/jobs/decidim/newsletters_opt_in_job.rb +0 -11
  492. data/app/jobs/decidim/user_report_job.rb +0 -11
  493. data/app/models/decidim/participatory_space_private_user.rb +0 -51
  494. data/app/packs/src/decidim/controllers/sticky_header/controller.js +0 -116
  495. data/app/packs/src/decidim/controllers/sticky_header/sticky_header.test.js +0 -320
  496. data/app/packs/src/decidim/dropdown_menu.js +0 -18
  497. data/app/packs/src/decidim/identity_selector_dialog.js +0 -33
  498. data/app/packs/src/decidim/vendor/tribute.js +0 -1890
  499. data/app/packs/stylesheets/decidim/_tribute.scss +0 -36
  500. data/app/presenters/decidim/admin_log/participatory_space_private_user_presenter.rb +0 -38
  501. data/app/presenters/decidim/participatory_space_private_user_presenter.rb +0 -46
  502. data/app/views/decidim/participants_account_mailer/removal_notification.html.erb +0 -11
  503. data/app/views/decidim/participatory_space_private_users/_participatory_space_private_user.html.erb +0 -1
  504. data/app/views/layouts/decidim/footer/_main_language_chooser.html.erb +0 -26
  505. data/app/views/layouts/decidim/header/_menu_breadcrumb_items.html.erb +0 -34
  506. data/app/views/layouts/decidim/header/_menu_breadcrumb_main_dropdown_desktop.html.erb +0 -10
  507. data/app/views/layouts/decidim/header/_menu_breadcrumb_main_dropdown_mobile.html.erb +0 -17
  508. data/app/views/layouts/decidim/header/_menu_breadcrumb_main_dropdown_top_left.html.erb +0 -2
  509. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +0 -33
  510. data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +0 -25
  511. data/lib/decidim/core/test/shared_examples/has_private_users.rb +0 -63
  512. data/lib/decidim/core/test/shared_examples/participatory_space_dropdown_metadata_cell_examples.rb +0 -81
  513. data/lib/decidim/has_private_users.rb +0 -57
  514. data/lib/decidim/maintenance/import_models/assembly_type.rb +0 -49
  515. data/lib/decidim/maintenance/import_models/participatory_process_type.rb +0 -21
  516. data/lib/decidim/query_extensions.rb +0 -64
@@ -1,3 +1,5 @@
1
+ @use "stylesheets/decidim/editor_suggestions";
2
+
1
3
  .editor-props {
2
4
  --editor-active-color: #487bda;
3
5
  --editor-disabled-color: #eee;
@@ -5,12 +7,6 @@
5
7
  --editor-bubble-shadow-color: #333;
6
8
  }
7
9
 
8
- .editor-suggestions-props {
9
- --editor-suggestions-border-color: #000;
10
- --editor-suggestions-background-color: #eee;
11
- --editor-suggestions-selected-highlight-color: #ccc;
12
- }
13
-
14
10
  .editor-active {
15
11
  @apply outline outline-4 outline-[var(--editor-active-color)];
16
12
  }
@@ -130,6 +126,16 @@
130
126
  [data-image-resizer-wrapper] {
131
127
  @apply relative inline-block;
132
128
 
129
+ [data-image-resizer-control],
130
+ [data-image-resizer-dimensions] {
131
+ z-index: 1;
132
+ }
133
+
134
+ > a {
135
+ position: relative;
136
+ z-index: 0;
137
+ }
138
+
133
139
  [data-image-resizer-control] {
134
140
  @apply absolute hidden w-2.5 h-2.5 rounded-none border border-solid border-[#000] bg-[#fff] opacity-80;
135
141
  }
@@ -176,30 +182,3 @@
176
182
  }
177
183
  }
178
184
  }
179
-
180
- .editor-suggestions {
181
- @apply editor-suggestions-props border-0 bg-[var(--editor-suggestions-background-color)] max-w-sm drop-shadow-md rounded-md;
182
-
183
- &:hover {
184
- .editor-suggestions-item[data-selected]:not(:hover) {
185
- @apply bg-transparent;
186
- }
187
- }
188
-
189
- .editor-suggestions-item {
190
- @apply block w-full text-left py-[0.375rem] px-1.5 border-0 rounded-none truncate text-sm;
191
-
192
- &:first-child {
193
- @apply rounded-t-md;
194
- }
195
-
196
- &:last-child {
197
- @apply rounded-b-md;
198
- }
199
-
200
- &:hover,
201
- &[data-selected] {
202
- @apply bg-[var(--editor-suggestions-selected-highlight-color)];
203
- }
204
- }
205
- }
@@ -1,5 +1,13 @@
1
- .geocoding-container {
2
- .user-device-location button {
1
+ .geocoding {
2
+ &__locate .geocoding__button {
3
3
  @apply mt-4;
4
+
5
+ &--locating {
6
+ @apply opacity-70 cursor-wait;
7
+ }
8
+
9
+ .geocoding__spinner {
10
+ @apply inline-block w-4 h-4 rounded-full animate-spin border-2 border-l-transparent border-y-transparent border-r-secondary align-middle;
11
+ }
4
12
  }
5
13
  }
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module AdminLog
5
+ module ParticipatorySpace
6
+ # This class holds the logic to present a `Decidim::ParticipatorySpace::MemberPresenter`
7
+ # for the `AdminLog` log.
8
+ #
9
+ # Usage should be automatic and you should not need to call this class
10
+ # directly, but here is an example:
11
+ #
12
+ # action_log = Decidim::ActionLog.last
13
+ # view_helpers # => this comes from the views
14
+ # MemberPresenter.new(action_log, view_helpers).present
15
+ class MemberPresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def diff_fields_mapping
19
+ {
20
+ name: :string,
21
+ email: :string
22
+ }
23
+ end
24
+
25
+ def action_string
26
+ case action
27
+ when "create", "create_via_csv", "delete"
28
+ "decidim.admin_log.member.#{action}"
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def i18n_labels_scope
35
+ "activemodel.attributes.member"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -11,7 +11,7 @@ module Decidim
11
11
  #
12
12
  # Returns an HTML-safe String.
13
13
  def resource_path
14
- @resource_path ||= h.decidim.page_path(resource)
14
+ @resource_path ||= h.decidim.page_path(resource, locale: I18n.locale)
15
15
  end
16
16
  end
17
17
  end
@@ -20,7 +20,7 @@ module Decidim
20
20
  # "Decidim::Bar" => "Bars: 456"
21
21
  # }
22
22
  #
23
- # @return [Hash<String => String>] The translated resource counts with the
23
+ # @return [Hash{String => String}] The translated resource counts with the
24
24
  # resource type as its keys and the translated text with the record count
25
25
  # as its values.
26
26
  def translated_record_counts
@@ -9,16 +9,16 @@ module Decidim
9
9
 
10
10
  def render
11
11
  content_tag :li, class: link_wrapper_classes do
12
- output = [arrow_link(label, url, link_options)]
12
+ output = [root_link(label, url, link_options)]
13
13
  output.push(@view.send(:simple_menu, **@menu_item.submenu).render) if @menu_item.submenu
14
14
 
15
15
  safe_join(output)
16
16
  end
17
17
  end
18
18
 
19
- def arrow_link(text, url, args = {})
19
+ def root_link(text, url, args = {})
20
20
  link_to url, class: args.with_indifferent_access[:class] do
21
- "<span>#{text}</span> #{icon("arrow-right-line")}".html_safe
21
+ "<span>#{text}</span>".html_safe
22
22
  end
23
23
  end
24
24
  end
@@ -3,10 +3,8 @@
3
3
  module Decidim
4
4
  # A presenter to render footer menu
5
5
  class FooterMenuPresenter < MenuPresenter
6
- def render
7
- content_tag(:nav, :role => "navigation", "aria-label" => @options[:label]) do
8
- safe_join([content_tag(:h2, @options[:label], class: "h5 mb-4"), render_menu])
9
- end
6
+ def render_menu_items
7
+ render_menu
10
8
  end
11
9
  end
12
10
  end
@@ -11,6 +11,7 @@ module Decidim
11
11
  # The only requirement for custom renderers is that they should respond to `present`.
12
12
  class UserPresenter
13
13
  include Decidim::SanitizeHelper
14
+
14
15
  # Public: Initializes the presenter.
15
16
  #
16
17
  # user - An instance of Decidim::User
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Log
5
+ module ValueTypes
6
+ # This class presents the given value as an access mode. Check
7
+ # the `DefaultPresenter` for more info on how value
8
+ # presenters work.
9
+ class AccessModePresenter < DefaultPresenter
10
+ # Public: Presents the value as an access mode. If the access mode can
11
+ # be found, it shows its translated label. Otherwise it shows a fallback message.
12
+ #
13
+ # Returns an HTML-safe String.
14
+ def present
15
+ return unless value
16
+ return translated_access_mode if valid_access_mode?
17
+
18
+ I18n.t("not_found", id: value, scope: "decidim.log.value_types.access_mode_presenter")
19
+ end
20
+
21
+ private
22
+
23
+ def valid_access_mode?
24
+ access_modes.has_value?(value.to_i)
25
+ end
26
+
27
+ def translated_access_mode
28
+ I18n.t(access_mode_key, scope: "decidim.log.value_types.access_mode_presenter.access_modes")
29
+ end
30
+
31
+ def access_mode_key
32
+ access_modes.key(value.to_i)
33
+ end
34
+
35
+ def access_modes
36
+ # Access modes are consistent across participatory spaces (assemblies, processes, etc.)
37
+ # open: 0 - fully visible and accessible
38
+ # transparent: 1 - visible but content restricted
39
+ # restricted: 2 - not visible, requires permission
40
+ { open: 0, transparent: 1, restricted: 2 }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -29,9 +29,9 @@ module Decidim
29
29
  delegate :content_tag, :safe_join, :link_to, :active_link_to_class, :is_active_link?, :icon, to: :@view
30
30
 
31
31
  def render
32
- content_tag :li, role: :menuitem, class: link_wrapper_classes do
32
+ content_tag :li, role: wrapper_role, class: link_wrapper_classes do
33
33
  output = if url == "#"
34
- [content_tag(:span, composed_label, class: "sidebar-menu__item-disabled")]
34
+ [content_tag(:span, composed_label, class: "sidebar-menu__item-disabled", role: menuitem_role)]
35
35
  else
36
36
  [link_to(composed_label, url, link_options)]
37
37
  end
@@ -52,7 +52,7 @@ module Decidim
52
52
  { aria: { current: "page" } }
53
53
  else
54
54
  {}
55
- end.merge({ class: link_classes })
55
+ end.merge({ class: link_classes, role: menuitem_role })
56
56
  end
57
57
 
58
58
  def composed_label
@@ -69,6 +69,18 @@ module Decidim
69
69
  [@options.element_wrapper_class, active_class].compact.join(" ")
70
70
  end
71
71
 
72
+ def menuitem_role
73
+ return if @options.role == false
74
+
75
+ @options.role || :menuitem
76
+ end
77
+
78
+ def wrapper_role
79
+ return if menuitem_role.blank?
80
+
81
+ :presentation
82
+ end
83
+
72
84
  def active_class
73
85
  active_link_to_class(
74
86
  url,
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ParticipatorySpace
5
+ #
6
+ # Decorator for participatory space members
7
+ #
8
+ class MemberPresenter < SimpleDelegator
9
+ delegate :profile_url, to: :user, allow_nil: true
10
+
11
+ def name
12
+ user ? user.name : full_name
13
+ end
14
+
15
+ def nickname
16
+ user.nickname if user
17
+ end
18
+
19
+ def avatar_url(variant = nil)
20
+ return user.avatar_url(variant) if user.present?
21
+
22
+ non_user_avatar_path(variant)
23
+ end
24
+
25
+ def non_user_avatar_path(variant = nil)
26
+ return non_user_avatar.default_url(variant) unless non_user_avatar.attached?
27
+
28
+ non_user_avatar.path(variant:)
29
+ end
30
+
31
+ def non_user_avatar
32
+ attached_uploader(:non_user_avatar)
33
+ end
34
+
35
+ def deleted?
36
+ false
37
+ end
38
+
39
+ private
40
+
41
+ def user
42
+ @user ||= if (user = __getobj__.user.presence)
43
+ Decidim::UserPresenter.new(user)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -55,8 +55,9 @@ module Decidim
55
55
  # Returns a String.
56
56
  def show(options = {})
57
57
  options.merge!(options_for_polymorphic)
58
+ locale = options.delete(:locale) || I18n.locale
58
59
 
59
- admin_route_proxy.send("#{member_route_name}_path", target, options)
60
+ normalize_locale_route(admin_route_proxy.send("#{member_route_name}_path", target, options), locale)
60
61
  end
61
62
 
62
63
  # Builds the admin edit path to the resource.
@@ -66,8 +67,9 @@ module Decidim
66
67
  # Returns a String.
67
68
  def edit(options = {})
68
69
  options.merge!(options_for_polymorphic)
70
+ locale = options.delete(:locale) || I18n.locale
69
71
 
70
- admin_route_proxy.send("edit_#{member_route_name}_path", target, options)
72
+ normalize_locale_route(admin_route_proxy.send("edit_#{member_route_name}_path", target, options), locale)
71
73
  end
72
74
 
73
75
  private
@@ -112,8 +114,9 @@ module Decidim
112
114
 
113
115
  def admin_collection_route(route_type, options)
114
116
  options.merge!(options_for_polymorphic)
117
+ locale = options.delete(:locale) || I18n.locale
115
118
 
116
- admin_route_proxy.send("#{collection_route_name}_#{route_type}", options)
119
+ normalize_locale_route(admin_route_proxy.send("#{collection_route_name}_#{route_type}", options), locale)
117
120
  end
118
121
 
119
122
  def manifest_for(record)
@@ -172,5 +175,21 @@ module Decidim
172
175
  def admin_route_proxy
173
176
  @admin_route_proxy ||= EngineRouter.admin_proxy(component || target)
174
177
  end
178
+
179
+ def normalize_locale_route(path, locale)
180
+ path = path.sub(/([?&])locale=[^&]*(?=&|\z)/, "\\1")
181
+ path = path.delete_suffix("?")
182
+ path = path.delete_suffix("&")
183
+ path = path.sub("?&", "?")
184
+
185
+ path = "/#{path}" unless path.start_with?("/")
186
+
187
+ locale_candidates = Regexp.union(I18n.available_locales.map(&:to_s).sort_by(&:length).reverse)
188
+ path = path.sub(%r{\A/#{locale_candidates}(?=/|\z)}, "")
189
+
190
+ path = "/" if path.empty?
191
+
192
+ "/#{locale}#{path}"
193
+ end
175
194
  end
176
195
  end
@@ -15,7 +15,7 @@ module Decidim
15
15
  hash[name][:data][idx] += value
16
16
  end
17
17
  else
18
- hash[name] = stat
18
+ hash[name] = stat.merge(data: stat[:data].dup)
19
19
  end
20
20
  end.values
21
21
  end
@@ -79,7 +79,7 @@ module Decidim
79
79
  Decidim.participatory_space_manifests.map do |manifest|
80
80
  klass = manifest.model_class_name.constantize
81
81
 
82
- condition = if klass.include?(Decidim::HasPrivateUsers)
82
+ condition = if klass.include?(Decidim::ParticipatorySpace::HasMembers)
83
83
  Arel.sql(
84
84
  <<~SQL.squish
85
85
  (
@@ -79,6 +79,9 @@ module Decidim
79
79
  image_url = image_element["src"]
80
80
  next if image_url.blank?
81
81
 
82
+ blob = find_blob_by_id(image_url.split("/").last)
83
+ return blob if blob.present?
84
+
82
85
  blob = find_blob_by_key(image_url.split("/").second_to_last)
83
86
  return blob if blob.present?
84
87
  end
@@ -120,6 +123,10 @@ module Decidim
120
123
  ActiveStorage::Blob.find_signed(blob_key) if blob_key.present?
121
124
  end
122
125
 
126
+ def find_blob_by_id(blob_id)
127
+ ActiveStorage::Blob.find_by(id: blob_id) if blob_id.present?
128
+ end
129
+
123
130
  def blob_from_attached_file(file)
124
131
  return unless file.respond_to?(:attached?) && file.attached?
125
132
 
@@ -27,7 +27,8 @@ module Decidim
27
27
  follows_count: resource.follows_count,
28
28
  short_description: resource.short_description,
29
29
  description: resource.description,
30
- promoted: resource.promoted
30
+ promoted: resource.promoted,
31
+ component_settings:
31
32
  }
32
33
  end
33
34
 
@@ -98,6 +99,19 @@ module Decidim
98
99
  serializer = Decidim::Exporters::ParticipatorySpaceComponentsSerializer.new(resource)
99
100
  serializer.run
100
101
  end
102
+
103
+ def component_settings
104
+ return unless resource.respond_to?(:components)
105
+
106
+ resource.components.map do |component|
107
+ {
108
+ id: component.id,
109
+ name: component.name,
110
+ manifest_name: component.manifest_name,
111
+ settings: component.settings.respond_to?(:as_json) ? component.settings.as_json : component.settings
112
+ }
113
+ end
114
+ end
101
115
  end
102
116
  end
103
117
  end
@@ -44,6 +44,7 @@ module Decidim
44
44
 
45
45
  def send_to_affected_users
46
46
  affected_users.each do |recipient|
47
+ next unless recipient.is_a?(Decidim::User)
47
48
  next unless ["all", "own-only"].include?(recipient.notification_types)
48
49
  next if recipient.deleted? || recipient.blocked?
49
50
 
@@ -53,6 +54,7 @@ module Decidim
53
54
 
54
55
  def send_to_followers
55
56
  followers.each do |recipient|
57
+ next unless recipient.is_a?(Decidim::User)
56
58
  next unless ["all", "followed-only"].include?(recipient.notification_types)
57
59
  next if recipient.deleted? || recipient.blocked?
58
60
 
@@ -37,10 +37,14 @@ module Decidim
37
37
  return unless event_class.types.include?(:notification)
38
38
 
39
39
  followers.each do |recipient|
40
+ next unless recipient.is_a?(Decidim::User)
41
+
40
42
  generate_notification_for(recipient, user_role: :follower) if ["all", "followed-only"].include?(recipient.notification_types)
41
43
  end
42
44
 
43
45
  affected_users.each do |recipient|
46
+ next unless recipient.is_a?(Decidim::User)
47
+
44
48
  generate_notification_for(recipient, user_role: :affected_user) if ["all", "own-only"].include?(recipient.notification_types)
45
49
  end
46
50
  end
@@ -4,6 +4,10 @@ module Decidim
4
4
  # This class manages the creation and deletion of user notifications
5
5
 
6
6
  class NotificationsSubscriptionsPersistor
7
+ include PushSubscriptionEndpointValidator
8
+
9
+ class UnsupportedPushSubscriptionEndpointError < StandardError; end
10
+
7
11
  attr_reader :user
8
12
 
9
13
  def initialize(user)
@@ -11,6 +15,8 @@ module Decidim
11
15
  end
12
16
 
13
17
  def add_subscription(params)
18
+ raise UnsupportedPushSubscriptionEndpointError unless supported_push_subscription_endpoint?(params[:endpoint])
19
+
14
20
  subscriptions = user.notification_settings["subscriptions"] || {}
15
21
  filtered_params = filter_params(params)
16
22
  new_subscription = { filtered_params[:auth] => filtered_params }
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # Shared validation for browser push subscription endpoints.
5
+ module PushSubscriptionEndpointValidator
6
+ private
7
+
8
+ def supported_push_subscription_endpoint?(endpoint)
9
+ return false if endpoint.blank?
10
+
11
+ uri = URI.parse(endpoint)
12
+ return false unless uri.is_a?(URI::HTTPS)
13
+
14
+ host = uri.host&.downcase
15
+ return false if host.blank?
16
+
17
+ allowed_push_subscription_endpoint_patterns.any? { |pattern| pattern.match?(host) }
18
+ rescue URI::InvalidURIError
19
+ false
20
+ end
21
+
22
+ # Override this method to customize the browser push endpoint allowlist.
23
+ def allowed_push_subscription_endpoint_patterns
24
+ [
25
+ /\A(?:.*\.)?push\.services\.mozilla\.com\z/,
26
+ /\A(?:.*\.)?fcm\.googleapis\.com\z/,
27
+ /\A(?:.*\.)?android\.googleapis\.com\z/,
28
+ /\A(?:.*\.)?push\.apple\.com\z/,
29
+ /\A(?:.*\.)?opera\.com\z/,
30
+ /\A(?:.*\.)?notify\.windows\.com\z/
31
+ ]
32
+ end
33
+ end
34
+ end
@@ -10,6 +10,7 @@ module Decidim
10
10
 
11
11
  class SendPushNotification
12
12
  include ActionView::Helpers::UrlHelper
13
+ include PushSubscriptionEndpointValidator
13
14
 
14
15
  # Send the push notification. Returns `nil` if the user did not allowed push notifications
15
16
  # or if the subscription to push notifications does not exist
@@ -24,9 +25,12 @@ module Decidim
24
25
  raise ArgumentError, "Need to provide a title if the notification is a PushNotificationMessage" if notification.is_a?(Decidim::PushNotificationMessage) && title.nil?
25
26
 
26
27
  user = notification.user
28
+ subscriptions = user.notifications_subscriptions.values.select do |subscription|
29
+ supported_push_subscription_endpoint?(subscription["endpoint"])
30
+ end
27
31
 
28
32
  I18n.with_locale(user.locale || user.organization.default_locale) do
29
- user.notifications_subscriptions.values.map do |subscription|
33
+ subscriptions.map do |subscription|
30
34
  payload = build_payload(message_params(notification, title), subscription)
31
35
  # Capture webpush exceptions in order to avoid this call to be repeated by the background job runner
32
36
  # Webpush::Error class is the parent class of all defined errors
@@ -8,7 +8,7 @@ module Decidim
8
8
  end
9
9
 
10
10
  def content_type_allowlist
11
- extension_allowlist.map { |ext| MiniMime.lookup_by_extension(ext).content_type }.uniq
11
+ extension_allowlist.filter_map { |ext| MiniMime.lookup_by_extension(ext)&.content_type }.uniq
12
12
  end
13
13
 
14
14
  # Fetches info about different variants, their processors and dimensions
@@ -7,7 +7,8 @@ module Decidim
7
7
  huge: 512,
8
8
  big: 192,
9
9
  medium: 180,
10
- small: 32
10
+ small: 32,
11
+ favicon: 256
11
12
  }.freeze
12
13
 
13
14
  set_variants do
@@ -16,17 +17,11 @@ module Decidim
16
17
  resize_and_pad: [value, value],
17
18
  format: :png
18
19
  }
19
- end.merge(
20
- favicon: {
21
- resize_and_pad: [256, 256],
22
- define: "icon:auto-resize=16,24,32,48,64,72,96,128,256",
23
- format: :ico
24
- }
25
- )
20
+ end
26
21
  end
27
22
 
28
23
  def extension_allowlist
29
- %w(png jpg jpeg webp ico)
24
+ %w(png jpg jpeg webp)
30
25
  end
31
26
  end
32
27
  end
@@ -3,7 +3,7 @@
3
3
  # This validator checks when the files to be uploaded are images and the attached uploader's
4
4
  # has enabled dimensions validation that the image dimensions are below the
5
5
  # limit defined by the uploader
6
- require "mini_magick"
6
+ require "vips"
7
7
 
8
8
  class UploaderImageDimensionsValidator < ActiveModel::Validations::FileContentTypeValidator
9
9
  def validate_each(record, attribute, value)
@@ -28,14 +28,16 @@ class UploaderImageDimensionsValidator < ActiveModel::Validations::FileContentTy
28
28
  return unless uploader.validable_dimensions
29
29
  return if (image = extract_image(file)).blank?
30
30
 
31
- record.errors.add attribute, I18n.t("decidim.errors.files.file_resolution_too_large") if image.dimensions.any? { |dimension| dimension > uploader.max_image_height_or_width }
32
- rescue MiniMagick::Error
33
- # The error may happen because of many reasons but most commonly the image
34
- # exceeds the default maximum dimensions set for ImageMagick when the
35
- # `identify` command fails to identify the image.
31
+ # A simple check to avoid DoS with maliciously crafted images, or just to
32
+ # avoid reckless users that upload images with too many pixels.
36
33
  #
37
- # To relax ImageMagick default limits, please refer to:
38
- # https://imagemagick.org/script/security-policy.php
34
+ # See https://hackerone.com/reports/390
35
+ max_dimension = [image.width, image.height].max
36
+ record.errors.add attribute, I18n.t("decidim.errors.files.file_resolution_too_large") if max_dimension > uploader.max_image_height_or_width
37
+ rescue Vips::Error
38
+ # The error may happen because of many reasons but most commonly the image
39
+ # exceeds the default maximum dimensions set for libvips when the image
40
+ # fails to load.
39
41
  #
40
42
  # Note that the error can also happen because of other reasons than only
41
43
  # the image dimensions being too large. But as we do not really know the
@@ -47,11 +49,11 @@ class UploaderImageDimensionsValidator < ActiveModel::Validations::FileContentTy
47
49
  return unless file.try(:content_type).to_s.start_with?("image")
48
50
 
49
51
  if uploaded_file?(file)
50
- MiniMagick::Image.new(file.path, File.extname(file.original_filename))
52
+ Vips::Image.new_from_file(file.path)
51
53
  elsif file.is_a?(ActiveStorage::Attached) && file.blob.persisted?
52
- MiniMagick::Image.read(file.blob.download, File.extname(file.blob.filename.to_s))
54
+ Vips::Image.new_from_buffer(file.blob.download, "")
53
55
  end
54
- rescue ActiveStorage::FileNotFoundError, MiniMagick::Invalid
56
+ rescue ActiveStorage::FileNotFoundError, Vips::Error
55
57
  # Although the blob is persisted, the file is not available to download and analyze
56
58
  # after committing the record
57
59
  nil
@@ -0,0 +1,6 @@
1
+ <p class="email-greeting"><%= t(".hello", username: @user_name) %></p>
2
+
3
+ <p class="email-instructions"><%= t ".body_1" %></p>
4
+ <p class="email-instructions"><%= t ".body_2" %></p>
5
+
6
+ <p class="email-closing"><%= t(".greetings_html", organization_name: organization_name(@organization), organization_url: decidim.root_url(host: @organization.host)) %></p>