decidim-core 0.32.0.rc1 → 0.32.0.rc2

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/content_blocks/html_cell.rb +1 -1
  3. data/app/cells/decidim/content_blocks/static_page/section_cell.rb +1 -1
  4. data/app/cells/decidim/content_blocks/static_page/summary_cell.rb +1 -1
  5. data/app/cells/decidim/content_blocks/static_page/two_pane_section_cell.rb +2 -2
  6. data/app/cells/decidim/data_consent/category.erb +5 -5
  7. data/app/cells/decidim/upload_modal_cell.rb +5 -0
  8. data/app/controllers/decidim/download_your_data_controller.rb +1 -1
  9. data/app/controllers/decidim/notifications_subscriptions_controller.rb +8 -0
  10. data/app/controllers/decidim/private_downloads_controller.rb +29 -0
  11. data/app/models/decidim/attachment.rb +20 -2
  12. data/app/models/decidim/authorization.rb +7 -0
  13. data/app/models/decidim/private_download.rb +61 -0
  14. data/app/models/decidim/private_export.rb +6 -0
  15. data/app/packs/src/decidim/sw/push-permissions.js +48 -13
  16. data/app/services/decidim/notifications_subscriptions_persistor.rb +6 -0
  17. data/app/services/decidim/push_subscription_endpoint_validator.rb +34 -0
  18. data/app/services/decidim/send_push_notification.rb +5 -1
  19. data/app/views/decidim/notifications_settings/show.html.erb +5 -5
  20. data/config/locales/ar.yml +2 -1
  21. data/config/locales/bg.yml +4 -3
  22. data/config/locales/bs-BA.yml +1 -0
  23. data/config/locales/ca-IT.yml +5 -3
  24. data/config/locales/ca.yml +5 -3
  25. data/config/locales/cs.yml +6 -3
  26. data/config/locales/da.yml +1 -0
  27. data/config/locales/de.yml +4 -3
  28. data/config/locales/el.yml +2 -1
  29. data/config/locales/en.yml +1 -0
  30. data/config/locales/eo.yml +1 -0
  31. data/config/locales/es-MX.yml +5 -3
  32. data/config/locales/es-PY.yml +5 -3
  33. data/config/locales/es.yml +5 -3
  34. data/config/locales/et.yml +1 -0
  35. data/config/locales/eu.yml +5 -3
  36. data/config/locales/fa-IR.yml +1 -0
  37. data/config/locales/fi-plain.yml +9 -3
  38. data/config/locales/fi.yml +11 -5
  39. data/config/locales/fr-CA.yml +5 -3
  40. data/config/locales/fr.yml +5 -3
  41. data/config/locales/ga-IE.yml +1 -0
  42. data/config/locales/gl.yml +2 -1
  43. data/config/locales/gn-PY.yml +1 -0
  44. data/config/locales/hr.yml +1 -0
  45. data/config/locales/hu.yml +2 -1
  46. data/config/locales/id-ID.yml +2 -1
  47. data/config/locales/is-IS.yml +2 -1
  48. data/config/locales/it.yml +12 -1
  49. data/config/locales/ja.yml +4 -3
  50. data/config/locales/ka-GE.yml +1 -0
  51. data/config/locales/kaa.yml +1 -0
  52. data/config/locales/ko.yml +1 -0
  53. data/config/locales/lb.yml +2 -1
  54. data/config/locales/lt.yml +2 -1
  55. data/config/locales/lv.yml +2 -1
  56. data/config/locales/mt.yml +1 -0
  57. data/config/locales/nl.yml +2 -1
  58. data/config/locales/no.yml +2 -1
  59. data/config/locales/oc-FR.yml +1 -0
  60. data/config/locales/pl.yml +4 -3
  61. data/config/locales/pt-BR.yml +5 -4
  62. data/config/locales/pt.yml +2 -1
  63. data/config/locales/ro-RO.yml +2 -1
  64. data/config/locales/ru.yml +1 -0
  65. data/config/locales/sk.yml +1437 -1
  66. data/config/locales/sl.yml +1 -0
  67. data/config/locales/sr-CS.yml +1 -0
  68. data/config/locales/sv.yml +5 -3
  69. data/config/locales/tr-TR.yml +2 -1
  70. data/config/locales/uk.yml +1 -0
  71. data/config/locales/vi.yml +1 -0
  72. data/config/locales/zh-CN.yml +2 -1
  73. data/config/locales/zh-TW.yml +2 -1
  74. data/config/routes.rb +2 -0
  75. data/decidim-core.gemspec +1 -1
  76. data/lib/decidim/core/version.rb +1 -1
  77. metadata +8 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: caaaf7bbddd64f7bb5d4341c6ebeab59b35dd0f69326b136d11958165e513801
