decidim-core 0.28.3 → 0.28.4
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/app/cells/decidim/content_blocks/stats_cell.rb +1 -1
- data/app/cells/decidim/endorsement_buttons_cell.rb +1 -1
- data/app/cells/decidim/nav_links/show.erb +3 -3
- data/app/cells/decidim/resource_types_filter/show.erb +11 -12
- data/app/commands/decidim/amendable/create_draft.rb +1 -0
- data/app/controllers/concerns/decidim/devise_authentication_methods.rb +1 -1
- data/app/controllers/concerns/decidim/direct_upload.rb +82 -0
- data/app/forms/decidim/upload_validation_form.rb +1 -1
- data/app/helpers/decidim/layout_helper.rb +28 -0
- data/app/helpers/decidim/menu_helper.rb +1 -1
- data/app/helpers/decidim/scopes_helper.rb +5 -2
- data/app/models/decidim/action_log.rb +11 -1
- data/app/packs/src/decidim/a11y.js +11 -15
- data/app/packs/src/decidim/append_redirect_url_to_modals.js +14 -12
- data/app/packs/src/decidim/direct_uploads/upload_modal.js +3 -0
- data/app/packs/stylesheets/decidim/_buttons.scss +1 -1
- data/app/packs/stylesheets/decidim/_modal_update.scss +4 -0
- data/app/presenters/decidim/menu_item_presenter.rb +1 -1
- data/app/queries/decidim/last_activity.rb +16 -5
- data/app/views/decidim/devise/omniauth_registrations/new.html.erb +1 -1
- data/app/views/decidim/offline/show.html.erb +1 -1
- data/app/views/decidim/pages/_tabbed.html.erb +3 -3
- data/app/views/decidim/shared/_filters.html.erb +5 -5
- data/app/views/decidim/shared/_orders.html.erb +3 -2
- data/app/views/decidim/shared/filters/_check_boxes_tree.html.erb +1 -1
- data/app/views/decidim/shared/filters/_collection.html.erb +1 -1
- data/app/views/layouts/decidim/_head.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_user_profile.html.erb +2 -2
- data/config/locales/ar.yml +0 -1
- data/config/locales/bg.yml +0 -1
- data/config/locales/ca.yml +1 -1
- data/config/locales/cs.yml +1 -1
- data/config/locales/de.yml +2 -2
- data/config/locales/el.yml +0 -1
- data/config/locales/en.yml +1 -1
- data/config/locales/es-MX.yml +1 -1
- data/config/locales/es-PY.yml +1 -1
- data/config/locales/es.yml +1 -1
- data/config/locales/eu.yml +4 -4
- data/config/locales/fi-plain.yml +1 -1
- data/config/locales/fi.yml +1 -1
- data/config/locales/fr-CA.yml +1 -1
- data/config/locales/fr.yml +1 -1
- data/config/locales/gl.yml +0 -1
- data/config/locales/hu.yml +0 -1
- data/config/locales/ja.yml +3 -3
- data/config/locales/lt.yml +0 -1
- data/config/locales/pl.yml +0 -1
- data/config/locales/pt-BR.yml +199 -1
- data/config/locales/ro-RO.yml +61 -48
- data/config/locales/sv.yml +446 -75
- data/config/locales/zh-TW.yml +0 -1
- data/lib/decidim/core/engine.rb +6 -0
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +10 -0
- data/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb +112 -14
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/core.rb +11 -0
- data/lib/decidim/organization_settings.rb +4 -1
- data/lib/decidim/view_model.rb +1 -1
- data/lib/tasks/upgrade/decidim_attachments.rake +14 -0
- metadata +8 -6
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3ec3e95f4dbf1ac0e6178188a46ddf5d702bea045e85d76f64c1193af3c0e2ed
         | 
| 4 | 
            +
              data.tar.gz: 684aeb5ae98c9d8fe9fa9488721d153fbc42be5dcd1cd13d1328e34da3ebdef8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fee3f0d928f2853e9953dc65e5232aae720997c23d7933e4e08a3154246ae6612a7688e4b9dc0c7af196b9f7043aacd08b7bee72e60ebe69b6b9ce057e17149c
         | 
| 7 | 
            +
              data.tar.gz: 02fb38ba880953a3b819f4ff1b3d88d6cde4dcd66a2753ddb5178fa524d73f8e8db14b4bcf4e862651ff16c45c1f6be4044520a6daf8d8f94b1ff9dfae299ec1
         | 
| @@ -77,7 +77,7 @@ module Decidim | |
| 77 77 | 
             
                end
         | 
| 78 78 |  | 
| 79 79 | 
             
                def user_has_verified_groups?
         | 
| 80 | 
            -
                  current_user && Decidim::UserGroups::ManageableUserGroups.for(current_user).verified.any?
         | 
| 80 | 
            +
                  current_user && current_organization.user_groups_enabled? && Decidim::UserGroups::ManageableUserGroups.for(current_user).verified.any?
         | 
| 81 81 | 
             
                end
         | 
| 82 82 |  | 
| 83 83 | 
             
                def endorse_translated
         | 
| @@ -1,12 +1,12 @@ | |
| 1 1 | 
             
            <div class="participatory-space__nav-container">
         | 
