decidim-core 0.31.0.rc2 → 0.31.1

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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +6 -0
  3. data/app/cells/decidim/card_g/show.erb +1 -1
  4. data/app/cells/decidim/notification/deleted.erb +12 -0
  5. data/app/cells/decidim/notification/not_available.erb +12 -0
  6. data/app/cells/decidim/notification_cell.rb +5 -1
  7. data/app/cells/decidim/share_widget/modal.erb +1 -1
  8. data/app/commands/decidim/destroy_account.rb +49 -4
  9. data/app/controllers/decidim/download_your_data_controller.rb +5 -2
  10. data/app/events/decidim/welcome_notification_event.rb +1 -1
  11. data/app/jobs/decidim/remove_search_indexes_job.rb +18 -0
  12. data/app/mailers/decidim/notifications_digest_mailer.rb +18 -1
  13. data/app/models/decidim/component.rb +3 -0
  14. data/app/models/decidim/notification.rb +12 -0
  15. data/app/models/decidim/private_export.rb +4 -0
  16. data/app/models/decidim/user.rb +4 -0
  17. data/app/models/decidim/user_base_entity.rb +1 -1
  18. data/app/packs/src/decidim/controllers/dropdown/controller.js +2 -2
  19. data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +25 -2
  20. data/app/packs/src/decidim/datepicker/generate_datepicker.js +6 -0
  21. data/app/packs/src/decidim/datepicker/generate_timepicker.js +9 -0
  22. data/app/packs/src/decidim/editor/extensions/link/index.js +0 -1
  23. data/app/packs/src/decidim/editor/index.js +5 -1
  24. data/app/packs/src/decidim/editor/test/extensions/link.test.js +2 -2
  25. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_link.js +1 -1
  26. data/app/packs/src/decidim/index.js +10 -5
  27. data/app/packs/src/decidim/map/controller/markers.js +3 -1
  28. data/app/packs/stylesheets/decidim/_conversations.scss +1 -1
  29. data/app/packs/stylesheets/decidim/_dropdown.scss +22 -5
  30. data/app/packs/stylesheets/decidim/_filters.scss +1 -1
  31. data/app/packs/stylesheets/decidim/_footer.scss +5 -0
  32. data/app/packs/stylesheets/decidim/_modal.scss +5 -1
  33. data/app/packs/stylesheets/decidim/_modal_fingerprint.scss +1 -1
  34. data/app/presenters/decidim/log/user_presenter.rb +2 -1
  35. data/app/presenters/decidim/organization_presenter.rb +4 -0
  36. data/app/presenters/decidim/user_presenter.rb +6 -1
  37. data/app/services/decidim/download_your_data_exporter.rb +15 -4
  38. data/app/services/decidim/open_data_exporter.rb +2 -1
  39. data/app/views/decidim/download_your_data/_export.html.erb +1 -1
  40. data/app/views/decidim/export_mailer/download_your_data_export.html.erb +1 -1
  41. data/app/views/decidim/export_mailer/export.html.erb +1 -1
  42. data/app/views/decidim/manifests/show.json.erb +1 -0
  43. data/app/views/decidim/pages/_tabbed.html.erb +1 -1
  44. data/app/views/decidim/shared/_orders.html.erb +1 -1
  45. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +15 -1
  46. data/config/locales/ar.yml +0 -4
  47. data/config/locales/bg.yml +0 -8
  48. data/config/locales/ca-IT.yml +17 -13
  49. data/config/locales/ca.yml +15 -11
  50. data/config/locales/cs.yml +19 -19
  51. data/config/locales/de.yml +10 -21
  52. data/config/locales/el.yml +0 -3
  53. data/config/locales/en.yml +21 -17
  54. data/config/locales/es-MX.yml +14 -10
  55. data/config/locales/es-PY.yml +14 -10
  56. data/config/locales/es.yml +13 -9
  57. data/config/locales/eu.yml +67 -55
  58. data/config/locales/fa-IR.yml +3 -0
  59. data/config/locales/fi-plain.yml +22 -8
  60. data/config/locales/fi.yml +22 -8
  61. data/config/locales/fr-CA.yml +33 -12
  62. data/config/locales/fr.yml +33 -12
  63. data/config/locales/ga-IE.yml +0 -4
  64. data/config/locales/gl.yml +0 -3
  65. data/config/locales/hu.yml +0 -6
  66. data/config/locales/id-ID.yml +0 -3
  67. data/config/locales/it.yml +2 -3
  68. data/config/locales/ja.yml +46 -22
  69. data/config/locales/ko.yml +3 -0
  70. data/config/locales/lb.yml +0 -3
  71. data/config/locales/lt.yml +0 -5
  72. data/config/locales/lv.yml +0 -3
  73. data/config/locales/mt.yml +3 -0
  74. data/config/locales/nl.yml +0 -3
  75. data/config/locales/no.yml +0 -3
  76. data/config/locales/pl.yml +11 -7
  77. data/config/locales/pt-BR.yml +626 -5
  78. data/config/locales/pt.yml +0 -3
  79. data/config/locales/ro-RO.yml +480 -121
  80. data/config/locales/ru.yml +0 -4
  81. data/config/locales/sk.yml +0 -3
  82. data/config/locales/sl.yml +0 -1
  83. data/config/locales/sv.yml +28 -13
  84. data/config/locales/tr-TR.yml +0 -5
  85. data/config/locales/uk.yml +0 -3
  86. data/config/locales/vi.yml +3 -0
  87. data/config/locales/zh-CN.yml +0 -3
  88. data/config/locales/zh-TW.yml +0 -5
  89. data/db/data/20251108232118_add_dummy_migration.rb +11 -0
  90. data/db/data/20251125144141_add_short_name_to_organizations.rb +35 -0
  91. data/db/migrate/20250819110800_convert_private_exports_id_to_uuid.rb +55 -0
  92. data/db/migrate/20251031150928_add_short_name_to_organization.rb +7 -0
  93. data/decidim-core.gemspec +2 -0
  94. data/lib/decidim/core/engine.rb +9 -0
  95. data/lib/decidim/core/seeds.rb +2 -2
  96. data/lib/decidim/core/test/factories.rb +32 -2
  97. data/lib/decidim/core/test/shared_examples/comments_examples.rb +51 -1
  98. data/lib/decidim/core/test/shared_examples/fingerprint_examples.rb +13 -0
  99. data/lib/decidim/core/version.rb +1 -1
  100. data/lib/decidim/events/base_event.rb +4 -0
  101. data/lib/decidim/has_private_users.rb +1 -0
  102. data/lib/decidim/seeds.rb +1 -1
  103. data/lib/decidim/shakapacker/configuration.rb +5 -1
  104. data/lib/tasks/decidim_procfile.rake +1 -1
  105. data/lib/tasks/upgrade/clean.rake +11 -0
  106. data/lib/tasks/upgrade/decidim_fix_action_log.rake +28 -0
  107. data/lib/tasks/upgrade/decidim_remove_deleted_users_left_data_tasks.rake +30 -0
  108. data/lib/tasks/upgrade/fix_deleted_private_follows.rake +26 -0
  109. metadata +44 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20b25b4c72d8bdd6dc49a4de6ef5c9668d93ee906c40e9ca1147883682b22a32