4
- data.tar.gz: e18600f3eec00c3fbf76b58e90a34bf4888a114f7dc5a9d35896540d30a7a20f
3
+ metadata.gz: 3b4e8ed9025a083ab3351ad881448605343e89a1823e3616b250b9473a2c9bdb
4
+ data.tar.gz: a6b96945cdd6f5722d251d41b0576bccf3ff2aae1e4aa0c51e0ab47a9c257360
5
5
  SHA512:
6
- metadata.gz: 847ec9bd0ae30f358fd5330e3bf61cd34adedca669986b5082e391473e3f66df4e7eab20bac8ad4478827fd66b2d3e94e46559ef4138cf289d5a71caf60e904b
7
- data.tar.gz: b832cfbd8827d809a1792ff0f7a6688f2282e3f2bfc8975bf821175465333ade87ef0e2fd92bf9ac3512f54e40d31872d2f35a28424b8549de717e2f645141b3
6
+ metadata.gz: 0ae197ee72cf96014bcb4ac200f53c19d0fe8d9bfffbcb32b266530a972b407a0f7dfd585b600d311830731231fb8a5ab96d740058f498120e30d41aa8aac17a
7
+ data.tar.gz: a526e1c79feb561a1cd2f90e0bba221301698ebb4df70dd1911de2685c705a2de703d42755904ddd2fd2b075ba8861eafa4f806490b5f86b69e8c45e6259bf18
@@ -8,7 +8,7 @@ module Decidim
8
8
  end
9
9
 
10
10
  def html_content
11
- translated_attribute(model.settings.html_content).html_safe
11
+ decidim_sanitize_editor_admin(translated_attribute(model.settings.html_content))
12
12
  end
13
13
  end
14
14
  end
@@ -5,7 +5,7 @@ module Decidim
5
5
  module StaticPage
6
6
  class SectionCell < Decidim::ViewModel
7
7
  def content
8
- translated_attribute(model.settings.content).html_safe
8
+ decidim_sanitize_editor_admin(translated_attribute(model.settings.content))
9
9
  end
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module Decidim
5
5
  module StaticPage
6
6
  class SummaryCell < Decidim::ViewModel
7
7
  def content
8
- translated_attribute(model.settings.summary).html_safe
8
+ decidim_sanitize_editor_admin(translated_attribute(model.settings.summary))
9
9
  end
10
10
  end
11
11
  end
@@ -5,11 +5,11 @@ module Decidim
5
5
  module StaticPage
6
6
  class TwoPaneSectionCell < Decidim::ViewModel
7
7
  def left_column
8
- translated_attribute(model.settings.left_column).html_safe
8
+ decidim_sanitize_editor_admin(translated_attribute(model.settings.left_column))
9
9
  end
10
10
 
11
11
  def right_column
12
- translated_attribute(model.settings.right_column).html_safe
12
+ decidim_sanitize_editor_admin(translated_attribute(model.settings.right_column))
13
13
  end
14
14
  end
15
15
  end
@@ -13,12 +13,12 @@
13
13
  <%= icon "close-line", class: "cookies__category-toggle-icon" %>
14
14
  </label>
15
15
 
16
- <div id="accordion-trigger-<%= category[:slug] %>" role="group" data-controls="accordion-panel-<%= category[:slug] %>" aria-labelledby="accordion-title-<%= category[:slug] %>">
17
- <h3 id="accordion-title-<%= category[:slug] %>" class="cookies__category-trigger-title">
18
- <%= category[:title] %>
19
- </h3>
16
+ <h3 id="accordion-title-<%= category[:slug] %>" class="cookies__category-trigger-title">
17
+ <%= category[:title] %>
18
+ </h3>
20
19
 