| 2 | 
            -
              <button id="dropdown-trigger-participatory-space" data-component="dropdown" data-target="dropdown-menu-participatory-space" data-auto-close="true" data-scroll-to-menu="true">
         | 
| 2 | 
            +
              <button id="dropdown-trigger-participatory-space" data-component="dropdown" data-target="dropdown-menu-participatory-space" data-auto-close="true" data-scroll-to-menu="true" data-open-md="true">
         | 
| 3 3 | 
             
                <span><%= t("decidim.searches.filters.jump_to") %></span>
         | 
| 4 4 | 
             
                <%= icon "arrow-down-s-line" %>
         | 
| 5 5 | 
             
                <%= icon "arrow-up-s-line" %>
         | 
| 6 6 | 
             
              </button>
         | 
| 7 | 
            -
              <ul id="dropdown-menu-participatory-space" class="participatory-space__nav" | 
| 7 | 
            +
              <ul id="dropdown-menu-participatory-space" class="participatory-space__nav">
         | 
| 8 8 | 
             
                <% model.each do |item| %>
         | 
| 9 | 
            -
                  <li>
         | 
| 9 | 
            +
                  <li role="menuitem">
         | 
| 10 10 | 
             
                    <%= link_to item[:url], class: "participatory-space__nav-item" do %>
         | 
| 11 11 | 
             
                      <%= item[:name] %>
         | 
| 12 12 | 
             
                      <%= icon "arrow-right-line" %>
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            <div id="<%= id %>" class="filter-container">
         | 
| 2 | 
            -
              <button id="dropdown-trigger-resource" data-component="dropdown" data-target="dropdown-menu-resource" data- | 
| 2 | 
            +
              <button id="dropdown-trigger-resource" data-component="dropdown" data-target="dropdown-menu-resource" data-open-md="true">
         | 
| 3 3 | 
             
                <% resource_types.each do |resource_type| %>
         | 
| 4 4 | 
             
                  <span data-value="<%= resource_type[0] %>" class="<%= "is-active" if filter_param == resource_type[0] %>">
         | 
| 5 5 | 
             
                    <%= text_with_resource_icon(*resource_type) %>
         | 
| @@ -8,15 +8,14 @@ | |
| 8 8 | 
             
                <%= icon "arrow-down-s-line" %>
         | 
| 9 9 | 
             
                <%= icon "arrow-up-s-line" %>
         | 
| 10 10 | 
             
              </button>
         | 
| 11 | 
            -
               | 
| 12 | 
            -
                 | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
                       | 
| 16 | 
            -
                       | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
              <% end %>
         | 
| 11 | 
            +
              <ul id="dropdown-menu-resource">
         | 
| 12 | 
            +
                <% resource_types.each do |resource_type| %>
         | 
| 13 | 
            +
                  <li role="menuitem">
         | 
| 14 | 
            +
                    <%= link_to decidim.last_activities_path(filter: { with_resource_type: resource_type[0] } ), class: "filter#{" is-active" if filter_param == resource_type[0]}" do %>
         | 
| 15 | 
            +
                      <span class="sr-only"><%= resource_type[1] %></span>
         | 
| 16 | 
            +
                      <%= text_with_resource_icon(*resource_type) %>
         | 
| 17 | 
            +
                    <% end %>
         | 
| 18 | 
            +
                  </li>
         | 
| 19 | 
            +
                <% end %>
         | 
| 20 | 
            +
              </ul>
         | 
| 22 21 | 
             
            </div>
         | 
| @@ -53,6 +53,7 @@ module Decidim | |
| 53 53 | 
             
                        emendation.body = { I18n.locale => form.emendation_params.with_indifferent_access[:body] }
         | 
| 54 54 | 
             
                        emendation.component = amendable.component
         | 
| 55 55 | 
             
                        emendation.add_author(current_user, user_group)
         | 
| 56 | 
            +
                        emendation.category = amendable.category if amendable.respond_to?(:category)
         | 
| 56 57 | 
             
                        emendation.save!
         | 
| 57 58 | 
             
                        emendation
         | 
| 58 59 | 
             
                      end
         | 
| @@ -12,7 +12,7 @@ module Decidim | |
| 12 12 | 
             
                    if user.present? && user.blocked?
         | 
| 13 13 | 
             
                      check_user_block_status(user)
         | 
| 14 14 | 
             
                    elsif user.needs_password_update?
         | 
| 15 | 
            -
                      change_password_path
         | 
| 15 | 
            +
                      decidim.change_password_path
         | 
| 16 16 | 
             
                    elsif first_login_and_not_authorized?(user) && !user.admin? && !pending_redirect?(user)
         | 
| 17 17 | 
             
                      decidim_verifications.first_login_authorizations_path
         | 
| 18 18 | 
             
                    else
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Decidim
         | 
| 4 | 
            +
              module DirectUpload
         | 
| 5 | 
            +
                extend ActiveSupport::Concern
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                included do
         | 
| 8 | 
            +
                  include Decidim::NeedsOrganization
         | 
| 9 | 
            +
                  skip_before_action :verify_organization
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  before_action :check_organization!,
         | 
| 12 | 
            +
                                :check_authenticated!,
         | 
| 13 | 
            +
                                :check_user_belongs_to_organization,
         | 
