diff --git a/.erb_lint.yml b/.erb_lint.yml index c834bd806cad1..7ef66fda69fe9 100644 --- a/.erb_lint.yml +++ b/.erb_lint.yml @@ -124,7 +124,6 @@ linters: - author-data__extra - author-data__main - author__avatar--small - - author__badge - author__date - author__name--container - author__nickname diff --git a/.github/workflows/test_app.yml b/.github/workflows/test_app.yml index 0c348ce4fa6b7..0d1896456b719 100644 --- a/.github/workflows/test_app.yml +++ b/.github/workflows/test_app.yml @@ -58,7 +58,7 @@ jobs: DECIDIM_SPAM_DETECTION_BACKEND_USER: "memory" services: validator: - image: ghcr.io/validator/validator@sha256:28bd490412bb5abf1cf23eae0e3c197e9fbbda43cb9d23239b9ce859dc585db0 + image: ghcr.io/validator/validator:latest ports: ["8888:8888"] postgres: image: postgres:14 diff --git a/decidim-accountability/app/controllers/decidim/accountability/results_controller.rb b/decidim-accountability/app/controllers/decidim/accountability/results_controller.rb index 49bfae03532a3..aac085f0fdf1d 100644 --- a/decidim-accountability/app/controllers/decidim/accountability/results_controller.rb +++ b/decidim-accountability/app/controllers/decidim/accountability/results_controller.rb @@ -8,7 +8,7 @@ class ResultsController < Decidim::Accountability::ApplicationController helper Decidim::TraceabilityHelper helper Decidim::Accountability::BreadcrumbHelper - helper_method :results, :result, :first_class_taxonomies, :count_calculator + helper_method :results, :result, :count_calculator, :selected_root_taxonomy, :selected_taxonomy_children, :selected_taxonomy_grandchildren? before_action :set_controller_breadcrumb @@ -46,8 +46,24 @@ def default_filter_params } end - def first_class_taxonomies - @first_class_taxonomies ||= current_organization.taxonomies.where(parent_id: current_component.available_root_taxonomies, id: current_component.available_taxonomy_ids) + def selected_taxonomy_grandchildren? + @selected_taxonomy_grandchildren ||= selected_root_taxonomy.all_children.count > selected_taxonomy_children.count + end + + def selected_taxonomy_children + return [] if selected_root_taxonomy.blank? + + @selected_taxonomy_children ||= current_organization.taxonomies.where(parent_id: selected_root_taxonomy.id, id: current_component.available_taxonomy_ids) + end + + def selected_root_taxonomy + @selected_root_taxonomy ||= if params[:root_taxonomy_id] == "list" + nil + elsif params[:root_taxonomy_id].blank? + current_component.available_root_taxonomies.find_by(id: component_settings.default_taxonomy) + else + current_component.available_root_taxonomies.find_by(id: params[:root_taxonomy_id]) + end end def count_calculator(taxonomy_id) diff --git a/decidim-accountability/app/packs/stylesheets/accountability.scss b/decidim-accountability/app/packs/stylesheets/accountability.scss index 04bf71ec3f51e..72a96ffadeaa1 100644 --- a/decidim-accountability/app/packs/stylesheets/accountability.scss +++ b/decidim-accountability/app/packs/stylesheets/accountability.scss @@ -97,10 +97,16 @@ } &__grid { - @apply grid md:grid-cols-3 items-start gap-x-10 gap-y-8 md:gap-y-16; + @apply grid md:grid-cols-3 items-start gap-x-6 gap-y-6; - & > :nth-child(even) { - @apply grid md:col-span-2; + &--one-level { + @apply grid; + } + + &--two-levels { + & > :nth-child(even) { + @apply grid md:col-span-2; + } } /* display the titles only for the first two rows in desktop */ @@ -171,4 +177,28 @@ &__filters { @apply w-full space-y-10; } + + &__taxonomies { + @apply w-full; + + ul { + @apply flex flex-wrap gap-4 mt-4; + + li { + @apply bg-white py-1 px-4 rounded; + + a { + @apply text-secondary font-semibold text-sm; + } + + &.active { + @apply bg-secondary text-white; + + a { + @apply text-white; + } + } + } + } + } } diff --git a/decidim-accountability/app/views/decidim/accountability/results/_filters.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_filters.html.erb new file mode 100644 index 0000000000000..2bce0495b2cf6 --- /dev/null +++ b/decidim-accountability/app/views/decidim/accountability/results/_filters.html.erb @@ -0,0 +1,4 @@ +
+ <%= render partial: "search" %> + <%= render partial: "root_taxonomies_selector" %> +
diff --git a/decidim-accountability/app/views/decidim/accountability/results/_home_aside.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_home_aside.html.erb new file mode 100644 index 0000000000000..7061e2fd903f3 --- /dev/null +++ b/decidim-accountability/app/views/decidim/accountability/results/_home_aside.html.erb @@ -0,0 +1,12 @@ +

<%= component_name %>

+ +<% if component_settings.display_progress_enabled? %> + <%= cell( + "decidim/accountability/status", + nil, + title: t("decidim.accountability.results.home_header.global_status"), + progress: progress_calculator(nil).presence, + extra_classes: "accountability__status__home" + ) %> +<% end %> +<%= render partial: "filters" %> diff --git a/decidim-accountability/app/views/decidim/accountability/results/_home_taxonomies.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_home_taxonomies.html.erb index d162597d586e8..22b5a6cac87e1 100644 --- a/decidim-accountability/app/views/decidim/accountability/results/_home_taxonomies.html.erb +++ b/decidim-accountability/app/views/decidim/accountability/results/_home_taxonomies.html.erb @@ -1,32 +1,9 @@ -
- <% first_class_taxonomies.each do |taxonomy| %> - <% subelements = cell( - "decidim/accountability/status", - taxonomy, - extra_classes: "accountability__status__background", - url: results_path(filter: { taxonomies_part_of_contains: taxonomy }), - render_blank: true - ) %> +

+ <%= translated_attribute(selected_root_taxonomy.name) %> +

-
- <%= subelements.call %> -
- -
- <% if subelements.has_results? %> -
- <% taxonomy.children.where(id: current_component.available_taxonomy_ids).each do |sub_taxonomy| %> - <%= cell( - "decidim/accountability/status", - sub_taxonomy, - extra_classes: "accountability__status__border", - url: results_path(filter: { taxonomies_part_of_contains: sub_taxonomy }) - ) %> - <% end %> -
- <% else %> - <%= cell("decidim/announcement", t("no_results", scope: "decidim.accountability.results")) %> - <% end %> -
- <% end %> -
+<% if selected_taxonomy_grandchildren? %> + <%= render "two_levels_taxonomies" %> +<% else %> + <%= render "one_level_taxonomies" %> +<% end %> diff --git a/decidim-accountability/app/views/decidim/accountability/results/_one_level_taxonomies.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_one_level_taxonomies.html.erb new file mode 100644 index 0000000000000..bc6ddfcb757fd --- /dev/null +++ b/decidim-accountability/app/views/decidim/accountability/results/_one_level_taxonomies.html.erb @@ -0,0 +1,17 @@ +
+ <% selected_taxonomy_children.each do |taxonomy| %> + <% subelements = cell( + "decidim/accountability/status", + taxonomy, + extra_classes: "accountability__status__background", + url: results_path(filter: { taxonomies_part_of_contains: taxonomy }), + render_blank: true + ) %> + + <% if subelements.has_results? %> +
+ <%= subelements.call %> +
+ <% end %> + <% end %> +
diff --git a/decidim-accountability/app/views/decidim/accountability/results/_projects_aside.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_projects_aside.html.erb index 64a3faf678aeb..ebc9e1cca0534 100644 --- a/decidim-accountability/app/views/decidim/accountability/results/_projects_aside.html.erb +++ b/decidim-accountability/app/views/decidim/accountability/results/_projects_aside.html.erb @@ -37,6 +37,4 @@ <% end %> -
- <%= render partial: "search" %> -
+<%= render partial: "filters" %> diff --git a/decidim-accountability/app/views/decidim/accountability/results/_root_taxonomies_selector.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_root_taxonomies_selector.html.erb new file mode 100644 index 0000000000000..4f330606f8d33 --- /dev/null +++ b/decidim-accountability/app/views/decidim/accountability/results/_root_taxonomies_selector.html.erb @@ -0,0 +1,17 @@ +
+

+ <%= t("decidim.accountability.results.root_taxonomies.title") %> +

+ +
diff --git a/decidim-accountability/app/views/decidim/accountability/results/_two_levels_taxonomies.html.erb b/decidim-accountability/app/views/decidim/accountability/results/_two_levels_taxonomies.html.erb new file mode 100644 index 0000000000000..6ad41c1ecbf36 --- /dev/null +++ b/decidim-accountability/app/views/decidim/accountability/results/_two_levels_taxonomies.html.erb @@ -0,0 +1,30 @@ +
+ <% selected_taxonomy_children.each do |taxonomy| %> + <% subelements = cell( + "decidim/accountability/status", + taxonomy, + extra_classes: "accountability__status__background", + url: results_path(filter: { taxonomies_part_of_contains: taxonomy }), + render_blank: true + ) %> + + <% if subelements.has_results? %> +
+ <%= subelements.call %> +
+ +
+
+ <% taxonomy.children.where(id: current_component.available_taxonomy_ids).each do |sub_taxonomy| %> + <%= cell( + "decidim/accountability/status", + sub_taxonomy, + extra_classes: "accountability__status__border", + url: results_path(filter: { taxonomies_part_of_contains: sub_taxonomy }) + ) %> + <% end %> +
+
+ <% end %> + <% end %> +
diff --git a/decidim-accountability/app/views/decidim/accountability/results/home.html.erb b/decidim-accountability/app/views/decidim/accountability/results/home.html.erb index c7f4add0e9689..37ef6489b4107 100644 --- a/decidim-accountability/app/views/decidim/accountability/results/home.html.erb +++ b/decidim-accountability/app/views/decidim/accountability/results/home.html.erb @@ -8,24 +8,10 @@ <%= append_stylesheet_pack_tag "decidim_accountability" %> <% content_for :aside do %> -

<%= component_name %>

- - <% if component_settings.display_progress_enabled? %> - <%= cell( - "decidim/accountability/status", - nil, - title: t("decidim.accountability.results.home_header.global_status"), - progress: progress_calculator(nil).presence, - extra_classes: "accountability__status__home" - ) %> - <% end %> -
- <%= render partial: "search" %> -
+ <%= render partial: "home_aside" %> <% end %> <%= render layout: "layouts/decidim/shared/layout_two_col" do %> - <% if Decidim::Map.available?(:geocoding, :dynamic) && component_settings.geocoding_enabled? %>
<%= cell "decidim/map", @all_geocoded_results, metadata_card: "decidim/accountability/result_metadata" %> @@ -36,14 +22,14 @@
<%= decidim_sanitize_admin translated_attribute(component_settings.intro) %>
-
- <% if first_class_taxonomies.empty? %> - <%= cell("decidim/announcement", - params[:filter].present? ? - t("empty_filters", scope: "decidim.accountability.results.home") : - t("empty", scope: "decidim.accountability.results.home")) %> - <% end %> - <%= render partial: "home_taxonomies" %> -
- + <% if selected_root_taxonomy.present? %> +
+ <%= render partial: "home_taxonomies" %> +
+ <% else %> +
+ <%= cell "decidim/accountability/results", results %> + <%= decidim_paginate results, order_start_time: params[:order_start_time] %> +
+ <% end %> <% end %> diff --git a/decidim-accountability/config/locales/en.yml b/decidim-accountability/config/locales/en.yml index d9644ef97d3ad..382e679684acf 100644 --- a/decidim-accountability/config/locales/en.yml +++ b/decidim-accountability/config/locales/en.yml @@ -274,12 +274,11 @@ en: results_count: one: 1 result other: "%{count} results" - home: - empty: There are no results yet. - empty_filters: There are no results with this criteria. home_header: global_status: Global execution status no_results: There are no projects + root_taxonomies: + title: 'View by:' search: search: Search for actions show: @@ -304,6 +303,8 @@ en: clear_all: Clear all comments_enabled: Comments enabled comments_max_length: Comments max length (Leave 0 for default value) + default_taxonomy: Default taxonomy + default_taxonomy_help: Select which taxonomy you want to show by default. If no taxonomy is selected, the results will be shown in a list format. define_taxonomy_filters: Please define some filters for this participatory space before using this setting. display_progress_enabled: Display progress geocoding_enabled: Maps enabled @@ -313,6 +314,7 @@ en: taxonomy_filters_add: Add filter step: comments_blocked: Comments blocked + visualization: Visualization download_your_data: show: result_comments: Result comments export diff --git a/decidim-accountability/lib/decidim/accountability/component.rb b/decidim-accountability/lib/decidim/accountability/component.rb index 0bf15a8c1e077..e4c0dac6ab7d5 100644 --- a/decidim-accountability/lib/decidim/accountability/component.rb +++ b/decidim-accountability/lib/decidim/accountability/component.rb @@ -31,6 +31,9 @@ settings.attribute :intro, type: :text, translated: true, editor: true settings.attribute :display_progress_enabled, type: :boolean, default: true settings.attribute :geocoding_enabled, type: :boolean, default: false + settings.attribute :default_taxonomy, type: :select, include_blank: true, raw_choices: true, choices: lambda { |context| + context[:component].available_root_taxonomies.map { |taxonomy| [taxonomy.name["en"], taxonomy.id] } + } end component.register_stat :results_count, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, _start_at, _end_at| diff --git a/decidim-accountability/lib/decidim/accountability/engine.rb b/decidim-accountability/lib/decidim/accountability/engine.rb index 301a34f8bcbb1..0289e16114600 100644 --- a/decidim-accountability/lib/decidim/accountability/engine.rb +++ b/decidim-accountability/lib/decidim/accountability/engine.rb @@ -13,6 +13,10 @@ class Engine < ::Rails::Engine routes do resources :results, only: [:index, :show] do resources :versions, only: [:show] + + collection do + get :home + end end root to: "results#home" end diff --git a/decidim-accountability/spec/system/explore_results_spec.rb b/decidim-accountability/spec/system/explore_results_spec.rb index 7b1f2a5613419..d5b2971f6254d 100644 --- a/decidim-accountability/spec/system/explore_results_spec.rb +++ b/decidim-accountability/spec/system/explore_results_spec.rb @@ -29,14 +29,13 @@ let(:taxonomy_filter_ids) { [] } it "shows an empty page with a message" do - expect(page).to have_content "There are no results yet" + expect(page).to have_content "There are no projects" end end context "with a taxonomy" do it "shows an empty page with a message" do within "main" do - expect(page).to have_i18n_content(taxonomy.name) expect(page).to have_content "There are no projects" end end @@ -85,14 +84,10 @@ expect(page).to have_css(".accountability__map") end - it "shows taxonomies and sub_taxonomies with results for enabled filters" do - [taxonomy, sub_taxonomy].each do |item| - taxonomy_count = Decidim::Accountability::ResultsCalculator.new(component, item.id).count - expect(page).to have_content(translated(item.name)) if taxonomy_count.positive? + it "shows root taxonomies filters" do + within("aside") do + expect(page).to have_content(translated(taxonomy.parent.name)) end - - expect(page).to have_no_content(translated(other_taxonomy.name)) - expect(page).to have_no_content(translated(other_sub_taxonomy.name)) end it "shows progress" do diff --git a/decidim-admin/app/helpers/decidim/admin/settings_helper.rb b/decidim-admin/app/helpers/decidim/admin/settings_helper.rb index 71d9e32e0c0e2..7bd1b3e94e4cc 100644 --- a/decidim-admin/app/helpers/decidim/admin/settings_helper.rb +++ b/decidim-admin/app/helpers/decidim/admin/settings_helper.rb @@ -91,15 +91,23 @@ def render_field_form_method(form_method, form, attribute, name, i18n_scope, opt # @param name (see #settings_attribute_input) # @param i18n_scope (see #settings_attribute_input) # @param options (see #settings_attribute_input) - # @option :tabs_prefix (see #settings_attribute_input) - # @option :readonly (see #settings_attribute_input) + # @option options [String] :tabs_prefix (see #settings_attribute_input) + # @option options [Boolean] :readonly (see #settings_attribute_input) # @option options [String] :label The label that this field has # @option options [String] :help_text The help text shown after the input field # @return (see #settings_attribute_input) def render_select_form_field(form, attribute, name, i18n_scope, options) + choices = attribute.build_choices(component: @component).map do |o| + if attribute.raw_choices + o + else + [t("#{name}_options.#{o}", scope: i18n_scope), o] + end + end + html = form.select( name, - attribute.build_choices.map { |o| [t("#{name}_options.#{o}", scope: i18n_scope), o] }, + choices, { include_blank: attribute.include_blank, label: options[:label] } ) html << content_tag(:p, options[:help_text], class: "help-text") if options[:help_text] diff --git a/decidim-admin/config/locales/en.yml b/decidim-admin/config/locales/en.yml index 2a7f077fd91a1..2805010609462 100644 --- a/decidim-admin/config/locales/en.yml +++ b/decidim-admin/config/locales/en.yml @@ -53,6 +53,7 @@ en: customize_welcome_notification: Customize welcome notification default_locale: Default locale description: Description + enable_machine_translations: Enable machine translations enable_omnipresent_banner: Show omnipresent banner enable_participatory_space_filters: Enable participatory space filters facebook_handler: Facebook handler diff --git a/decidim-admin/spec/helpers/settings_helper_spec.rb b/decidim-admin/spec/helpers/settings_helper_spec.rb index 0477f5698a5a4..2010bb0ba5548 100644 --- a/decidim-admin/spec/helpers/settings_helper_spec.rb +++ b/decidim-admin/spec/helpers/settings_helper_spec.rb @@ -143,7 +143,7 @@ def render_input context "when choices is a lambda function" do let(:choices) do - -> { full_choices.map(&:last) } + ->(_context) { full_choices.map(&:last) } end it "is supported" do diff --git a/decidim-budgets/lib/decidim/budgets/component.rb b/decidim-budgets/lib/decidim/budgets/component.rb index 841e36543f546..3b3cc942b6cd1 100644 --- a/decidim-budgets/lib/decidim/budgets/component.rb +++ b/decidim-budgets/lib/decidim/budgets/component.rb @@ -76,7 +76,7 @@ component.settings(:global) do |settings| settings.attribute :taxonomy_filters, type: :taxonomy_filters - settings.attribute :workflow, type: :enum, default: "one", choices: -> { Decidim::Budgets.workflows.keys.map(&:to_s) } + settings.attribute :workflow, type: :enum, default: "one", choices: ->(_context) { Decidim::Budgets.workflows.keys.map(&:to_s) } settings.attribute :projects_per_page, type: :integer, default: 12 settings.attribute :vote_rule_threshold_percent_enabled, type: :boolean, default: true settings.attribute :vote_threshold_percent, type: :integer, default: 70 diff --git a/decidim-core/app/cells/decidim/author/badge.erb b/decidim-core/app/cells/decidim/author/badge.erb new file mode 100644 index 0000000000000..ca63f9f807c37 --- /dev/null +++ b/decidim-core/app/cells/decidim/author/badge.erb @@ -0,0 +1,6 @@ +<% if show_badge? %> + + <%= icon "star-s-fill" %> + <%= officialization_text %> + +<% end %> diff --git a/decidim-core/app/cells/decidim/author/show.erb b/decidim-core/app/cells/decidim/author/show.erb index 47c80b652c242..f17422a7119cc 100644 --- a/decidim-core/app/cells/decidim/author/show.erb +++ b/decidim-core/app/cells/decidim/author/show.erb @@ -6,7 +6,10 @@ <%= render :avatar %> - <%= render :name %> + + <%= render :name %> + <%= render :badge %> + <% context_actions.each do |action| %> <%= render action %> @@ -17,6 +20,7 @@ <% else %> <%= render :avatar %> <%= render :name %> + <%= render :badge %> <% end %> <% end %> diff --git a/decidim-core/app/cells/decidim/author_cell.rb b/decidim-core/app/cells/decidim/author_cell.rb index c55259cd44fb4..00dee59273cb7 100644 --- a/decidim-core/app/cells/decidim/author_cell.rb +++ b/decidim-core/app/cells/decidim/author_cell.rb @@ -186,5 +186,15 @@ def has_tooltip? model.has_tooltip? end + + def show_badge? + return false unless model.respond_to? :officialized? + + model.officialized? + end + + def officialization_text + translated_attribute(model.officialized_as).presence || t("decidim.profiles.show.officialized") + end end end diff --git a/decidim-core/app/cells/decidim/profile/avatar.erb b/decidim-core/app/cells/decidim/profile/avatar.erb index ebdf3c7da8746..48813df7d9888 100644 --- a/decidim-core/app/cells/decidim/profile/avatar.erb +++ b/decidim-core/app/cells/decidim/profile/avatar.erb @@ -2,6 +2,4 @@
<%= image_tag avatar_url, alt: t("decidim.author.avatar", name: decidim_sanitize(presented_profile.name)) %>
- - <%= render :badge if show_badge? %>
diff --git a/decidim-core/app/cells/decidim/profile/badge.erb b/decidim-core/app/cells/decidim/profile/badge.erb index 3d7e0adca73e2..c81bab5b81f72 100644 --- a/decidim-core/app/cells/decidim/profile/badge.erb +++ b/decidim-core/app/cells/decidim/profile/badge.erb @@ -1,4 +1,4 @@ -
+ <%= icon "star-s-fill" %> - <%= officialization_text %> -
+ <%= officialization_text %> + diff --git a/decidim-core/app/cells/decidim/profile/details.erb b/decidim-core/app/cells/decidim/profile/details.erb index 7172437ba559a..9b84d48b0b1aa 100644 --- a/decidim-core/app/cells/decidim/profile/details.erb +++ b/decidim-core/app/cells/decidim/profile/details.erb @@ -1,7 +1,8 @@
-

+

<%= presented_profile.name %> <%= tab_items.find { |tab_item| is_active_link?(tab_item[:path]) }&.dig(:text) %> (<%= presented_profile.name %>) + <%= render :badge if show_badge? %>

<% details_items.each do |detail| %> diff --git a/decidim-core/app/cells/decidim/user_activity_cell.rb b/decidim-core/app/cells/decidim/user_activity_cell.rb index 0ee51977d51a3..7f6d25f41f282 100644 --- a/decidim-core/app/cells/decidim/user_activity_cell.rb +++ b/decidim-core/app/cells/decidim/user_activity_cell.rb @@ -11,7 +11,12 @@ def show end def activities - context[:activities] + resource_ids_to_filter = context[:activities].select { |log| log[:action] == "delete" && log[:resource_type] == "Decidim::Comments::Comment" }.map(&:resource_id) + if resource_ids_to_filter.any? + context[:activities].where.not("resource_id in (?) AND resource_type = ?", resource_ids_to_filter, "Decidim::Comments::Comment") + else + context[:activities] + end end def resource_types diff --git a/decidim-core/app/forms/decidim/user_group_form.rb b/decidim-core/app/forms/decidim/user_group_form.rb index e29a26691d35b..add1de002af2b 100644 --- a/decidim-core/app/forms/decidim/user_group_form.rb +++ b/decidim-core/app/forms/decidim/user_group_form.rb @@ -16,10 +16,12 @@ class UserGroupForm < Form attribute :phone validates :name, presence: true + validates :name, format: { with: Decidim::UserBaseEntity::REGEXP_NAME } validates :email, presence: true, "valid_email_2/email": { disposable: true } validates :nickname, presence: true validates :nickname, length: { maximum: Decidim::User.nickname_max_length, allow_blank: true } + validates :nickname, format: { with: Decidim::UserBaseEntity::REGEXP_NICKNAME } validates :avatar, passthru: { to: Decidim::UserGroup } validate :unique_document_number diff --git a/decidim-core/app/packs/stylesheets/decidim/_author.scss b/decidim-core/app/packs/stylesheets/decidim/_author.scss index d58a346fe74fa..ad47a1b2e4e0c 100644 --- a/decidim-core/app/packs/stylesheets/decidim/_author.scss +++ b/decidim-core/app/packs/stylesheets/decidim/_author.scss @@ -33,6 +33,10 @@ @apply text-secondary font-semibold; } + &__badge svg { + @apply grid overflow-hidden place-items-center bg-primary rounded-full w-4 h-4 text-white fill-current; + } + &__metadata { @apply flex items-center gap-1 text-gray-2 text-sm; diff --git a/decidim-core/app/packs/stylesheets/decidim/_profile.scss b/decidim-core/app/packs/stylesheets/decidim/_profile.scss index 1a20e680ac393..530ac405e420f 100644 --- a/decidim-core/app/packs/stylesheets/decidim/_profile.scss +++ b/decidim-core/app/packs/stylesheets/decidim/_profile.scss @@ -9,18 +9,18 @@ &-container { @apply w-24 h-24 relative; } + } + + &__details { + @apply pb-3 space-y-2; &-badge { - @apply absolute top-full right-0 -translate-y-full grid place-items-center w-6 h-6 rounded-full overflow-hidden bg-primary border border-white; + @apply flex items-center gap-1 text-sm text-gray-2; svg { - @apply w-4 h-4 text-white fill-current; + @apply w-4 h-4 inline-block bg-primary rounded-full text-white fill-current; } } - } - - &__details { - @apply pb-3 space-y-2; &-data { @apply flex flex-wrap gap-x-6 gap-y-4; diff --git a/decidim-core/app/packs/stylesheets/decidim/editor.scss b/decidim-core/app/packs/stylesheets/decidim/editor.scss index 5035beaa02391..dcdb902403440 100644 --- a/decidim-core/app/packs/stylesheets/decidim/editor.scss +++ b/decidim-core/app/packs/stylesheets/decidim/editor.scss @@ -89,7 +89,9 @@ } .ProseMirror { - @apply relative p-2.5 outline-0 min-h-full prose max-w-none prose-headings:first:mt-0 prose-p:first:mt-0 prose-ul:first:mt-0 prose-ol:first:mt-0 prose-blockquote:first:mt-0 prose-pre:first:mt-0; + @apply relative p-2.5 outline-0 resize-y overflow-hidden prose max-w-none prose-headings:first:mt-0 prose-p:first:mt-0 prose-ul:first:mt-0 prose-ol:first:mt-0 prose-blockquote:first:mt-0 prose-pre:first:mt-0; + + min-height: inherit; &.ProseMirror-focused, &.dialog-open { diff --git a/decidim-core/app/validators/etiquette_validator.rb b/decidim-core/app/validators/etiquette_validator.rb index e298189b48f6e..16fdebb1dad4b 100644 --- a/decidim-core/app/validators/etiquette_validator.rb +++ b/decidim-core/app/validators/etiquette_validator.rb @@ -24,7 +24,7 @@ def clean_value(value) end def validate_caps(record, attribute, value) - number_of_caps = value.scan(/[A-Z]/).length + number_of_caps = value.scan(/[[:upper:]]/).length return if number_of_caps.zero? || number_of_caps < value.length / 2 # 50% record.errors.add(attribute, options[:message] || :too_much_caps) @@ -37,7 +37,7 @@ def validate_marks(record, attribute, value) end def validate_caps_first(record, attribute, value) - return if value.scan(/\A[a-z]{1}/).empty? + return if value.scan(/\A[[:lower:]]{1}/).empty? record.errors.add(attribute, options[:message] || :must_start_with_caps) end diff --git a/decidim-core/lib/decidim/form_builder.rb b/decidim-core/lib/decidim/form_builder.rb index 53cde5122a1e1..37b78ea251384 100644 --- a/decidim-core/lib/decidim/form_builder.rb +++ b/decidim-core/lib/decidim/form_builder.rb @@ -230,7 +230,7 @@ def editor(name, options = {}) disabled: options[:disabled], options: editor_options[:editor] } - ) { content_tag(:div, nil, class: "editor-input", style: "height: #{lines}rem") } + ) { content_tag(:div, nil, class: "editor-input", style: "min-height: #{lines}rem") } template += error_for(name, options) if error?(name) template += editor_upload(editor_image, editor_options[:upload]) template.html_safe diff --git a/decidim-core/lib/decidim/settings_manifest.rb b/decidim-core/lib/decidim/settings_manifest.rb index 3f32b8b7ab73d..028dbf143d4a5 100644 --- a/decidim-core/lib/decidim/settings_manifest.rb +++ b/decidim-core/lib/decidim/settings_manifest.rb @@ -129,6 +129,7 @@ class Attribute attribute :required_for_authorization, Boolean, default: false attribute :readonly attribute :choices + attribute :raw_choices, Boolean, default: false attribute :units attribute :include_blank, Boolean, default: false @@ -154,8 +155,8 @@ def default_value default || TYPES[type][:default] end - def build_choices - choices.try(:call) || choices + def build_choices(context = nil) + choices.try(:call, context) || choices end def build_units diff --git a/decidim-core/spec/cells/decidim/author_cell_spec.rb b/decidim-core/spec/cells/decidim/author_cell_spec.rb index 2496acd3b41fe..d45d38041ca99 100644 --- a/decidim-core/spec/cells/decidim/author_cell_spec.rb +++ b/decidim-core/spec/cells/decidim/author_cell_spec.rb @@ -8,7 +8,7 @@ controller Decidim::PagesController let(:my_cell) { cell("decidim/author", model) } - let!(:organization) { create(:organization) } + let!(:organization) { build(:organization) } let(:user) { create(:user, :confirmed, organization:) } let(:user_group) { create(:user_group, :verified) } let(:model) { Decidim::UserPresenter.new(user) } @@ -17,6 +17,14 @@ it "renders a User author card" do expect(subject).to have_css("[data-author]") end + + context "and when this user is officialized" do + let(:user) { create(:user, :confirmed, :officialized, organization:) } + + it "shows the officialization badge" do + expect(subject).to have_xpath("//svg/use[contains(@href, 'ri-star-s-fill')]") + end + end end context "when rendering a user group" do diff --git a/decidim-core/spec/cells/decidim/profile_cell_spec.rb b/decidim-core/spec/cells/decidim/profile_cell_spec.rb index 0c3703714b193..65768a668a4eb 100644 --- a/decidim-core/spec/cells/decidim/profile_cell_spec.rb +++ b/decidim-core/spec/cells/decidim/profile_cell_spec.rb @@ -6,9 +6,9 @@ controller Decidim::ProfilesController subject { my_cell.call } - let(:organization) { create(:organization, user_groups_enabled: true) } - let(:user) { create(:user, :managed, organization:, blocked: false) } - let(:context) { { content_cell: "decidim/user_conversations", conversations: [] } } + let(:organization) { build(:organization, user_groups_enabled: true) } + let(:user) { build(:user, :managed, organization:, blocked: false) } + let(:context) { { content_cell: "decidim/badges" } } let(:my_cell) { cell("decidim/profile", user, context:) } context "when show is rendered" do @@ -19,7 +19,7 @@ context "when the user displayed is blocked" do context "and is an admin" do - let(:user) { create(:user, :managed, organization:, blocked: true, admin: true) } + let(:user) { build(:user, :managed, organization:, blocked: true, admin: true) } it "shows the user profile" do expect(subject).to have_no_text("This profile is inaccessible due to terms of service violation!") @@ -27,11 +27,23 @@ end context "and is not an admin" do - let(:user) { create(:user, :managed, organization:, blocked: true, admin: false) } + let(:user) { build(:user, :managed, organization:, blocked: true, admin: false) } it "shows the inaccessible profile alert" do expect(subject).to have_text("This profile is inaccessible due to terms of service violation!") end end end + + context "when the user displayed is officialized" do + let(:user) { build(:user, :officialized, organization:) } + + it "shows the officialization badge" do + expect(subject).to have_xpath("//svg/use[contains(@href, 'ri-star-s-fill')]") + end + + it "shows the officialization name" do + expect(subject).to have_content(decidim_sanitize_translated(user.officialized_as)) + end + end end diff --git a/decidim-core/spec/cells/decidim/user_activity_cell_spec.rb b/decidim-core/spec/cells/decidim/user_activity_cell_spec.rb index 57116de1cbcaa..40265cf18496b 100644 --- a/decidim-core/spec/cells/decidim/user_activity_cell_spec.rb +++ b/decidim-core/spec/cells/decidim/user_activity_cell_spec.rb @@ -89,6 +89,33 @@ end end + context "when comment is deleted" do + let!(:logs) do + comments.first(14).map do |comment| + create( + :action_log, + action: "publish", + visibility: "all", + user: model, + resource: comment, + organization: component.organization, + participatory_space: component.participatory_space + ) + end + end + let!(:log_one) { create(:action_log, action: "create", visibility: "all", user: model, resource: comments.last, organization: component.organization, participatory_space: component.participatory_space) } + let!(:log_two) { create(:action_log, action: "delete", visibility: "all", user: model, resource: comments.last, organization: component.organization, participatory_space: component.participatory_space) } + + it "does not display the references to the comment on the first page if comment has been deleted" do + logs.last(2) do |log| + root_link = Decidim::ResourceLocatorPresenter.new(log.resource.root_commentable).path + comment_link = "#{root_link}?commentId=#{log.resource.id}#comment_#{log.resource.id}" + title = html_truncate(translated_attribute(log.resource.root_commentable.title), length: 80) + expect(subject).to have_no_link(title, href: comment_link) + end + end + end + context "when on the second page" do let(:current_page) { 2 } diff --git a/decidim-core/spec/lib/settings_manifest_spec.rb b/decidim-core/spec/lib/settings_manifest_spec.rb index a7671f3984ebd..596d8b991aa3a 100644 --- a/decidim-core/spec/lib/settings_manifest_spec.rb +++ b/decidim-core/spec/lib/settings_manifest_spec.rb @@ -91,7 +91,7 @@ module Decidim subject.attribute :something, choices: %w(a b c) expect(subject.attributes[:something].build_choices).to eq(%w(a b c)) - subject.attribute :something, choices: -> { %w(a b c) } + subject.attribute :something, choices: ->(_context) { %w(a b c) } expect(subject.attributes[:something].build_choices).to eq(%w(a b c)) end diff --git a/decidim-core/spec/system/user_group_creation_spec.rb b/decidim-core/spec/system/user_group_creation_spec.rb index 0c326d756e421..c049c795530e1 100644 --- a/decidim-core/spec/system/user_group_creation_spec.rb +++ b/decidim-core/spec/system/user_group_creation_spec.rb @@ -36,4 +36,40 @@ expect(page).to have_content(user.name) end end + + context "when nickname has invalid format" do + it "shows validation error in the form instead of raising exception" do + click_on "Create group" + + fill_in "Name", with: "Valid Group Name" + fill_in "Nickname", with: "Invalid Nickname" + fill_in "Email", with: "user_group@decidim.org" + fill_in "Document number", with: "12345678X" + fill_in "Phone", with: "12345678" + + click_on "Create group" + + expect(page).to have_content("There was a problem creating the group") + expect(page).to have_css("small.form-error.is-visible", text: "is invalid") + expect(page).to have_css(".is-invalid-input#group_nickname") + end + end + + context "when name has invalid format" do + it "shows validation error in the form instead of raising exception" do + click_on "Create group" + + fill_in "Name", with: "" + fill_in "Nickname", with: "valid_nickname" + fill_in "Email", with: "user_group@decidim.org" + fill_in "Document number", with: "12345678X" + fill_in "Phone", with: "12345678" + + click_on "Create group" + + expect(page).to have_content("There was a problem creating the group") + expect(page).to have_css("small.form-error.is-visible", text: "is invalid") + expect(page).to have_css(".is-invalid-input#group_name") + end + end end diff --git a/decidim-core/spec/validators/etiquette_validator_spec.rb b/decidim-core/spec/validators/etiquette_validator_spec.rb index 885620d65b25c..4c0091810bfc5 100644 --- a/decidim-core/spec/validators/etiquette_validator_spec.rb +++ b/decidim-core/spec/validators/etiquette_validator_spec.rb @@ -24,7 +24,8 @@ def self.model_name [ %(I am a very reasonable body, ain't I? I have the right length, the right style, the right words. Yup.), %("Validate bodies", they said. "It is gonna be fun!", they said.), - %(I contain special characters because I am à la mode.) + %(I contain special characters because I am à la mode.), + %(À la mode, I want to contain special characters.) ].each do |a_body| describe "like \"#{a_body}\"" do let(:body) { a_body } @@ -50,6 +51,12 @@ def self.model_name context "when etiquette_validator is enabled" do it { is_expected.to be_invalid } end + + context "when the text has non-ascii uppercase characters" do + let(:body) { "À ÑÓ ÂÊ" } + + it { is_expected.to be_invalid } + end end context "when the text has too many marks" do @@ -85,6 +92,12 @@ def self.model_name it { is_expected.to be_invalid } end + context "with non ascii characters" do + let(:body) { "à la mode, we start with a non-ascii character in downcase." } + + it { is_expected.to be_invalid } + end + context "with a multiple line body with the second line starting in downcase" do let(:body) { "This is a multiline body\nwith a line starting with downcase." } diff --git a/decidim-dev/lib/decidim/dev/common_rake.rb b/decidim-dev/lib/decidim/dev/common_rake.rb index 3350d811aa6cc..001edb5748e7e 100644 --- a/decidim-dev/lib/decidim/dev/common_rake.rb +++ b/decidim-dev/lib/decidim/dev/common_rake.rb @@ -3,7 +3,6 @@ require "bundler/gem_tasks" require "rspec/core/rake_task" require "decidim/dev" -require "parallel_tests/tasks" RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = "--format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml" if ENV["CI"] diff --git a/decidim-forms/config/locales/en.yml b/decidim-forms/config/locales/en.yml index 2fe8fd83dae29..e42bdf27a76ad 100644 --- a/decidim-forms/config/locales/en.yml +++ b/decidim-forms/config/locales/en.yml @@ -55,7 +55,7 @@ en: tos: Terms of service questionnaires: actions: - back: Back to questions + back: Back to responses publish_answers: Publish answers show: Show responses answer_option: diff --git a/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb b/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb index 2ddd3dd899731..ae4bcfccf86b5 100644 --- a/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb +++ b/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb @@ -32,7 +32,15 @@ <%= present(meeting).taxonomy_names.join(", ") %> - + "> + <% if meeting.published? %> + <%= t("admin.meetings.index.published", scope: "decidim.meetings") %> + <% else %> + <%= t("admin.meetings.index.unpublished", scope: "decidim.meetings") %> + <% end %> + + + "> <% if is_linked %> <%= t("index.linked_meeting_warning_html", href: edit_meeting_path(meeting), name: present(meeting).space_title, scope: "decidim.meetings.admin.meetings") %> <% else %> diff --git a/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meetings-thead.html.erb b/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meetings-thead.html.erb index c432644fa8446..eb7eb20d0a364 100644 --- a/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meetings-thead.html.erb +++ b/decidim-meetings/app/views/decidim/meetings/admin/meetings/_meetings-thead.html.erb @@ -21,6 +21,9 @@ <%= sort_link(query, :scope_name, t("models.meeting.fields.taxonomies", scope: "decidim.meetings") ) %> + + <%= t("models.meeting.fields.published", scope: "decidim.meetings") %> + <%= t("actions.title", scope: "decidim.meetings") %> diff --git a/decidim-meetings/config/locales/en.yml b/decidim-meetings/config/locales/en.yml index 63720c7282b3f..16f3aab6d4c56 100644 --- a/decidim-meetings/config/locales/en.yml +++ b/decidim-meetings/config/locales/en.yml @@ -365,7 +365,9 @@ en: select_an_iframe_access_level: Please select an iframe access level index: linked_meeting_warning_html: This meeting must be edited from
%{name} + published: Published title: Meetings + unpublished: Unpublished linked_spaces: assign: Assign link_a_space: Link a space @@ -637,6 +639,7 @@ en: id: ID map: Map official_meeting: Official meeting + published: Published start_time: Start date taxonomies: Taxonomies title: Title diff --git a/decidim-meetings/spec/system/admin/admin_manages_meetings_spec.rb b/decidim-meetings/spec/system/admin/admin_manages_meetings_spec.rb index e828626e0d1c5..efed7584da53e 100644 --- a/decidim-meetings/spec/system/admin/admin_manages_meetings_spec.rb +++ b/decidim-meetings/spec/system/admin/admin_manages_meetings_spec.rb @@ -48,7 +48,11 @@ accept_confirm { click_on "Unpublish" } end - expect(page).to have_admin_callout("successfully") + within "tr", text: Decidim::Meetings::MeetingPresenter.new(meeting).title do + expect(page).to have_content("Unpublished") + end + + expect(page).to have_admin_callout("Meeting successfully unpublished") within "tr", text: Decidim::Meetings::MeetingPresenter.new(meeting).title do expect(page).to have_css(".action-icon--publish") @@ -58,7 +62,11 @@ click_on "Publish" end - expect(page).to have_admin_callout("successfully") + within "tr", text: Decidim::Meetings::MeetingPresenter.new(meeting).title do + expect(page).to have_content("Published") + end + + expect(page).to have_admin_callout("Meeting successfully published") within "tr", text: Decidim::Meetings::MeetingPresenter.new(meeting).title do expect(page).to have_css(".action-icon--unpublish") diff --git a/decidim-participatory_processes/app/cells/decidim/participatory_processes/content_blocks/extra_data_cell.rb b/decidim-participatory_processes/app/cells/decidim/participatory_processes/content_blocks/extra_data_cell.rb index 0021c1b281120..c4e8f7d946f77 100644 --- a/decidim-participatory_processes/app/cells/decidim/participatory_processes/content_blocks/extra_data_cell.rb +++ b/decidim-participatory_processes/app/cells/decidim/participatory_processes/content_blocks/extra_data_cell.rb @@ -11,7 +11,7 @@ class ExtraDataCell < Decidim::ContentBlocks::ParticipatorySpaceExtraDataCell private def extra_data_items - [step_item, dates_item, group_item].compact + [dates_item, step_item, group_item].compact end def active_step_name diff --git a/decidim-proposals/app/commands/decidim/proposals/create_proposal.rb b/decidim-proposals/app/commands/decidim/proposals/create_proposal.rb index 76803e3a46ee1..f88f776cd89ee 100644 --- a/decidim-proposals/app/commands/decidim/proposals/create_proposal.rb +++ b/decidim-proposals/app/commands/decidim/proposals/create_proposal.rb @@ -84,7 +84,11 @@ def create_proposal proposal.taxonomizations = form.taxonomizations if form.taxonomizations.present? proposal.documents = form.documents if form.documents.present? - proposal.address = form.address if form.has_address? && !form.geocoded? + if form.geocoded? + proposal.latitude = form.latitude + proposal.longitude = form.longitude + end + proposal.address = form.address if form.has_address? proposal.add_coauthor(@current_user, user_group:) proposal.save! @attached_to = proposal diff --git a/decidim-proposals/lib/decidim/proposals/component.rb b/decidim-proposals/lib/decidim/proposals/component.rb index e76f16ef5e2b6..cad6e2e8c6c7d 100644 --- a/decidim-proposals/lib/decidim/proposals/component.rb +++ b/decidim-proposals/lib/decidim/proposals/component.rb @@ -34,12 +34,12 @@ settings.attribute :minimum_votes_per_user, type: :integer, default: 0, required: true settings.attribute :proposal_limit, type: :integer, default: 0, required: true settings.attribute :proposal_length, type: :integer, default: 500 - settings.attribute :proposal_edit_time, type: :enum, default: "limited", choices: -> { %w(infinite limited) } + settings.attribute :proposal_edit_time, type: :enum, default: "limited", choices: ->(_context) { %w(infinite limited) } settings.attribute :edit_time, type: :integer_with_units, default: [5, "minutes"], required: true, units: %w(minutes hours days) settings.attribute :threshold_per_proposal, type: :integer, default: 0, required: true settings.attribute :can_accumulate_votes_beyond_threshold, type: :boolean, default: false settings.attribute :proposal_answering_enabled, type: :boolean, default: true - settings.attribute :default_sort_order, type: :select, default: "automatic", choices: -> { POSSIBLE_SORT_ORDERS } + settings.attribute :default_sort_order, type: :select, default: "automatic", choices: ->(_context) { POSSIBLE_SORT_ORDERS } settings.attribute :official_proposals_enabled, type: :boolean, default: true settings.attribute :comments_enabled, type: :boolean, default: true settings.attribute :comments_max_length, type: :integer, required: true @@ -73,13 +73,13 @@ settings.attribute :proposal_answering_enabled, type: :boolean, default: true settings.attribute :publish_answers_immediately, type: :boolean, default: true settings.attribute :answers_with_costs, type: :boolean, default: false - settings.attribute :default_sort_order, type: :select, include_blank: true, choices: -> { POSSIBLE_SORT_ORDERS } + settings.attribute :default_sort_order, type: :select, include_blank: true, choices: ->(_context) { POSSIBLE_SORT_ORDERS } settings.attribute :amendment_creation_enabled, type: :boolean, default: true settings.attribute :amendment_reaction_enabled, type: :boolean, default: true settings.attribute :amendment_promotion_enabled, type: :boolean, default: true settings.attribute :amendments_visibility, type: :enum, default: "all", - choices: -> { Decidim.config.amendments_visibility_options } + choices: ->(_context) { Decidim.config.amendments_visibility_options } settings.attribute :announcement, type: :text, translated: true, editor: true settings.attribute :automatic_hashtags, type: :text, editor: false, required: false settings.attribute :suggested_hashtags, type: :text, editor: false, required: false diff --git a/decidim-proposals/spec/commands/decidim/proposals/create_proposal_spec.rb b/decidim-proposals/spec/commands/decidim/proposals/create_proposal_spec.rb index 3b2cbc5e01b51..3b5774fd00875 100644 --- a/decidim-proposals/spec/commands/decidim/proposals/create_proposal_spec.rb +++ b/decidim-proposals/spec/commands/decidim/proposals/create_proposal_spec.rb @@ -193,6 +193,28 @@ module Proposals end end end + + describe "when geocoding is enabled" do + let(:component) { create(:proposal_component, :with_geocoding_enabled) } + let(:form_params) do + { + title: "A reasonable proposal title", + body: "A reasonable proposal body", + address: "Barcelona", + latitude: 41.394897, + longitude: 2.153088 + } + end + + it "saves geocoding data" do + expect { command.call }.to broadcast(:ok) + proposal = Decidim::Proposals::Proposal.last + + expect(proposal.address).to eq(form_params[:address]) + expect(proposal.latitude).to eq(form_params[:latitude]) + expect(proposal.longitude).to eq(form_params[:longitude]) + end + end end end end diff --git a/decidim-surveys/app/views/decidim/surveys/admin/publish_answers/index.html.erb b/decidim-surveys/app/views/decidim/surveys/admin/publish_answers/index.html.erb index 0d320a474a665..3bc756ef83171 100644 --- a/decidim-surveys/app/views/decidim/surveys/admin/publish_answers/index.html.erb +++ b/decidim-surveys/app/views/decidim/surveys/admin/publish_answers/index.html.erb @@ -4,7 +4,7 @@

<%= t(".title") %> - <%= link_to t("actions.back", scope: "decidim.forms.admin.questionnaires"), questionnaire_url, class: "button button__sm button__secondary new" %> + <%= link_to t("actions.back", scope: "decidim.forms.admin.questionnaires"), survey_answers_path, class: "button button__sm button__secondary new" %>

diff --git a/docs/modules/customize/assets/images/content_security_policy.png b/docs/modules/customize/assets/images/content_security_policy.png new file mode 100644 index 0000000000000..ab67864b33335 Binary files /dev/null and b/docs/modules/customize/assets/images/content_security_policy.png differ diff --git a/docs/modules/customize/pages/content_security_policy.adoc b/docs/modules/customize/pages/content_security_policy.adoc index ec84b02988ff0..e5ce7becba6ef 100644 --- a/docs/modules/customize/pages/content_security_policy.adoc +++ b/docs/modules/customize/pages/content_security_policy.adoc @@ -19,14 +19,20 @@ By default, the CSP is enabled, and is configured to be as restrictive as possib In order to customize the CSP we are providing, have 2 options, either by using a configuration key the initializer `config/initializers/decidim.rb` or by setting values in the Organization's system admin. -Using the initializer is the recommended way to customize the CSP when you have multiple organizations sharing the same Decidim instance, sharing the same tools. Fox example, if you are using a custom map provider, you will need to add the domain to the CSP, so that the map can be displayed. In this case, you will need to add the following to your initializer: +We recommend using the System panel, as you will not need to be restarting the server when making changes. + +image::content_security_policy.png[Content Security Policy] + +On the other hand, the initializer is more comfortable when you have multiple organizations sharing the same Decidim instance, sharing the same tools. For example, if you are using a custom map provider, you will need to add the domain to the CSP, so that the map can be displayed. In this case, you will need to add the following to your initializer: [source,ruby] .... -config.content_security_policies_extra = { - "connect-src" => %w(https://*.example.com), - "img-src" => %w(https://*.example.com) -} +Decidim.configure do |config| + config.content_security_policies_extra = { + "connect-src" => %w(https://*.example.com), + "img-src" => %w(https://*.example.com) + } +end .... For specific organization setup, you could use the system panel to customize the Content Security Policies, by adding the domains or directives that you need to allow in the predesignated spots. diff --git a/docs/modules/develop/pages/components.adoc b/docs/modules/develop/pages/components.adoc index 805bb47035505..21167121ecc52 100644 --- a/docs/modules/develop/pages/components.adoc +++ b/docs/modules/develop/pages/components.adoc @@ -140,7 +140,7 @@ Decidim.register_component(:my_component) do |component| component.settings(:step) do |settings| settings.attribute :a_text_setting, type: :text, default: false, required: true, translated: true, editor: true - settings.attribute :a_lambda_enum_setting, type: :enum, default: "all", choices: -> { SomeClass.enum_options } + settings.attribute :a_lambda_enum_setting, type: :enum, default: "all", choices: ->(_context) { SomeClass.enum_options } settings.attribute :a_readonly_setting, type: :string, readonly: ->(context) { SomeClass.readonly?(context[:component]) } end diff --git a/docs/modules/services/pages/activestorage.adoc b/docs/modules/services/pages/activestorage.adoc index e895773282adc..6b75c6099d3c8 100644 --- a/docs/modules/services/pages/activestorage.adoc +++ b/docs/modules/services/pages/activestorage.adoc @@ -4,6 +4,11 @@ Decidim uses https://edgeguides.rubyonrails.org/active_storage_overview.html[Act In order to plug Decidim into a storage provider of your choice, you need to generate the Decidim application using the `--storage` modifier as explained at the xref:configure:index.adoc[configuration guide]. You will also need to provide the correct configuration options for the selected storage provider as explained at the xref:configure:environment_variables.adoc[environment variables guide]. +[NOTE] +==== +If you use any other provider than the default (`local`) you will need to also configure the xref:customize:content_security_policy.adoc[Content security policy]. For the directives "default-src", "img-src", "media-src", and "connect-src"`. +==== + == Dynamic file uploads Decidim allows participants to upload files dynamically from their browsers to the website using the upload modals where participants can drag and drop the files to or select them from their computer. With the default configuration using a local file storage, this feature does not require any additional configuration. With external storage providers, you have to configure those storages properly to support the client-side file uploads to Decidim.