4
- data.tar.gz: aa75ba1beac71b39a10ac9dc539be1c8390aaa6ac61d253f16b02889a58933a9
3
+ metadata.gz: a09ab97ba5e294cac771913b5136ad740e670befc9509ca5827cf86d215f9fa6
4
+ data.tar.gz: ad5ddc3a2ea6c21d8ed9dea42ee2a9307ba44799a8f4ca90c5253f3a75c217dd
5
5
  SHA512:
6
- metadata.gz: 108a66dff2b65eb213285e9335ae7c4e365f541fb49460973c07e4a372ebbf5673514113cfc5e3fb451ee867ad6a0de40a520f7953da10d95c5ad05ed97e98cb
7
- data.tar.gz: fd774c108044e92d3181a8d3fb57d5b2c6cfa42bd411485765bb53e302dc3bf480022f9bda4fa4bbec7050c8a898db5ba7c267a68ec58ecccd1324c8d7554a07
6
+ metadata.gz: 0cbb0319b8de1e90e2f6d3f4eb52d81ff023c9849d902a6569084065b8e9ec2ef76b48d811d23ada7731f48a2defa940764c6846bf1ae59797d83ab78afb351b
7
+ data.tar.gz: 34ef4ee5189225382525aa6c01c09f7b1a4bc39c1edbec9a7beff9408acacb8766ece1cf6688962d70d4695e2be708b48f74f9fe69d023d675ffe2e10ebf51e3
@@ -12,6 +12,12 @@ module Decidim
12
12
  return unless renderable?