| 14 | 
            +
                                :validate_direct_upload
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                protected
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def validate_direct_upload
         | 
| 20 | 
            +
                  # We skip the validation if we are in system panel. `current_admin` refers to the main system admin user.
         | 
| 21 | 
            +
                  return if current_admin.present?
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  head :unprocessable_entity unless [
         | 
| 24 | 
            +
                    maximum_allowed_size.try(:to_i) >= blob_args[:byte_size].try(:to_i),
         | 
| 25 | 
            +
                    content_types.any? { |pattern| pattern.match?(blob_args[:content_type]) },
         | 
| 26 | 
            +
                    content_types.any? { |pattern| pattern.match?(MiniMime.lookup_by_extension(extension)&.content_type) },
         | 
| 27 | 
            +
                    allowed_extensions.any? { |pattern| pattern.match?(extension) }
         | 
| 28 | 
            +
                  ].all?
         | 
| 29 | 
            +
                rescue NoMethodError
         | 
| 30 | 
            +
                  head :unprocessable_entity
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def extension
         | 
| 34 | 
            +
                  File.extname(blob_args[:filename]).delete(".")
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def maximum_allowed_size
         | 
| 38 | 
            +
                  current_organization.settings.upload_maximum_file_size
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def check_organization!
         | 
| 42 | 
            +
                  head :unauthorized if current_organization.blank? && current_admin.blank?
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def check_authenticated!
         | 
| 46 | 
            +
                  head :unauthorized if current_user.blank? && current_admin.blank?
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def check_user_belongs_to_organization
         | 
| 50 | 
            +
                  return if current_admin.present?
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  head :unauthorized unless current_organization == current_user.organization
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def allowed_extensions
         | 
| 56 | 
            +
                  if user_has_elevated_role?
         | 
| 57 | 
            +
                    current_organization.settings.upload_allowed_file_extensions_admin
         | 
| 58 | 
            +
                  else
         | 
| 59 | 
            +
                    current_organization.settings.upload_allowed_file_extensions
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def content_types
         | 
| 64 | 
            +
                  if user_has_elevated_role?
         | 
| 65 | 
            +
                    current_organization.settings.upload_allowed_content_types_admin
         | 
| 66 | 
            +
                  else
         | 
| 67 | 
            +
                    current_organization.settings.upload_allowed_content_types
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                private
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def user_has_elevated_role?
         | 
| 74 | 
            +
                  [
         | 
| 75 | 
            +
                    current_user&.admin?,
         | 
| 76 | 
            +
                    defined?(Decidim::Assemblies::AssembliesWithUserRole) && Decidim::Assemblies::AssembliesWithUserRole.for(current_user).any?,
         | 
| 77 | 
            +
                    defined?(Decidim::Conferences::ConferencesWithUserRole) && Decidim::Conferences::ConferencesWithUserRole.for(current_user).any?,
         | 
| 78 | 
            +
                    defined?(Decidim::ParticipatoryProcessesWithUserRole) && Decidim::ParticipatoryProcessesWithUserRole.for(current_user).any?
         | 
| 79 | 
            +
                  ].any?
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
| @@ -10,7 +10,7 @@ module Decidim | |
| 10 10 | 
             
                # Property is named as attribute in upload modal and passthru validator, but
         | 
| 11 11 | 
             
                # it cannot be named as attribute here.
         | 
| 12 12 | 
             
                attribute :property, String
         | 
| 13 | 
            -
                attribute :blob,  | 
| 13 | 
            +
                attribute :blob, Decidim::Attributes::Blob
         | 
| 14 14 | 
             
                attribute :form_class, String
         | 
| 15 15 |  | 
| 16 16 | 
             
                validates :resource_class, presence: true
         | 
| @@ -169,6 +169,20 @@ module Decidim | |
| 169 169 | 
             
                                                      end
         | 
| 170 170 | 
             
                end
         | 
| 171 171 |  | 
| 172 | 
            +
                def current_url(params = request.parameters)
         | 
| 173 | 
            +
                  return url_for(params) if respond_to?(:current_participatory_space) || respond_to?(:current_component)
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  each_decidim_engine do |helpers|
         | 
| 176 | 
            +
                    return helpers.url_for(params)
         | 
| 177 | 
            +
                  rescue ActionController::UrlGenerationError
         | 
| 178 | 
            +
                    # Continue to next engine in case the URL is not available.
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  main_app.url_for(params)
         | 
| 182 | 
            +
                rescue ActionController::UrlGenerationError
         | 
| 183 | 
            +
                  "#{request.base_url}#{"?#{params.to_query}" unless params.empty?}"
         | 
| 184 | 
            +
                end
         | 
| 185 | 
            +
             | 
| 172 186 | 
             
                private
         | 
| 173 187 |  | 
| 174 188 | 
             
                def empty_organization_description?
         | 
| @@ -177,6 +191,20 @@ module Decidim | |
| 177 191 | 
             
                  organization_description.blank? || organization_description == "<p></p>"
         | 
| 178 192 | 
             
                end
         | 
| 179 193 |  | 
| 194 | 
            +
                def each_decidim_engine
         | 
| 195 | 
            +
                  Rails.application.railties.each do |engine|
         | 
