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.
- checksums.yaml +4 -4
- data/app/cells/decidim/content_blocks/html_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/static_page/section_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/static_page/summary_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/static_page/two_pane_section_cell.rb +2 -2
- data/app/cells/decidim/data_consent/category.erb +5 -5
- data/app/cells/decidim/upload_modal_cell.rb +5 -0
- data/app/controllers/decidim/download_your_data_controller.rb +1 -1
- data/app/controllers/decidim/notifications_subscriptions_controller.rb +8 -0
- data/app/controllers/decidim/private_downloads_controller.rb +29 -0
- data/app/models/decidim/attachment.rb +20 -2
- data/app/models/decidim/authorization.rb +7 -0
- data/app/models/decidim/private_download.rb +61 -0
- data/app/models/decidim/private_export.rb +6 -0
- data/app/packs/src/decidim/sw/push-permissions.js +48 -13
- data/app/services/decidim/notifications_subscriptions_persistor.rb +6 -0
- data/app/services/decidim/push_subscription_endpoint_validator.rb +34 -0
- data/app/services/decidim/send_push_notification.rb +5 -1
- data/app/views/decidim/notifications_settings/show.html.erb +5 -5
- data/config/locales/ar.yml +2 -1
- data/config/locales/bg.yml +4 -3
- data/config/locales/bs-BA.yml +1 -0
- data/config/locales/ca-IT.yml +5 -3
- data/config/locales/ca.yml +5 -3
- data/config/locales/cs.yml +6 -3
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +4 -3
- data/config/locales/el.yml +2 -1
- data/config/locales/en.yml +1 -0
- data/config/locales/eo.yml +1 -0
- data/config/locales/es-MX.yml +5 -3
- data/config/locales/es-PY.yml +5 -3
- data/config/locales/es.yml +5 -3
- data/config/locales/et.yml +1 -0
- data/config/locales/eu.yml +5 -3
- data/config/locales/fa-IR.yml +1 -0
- data/config/locales/fi-plain.yml +9 -3
- data/config/locales/fi.yml +11 -5
- data/config/locales/fr-CA.yml +5 -3
- data/config/locales/fr.yml +5 -3
- data/config/locales/ga-IE.yml +1 -0
- data/config/locales/gl.yml +2 -1
- data/config/locales/gn-PY.yml +1 -0
- data/config/locales/hr.yml +1 -0
- data/config/locales/hu.yml +2 -1
- data/config/locales/id-ID.yml +2 -1
- data/config/locales/is-IS.yml +2 -1
- data/config/locales/it.yml +12 -1
- data/config/locales/ja.yml +4 -3
- data/config/locales/ka-GE.yml +1 -0
- data/config/locales/kaa.yml +1 -0
- data/config/locales/ko.yml +1 -0
- data/config/locales/lb.yml +2 -1
- data/config/locales/lt.yml +2 -1
- data/config/locales/lv.yml +2 -1
- data/config/locales/mt.yml +1 -0
- data/config/locales/nl.yml +2 -1
- data/config/locales/no.yml +2 -1
- data/config/locales/oc-FR.yml +1 -0
- data/config/locales/pl.yml +4 -3
- data/config/locales/pt-BR.yml +5 -4
- data/config/locales/pt.yml +2 -1
- data/config/locales/ro-RO.yml +2 -1
- data/config/locales/ru.yml +1 -0
- data/config/locales/sk.yml +1437 -1
- data/config/locales/sl.yml +1 -0
- data/config/locales/sr-CS.yml +1 -0
- data/config/locales/sv.yml +5 -3
- data/config/locales/tr-TR.yml +2 -1
- data/config/locales/uk.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +2 -1
- data/config/locales/zh-TW.yml +2 -1
- data/config/routes.rb +2 -0
- data/decidim-core.gemspec +1 -1
- data/lib/decidim/core/version.rb +1 -1
- metadata +8 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b4e8ed9025a083ab3351ad881448605343e89a1823e3616b250b9473a2c9bdb
|
|
4
|
+
data.tar.gz: a6b96945cdd6f5722d251d41b0576bccf3ff2aae1e4aa0c51e0ab47a9c257360
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ae197ee72cf96014bcb4ac200f53c19d0fe8d9bfffbcb32b266530a972b407a0f7dfd585b600d311830731231fb8a5ab96d740058f498120e30d41aa8aac17a
|
|
7
|
+
data.tar.gz: a526e1c79feb561a1cd2f90e0bba221301698ebb4df70dd1911de2685c705a2de703d42755904ddd2fd2b075ba8861eafa4f806490b5f86b69e8c45e6259bf18
|
|
@@ -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)
|
|
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)
|
|
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
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</h3>
|
|
16
|
+
<h3 id="accordion-title-<%= category[:slug] %>" class="cookies__category-trigger-title">
|
|
17
|
+
<%= category[:title] %>
|
|
18
|
+
</h3>
|
|
20
19
|
|
|
21
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
document.addEventListener("turbo:load", async () => {
|
|
2
2
|
const GRANTED_PERMISSION = "granted"
|
|
3
3
|
|
|
4
4
|
const hideReminder = function() {
|
|
5
|
-
const reminder = document.querySelector("
|
|
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("
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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.
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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">
|
data/config/locales/ar.yml
CHANGED
|
@@ -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} أشخاص يتابعونك."
|
data/config/locales/bg.yml
CHANGED
|
@@ -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
|
-
|
|
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} човека."
|
data/config/locales/bs-BA.yml
CHANGED
data/config/locales/ca-IT.yml
CHANGED
|
@@ -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
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
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:
|
data/config/locales/ca.yml
CHANGED
|
@@ -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
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
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:
|