13
13
 
14
14
  render
15
+ rescue NoMethodError => e
16
+ # Soft-deleted components or participatory spaces could cause errors
17
+ # when rendering activities. We log them for further inspection but
18
+ # avoid breaking the entire activity feed.
19
+ Rails.logger.error("Error rendering activity cell for #{model.id}: #{e.message}")
20
+ nil
15
21
  end
16
22
 
17
23
  # Since activity logs could be linked to resource no longer available
@@ -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: alt_title %>
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 %>
@@ -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.event_class_instance.try(:hidden_resource?)
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
 
@@ -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.destroy_all
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).destroy_all
56
- Decidim::Follow.where(user: current_user).destroy_all
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).destroy_all
105
+ Decidim::ParticipatorySpacePrivateUser.where(user: current_user).find_each(&:destroy)
61
106
  end
62
107
 
63
108
  def delegate_destroy_to_participatory_spaces
@@ -43,7 +43,10 @@ module Decidim
43
43
  def download_file
44
44
  enforce_permission_to(:download, :user, current_user:)
45
45
 
46
- if private_export.expired?
46
+ if private_export.blank?
47
+ flash[:error] = t("decidim.account.download_your_data_export.export_not_found")
48
+ redirect_to download_your_data_path
49
+ elsif private_export.expired?
47
50
  flash[:error] = t("decidim.account.download_your_data_export.export_expired")
48
51
  redirect_to download_your_data_path
49
52
  elsif private_export.file.attached?
@@ -57,7 +60,7 @@ module Decidim
57
60
  private
58
61
 
59
62
  def private_export
60
- @private_export ||= current_user.private_exports.find(params[:uuid])
63
+ @private_export ||= current_user.private_exports.where(uuid: params[:uuid]).first
61
64
  end
62
65
 
63
66
  def help_definitions
@@ -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))
@@ -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
@@ -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
@@ -12,6 +12,10 @@ module Decidim
12
12
 
13
13
  default_scope { order(created_at: :desc) }
14
14
 
15
+ before_create do |record|
16
+ record.uuid = SecureRandom.uuid
17
+ end
18
+
15
19
  def expired?
16
20
  expires_at < Time.zone.now
17
21
  end
@@ -153,6 +153,10 @@ module Decidim
153
153
  deleted_at.present?
154
154
  end
155
155
 
156
+ def can_participate?(_user)
157
+ true
158
+ end
159
+
156
160
  # Public: whether the user has been officialized or not
157
161
  def officialized?
158
162
  !officialized_at.nil?
@@ -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
@@ -116,10 +116,10 @@ export default class extends Controller {
116
116
  changeStyleOfSelectedElement() {
117
117
  this.element.addEventListener("click", function(event) {
118
118
  event.target.parentNode.parentNode.querySelectorAll("a").forEach((link) => {
119
- link.parentNode.classList.remove("dropdown__item-hovered")
119
+ link.parentNode.classList.remove("dropdown__item-opened");
120
120
  })
121
121
 
122
- event.target.parentNode.classList.add("dropdown__item-hovered")
122
+ event.target.parentNode.classList.add("dropdown__item-opened")
123
123
  })
124
124
  }
125
125
  }