21
- <span>
20
+ <div id="accordion-trigger-<%= category[:slug] %>" role="group" data-controls="accordion-panel-<%= category[:slug] %>" aria-labelledby="accordion-title-<%= category[:slug] %>">
21
+ <span aria-hidden="true">
22
22
  <%= icon "arrow-down-s-line", class: "cookies__category-trigger-arrow" %>
23
23
  <%= icon "arrow-up-s-line", class: "cookies__category-trigger-arrow" %>
24
24
  </span>
@@ -177,6 +177,11 @@ module Decidim
177
177
 
178
178
  def file_attachment_path(attachment)
179
179
  return unless attachment
180
+
181
+ if attachment.respond_to?(:record) && attachment.record.is_a?(Decidim::Authorization) && attachment.name.to_s == "verification_attachment"
182
+ return decidim.private_download_path(Decidim::PrivateDownload.for(attachment.record, attachment_name: attachment.name).token)
183
+ end
184
+
180
185
  return Rails.application.routes.url_helpers.rails_blob_url(attachment, only_path: true) if attachment.is_a? ActiveStorage::Blob
181
186
 
182
187
  if attachment.try(:attached?)
@@ -50,7 +50,7 @@ module Decidim
50
50
  flash[:error] = t("decidim.account.download_your_data_export.export_expired")
51
51
  redirect_to download_your_data_path
52
52
  elsif private_export.file.attached?
53
- redirect_to Rails.application.routes.url_helpers.rails_blob_url(private_export.file.blob, only_path: true)
53
+ redirect_to private_download_path(Decidim::PrivateDownload.for(private_export, attachment_name: :file).token)
54
54
  else
55
55
  flash[:error] = t("decidim.account.download_your_data_export.file_no_exists")
56
56
  redirect_to download_your_data_path
@@ -3,6 +3,8 @@
3
3
  module Decidim
4
4
  # The controller to handle the subscriptions to push notifications
5
5
  class NotificationsSubscriptionsController < Decidim::ApplicationController
6
+ rescue_from Decidim::NotificationsSubscriptionsPersistor::UnsupportedPushSubscriptionEndpointError, with: :unsupported_browser
7
+
6
8
  def create
7
9
  Decidim::NotificationsSubscriptionsPersistor.new(current_user).add_subscription(params)
8
10
  head :ok
@@ -12,5 +14,11 @@ module Decidim
12
14
  Decidim::NotificationsSubscriptionsPersistor.new(current_user).delete_subscription(params[:auth])
13
15
  head :ok
14
16
  end
17
+
18
+ private
19
+
20
+ def unsupported_browser
21
+ render json: { error: I18n.t("notifications_settings.show.push_notifications_unsupported_browser", scope: "decidim") }, status: :unprocessable_content
22
+ end
15
23
  end
16
24
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class PrivateDownloadsController < Decidim::ApplicationController
5
+ before_action :authenticate_user!
6
+
7
+ def show
8
+ return head :not_found unless private_download.attached?
9
+ return head :not_found unless private_download.authorized_for?(current_user)
10
+
11
+ disposition = private_download.attachment.content_type.start_with?("image/") ? :inline : :attachment
12
+
13
+ send_data(
14
+ private_download.attachment.download,
15
+ filename: private_download.attachment.filename.to_s,
16
+ type: private_download.attachment.content_type,
17
+ disposition:
18
+ )
19
+ rescue Decidim::PrivateDownload::InvalidTokenError
20
+ head :not_found
21
+ end
22
+
23
+ private
24
+
25
+ def private_download
26
+ @private_download ||= Decidim::PrivateDownload.from_token(params[:id])
27
+ end
28
+ end
29
+ end
@@ -88,7 +88,7 @@ module Decidim
88
88
  # Returns String.
89
89
  def file_type
90
90
  if file?
91
- url&.split(".")&.last&.split("&")&.first&.downcase
91
+ file.filename.extension&.downcase
92
92
  elsif link?
