decidim-core 0.30.2 → 0.31.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.
- checksums.yaml +4 -4
- data/README.md +0 -4
- data/app/cells/decidim/activity_cell.rb +0 -3
- data/app/cells/decidim/address/show.erb +1 -0
- data/app/cells/decidim/address_cell.rb +7 -1
- data/app/cells/decidim/amendable/amend_button_card/show.erb +2 -2
- data/app/cells/decidim/author/likes.erb +4 -0
- data/app/cells/decidim/author/show.erb +1 -3
- data/app/cells/decidim/author_cell.rb +5 -28
- data/app/cells/decidim/badge_cell.rb +1 -7
- data/app/cells/decidim/card_cell.rb +2 -12
- data/app/cells/decidim/card_metadata_cell.rb +4 -4
- data/app/cells/decidim/coauthorships_cell.rb +1 -1
- data/app/cells/decidim/collapsible_authors/show.erb +1 -1
- data/app/cells/decidim/content_blocks/hero_cell.rb +23 -4
- data/app/cells/decidim/content_blocks/hero_settings_form/show.erb +16 -2
- data/app/cells/decidim/content_blocks/highlighted_content_banner/show.erb +6 -6
- data/app/cells/decidim/content_blocks/highlighted_content_banner_cell.rb +28 -1
- data/app/cells/decidim/content_blocks/highlighted_content_banner_settings_form/show.erb +27 -0
- data/app/cells/decidim/content_blocks/highlighted_content_banner_settings_form_cell.rb +13 -0
- data/app/cells/decidim/content_blocks/highlighted_elements_for_component_settings_form/show.erb +9 -2
- data/app/cells/decidim/content_blocks/html_settings_form/show.erb +3 -1
- data/app/cells/decidim/content_blocks/last_activity_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/last_activity_settings_form/show.erb +3 -1
- data/app/cells/decidim/content_blocks/menu_breadcrumb_last_activity_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_extra_data/recent_users.erb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_hero/show.erb +0 -5
- data/app/cells/decidim/content_blocks/participatory_space_hero_cell.rb +1 -13
- data/app/cells/decidim/content_blocks/participatory_space_hero_settings_form/show.erb +10 -3
- data/app/cells/decidim/content_blocks/participatory_space_last_activity/content.erb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_last_activity/recent_avatars.erb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_last_activity_cell.rb +3 -3
- data/app/cells/decidim/content_blocks/participatory_space_main_data/title.erb +17 -5
- data/app/cells/decidim/content_blocks/participatory_space_main_data_cell.rb +4 -0
- data/app/cells/decidim/content_blocks/stats/show.erb +2 -3
- data/app/cells/decidim/data_consent/modal.erb +1 -1
- data/app/cells/decidim/like_block/show.erb +3 -0
- data/app/cells/decidim/{endorsement_block_cell.rb → like_block_cell.rb} +2 -2
- data/app/cells/decidim/like_buttons/button_content.erb +2 -0
- data/app/cells/decidim/like_buttons/show.erb +17 -0
- data/app/cells/decidim/{endorsement_buttons → like_buttons}/verification_modal.erb +3 -3
- data/app/cells/decidim/like_buttons_cell.rb +77 -0
- data/app/cells/decidim/likers_list/empty.erb +3 -0
- data/app/cells/decidim/{endorsers_list → likers_list}/full.erb +3 -3
- data/app/cells/decidim/likers_list/show.erb +20 -0
- data/app/cells/decidim/likers_list_cell.rb +64 -0
- data/app/cells/decidim/nav_links/show.erb +1 -1
- data/app/cells/decidim/notification_actions/buttons_cell.rb +1 -1
- data/app/cells/decidim/onboarding_action_message_cell.rb +1 -1
- data/app/cells/decidim/participatory_space_dropdown_metadata/metadata.erb +0 -8
- data/app/cells/decidim/participatory_space_dropdown_metadata_cell.rb +0 -8
- data/app/cells/decidim/profile/details.erb +1 -1
- data/app/cells/decidim/profile/tabs.erb +1 -1
- data/app/cells/decidim/profile_actions/show.erb +0 -1
- data/app/cells/decidim/profile_actions_cell.rb +5 -91
- data/app/cells/decidim/profile_cell.rb +4 -39
- data/app/cells/decidim/report_button/content.erb +2 -0
- data/app/cells/decidim/report_button/flag_modal.erb +1 -1
- data/app/cells/decidim/report_button_cell.rb +1 -1
- data/app/cells/decidim/report_user_button/flag_modal.erb +1 -1
- data/app/cells/decidim/resource_history_cell.rb +2 -1
- data/app/cells/decidim/resource_types_filter/show.erb +1 -1
- data/app/cells/decidim/share_button_cell.rb +1 -1
- data/app/cells/decidim/share_text_widget/modal.erb +16 -0
- data/app/cells/decidim/share_text_widget/show.erb +2 -0
- data/app/cells/decidim/share_text_widget_cell.rb +27 -0
- data/app/cells/decidim/share_widget/modal.erb +19 -8
- data/app/cells/decidim/share_widget/qr_code_modal.erb +26 -0
- data/app/cells/decidim/share_widget/show.erb +1 -0
- data/app/cells/decidim/share_widget_cell.rb +10 -6
- data/app/cells/decidim/statistic/show.erb +23 -5
- data/app/cells/decidim/statistic_cell.rb +28 -3
- data/app/cells/decidim/tab_panels/show.erb +1 -1
- data/app/cells/decidim/user_profile/show.erb +1 -1
- data/app/cells/decidim/user_profile_cell.rb +1 -9
- data/app/cells/decidim/version_author_cell.rb +0 -7
- data/app/commands/decidim/amendable/accept.rb +1 -3
- data/app/commands/decidim/amendable/create_draft.rb +2 -3
- data/app/commands/decidim/amendable/update_draft.rb +2 -3
- data/app/commands/decidim/create_ephemeral_user.rb +7 -1
- data/app/commands/decidim/create_omniauth_registration.rb +8 -7
- data/app/commands/decidim/create_report.rb +9 -1
- data/app/commands/decidim/destroy_account.rb +0 -5
- data/app/commands/decidim/like_resource.rb +52 -0
- data/app/commands/decidim/messaging/reply_to_conversation.rb +2 -21
- data/app/commands/decidim/messaging/start_conversation.rb +2 -21
- data/app/commands/decidim/unlike_resource.rb +34 -0
- data/app/commands/decidim/update_account.rb +1 -1
- data/app/controllers/concerns/decidim/ephemeral_session_checker.rb +6 -2
- data/app/controllers/concerns/decidim/user_profile.rb +0 -1
- data/app/controllers/decidim/account_controller.rb +1 -1
- data/app/controllers/decidim/amendments_controller.rb +4 -5
- data/app/controllers/decidim/application_controller.rb +2 -1
- data/app/controllers/decidim/devise/confirmations_controller.rb +0 -5
- data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -1
- data/app/controllers/decidim/devise/passwords_controller.rb +1 -1
- data/app/controllers/decidim/devise/registrations_controller.rb +1 -1
- data/app/controllers/decidim/doorkeeper/authorizations_controller.rb +19 -1
- data/app/controllers/decidim/download_your_data_controller.rb +2 -2
- data/app/controllers/decidim/likes_controller.rb +57 -0
- data/app/controllers/decidim/links_controller.rb +1 -0
- data/app/controllers/decidim/messaging/conversations_controller.rb +1 -8
- data/app/controllers/decidim/newsletters_controller.rb +1 -1
- data/app/controllers/decidim/notifications_settings_controller.rb +2 -2
- data/app/controllers/decidim/pages_controller.rb +0 -1
- data/app/controllers/decidim/profiles_controller.rb +2 -57
- data/app/controllers/decidim/qr_controller.rb +73 -0
- data/app/controllers/decidim/resource_autocomplete_controller.rb +56 -0
- data/app/controllers/decidim/user_activities_controller.rb +0 -1
- data/app/events/decidim/resource_liked_event.rb +36 -0
- data/app/forms/decidim/amendable/create_form.rb +0 -1
- data/app/forms/decidim/amendable/edit_form.rb +0 -1
- data/app/forms/decidim/amendable/form.rb +4 -4
- data/app/forms/decidim/ephemeral_user_form.rb +1 -0
- data/app/forms/decidim/notifications_settings_form.rb +1 -1
- data/app/forms/decidim/omniauth_registration_form.rb +1 -1
- data/app/forms/decidim/upload_validation_form.rb +9 -1
- data/app/helpers/decidim/amendments_helper.rb +1 -2
- data/app/helpers/decidim/animations_helper.rb +14 -0
- data/app/helpers/decidim/decidim_form_helper.rb +17 -6
- data/app/helpers/decidim/layout_helper.rb +1 -15
- data/app/helpers/decidim/likeable_helper.rb +46 -0
- data/app/helpers/decidim/messaging/conversation_helper.rb +1 -1
- data/app/helpers/decidim/omniauth_helper.rb +1 -1
- data/app/helpers/decidim/qr_code_helper.rb +37 -0
- data/app/helpers/decidim/rich_text_editor_helper.rb +0 -1
- data/app/helpers/decidim/sanitize_helper.rb +5 -5
- data/app/helpers/decidim/social_share_button_helper.rb +11 -10
- data/app/jobs/decidim/delete_inactive_participants_job.rb +41 -0
- data/app/jobs/decidim/process_inactive_participant_job.rb +48 -0
- data/app/mailers/decidim/participants_account_mailer.rb +38 -0
- data/app/mailers/decidim/user_group_mailer.rb +28 -0
- data/app/models/decidim/action_log.rb +3 -5
- data/app/models/decidim/amendment.rb +1 -1
- data/app/models/decidim/attachment_collection.rb +2 -0
- data/app/models/decidim/authorization_transfer.rb +0 -1
- data/app/models/decidim/coauthorship.rb +1 -1
- data/app/models/decidim/component.rb +4 -1
- data/app/models/decidim/{endorsement.rb → like.rb} +4 -4
- data/app/models/decidim/messaging/conversation.rb +3 -5
- data/app/models/decidim/messaging/message.rb +3 -15
- data/app/models/decidim/omniauth_provider.rb +1 -1
- data/app/models/decidim/organization.rb +3 -8
- data/app/models/decidim/participatory_space_role_config/{valuator.rb → evaluator.rb} +1 -1
- data/app/models/decidim/reminder_record.rb +1 -1
- data/app/models/decidim/searchable_resource.rb +16 -0
- data/app/models/decidim/taxonomy.rb +6 -1
- data/app/models/decidim/user.rb +32 -15
- data/app/models/decidim/user_base_entity.rb +2 -2
- data/app/packs/entrypoints/decidim_conference_diploma.scss +2 -2
- data/app/packs/entrypoints/decidim_core.scss +2 -2
- data/app/packs/entrypoints/decidim_editor.js +0 -4
- data/app/packs/entrypoints/decidim_map.scss +2 -2
- data/app/packs/entrypoints/decidim_overrides.scss +1 -1
- data/app/packs/entrypoints/decidim_qr.js +1 -0
- data/app/packs/images/decidim/remixicon.symbol.svg +3 -3
- data/app/packs/src/decidim/a11y.js +1 -120
- data/app/packs/src/decidim/append_redirect_url_to_modals.js +1 -1
- data/app/packs/src/decidim/attachments/file_or_link_tabs.js +1 -1
- data/app/packs/src/decidim/confirm.js +27 -20
- data/app/packs/src/decidim/controllers/.keep +0 -0
- data/app/packs/src/decidim/controllers/accordion/controller.js +100 -0
- data/app/packs/src/decidim/controllers/account_form/account_form.test.js +423 -0
- data/app/packs/src/decidim/controllers/account_form/controller.js +69 -0
- data/app/packs/src/decidim/{input_character_counter.js → controllers/character_counter/controller.js} +90 -106
- data/app/packs/src/decidim/controllers/clipboard_copy/clipboard_copy.test.js +305 -0
- data/app/packs/src/decidim/controllers/clipboard_copy/controller.js +403 -0
- data/app/packs/src/decidim/controllers/date_picker/controller.js +12 -0
- data/app/packs/src/decidim/controllers/delete_account_form/controller.js +45 -0
- data/app/packs/src/decidim/controllers/delete_account_form/delete_account_form.test.js +216 -0
- data/app/packs/src/decidim/controllers/dropdown/controller.js +125 -0
- data/app/packs/src/decidim/controllers/editor/controller.js +8 -0
- data/app/packs/src/decidim/controllers/emoji/controller.js +26 -0
- data/app/packs/src/decidim/{input_emoji.js → controllers/emoji/emoji.js} +1 -15
- data/app/packs/src/decidim/{form_filter.js → controllers/form_filter/controller.js} +68 -51
- data/app/packs/src/decidim/controllers/form_filter/form_filter.test.js +648 -0
- data/app/packs/src/decidim/controllers/form_validator/controller.js +23 -0
- data/app/packs/src/decidim/controllers/form_validator/form_validator.js +789 -0
- data/app/packs/src/decidim/controllers/form_validator/form_validator.test.js +685 -0
- data/app/packs/src/decidim/controllers/impersonation_warning/controller.js +137 -0
- data/app/packs/src/decidim/controllers/impersonation_warning/impersonation.test.js +382 -0
- data/app/packs/src/decidim/controllers/input_tags/controller.js +28 -0
- data/app/packs/src/decidim/controllers/mention/controller.js +243 -0
- data/app/packs/src/decidim/controllers/mention/input_mentions.test.js +562 -0
- data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +215 -0
- data/app/packs/src/decidim/controllers/multiple_mentions/input_multiple_mentions.test.js +645 -0
- data/app/packs/src/decidim/controllers/notification_action/controller.js +210 -0
- data/app/packs/src/decidim/controllers/notification_action/notification_action.test.js +388 -0
- data/app/packs/src/decidim/controllers/password_toggler/controller.js +232 -0
- data/app/packs/src/decidim/controllers/password_toggler/password_toggler.test.js +229 -0
- data/app/packs/src/decidim/controllers/report_form/controller.js +184 -0
- data/app/packs/src/decidim/controllers/report_form/report_form_manager.test.js +511 -0
- data/app/packs/src/decidim/controllers/scroll_to_last/controller.js +9 -0
- data/app/packs/src/decidim/controllers/scroll_to_last/scroll_to_last_child.test.js +225 -0
- data/app/packs/src/decidim/controllers/sticky_buttons/controller.js +55 -0
- data/app/packs/src/decidim/controllers/sticky_buttons/sticky_buttons.test.js +334 -0
- data/app/packs/src/decidim/controllers/sticky_header/controller.js +116 -0
- data/app/packs/src/decidim/controllers/sticky_header/sticky_header.test.js +320 -0
- data/app/packs/src/decidim/controllers/toggle/controller.js +111 -0
- data/app/packs/src/decidim/controllers/toggle/toggle.test.js +260 -0
- data/app/packs/src/decidim/controllers/tooltip/controller.js +287 -0
- data/app/packs/src/decidim/controllers/tooltip/tooltip.test.js +380 -0
- data/app/packs/src/decidim/controllers/user_registration_form/controller.js +160 -0
- data/app/packs/src/decidim/controllers/user_registration_form/omniauth_registration_form.test.js +441 -0
- data/app/packs/src/decidim/controllers/user_registration_form/user_registration_form.test.js +386 -0
- data/app/packs/src/decidim/data_consent/index.js +1 -1
- data/app/packs/src/decidim/datalist_select.js +1 -1
- data/app/packs/src/decidim/datepicker/form_datepicker.js +1 -1
- data/app/packs/src/decidim/datepicker/generate_datepicker.js +2 -2
- data/app/packs/src/decidim/datepicker/generate_timepicker.js +2 -2
- data/app/packs/src/decidim/direct_uploads/upload_field.js +3 -5
- data/app/packs/src/decidim/direct_uploads/upload_modal.js +1 -1
- data/app/packs/src/decidim/dropdown_menu.js +1 -1
- data/app/packs/src/decidim/editor/common/input_dialog.js +1 -1
- data/app/packs/src/decidim/editor/common/suggestion.js +10 -2
- data/app/packs/src/decidim/editor/common/upload_dialog.js +1 -1
- data/app/packs/src/decidim/editor/extensions/decidim_kit/index.js +7 -7
- data/app/packs/src/decidim/editor/extensions/emoji/index.js +2 -3
- data/app/packs/src/decidim/editor/extensions/image/index.js +1 -1
- data/app/packs/src/decidim/editor/extensions/image/node_view.js +1 -1
- data/app/packs/src/decidim/editor/extensions/link/bubble_menu.js +1 -1
- data/app/packs/src/decidim/editor/extensions/link/index.js +1 -1
- data/app/packs/src/decidim/editor/extensions/mention/index.js +1 -2
- data/app/packs/src/decidim/editor/extensions/mention_resource/index.js +69 -0
- data/app/packs/src/decidim/editor/extensions/video_embed/index.js +1 -1
- data/app/packs/src/decidim/editor/index.js +6 -5
- data/app/packs/src/decidim/editor/test/extensions/decidim_kit.test.js +1 -2
- data/app/packs/src/decidim/editor/test/extensions/emoji.test.js +17 -0
- data/app/packs/src/decidim/editor/test/extensions/heading.test.js +2 -2
- data/app/packs/src/decidim/editor/toolbar.js +1 -1
- data/app/packs/src/decidim/form_attachments.js +1 -1
- data/app/packs/src/decidim/geocoding/attach_input.js +3 -0
- data/app/packs/src/decidim/geocoding/provider/here.js +1 -1
- data/app/packs/src/decidim/geocoding/provider/photon.js +1 -1
- data/app/packs/src/decidim/geocoding/reverse_geocoding.js +2 -2
- data/app/packs/src/decidim/geocoding.js +2 -2
- data/app/packs/src/decidim/index.js +127 -95
- data/app/packs/src/decidim/map/integration/forms.js +1 -1
- data/app/packs/src/decidim/map/legacy.js +1 -1
- data/app/packs/src/decidim/map/provider/default.js +1 -1
- data/app/packs/src/decidim/map/provider/here.js +1 -1
- data/app/packs/src/decidim/map.js +1 -1
- data/app/packs/src/decidim/{external_link.js → refactor/implementation/external_link.js} +1 -1
- data/app/packs/src/decidim/{onboarding_pending_action.js → refactor/integration/onboarding_pending_action.js} +4 -4
- data/app/packs/src/decidim/{check_boxes_tree.js → refactor/moved/check_boxes_tree.js} +24 -9
- data/app/packs/src/decidim/{i18n.test.js → refactor/moved/i18n.test.js} +1 -1
- data/app/packs/src/decidim/{icon.js → refactor/moved/icon.js} +2 -0
- data/app/packs/src/decidim/refactor/support/stimulus.js +39 -0
- data/app/packs/src/decidim/refactor/test/configuration.test.js +360 -0
- data/app/packs/src/decidim/{external_domain_warning.test.js → refactor/test/external_domain_warning.test.js} +1 -1
- data/app/packs/src/decidim/{external_link.test.js → refactor/test/external_link.test.js} +1 -1
- data/app/packs/src/decidim/results_listing.js +2 -2
- data/app/packs/src/decidim/security/selfxss_warning.js +1 -1
- data/app/packs/src/decidim/session_timeouter.js +1 -1
- data/app/packs/src/decidim/sw/push-permissions.js +1 -1
- data/app/packs/src/decidim/utilities/dom.js +1 -1
- data/app/packs/stylesheets/decidim/_accordion.scss +37 -3
- data/app/packs/stylesheets/decidim/_author.scss +0 -21
- data/app/packs/stylesheets/decidim/_buttons.scss +4 -0
- data/app/packs/stylesheets/decidim/_callout.scss +1 -1
- data/app/packs/stylesheets/decidim/_cards.scss +1 -1
- data/app/packs/stylesheets/decidim/_content_blocks.scss +1 -1
- data/app/packs/stylesheets/decidim/_cookies.scss +1 -1
- data/app/packs/stylesheets/decidim/_datepicker.scss +0 -4
- data/app/packs/stylesheets/decidim/_dropdown.scss +56 -19
- data/app/packs/stylesheets/decidim/_forms.scss +6 -6
- data/app/packs/stylesheets/decidim/_header.scss +17 -1
- data/app/packs/stylesheets/decidim/_labels.scss +1 -1
- data/app/packs/stylesheets/decidim/_layout.scss +12 -2
- data/app/packs/stylesheets/decidim/{_endorsers_list.scss → _likes_list.scss} +1 -1
- data/app/packs/stylesheets/decidim/_modal.scss +5 -1
- data/app/packs/stylesheets/decidim/_modal_share.scss +83 -9
- data/app/packs/stylesheets/decidim/_modal_update.scss +1 -5
- data/app/packs/stylesheets/decidim/_participatory_spaces.scss +1 -1
- data/app/packs/stylesheets/decidim/_rich_text.scss +13 -0
- data/app/packs/stylesheets/decidim/_statistics.scss +35 -3
- data/app/packs/stylesheets/decidim/_tooltip.scss +1 -1
- data/app/packs/stylesheets/decidim/_typography.scss +6 -0
- data/app/packs/stylesheets/decidim/_wizard_steps.scss +1 -1
- data/app/packs/stylesheets/decidim/application.scss +76 -76
- data/app/packs/stylesheets/decidim/editor.scss +11 -12
- data/app/packs/stylesheets/decidim/legacy/email.scss +6 -5
- data/app/packs/stylesheets/decidim/map.scss +5 -3
- data/app/packs/stylesheets/decidim/qr.scss +65 -0
- data/app/permissions/decidim/default_permissions.rb +16 -0
- data/app/permissions/decidim/permissions.rb +5 -26
- data/app/presenters/decidim/admin_log/base_user_presenter.rb +2 -2
- data/app/presenters/decidim/admin_log/organization_presenter.rb +0 -15
- data/app/presenters/decidim/home_stats_presenter.rb +4 -54
- data/app/presenters/decidim/log/base_presenter.rb +1 -1
- data/app/presenters/decidim/menu_item_presenter.rb +10 -3
- data/app/presenters/decidim/official_author_presenter.rb +0 -4
- data/app/presenters/decidim/participatory_space_private_user_presenter.rb +0 -4
- data/app/presenters/decidim/resource_locator_presenter.rb +15 -4
- data/app/presenters/decidim/resource_presenter.rb +8 -8
- data/app/presenters/decidim/stats_presenter.rb +39 -60
- data/app/presenters/decidim/taxonomy_presenter.rb +5 -1
- data/app/presenters/decidim/user_presenter.rb +0 -6
- data/app/queries/decidim/authorized_users.rb +26 -0
- data/app/queries/decidim/inactive_users_query.rb +31 -0
- data/app/queries/decidim/public_activities.rb +2 -2
- data/app/queries/decidim/stats_followers_count.rb +43 -0
- data/app/queries/decidim/stats_participants_count.rb +102 -0
- data/app/serializers/decidim/exporters/open_data_user_serializer.rb +1 -5
- data/app/serializers/decidim/exporters/participatory_space_serializer.rb +0 -1
- data/app/services/decidim/action_logger.rb +2 -1
- data/app/services/decidim/base_diff_renderer.rb +0 -17
- data/app/services/decidim/onboarding_manager.rb +4 -0
- data/app/services/decidim/open_data_exporter.rb +19 -11
- data/app/services/decidim/send_push_notification.rb +4 -3
- data/app/services/decidim/traceability.rb +1 -1
- data/app/validators/etiquette_validator.rb +7 -0
- data/app/validators/translated_etiquette_validator.rb +1 -0
- data/app/views/decidim/account/_old_password_field.html.erb +1 -1
- data/app/views/decidim/account/_password_fields.html.erb +1 -1
- data/app/views/decidim/account/delete.html.erb +1 -1
- data/app/views/decidim/account/show.html.erb +3 -3
- data/app/views/decidim/amendments/edit_draft.html.erb +0 -4
- data/app/views/decidim/amendments/new.html.erb +0 -4
- data/app/views/decidim/amendments/preview_draft.html.erb +1 -1
- data/app/views/decidim/application/_accordion_section.html.erb +1 -1
- data/app/views/decidim/application/_collection.html.erb +1 -1
- data/app/views/decidim/application/_radio_accordion.html.erb +30 -0
- data/app/views/decidim/devise/omniauth_registrations/new.html.erb +1 -1
- data/app/views/decidim/devise/omniauth_registrations/new_tos_fields.html.erb +1 -1
- data/app/views/decidim/devise/registrations/new.html.erb +1 -1
- data/app/views/decidim/devise/shared/_links.html.erb +1 -1
- data/app/views/decidim/doorkeeper/authorizations/new.html.erb +39 -8
- data/app/views/decidim/errors/internal_server_error.html.erb +1 -0
- data/app/views/decidim/likes/update_buttons_and_counters.js.erb +21 -0
- data/app/views/decidim/messaging/conversations/_add_conversation_users.html.erb +2 -2
- data/app/views/decidim/messaging/conversations/_reply_form.html.erb +1 -1
- data/app/views/decidim/messaging/conversations/_start.html.erb +1 -1
- data/app/views/decidim/messaging/conversations/create.js.erb +0 -1
- data/app/views/decidim/messaging/conversations/new.html.erb +1 -1
- data/app/views/decidim/messaging/conversations/show.html.erb +1 -1
- data/app/views/decidim/notifications_settings/show.html.erb +3 -3
- data/app/views/decidim/pages/_tabbed.html.erb +1 -1
- data/app/views/decidim/pages/index.html.erb +1 -1
- data/app/views/decidim/participants_account_mailer/inactivity_notification.html.erb +13 -0
- data/app/views/decidim/participants_account_mailer/removal_notification.html.erb +11 -0
- data/app/views/decidim/profiles/show.html.erb +1 -1
- data/app/views/decidim/qr/show.html.erb +28 -0
- data/app/views/decidim/searches/_filters.html.erb +18 -2
- data/app/views/decidim/searches/index.html.erb +5 -2
- data/app/views/decidim/shared/_confirm_modal.html.erb +5 -3
- data/app/views/decidim/shared/_filters.html.erb +2 -2
- data/app/views/decidim/shared/_orders.html.erb +1 -1
- data/app/views/decidim/shared/_resource_actions.html.erb +8 -5
- data/app/views/decidim/user_activities/index.html.erb +1 -1
- data/app/views/decidim/user_group_mailer/notify_deprecation_to_member.html.erb +15 -0
- data/app/views/decidim/user_group_mailer/notify_deprecation_to_owner.html.erb +22 -0
- data/app/views/layouts/decidim/_head.html.erb +15 -12
- data/app/views/layouts/decidim/_impersonation_warning.html.erb +1 -1
- data/app/views/layouts/decidim/_js_configuration.html.erb +17 -19
- data/app/views/layouts/decidim/_meta_tags_context.html.erb +3 -0
- data/app/views/layouts/decidim/_offline_banner.html.erb +1 -1
- data/app/views/layouts/decidim/_wrapper.html.erb +8 -6
- data/app/views/layouts/decidim/footer/_focus_mode_main.html.erb +7 -0
- data/app/views/layouts/decidim/footer/_main_language_chooser.html.erb +1 -1
- data/app/views/layouts/decidim/header/_close_ephemeral_session.html.erb +12 -2
- data/app/views/layouts/decidim/header/_focus_mode_back_button.html.erb +24 -0
- data/app/views/layouts/decidim/header/_main.html.erb +28 -22
- data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +1 -1
- data/app/views/layouts/decidim/header/_main_links_dropdown.html.erb +18 -18
- data/app/views/layouts/decidim/header/_main_links_mobile_item_account.html.erb +1 -1
- data/app/views/layouts/decidim/header/_main_menu_mobile.html.erb +1 -1
- data/app/views/layouts/decidim/header/_menu_breadcrumb_desktop.html.erb +1 -1
- data/app/views/layouts/decidim/header/_menu_breadcrumb_items.html.erb +1 -1
- data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
- data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +1 -1
- data/app/views/layouts/decidim/mailer.html.erb +2 -2
- data/app/views/layouts/decidim/newsletter_base.html.erb +2 -2
- data/app/views/layouts/decidim/shared/_layout_two_col.html.erb +3 -1
- data/app/views/layouts/decidim/shared/_layout_user_profile.html.erb +7 -3
- data/config/assets.rb +3 -3
- data/config/initializers/devise.rb +8 -0
- data/config/initializers/omniauth.rb +1 -1
- data/config/locales/ar.yml +0 -209
- data/config/locales/bg.yml +0 -238
- data/config/locales/bs-BA.yml +0 -30
- data/config/locales/ca-IT.yml +112 -286
- data/config/locales/ca.yml +112 -286
- data/config/locales/cs.yml +85 -279
- data/config/locales/de.yml +112 -285
- data/config/locales/el.yml +0 -224
- data/config/locales/en.yml +112 -286
- data/config/locales/eo.yml +0 -2
- data/config/locales/es-MX.yml +112 -286
- data/config/locales/es-PY.yml +112 -286
- data/config/locales/es.yml +112 -286
- data/config/locales/eu.yml +125 -299
- data/config/locales/fi-plain.yml +112 -286
- data/config/locales/fi.yml +112 -286
- data/config/locales/fr-CA.yml +85 -275
- data/config/locales/fr.yml +85 -275
- data/config/locales/ga-IE.yml +0 -28
- data/config/locales/gl.yml +0 -140
- data/config/locales/hu.yml +0 -237
- data/config/locales/id-ID.yml +0 -136
- data/config/locales/is-IS.yml +0 -14
- data/config/locales/it.yml +1 -234
- data/config/locales/ja.yml +108 -282
- data/config/locales/lb.yml +0 -189
- data/config/locales/lt.yml +0 -240
- data/config/locales/lv.yml +0 -186
- data/config/locales/nl.yml +0 -189
- data/config/locales/no.yml +0 -189
- data/config/locales/pl.yml +0 -244
- data/config/locales/pt-BR.yml +0 -213
- data/config/locales/pt.yml +0 -189
- data/config/locales/ro-RO.yml +54 -218
- data/config/locales/ru.yml +0 -62
- data/config/locales/sk.yml +0 -164
- data/config/locales/sr-CS.yml +0 -31
- data/config/locales/sv.yml +80 -263
- data/config/locales/tr-TR.yml +0 -189
- data/config/locales/uk.yml +0 -23
- data/config/locales/zh-CN.yml +0 -180
- data/config/locales/zh-TW.yml +0 -229
- data/config/routes.rb +3 -36
- data/db/migrate/20250217192438_convert_user_groups_into_users.rb +38 -0
- data/db/migrate/20250221085317_add_user_type_to_action_logs.rb +12 -0
- data/db/migrate/20250221090905_create_decidim_api_jwt_denylist.rb +11 -0
- data/db/migrate/20250221091348_add_api_key_to_decidim_api_user.rb +7 -0
- data/db/migrate/20250327143657_remove_metrics_content_blocks.rb +9 -0
- data/db/migrate/20250401153444_enable_pkce.rb +8 -0
- data/db/migrate/20250416130426_rename_doorkeeper_scopes.rb +25 -0
- data/db/migrate/20250515105231_rename_endorsements_to_likes.rb +62 -0
- data/db/migrate/20250523104311_move_cta_to_hero_content_block.rb +34 -0
- data/db/migrate/20250527122040_move_highlighted_content_banner_settings_to_content_block.rb +69 -0
- data/db/migrate/20250603103953_add_refresh_tokens_enabled_to_doorkeeper_applications.rb +7 -0
- data/db/migrate/20250609073104_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb +10 -0
- data/db/migrate/20250613122148_remove_hashtag_table_from_core.rb +7 -0
- data/decidim-core.gemspec +4 -4
- data/{app/helpers → lib}/decidim/action_authorization_helper.rb +5 -4
- data/lib/decidim/amendable.rb +4 -4
- data/lib/decidim/api/functions/user_entity_finder.rb +1 -1
- data/lib/decidim/api/input_filters/component_input_filter.rb +3 -3
- data/lib/decidim/api/input_filters/user_entity_input_filter.rb +0 -21
- data/lib/decidim/api/input_sorts/has_likeable_input_sort.rb +15 -0
- data/lib/decidim/api/interfaces/attachable_collection_interface.rb +13 -0
- data/lib/decidim/api/interfaces/author_interface.rb +1 -2
- data/lib/decidim/api/interfaces/authorable_interface.rb +2 -2
- data/lib/decidim/api/interfaces/coauthorable_interface.rb +1 -1
- data/lib/decidim/api/interfaces/component_interface.rb +6 -0
- data/lib/decidim/api/interfaces/followable_interface.rb +13 -0
- data/lib/decidim/api/interfaces/likeable_interface.rb +19 -0
- data/lib/decidim/api/interfaces/localizable_interface.rb +18 -0
- data/lib/decidim/api/interfaces/participatory_space_interface.rb +6 -8
- data/lib/decidim/api/interfaces/referable_interface.rb +13 -0
- data/lib/decidim/api/interfaces/timestamps_interface.rb +0 -1
- data/lib/decidim/api/interfaces/traceable_interface.rb +1 -1
- data/lib/decidim/api/types/attachment_collection_type.rb +14 -0
- data/lib/decidim/api/types/organization_type.rb +3 -1
- data/lib/decidim/api/types/session_type.rb +0 -5
- data/lib/decidim/api/types/statistic_type.rb +29 -3
- data/lib/decidim/api/types/trace_version_type.rb +1 -1
- data/lib/decidim/api/types/user_type.rb +1 -2
- data/lib/decidim/api_response_helper.rb +12 -0
- data/lib/decidim/asset_router/storage.rb +2 -4
- data/lib/decidim/assets/tailwind/instance.rb +1 -1
- data/lib/decidim/assets/tailwind/tailwind.config.js.erb +4 -3
- data/lib/decidim/attribute_encryptor.rb +1 -1
- data/lib/decidim/attributes/time_with_zone.rb +1 -1
- data/lib/decidim/authorable.rb +5 -34
- data/lib/decidim/coauthorable.rb +13 -31
- data/lib/decidim/content_parsers/blob_parser.rb +1 -1
- data/lib/decidim/content_parsers/mention_resource_parser.rb +23 -0
- data/lib/decidim/content_parsers/tag_parser.rb +2 -2
- data/lib/decidim/content_parsers/user_parser.rb +2 -2
- data/lib/decidim/content_parsers.rb +14 -2
- data/lib/decidim/content_processor.rb +1 -1
- data/lib/decidim/content_renderers/mention_resource_renderer.rb +77 -0
- data/lib/decidim/content_renderers.rb +1 -2
- data/lib/decidim/core/api.rb +7 -8
- data/lib/decidim/core/content_blocks/registry_manager.rb +19 -5
- data/lib/decidim/core/engine.rb +72 -104
- data/lib/decidim/core/menu.rb +0 -7
- data/lib/decidim/core/seeds.rb +11 -39
- data/lib/decidim/core/test/factories.rb +22 -121
- data/lib/decidim/core/test/shared_examples/authorable.rb +0 -17
- data/lib/decidim/core/test/shared_examples/coauthorable.rb +5 -48
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +7 -56
- data/lib/decidim/core/test/shared_examples/likeable.rb +22 -0
- data/lib/decidim/core/test/shared_examples/manage_share_tokens_examples.rb +16 -3
- data/lib/decidim/core/test/shared_examples/map_examples.rb +1 -1
- data/lib/decidim/core/test/shared_examples/oauth_application_examples.rb +137 -0
- data/lib/decidim/core/test/shared_examples/participatory_space_dropdown_metadata_cell_examples.rb +0 -18
- data/lib/decidim/core/test/shared_examples/permissions.rb +0 -46
- data/lib/decidim/core/test/shared_examples/reports_examples.rb +2 -2
- data/lib/decidim/core/test/shared_examples/{resource_endorsed_event_examples.rb → resource_liked_event_examples.rb} +7 -7
- data/lib/decidim/core/test/shared_examples/resource_search_examples.rb +0 -14
- data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +8 -34
- data/lib/decidim/core/test/shared_examples/social_share_examples.rb +79 -0
- data/lib/decidim/core/test/shared_examples/system_like_resource_examples.rb +192 -0
- data/lib/decidim/core/test/shared_examples/uncommentable_component_examples.rb +2 -2
- data/lib/decidim/core/test/shared_examples/{with_endorsable_permissions_examples.rb → with_likeable_permissions_examples.rb} +22 -22
- data/lib/decidim/core/test.rb +6 -9
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/core.rb +237 -88
- data/lib/decidim/deprecations.rb +1 -1
- data/lib/decidim/download_your_data_serializers.rb +6 -3
- data/lib/decidim/env.rb +3 -1
- data/lib/decidim/events.rb +0 -1
- data/lib/decidim/form_builder.rb +26 -43
- data/lib/decidim/gamification/badge_scorer.rb +0 -2
- data/lib/decidim/gamification.rb +1 -1
- data/lib/decidim/has_conversations.rb +0 -44
- data/lib/decidim/has_workflows.rb +42 -0
- data/lib/decidim/icon_registry.rb +4 -4
- data/lib/decidim/legacy_form_builder.rb +17 -2
- data/lib/decidim/likeable.rb +24 -0
- data/lib/decidim/manifest_registry.rb +1 -1
- data/lib/decidim/map/provider/dynamic_map/here.rb +0 -19
- data/lib/decidim/map/provider/static_map/here.rb +2 -10
- data/lib/decidim/menu.rb +1 -1
- data/lib/decidim/moderation_tools.rb +1 -1
- data/lib/decidim/mutation_registry.rb +17 -0
- data/lib/decidim/newsletter_encryptor.rb +1 -1
- data/lib/decidim/oauth/token_generator.rb +35 -0
- data/lib/decidim/oauth.rb +7 -0
- data/lib/decidim/organization_settings.rb +2 -10
- data/lib/decidim/paddable.rb +1 -1
- data/lib/decidim/participatory_space_resourceable.rb +2 -2
- data/lib/decidim/participatory_space_user.rb +1 -1
- data/lib/decidim/pdf_signature_example.rb +108 -0
- data/lib/decidim/query.rb +1 -1
- data/lib/decidim/query_extensions.rb +0 -33
- data/lib/decidim/reportable.rb +1 -1
- data/lib/decidim/searchable.rb +4 -2
- data/lib/decidim/seeds.rb +8 -0
- data/lib/decidim/settings_manifest.rb +3 -2
- data/lib/decidim/{webpacker → shakapacker}/configuration.rb +4 -4
- data/lib/decidim/{webpacker → shakapacker}/runner.rb +3 -3
- data/lib/decidim/shakapacker/shakapacker.rb +8 -0
- data/lib/decidim/{webpacker → shakapacker}/shakapacker.yml +3 -1
- data/lib/decidim/{webpacker → shakapacker}/webpack/custom.js +1 -1
- data/lib/decidim/shakapacker.rb +34 -0
- data/lib/decidim/social_share_service_manifest.rb +7 -12
- data/lib/decidim/stats_registry.rb +15 -3
- data/{app/helpers → lib}/decidim/tooltip_helper.rb +3 -3
- data/lib/decidim/url_option_resolver.rb +1 -1
- data/lib/decidim/view_model.rb +0 -3
- data/lib/decidim/webpacker.rb +11 -20
- data/lib/decidim/workflow_registry.rb +61 -0
- data/lib/tasks/decidim_participants_tasks.rake +22 -0
- data/lib/tasks/{decidim_webpacker_tasks.rake → decidim_shakapacker_tasks.rake} +35 -33
- data/lib/tasks/decidim_tasks.rake +4 -1
- data/lib/tasks/decidim_taxonomies.rake +0 -34
- data/lib/tasks/upgrade/clean.rake +1 -1
- data/lib/tasks/upgrade/decidim_action_log_valuation_assignment.rake +13 -0
- data/lib/tasks/upgrade/decidim_change_valuator_tasks.rake +12 -0
- data/lib/tasks/upgrade/{decidim_deduplicate_endorsements.rake → decidim_deduplicate_likes.rake} +10 -10
- data/lib/tasks/upgrade/decidim_migrate_wysiwyg_content.rake +2 -2
- data/lib/tasks/upgrade/decidim_paper_trail_valuation_assignment.rake +13 -0
- data/lib/tasks/upgrade/user_groups_migration.rake +155 -0
- metadata +204 -256
- data/app/cells/decidim/author/endorsements.erb +0 -4
- data/app/cells/decidim/author/profile_minicard.erb +0 -30
- data/app/cells/decidim/content_blocks/organization_metrics_cell.rb +0 -21
- data/app/cells/decidim/content_blocks/participatory_space_metrics/content.erb +0 -18
- data/app/cells/decidim/content_blocks/participatory_space_metrics_cell.rb +0 -43
- data/app/cells/decidim/endorsement_block/show.erb +0 -3
- data/app/cells/decidim/endorsement_buttons/button_content.erb +0 -2
- data/app/cells/decidim/endorsement_buttons/select_identity_button.erb +0 -7
- data/app/cells/decidim/endorsement_buttons/show.erb +0 -17
- data/app/cells/decidim/endorsement_buttons_cell.rb +0 -87
- data/app/cells/decidim/endorsers_list/empty.erb +0 -3
- data/app/cells/decidim/endorsers_list/show.erb +0 -20
- data/app/cells/decidim/endorsers_list_cell.rb +0 -64
- data/app/cells/decidim/group_admins/show.erb +0 -34
- data/app/cells/decidim/group_admins_cell.rb +0 -8
- data/app/cells/decidim/group_members/show.erb +0 -46
- data/app/cells/decidim/group_members_cell.rb +0 -8
- data/app/cells/decidim/groups/show.erb +0 -14
- data/app/cells/decidim/groups_cell.rb +0 -22
- data/app/cells/decidim/members/show.erb +0 -12
- data/app/cells/decidim/members_cell.rb +0 -34
- data/app/cells/decidim/profile_actions/dropdown_actions.erb +0 -21
- data/app/cells/decidim/represent_user_group/show.erb +0 -11
- data/app/cells/decidim/represent_user_group_cell.rb +0 -46
- data/app/cells/decidim/user_conversation/conversation_header.erb +0 -14
- data/app/cells/decidim/user_conversation/messages.erb +0 -1
- data/app/cells/decidim/user_conversation/new.erb +0 -6
- data/app/cells/decidim/user_conversation/reply.erb +0 -9
- data/app/cells/decidim/user_conversation/show.erb +0 -19
- data/app/cells/decidim/user_conversation/start.erb +0 -12
- data/app/cells/decidim/user_conversation_cell.rb +0 -62
- data/app/cells/decidim/user_conversations/new_conversation_button.erb +0 -20
- data/app/cells/decidim/user_conversations/show.erb +0 -13
- data/app/cells/decidim/user_conversations_cell.rb +0 -28
- data/app/cells/decidim/user_group_pending_invitations_list/show.erb +0 -28
- data/app/cells/decidim/user_group_pending_invitations_list_cell.rb +0 -26
- data/app/cells/decidim/user_group_pending_requests_list/show.erb +0 -27
- data/app/cells/decidim/user_group_pending_requests_list_cell.rb +0 -26
- data/app/commands/decidim/accept_group_invitation.rb +0 -43
- data/app/commands/decidim/accept_user_group_join_request.rb +0 -53
- data/app/commands/decidim/create_user_group.rb +0 -73
- data/app/commands/decidim/demote_membership.rb +0 -57
- data/app/commands/decidim/endorse_resource.rb +0 -66
- data/app/commands/decidim/invite_user_to_group.rb +0 -63
- data/app/commands/decidim/join_user_group.rb +0 -57
- data/app/commands/decidim/leave_user_group.rb +0 -47
- data/app/commands/decidim/promote_membership.rb +0 -55
- data/app/commands/decidim/reject_group_invitation.rb +0 -42
- data/app/commands/decidim/reject_user_group_join_request.rb +0 -53
- data/app/commands/decidim/remove_user_from_group.rb +0 -53
- data/app/commands/decidim/unendorse_resource.rb +0 -39
- data/app/commands/decidim/update_user_group.rb +0 -72
- data/app/controllers/concerns/decidim/user_groups.rb +0 -23
- data/app/controllers/decidim/endorsements_controller.rb +0 -71
- data/app/controllers/decidim/group_email_confirmations_controller.rb +0 -32
- data/app/controllers/decidim/group_invites_controller.rb +0 -104
- data/app/controllers/decidim/group_members_controller.rb +0 -75
- data/app/controllers/decidim/groups_controller.rb +0 -99
- data/app/controllers/decidim/own_user_groups_controller.rb +0 -15
- data/app/controllers/decidim/user_conversations_controller.rb +0 -133
- data/app/controllers/decidim/user_group_join_requests_controller.rb +0 -72
- data/app/events/decidim/demoted_membership_event.rb +0 -28
- data/app/events/decidim/invited_to_group_event.rb +0 -62
- data/app/events/decidim/join_request_accepted_event.rb +0 -28
- data/app/events/decidim/join_request_created_event.rb +0 -28
- data/app/events/decidim/join_request_rejected_event.rb +0 -28
- data/app/events/decidim/promoted_to_admin_event.rb +0 -28
- data/app/events/decidim/removed_from_group_event.rb +0 -28
- data/app/events/decidim/resource_endorsed_event.rb +0 -36
- data/app/events/decidim/user_group_admin_event.rb +0 -37
- data/app/events/decidim/user_group_created_event.rb +0 -6
- data/app/events/decidim/user_group_updated_event.rb +0 -6
- data/app/forms/decidim/invite_user_to_group_form.rb +0 -29
- data/app/forms/decidim/user_group_form.rb +0 -85
- data/app/helpers/decidim/cta_button_helper.rb +0 -31
- data/app/helpers/decidim/endorsable_helper.rb +0 -84
- data/app/helpers/decidim/twitter_search_helper.rb +0 -14
- data/app/helpers/decidim/user_group_helper.rb +0 -22
- data/app/jobs/decidim/metric_job.rb +0 -15
- data/app/models/decidim/metric.rb +0 -25
- data/app/models/decidim/user_group.rb +0 -217
- data/app/models/decidim/user_group_membership.rb +0 -17
- data/app/packs/entrypoints/decidim_widget.js +0 -1
- data/app/packs/src/decidim/abide_form_validator_fixer.js +0 -44
- data/app/packs/src/decidim/account_form.js +0 -96
- data/app/packs/src/decidim/append_elements.js +0 -6
- data/app/packs/src/decidim/back_to_list.js +0 -26
- data/app/packs/src/decidim/change_report_form_behavior.js +0 -45
- data/app/packs/src/decidim/clipboard.js +0 -128
- data/app/packs/src/decidim/editor/extensions/hashtag/index.js +0 -66
- data/app/packs/src/decidim/editor/extensions/heading/index.js +0 -33
- data/app/packs/src/decidim/editor/test/extensions/hashtag.test.js +0 -105
- data/app/packs/src/decidim/form_filter.component_for_testing.js +0 -7
- data/app/packs/src/decidim/form_validator.js +0 -56
- data/app/packs/src/decidim/gallery.js +0 -5
- data/app/packs/src/decidim/impersonation.js +0 -23
- data/app/packs/src/decidim/input_autojump.js +0 -38
- data/app/packs/src/decidim/input_hashtags.js +0 -91
- data/app/packs/src/decidim/input_mentions.js +0 -143
- data/app/packs/src/decidim/input_multiple_mentions.js +0 -115
- data/app/packs/src/decidim/input_tags.js +0 -16
- data/app/packs/src/decidim/notifications_actions.js +0 -40
- data/app/packs/src/decidim/password_toggler.js +0 -104
- data/app/packs/src/decidim/remote_tooltips.js +0 -38
- data/app/packs/src/decidim/responsive_horizontal_tabs.js +0 -12
- data/app/packs/src/decidim/scroll_to_last_child.js +0 -14
- data/app/packs/src/decidim/slug_form.js +0 -9
- data/app/packs/src/decidim/sticky_footer.js +0 -48
- data/app/packs/src/decidim/sticky_header.js +0 -62
- data/app/packs/src/decidim/toggle.js +0 -37
- data/app/packs/src/decidim/tooltips.js +0 -152
- data/app/packs/src/decidim/user_registrations.js +0 -65
- data/app/packs/src/decidim/vizzs/areachart.js +0 -251
- data/app/packs/src/decidim/vizzs/index.js +0 -82
- data/app/packs/src/decidim/widget.js +0 -14
- data/app/packs/stylesheets/decidim/_hashtags.scss +0 -5
- data/app/packs/stylesheets/decidim/_metrics.scss +0 -70
- data/app/packs/stylesheets/decidim/_modal_identities.scss +0 -23
- data/app/presenters/decidim/admin_log/user_group_presenter.rb +0 -31
- data/app/presenters/decidim/hashtag_presenter.rb +0 -37
- data/app/presenters/decidim/metric_charts_presenter.rb +0 -92
- data/app/presenters/decidim/metric_object_presenter.rb +0 -32
- data/app/presenters/decidim/user_group_presenter.rb +0 -38
- data/app/queries/decidim/metric_manage.rb +0 -81
- data/app/queries/decidim/metric_measure.rb +0 -36
- data/app/queries/decidim/metrics/blocked_users_metric_manage.rb +0 -26
- data/app/queries/decidim/metrics/followers_metric_manage.rb +0 -65
- data/app/queries/decidim/metrics/participants_metric_manage.rb +0 -79
- data/app/queries/decidim/metrics/reported_users_metric_manage.rb +0 -26
- data/app/queries/decidim/metrics/user_reports_metric_manage.rb +0 -26
- data/app/queries/decidim/metrics/users_metric_manage.rb +0 -26
- data/app/queries/decidim/user_groups/accepted_memberships.rb +0 -36
- data/app/queries/decidim/user_groups/accepted_user_groups.rb +0 -38
- data/app/queries/decidim/user_groups/accepted_users.rb +0 -36
- data/app/queries/decidim/user_groups/admin_memberships.rb +0 -37
- data/app/queries/decidim/user_groups/invited_memberships.rb +0 -36
- data/app/queries/decidim/user_groups/manageable_user_groups.rb +0 -39
- data/app/queries/decidim/user_groups/member_memberships.rb +0 -37
- data/app/resolvers/decidim/core/metric_resolver.rb +0 -71
- data/app/resolvers/decidim/hashtags_resolver.rb +0 -15
- data/app/serializers/decidim/exporters/open_data_metric_serializer.rb +0 -22
- data/app/serializers/decidim/exporters/open_data_user_group_serializer.rb +0 -38
- data/app/views/decidim/endorsements/_identity.html.erb +0 -4
- data/app/views/decidim/endorsements/identities.html.erb +0 -18
- data/app/views/decidim/endorsements/update_buttons_and_counters.js.erb +0 -25
- data/app/views/decidim/group_invites/index.html.erb +0 -26
- data/app/views/decidim/groups/_form.html.erb +0 -17
- data/app/views/decidim/groups/edit.html.erb +0 -19
- data/app/views/decidim/groups/new.html.erb +0 -23
- data/app/views/decidim/messaging/conversation_mailer/comanagers_new_conversation.html.erb +0 -17
- data/app/views/decidim/messaging/conversation_mailer/comanagers_new_message.html.erb +0 -17
- data/app/views/decidim/messaging/conversation_mailer/new_group_conversation.html.erb +0 -17
- data/app/views/decidim/messaging/conversation_mailer/new_group_message.html.erb +0 -17
- data/app/views/decidim/own_user_groups/index.html.erb +0 -10
- data/app/views/decidim/shared/_extended_navigation_bar.html.erb +0 -50
- data/app/views/decidim/user_conversations/index.html.erb +0 -4
- data/app/views/decidim/user_conversations/show.html.erb +0 -4
- data/app/views/decidim/user_conversations/update.js.erb +0 -8
- data/config/initializers/new_framework_defaults_7_0.rb +0 -145
- data/lib/decidim/api/input_filters/has_hastaggable_input_filter.rb +0 -15
- data/lib/decidim/api/input_sorts/has_endorsable_input_sort.rb +0 -15
- data/lib/decidim/api/interfaces/endorsable_interface.rb +0 -19
- data/lib/decidim/api/types/hashtag_type.rb +0 -13
- data/lib/decidim/api/types/metric_history_type.rb +0 -20
- data/lib/decidim/api/types/metric_type.rb +0 -13
- data/lib/decidim/api/types/user_group_type.rb +0 -55
- data/lib/decidim/content_parsers/hashtag_parser.rb +0 -66
- data/lib/decidim/content_parsers/user_group_parser.rb +0 -33
- data/lib/decidim/content_renderers/hashtag_renderer.rb +0 -78
- data/lib/decidim/content_renderers/user_group_renderer.rb +0 -31
- data/lib/decidim/core/test/shared_examples/amendable_interface_examples.rb +0 -14
- data/lib/decidim/core/test/shared_examples/amendable_proposals_interface_examples.rb +0 -50
- data/lib/decidim/core/test/shared_examples/assembly_announcements_examples.rb +0 -38
- data/lib/decidim/core/test/shared_examples/attachable_interface_examples.rb +0 -16
- data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +0 -59
- data/lib/decidim/core/test/shared_examples/categories_container_examples.rb +0 -22
- data/lib/decidim/core/test/shared_examples/categorizable_interface_examples.rb +0 -27
- data/lib/decidim/core/test/shared_examples/coauthorable_interface_examples.rb +0 -91
- data/lib/decidim/core/test/shared_examples/endorsable.rb +0 -69
- data/lib/decidim/core/test/shared_examples/endorsable_interface_examples.rb +0 -22
- data/lib/decidim/core/test/shared_examples/fingerprintable_interface_examples.rb +0 -17
- data/lib/decidim/core/test/shared_examples/input_filter_examples.rb +0 -118
- data/lib/decidim/core/test/shared_examples/input_sort_examples.rb +0 -126
- data/lib/decidim/core/test/shared_examples/metric_manage_shared_context.rb +0 -9
- data/lib/decidim/core/test/shared_examples/participatory_space_resourcable_interface_examples.rb +0 -43
- data/lib/decidim/core/test/shared_examples/process_announcements_examples.rb +0 -37
- data/lib/decidim/core/test/shared_examples/scopable_interface_examples.rb +0 -19
- data/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb +0 -298
- data/lib/decidim/core/test/shared_examples/taxonomizable_interface_examples.rb +0 -20
- data/lib/decidim/core/test/shared_examples/timestamps_interface_examples.rb +0 -21
- data/lib/decidim/core/test/shared_examples/traceable_interface_examples.rb +0 -47
- data/lib/decidim/download_your_data_serializers/download_your_data_user_group_serializer.rb +0 -21
- data/lib/decidim/endorsable.rb +0 -29
- data/lib/decidim/events/user_group_event.rb +0 -43
- data/lib/decidim/hashtag.rb +0 -13
- data/lib/decidim/metric_manifest.rb +0 -35
- data/lib/decidim/metric_operation.rb +0 -58
- data/lib/decidim/metric_operation_manifest.rb +0 -21
- data/lib/decidim/metric_registry.rb +0 -109
- data/lib/gem_overrides/shakapacker/runner.rb +0 -9
- data/lib/tasks/decidim_metrics_tasks.rake +0 -89
- /data/app/cells/decidim/{endorsement_buttons/disabled_endorsements.erb → like_buttons/disabled_likes.erb} +0 -0
- /data/app/packs/src/decidim/{configuration.js → refactor/implementation/configuration.js} +0 -0
- /data/app/packs/src/decidim/{external_domain_warning.js → refactor/implementation/external_domain_warning.js} +0 -0
- /data/app/packs/src/decidim/{autocomplete.js → refactor/moved/autocomplete.js} +0 -0
- /data/app/packs/src/decidim/{delayed.js → refactor/moved/delayed.js} +0 -0
- /data/app/packs/src/decidim/{focus_guard.js → refactor/moved/focus_guard.js} +0 -0
- /data/app/packs/src/decidim/{history.js → refactor/moved/history.js} +0 -0
- /data/app/packs/src/decidim/{i18n.js → refactor/moved/i18n.js} +0 -0
- /data/lib/decidim/core/test/shared_examples/{endorsements_controller_shared_context.rb → likes_controller_shared_context.rb} +0 -0
- /data/{app/helpers → lib}/decidim/resource_helper.rb +0 -0
- /data/lib/decidim/{webpacker → shakapacker}/esbuild.config.js +0 -0
- /data/lib/decidim/{webpacker → shakapacker}/postcss.config.js +0 -0
- /data/lib/decidim/{webpacker → shakapacker}/tsconfig.json +0 -0
- /data/lib/decidim/{webpacker → shakapacker}/webpack/.modernizrrc +0 -0
- /data/lib/decidim/{webpacker → shakapacker}/webpack/webpack.config.js +0 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
/* eslint max-lines: ["error", 450] */
|
2
|
+
import { Controller } from "@hotwired/stimulus"
|
3
|
+
import select from "select";
|
4
|
+
|
5
|
+
/**
|
6
|
+
* ClipboardCopy - A class that provides clipboard copy functionality for buttons.
|
7
|
+
*
|
8
|
+
* This class eliminates jQuery dependencies and provides a clean, standalone
|
9
|
+
* implementation for copying text to the clipboard with visual feedback and
|
10
|
+
* accessibility features.
|
11
|
+
*
|
12
|
+
* Usage:
|
13
|
+
* 1. Create a button with data attributes:
|
14
|
+
* <button class="button"
|
15
|
+
* data-controller="clipboard-copy"
|
16
|
+
* data-clipboard-copy="#target-input-element"
|
17
|
+
* data-clipboard-copy-label="Copied!"
|
18
|
+
* data-clipboard-copy-message="The text was successfully copied to clipboard."
|
19
|
+
* aria-label="Copy the text to clipboard">
|
20
|
+
* Copy to clipboard
|
21
|
+
* </button>
|
22
|
+
*
|
23
|
+
* 2. Ensure the target element exists:
|
24
|
+
* <input id="target-input-element" type="text" value="This text will be copied.">
|
25
|
+
*
|
26
|
+
* Data attributes:
|
27
|
+
* - data-clipboard-copy: CSS selector for the target element containing text to copy
|
28
|
+
* - data-clipboard-content: Optional custom text to copy (overrides target element content)
|
29
|
+
* - data-clipboard-copy-label: Text to display in button after successful copy
|
30
|
+
* - data-clipboard-copy-message: Message announced to screen readers after copy
|
31
|
+
*/
|
32
|
+
export default class extends Controller {
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Initialize the clipboard copy controller and set up initial state.
|
36
|
+
*
|
37
|
+
* This method is automatically called by Stimulus when the controller is connected
|
38
|
+
* to the DOM. It extracts configuration from data attributes, initializes state
|
39
|
+
* management properties, and binds event listeners.
|
40
|
+
*
|
41
|
+
* Sets up the following properties from data attributes:
|
42
|
+
* - targetSelector: CSS selector for the element containing text to copy
|
43
|
+
* - customContent: Optional custom text content to copy instead of target element
|
44
|
+
* - copyLabel: Text to temporarily display after successful copy
|
45
|
+
* - copyMessage: Accessibility message for screen readers
|
46
|
+
*
|
47
|
+
* Initializes state tracking properties:
|
48
|
+
* - originalLabel: Stores the original button text for restoration
|
49
|
+
* - labelTimeout: Manages the timeout for restoring original button text
|
50
|
+
* - messageElement: DOM element for screen reader announcements
|
51
|
+
*
|
52
|
+
* @returns {void}
|
53
|
+
* @public
|
54
|
+
*/
|
55
|
+
connect() {
|
56
|
+
this.targetSelector = this.element.dataset.clipboardCopy;
|
57
|
+
this.customContent = this.element.dataset.clipboardContent;
|
58
|
+
this.copyLabel = this.element.dataset.clipboardCopyLabel;
|
59
|
+
this.copyMessage = this.element.dataset.clipboardCopyMessage;
|
60
|
+
|
61
|
+
this.originalLabel = null;
|
62
|
+
this.labelTimeout = null;
|
63
|
+
this.messageElement = null;
|
64
|
+
|
65
|
+
this.bindEvents();
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Clean up resources and remove event listeners when controller is disconnected.
|
70
|
+
*
|
71
|
+
* This method is automatically called by Stimulus when the controller is
|
72
|
+
* disconnected from the DOM. It performs cleanup operations to prevent
|
73
|
+
* memory leaks and ensures proper resource management.
|
74
|
+
*
|
75
|
+
* Cleanup operations include:
|
76
|
+
* - Clearing any pending timeout for label restoration
|
77
|
+
* - Removing screen reader announcement elements from DOM
|
78
|
+
* - Removing click event listener from the trigger element
|
79
|
+
*
|
80
|
+
* This method should be called automatically by Stimulus lifecycle, but can
|
81
|
+
* be called manually if needed for cleanup in special cases.
|
82
|
+
*
|
83
|
+
* @returns {void}
|
84
|
+
* @public
|
85
|
+
*/
|
86
|
+
disconnect() {
|
87
|
+
// Clear any pending timeout
|
88
|
+
if (this.labelTimeout) {
|
89
|
+
clearTimeout(this.labelTimeout);
|
90
|
+
}
|
91
|
+
|
92
|
+
if (this.messageElement) {
|
93
|
+
this.messageElement.remove();
|
94
|
+
}
|
95
|
+
|
96
|
+
this.element.removeEventListener("click", this.handleClick);
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Bind click event listener to the controller element.
|
101
|
+
*
|
102
|
+
* This method attaches a click event listener to the controller's DOM element
|
103
|
+
* (typically a button). The listener is bound with proper context to ensure
|
104
|
+
* 'this' refers to the controller instance within the event handler.
|
105
|
+
*
|
106
|
+
* The bound event handler will trigger the clipboard copy functionality
|
107
|
+
* when the user clicks the element.
|
108
|
+
*
|
109
|
+
* @returns {void}
|
110
|
+
* @private
|
111
|
+
*/
|
112
|
+
bindEvents() {
|
113
|
+
this.element.addEventListener("click", this.handleClick.bind(this));
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Handle click events on the copy button and orchestrate the copy operation.
|
118
|
+
*
|
119
|
+
* This method serves as the main entry point for clipboard copy functionality.
|
120
|
+
* It coordinates the entire copy process from validation to user feedback.
|
121
|
+
*
|
122
|
+
* Process flow:
|
123
|
+
* 1. Prevents default click behavior to avoid form submissions or navigation
|
124
|
+
* 2. Validates that a target selector is configured
|
125
|
+
* 3. Locates the target element in the DOM using the CSS selector
|
126
|
+
* 4. Extracts or determines the text content to be copied
|
127
|
+
* 5. Performs the actual clipboard copy operation
|
128
|
+
* 6. Provides visual and accessibility feedback on success
|
129
|
+
*
|
130
|
+
* Early returns occur if:
|
131
|
+
* - No target selector is configured
|
132
|
+
* - Target element cannot be found in DOM
|
133
|
+
* - No text content is available to copy
|
134
|
+
*
|
135
|
+
* @param {Event} event - The DOM click event object containing event details
|
136
|
+
* @returns {void}
|
137
|
+
* @private
|
138
|
+
*/
|
139
|
+
handleClick(event) {
|
140
|
+
event.preventDefault();
|
141
|
+
|
142
|
+
if (!this.targetSelector || this.targetSelector.length < 1) {
|
143
|
+
return;
|
144
|
+
}
|
145
|
+
|
146
|
+
const targetElement = document.querySelector(this.targetSelector);
|
147
|
+
if (!targetElement) {
|
148
|
+
return;
|
149
|
+
}
|
150
|
+
|
151
|
+
const textToCopy = this.getTextToCopy(targetElement);
|
152
|
+
if (!textToCopy || textToCopy.length < 1) {
|
153
|
+
return;
|
154
|
+
}
|
155
|
+
|
156
|
+
const success = this.copyToClipboard(textToCopy);
|
157
|
+
if (success) {
|
158
|
+
this.showSuccessMessage(targetElement);
|
159
|
+
this.announceToScreenReader();
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Determine what text content should be copied to the clipboard.
|
165
|
+
*
|
166
|
+
* This method implements a priority system for determining copy content:
|
167
|
+
* 1. Custom content from data-clipboard-content attribute (highest priority)
|
168
|
+
* 2. Selected text from form input elements using the select library
|
169
|
+
*
|
170
|
+
* For form input elements (input, textarea, select), this method uses
|
171
|
+
* the 'select' library to properly select and extract the element's content.
|
172
|
+
* This approach handles different input types and ensures proper text selection.
|
173
|
+
*
|
174
|
+
* @param {HTMLElement} targetElement - The DOM element containing text to copy
|
175
|
+
* @returns {string} The text content to be copied to clipboard, empty string if none found
|
176
|
+
* @private
|
177
|
+
*/
|
178
|
+
getTextToCopy(targetElement) {
|
179
|
+
let selectedText = this.customContent || "";
|
180
|
+
|
181
|
+
if (selectedText === "" && this.isInputElement(targetElement)) {
|
182
|
+
selectedText = select(targetElement);
|
183
|
+
}
|
184
|
+
|
185
|
+
return selectedText;
|
186
|
+
}
|
187
|
+
|
188
|
+
/**
|
189
|
+
* Check if a DOM element is a form input element.
|
190
|
+
*
|
191
|
+
* This method determines whether the given element is one of the standard
|
192
|
+
* HTML form input elements that can contain user-editable text content.
|
193
|
+
* This check is used to determine the appropriate method for text extraction.
|
194
|
+
*
|
195
|
+
* Recognized input elements:
|
196
|
+
* - input: Text inputs, password fields, email fields, etc.
|
197
|
+
* - textarea: Multi-line text input areas
|
198
|
+
* - select: Dropdown selection elements
|
199
|
+
*
|
200
|
+
* The comparison is case-insensitive to handle potential variations in
|
201
|
+
* element tag casing.
|
202
|
+
*
|
203
|
+
* @param {HTMLElement} element - The DOM element to examine
|
204
|
+
* @returns {boolean} True if the element is a form input element, false otherwise
|
205
|
+
* @private
|
206
|
+
*/
|
207
|
+
isInputElement(element) {
|
208
|
+
const inputTags = ["input", "textarea", "select"];
|
209
|
+
return inputTags.includes(element.tagName.toLowerCase());
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Copy text to the system clipboard using the deprecated execCommand method.
|
214
|
+
*
|
215
|
+
* This method implements clipboard copying using the legacy document.execCommand
|
216
|
+
* approach for maximum browser compatibility, including older versions of
|
217
|
+
* Internet Explorer that does not support the modern Clipboard API.
|
218
|
+
*
|
219
|
+
* Implementation details:
|
220
|
+
* 1. Creates a temporary invisible textarea element
|
221
|
+
* 2. Sets the textarea content to the text to be copied
|
222
|
+
* 3. Positions textarea off-screen to hide it from users
|
223
|
+
* 4. Inserts textarea into DOM adjacent to the trigger element
|
224
|
+
* 5. Selects all text within the temporary textarea
|
225
|
+
* 6. Executes the copy command using deprecated execCommand
|
226
|
+
* 7. Removes temporary element and restores focus
|
227
|
+
*
|
228
|
+
* The textarea is positioned at coordinates (-9999px) to ensure it is
|
229
|
+
* completely off-screen while remaining part of the document flow
|
230
|
+
* for proper text selection.
|
231
|
+
*
|
232
|
+
* Note: This method uses the deprecated execCommand API for IE compatibility.
|
233
|
+
* Modern applications should consider migrating to the Clipboard API when
|
234
|
+
* legacy browser support is no longer required.
|
235
|
+
*
|
236
|
+
* @param {string} text - The text content to copy to the system clipboard
|
237
|
+
* @returns {boolean} True if the copy operation succeeded, false if it failed
|
238
|
+
* @private
|
239
|
+
*/
|
240
|
+
copyToClipboard(text) {
|
241
|
+
//
|
242
|
+
const tempTextarea = document.createElement("textarea");
|
243
|
+
tempTextarea.value = text;
|
244
|
+
|
245
|
+
tempTextarea.style.cssText = "width: 1px; height: 1px; position: absolute; left: -9999px;";
|
246
|
+
this.element.parentNode.insertBefore(tempTextarea, this.element.nextSibling);
|
247
|
+
|
248
|
+
tempTextarea.select();
|
249
|
+
|
250
|
+
let success = false;
|
251
|
+
try {
|
252
|
+
// Use deprecated execCommand for IE compatibility
|
253
|
+
// Modern browsers support the Clipboard API, but IE does not
|
254
|
+
success = document.execCommand("copy");
|
255
|
+
} catch (err) {
|
256
|
+
success = false;
|
257
|
+
}
|
258
|
+
|
259
|
+
tempTextarea.remove();
|
260
|
+
this.element.focus();
|
261
|
+
|
262
|
+
return success;
|
263
|
+
}
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Display visual feedback by temporarily changing the button or target element text.
|
267
|
+
*
|
268
|
+
* This method provides immediate visual confirmation to users that the copy
|
269
|
+
* operation completed successfully. It temporarily replaces the element's
|
270
|
+
* content with a success message, then restores the original content after
|
271
|
+
* a predefined timeout period.
|
272
|
+
*
|
273
|
+
* Behavior:
|
274
|
+
* 1. Determines the appropriate element to show the message on
|
275
|
+
* 2. Stores the original content for later restoration
|
276
|
+
* 3. Displays the success message from copyLabel configuration
|
277
|
+
* 4. Sets a 5-second timeout to restore the original content
|
278
|
+
* 5. Manages timeout cleanup to prevent overlapping operations
|
279
|
+
*
|
280
|
+
* The method handles multiple rapid clicks by clearing any existing
|
281
|
+
* timeout before setting a new one, ensuring the message display
|
282
|
+
* duration is always 5 seconds from the most recent successful copy.
|
283
|
+
*
|
284
|
+
* Early return occurs if no copyLabel is configured, allowing the
|
285
|
+
* component to work without visual feedback if desired.
|
286
|
+
*
|
287
|
+
* @param {HTMLElement} targetElement - The target element containing copied text,
|
288
|
+
* used for determining message placement
|
289
|
+
* @returns {void}
|
290
|
+
* @private
|
291
|
+
*/
|
292
|
+
showSuccessMessage(targetElement) {
|
293
|
+
if (!this.copyLabel) {
|
294
|
+
return;
|
295
|
+
}
|
296
|
+
|
297
|
+
const messageElement = this.getMessageElement(targetElement);
|
298
|
+
|
299
|
+
if (this.labelTimeout) {
|
300
|
+
clearTimeout(this.labelTimeout);
|
301
|
+
}
|
302
|
+
|
303
|
+
if (!this.originalLabel) {
|
304
|
+
this.originalLabel = messageElement.innerHTML;
|
305
|
+
}
|
306
|
+
|
307
|
+
messageElement.innerHTML = this.copyLabel;
|
308
|
+
|
309
|
+
this.labelTimeout = setTimeout(() => {
|
310
|
+
messageElement.innerHTML = this.originalLabel;
|
311
|
+
this.originalLabel = null;
|
312
|
+
this.labelTimeout = null;
|
313
|
+
}, 5000);
|
314
|
+
}
|
315
|
+
|
316
|
+
/**
|
317
|
+
* Determine which DOM element should display the visual success message.
|
318
|
+
*
|
319
|
+
* This method implements a simple heuristic to choose the most appropriate
|
320
|
+
* element for displaying the temporary success message based on the
|
321
|
+
* controller element's content state.
|
322
|
+
*
|
323
|
+
* Decision logic:
|
324
|
+
* - If the trigger element (button) has no visible text content, the success
|
325
|
+
* message is shown on the target element (the element containing copied text)
|
326
|
+
* - If the trigger element has text content, the message is shown on the
|
327
|
+
* trigger element itself (typically the button)
|
328
|
+
*
|
329
|
+
* This approach ensures that users always see visual feedback in a logical
|
330
|
+
* location, whether they are using a text button or an icon-only button.
|
331
|
+
*
|
332
|
+
* @param {HTMLElement} targetElement - The target element containing the copied text
|
333
|
+
* @returns {HTMLElement} The DOM element where the success message should be displayed
|
334
|
+
* @private
|
335
|
+
*/
|
336
|
+
getMessageElement(targetElement) {
|
337
|
+
if (this.element.textContent.trim() === "") {
|
338
|
+
return targetElement;
|
339
|
+
}
|
340
|
+
return this.element;
|
341
|
+
}
|
342
|
+
|
343
|
+
/**
|
344
|
+
* Create and manage screen reader announcements for accessibility compliance.
|
345
|
+
*
|
346
|
+
* This method ensures that users of assistive technologies (screen readers)
|
347
|
+
* receive notification when clipboard copy operations complete successfully.
|
348
|
+
* Since screen readers cannot detect clipboard changes directly, this method
|
349
|
+
* creates ARIA live regions to announce the success.
|
350
|
+
*
|
351
|
+
* Implementation details:
|
352
|
+
* 1. Creates a screen reader announcement element if it does not exist
|
353
|
+
* 2. Configures ARIA attributes for optimal screen reader support:
|
354
|
+
* - aria-role="alert": Indicates important, time-sensitive information
|
355
|
+
* - aria-live="assertive": Ensures immediate announcement, interrupting other content
|
356
|
+
* - aria-atomic="true": Announces the entire message as a unit
|
357
|
+
* 3. Applies "sr-only" CSS class to hide the element visually while keeping it
|
358
|
+
* accessible to screen readers
|
359
|
+
* 4. Handles edge cases where the message element might be removed from DOM
|
360
|
+
* 5. Prevents duplicate announcements by appending non-breaking space when needed
|
361
|
+
*
|
362
|
+
* The method includes DOM validation and automatic recovery if the message
|
363
|
+
* element is unexpectedly removed from the document, ensuring reliable
|
364
|
+
* accessibility feedback.
|
365
|
+
*
|
366
|
+
* Early return occurs if no copyMessage is configured, making accessibility
|
367
|
+
* announcements optional while maintaining functionality.
|
368
|
+
*
|
369
|
+
* @returns {void}
|
370
|
+
* @private
|
371
|
+
*/
|
372
|
+
announceToScreenReader() {
|
373
|
+
if (!this.copyMessage) {
|
374
|
+
return;
|
375
|
+
}
|
376
|
+
|
377
|
+
if (!this.messageElement) {
|
378
|
+
this.messageElement = document.createElement("div");
|
379
|
+
|
380
|
+
this.messageElement.setAttribute("aria-role", "alert");
|
381
|
+
this.messageElement.setAttribute("aria-live", "assertive");
|
382
|
+
this.messageElement.setAttribute("aria-atomic", "true");
|
383
|
+
|
384
|
+
this.messageElement.className = "sr-only";
|
385
|
+
|
386
|
+
this.element.appendChild(this.messageElement);
|
387
|
+
}
|
388
|
+
|
389
|
+
if (!document.body.contains(this.messageElement)) {
|
390
|
+
console.warn("Message element was removed from DOM, recreating...");
|
391
|
+
this.messageElement = null;
|
392
|
+
this.announceToScreenReader();
|
393
|
+
return;
|
394
|
+
}
|
395
|
+
|
396
|
+
let message = this.copyMessage;
|
397
|
+
if (this.messageElement.innerHTML === message) {
|
398
|
+
message += " ";
|
399
|
+
}
|
400
|
+
|
401
|
+
this.messageElement.innerHTML = message;
|
402
|
+
}
|
403
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import formDatePicker from "src/decidim/datepicker/form_datepicker"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
this.datepicker = formDatePicker(this.element)
|
7
|
+
}
|
8
|
+
|
9
|
+
disconnect() {
|
10
|
+
this.datepicker = null;
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
/**
|
4
|
+
* DeleteAccount class handles the delete account functionality.
|
5
|
+
* Since the delete account has a modal to confirm it, we need to copy the content of the
|
6
|
+
* reason field to the hidden field in the form inside the modal.
|
7
|
+
*/
|
8
|
+
export default class extends Controller {
|
9
|
+
connect() {
|
10
|
+
this.deleteAccountModalForm = document.querySelector(".delete-account-modal");
|
11
|
+
this.openModalButton = document.querySelector(".open-modal-button");
|
12
|
+
|
13
|
+
this.bindEvents();
|
14
|
+
}
|
15
|
+
|
16
|
+
disconnect() {
|
17
|
+
if (this.boundModalOpen) {
|
18
|
+
this.openModalButton.removeEventListener("click", this.boundModalOpen)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
bindEvents() {
|
23
|
+
if (this.openModalButton) {
|
24
|
+
this.boundModalOpen = this.handleModalOpen.bind(this)
|
25
|
+
this.openModalButton.addEventListener("click", this.boundModalOpen);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
handleModalOpen(event) {
|
30
|
+
try {
|
31
|
+
const reasonTextarea = this.element.querySelector('[name="delete_account[delete_reason]"]');
|
32
|
+
const reasonInput = this.deleteAccountModalForm.querySelector('[name="delete_account[delete_reason]"]');
|
33
|
+
|
34
|
+
if (reasonTextarea && reasonInput) {
|
35
|
+
reasonInput.value = reasonTextarea.value;
|
36
|
+
}
|
37
|
+
} catch (error) {
|
38
|
+
console.error(error); // eslint-disable-line no-console
|
39
|
+
}
|
40
|
+
|
41
|
+
event.preventDefault();
|
42
|
+
event.stopPropagation();
|
43
|
+
return false;
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,216 @@
|
|
1
|
+
/* global jest */
|
2
|
+
/* eslint max-lines: ["error", 360] */
|
3
|
+
import { Application } from "@hotwired/stimulus"
|
4
|
+
import DeleteAccountFormController from "src/decidim/controllers/delete_account_form/controller";
|
5
|
+
|
6
|
+
describe("DeleteAccount", () => {
|
7
|
+
let mockDeleteAccountForm = null;
|
8
|
+
let mockDeleteAccountModalForm = null;
|
9
|
+
let mockOpenModalButton = null;
|
10
|
+
let mockReasonTextarea = null;
|
11
|
+
let mockReasonInput = null;
|
12
|
+
let application = null;
|
13
|
+
let controller = null;
|
14
|
+
|
15
|
+
beforeEach(() => {
|
16
|
+
application = Application.start();
|
17
|
+
application.register("delete-account-form", DeleteAccountFormController);
|
18
|
+
// Set up DOM structure
|
19
|
+
document.body.innerHTML = `
|
20
|
+
<form data-controller="delete-account-form" data-live-validate="true" data-validate-on-blur="true" class="form-defaults delete-account" id="delete_user_edit_delete_account_1" novalidate="novalidate" action="/account" accept-charset="UTF-8" method="post">
|
21
|
+
<input type="hidden" name="_method" value="delete" autocomplete="off" />
|
22
|
+
<input type="hidden" name="authenticity_token" value="K1Dy_nKLQ4KwgIZIFddT_c__UrNlRJ2kDF_F48MKUfN-zBxDr_sc24OXRiWWJtQVkxhH7kmFmKE88TRxnNO6CQ" autocomplete="off" />
|
23
|
+
<div class="form__wrapper">
|
24
|
+
<label>
|
25
|
+
<span class="help-text">Reason to delete your account</span>
|
26
|
+
<textarea rows="2" name="delete_account[delete_reason]" id="delete_user_delete_account_delete_reason"></textarea>
|
27
|
+
</label>
|
28
|
+
</div>
|
29
|
+
<div class="form__wrapper-block">
|
30
|
+
<button type="button" data-dialog-open="delete-account" class="button button__sm md:button__lg button__secondary mr-auto !ml-0 open-modal-button"> Delete my account </button>
|
31
|
+
</div>
|
32
|
+
</form>
|
33
|
+
<div id="delete-account" data-dialog="delete-account">
|
34
|
+
<div id="delete-account-content" class="verification-modal">
|
35
|
+
<button type="button" data-dialog-close="delete-account" data-dialog-closable="" aria-label="Close modal">×</button>
|
36
|
+
<form data-live-validate="true" data-validate-on-blur="true" class="delete-account-modal" id="delete_user_confirm_edit_delete_account_1" novalidate="novalidate" action="/account" accept-charset="UTF-8" method="post">
|
37
|
+
<input type="hidden" name="_method" value="delete" autocomplete="off" />
|
38
|
+
<input type="hidden" name="authenticity_token" value="WteUAxJawfORr3tuOkVF7UHKnAM6pl5n8ygK9ptPKnUPS3q-zyqeqqK4uwO5tMIFHS2JXhZnW2LDhvtkxJbBjw" autocomplete="off" />
|
39
|
+
<input autocomplete="off" type="hidden" name="delete_account[delete_reason]" id="delete_user_confirm_delete_account_delete_reason" />
|
40
|
+
<div data-dialog-container>
|
41
|
+
<svg width="1em" height="1em" role="img" aria-hidden="true">
|
42
|
+
<use href="/decidim-packs/media/images/remixicon.symbol-e643e553623ffcd49f94.svg#ri-close-circle-line"></use>
|
43
|
+
</svg>
|
44
|
+
<h3 class="h3" id="dialog-title-delete-account" data-dialog-title> Are you sure you want to delete your account? </h3>
|
45
|
+
</div>
|
46
|
+
<div data-dialog-actions>
|
47
|
+
<button type="button" class="button button__sm md:button__lg button__transparent-secondary" data-dialog-close="delete-account"> Close window </button>
|
48
|
+
<input type="submit" class="button button__sm md:button__lg button__secondary" value="Yes, I want to delete my account">
|
49
|
+
</div>
|
50
|
+
</form>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
`;
|
54
|
+
|
55
|
+
// Get references to DOM elements
|
56
|
+
mockDeleteAccountForm = document.querySelector(".delete-account");
|
57
|
+
mockDeleteAccountModalForm = document.querySelector(".delete-account-modal");
|
58
|
+
mockOpenModalButton = document.querySelector(".open-modal-button");
|
59
|
+
mockReasonTextarea = document.querySelector("#delete_user_delete_account_delete_reason");
|
60
|
+
mockReasonInput = document.querySelector("#delete_user_confirm_delete_account_delete_reason");
|
61
|
+
|
62
|
+
// Mock console.error
|
63
|
+
jest.spyOn(console, "error").mockImplementation(() => {});
|
64
|
+
|
65
|
+
return new Promise((resolve) => {
|
66
|
+
setTimeout(() => {
|
67
|
+
controller = application.getControllerForElementAndIdentifier(mockDeleteAccountForm, "delete-account-form");
|
68
|
+
resolve();
|
69
|
+
}, 0);
|
70
|
+
});
|
71
|
+
});
|
72
|
+
|
73
|
+
afterEach(() => {
|
74
|
+
application.stop();
|
75
|
+
// Clean up
|
76
|
+
document.body.innerHTML = "";
|
77
|
+
console.error.mockRestore();
|
78
|
+
});
|
79
|
+
|
80
|
+
describe("constructor", () => {
|
81
|
+
it("should initialize with correct DOM elements", () => {
|
82
|
+
expect(controller.element).toBe(mockDeleteAccountForm);
|
83
|
+
expect(controller.deleteAccountModalForm).toBe(mockDeleteAccountModalForm);
|
84
|
+
expect(controller.openModalButton).toBe(mockOpenModalButton);
|
85
|
+
});
|
86
|
+
});
|
87
|
+
|
88
|
+
describe("init()", () => {
|
89
|
+
it("should call bindEvents when delete account form exists", () => {
|
90
|
+
const bindEventsSpy = jest.spyOn(controller, "bindEvents");
|
91
|
+
|
92
|
+
controller.disconnect()
|
93
|
+
controller.connect()
|
94
|
+
|
95
|
+
expect(bindEventsSpy).toHaveBeenCalled();
|
96
|
+
bindEventsSpy.mockRestore();
|
97
|
+
});
|
98
|
+
});
|
99
|
+
|
100
|
+
describe("bindEvents()", () => {
|
101
|
+
it("should add click event listener to open modal button", () => {
|
102
|
+
const addEventListenerSpy = jest.spyOn(mockOpenModalButton, "addEventListener");
|
103
|
+
|
104
|
+
controller.disconnect()
|
105
|
+
controller.connect()
|
106
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith("click", expect.any(Function));
|
107
|
+
addEventListenerSpy.mockRestore();
|
108
|
+
});
|
109
|
+
|
110
|
+
it("should not add event listener when open modal button does not exist", () => {
|
111
|
+
document.querySelector(".open-modal-button").remove();
|
112
|
+
|
113
|
+
controller.disconnect()
|
114
|
+
controller.connect()
|
115
|
+
|
116
|
+
// Should not throw an error
|
117
|
+
expect(controller.openModalButton).toBeNull();
|
118
|
+
});
|
119
|
+
});
|
120
|
+
|
121
|
+
describe("handleModalOpen()", () => {
|
122
|
+
it("should copy textarea value to hidden input", () => {
|
123
|
+
const mockEvent = {
|
124
|
+
preventDefault: jest.fn(),
|
125
|
+
stopPropagation: jest.fn()
|
126
|
+
};
|
127
|
+
|
128
|
+
mockReasonTextarea.value = "User wants to delete account";
|
129
|
+
const result = controller.handleModalOpen(mockEvent);
|
130
|
+
|
131
|
+
expect(mockReasonInput.value).toBe("User wants to delete account");
|
132
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
133
|
+
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
134
|
+
expect(result).toBe(false);
|
135
|
+
});
|
136
|
+
|
137
|
+
it("should handle empty textarea value", () => {
|
138
|
+
const mockEvent = {
|
139
|
+
preventDefault: jest.fn(),
|
140
|
+
stopPropagation: jest.fn()
|
141
|
+
};
|
142
|
+
|
143
|
+
mockReasonTextarea.value = "";
|
144
|
+
controller.handleModalOpen(mockEvent);
|
145
|
+
|
146
|
+
expect(mockReasonInput.value).toBe("");
|
147
|
+
});
|
148
|
+
|
149
|
+
it("should handle case when textarea does not exist", () => {
|
150
|
+
const mockEvent = {
|
151
|
+
preventDefault: jest.fn(),
|
152
|
+
stopPropagation: jest.fn()
|
153
|
+
};
|
154
|
+
|
155
|
+
mockReasonTextarea.remove();
|
156
|
+
controller.handleModalOpen(mockEvent);
|
157
|
+
|
158
|
+
expect(mockReasonInput.value).toBe("");
|
159
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
160
|
+
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
161
|
+
});
|
162
|
+
|
163
|
+
it("should handle case when input does not exist", () => {
|
164
|
+
const mockEvent = {
|
165
|
+
preventDefault: jest.fn(),
|
166
|
+
stopPropagation: jest.fn()
|
167
|
+
};
|
168
|
+
|
169
|
+
mockReasonInput.remove();
|
170
|
+
mockReasonTextarea.value = "Test reason";
|
171
|
+
controller.handleModalOpen(mockEvent);
|
172
|
+
|
173
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
174
|
+
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
175
|
+
});
|
176
|
+
|
177
|
+
it("should handle errors gracefully", () => {
|
178
|
+
const mockEvent = {
|
179
|
+
preventDefault: jest.fn(),
|
180
|
+
stopPropagation: jest.fn()
|
181
|
+
};
|
182
|
+
|
183
|
+
// Mock querySelector to throw an error
|
184
|
+
const originalQuerySelector = controller.element.querySelector;
|
185
|
+
controller.element.querySelector = jest.fn(() => {
|
186
|
+
throw new Error("Test error");
|
187
|
+
});
|
188
|
+
|
189
|
+
controller.handleModalOpen(mockEvent);
|
190
|
+
|
191
|
+
expect(console.error).toHaveBeenCalledWith(expect.any(Error));
|
192
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
193
|
+
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
194
|
+
|
195
|
+
// Restore original method
|
196
|
+
controller.element.querySelector = originalQuerySelector;
|
197
|
+
});
|
198
|
+
});
|
199
|
+
|
200
|
+
describe("event integration", () => {
|
201
|
+
it("should handle click event on open modal button", () => {
|
202
|
+
mockReasonTextarea.value = "Integration test reason";
|
203
|
+
|
204
|
+
const mockEvent = new Event("click", { bubbles: true, cancelable: true });
|
205
|
+
const preventDefaultSpy = jest.spyOn(mockEvent, "preventDefault");
|
206
|
+
const stopPropagationSpy = jest.spyOn(mockEvent, "stopPropagation");
|
207
|
+
|
208
|
+
mockOpenModalButton.dispatchEvent(mockEvent);
|
209
|
+
|
210
|
+
expect(mockReasonInput.value).toBe("Integration test reason");
|
211
|
+
expect(preventDefaultSpy).toHaveBeenCalled();
|
212
|
+
expect(stopPropagationSpy).toHaveBeenCalled();
|
213
|
+
});
|
214
|
+
});
|
215
|
+
|
216
|
+
});
|