decidim-core 0.32.0.rc2 → 0.32.0
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/amendable/amend_button_card/show.erb +2 -2
- data/app/cells/decidim/author_cell.rb +0 -4
- data/app/cells/decidim/card_metadata_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/cta_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/hero_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/highlighted_content_banner_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/highlighted_content_banner_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/highlighted_participatory_spaces_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_hero_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/static_page/section_settings_form/show.erb +3 -1
- data/app/cells/decidim/content_blocks/static_page/summary_settings_form/show.erb +3 -1
- data/app/cells/decidim/content_blocks/static_page/two_pane_section_settings_form/show.erb +4 -2
- data/app/cells/decidim/resource_types_filter/show.erb +2 -2
- data/app/commands/decidim/destroy_account.rb +12 -1
- data/app/commands/decidim/multiple_attachments_methods.rb +28 -27
- data/app/controllers/concerns/decidim/devise_controllers.rb +9 -0
- data/app/events/decidim/amendable/amendment_base_event.rb +7 -1
- data/app/jobs/decidim/process_inactive_participant_job.rb +0 -7
- data/app/mailers/decidim/delete_user_mailer.rb +14 -0
- data/app/mailers/decidim/participants_account_mailer.rb +0 -16
- data/app/models/decidim/moderation.rb +1 -1
- data/app/models/decidim/participatory_space/member.rb +1 -1
- data/app/models/decidim/user_base_entity.rb +17 -2
- data/app/models/decidim/user_moderation.rb +1 -1
- data/app/packs/src/decidim/controllers/breadcrumb_truncate/breadcrumb_truncate.test.js +230 -0
- data/app/packs/src/decidim/controllers/breadcrumb_truncate/controller.js +172 -0
- data/app/packs/src/decidim/controllers/form_validator/form_validator.js +6 -6
- data/app/packs/src/decidim/controllers/form_validator/form_validator.test.js +23 -1
- data/app/packs/src/decidim/controllers/main_menu/controller.js +33 -0
- data/app/packs/src/decidim/controllers/main_menu/main_menu.test.js +77 -0
- data/app/packs/src/decidim/controllers/mention/controller.js +296 -140
- data/app/packs/src/decidim/controllers/mention/input_mentions.test.js +120 -457
- data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +68 -32
- data/app/packs/src/decidim/controllers/multiple_mentions/input_multiple_mentions.test.js +30 -23
- data/app/packs/src/decidim/direct_uploads/upload_field.js +4 -4
- data/app/packs/src/decidim/direct_uploads/upload_modal.js +11 -7
- data/app/packs/src/decidim/editor/common/suggestion.js +3 -1
- data/app/packs/src/decidim/editor/extensions/indent/index.js +9 -0
- data/app/packs/src/decidim/geocoding/reverse_geocoding.js +15 -5
- data/app/packs/src/decidim/geocoding/reverse_geocoding.test.js +197 -0
- data/app/packs/src/decidim/index.js +4 -3
- data/app/packs/src/decidim/sw/sw.js +1 -1
- data/app/packs/src/decidim/utilities/text.js +6 -6
- data/app/packs/stylesheets/decidim/_conversations.scss +14 -0
- data/app/packs/stylesheets/decidim/_dropdown.scss +1 -1
- data/app/packs/stylesheets/decidim/_editor_suggestions.scss +49 -0
- data/app/packs/stylesheets/decidim/_floating_help.scss +1 -1
- data/app/packs/stylesheets/decidim/_header.scss +41 -8
- data/app/packs/stylesheets/decidim/_tom_select.scss +23 -0
- data/app/packs/stylesheets/decidim/application.scss +2 -0
- data/app/packs/stylesheets/decidim/editor.scss +2 -33
- data/app/packs/stylesheets/decidim/geocoding_addons.scss +10 -2
- data/app/presenters/decidim/menu_item_presenter.rb +9 -3
- data/app/presenters/decidim/stats_presenter.rb +1 -1
- data/app/uploaders/decidim/image_uploader.rb +1 -1
- data/app/views/decidim/delete_user_mailer/delete.html.erb +6 -0
- data/app/views/decidim/gamification/badges/index.html.erb +1 -1
- data/app/views/decidim/homepage/show.html.erb +1 -1
- data/app/views/decidim/last_activities/index.html.erb +1 -1
- data/app/views/decidim/messaging/conversations/_error_modal.html.erb +11 -19
- data/app/views/decidim/messaging/conversations/error.js.erb +12 -7
- data/app/views/decidim/newsletters/unsubscribe.html.erb +1 -1
- data/app/views/decidim/offline/show.html.erb +1 -1
- data/app/views/decidim/pages/index.html.erb +1 -1
- data/app/views/decidim/profiles/show.html.erb +1 -1
- data/app/views/decidim/shared/_resource_actions.html.erb +4 -4
- data/app/views/decidim/user_activities/index.html.erb +1 -1
- data/app/views/layouts/decidim/_wrapper.html.erb +2 -2
- data/app/views/layouts/decidim/header/_menu.html.erb +1 -1
- data/app/views/layouts/decidim/header/_menu_breadcrumb_desktop.html.erb +31 -4
- data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile.html.erb +18 -13
- data/app/views/layouts/decidim/shared/_layout_center.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_item.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_two_col.html.erb +1 -1
- data/config/initializers/decidim_locale_aware_named_route_helper.rb +16 -0
- data/config/locales/ar.yml +0 -25
- data/config/locales/bg.yml +0 -25
- data/config/locales/ca-IT.yml +46 -35
- data/config/locales/ca.yml +46 -35
- data/config/locales/cs.yml +12 -34
- data/config/locales/de.yml +5 -46
- data/config/locales/el.yml +0 -22
- data/config/locales/en.yml +45 -34
- data/config/locales/es-MX.yml +45 -34
- data/config/locales/es-PY.yml +45 -34
- data/config/locales/es.yml +45 -34
- data/config/locales/eu.yml +85 -74
- data/config/locales/fi-plain.yml +46 -34
- data/config/locales/fi.yml +46 -34
- data/config/locales/fr-CA.yml +24 -30
- data/config/locales/fr.yml +24 -30
- data/config/locales/gl.yml +0 -22
- data/config/locales/hu.yml +0 -22
- data/config/locales/id-ID.yml +0 -21
- data/config/locales/is-IS.yml +0 -6
- data/config/locales/it.yml +1 -20
- data/config/locales/ja.yml +55 -44
- data/config/locales/lb.yml +0 -23
- data/config/locales/lt.yml +0 -24
- data/config/locales/lv.yml +0 -21
- data/config/locales/nl.yml +0 -23
- data/config/locales/no.yml +0 -23
- data/config/locales/pl.yml +2 -27
- data/config/locales/pt-BR.yml +2 -40
- data/config/locales/pt.yml +0 -23
- data/config/locales/ro-RO.yml +1 -32
- data/config/locales/ru.yml +0 -8
- data/config/locales/sk.yml +1 -43
- data/config/locales/sv.yml +6 -40
- data/config/locales/tr-TR.yml +0 -24
- data/config/locales/uk.yml +0 -6
- data/config/locales/zh-CN.yml +0 -21
- data/config/locales/zh-TW.yml +0 -24
- data/decidim-core.gemspec +1 -1
- data/lib/decidim/api/functions/user_entity_list.rb +2 -0
- data/lib/decidim/attachment_attributes.rb +58 -9
- data/lib/decidim/command.rb +1 -1
- data/lib/decidim/content_renderers/base_renderer.rb +112 -0
- data/lib/decidim/content_renderers/blob_renderer.rb +4 -7
- data/lib/decidim/content_renderers/mention_resource_renderer.rb +10 -6
- data/lib/decidim/content_renderers/resource_renderer.rb +16 -7
- data/lib/decidim/content_renderers/user_renderer.rb +11 -9
- data/lib/decidim/core/content_blocks/registry_manager.rb +4 -4
- data/lib/decidim/core/engine.rb +8 -0
- data/lib/decidim/core/test/factories.rb +3 -0
- data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +10 -10
- data/lib/decidim/core/test/shared_examples/amendable/amendment_event_examples.rb +2 -2
- data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +3 -3
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +6 -6
- data/lib/decidim/core/test/shared_examples/has_space_in_mcell_examples.rb +2 -1
- data/lib/decidim/core/test/shared_examples/resource_liked_event_examples.rb +3 -3
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/map/autocomplete.rb +4 -3
- data/lib/decidim/searchable.rb +5 -0
- data/lib/decidim/view_model.rb +1 -1
- data/lib/tasks/decidim_mailers_tasks.rake +31 -9
- metadata +13 -9
- data/app/commands/decidim/gallery_methods.rb +0 -107
- data/app/packs/src/decidim/vendor/tribute.js +0 -1890
- data/app/packs/stylesheets/decidim/_tribute.scss +0 -36
- data/app/views/decidim/participants_account_mailer/removal_notification.html.erb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50d3683adf392d4b4b2900c05d390051d922759af5aca3a012a0e2d6b1db501a
|
|
4
|
+
data.tar.gz: 6c44f0977cb804af860603ede5ad244515e6cc92d57071cfe594f687ee1c2a96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '097e493989f0f1cdc7d260ba099d36810cb0f85266165a122a39aab7a7befe35560b6efa701c7672541c9016c06cacf455521a9e927fcc1e791bc9caec2d8bcf'
|
|
7
|
+
data.tar.gz: f9df0bd52556860de3d48910823fa559a6a0614eb6aaf7cd335c7e9362659a0ccdcc6be27f84f90d20da6e08f8fb6d6935132bafca33ffaf8d2053f25a015461
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<li role="
|
|
2
|
-
<%= action_authorized_link_to :amend, new_amend_path, resource: model, data: { "redirect_url" => new_amend_path }, id: "amend-button", class: "dropdown__button" do %>
|
|
1
|
+
<li role="presentation" class="dropdown__item">
|
|
2
|
+
<%= action_authorized_link_to :amend, new_amend_path, resource: model, data: { "redirect_url" => new_amend_path }, id: "amend-button", class: "dropdown__button", role: "menuitem" do %>
|
|
3
3
|
<span><%= t("button", scope: "decidim.amendments.amendable", model_name: nil) %></span>
|
|
4
4
|
<%= icon "pencil-line" %>
|
|
5
5
|
<% end %>
|
|
@@ -29,7 +29,7 @@ module Decidim
|
|
|
29
29
|
return unless show_space?
|
|
30
30
|
|
|
31
31
|
{
|
|
32
|
-
text:
|
|
32
|
+
text: translated_attribute(participatory_space.title),
|
|
33
33
|
icon: resource_type_icon_key(participatory_space.class),
|
|
34
34
|
url: Decidim::ResourceLocatorPresenter.new(participatory_space).path
|
|
35
35
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
<%= settings_fields.translated :text_field, :description, label: t("decidim.content_blocks.cta_settings_form.description") %>
|
|
5
5
|
<% end %>
|
|
6
6
|
|
|
7
|
-
<% form.fields_for :images,
|
|
7
|
+
<% form.fields_for :images, content_block.images_container do |images_fields| %>
|
|
8
8
|
<%= images_fields.upload :background_image, label: t("decidim.content_blocks.cta_settings_form.background_image") %>
|
|
9
9
|
<% end %>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
</div>
|
|
15
15
|
<% end %>
|
|
16
16
|
|
|
17
|
-
<% form.fields_for :images,
|
|
17
|
+
<% form.fields_for :images, content_block.images_container do |images_fields| %>
|
|
18
18
|
<div class="row column">
|
|
19
19
|
<%= images_fields.upload :background_image, label: t(".background_image"), button_class: "button button__sm button__transparent-secondary" %>
|
|
20
20
|
</div>
|
|
@@ -32,7 +32,7 @@ module Decidim
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
34
|
def render?
|
|
35
|
-
required_keys = [:title, :
|
|
35
|
+
required_keys = [:title, :action_button_title, :action_button_subtitle, :action_button_url]
|
|
36
36
|
required_keys.all? { |key| model.settings.public_send(key).present? }
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
</div>
|
|
21
21
|
<% end %>
|
|
22
22
|
|
|
23
|
-
<% form.fields_for :images,
|
|
23
|
+
<% form.fields_for :images, content_block.images_container do |images_fields| %>
|
|
24
24
|
<div class="row column">
|
|
25
25
|
<%= images_fields.upload :background_image, label: t(".background_image"), button_class: "button button__sm button__transparent-secondary" %>
|
|
26
26
|
</div>
|
|
@@ -28,7 +28,7 @@ module Decidim
|
|
|
28
28
|
private
|
|
29
29
|
|
|
30
30
|
def cache_hash
|
|
31
|
-
[I18n.locale, highlighted_spaces.map(&:cache_key_with_version)].join(Decidim.cache_key_separator)
|
|
31
|
+
[I18n.locale, model.cache_key_with_version, highlighted_spaces.map(&:cache_key_with_version)].join(Decidim.cache_key_separator)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def section_class
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
<% end %>
|
|
10
10
|
|
|
11
|
-
<% form.fields_for :images,
|
|
11
|
+
<% form.fields_for :images, content_block.images_container do |images_fields| %>
|
|
12
12
|
<div class="row column">
|
|
13
13
|
<%= images_fields.upload :background_image, label: t("decidim.content_blocks.cta_settings_form.background_image"), button_class: "button button__sm button__transparent-secondary" %>
|
|
14
14
|
</div>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<% form.fields_for :settings, form.object.settings do |settings_fields| %>
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
<div class="row column">
|
|
3
|
+
<%= settings_fields.translated :editor, :left_column, label: label_left_column %>
|
|
4
|
+
<%= settings_fields.translated :editor, :right_column, label: label_right_column %>
|
|
5
|
+
</div>
|
|
4
6
|
<% end %>
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
</button>
|
|
11
11
|
<ul id="dropdown-menu-resource">
|
|
12
12
|
<% resource_types.each do |resource_type| %>
|
|
13
|
-
<li role="
|
|
14
|
-
<%= link_to filter_url(resource_type[0]), class: "filter#{" is-active" if filter_param == resource_type[0]}" do %>
|
|
13
|
+
<li role="presentation">
|
|
14
|
+
<%= link_to filter_url(resource_type[0]), class: "filter#{" is-active" if filter_param == resource_type[0]}", role: "menuitem" do %>
|
|
15
15
|
<span class="sr-only"><%= resource_type[1] %></span>
|
|
16
16
|
<%= text_with_resource_icon(*resource_type) %>
|
|
17
17
|
<% end %>
|
|
@@ -15,7 +15,7 @@ module Decidim
|
|
|
15
15
|
def call
|
|
16
16
|
return broadcast(:invalid) unless @form.valid?
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
with_events(with_transaction: true) do
|
|
19
19
|
destroy_user_account!
|
|
20
20
|
destroy_user_identities
|
|
21
21
|
destroy_follows
|
|
@@ -110,5 +110,16 @@ module Decidim
|
|
|
110
110
|
space_manifest.invoke_on_destroy_account(current_user)
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
|
+
|
|
114
|
+
# We use memoization in this particular email, as we want to have the data available before the actual anonymization
|
|
115
|
+
def event_arguments
|
|
116
|
+
@event_arguments ||= {
|
|
117
|
+
user_id: current_user.id,
|
|
118
|
+
user_email: current_user.email,
|
|
119
|
+
user_name: current_user.name,
|
|
120
|
+
locale: current_user.locale,
|
|
121
|
+
organization: current_user.organization
|
|
122
|
+
}
|
|
123
|
+
end
|
|
113
124
|
end
|
|
114
125
|
end
|
|
@@ -5,16 +5,16 @@ module Decidim
|
|
|
5
5
|
private
|
|
6
6
|
|
|
7
7
|
def build_attachments
|
|
8
|
-
@
|
|
9
|
-
@form.
|
|
8
|
+
@attachments = []
|
|
9
|
+
@form.add_attachments.compact_blank.each do |attachment|
|
|
10
10
|
if attachment.is_a?(Hash) && attachment.has_key?(:id)
|
|
11
11
|
update_attachment_title_for(attachment)
|
|
12
12
|
next
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
@
|
|
15
|
+
@attachments << Attachment.new(
|
|
16
16
|
title: title_for(attachment),
|
|
17
|
-
attached_to: @attached_to ||
|
|
17
|
+
attached_to: @attached_to || attachments_attached_to,
|
|
18
18
|
file: signed_id_for(attachment),
|
|
19
19
|
content_type: content_type_for(attachment)
|
|
20
20
|
)
|
|
@@ -26,11 +26,11 @@ module Decidim
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def attachments_invalid?
|
|
29
|
-
@
|
|
30
|
-
next if
|
|
29
|
+
@attachments.each do |attachment|
|
|
30
|
+
next if attachment.valid? || !attachment.errors.has_key?(:file)
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
@form.errors.add(:
|
|
32
|
+
attachment.errors[:file].each do |error|
|
|
33
|
+
@form.errors.add(:add_attachments, error)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
return true
|
|
@@ -41,37 +41,38 @@ module Decidim
|
|
|
41
41
|
|
|
42
42
|
def create_attachments(first_weight: 0)
|
|
43
43
|
weight = first_weight
|
|
44
|
-
# Add the weights first to the old
|
|
45
|
-
|
|
46
|
-
Decidim::Attachment.where(id:
|
|
47
|
-
|
|
44
|
+
# Add the weights first to the old attachments
|
|
45
|
+
attachment_ids = keep_ids
|
|
46
|
+
Decidim::Attachment.where(id: attachment_ids).each do |attachment|
|
|
47
|
+
attachment.update!(weight:)
|
|
48
48
|
weight += 1
|
|
49
49
|
end
|
|
50
|
-
@
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
@attachments.map! do |attachment|
|
|
51
|
+
attachment.weight = weight
|
|
52
|
+
attachment.attached_to = attachments_attached_to
|
|
53
|
+
attachment.save!
|
|
54
54
|
weight += 1
|
|
55
|
-
@form.
|
|
55
|
+
@form.attachments << attachment
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
def
|
|
60
|
-
|
|
59
|
+
def attachment_cleanup!(include_all_attachments: false)
|
|
60
|
+
attachments = include_all_attachments ? attachments_attached_to.attachments.with_attached_file : attachments_attached_to.attachments
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
attachments.each do |attachment|
|
|
63
|
+
attachment.destroy! unless keep_ids.include?(attachment.id)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
attachments_attached_to.reload
|
|
67
|
+
attachments_attached_to.instance_variable_set(:@attachments, nil)
|
|
68
|
+
attachments_attached_to.instance_variable_set(:@photos, nil)
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
def process_attachments?
|
|
71
|
-
@form.
|
|
72
|
+
@form.add_attachments.any?
|
|
72
73
|
end
|
|
73
74
|
|
|
74
|
-
def
|
|
75
|
+
def attachments_attached_to
|
|
75
76
|
return @attached_to if @attached_to.present?
|
|
76
77
|
return form.current_organization if form.respond_to?(:current_organization)
|
|
77
78
|
|
|
@@ -101,8 +102,8 @@ module Decidim
|
|
|
101
102
|
end
|
|
102
103
|
|
|
103
104
|
def keep_ids
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
attachments_array = Array(@form.attachments)
|
|
106
|
+
attachments_array.map do |doc|
|
|
106
107
|
case doc
|
|
107
108
|
when Decidim::Attachment
|
|
108
109
|
doc.id
|
|
@@ -41,6 +41,10 @@ module Decidim
|
|
|
41
41
|
|
|
42
42
|
layout "layouts/decidim/application"
|
|
43
43
|
|
|
44
|
+
# Ensure locale is set before Devise's own prepended callbacks and reset
|
|
45
|
+
# after the request finishes.
|
|
46
|
+
prepend_around_action :set_current_locale
|
|
47
|
+
|
|
44
48
|
# Saves the location before loading each page so we can return to the
|
|
45
49
|
# right page.
|
|
46
50
|
before_action :store_current_location
|
|
@@ -58,6 +62,11 @@ module Decidim
|
|
|
58
62
|
|
|
59
63
|
store_location_for(:user, redirect_url)
|
|
60
64
|
end
|
|
65
|
+
|
|
66
|
+
def set_current_locale(&)
|
|
67
|
+
locale = Decidim::LocaleRouterDetector.new(request, params).locale
|
|
68
|
+
I18n.with_locale(locale, &)
|
|
69
|
+
end
|
|
61
70
|
end
|
|
62
71
|
end
|
|
63
72
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Decidim::Amendable
|
|
4
4
|
class AmendmentBaseEvent < Decidim::Events::SimpleEvent
|
|
5
5
|
i18n_attributes :amendable_path, :amendable_type, :amendable_title,
|
|
6
|
-
:emendation_path, :emendation_author_nickname, :emendation_author_path
|
|
6
|
+
:emendation_path, :emendation_author_nickname, :emendation_author_name, :emendation_author_path
|
|
7
7
|
|
|
8
8
|
def amendable_title
|
|
9
9
|
@amendable_title ||= translated_attribute(amendable_resource.title)
|
|
@@ -33,6 +33,12 @@ module Decidim::Amendable
|
|
|
33
33
|
@emendation_author_nickname ||= emendation_author.nickname
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def emendation_author_name
|
|
37
|
+
return unless emendation_resource
|
|
38
|
+
|
|
39
|
+
@emendation_author_name ||= emendation_author.name
|
|
40
|
+
end
|
|
41
|
+
|
|
36
42
|
def emendation_author_path
|
|
37
43
|
return unless emendation_resource
|
|
38
44
|
|
|
@@ -31,13 +31,6 @@ module Decidim
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def process_remove_user(user)
|
|
34
|
-
email = user.email
|
|
35
|
-
name = user.name
|
|
36
|
-
locale = user.locale
|
|
37
|
-
organization = user.organization
|
|
38
|
-
|
|
39
|
-
ParticipantsAccountMailer.removal_notification(email, name, locale, organization).deliver_later
|
|
40
|
-
|
|
41
34
|
Decidim::DestroyAccount.call(
|
|
42
35
|
Decidim::DeleteAccountForm.from_params(
|
|
43
36
|
delete_reason: I18n.t("decidim.account.destroy.inactive_account_removal_reason", inactivity_period: Decidim.delete_inactive_users_after_days)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
class DeleteUserMailer < ApplicationMailer
|
|
5
|
+
# This email is being sent when a user deletes his own account, or when the user was inactive for too long.
|
|
6
|
+
def delete(user_email:, user_name:, locale:, organization:)
|
|
7
|
+
I18n.with_locale(locale) do
|
|
8
|
+
@user_name = user_name
|
|
9
|
+
@organization = organization
|
|
10
|
+
mail(to: user_email, subject: I18n.t("decidim.delete_user_mailer.subject"))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -18,21 +18,5 @@ module Decidim
|
|
|
18
18
|
mail(to: user.email, subject:)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
# Notify user about account removal due to inactivity
|
|
23
|
-
def removal_notification(email, name, locale, organization)
|
|
24
|
-
@email = email
|
|
25
|
-
@user_name = name
|
|
26
|
-
@organization = organization
|
|
27
|
-
|
|
28
|
-
I18n.with_locale(locale) do
|
|
29
|
-
subject = I18n.t(
|
|
30
|
-
"decidim.participants_account_mailer.removal_notification.subject",
|
|
31
|
-
organization_name: organization_name(@organization)
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
mail(to: email, subject:)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
21
|
end
|
|
38
22
|
end
|
|
@@ -32,7 +32,7 @@ module Decidim
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def self.ransackable_attributes(_auth_object = nil)
|
|
35
|
-
%w(reported_id_string reported_content created_at)
|
|
35
|
+
%w(reported_id_string reported_content created_at report_count)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def self.ransackable_associations(_auth_object = nil)
|
|
@@ -42,7 +42,7 @@ module Decidim
|
|
|
42
42
|
def self.ransackable_attributes(auth_object = nil)
|
|
43
43
|
return [] unless auth_object&.admin?
|
|
44
44
|
|
|
45
|
-
%w(name nickname email invitation_accepted_at last_sign_in_at invitation_sent_at role)
|
|
45
|
+
%w(name nickname email invitation_accepted_at last_sign_in_at invitation_sent_at role published)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def self.ransackable_associations(_auth_object = nil)
|
|
@@ -69,12 +69,27 @@ module Decidim
|
|
|
69
69
|
Decidim::UserBaseEntity.joins(:follows).where(decidim_follows: { user: self }).blocked.exists?
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
ransacker :role do
|
|
73
|
+
Arel.sql(%{CASE WHEN "decidim_users"."admin" = true THEN 'admin' ELSE cast("decidim_users"."roles" as text) END})
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
ransacker :user_moderation_report_count do
|
|
77
|
+
query = <<~SQL.squish
|
|
78
|
+
(
|
|
79
|
+
SELECT COALESCE(MAX(decidim_user_moderations.report_count), 0)
|
|
80
|
+
FROM decidim_user_moderations
|
|
81
|
+
WHERE decidim_user_moderations.decidim_user_id = decidim_users.id
|
|
82
|
+
)
|
|
83
|
+
SQL
|
|
84
|
+
Arel.sql(query)
|
|
85
|
+
end
|
|
86
|
+
|
|
72
87
|
def self.ransackable_attributes(auth_object = nil)
|
|
73
|
-
base = %w(name email nickname last_sign_in_at)
|
|
88
|
+
base = %w(name email nickname last_sign_in_at created_at)
|
|
74
89
|
|
|
75
90
|
return base unless auth_object&.admin?
|
|
76
91
|
|
|
77
|
-
base + %w(invitation_sent_at invitation_accepted_at officialized_at)
|
|
92
|
+
base + %w(invitation_sent_at invitation_accepted_at officialized_at role user_moderation_report_count)
|
|
78
93
|
end
|
|
79
94
|
|
|
80
95
|
def self.ransackable_associations(_auth_object = nil)
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* global jest */
|
|
2
|
+
import { Application } from "@hotwired/stimulus"
|
|
3
|
+
import BreadcrumbTruncateController from "src/decidim/controllers/breadcrumb_truncate/controller"
|
|
4
|
+
|
|
5
|
+
describe("BreadcrumbTruncateController", () => {
|
|
6
|
+
let application = null
|
|
7
|
+
let controller = null
|
|
8
|
+
let element = null
|
|
9
|
+
let textElement = null
|
|
10
|
+
let resizeObserverMock = null
|
|
11
|
+
let originalResizeObserver = null
|
|
12
|
+
|
|
13
|
+
const startController = () => new Promise((resolve) => {
|
|
14
|
+
setTimeout(() => {
|
|
15
|
+
controller = application.getControllerForElementAndIdentifier(element, "breadcrumb-truncate")
|
|
16
|
+
resolve()
|
|
17
|
+
}, 0)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
originalResizeObserver = window.ResizeObserver
|
|
22
|
+
resizeObserverMock = {
|
|
23
|
+
observe: jest.fn(),
|
|
24
|
+
disconnect: jest.fn()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
window.ResizeObserver = jest.fn().mockImplementation(() => resizeObserverMock)
|
|
28
|
+
|
|
29
|
+
document.body.innerHTML = `
|
|
30
|
+
<span data-controller="breadcrumb-truncate" class="truncate">
|
|
31
|
+
<span data-breadcrumb-truncate-target="text">Very long participatory space title example</span>
|
|
32
|
+
</span>
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
application = Application.start()
|
|
36
|
+
application.register("breadcrumb-truncate", BreadcrumbTruncateController)
|
|
37
|
+
|
|
38
|
+
element = document.querySelector('[data-controller="breadcrumb-truncate"]')
|
|
39
|
+
textElement = element.querySelector('[data-breadcrumb-truncate-target="text"]')
|
|
40
|
+
|
|
41
|
+
return startController()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
application.stop()
|
|
46
|
+
document.body.innerHTML = ""
|
|
47
|
+
window.ResizeObserver = originalResizeObserver
|
|
48
|
+
jest.restoreAllMocks()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it("keeps the full text when it fits", () => {
|
|
52
|
+
Reflect.defineProperty(element, "clientWidth", { configurable: true, value: 100 })
|
|
53
|
+
Reflect.defineProperty(element, "scrollWidth", { configurable: true, get: () => 80 })
|
|
54
|
+
|
|
55
|
+
controller.refresh()
|
|
56
|
+
|
|
57
|
+
expect(textElement.textContent).toBe("Very long participatory space title example")
|
|
58
|
+
expect(element.hasAttribute("title")).toBe(false)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("truncates at the last fitting word and adds an ellipsis", () => {
|
|
62
|
+
Reflect.defineProperty(element, "clientWidth", { configurable: true, value: 100 })
|
|
63
|
+
Reflect.defineProperty(element, "scrollWidth", {
|
|
64
|
+
configurable: true,
|
|
65
|
+
get: () => (textElement.textContent.length > 16
|
|
66
|
+
? 120
|
|
67
|
+
: 80)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
controller.refresh()
|
|
71
|
+
|
|
72
|
+
expect(textElement.textContent).toBe("Very long...")
|
|
73
|
+
expect(element.getAttribute("title")).toBe("Very long participatory space title example")
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("falls back to character truncation for a single long word", () => {
|
|
77
|
+
textElement.textContent = "veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery"
|
|
78
|
+
controller.originalTexts = [textElement.textContent]
|
|
79
|
+
|
|
80
|
+
Reflect.defineProperty(element, "clientWidth", { configurable: true, value: 100 })
|
|
81
|
+
Reflect.defineProperty(element, "scrollWidth", {
|
|
82
|
+
configurable: true,
|
|
83
|
+
get: () => (textElement.textContent.length > 9
|
|
84
|
+
? 120
|
|
85
|
+
: 80)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
controller.refresh()
|
|
89
|
+
|
|
90
|
+
expect(textElement.textContent.endsWith("...")).toBe(true)
|
|
91
|
+
expect(textElement.textContent.includes(" ")).toBe(false)
|
|
92
|
+
expect(textElement.textContent.length).toBeGreaterThan(3)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it("disconnects the resize observer", () => {
|
|
96
|
+
controller.disconnect()
|
|
97
|
+
|
|
98
|
+
expect(resizeObserverMock.disconnect).toHaveBeenCalled()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("does nothing when no text target exists", async () => {
|
|
102
|
+
document.body.innerHTML = "<nav data-controller=\"breadcrumb-truncate\"></nav>"
|
|
103
|
+
|
|
104
|
+
application.stop()
|
|
105
|
+
application = Application.start()
|
|
106
|
+
application.register("breadcrumb-truncate", BreadcrumbTruncateController)
|
|
107
|
+
|
|
108
|
+
element = document.querySelector('[data-controller="breadcrumb-truncate"]')
|
|
109
|
+
|
|
110
|
+
await startController()
|
|
111
|
+
|
|
112
|
+
expect(() => controller.refresh()).not.toThrow()
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it("prioritizes truncating the widest item in group mode", async () => {
|
|
116
|
+
document.body.innerHTML = `
|
|
117
|
+
<nav data-controller="breadcrumb-truncate">
|
|
118
|
+
<span data-breadcrumb-truncate-target="item">
|
|
119
|
+
<span data-breadcrumb-truncate-target="text">Very long participatory space title example</span>
|
|
120
|
+
</span>
|
|
121
|
+
<span data-breadcrumb-truncate-target="item">
|
|
122
|
+
<span data-breadcrumb-truncate-target="text">Proposals</span>
|
|
123
|
+
</span>
|
|
124
|
+
</nav>
|
|
125
|
+
`
|
|
126
|
+
|
|
127
|
+
application.stop()
|
|
128
|
+
application = Application.start()
|
|
129
|
+
application.register("breadcrumb-truncate", BreadcrumbTruncateController)
|
|
130
|
+
|
|
131
|
+
element = document.querySelector('[data-controller="breadcrumb-truncate"]')
|
|
132
|
+
const itemElements = element.querySelectorAll('[data-breadcrumb-truncate-target="item"]')
|
|
133
|
+
const textElements = element.querySelectorAll('[data-breadcrumb-truncate-target="text"]')
|
|
134
|
+
const initialWidths = [150, 50]
|
|
135
|
+
|
|
136
|
+
Reflect.defineProperty(element, "clientWidth", { configurable: true, value: 100 })
|
|
137
|
+
Reflect.defineProperty(itemElements[0], "clientWidth", {
|
|
138
|
+
configurable: true,
|
|
139
|
+
get: () => Number.parseInt(itemElements[0].style.maxWidth || `${initialWidths[0]}`, 10)
|
|
140
|
+
})
|
|
141
|
+
Reflect.defineProperty(itemElements[1], "clientWidth", {
|
|
142
|
+
configurable: true,
|
|
143
|
+
get: () => Number.parseInt(itemElements[1].style.maxWidth || `${initialWidths[1]}`, 10)
|
|
144
|
+
})
|
|
145
|
+
Reflect.defineProperty(element, "scrollWidth", {
|
|
146
|
+
configurable: true,
|
|
147
|
+
get: () => Array.from(itemElements).reduce((sum, item) => sum + item.clientWidth, 0)
|
|
148
|
+
})
|
|
149
|
+
Reflect.defineProperty(itemElements[0], "scrollWidth", {
|
|
150
|
+
configurable: true,
|
|
151
|
+
get: () => (textElements[0].textContent.length > 16
|
|
152
|
+
? 120
|
|
153
|
+
: 80)
|
|
154
|
+
})
|
|
155
|
+
Reflect.defineProperty(itemElements[1], "scrollWidth", {
|
|
156
|
+
configurable: true,
|
|
157
|
+
get: () => 50
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
await startController()
|
|
161
|
+
controller.refresh()
|
|
162
|
+
|
|
163
|
+
expect(textElements[0].textContent).not.toBe("Very long participatory space title example")
|
|
164
|
+
expect(textElements[0].textContent.endsWith("...")).toBe(true)
|
|
165
|
+
expect(textElements[1].textContent).toBe("Proposals")
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it("preserves shorter deep breadcrumb labels while shrinking the longest one first", async () => {
|
|
169
|
+
document.body.innerHTML = `
|
|
170
|
+
<nav data-controller="breadcrumb-truncate">
|
|
171
|
+
<span data-breadcrumb-truncate-target="item">
|
|
172
|
+
<span data-breadcrumb-truncate-target="text">This is a very long title for a participatory process for a test</span>
|
|
173
|
+
</span>
|
|
174
|
+
<span data-breadcrumb-truncate-target="item">
|
|
175
|
+
<span data-breadcrumb-truncate-target="text">Debates</span>
|
|
176
|
+
</span>
|
|
177
|
+
<span data-breadcrumb-truncate-target="item">
|
|
178
|
+
<span data-breadcrumb-truncate-target="text">Debate made by a participant</span>
|
|
179
|
+
</span>
|
|
180
|
+
</nav>
|
|
181
|
+
`
|
|
182
|
+
|
|
183
|
+
application.stop()
|
|
184
|
+
application = Application.start()
|
|
185
|
+
application.register("breadcrumb-truncate", BreadcrumbTruncateController)
|
|
186
|
+
|
|
187
|
+
element = document.querySelector('[data-controller="breadcrumb-truncate"]')
|
|
188
|
+
const itemElements = element.querySelectorAll('[data-breadcrumb-truncate-target="item"]')
|
|
189
|
+
const textElements = element.querySelectorAll('[data-breadcrumb-truncate-target="text"]')
|
|
190
|
+
|
|
191
|
+
Reflect.defineProperty(element, "clientWidth", { configurable: true, value: 220 })
|
|
192
|
+
|
|
193
|
+
const initialWidths = [180, 56, 104]
|
|
194
|
+
|
|
195
|
+
itemElements.forEach((item, index) => {
|
|
196
|
+
Reflect.defineProperty(item, "clientWidth", {
|
|
197
|
+
configurable: true,
|
|
198
|
+
get: () => Number.parseInt(item.style.maxWidth || `${initialWidths[index]}`, 10)
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
Reflect.defineProperty(element, "scrollWidth", {
|
|
202
|
+
configurable: true,
|
|
203
|
+
get: () => Array.from(itemElements).reduce((sum, item) => sum + item.clientWidth, 0)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
Reflect.defineProperty(itemElements[0], "scrollWidth", {
|
|
207
|
+
configurable: true,
|
|
208
|
+
get: () => (textElements[0].textContent.length > 18
|
|
209
|
+
? 160
|
|
210
|
+
: 80)
|
|
211
|
+
})
|
|
212
|
+
Reflect.defineProperty(itemElements[1], "scrollWidth", {
|
|
213
|
+
configurable: true,
|
|
214
|
+
get: () => 56
|
|
215
|
+
})
|
|
216
|
+
Reflect.defineProperty(itemElements[2], "scrollWidth", {
|
|
217
|
+
configurable: true,
|
|
218
|
+
get: () => (textElements[2].textContent.length > 20
|
|
219
|
+
? 120
|
|
220
|
+
: 96)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
await startController()
|
|
224
|
+
controller.refresh()
|
|
225
|
+
|
|
226
|
+
expect(textElements[0].textContent.endsWith("...")).toBe(true)
|
|
227
|
+
expect(textElements[1].textContent).toBe("Debates")
|
|
228
|
+
expect(textElements[2].textContent).toBe("Debate made by a participant")
|
|
229
|
+
})
|
|
230
|
+
})
|