@@ -16,11 +16,30 @@ export default class extends Controller {
16
16
 
17
17
  this.initializeEmptyFocusElement();
18
18
  this.initializeAutoComplete();
19
- this.searchInput.addEventListener("selection", (event) => {
19
+ this.setupSelectionListener();
20
+ }
21
+
22
+ /**
23
+ * Setup the selection event listener
24
+ * @returns {void}
25
+ */
26
+ setupSelectionListener() {
27
+ this.selectionHandler = (event) => {
20
28
  const feedback = event.detail;
21
29
  const selection = feedback.selection;
22
30
  this.handleSelection(selection);
23
- });
31
+ };
32
+ this.searchInput.addEventListener("selection", this.selectionHandler);
33
+ }
34
+
35
+ /*
36
+ * Remove event listener to prevent duplicates
37
+ * @returns {void}
38
+ */
39
+ disconnect() {
40
+ if (this.searchInput && this.selectionHandler) {
41
+ this.searchInput.removeEventListener("selection", this.selectionHandler);
42
+ }
24
43
  }
25
44
 
26
45
  /**
@@ -150,6 +169,10 @@ export default class extends Controller {
150
169
  this.addSelectedUser(selection, id);
151
170
  this.autoComplete.setInput("");
152
171
  this.selected.push(id);
172
+
173
+ if (this.autoComplete && this.autoComplete.autocomplete) {
174
+ this.autoComplete.autocomplete.close();
175
+ }
153
176
  }
154
177
 
155
178
  /**
@@ -14,12 +14,18 @@ export default function generateDatePicker(input, row, formats) {
14
14
  date.setAttribute("id", `${input.id}_date`);
15
15
  date.setAttribute("type", "text");
16
16
  date.setAttribute("aria-label", input.dataset.dateLabel);
17
+ if (input.attributes.disabled) {
18
+ date.setAttribute("disabled", input.attributes.disabled);
19
+ };
17
20
 
18
21
  const calendar = document.createElement("button");
19
22
  calendar.innerHTML = icon("calendar-line");
20
23
  calendar.setAttribute("class", "datepicker__calendar-button");
21
24
  calendar.setAttribute("type", "button");
22
25
  calendar.setAttribute("aria-label", input.dataset.buttonDateLabel);
26
+ if (input.attributes.disabled) {
27
+ calendar.setAttribute("disabled", input.attributes.disabled);
28
+ };
23
29
 
24
30
  dateColumn.appendChild(date);
25
31
  dateColumn.appendChild(calendar);
@@ -1,4 +1,6 @@
1
1
  /* eslint-disable require-jsdoc */
2
+ /* eslint max-lines: ["error", 310] */
3
+
2
4
  import icon from "src/decidim/refactor/moved/icon"
3
5
  import { changeHourDisplay, changeMinuteDisplay, formatDate, hourDisplay, minuteDisplay, formatTime, setHour, setMinute, updateTimeValue, updateInputValue } from "src/decidim/datepicker/datepicker_functions"
4
6
  import { timeKeyDownListener, timeBeforeInputListener } from "src/decidim/datepicker/datepicker_listeners";
