decidim-core 0.31.0 → 0.31.2
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/announcement_cell.rb +10 -2
- data/app/cells/decidim/attachments_file_tab/show.erb +1 -1
- data/app/cells/decidim/card_g/show.erb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_metadata/content.erb +2 -2
- data/app/cells/decidim/nav_links/show.erb +3 -3
- data/app/cells/decidim/notification/deleted.erb +12 -0
- data/app/cells/decidim/notification/not_available.erb +12 -0
- data/app/cells/decidim/notification_cell.rb +5 -1
- data/app/cells/decidim/share_widget/modal.erb +1 -1
- data/app/cells/decidim/statistic/show.erb +6 -6
- data/app/cells/decidim/upload_modal/files.erb +12 -8
- data/app/cells/decidim/upload_modal_cell.rb +11 -4
- data/app/commands/decidim/destroy_account.rb +49 -4
- data/app/controllers/concerns/decidim/direct_upload.rb +2 -12
- data/app/controllers/decidim/devise/sessions_controller.rb +7 -0
- data/app/events/decidim/welcome_notification_event.rb +1 -1
- data/app/helpers/concerns/decidim/flash_helper_extensions.rb +2 -2
- data/app/jobs/decidim/export_participatory_space_job.rb +1 -1
- data/app/jobs/decidim/remove_search_indexes_job.rb +18 -0
- data/app/mailers/decidim/notifications_digest_mailer.rb +18 -1
- data/app/models/decidim/attachment.rb +22 -1
- data/app/models/decidim/component.rb +3 -0
- data/app/models/decidim/notification.rb +12 -0
- data/app/models/decidim/user.rb +4 -0
- data/app/models/decidim/user_base_entity.rb +1 -1
- data/app/packs/src/decidim/controllers/account_form/controller.js +8 -0
- data/app/packs/src/decidim/controllers/language_change/controller.js +38 -0
- data/app/packs/src/decidim/controllers/language_change/language_change.test.js +105 -0
- data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +25 -2
- data/app/packs/src/decidim/direct_uploads/upload_field.js +1 -1
- data/app/packs/src/decidim/editor/extensions/link/index.js +0 -1
- data/app/packs/src/decidim/editor/index.js +5 -1
- data/app/packs/src/decidim/editor/test/extensions/link.test.js +2 -2
- data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_link.js +1 -1
- data/app/packs/stylesheets/decidim/_conversations.scss +1 -1
- data/app/packs/stylesheets/decidim/_flash.scss +1 -1
- data/app/packs/stylesheets/decidim/_modal.scss +5 -1
- data/app/packs/stylesheets/decidim/_modal_fingerprint.scss +1 -1
- data/app/packs/stylesheets/decidim/_modal_update.scss +1 -1
- data/app/packs/stylesheets/decidim/_participatory_spaces.scss +1 -1
- data/app/presenters/decidim/log/user_presenter.rb +2 -1
- data/app/presenters/decidim/organization_presenter.rb +4 -0
- data/app/presenters/decidim/user_presenter.rb +6 -1
- data/app/services/decidim/open_data_exporter.rb +1 -1
- data/app/views/decidim/manifests/show.json.erb +1 -0
- data/app/views/decidim/pages/_tabbed.html.erb +1 -1
- data/app/views/decidim/shared/_orders.html.erb +1 -1
- data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +16 -2
- data/config/locales/ar.yml +0 -4
- data/config/locales/bg.yml +0 -8
- data/config/locales/ca-IT.yml +18 -11
- data/config/locales/ca.yml +18 -11
- data/config/locales/cs.yml +53 -16
- data/config/locales/de.yml +9 -20
- data/config/locales/el.yml +1 -3
- data/config/locales/en.yml +24 -17
- data/config/locales/es-MX.yml +17 -10
- data/config/locales/es-PY.yml +17 -10
- data/config/locales/es.yml +16 -9
- data/config/locales/eu.yml +49 -44
- data/config/locales/fa-IR.yml +3 -0
- data/config/locales/fi-plain.yml +11 -8
- data/config/locales/fi.yml +11 -8
- data/config/locales/fr-CA.yml +24 -11
- data/config/locales/fr.yml +25 -11
- data/config/locales/ga-IE.yml +0 -4
- data/config/locales/gl.yml +0 -3
- data/config/locales/hu.yml +0 -6
- data/config/locales/id-ID.yml +0 -3
- data/config/locales/it.yml +0 -3
- data/config/locales/ja.yml +28 -22
- data/config/locales/ko.yml +3 -0
- data/config/locales/lb.yml +0 -3
- data/config/locales/lt.yml +0 -5
- data/config/locales/lv.yml +0 -3
- data/config/locales/mt.yml +3 -0
- data/config/locales/nl.yml +0 -3
- data/config/locales/no.yml +0 -3
- data/config/locales/pl.yml +11 -7
- data/config/locales/pt-BR.yml +626 -5
- data/config/locales/pt.yml +0 -3
- data/config/locales/ro-RO.yml +483 -121
- data/config/locales/ru.yml +0 -3
- data/config/locales/sk.yml +0 -3
- data/config/locales/sv.yml +303 -47
- data/config/locales/tr-TR.yml +0 -5
- data/config/locales/uk.yml +0 -3
- data/config/locales/vi.yml +3 -0
- data/config/locales/zh-CN.yml +0 -3
- data/config/locales/zh-TW.yml +0 -5
- data/db/data/20251125144141_add_short_name_to_organizations.rb +35 -0
- data/db/migrate/20251031150928_add_short_name_to_organization.rb +7 -0
- data/decidim-core.gemspec +1 -0
- data/lib/decidim/core/seeds.rb +2 -2
- data/lib/decidim/core/test/factories.rb +25 -0
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +27 -1
- data/lib/decidim/core/test/shared_examples/fingerprint_examples.rb +13 -0
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/events/base_event.rb +4 -0
- data/lib/decidim/form_builder.rb +41 -7
- data/lib/decidim/has_private_users.rb +1 -0
- data/lib/decidim/seeds.rb +1 -1
- data/lib/tasks/decidim_procfile.rake +1 -1
- data/lib/tasks/upgrade/decidim_remove_deleted_users_left_data_tasks.rake +30 -0
- data/lib/tasks/upgrade/fix_deleted_private_follows.rake +26 -0
- metadata +29 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d24b967768820d8eb01abd435e318b7a869af854b0f374ca54c54a6dddbdddb6
|
|
4
|
+
data.tar.gz: f4374ee02ae0c5c9a580c8ad1c371c6552719be39b40e22cdf3d37018464950c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4baebaa527bd42897989aee6feeb29b4bf2773872b3bb7f8379a230111a94f0ee8565e15b02599fb6a1c20bb83a24feed96707448eecc70b866d74a8dd34bec
|
|
7
|
+
data.tar.gz: 47436bb0abfd556afcebe781b153aaaa9e960653be07fdf19486f11d82bfc40b61472dc24cfbc397838b19638a395bbad3dc3af8e1d791b2277412b3b56e719d
|
|
@@ -68,15 +68,23 @@ module Decidim
|
|
|
68
68
|
def clean_body
|
|
69
69
|
return unless body
|
|
70
70
|
|
|
71
|
-
Array(body).map { |paragraph|
|
|
71
|
+
Array(body).map { |paragraph| clean(paragraph) }.join
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def clean_announcement
|
|
75
|
+
return if announcement.is_a?(Hash) && announcement.values.all?(&:blank?)
|
|
76
|
+
|
|
75
77
|
clean(announcement)
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
def clean(value)
|
|
79
|
-
|
|
81
|
+
return if value.blank? || value.nil?
|
|
82
|
+
|
|
83
|
+
if value.include?("rich-text-display")
|
|
84
|
+
decidim_sanitize_admin(translated_attribute(value))
|
|
85
|
+
else
|
|
86
|
+
tag.p(decidim_sanitize_admin(translated_attribute(value)))
|
|
87
|
+
end
|
|
80
88
|
end
|
|
81
89
|
end
|
|
82
90
|
end
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<div class="row column">
|
|
2
|
-
<%= form.upload :file, button_class: "button button__sm button__transparent-secondary" %>
|
|
2
|
+
<%= form.upload :file, attachments: form.object.file.present? ? [form.object.file] : [], button_class: "button button__sm button__transparent-secondary" %>
|
|
3
3
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%= link_to resource_path, class: classes[:default], id: resource_id do %>
|
|
2
2
|
<div class="<%= classes[:img] %>">
|
|
3
3
|
<% if has_image? %>
|
|
4
|
-
<%= image_tag resource_image_url, alt:
|
|
4
|
+
<%= image_tag resource_image_url, alt: "" %>
|
|
5
5
|
<% else %>
|
|
6
6
|
<%= external_icon "media/images/placeholder-card-g.svg", class: "card__placeholder-g" %>
|
|
7
7
|
<% end %>
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<% metadata_valued_items.each do |item| %>
|
|
3
3
|
<div class="participatory-space__metadata-item">
|
|
4
4
|
<div class="participatory-space__metadata-item-title">
|
|
5
|
-
<
|
|
5
|
+
<p><%= item[:title] %></p>
|
|
6
6
|
</div>
|
|
7
|
-
<
|
|
7
|
+
<p><%= item[:value] %></p>
|
|
8
8
|
</div>
|
|
9
9
|
<% end %>
|
|
10
10
|
</div>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<div class="participatory-space__nav-container">
|
|
2
|
-
<button id="dropdown-trigger-participatory-space" data-controller="dropdown" data-target="dropdown-menu-participatory-space" data-auto-close="true" data-scroll-to-menu="true"
|
|
2
|
+
<button id="dropdown-trigger-participatory-space" data-controller="dropdown" data-target="dropdown-menu-participatory-space" data-auto-close="true" data-scroll-to-menu="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" aria-hidden="true">
|
|
8
8
|
<% model.each do |item| %>
|
|
9
9
|
<li role="menuitem">
|
|
10
10
|
<%= link_to item[:url], class: "participatory-space__nav-item" do %>
|
|
11
|
-
<%= item[:name] %>
|
|
11
|
+
<%= decidim_escape_translated(item[:name]) %>
|
|
12
12
|
<%= icon "arrow-right-line" %>
|
|
13
13
|
<% end %>
|
|
14
14
|
</li>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div class="notification" data-notification>
|
|
2
|
+
<div class="notification__wrapper">
|
|
3
|
+
<div class="notification__time" title="<%= l(notification.created_at) %>"> <%= notification.created_at_in_words %></div>
|
|
4
|
+
<div class="notification__snippet">
|
|
5
|
+
<span class="notification__snippet-title text-gray"><%= t("decidim.notifications.show.deleted") %></span>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<%= link_to model, remote: true, method: :delete, class: "notification__button", data: { "notification-read": "" } do %>
|
|
9
|
+
<span class="sr-only md:not-sr-only"><%= t("mark_as_read", scope: "layouts.decidim.notifications_dashboard") %></span>
|
|
10
|
+
<%= icon "check-line", class: "fill-current" %>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div class="notification" data-notification>
|
|
2
|
+
<div class="notification__wrapper">
|
|
3
|
+
<div class="notification__time" title="<%= l(notification.created_at) %>"> <%= notification.created_at_in_words %></div>
|
|
4
|
+
<div class="notification__snippet">
|
|
5
|
+
<span class="notification__snippet-title text-gray"><%= t("decidim.notifications.show.not_available") %></span>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<%= link_to model, remote: true, method: :delete, class: "notification__button", data: { "notification-read": "" } do %>
|
|
9
|
+
<span class="sr-only md:not-sr-only"><%= t("mark_as_read", scope: "layouts.decidim.notifications_dashboard") %></span>
|
|
10
|
+
<%= icon "check-line", class: "fill-current" %>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
@@ -7,7 +7,11 @@ module Decidim
|
|
|
7
7
|
include Decidim::Core::Engine.routes.url_helpers
|
|
8
8
|
|
|
9
9
|
def show
|
|
10
|
-
if notification.
|
|
10
|
+
if !notification.can_participate?(current_user)
|
|
11
|
+
render :not_available
|
|
12
|
+
elsif notification.deleted_resource?
|
|
13
|
+
render :deleted
|
|
14
|
+
elsif notification.hidden_resource?
|
|
11
15
|
render :moderated
|
|
12
16
|
else
|
|
13
17
|
render :show
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<%= decidim_modal id: "socialShare", class: "share-modal" do %>
|
|
2
2
|
<div data-dialog-container>
|
|
3
|
-
<h2 tabindex="-1" data-dialog-title><%= t("share", scope: "decidim.shared.share_modal") %></h2>
|
|
3
|
+
<h2 id="dialog-title-social-share-modal" tabindex="-1" data-dialog-title><%= t("share", scope: "decidim.shared.share_modal") %></h2>
|
|
4
4
|
|
|
5
5
|
<div>
|
|
6
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div class="statistic <%= stat_dom_class %>" data-statistic>
|
|
2
2
|
<div class="statistic__header">
|
|
3
3
|
<div class="statistic__metric">
|
|
4
|
-
<
|
|
4
|
+
<p class="statistic__title" title="<%= stat_title %>">
|
|
5
5
|
<%= stat_title %>
|
|
6
|
-
</
|
|
6
|
+
</p>
|
|
7
7
|
<div class="statistic__tooltip">
|
|
8
8
|
<%= information_tooltip %>
|
|
9
9
|
</div>
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
<% if second_stat_number %>
|
|
16
|
-
<
|
|
16
|
+
<p class="statistic__second-number">
|
|
17
17
|
<%= stat_sub_title %>
|
|
18
18
|
<span class="font-semibold"><%= second_stat_number %></span>
|
|
19
|
-
</
|
|
19
|
+
</p>
|
|
20
20
|
<% else %>
|
|
21
21
|
<p class="h-[21px]"></p>
|
|
22
22
|
<% end %>
|
|
23
|
-
<
|
|
23
|
+
<p class="statistic__number">
|
|
24
24
|
<%= stat_number %>
|
|
25
|
-
</
|
|
25
|
+
</p>
|
|
26
26
|
</div>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div class="upload-modal__files-container upload-container-for-<%= attribute %> <%= with_title %>">
|
|
2
2
|
<div>
|
|
3
|
-
<%= label %>
|
|
3
|
+
<%= options[:paragraph] == true ? paragraph : label %>
|
|
4
4
|
|
|
5
5
|
<% if options[:help_text].present? %>
|
|
6
|
-
<
|
|
6
|
+
<p class="help-text"><%= options[:help_text] %></p>
|
|
7
7
|
<% end %>
|
|
8
8
|
|
|
9
9
|
<%# NOTE: this block is about wrapping a default image for the avatar with the new styles,
|
|
@@ -22,24 +22,28 @@
|
|
|
22
22
|
<div class="upload-modal__files" data-active-uploads="<%= modal_id %>">
|
|
23
23
|
<% attachments.each do |attachment| %>
|
|
24
24
|
<% next if [Array, Hash].any? { |klass| attachment.is_a? klass } %>
|
|
25
|
+
<% is_persisted_attachment = attachment.is_a?(Decidim::Attachment) && attachment.persisted? %>
|
|
26
|
+
<% attachment_blob = blob(attachment) %>
|
|
25
27
|
|
|
26
|
-
<div class="attachment-details" data-attachment-id="<%= attachment.id %>" data-title="<%= title_for(attachment) %>" data-filename="<%= file_name_for(attachment) %>" data-state="uploaded">
|
|
27
|
-
<% if file_attachment_path(attachment) &&
|
|
28
|
+
<div class="attachment-details"<% if is_persisted_attachment %> data-attachment-id="<%= attachment.id %>"<% end %> data-title="<%= title_for(attachment) %>" data-filename="<%= file_name_for(attachment) %>" data-state="uploaded" data-hidden-field="<%= attachment_blob&.signed_id %>">
|
|
29
|
+
<% if file_attachment_path(attachment) && attachment_blob&.image? %>
|
|
28
30
|
<div><%= image_tag(file_attachment_path(attachment), alt: "") %></div>
|
|
29
31
|
<% elsif uploader_default_image_path(attribute).present? %>
|
|
30
32
|
<div><%= image_tag uploader_default_image_path(attribute) %></div>
|
|
31
33
|
<% end %>
|
|
32
34
|
|
|
33
35
|
<% if has_title? %>
|
|
34
|
-
<
|
|
35
|
-
<%= form.hidden_field attribute, multiple: true, value: attachment.id, id: attachment.id %>
|
|
36
|
+
<p><%= title_for(attachment) %></p>
|
|
36
37
|
<% else %>
|
|
37
|
-
<% if
|
|
38
|
-
<
|
|
38
|
+
<% if attachment_blob&.image? %>
|
|
39
|
+
<p><%= title_for(attachment) %></p>
|
|
39
40
|
<% else %>
|
|
40
41
|
<%= link_to title_for(attachment), file_attachment_path(attachment), class: "w-full break-all mb-2" %>
|
|
41
42
|
<% end %>
|
|
42
43
|
<% end %>
|
|
44
|
+
<% if attachment_blob.present? %>
|
|
45
|
+
<%= form.hidden_field attribute, value: attachment_blob.signed_id, id: "hidden_#{attribute}_#{attachment_blob.id}" %>
|
|
46
|
+
<% end %>
|
|
43
47
|
</div>
|
|
44
48
|
<% end %>
|
|
45
49
|
</div>
|
|
@@ -30,6 +30,10 @@ module Decidim
|
|
|
30
30
|
form.send(:custom_label, attribute, options[:label], { required: required?, for: nil })
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def paragraph
|
|
34
|
+
form.send(:custom_paragraph, attribute, options[:label], { required: required? })
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
def button_label
|
|
34
38
|
return button_edit_label if attachments.count.positive?
|
|
35
39
|
|
|
@@ -71,13 +75,16 @@ module Decidim
|
|
|
71
75
|
end
|
|
72
76
|
|
|
73
77
|
# By default FoundationRailsHelper adds form errors next to input, but since input is in the modal
|
|
74
|
-
# and modal is hidden by default, we
|
|
78
|
+
# and modal is hidden by default, we add a hidden checkbox field to handle HTML5 validation.
|
|
75
79
|
# This should only be necessary when file is required by the form.
|
|
80
|
+
# Note: Validation errors are now displayed in the main form area, not inside the modal.
|
|
76
81
|
def input_validation_field
|
|
77
82
|
object_name = form.object.present? ? "#{form.object.model_name.param_key}[#{add_attribute}_validation]" : "#{add_attribute}_validation"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
check_box_tag object_name, 1, attachments.present?, class: "reset-defaults", hidden: true, label: false, required: required?, id: validation_field_id
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def validation_field_id
|
|
87
|
+
"#{attribute}_validation"
|
|
81
88
|
end
|
|
82
89
|
|
|
83
90
|
def explanation
|
|
@@ -19,6 +19,15 @@ module Decidim
|
|
|
19
19
|
destroy_user_account!
|
|
20
20
|
destroy_user_identities
|
|
21
21
|
destroy_follows
|
|
22
|
+
destroy_user_versions
|
|
23
|
+
destroy_user_private_exports
|
|
24
|
+
destroy_user_access_grants
|
|
25
|
+
destroy_user_access_tokens
|
|
26
|
+
destroy_user_reminders
|
|
27
|
+
destroy_user_notifications
|
|
28
|
+
destroy_user_badges
|
|
29
|
+
destroy_user_likes
|
|
30
|
+
destroy_user_reports
|
|
22
31
|
destroy_participatory_space_private_user
|
|
23
32
|
delegate_destroy_to_participatory_spaces
|
|
24
33
|
end
|
|
@@ -47,17 +56,53 @@ module Decidim
|
|
|
47
56
|
current_user.save!
|
|
48
57
|
end
|
|
49
58
|
|
|
59
|
+
def destroy_user_badges
|
|
60
|
+
Decidim::Gamification::BadgeScore.where(user: current_user).find_each(&:destroy)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def destroy_user_reports
|
|
64
|
+
Decidim::UserModeration.where(user: current_user).find_each(&:destroy)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def destroy_user_likes
|
|
68
|
+
Decidim::Like.where(author: current_user).find_each(&:destroy)
|
|
69
|
+
end
|
|
70
|
+
|
|
50
71
|
def destroy_user_identities
|
|
51
|
-
current_user.identities.
|
|
72
|
+
current_user.identities.find_each(&:destroy)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def destroy_user_versions
|
|
76
|
+
current_user.versions.find_each(&:destroy)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def destroy_user_private_exports
|
|
80
|
+
current_user.private_exports.find_each(&:destroy)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def destroy_user_access_grants
|
|
84
|
+
current_user.access_grants.find_each(&:destroy)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def destroy_user_access_tokens
|
|
88
|
+
current_user.access_tokens.find_each(&:destroy)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def destroy_user_reminders
|
|
92
|
+
current_user.reminders.find_each(&:destroy)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def destroy_user_notifications
|
|
96
|
+
current_user.notifications.find_each(&:destroy)
|
|
52
97
|
end
|
|
53
98
|
|
|
54
99
|
def destroy_follows
|
|
55
|
-
Decidim::Follow.where(followable: current_user).
|
|
56
|
-
Decidim::Follow.where(user: current_user).
|
|
100
|
+
Decidim::Follow.where(followable: current_user).find_each(&:destroy)
|
|
101
|
+
Decidim::Follow.where(user: current_user).find_each(&:destroy)
|
|
57
102
|
end
|
|
58
103
|
|
|
59
104
|
def destroy_participatory_space_private_user
|
|
60
|
-
Decidim::ParticipatorySpacePrivateUser.where(user: current_user).
|
|
105
|
+
Decidim::ParticipatorySpacePrivateUser.where(user: current_user).find_each(&:destroy)
|
|
61
106
|
end
|
|
62
107
|
|
|
63
108
|
def delegate_destroy_to_participatory_spaces
|
|
@@ -9,8 +9,6 @@ module Decidim
|
|
|
9
9
|
skip_before_action :verify_organization
|
|
10
10
|
|
|
11
11
|
before_action :check_organization!,
|
|
12
|
-
:check_authenticated!,
|
|
13
|
-
:check_user_belongs_to_organization,
|
|
14
12
|
:validate_direct_upload
|
|
15
13
|
end
|
|
16
14
|
|
|
@@ -42,16 +40,6 @@ module Decidim
|
|
|
42
40
|
head :unauthorized if current_organization.blank? && current_admin.blank?
|
|
43
41
|
end
|
|
44
42
|
|
|
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
43
|
def allowed_extensions
|
|
56
44
|
if user_has_elevated_role?
|
|
57
45
|
current_organization.settings.upload_allowed_file_extensions_admin
|
|
@@ -71,6 +59,8 @@ module Decidim
|
|
|
71
59
|
private
|
|
72
60
|
|
|
73
61
|
def user_has_elevated_role?
|
|
62
|
+
return false if current_user.blank? || current_organization.blank? || current_user.organization != current_organization
|
|
63
|
+
|
|
74
64
|
[
|
|
75
65
|
current_user&.admin?,
|
|
76
66
|
defined?(Decidim::Assemblies::AssembliesWithUserRole) && Decidim::Assemblies::AssembliesWithUserRole.for(current_user).any?,
|
|
@@ -9,6 +9,8 @@ module Decidim
|
|
|
9
9
|
|
|
10
10
|
before_action :check_sign_in_enabled, only: :create
|
|
11
11
|
|
|
12
|
+
rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_referer_or_path
|
|
13
|
+
|
|
12
14
|
def create
|
|
13
15
|
super do |user|
|
|
14
16
|
if user.admin?
|
|
@@ -44,6 +46,11 @@ module Decidim
|
|
|
44
46
|
|
|
45
47
|
private
|
|
46
48
|
|
|
49
|
+
def redirect_to_referer_or_path
|
|
50
|
+
set_flash_message(:alert, "csrf_token", scope: "devise.failure")
|
|
51
|
+
redirect_back(fallback_location: root_path) && return
|
|
52
|
+
end
|
|
53
|
+
|
|
47
54
|
def check_sign_in_enabled
|
|
48
55
|
redirect_to new_user_session_path unless current_organization.sign_in_enabled?
|
|
49
56
|
end
|
|
@@ -43,7 +43,7 @@ module Decidim
|
|
|
43
43
|
|
|
44
44
|
def interpolate(template)
|
|
45
45
|
template
|
|
46
|
-
.gsub("{{name}}", user.name)
|
|
46
|
+
.gsub("{{name}}", user.presenter.name)
|
|
47
47
|
.gsub("{{organization}}", organization_name(organization))
|
|
48
48
|
.gsub("{{help_url}}", url_helpers.pages_url(host: organization.host))
|
|
49
49
|
.gsub("{{badges_url}}", url_helpers.gamification_badges_url(host: organization.host))
|
|
@@ -117,9 +117,9 @@ module Decidim
|
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def message(value)
|
|
120
|
-
return content_tag(:
|
|
120
|
+
return content_tag(:p, value, class: "flash__message") unless value.is_a?(Hash)
|
|
121
121
|
|
|
122
|
-
content_tag(:
|
|
122
|
+
content_tag(:p, class: "flash__message") do
|
|
123
123
|
concat value[:title]
|
|
124
124
|
concat content_tag(:span, value[:body], class: "flash__message-body")
|
|
125
125
|
end
|
|
@@ -11,7 +11,7 @@ module Decidim
|
|
|
11
11
|
manifest.name == name.to_sym
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
collection = export_manifest.collection.call(participatory_space)
|
|
14
|
+
collection = export_manifest.collection.call(participatory_space, user)
|
|
15
15
|
serializer = export_manifest.serializer
|
|
16
16
|
|
|
17
17
|
export_data = Decidim::Exporters.find_exporter(format).new(collection, serializer).export
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
class RemoveSearchIndexesJob < ApplicationJob
|
|
5
|
+
queue_as :default
|
|
6
|
+
|
|
7
|
+
def perform(elements)
|
|
8
|
+
elements.each do |element|
|
|
9
|
+
element.remove_from_index(element)
|
|
10
|
+
next unless element.respond_to?(:comments)
|
|
11
|
+
|
|
12
|
+
element.comments.each do |comment|
|
|
13
|
+
Decidim::RemoveSearchIndexesJob.perform_later([comment])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -17,13 +17,30 @@ module Decidim
|
|
|
17
17
|
# Note that this could be improved by adding a "type" column to the notifications table
|
|
18
18
|
# This fix can generate lists of notifications that are below the SIZE_LIMIT
|
|
19
19
|
@notifications = notifications[0...SIZE_LIMIT].filter_map do |notification|
|
|
20
|
+
# Check if is a notification that can be sent on email
|
|
20
21
|
next unless notification.event_class_instance.respond_to?(:email_intro)
|
|
22
|
+
# checks if the resource exists, as we have implemented the possibility of soft deleting resources
|
|
23
|
+
next unless resource_is_present?(notification)
|
|
24
|
+
# checks if the resource is visible
|
|
25
|
+
next unless notification.can_participate?(@user)
|
|
26
|
+
# It usually checks if the resource is reportable and is not hidden, however, there are some exceptions
|
|
27
|
+
# like in the comments, where we check if the resource and intended comment is visible.
|
|
28
|
+
next if notification.hidden_resource?
|
|
29
|
+
# It usually checks if the resource is deletable and is not deleted, however, there are some exceptions
|
|
30
|
+
# like in the comments, where we check if the resource and intended comment is visible.
|
|
31
|
+
next if notification.deleted_resource?
|
|
21
32
|
|
|
22
33
|
Decidim::NotificationToMailerPresenter.new(notification)
|
|
23
34
|
end
|
|
24
35
|
|
|
25
|
-
mail(to: user.email, subject: @notifications_digest.subject)
|
|
36
|
+
mail(to: user.email, subject: @notifications_digest.subject) if @notifications.any?
|
|
26
37
|
end
|
|
27
38
|
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def resource_is_present?(notification)
|
|
43
|
+
notification.resource
|
|
44
|
+
end
|
|
28
45
|
end
|
|
29
46
|
end
|
|
@@ -9,7 +9,7 @@ module Decidim
|
|
|
9
9
|
include Traceable
|
|
10
10
|
|
|
11
11
|
before_save :set_content_type_and_size, if: :attached?
|
|
12
|
-
before_validation :set_link_content_type_and_size, if: :
|
|
12
|
+
before_validation :set_link_content_type_and_size, if: :editable_link?
|
|
13
13
|
|
|
14
14
|
translatable_fields :title, :description
|
|
15
15
|
belongs_to :attachment_collection, class_name: "Decidim::AttachmentCollection", optional: true
|
|
@@ -69,6 +69,20 @@ module Decidim
|
|
|
69
69
|
link.present?
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# Whether this attachment is a link that can be edited or not.
|
|
73
|
+
#
|
|
74
|
+
# Returns Boolean.
|
|
75
|
+
def editable_link?
|
|
76
|
+
!destroyed? && !frozen? && link?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Whether this attachment has a file or not.
|
|
80
|
+
#
|
|
81
|
+
# Returns Boolean.
|
|
82
|
+
def file?
|
|
83
|
+
file.attached?
|
|
84
|
+
end
|
|
85
|
+
|
|
72
86
|
# Which kind of file this is.
|
|
73
87
|
#
|
|
74
88
|
# Returns String.
|
|
@@ -123,5 +137,12 @@ module Decidim
|
|
|
123
137
|
def self.log_presenter_class_for(_log)
|
|
124
138
|
Decidim::AdminLog::AttachmentPresenter
|
|
125
139
|
end
|
|
140
|
+
|
|
141
|
+
def can_participate?(user)
|
|
142
|
+
return true unless attached_to
|
|
143
|
+
return true unless attached_to.respond_to?(:can_participate?)
|
|
144
|
+
|
|
145
|
+
attached_to.can_participate?(user)
|
|
146
|
+
end
|
|
126
147
|
end
|
|
127
148
|
end
|
|
@@ -100,11 +100,14 @@ module Decidim
|
|
|
100
100
|
def resource_description; end
|
|
101
101
|
|
|
102
102
|
def can_participate_in_space?(user)
|
|
103
|
+
return false unless published?
|
|
104
|
+
return false unless participatory_space.published?
|
|
103
105
|
return true unless participatory_space.try(:private_space?)
|
|
104
106
|
return false unless user
|
|
105
107
|
|
|
106
108
|
participatory_space.can_participate?(user)
|
|
107
109
|
end
|
|
110
|
+
alias can_participate? can_participate_in_space?
|
|
108
111
|
|
|
109
112
|
def private_non_transparent_space?
|
|
110
113
|
return false unless participatory_space.respond_to?(:private_space?)
|
|
@@ -37,8 +37,20 @@ module Decidim
|
|
|
37
37
|
where(decidim_user_id: user.id)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
def can_participate?(user)
|
|
41
|
+
resource.can_participate?(user)
|
|
42
|
+
end
|
|
43
|
+
|
|
40
44
|
def self.export_serializer
|
|
41
45
|
Decidim::DownloadYourDataSerializers::DownloadYourDataNotificationSerializer
|
|
42
46
|
end
|
|
47
|
+
|
|
48
|
+
def hidden_resource?
|
|
49
|
+
event_class_instance.respond_to?(:hidden_resource?) && event_class_instance.hidden_resource?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def deleted_resource?
|
|
53
|
+
event_class_instance.respond_to?(:deleted_resource?) && event_class_instance.deleted_resource?
|
|
54
|
+
end
|
|
43
55
|
end
|
|
44
56
|
end
|
data/app/models/decidim/user.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Decidim
|
|
|
20
20
|
has_one :blocking, class_name: "Decidim::UserBlock", foreign_key: :id, primary_key: :block_id, dependent: :destroy
|
|
21
21
|
|
|
22
22
|
# Regex for name & nickname format validations
|
|
23
|
-
REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}
|
|
23
|
+
REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}\\|\n\r])/m
|
|
24
24
|
REGEXP_NICKNAME = /\A[a-z0-9_-]+\z/
|
|
25
25
|
|
|
26
26
|
has_one_attached :avatar
|
|
@@ -42,6 +42,10 @@ export default class extends Controller {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
setupMutationObserver() {
|
|
45
|
+
if (!this.newPasswordPanel) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
this.observer = new MutationObserver(() => {
|
|
46
50
|
let ariaHiddenValue = this.newPasswordPanel.getAttribute("aria-hidden");
|
|
47
51
|
this.newPwVisible = ariaHiddenValue === "false";
|
|
@@ -54,6 +58,10 @@ export default class extends Controller {
|
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
setupEmailChangeListener() {
|
|
61
|
+
if (!this.emailField) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
this.emailField.addEventListener("change", () => {
|
|
58
66
|
this.emailChanged = this.emailField.value !== this.originalEmail;
|
|
59
67
|
this.toggleOldPassword();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This controller is used to change the active tab when the language is changed in the admin or system panel.
|
|
5
|
+
* It uses a select element to list the languages available in the platform and adds an observer that would set
|
|
6
|
+
* the tab the active tab to what is selected in the select element by toggling the aria-hidden attribute on the
|
|
7
|
+
* tab container.
|
|
8
|
+
*/
|
|
9
|
+
export default class extends Controller {
|
|
10
|
+
connect() {
|
|
11
|
+
this.handleChange = this.handleChange.bind(this);
|
|
12
|
+
this.element.addEventListener("change", this.handleChange);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
disconnect() {
|
|
16
|
+
this.element.removeEventListener("change", this.handleChange)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
handleChange(event) {
|
|
20
|
+
let targetTabPaneSelector = event.target.value;
|
|
21
|
+
let tabsContent = event.target.parentElement.parentElement.nextElementSibling;
|
|
22
|
+
|
|
23
|
+
if (!tabsContent) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let activeTabContent = tabsContent.querySelector(".is-active");
|
|
28
|
+
if (activeTabContent) {
|
|
29
|
+
activeTabContent.ariaHidden = "true";
|
|
30
|
+
activeTabContent.classList.remove("is-active");
|
|
31
|
+
}
|
|
32
|
+
let activePane = tabsContent.querySelector(targetTabPaneSelector);
|
|
33
|
+
if (activePane) {
|
|
34
|
+
activePane.ariaHidden = "false";
|
|
35
|
+
activePane.classList.add("is-active");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|