| 196 | 
            +
                    next unless engine.is_a?(Rails::Engine)
         | 
| 197 | 
            +
                    next unless engine.isolated?
         | 
| 198 | 
            +
                    next unless engine.engine_name.start_with?("decidim_")
         | 
| 199 | 
            +
                    next unless respond_to?(engine.engine_name)
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                    yield public_send(engine.engine_name)
         | 
| 202 | 
            +
                  end
         | 
| 203 | 
            +
                  return unless respond_to?(:decidim)
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                  yield decidim
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
             | 
| 180 208 | 
             
                def tag_builder
         | 
| 181 209 | 
             
                  @tag_builder ||= ActionView::Helpers::TagHelper::TagBuilder.new(self)
         | 
| 182 210 | 
             
                end
         | 
| @@ -49,7 +49,7 @@ module Decidim | |
| 49 49 | 
             
                    self,
         | 
| 50 50 | 
             
                    element_class: "font-semibold underline",
         | 
| 51 51 | 
             
                    active_class: "is-active",
         | 
| 52 | 
            -
                    container_options: { class: "space-y-4 break-inside-avoid" },
         | 
| 52 | 
            +
                    container_options: { class: "space-y-4 break-inside-avoid", role: :menu },
         | 
| 53 53 | 
             
                    label: t("layouts.decidim.footer.decidim_title")
         | 
| 54 54 | 
             
                  )
         | 
| 55 55 | 
             
                end
         | 
| @@ -53,11 +53,14 @@ module Decidim | |
| 53 53 | 
             
                # options       - An optional Hash with options:
         | 
| 54 54 | 
             
                #
         | 
| 55 55 | 
             
                # Returns nothing.
         | 
| 56 | 
            -
                def scopes_select_field(form, name, root: false, options: {})
         | 
| 56 | 
            +
                def scopes_select_field(form, name, root: false, options: {}, html_options: {})
         | 
| 57 | 
            +
                  options = options.merge(include_blank: I18n.t("decidim.scopes.prompt")) unless options.has_key?(:include_blank)
         | 
| 58 | 
            +
             | 
| 57 59 | 
             
                  form.select(
         | 
| 58 60 | 
             
                    name,
         | 
| 59 61 | 
             
                    ordered_scopes_descendants_for_select(root),
         | 
| 60 | 
            -
                    options | 
| 62 | 
            +
                    options,
         | 
| 63 | 
            +
                    html_options
         | 
| 61 64 | 
             
                  )
         | 
| 62 65 | 
             
                end
         | 
| 63 66 |  | 
| @@ -138,6 +138,7 @@ module Decidim | |
| 138 138 | 
             
                    Decidim::Proposals::Proposal
         | 
| 139 139 | 
             
                    Decidim::Surveys::Survey
         | 
| 140 140 | 
             
                    Decidim::Assembly
         | 
| 141 | 
            +
                    Decidim::Conference
         | 
| 141 142 | 
             
                    Decidim::Initiative
         | 
| 142 143 | 
             
                    Decidim::ParticipatoryProcess
         | 
| 143 144 | 
             
                  ).select do |klass|
         | 
| @@ -154,7 +155,16 @@ module Decidim | |
| 154 155 | 
             
                end
         | 
| 155 156 |  | 
| 156 157 | 
             
                def self.publicable_public_resource_types
         | 
| 157 | 
            -
                  @publicable_public_resource_types ||= public_resource_types | 
| 158 | 
            +
                  @publicable_public_resource_types ||= public_resource_types
         | 
| 159 | 
            +
                                                        .select { |klass| klass.constantize.column_names.include?("published_at") } - publicable_exceptions
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                def self.publicable_exceptions
         | 
| 163 | 
            +
                  @publicable_exceptions = %w(
         | 
| 164 | 
            +
                    Decidim::Blogs::Post
         | 
| 165 | 
            +
                  ).select do |klass|
         | 
| 166 | 
            +
                    klass.safe_constantize.present?
         | 
| 167 | 
            +
                  end
         | 
| 158 168 | 
             
                end
         | 
| 159 169 |  | 
| 160 170 | 
             
                def self.ransackable_scopes(auth_object = nil)
         | 
| @@ -59,7 +59,6 @@ const createDropdown = (component) => { | |
| 59 59 | 
             
              const dropdownOptions = {};
         | 
| 60 60 | 
             
              dropdownOptions.dropdown = component.dataset.target;
         | 
| 61 61 | 
             
              dropdownOptions.hover = component.dataset.hover === "true";
         | 
| 62 | 
            -
              dropdownOptions.isOpen = component.dataset.open === "true";
         | 
| 63 62 | 
             
              dropdownOptions.autoClose = component.dataset.autoClose === "true";
         | 
| 64 63 |  | 
| 65 64 | 
             
              // This snippet allows to disable the dropdown based on the current viewport
         | 
| @@ -78,6 +77,17 @@ const createDropdown = (component) => { | |
| 78 77 | 
             
                return
         | 
| 79 78 | 
             
              }
         | 
| 80 79 |  | 
| 80 | 
            +
              dropdownOptions.isOpen = component.dataset.open === "true";
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              const isOpen = Object.keys(screens).some((key) => {
         | 
| 83 | 
            +
                if (!isScreenSize(key)) {
         | 
| 84 | 
            +
                  return false;
         | 
| 85 | 
            +
                }
         | 
| 86 | 
            +
                return Boolean(component.dataset[`open-${key}`.replace(/-([a-z])/g, (str) => str[1].toUpperCase())]);
         | 
| 87 | 
            +
              });
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              dropdownOptions.isOpen = dropdownOptions.isOpen || isOpen;
         | 
| 90 | 
            +
             | 
| 81 91 | 
             
              if (!component.id) {
         | 
| 82 92 | 
             
                // when component has no id, we enforce to have it one
         | 
| 83 93 | 
             
                component.id = `dropdown-${Math.random().toString(36).substring(7)}`
         | 
| @@ -104,20 +114,6 @@ const createDropdown = (component) => { | |
| 104 114 | 
             
                });
         | 
| 105 115 | 
             
              }
         | 