@@ -14,12 +16,19 @@ export default function generateTimePicker(input, row, formats) {
14
16
  time.setAttribute("id", `${input.id}_time`);
15
17
  time.setAttribute("type", "text");
16
18
  time.setAttribute("aria-label", input.dataset.timeLabel);
19
+ if (input.attributes.disabled) {
20
+ time.setAttribute("disabled", input.attributes.disabled);
21
+ };
17
22
 
18
23
  const clock = document.createElement("button");
19
24
  clock.innerHTML = icon("time-line")
20
25
  clock.setAttribute("class", "datepicker__clock-button");
21
26
  clock.setAttribute("type", "button");
22
27
  clock.setAttribute("aria-label", input.dataset.buttonTimeLabel);
28
+ if (input.attributes.disabled) {
29
+ clock.setAttribute("disabled", input.attributes.disabled);
30
+ };
31
+
23
32
 
24
33
  timeColumn.appendChild(time);
25
34
  timeColumn.appendChild(clock);
@@ -28,7 +28,6 @@ export default Link.extend({
28
28
  ...this.parent?.(),
29
29
  allowTargetControl: false,
30
30
  HTMLAttributes: {
31
- target: "_blank",
32
31
  class: null
33
32
  }
34
33
  }
@@ -81,7 +81,11 @@ export default function createEditor(container) {
81
81
  const toolbar = createEditorToolbar(editor);
82
82
  container.insertBefore(toolbar, editorContainer);
83
83
 
84
- editor.on("update", () => (input.value = editor.getHTML()));
84
+ editor.on("update", () => {
85
+ input.value = editor.isEmpty
86
+ ? ""
87
+ : editor.getHTML();
88
+ });
85
89
 
86
90
  return editor;
87
91
  }
@@ -37,14 +37,14 @@ describe("Link", () => {
37
37
  await sleep(50);
38
38
 
39
39
  expect(editor.getHTML()).toEqual(
40
- '<p>Hello, <a target="_blank" href="https://decidim.org">world</a>!</p>'
40
+ '<p>Hello, <a href="https://decidim.org" target="_blank">world</a>!</p>'
41
41
  );
42
42
  });
43
43
 
44
44
  it("allows editing the link through the dialog", async () => {
45
45
  editorElement.focus();
46
46
  await updateContent(editorElement,
47
- '<p>Hello, <a target="_blank" href="https://decidim.org">world</a>!</p>'
47
+ '<p>Hello, <a href="https://decidim.org" target="_blank" >world</a>!</p>'
48
48
  );
49
49
 
50
50
  // Set the editor cursor inside the link
@@ -26,7 +26,7 @@ export default (ctx) => {
26
26
  await sleep(0);
27
27
 
28
28
  expect(ctx.prosemirror.innerHTML).toEqual(
29
- '<p>Hello, <a target="_blank" href="https://decidim.org">world</a>!</p>'
29
+ '<p>Hello, <a href="https://decidim.org" target="_blank">world</a>!</p>'
30
30
  );
31
31
  });
32
32
  });
@@ -70,16 +70,21 @@ const deprecate = (element, targetController, oldSyntax) => {
70
70
  return;
71
71
  }
72
72
 
73
- console.warn(element)
74
73
  console.warn(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - data-controller="${targetController}" - ${window.location.href}`)
75
- // eslint-disable-next-line no-alert
76
- alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - data-controller="${targetController}"`)
74
+
75
+ if (typeof window.Decidim.dev !== "undefined" && window.Decidim.dev === true) {
76
+ // eslint-disable-next-line no-alert
77
+ alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - data-controller="${targetController}"`)
78
+ }
77
79
  }
78
80
 
79
81
  const deprecationMessage = (element, oldSyntax, newSyntax) => {
80
82
  console.warn(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - ${newSyntax}`)
81
- // eslint-disable-next-line no-alert
82
- alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - ${newSyntax}`)
83
+
84
+ if (typeof window.Decidim.dev !== "undefined" && window.Decidim.dev === true) {
85
+ // eslint-disable-next-line no-alert
86
+ alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - ${newSyntax}`)
87
+ }
83
88
  }
84
89
 
85
90
  window.deprecate = deprecate;
@@ -67,7 +67,9 @@ export default class MapMarkersController extends MapController {
67
67
  }
68
68
 
69
69
  clearMarkers() {
70
- this.map.removeLayer(this.markerClusters);
70
+ if (this.markerClusters !== null) {
71
+ this.map.removeLayer(this.markerClusters);
72
+ }
71
73
  this.markerClusters = new L.MarkerClusterGroup();
72
74
  this.map.addLayer(this.markerClusters);
73
75
  }
@@ -56,7 +56,7 @@
56
56
  }
57
57
 
58
58
  &__participants {
59
- @apply bg-background rounded px-4 py-2 flex flex-col md:flex-row items-start md:items-center gap-2 md:gap-6 mb-14;
59
+ @apply bg-background rounded px-4 py-2 flex flex-wrap items-start md:items-center gap-2 md:gap-6 mb-14;
60
60
  }
61
61
 
62
62
  &__message {
@@ -106,12 +106,9 @@
106
106
  @apply text-gray-2 mr-1;
107
107
  }
108
108
  }
109
- }
110
109
 