93
93
  "link"
94
94
  end
@@ -100,7 +100,13 @@ module Decidim
100
100
  def url
101
101
  @url ||=
102
102
  if file?
103
- attached_uploader(:file).url
103
+ if private_download_required?
104
+ Decidim::Core::Engine.routes.url_helpers.private_download_path(
105
+ Decidim::PrivateDownload.for(self, attachment_name: :file).token
106
+ )
107
+ else
108
+ attached_uploader(:file).url
109
+ end
104
110
  elsif link?
105
111
  link
106
112
  end
@@ -144,5 +150,17 @@ module Decidim
144
150
 
145
151
  attached_to.can_participate?(user)
146
152
  end
153
+
154
+ def private_download_authorized?(user, requested_attachment_name)
155
+ return false unless requested_attachment_name.to_s == "file"
156
+
157
+ can_participate?(user)
158
+ end
159
+
160
+ def private_download_required?
161
+ return attached_to.restricted? if attached_to.respond_to?(:restricted?)
162
+
163
+ attached_to.respond_to?(:component) && attached_to.component&.restricted_space?
164
+ end
147
165
  end
148
166
  end
@@ -91,6 +91,13 @@ module Decidim
91
91
  Decidim::AuthorizationTransfer.perform!(self, handler)
92
92
  end
93
93
 
94
+ def private_download_authorized?(user, requested_attachment_name)
95
+ return false unless requested_attachment_name.to_s == "verification_attachment"
96
+ return true if user&.admin? && user.organization == organization
97
+
98
+ user == self.user
99
+ end
100
+
94
101
  private
95
102
 
96
103
  def active_handler?
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class PrivateDownload
5
+ class InvalidTokenError < StandardError; end
6
+
7
+ VERIFIER_PURPOSE = :private_download
8
+
9
+ def self.for(record, attachment_name:)
10
+ new(record:, attachment_name:)
11
+ end
12
+
13
+ def self.from_token(token)
14
+ payload = verifier.verify(token, purpose: VERIFIER_PURPOSE).with_indifferent_access
15
+ record = GlobalID::Locator.locate(payload[:gid])
16
+
17
+ raise InvalidTokenError if record.blank?
18
+
19
+ new(record:, attachment_name: payload[:attachment_name])
20
+ rescue ActiveSupport::MessageVerifier::InvalidSignature, TypeError
21
+ raise InvalidTokenError
22
+ end
23
+
24
+ def self.verifier
25
+ @verifier ||= ActiveSupport::MessageVerifier.new(Rails.application.secret_key_base, serializer: JSON)
26
+ end
27
+
28
+ def initialize(record:, attachment_name:)
29
+ @record = record
30
+ @attachment_name = attachment_name.to_s
31
+ end
32
+
33
+ def token
34
+ self.class.verifier.generate(
35
+ {
36
+ gid: record.to_global_id.to_s,
37
+ attachment_name:
38
+ },
39
+ purpose: VERIFIER_PURPOSE
40
+ )
41
+ end
42
+
43
+ def attachment
44
+ record.public_send(attachment_name)
45
+ end
46
+
47
+ def attached?
48
+ attachment.respond_to?(:attached?) && attachment.attached?
49
+ end
50
+
51
+ def authorized_for?(user)
52
+ return false unless record.respond_to?(:private_download_authorized?)
53
+
54
+ record.private_download_authorized?(user, attachment_name)
55
+ end
56
+
57
+ private
58
+
59
+ attr_reader :record, :attachment_name
60
+ end
61
+ end
@@ -24,5 +24,11 @@ module Decidim
24
24
  self.content_type = file.content_type
25
25
  self.file_size = file.byte_size
26
26
  end
27
+
28
+ def private_download_authorized?(user, requested_attachment_name)
29
+ return false unless requested_attachment_name.to_s == "file"
30
+
31
+ attached_to == user
32
+ end
27
33
  end
28
34
  end