| 106 116 |  | 
| 107 | 
            -
              // Disable focus on children elements so we can pass the AXE accessibility tests
         | 
| 108 | 
            -
              const dropdownMenu = document.getElementById(dropdownOptions.dropdown);
         | 
| 109 | 
            -
              if (dropdownMenu.getAttribute("aria-hidden") === "true") {
         | 
| 110 | 
            -
                dropdownMenu.
         | 
| 111 | 
            -
                  querySelectorAll("a, input, button").
         | 
| 112 | 
            -
                  forEach((element) => { element.tabIndex = -1 })
         | 
| 113 | 
            -
              }
         | 
| 114 | 
            -
             | 
| 115 | 
            -
              component.addEventListener("click", () => {
         | 
| 116 | 
            -
                dropdownMenu.
         | 
| 117 | 
            -
                  querySelectorAll("a, input, button").
         | 
| 118 | 
            -
                  forEach((element) => { element.tabIndex = 0 })
         | 
| 119 | 
            -
              })
         | 
| 120 | 
            -
             | 
| 121 117 | 
             
              Dropdowns.render(component.id, dropdownOptions);
         | 
| 122 118 | 
             
            }
         | 
| 123 119 |  | 
| @@ -53,21 +53,23 @@ $(() => { | |
| 53 53 | 
             
              }
         | 
| 54 54 |  | 
| 55 55 | 
             
              $(document).on("click.zf.trigger", (event) => {
         | 
| 56 | 
            -
                const target = `#${$(event.target).data(" | 
| 56 | 
            +
                const target = `#${$(event.target).data("dialogOpen")}`;
         | 
| 57 57 | 
             
                const redirectUrl = $(event.target).data("redirectUrl");
         | 
| 58 58 |  | 
| 59 | 
            -
                if (target  | 
| 60 | 
            -
                   | 
| 61 | 
            -
                    attr("id", "redirect_url").
         | 
| 62 | 
            -
                    attr("name", "redirect_url").
         | 
| 63 | 
            -
                    attr("value", redirectUrl).
         | 
| 64 | 
            -
                    appendTo(`${target} form`);
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                  $(`${target} a`).attr("href", (index, href) => {
         | 
| 67 | 
            -
                    const querystring = jQuery.param({"redirect_url": redirectUrl});
         | 
| 68 | 
            -
                    return href + (href.match(/\?/) ? "&" : "?") + querystring;
         | 
| 69 | 
            -
                  });
         | 
| 59 | 
            +
                if (!target || !redirectUrl) {
         | 
| 60 | 
            +
                  return;
         | 
| 70 61 | 
             
                }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                $("<input type='hidden' />").
         | 
| 64 | 
            +
                  attr("id", "redirect_url").
         | 
| 65 | 
            +
                  attr("name", "redirect_url").
         | 
| 66 | 
            +
                  attr("value", redirectUrl).
         | 
| 67 | 
            +
                  appendTo(`${target} form`);
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                $(`${target} a`).attr("href", (index, href) => {
         | 
| 70 | 
            +
                  const querystring = jQuery.param({ "redirect_url": redirectUrl });
         | 
| 71 | 
            +
                  return href + (href.match(/\?/) ? "&" : "?") + querystring;
         | 
| 72 | 
            +
                });
         | 
| 71 73 | 
             
              });
         | 
| 72 74 |  | 
| 73 75 | 
             
              $(document).on("closed.zf.reveal", (event) => {
         | 
| @@ -77,6 +77,9 @@ export default class UploadModal { | |
| 77 77 | 
             
                  uploader.upload.create((error, blob) => {
         | 
| 78 78 | 
             
                    if (error) {
         | 
| 79 79 | 
             
                      uploader.errors = [error]
         | 
| 80 | 
            +
                      this.uploadItems.replaceChild(this.createUploadItem(file, [error], { value: 100 }), item);
         | 
| 81 | 
            +
                      this.updateDropZone();
         | 
| 82 | 
            +
             | 
| 80 83 | 
             
                    } else {
         | 
| 81 84 | 
             
                      // attach the file hash to submit the form, when the file has been uploaded
         | 
| 82 85 | 
             
                      file.hiddenField = blob.signed_id
         | 
| @@ -26,7 +26,7 @@ module Decidim | |
| 26 26 | 
             
                delegate :content_tag, :safe_join, :link_to, :active_link_to_class, :is_active_link?, :icon, to: :@view
         | 
| 27 27 |  | 
| 28 28 | 
             
                def render
         | 
| 29 | 
            -
                  content_tag :li, class: link_wrapper_classes do
         | 
| 29 | 
            +
                  content_tag :li, role: :menuitem, class: link_wrapper_classes do
         | 
| 30 30 | 
             
                    output = if url == "#"
         | 
| 31 31 | 
             
                               [content_tag(:span, composed_label, class: "sidebar-menu__item-disabled")]
         | 
| 32 32 | 
             
                             else
         | 
| @@ -56,13 +56,24 @@ module Decidim | |
| 56 56 |  | 
| 57 57 | 
             
                    condition = if klass.include?(Decidim::HasPrivateUsers)
         | 
| 58 58 | 
             
                                  Arel.sql(
         | 
| 59 | 
            -
                                     | 
| 60 | 
            -
                                       | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 59 | 
            +
                                    <<~SQL.squish
         | 
| 60 | 
            +
                                      (
         | 
| 61 | 
            +
                                        decidim_action_logs.participatory_space_type = '#{manifest.model_class_name}' AND#{" "}
         | 
| 62 | 
            +
                                        decidim_action_logs.participatory_space_id IN (#{Arel.sql(klass.visible_for(current_user).select(:id).to_sql)})
         | 
| 63 | 
            +
                                      ) OR#{" "}
         | 
| 64 | 
            +
                                      (
         | 
| 65 | 
            +
                                        decidim_action_logs.resource_type = '#{manifest.model_class_name}' AND#{" "}
         | 
| 66 | 
            +
                                        decidim_action_logs.resource_id IN (#{Arel.sql(klass.visible_for(current_user).select(:id).to_sql)})
         | 
| 67 | 
            +
                                      )
         | 
| 68 | 
            +
                                    SQL
         | 
| 63 69 | 
             
                                  ).to_s
         | 
| 64 70 | 
             
                                else
         | 
| 65 | 
            -
                                  Arel.sql( | 
| 71 | 
            +
                                  Arel.sql(
         | 
| 72 | 
            +
                                    [
         | 
| 73 | 
            +
                                      "decidim_action_logs.resource_type = '#{manifest.model_class_name}'",
         | 
| 74 | 
            +
                                      "decidim_action_logs.participatory_space_type = '#{manifest.model_class_name}'"
         | 
| 75 | 
            +
                                    ].join(" OR ")
         | 
| 76 | 
            +
                                  ).to_s
         | 
| 66 77 | 
             
                                end
         | 
| 67 78 |  | 
| 68 79 | 
             
                    conditions << "(#{condition})"
         | 
| @@ -13,7 +13,7 @@ | |
| 13 13 | 
             
                <%= form_required_explanation %>
         | 
| 14 14 | 
             
              </div>
         | 
| 15 15 |  | 
| 16 | 
            -
              <%= decidim_form_for(@form, namespace: "registration", as: resource_name, url: omniauth_registrations_path(resource_name)) do |f| %>
         | 
| 16 | 
            +
              <%= decidim_form_for(@form, namespace: "registration", as: resource_name, url: decidim.omniauth_registrations_path(resource_name)) do |f| %>
         | 
| 17 17 |  | 
| 18 18 | 
             
                <div class="form__wrapper">
         | 
| 19 19 | 
             
                  <%= f.text_field :name, help_text: t("decidim.devise.omniauth_registrations.new.username_help"), autocomplete: "name", placeholder: "John Doe" %>
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            <% add_decidim_page_title(t("decidim.offline.name")) %>
         | 
| 2 2 |  | 
| 3 | 
            -
            <main id="offline-fallback-html" class="text-center  | 
| 3 | 
            +
            <main id="offline-fallback-html" class="text-center my-8">
         | 
| 4 4 | 
             
              <div class="flex justify-center">
         | 
| 5 5 | 
             
                <svg xmlns="http://www.w3.org/2000/svg" width="5rem" height="r5em" viewBox="0 0 25 25">
         | 
| 6 6 | 
             
                  <path
         | 
| @@ -11,16 +11,16 @@ | |
| 11 11 |  | 
| 12 12 | 
             
              <div class="vertical-tabs">
         | 
| 13 13 | 
             
                <nav>
         | 
| 14 | 
            -
                  <button id="dropdown-trigger-pages" data-component="dropdown" data-target="dropdown-menu-pages" data-auto-close="true">
         | 
| 14 | 
            +
                  <button id="dropdown-trigger-pages" data-component="dropdown" data-target="dropdown-menu-pages" data-open-md="true" data-auto-close="true">
         | 
| 15 15 | 
             
                    <span>
         | 
| 16 16 | 
             
                      <%= translated_attribute(page.title) %>
         | 
| 17 17 | 
             
                    </span>
         | 
| 18 18 | 
             
                    <%= icon "arrow-down-s-line" %>
         | 
| 19 19 | 
             
                    <%= icon "arrow-up-s-line" %>
         | 
| 20 20 | 
             
                  </button>
         | 
| 21 | 
            -
                  <ul id="dropdown-menu-pages" class="vertical-tabs__list"  | 
| 21 | 
            +
                  <ul id="dropdown-menu-pages" class="vertical-tabs__list" role="menu">
         | 
| 22 22 | 
             
                    <% pages.each do |sibling| %>
         | 
| 23 | 
            -
                      <li class="<%= "is-active" if page == sibling %>">
         | 
| 23 | 
            +
                      <li class="<%= "is-active" if page == sibling %>" role="menuitem">
         | 
| 24 24 | 
             
                        <%= link_to translated_attribute(sibling.title), page_path(sibling.slug) %>
         | 
| 25 25 | 
             
                      </li>
         | 
| 26 26 | 
             
                    <% end %>
         | 
| @@ -4,7 +4,7 @@ | |
| 4 4 | 
             
            <% if filter_sections.present? || local_assigns.has_key?(:search_variable) %>
         | 
| 5 5 | 
             
              <%= filter_form_for filter, url_for, class: "new_filter self-stretch", data: { filters: "", component: "accordion" } do |form| %>
         | 
| 6 6 |  | 
| 7 | 
            -
                <button id="dropdown-trigger-filters" data-component="dropdown" data-target="dropdown-menu-filters">
         | 
| 7 | 
            +
                <button id="dropdown-trigger-filters" data-component="dropdown" data-target="dropdown-menu-filters" data-open-md="true">
         | 
| 8 8 | 
             
                  <%= icon "arrow-down-s-line" %>
         | 
| 9 9 | 
             
                  <%= icon "arrow-up-s-line" %>
         | 
| 10 10 | 
             
                  <span>
         | 
| @@ -12,14 +12,14 @@ | |
| 12 12 | 
             
                  </span>
         | 
| 13 13 | 
             
                </button>
         | 
| 14 14 |  | 
| 15 | 
            -
                <div id="dropdown-menu-filters" | 
| 15 | 
            +
                <div id="dropdown-menu-filters">
         | 
| 16 16 | 
             
                  <% if local_assigns.has_key?(:skip_to_id) %>
         | 
| 17 | 
            -
                    <%= link_to t("skip", scope: "decidim.shared.filter_form_help"), "##{skip_to_id}", class: "filter-skip" %>
         | 
| 17 | 
            +
                    <%= link_to t("skip", scope: "decidim.shared.filter_form_help"), "##{skip_to_id}", class: "filter-skip", role: "menuitem" %>
         | 
| 18 18 | 
             
                  <% end %>
         | 
| 19 | 
            -
                  <p class="filter-help"><%= t("help", scope: "decidim.shared.filter_form_help") %></p>
         | 
| 19 | 
            +
                  <p class="filter-help" role="menuitem" aria-disabled="true"><%= t("help", scope: "decidim.shared.filter_form_help") %></p>
         | 
| 20 20 |  | 
| 21 21 | 
             
                  <% if local_assigns.has_key?(:search_variable) %>
         | 
| 22 | 
            -
                    <div class="filter-search filter-container">
         | 
| 22 | 
            +
                    <div class="filter-search filter-container" role="menuitem">
         | 
| 23 23 | 
             
                      <%= form.search_field search_variable,
         | 
| 24 24 | 
             
                                            label: false,
         | 
| 25 25 | 
             
                                            placeholder: search_label,
         | 
| @@ -1,13 +1,14 @@ | |
| 1 | 
            -
            <button class="order-by__button" id="dropdown-trigger-order" data-component="dropdown" data-target="dropdown-menu-order" data-open=" | 
| 1 | 
            +
            <button class="order-by__button" id="dropdown-trigger-order" data-component="dropdown" data-target="dropdown-menu-order" data-open-md="true">
         | 
| 2 2 | 
             
              <%= icon "arrow-down-s-line" %>
         | 
| 3 3 | 
             
              <%= icon "arrow-up-s-line" %>
         | 
| 4 4 | 
             
              <span><%= t("#{i18n_scope}.label") %></span>
         | 
| 5 5 | 
             
            </button>
         | 
| 6 | 
            -
            <div id="dropdown-menu-order" class="order-by" | 
| 6 | 
            +
            <div id="dropdown-menu-order" class="order-by">
         | 
| 7 7 | 
             
              <% orders.each do |order_name| %>
         | 
| 8 8 | 
             
                <%= order_link order_name,
         | 
| 9 9 | 
             
                               i18n_scope:,
         | 
| 10 10 | 
             
                               title: t("#{i18n_scope}.label"),
         | 
| 11 | 
            +
                               role: :menuitem,
         | 
| 11 12 | 
             
                               class: "button button__sm button__text-secondary #{order_name == order ? "underline font-bold" : "font-normal"}" %>
         | 
| 12 13 | 
             
              <% end %>
         | 
| 13 14 | 
             
            </div>
         | 
| @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
         | 
| 4 4 | 
             
            <% available_locales.each do |locale| %>
         | 
| 5 | 
            -
              <link rel="alternate" href="<%=  | 
| 5 | 
            +
              <link rel="alternate" href="<%= current_url(request.parameters.merge(locale:)) %>" hreflang="<%= locale %>">
         | 
| 6 6 | 
             
            <% end %>
         | 
| 7 7 |  | 
| 8 8 | 
             
            <meta name="twitter:card" content="summary_large_image">
         | 
| @@ -10,12 +10,12 @@ | |
| 10 10 |  | 
| 11 11 | 
             
              <div class="vertical-tabs">
         | 
| 12 12 | 
             
                <nav aria-label="menu-vertical">
         | 
| 13 | 
            -
                  <button id="dropdown-trigger-profile" data-component="dropdown" data-target="dropdown-menu-profile">
         | 
| 13 | 
            +
                  <button id="dropdown-trigger-profile" data-open-md="true" data-component="dropdown" data-target="dropdown-menu-profile">
         | 
| 14 14 | 
             
                    <span><%= user_menu.active_item&.label || t("decidim.searches.filters.jump_to") %></span>
         | 
| 15 15 | 
             
                    <%= icon "arrow-down-s-line" %>
         | 
| 16 16 | 
             
                    <%= icon "arrow-up-s-line" %>
         | 
| 17 17 | 
             
                  </button>
         | 
| 18 | 
            -
                  <ul id="dropdown-menu-profile" class="vertical-tabs__list" | 
| 18 | 
            +
                  <ul id="dropdown-menu-profile" class="vertical-tabs__list">
         | 
| 19 19 | 
             
                    <%= user_menu.render %>
         | 
| 20 20 | 
             
                  </ul>
         | 
| 21 21 | 
             
                </nav>
         | 
    
        data/config/locales/ar.yml
    CHANGED
    
    
    
        data/config/locales/bg.yml
    CHANGED
    
    | @@ -923,7 +923,6 @@ bg: | |
| 923 923 | 
             
                      title_required: Заглавието е задължително!
         | 
| 924 924 | 
             
                      uploaded: Качено
         | 
| 925 925 | 
             
                      validating: Валидиране...
         | 
| 926 | 
            -
                      validation_error: Грешка при валидирането!
         | 
| 927 926 | 
             
                    select_file: Избор на файл
         | 
| 928 927 | 
             
                  upload_help:
         | 
| 929 928 | 
             
                    dropzone: Пуснете файлове тук или щракнете върху бутона за качване
         | 
    
        data/config/locales/ca.yml
    CHANGED
    
    | @@ -918,7 +918,7 @@ ca: | |
| 918 918 | 
             
                      title_required: Un títol és necessari!
         | 
| 919 919 | 
             
                      uploaded: Pujat
         | 
| 920 920 | 
             
                      validating: Validant...
         | 
| 921 | 
            -
                      validation_error: Error  | 
| 921 | 
            +
                      validation_error: Error en la validació! Si us plau, revisa que l'arxiu tingui una extensió o una mida permesa.
         | 
| 922 922 | 
             
                    select_file: Selecciona un fitxer
         | 
| 923 923 | 
             
                  upload_help:
         | 
| 924 924 | 
             
                    dropzone: Arrossega-hi els arxius o fes clic per a pujar-los
         | 
    
        data/config/locales/cs.yml
    CHANGED
    
    | @@ -961,7 +961,7 @@ cs: | |
| 961 961 | 
             
                      title_required: Název je povinný!
         | 
| 962 962 | 
             
                      uploaded: Nahráno
         | 
| 963 963 | 
             
                      validating: Probíhá ověřování...
         | 
| 964 | 
            -
                      validation_error: Chyba ověření!
         | 
| 964 | 
            +
                      validation_error: Chyba ověření! Zkontrolujte, zda má soubor povolenou příponu nebo velikost.
         | 
| 965 965 | 
             
                    select_file: Vyberte soubor
         | 
| 966 966 | 
             
                  upload_help:
         | 
| 967 967 | 
             
                    dropzone: Přetáhněte soubory sem nebo klikněte na tlačítko pro nahrání
         | 
    
        data/config/locales/de.yml
    CHANGED
    
    | @@ -923,7 +923,7 @@ de: | |
| 923 923 | 
             
                      title_required: Titel ist erforderlich!
         | 
| 924 924 | 
             
                      uploaded: Hochgeladen
         | 
| 925 925 | 
             
                      validating: Wird validiert...
         | 
| 926 | 
            -
                      validation_error: Validierungsfehler!
         | 
| 926 | 
            +
                      validation_error: Validierungsfehler! Überprüfen Sie, ob die Datei einen erlaubten Dateityp oder Größe hat.
         | 
| 927 927 | 
             
                    select_file: Datei auswählen
         | 
| 928 928 | 
             
                  upload_help:
         | 
| 929 929 | 
             
                    dropzone: Dateien hier ablegen oder die Schaltfläche zum Hochladen benutzen
         | 
| @@ -1468,7 +1468,7 @@ de: | |
| 1468 1468 | 
             
                    more: Mehr
         | 
| 1469 1469 | 
             
                    unfold: Entfalten
         | 
| 1470 1470 | 
             
                  filter_form_help:
         | 
| 1471 | 
            -
                    help:  | 
| 1471 | 
            +
                    help: Die Suchresultate werden bei geänderten Filtereinstellungen automatisch aktualisiert.
         | 
| 1472 1472 | 
             
                    skip: Zu Ergebnissen springen
         | 
| 1473 1473 | 
             
                  flag_modal:
         | 
| 1474 1474 | 
             
                    already_reported: Dieser Inhalt wurde bereits gemeldet und wird von einem Administrator überprüft.
         |