111
- &:hover,
112
- &-hovered {
113
- > .dropdown__button {
114
- @apply text-white no-underline;
110
+ &:hover {
111
+ @apply text-white no-underline !bg-secondary;
115
112
 
116
113
  span {
117
114
  @apply text-white;
@@ -122,6 +119,26 @@
122
119
  }
123
120
  }
124
121
  }
122
+
123
+ &-opened {
124
+ > .dropdown__button {
125
+ @apply text-secondary no-underline;
126
+
127
+ span {
128
+ @apply text-secondary;
129
+ }
130
+
131
+ svg {
132
+ fill: rgb(var(--secondary-rgb) / var(--tw-bg-opacity)) !important;
133
+ }
134
+
135
+ &:hover {
136
+ svg {
137
+ fill: white !important;
138
+ }
139
+ }
140
+ }
141
+ }
125
142
  }
126
143
 
127
144
  &__bottom {
@@ -129,7 +129,7 @@
129
129
 
130
130
  /* overwrite default dropdowns */
131
131
  [data-target="dropdown-menu-filters"] {
132
- @apply px-0 justify-start [&>span]:text-gray-2;
132
+ @apply px-0 justify-start [&>span]:text-gray-2 md:hidden;
133
133
 
134
134
  > svg {
135
135
  @apply h-4 w-4 text-gray;
@@ -4,6 +4,11 @@ footer {
4
4
 
5
5
  &__top {
6
6
  @apply hidden lg:flex flex-row gap-8 container py-10;
7
+
8
+ .prose a,
9
+ .prose strong {
10
+ @apply text-white;
11
+ }
7
12
  }
8
13
 
9
14
  &__down {
@@ -2,7 +2,11 @@
2
2
  @apply invisible opacity-0 fixed z-50 inset-0 bg-[rgba(0,0,0,0.25)] transition duration-300;
3
3
 
4
4
  & > * {
5
- @apply absolute inset-1/2 ltr:-translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2 w-[90%] lg:max-w-[900px] max-h-[95vh] h-fit overflow-y-auto p-6 bg-white z-50 rounded shadow-[0_4px_6px_rgba(211,211,211,0.25)];
5
+ @apply absolute inset-1/2 translate-x-[-50%] translate-y-[-50%] w-[90%] lg:max-w-[900px] max-h-[95vh] h-fit overflow-y-auto p-6 bg-white z-50 rounded shadow-[0_4px_6px_rgba(211,211,211,0.25)];
6
+
7
+ [dir="rtl"] & {
8
+ @apply translate-x-[50%];
9
+ }
6
10
 
7
11
  & > svg:only-child {
8
12
  @apply w-8 h-8 mx-auto text-gray-2 fill-current animate-spin;
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  &__code {
11
- @apply block max-h-24 overflow-auto border border-gray-3 bg-background p-2 break-all;
11
+ @apply block max-h-24 overflow-auto border border-gray-3 bg-background p-2 break-words whitespace-pre-wrap;
12
12
  }
13
13
 
14
14
  a {
@@ -10,6 +10,7 @@ module Decidim
10
10
  # overwrite `BasePresenter#user_presenter` to return your custom user presenter.
11
11
  # The only requirement for custom renderers is that they should respond to `present`.
12
12
  class UserPresenter
13
+ include Decidim::SanitizeHelper
13
14
  # Public: Initializes the presenter.
14
15
  #
15
16
  # user - An instance of Decidim::User
@@ -60,7 +61,7 @@ module Decidim
60
61
  #
61
62
  # Returns an HTML-safe String.
62
63
  def present_user_name
63
- extra["name"].html_safe
64
+ decidim_sanitize_translated(extra["name"]).html_safe
64
65
  end
65
66
 
66
67
  # Private: Presents the nickname of the user performing the action.
@@ -7,6 +7,10 @@ module Decidim
7
7
  translated_attribute(name).html_safe
8
8
  end
9
9
 
10
+ def html_short_name
11
+ translated_attribute(short_name).html_safe
12
+ end
13
+
10
14
  def translated_description
11
15
  ActionView::Base.full_sanitizer.sanitize(translated_attribute(description)).html_safe
12
16
  end