@@ -1,16 +1,38 @@
1
- window.addEventListener("turbo:load", async () => {
1
+ document.addEventListener("turbo:load", async () => {
2
2
  const GRANTED_PERMISSION = "granted"
3
3
 
4
4
  const hideReminder = function() {
5
- const reminder = document.querySelector("#push-notifications-reminder")
5
+ const reminder = document.querySelector("[data-push-notifications-reminder]")
6
+ if (!reminder) {
7
+ return;
8
+ }
9
+
6
10
  reminder.classList.add("hide")
7
11
  }
8
12
 
13
+ const showError = (message) => {
14
+ const container = document.querySelector("[data-push-notifications-container]")
15
+ if (!container) {
16
+ return;
17
+ }
18
+
19
+ const existingError = container.querySelector("[data-push-notifications-error]")
20
+ if (existingError) {
21
+ existingError.remove()
22
+ }
23
+
24
+ const errorElement = document.createElement("div")
25
+ errorElement.dataset.pushNotificationsError = "true"
26
+ errorElement.classList.add("flash", "alert", "push-notifications__error")
27
+ errorElement.innerText = message
28
+ container.prepend(errorElement)
29
+ }
30
+
9
31
  const subscribeToNotifications = async (registration) => {
10
32
  const permission = await window.Notification.requestPermission();
11
33
 
12
34
  if (registration && permission === GRANTED_PERMISSION) {
13
- const vapidElement = document.querySelector("#vapidPublicKey")
35
+ const vapidElement = document.querySelector("[data-push-vapid-public-key]")
14
36
  // element could not exist in DOM
15
37
  if (vapidElement) {
16
38
  const vapidPublicKeyElement = JSON.parse(vapidElement.value)
@@ -20,7 +42,7 @@ window.addEventListener("turbo:load", async () => {
20
42
  });
21
43
 
22
44
  if (subscription) {
23
- await fetch("/notifications_subscriptions", {
45
+ const response = await fetch("/notifications_subscriptions", {
24
46
  headers: {
25
47
  "Content-Type": "application/json",
26
48
  "X-CSRF-Token": document.querySelector("meta[name=csrf-token]")?.content
@@ -28,6 +50,11 @@ window.addEventListener("turbo:load", async () => {
28
50
  method: "POST",
29
51
  body: JSON.stringify(subscription)
30
52
  });
53
+
54
+ if (!response.ok) {
55
+ const body = await response.json()
56
+ throw new Error(body.error)
57
+ }
31
58
  }
32
59
  }
33
60
  hideReminder()
@@ -57,10 +84,13 @@ window.addEventListener("turbo:load", async () => {
57
84
  hideReminder()
58
85
  if (currentSubscription) {
59
86
  const auth = currentSubscription.toJSON().keys.auth
60
- const subKeys = JSON.parse(document.querySelector("#subKeys").value)
61
- // Subscribed && browser notifications enabled
62
- if (subKeys.includes(auth)) {
63
- toggleChecked = true
87
+ const subKeysElement = document.querySelector("[data-push-sub-keys]")
88
+ if (subKeysElement) {
89
+ const subKeys = JSON.parse(subKeysElement.value)
90
+ // Subscribed && browser notifications enabled
91
+ if (subKeys.includes(auth)) {
92
+ toggleChecked = true
93
+ }
64
94
  }
65
95
  }
66
96
  }
@@ -68,7 +98,7 @@ window.addEventListener("turbo:load", async () => {
68
98
  }
69
99
 
70
100
  if ("serviceWorker" in navigator) {
71
- const toggle = document.getElementById("allow_push_notifications")
101
+ const toggle = document.querySelector("[data-push-notifications-toggle]")
72
102
 
73
103
  if (toggle) {
74
104
  const registration = await navigator.serviceWorker.ready
@@ -76,10 +106,15 @@ window.addEventListener("turbo:load", async () => {
76
106
  setToggleState(registration, toggle)
77
107
 
78
108
  toggle.addEventListener("change", async ({ target }) => {
79
- if (target.checked) {
80
- await subscribeToNotifications(registration);
81
- } else {
82
- await unsubscribeFromNotifications(registration)
109
+ try {
110
+ if (target.checked) {
111
+ await subscribeToNotifications(registration)
112
+ } else {
113
+ await unsubscribeFromNotifications(registration)
114
+ }
115
+ } catch (error) {
116
+ target.checked = false
117
+ showError(error.message)
83
118
  }
84
119
  })
85
120
  }
@@ -4,6 +4,10 @@ module Decidim
4
4
  # This class manages the creation and deletion of user notifications
5
5
 
6
6
  class NotificationsSubscriptionsPersistor
7
+ include PushSubscriptionEndpointValidator
8
+
9
+ class UnsupportedPushSubscriptionEndpointError < StandardError; end
10
+
7
11
  attr_reader :user
8
12
 
9
13
  def initialize(user)
@@ -11,6 +15,8 @@ module Decidim
11
15
  end
12
16
 
13
17
  def add_subscription(params)
18
+ raise UnsupportedPushSubscriptionEndpointError unless supported_push_subscription_endpoint?(params[:endpoint])
19
+
14
20
  subscriptions = user.notification_settings["subscriptions"] || {}
15
21
  filtered_params = filter_params(params)
16
22
  new_subscription = { filtered_params[:auth] => filtered_params }
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # Shared validation for browser push subscription endpoints.
5
+ module PushSubscriptionEndpointValidator
6
+ private
7
+
8
+ def supported_push_subscription_endpoint?(endpoint)
9
+ return false if endpoint.blank?
10
+
11
+ uri = URI.parse(endpoint)
12
+ return false unless uri.is_a?(URI::HTTPS)
13
+
14
+ host = uri.host&.downcase
15
+ return false if host.blank?
16
+
17
+ allowed_push_subscription_endpoint_patterns.any? { |pattern| pattern.match?(host) }
18
+ rescue URI::InvalidURIError
19
+ false
20
+ end
21
+
22
+ # Override this method to customize the browser push endpoint allowlist.
23
+ def allowed_push_subscription_endpoint_patterns
24
+ [
25
+ /\A(?:.*\.)?push\.services\.mozilla\.com\z/,
26
+ /\A(?:.*\.)?fcm\.googleapis\.com\z/,
27
+ /\A(?:.*\.)?android\.googleapis\.com\z/,
28
+ /\A(?:.*\.)?push\.apple\.com\z/,
29
+ /\A(?:.*\.)?opera\.com\z/,
30
+ /\A(?:.*\.)?notify\.windows\.com\z/
31
+ ]
32
+ end
33
+ end
34
+ end
@@ -10,6 +10,7 @@ module Decidim
10
10
 
11
11
  class SendPushNotification
12
12
  include ActionView::Helpers::UrlHelper
13
+ include PushSubscriptionEndpointValidator
13
14
 
14
15
  # Send the push notification. Returns `nil` if the user did not allowed push notifications
15
16
  # or if the subscription to push notifications does not exist
@@ -24,9 +25,12 @@ module Decidim
24
25
  raise ArgumentError, "Need to provide a title if the notification is a PushNotificationMessage" if notification.is_a?(Decidim::PushNotificationMessage) && title.nil?
25
26
 
26
27
  user = notification.user
28
+ subscriptions = user.notifications_subscriptions.values.select do |subscription|
29
+ supported_push_subscription_endpoint?(subscription["endpoint"])
30
+ end
27
31
 
28
32
  I18n.with_locale(user.locale || user.organization.default_locale) do
29
- user.notifications_subscriptions.values.map do |subscription|
33
+ subscriptions.map do |subscription|
30
34
  payload = build_payload(message_params(notification, title), subscription)
31
35
  # Capture webpush exceptions in order to avoid this call to be repeated by the background job runner
32
36
  # Webpush::Error class is the parent class of all defined errors
@@ -167,20 +167,20 @@
167
167
  <% end %>
168
168
 
169
169
  <% if @notifications_settings.meet_push_notifications_requirements? %>
170
- <div class="push-notifications js-sw-mandatory">
170
+ <div class="push-notifications js-sw-mandatory" data-push-notifications-container>
171
171
  <label>
172
172
  <%= t("push_notifications", scope: "decidim.notifications_settings.show") %>
173
173
  </label>
174
- <p id="push-notifications-reminder" class="push-notifications__reminder block my-4">
174
+ <p id="push-notifications-reminder" class="push-notifications__reminder block my-4" data-push-notifications-reminder>
175
175
  <%= t("push_notifications_reminder", scope: "decidim.notifications_settings.show") %>
176
176
  </p>
177
177
  <div class="toggle__switch-trigger">
178
178
  <label class="toggle__switch-toggle" for="allow_push_notifications">
179
179
  <span>
180
180
  <input
181
- <%== %(checked="checked") if @notifications_settings.meet_push_notifications_requirements? %>
182
181
  id="allow_push_notifications"
183
182
  type="checkbox"
183
+ data-push-notifications-toggle
184
184
  name="allow_push_notifications">
185
185
  <span class="toggle__switch-toggle-content">
186
186
  </span>
@@ -194,8 +194,8 @@
194
194
  </div>
195
195
  </div>
196
196
 
197
- <input id="vapidPublicKey" name="vapid_public_key" type="hidden" value="<%= Base64.urlsafe_decode64(Decidim.vapid_public_key.to_s).bytes %>">
198
- <input id="subKeys" name="sub_key" type="hidden" value="<%= current_user.notifications_subscriptions.keys %>">
197
+ <input id="vapidPublicKey" name="vapid_public_key" data-push-vapid-public-key type="hidden" value="<%= Base64.urlsafe_decode64(Decidim.vapid_public_key.to_s).bytes.to_json %>">
198
+ <input id="subKeys" name="sub_key" data-push-sub-keys type="hidden" value="<%= current_user.notifications_subscriptions.keys.to_json %>">
199
199
  <% end %>
200
200
 
201
201
  <div class="form__wrapper-block">
@@ -1,3 +1,4 @@
1
+ ---
1
2
  ar:
2
3
  activemodel:
3
4
  attributes:
@@ -718,7 +719,7 @@ ar:
718
719
  badges:
719
720
  followers:
720
721
  conditions:
721
- - كونك نشيطًا ومتابعة أشخاصًا آخرين سيجعل بالتأكيد أشخاصًا آخرين يتبعونك.
722
+ - كونك نشيطًا ومتابعة أشخاصًا آخرين سيجعل بالتأكيد أشخاصًا آخرين يتبعونك.
722
723
  description: تُمنح هذه الشارة عندما تصل إلى عدد معين من المتابعين. %{organization_name} هي شبكة اجتماعية وسياسية ، نسج الويب الخاص بك للتواصل مع أشخاص آخرين في المنصة.
723
724
  description_another: هذا المشارك لديه %{score} متابعين.
724
725
  description_own: "%{score} أشخاص يتابعونك."
@@ -1,3 +1,4 @@
1
+ ---
1
2
  bg:
2
3
  activemodel:
3
4
  attributes:
@@ -777,8 +778,8 @@ bg:
777
778
  attachment_link:
778
779
  explanation: Ръководство за прикачени линкове
779
780
  help_messages:
780
- - Приемат се само правилни URL формати.
781
- - Моля, имайте предвид, че линковете трябва да са публични, така че всички участници да имат достъп до тях.
781
+ - Приемат се само правилни URL формати.
782
+ - Моля, имайте предвид, че линковете трябва да са публични, така че всички участници да имат достъп до тях.
782
783
  errors:
783
784
  decidim/user:
784
785
  password: Паролата е твърде къса.
@@ -826,7 +827,7 @@ bg:
826
827
  badges:
827
828
  followers:
828
829
  conditions:
829
- - Ако сте активни и следвате други хора, със сигурност ще накарате други хора да ви следват.
830
+ - Ако сте активни и следвате други хора, със сигурност ще накарате други хора да ви следват.
830
831
  description: Ще заслужите тази значка, ако достигнете определен брой последователи. %{organization_name} е социална и политическа мрежа. Изградете собствена мрежа, за да общувате с други хора в платформата.
831
832
  description_another: Този участник има %{score} последователи.
832
833
  description_own: "Следват Ви %{score} човека."
@@ -1,3 +1,4 @@
1
+ ---
1
2
  bs:
2
3
  decidim:
3
4
  accessibility:
@@ -1,3 +1,4 @@
1
+ ---
1
2
  ca-IT:
2
3
  activemodel:
3
4
  attributes:
@@ -1034,8 +1035,8 @@ ca-IT:
1034
1035
  attachment_link:
1035
1036
  explanation: Guia per enllaços adjunts
1036
1037
  help_messages:
1037
- - Només s'accepten format d'URL correctes.
1038
- - Tingues en compte que els enllaços han de ser públics perquè tots els participants hi puguin accedir.
1038
+ - Només s'accepten format d'URL correctes.
1039
+ - Tingues en compte que els enllaços han de ser públics perquè tots els participants hi puguin accedir.
1039
1040
  errors:
1040
1041
  decidim/user:
1041
1042
  password: La contrasenya és massa curta.
@@ -1089,7 +1090,7 @@ ca-IT:
1089
1090
  badges:
1090
1091
  followers:
1091
1092
  conditions:
1092
- - Ser activa i seguir a altres persones segurament farà que altres persones et segueixin.
1093
+ - Ser activa i seguir a altres persones segurament farà que altres persones et segueixin.
1093
1094
  description: Aquesta distinció es concedeix quan arribis a un cert nombre de seguidors. %{organization_name} és una xarxa social i política, teixeix la teva web per comunicar-te amb altres persones a la plataforma.
1094
1095
  description_another: Aquesta participant té %{score} seguidores.
1095
1096
  description_own: "%{score} participants et segueixen."
@@ -1340,6 +1341,7 @@ ca-IT:
1340
1341
  own_activity: La meva pròpia activitat, com quan algú fa comentaris a la meva proposta o em menciona
1341
1342
  push_notifications: Notificacions emergents
1342
1343
  push_notifications_reminder: Per rebre notificacions de la plataforma, primer les has de permetre a la configuració del teu navegador.
1344
+ push_notifications_unsupported_browser: El navegador no és compatible.
1343
1345
  receive_notifications_about: Vull rebre notificacions
1344
1346
  update_notifications_settings: Guardar canvis
1345
1347
  update:
@@ -1,3 +1,4 @@
1
+ ---
1
2
  ca:
2
3
  activemodel:
3
4
  attributes:
@@ -1034,8 +1035,8 @@ ca:
1034
1035
  attachment_link:
1035
1036
  explanation: Guia per enllaços adjunts
1036
1037
  help_messages:
1037
- - Només s'accepten format d'URL correctes.
1038
- - Tingues en compte que els enllaços han de ser públics perquè tots els participants hi puguin accedir.
1038
+ - Només s'accepten format d'URL correctes.
1039
+ - Tingues en compte que els enllaços han de ser públics perquè tots els participants hi puguin accedir.
1039
1040
  errors:
1040
1041
  decidim/user:
1041
1042
  password: La contrasenya és massa curta.
@@ -1089,7 +1090,7 @@ ca:
1089
1090
  badges:
1090
1091
  followers:
1091
1092
  conditions:
1092
- - Ser activa i seguir a altres persones segurament farà que altres persones et segueixin.
1093
+ - Ser activa i seguir a altres persones segurament farà que altres persones et segueixin.
1093
1094
  description: Aquesta distinció es concedeix quan arribis a un cert nombre de seguidors. %{organization_name} és una xarxa social i política, teixeix la teva web per comunicar-te amb altres persones a la plataforma.
1094
1095
  description_another: Aquesta participant té %{score} seguidores.
1095
1096
  description_own: "%{score} participants et segueixen."
@@ -1340,6 +1341,7 @@ ca:
1340
1341
  own_activity: La meva pròpia activitat, com quan algú fa comentaris a la meva proposta o em menciona
1341
1342
  push_notifications: Notificacions emergents
1342
1343
  push_notifications_reminder: Per rebre notificacions de la plataforma, primer les has de permetre a la configuració del teu navegador.
1344
+ push_notifications_unsupported_browser: El navegador no és compatible.
1343
1345
  receive_notifications_about: Vull rebre notificacions
1344
1346
  update_notifications_settings: Guardar canvis
1345
1347
  update: