decidim-core 0.28.3 → 0.28.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/cells/decidim/activity_cell.rb +1 -4
- data/app/cells/decidim/author/show.erb +5 -4
- data/app/cells/decidim/author_cell.rb +26 -0
- data/app/cells/decidim/card_s/show.erb +5 -3
- data/app/cells/decidim/content_blocks/stats_cell.rb +1 -1
- data/app/cells/decidim/diff_cell.rb +4 -0
- data/app/cells/decidim/endorsement_buttons_cell.rb +1 -1
- data/app/cells/decidim/nav_links/show.erb +3 -3
- data/app/cells/decidim/newsletter_templates/image_text_cta_cell.rb +1 -1
- data/app/cells/decidim/resource_types_filter/show.erb +11 -12
- data/app/cells/decidim/translation_bar/show.erb +3 -3
- data/app/cells/decidim/translation_bar_cell.rb +1 -1
- data/app/commands/decidim/amendable/create_draft.rb +1 -0
- data/app/commands/decidim/destroy_account.rb +3 -0
- data/app/controllers/concerns/decidim/devise_authentication_methods.rb +1 -1
- data/app/controllers/concerns/decidim/direct_upload.rb +82 -0
- data/app/controllers/decidim/doorkeeper/credentials_controller.rb +1 -1
- data/app/controllers/decidim/links_controller.rb +1 -1
- data/app/controllers/decidim/profiles_controller.rb +4 -0
- data/app/forms/decidim/upload_validation_form.rb +1 -1
- data/app/helpers/concerns/decidim/user_role_checker.rb +46 -0
- data/app/helpers/decidim/cta_button_helper.rb +1 -1
- data/app/helpers/decidim/layout_helper.rb +28 -0
- data/app/helpers/decidim/map_helper.rb +6 -1
- data/app/helpers/decidim/menu_helper.rb +1 -1
- data/app/helpers/decidim/sanitize_helper.rb +11 -2
- data/app/helpers/decidim/scopes_helper.rb +5 -2
- data/app/models/decidim/action_log.rb +11 -1
- data/app/models/decidim/attachment.rb +1 -1
- data/app/packs/src/decidim/a11y.js +11 -15
- data/app/packs/src/decidim/append_redirect_url_to_modals.js +24 -14
- data/app/packs/src/decidim/direct_uploads/upload_field.js +21 -8
- data/app/packs/src/decidim/direct_uploads/upload_modal.js +3 -0
- data/app/packs/src/decidim/index.js +3 -0
- data/app/packs/src/decidim/remote_tooltips.js +38 -0
- data/app/packs/src/decidim/toggle.js +1 -1
- data/app/packs/src/decidim/tooltips.js +42 -22
- data/app/packs/stylesheets/decidim/_buttons.scss +1 -1
- data/app/packs/stylesheets/decidim/_modal_update.scss +4 -0
- data/app/packs/stylesheets/decidim/_profile.scss +1 -1
- data/app/packs/stylesheets/decidim/_progress-bar.scss +1 -1
- data/app/packs/stylesheets/decidim/legacy/conference-diploma.scss +2 -1
- data/app/presenters/decidim/attachment_presenter.rb +1 -1
- data/app/presenters/decidim/menu_item_presenter.rb +1 -1
- data/app/queries/decidim/last_activity.rb +16 -5
- data/app/services/decidim/base_diff_renderer.rb +26 -2
- data/app/services/decidim/email_notification_generator.rb +14 -5
- data/app/views/decidim/devise/omniauth_registrations/new.html.erb +1 -1
- data/app/views/decidim/offline/show.html.erb +1 -1
- data/app/views/decidim/pages/_tabbed.html.erb +5 -5
- data/app/views/decidim/shared/_filters.html.erb +5 -5
- data/app/views/decidim/shared/_orders.html.erb +3 -2
- data/app/views/decidim/shared/filters/_check_boxes_tree.html.erb +1 -1
- data/app/views/decidim/shared/filters/_collection.html.erb +1 -1
- data/app/views/layouts/decidim/_head.html.erb +1 -1
- data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_user_profile.html.erb +2 -2
- data/config/locales/ar.yml +12 -1
- data/config/locales/bg.yml +0 -1
- data/config/locales/bn-BD.yml +1 -0
- data/config/locales/bs-BA.yml +98 -0
- data/config/locales/ca.yml +14 -10
- data/config/locales/cs.yml +6 -1
- data/config/locales/de.yml +18 -14
- data/config/locales/el.yml +3 -1
- data/config/locales/en.yml +5 -1
- data/config/locales/es-MX.yml +6 -2
- data/config/locales/es-PY.yml +6 -2
- data/config/locales/es.yml +12 -8
- data/config/locales/eu.yml +202 -185
- data/config/locales/fi-plain.yml +5 -1
- data/config/locales/fi.yml +40 -36
- data/config/locales/fr-CA.yml +6 -2
- data/config/locales/fr.yml +5 -1
- data/config/locales/ga-IE.yml +5 -0
- data/config/locales/gl.yml +4 -1
- data/config/locales/hu.yml +1 -2
- data/config/locales/id-ID.yml +4 -0
- data/config/locales/is-IS.yml +4 -1
- data/config/locales/it.yml +40 -0
- data/config/locales/ja.yml +18 -16
- data/config/locales/lb.yml +5 -0
- data/config/locales/lt.yml +1 -2
- data/config/locales/lv.yml +4 -0
- data/config/locales/nl.yml +6 -1
- data/config/locales/no.yml +5 -0
- data/config/locales/pl.yml +1 -2
- data/config/locales/pt-BR.yml +199 -1
- data/config/locales/pt.yml +11 -0
- data/config/locales/ro-RO.yml +302 -180
- data/config/locales/ru.yml +4 -0
- data/config/locales/sk.yml +5 -1
- data/config/locales/sv.yml +452 -81
- data/config/locales/tr-TR.yml +6 -1
- data/config/locales/uk.yml +4 -1
- data/config/locales/zh-CN.yml +5 -0
- data/config/locales/zh-TW.yml +4 -1
- data/config/routes.rb +1 -0
- data/decidim-core.gemspec +4 -1
- data/lib/decidim/api/functions/component_list.rb +1 -1
- data/lib/decidim/api/functions/participatory_space_finder_base.rb +11 -1
- data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -1
- data/lib/decidim/api/types/component_type.rb +7 -0
- data/lib/decidim/api/types/user_group_type.rb +4 -0
- data/lib/decidim/api/types/user_type.rb +4 -0
- data/lib/decidim/attributes/rich_text.rb +38 -0
- data/lib/decidim/attributes/time_with_zone.rb +11 -1
- data/lib/decidim/attributes.rb +2 -0
- data/lib/decidim/content_parsers/blob_parser.rb +93 -0
- data/lib/decidim/content_parsers.rb +1 -0
- data/lib/decidim/content_renderers/blob_renderer.rb +90 -0
- data/lib/decidim/content_renderers.rb +1 -0
- data/lib/decidim/core/engine.rb +35 -1
- data/lib/decidim/core/test/factories.rb +28 -0
- data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +1 -1
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +25 -2
- data/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb +112 -14
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/core.rb +11 -0
- data/lib/decidim/diffy_extension.rb +18 -0
- data/lib/decidim/form_builder.rb +1 -1
- data/lib/decidim/map/autocomplete.rb +1 -0
- data/lib/decidim/organization_settings.rb +4 -1
- data/lib/decidim/participatory_space_user.rb +4 -0
- data/lib/decidim/query_extensions.rb +0 -26
- data/lib/decidim/settings_manifest.rb +2 -0
- data/lib/decidim/translatable_attributes.rb +6 -1
- data/lib/decidim/view_model.rb +1 -1
- data/lib/tasks/upgrade/decidim_attachments.rake +14 -0
- data/lib/tasks/upgrade/decidim_fix_categorization.rake +34 -8
- metadata +30 -7
@@ -53,21 +53,31 @@ $(() => {
|
|
53
53
|
}
|
54
54
|
|
55
55
|
$(document).on("click.zf.trigger", (event) => {
|
56
|
-
|
57
|
-
const
|
58
|
-
|
59
|
-
if
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
});
|
56
|
+
// Try to get the <a> directly or find the closest parent <a>
|
57
|
+
const $target = $(event.target).closest("a");
|
58
|
+
|
59
|
+
// Check if an <a> was found
|
60
|
+
if (!$target) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
const dialogTarget = `#${$target.data("dialog-open")}`;
|
65
|
+
const redirectUrl = $target.data("redirectUrl");
|
66
|
+
|
67
|
+
if (!dialogTarget || !redirectUrl) {
|
68
|
+
return;
|
70
69
|
}
|
70
|
+
|
71
|
+
$("<input type='hidden' />").
|
72
|
+
attr("id", "redirect_url").
|
73
|
+
attr("name", "redirect_url").
|
74
|
+
attr("value", redirectUrl).
|
75
|
+
appendTo(`${dialogTarget} form`);
|
76
|
+
|
77
|
+
$(`${dialogTarget} a`).attr("href", (index, href) => {
|
78
|
+
const querystring = jQuery.param({"redirect_url": redirectUrl});
|
79
|
+
return href + (href.match(/\?/) ? "&" : "?") + querystring;
|
80
|
+
});
|
71
81
|
});
|
72
82
|
|
73
83
|
$(document).on("closed.zf.reveal", (event) => {
|
@@ -21,6 +21,7 @@ const updateActiveUploads = (modal) => {
|
|
21
21
|
const files = document.querySelector(`[data-active-uploads=${modal.modal.id}]`)
|
22
22
|
const previousId = Array.from(files.querySelectorAll("[type=hidden][id]"))
|
23
23
|
const isMultiple = modal.options.multiple
|
24
|
+
const isTitled = modal.options.titled
|
24
25
|
|
25
26
|
// fastest way to clean children nodes
|
26
27
|
files.textContent = ""
|
@@ -34,16 +35,26 @@ const updateActiveUploads = (modal) => {
|
|
34
35
|
let hidden = ""
|
35
36
|
if (file.hiddenField) {
|
36
37
|
// if there is hiddenField, this file is new
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
let fileField = null;
|
39
|
+
if (isMultiple) {
|
40
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][file]`
|
41
|
+
} else if (isTitled) {
|
42
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][file]`
|
43
|
+
} else {
|
44
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}]`;
|
45
|
+
}
|
40
46
|
|
41
47
|
hidden = `<input type="hidden" name="${fileField}" value="${file.hiddenField}" />`
|
42
48
|
} else {
|
43
49
|
// otherwise, we keep the attachmentId
|
44
|
-
|
45
|
-
|
46
|
-
|
50
|
+
let fileField = null;
|
51
|
+
if (isMultiple) {
|
52
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][id]`;
|
53
|
+
} else if (isTitled) {
|
54
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][id]`;
|
55
|
+
} else {
|
56
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}]`;
|
57
|
+
}
|
47
58
|
|
48
59
|
// convert all node attributes to string
|
49
60
|
const attributes = Array.from(previousId.find(({ id }) => id === file.attachmentId).attributes).reduce((acc, { name, value }) => `${acc} ${name}="${value}"`, "")
|
@@ -51,10 +62,12 @@ const updateActiveUploads = (modal) => {
|
|
51
62
|
hidden += `<input type="hidden" name="${fileField}" value="${file.attachmentId}" />`
|
52
63
|
}
|
53
64
|
|
54
|
-
if (
|
65
|
+
if (isTitled) {
|
55
66
|
const titleValue = modal.modal.querySelectorAll('input[type="text"]')[ix].value
|
56
67
|
// NOTE - Renaming the attachment is not supported when multiple uploader is disabled
|
57
|
-
const titleField =
|
68
|
+
const titleField = isMultiple
|
69
|
+
? `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][title]`
|
70
|
+
: `${modal.options.resourceName}[${modal.options.addAttribute}][title]`
|
58
71
|
hidden += `<input type="hidden" name="${titleField}" value="${escapeQuotes(titleValue)}" />`
|
59
72
|
|
60
73
|
title = titleValue
|
@@ -77,6 +77,9 @@ export default class UploadModal {
|
|
77
77
|
uploader.upload.create((error, blob) => {
|
78
78
|
if (error) {
|
79
79
|
uploader.errors = [error]
|
80
|
+
this.uploadItems.replaceChild(this.createUploadItem(file, [error], { value: 100 }), item);
|
81
|
+
this.updateDropZone();
|
82
|
+
|
80
83
|
} else {
|
81
84
|
// attach the file hash to submit the form, when the file has been uploaded
|
82
85
|
file.hiddenField = blob.signed_id
|
@@ -68,6 +68,7 @@ import markAsReadNotifications from "src/decidim/notifications"
|
|
68
68
|
import RemoteModal from "src/decidim/remote_modal"
|
69
69
|
import selectActiveIdentity from "src/decidim/identity_selector_dialog"
|
70
70
|
import createTooltip from "src/decidim/tooltips"
|
71
|
+
import fetchRemoteTooltip from "src/decidim/remote_tooltips"
|
71
72
|
import createToggle from "src/decidim/toggle"
|
72
73
|
import {
|
73
74
|
createAccordion,
|
@@ -189,6 +190,8 @@ const initializer = (element = document) => {
|
|
189
190
|
// Initialize data-toggles
|
190
191
|
element.querySelectorAll("[data-toggle]").forEach((elem) => createToggle(elem))
|
191
192
|
|
193
|
+
element.querySelectorAll("[data-remote-tooltip]").forEach((elem) => fetchRemoteTooltip(elem))
|
194
|
+
|
192
195
|
element.querySelectorAll(".new_report").forEach((elem) => changeReportFormBehavior(elem))
|
193
196
|
}
|
194
197
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import createTooltip from "src/decidim/tooltips"
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Given the following HTML structure,
|
5
|
+
* <span data-remote-tooltip="true" tooltip-url="some url" data-author="true">
|
6
|
+
* <span></span>
|
7
|
+
* </span>
|
8
|
+
*
|
9
|
+
* This function will check if the HTMLElement where is attached to has a child, and will add a data tooltip attribute
|
10
|
+
* to the respective child in order to attach the fetched HTML content fetched under a json key as the content of the
|
11
|
+
* HTML tooltip. The DOM structure is expected to be like follows:
|
12
|
+
*
|
13
|
+
* <span data-remote-tooltip="true" tooltip-url="some url" data-author="true">
|
14
|
+
* <span data-tooltip="HTML content from json data field"></span>
|
15
|
+
* </span>
|
16
|
+
*
|
17
|
+
* @param {HTMLElement} node The element holding the initialization data
|
18
|
+
* @returns {void}
|
19
|
+
*/
|
20
|
+
export default async function(node) {
|
21
|
+
const container = node.firstElementChild;
|
22
|
+
|
23
|
+
if (container) {
|
24
|
+
const response = await fetch(node.dataset.tooltipUrl, {
|
25
|
+
headers: {
|
26
|
+
"Content-Type": "application/json"
|
27
|
+
}
|
28
|
+
});
|
29
|
+
if (response.ok) {
|
30
|
+
const json = await response.json();
|
31
|
+
|
32
|
+
container.dataset.tooltip = json.data;
|
33
|
+
createTooltip(container);
|
34
|
+
} else {
|
35
|
+
console.error(response.status, response.statusText);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
@@ -9,7 +9,7 @@ export default function createToggle(component) {
|
|
9
9
|
const { toggle } = component.dataset
|
10
10
|
|
11
11
|
if (!component.id) {
|
12
|
-
// when component has no id, we enforce to have
|
12
|
+
// when component has no id, we enforce it to have one
|
13
13
|
component.id = `toggle-${Math.random().toString(36).substring(7)}`
|
14
14
|
}
|
15
15
|
|
@@ -63,9 +63,32 @@ export default function(node) {
|
|
63
63
|
// append to dom hidden, to apply css transitions
|
64
64
|
tooltip.setAttribute("aria-hidden", true)
|
65
65
|
|
66
|
-
|
67
|
-
|
66
|
+
// used to detect if the user is on a mobile device by checking the user agent
|
67
|
+
const useMobile = (/Mobi|Android/i).test(navigator.userAgent);
|
68
|
+
|
69
|
+
// used not to collapse tooltip
|
70
|
+
let removeTooltip = () => {
|
71
|
+
tooltip.setAttribute("aria-hidden", "true");
|
72
|
+
}
|
73
|
+
|
74
|
+
// used to allow clicks outside the tooltip to take place on the page or device
|
75
|
+
const OutsideClick = (event) => {
|
76
|
+
if (!tooltip.contains(event.target) && event.target !== node) {
|
77
|
+
removeTooltip();
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
// function called again to allow clicks outside the tooltip to collapse the tooltip
|
82
|
+
removeTooltip = () => {
|
83
|
+
tooltip.setAttribute("aria-hidden", "true");
|
84
|
+
document.removeEventListener("click", OutsideClick)
|
85
|
+
}
|
86
|
+
|
87
|
+
const toggleTooltip = (event) => {
|
88
|
+
event.preventDefault();
|
89
|
+
// if the tooltip is visible in the DOM, hide it otherwise display
|
68
90
|
if (tooltip.getAttribute("aria-hidden") === "false") {
|
91
|
+
tooltip.setAttribute("aria-hidden", "true");
|
69
92
|
return
|
70
93
|
}
|
71
94
|
|
@@ -106,27 +129,24 @@ export default function(node) {
|
|
106
129
|
tooltip.style.left = `${positionX}px`
|
107
130
|
|
108
131
|
tooltip.setAttribute("aria-hidden", false)
|
109
|
-
}
|
110
|
-
|
111
|
-
// in order to revoke the remove event when the mouse is over the trigger/tooltip
|
112
|
-
let cancelRemove = false
|
113
132
|
|
114
|
-
|
115
|
-
|
116
|
-
// give some sleep time before hiding the element from the DOM
|
117
|
-
setTimeout(() => !cancelRemove && tooltip.setAttribute("aria-hidden", true), 500);
|
133
|
+
// sleep time before hiding the element from the DOM
|
134
|
+
setTimeout(() => document.addEventListener("click", OutsideClick))
|
118
135
|
}
|
119
136
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
137
|
+
if (useMobile) {
|
138
|
+
// mobile use to click and toggle the tooltip
|
139
|
+
node.addEventListener("click", toggleTooltip);
|
140
|
+
window.addEventListener("keydown", (event) => event.key === "Escape" && removeTooltip())
|
141
|
+
} else {
|
142
|
+
// desktop use for hover and blur over tooltip
|
143
|
+
node.addEventListener("mouseenter", toggleTooltip)
|
144
|
+
node.addEventListener("mouseleave", removeTooltip)
|
145
|
+
node.addEventListener("focus", toggleTooltip)
|
146
|
+
node.addEventListener("blur", removeTooltip)
|
147
|
+
|
148
|
+
// tooltip hover listeners to prevent hiding when hovered
|
149
|
+
tooltip.addEventListener("mouseenter", () => tooltip.setAttribute("aria-hidden", false))
|
150
|
+
tooltip.addEventListener("mouseleave", removeTooltip)
|
151
|
+
}
|
132
152
|
}
|
@@ -37,7 +37,6 @@
|
|
37
37
|
}
|
38
38
|
|
39
39
|
.conference-diploma .diploma__logo {
|
40
|
-
border: 1px solid #333;
|
41
40
|
padding: 2rem;
|
42
41
|
}
|
43
42
|
|
@@ -47,10 +46,12 @@
|
|
47
46
|
max-height: 100%;
|
48
47
|
max-width: 100%;
|
49
48
|
margin: 0;
|
49
|
+
padding-top: 10%;
|
50
50
|
}
|
51
51
|
|
52
52
|
.conference-diploma .diploma__border {
|
53
53
|
margin: 0;
|
54
|
+
margin-top: 15%;
|
54
55
|
-moz-border-image: url("../images/decidim/pattern.png") 20 repeat;
|
55
56
|
-webkit-border-image: url("../images/decidim/pattern.png") 20 repeat;
|
56
57
|
-o-border-image: url("../images/decidim/pattern.png") 20 repeat;
|
@@ -26,7 +26,7 @@ module Decidim
|
|
26
26
|
delegate :content_tag, :safe_join, :link_to, :active_link_to_class, :is_active_link?, :icon, to: :@view
|
27
27
|
|
28
28
|
def render
|
29
|
-
content_tag :li, class: link_wrapper_classes do
|
29
|
+
content_tag :li, role: :menuitem, class: link_wrapper_classes do
|
30
30
|
output = if url == "#"
|
31
31
|
[content_tag(:span, composed_label, class: "sidebar-menu__item-disabled")]
|
32
32
|
else
|
@@ -56,13 +56,24 @@ module Decidim
|
|
56
56
|
|
57
57
|
condition = if klass.include?(Decidim::HasPrivateUsers)
|
58
58
|
Arel.sql(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
<<~SQL.squish
|
60
|
+
(
|
61
|
+
decidim_action_logs.participatory_space_type = '#{manifest.model_class_name}' AND#{" "}
|
62
|
+
decidim_action_logs.participatory_space_id IN (#{Arel.sql(klass.visible_for(current_user).select(:id).to_sql)})
|
63
|
+
) OR#{" "}
|
64
|
+
(
|
65
|
+
decidim_action_logs.resource_type = '#{manifest.model_class_name}' AND#{" "}
|
66
|
+
decidim_action_logs.resource_id IN (#{Arel.sql(klass.visible_for(current_user).select(:id).to_sql)})
|
67
|
+
)
|
68
|
+
SQL
|
63
69
|
).to_s
|
64
70
|
else
|
65
|
-
Arel.sql(
|
71
|
+
Arel.sql(
|
72
|
+
[
|
73
|
+
"decidim_action_logs.resource_type = '#{manifest.model_class_name}'",
|
74
|
+
"decidim_action_logs.participatory_space_type = '#{manifest.model_class_name}'"
|
75
|
+
].join(" OR ")
|
76
|
+
).to_s
|
66
77
|
end
|
67
78
|
|
68
79
|
conditions << "(#{condition})"
|
@@ -41,7 +41,7 @@ module Decidim
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def parse_i18n_changeset(attribute, values, type, diff)
|
44
|
-
values.last.keys.each do |locale, _value|
|
44
|
+
(values.last.keys - ["machine_translations"]).each do |locale, _value|
|
45
45
|
first_value = values.first.try(:[], locale)
|
46
46
|
last_value = values.last.try(:[], locale)
|
47
47
|
next if first_value == last_value
|
@@ -56,6 +56,27 @@ module Decidim
|
|
56
56
|
}
|
57
57
|
)
|
58
58
|
end
|
59
|
+
|
60
|
+
return diff unless values.last.has_key?("machine_translations")
|
61
|
+
|
62
|
+
values.last.fetch("machine_translations").each_key do |locale, _value|
|
63
|
+
next unless I18n.available_locales.include?(locale.to_sym)
|
64
|
+
|
65
|
+
first_value = values.first.try(:[], "machine_translations").try(:[], locale)
|
66
|
+
last_value = values.last.try(:[], "machine_translations").try(:[], locale)
|
67
|
+
|
68
|
+
attribute_locale = :"#{attribute}_machine_translations_#{locale}"
|
69
|
+
|
70
|
+
diff.update(
|
71
|
+
attribute_locale => {
|
72
|
+
type:,
|
73
|
+
label: generate_i18n_label(attribute, locale, "decidim.machine_translations.automatic"),
|
74
|
+
old_value: first_value,
|
75
|
+
new_value: last_value
|
76
|
+
}
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
59
80
|
diff
|
60
81
|
end
|
61
82
|
|
@@ -108,7 +129,8 @@ module Decidim
|
|
108
129
|
end
|
109
130
|
|
110
131
|
# Returns a String.
|
111
|
-
|
132
|
+
# i18n-tasks-use t("decidim.machine_translations.automatic")
|
133
|
+
def generate_i18n_label(attribute, locale, postfix = "")
|
112
134
|
label = I18n.t(attribute, scope: i18n_scope)
|
113
135
|
locale_name = if I18n.available_locales.include?(locale.to_sym)
|
114
136
|
I18n.t("locale.name", locale:)
|
@@ -116,6 +138,8 @@ module Decidim
|
|
116
138
|
locale
|
117
139
|
end
|
118
140
|
|
141
|
+
locale_name = I18n.t(postfix, locale_name:, locale:) if postfix.present?
|
142
|
+
|
119
143
|
"#{label} (#{locale_name})"
|
120
144
|
end
|
121
145
|
|
@@ -36,20 +36,29 @@ module Decidim
|
|
36
36
|
return unless resource
|
37
37
|
return unless event_class.types.include?(:email)
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
send_to_followers
|
40
|
+
send_to_affected_users
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
end
|
43
|
+
private
|
44
44
|
|
45
|
+
def send_to_affected_users
|
45
46
|
affected_users.each do |recipient|
|
46
47
|
next unless ["all", "own-only"].include?(recipient.notification_types)
|
48
|
+
next if recipient.deleted? || recipient.blocked?
|
47
49
|
|
48
50
|
send_email_to(recipient, user_role: :affected_user)
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
52
|
-
|
54
|
+
def send_to_followers
|
55
|
+
followers.each do |recipient|
|
56
|
+
next unless ["all", "followed-only"].include?(recipient.notification_types)
|
57
|
+
next if recipient.deleted? || recipient.blocked?
|
58
|
+
|
59
|
+
send_email_to(recipient, user_role: :follower)
|
60
|
+
end
|
61
|
+
end
|
53
62
|
|
54
63
|
attr_reader :event, :event_class, :resource, :followers, :affected_users, :extra
|
55
64
|
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<%= form_required_explanation %>
|
14
14
|
</div>
|
15
15
|
|
16
|
-
<%= decidim_form_for(@form, namespace: "registration", as: resource_name, url: omniauth_registrations_path(resource_name)) do |f| %>
|
16
|
+
<%= decidim_form_for(@form, namespace: "registration", as: resource_name, url: decidim.omniauth_registrations_path(resource_name)) do |f| %>
|
17
17
|
|
18
18
|
<div class="form__wrapper">
|
19
19
|
<%= f.text_field :name, help_text: t("decidim.devise.omniauth_registrations.new.username_help"), autocomplete: "name", placeholder: "John Doe" %>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<% add_decidim_page_title(t("decidim.offline.name")) %>
|
2
2
|
|
3
|
-
<main id="offline-fallback-html" class="text-center
|
3
|
+
<main id="offline-fallback-html" class="text-center my-8">
|
4
4
|
<div class="flex justify-center">
|
5
5
|
<svg xmlns="http://www.w3.org/2000/svg" width="5rem" height="r5em" viewBox="0 0 25 25">
|
6
6
|
<path
|
@@ -10,18 +10,18 @@
|
|
10
10
|
</header>
|
11
11
|
|
12
12
|
<div class="vertical-tabs">
|
13
|
-
<nav>
|
14
|
-
<button id="dropdown-trigger-pages" data-component="dropdown" data-target="dropdown-menu-pages" data-auto-close="true">
|
13
|
+
<nav role="navigation" aria-label="<%= I18n.t("layouts.decidim.navigation.aria_label", title: translated_attribute(page.title)) %>">
|
14
|
+
<button id="dropdown-trigger-pages" data-component="dropdown" data-target="dropdown-menu-pages" data-open-md="true" data-auto-close="true">
|
15
15
|
<span>
|
16
16
|
<%= translated_attribute(page.title) %>
|
17
17
|
</span>
|
18
18
|
<%= icon "arrow-down-s-line" %>
|
19
19
|
<%= icon "arrow-up-s-line" %>
|
20
20
|
</button>
|
21
|
-
<ul id="dropdown-menu-pages" class="vertical-tabs__list"
|
21
|
+
<ul id="dropdown-menu-pages" class="vertical-tabs__list" role="menu">
|
22
22
|
<% pages.each do |sibling| %>
|
23
|
-
<li class="<%= "is-active" if page == sibling %>">
|
24
|
-
<%= link_to translated_attribute(sibling.title), page_path(sibling.slug) %>
|
23
|
+
<li class="<%= "is-active" if page == sibling %>" role="menuitem">
|
24
|
+
<%= link_to translated_attribute(sibling.title), page_path(sibling.slug), "aria-current": (page == sibling).to_s %>
|
25
25
|
</li>
|
26
26
|
<% end %>
|
27
27
|
</ul>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<% if filter_sections.present? || local_assigns.has_key?(:search_variable) %>
|
5
5
|
<%= filter_form_for filter, url_for, class: "new_filter self-stretch", data: { filters: "", component: "accordion" } do |form| %>
|
6
6
|
|
7
|
-
<button id="dropdown-trigger-filters" data-component="dropdown" data-target="dropdown-menu-filters">
|
7
|
+
<button id="dropdown-trigger-filters" data-component="dropdown" data-target="dropdown-menu-filters" data-open-md="true">
|
8
8
|
<%= icon "arrow-down-s-line" %>
|
9
9
|
<%= icon "arrow-up-s-line" %>
|
10
10
|
<span>
|
@@ -12,14 +12,14 @@
|
|
12
12
|
</span>
|
13
13
|
</button>
|
14
14
|
|
15
|
-
<div id="dropdown-menu-filters"
|
15
|
+
<div id="dropdown-menu-filters">
|
16
16
|
<% if local_assigns.has_key?(:skip_to_id) %>
|
17
|
-
<%= link_to t("skip", scope: "decidim.shared.filter_form_help"), "##{skip_to_id}", class: "filter-skip" %>
|
17
|
+
<%= link_to t("skip", scope: "decidim.shared.filter_form_help"), "##{skip_to_id}", class: "filter-skip", role: "menuitem" %>
|
18
18
|
<% end %>
|
19
|
-
<p class="filter-help"><%= t("help", scope: "decidim.shared.filter_form_help") %></p>
|
19
|
+
<p class="filter-help" role="menuitem" aria-disabled="true"><%= t("help", scope: "decidim.shared.filter_form_help") %></p>
|
20
20
|
|
21
21
|
<% if local_assigns.has_key?(:search_variable) %>
|
22
|
-
<div class="filter-search filter-container">
|
22
|
+
<div class="filter-search filter-container" role="menuitem">
|
23
23
|
<%= form.search_field search_variable,
|
24
24
|
label: false,
|
25
25
|
placeholder: search_label,
|
@@ -1,13 +1,14 @@
|
|
1
|
-
<button class="order-by__button" id="dropdown-trigger-order" data-component="dropdown" data-target="dropdown-menu-order" data-open="
|
1
|
+
<button class="order-by__button" id="dropdown-trigger-order" data-component="dropdown" data-target="dropdown-menu-order" data-open-md="true">
|
2
2
|
<%= icon "arrow-down-s-line" %>
|
3
3
|
<%= icon "arrow-up-s-line" %>
|
4
4
|
<span><%= t("#{i18n_scope}.label") %></span>
|
5
5
|
</button>
|
6
|
-
<div id="dropdown-menu-order" class="order-by"
|
6
|
+
<div id="dropdown-menu-order" class="order-by">
|
7
7
|
<% orders.each do |order_name| %>
|
8
8
|
<%= order_link order_name,
|
9
9
|
i18n_scope:,
|
10
10
|
title: t("#{i18n_scope}.label"),
|
11
|
+
role: :menuitem,
|
11
12
|
class: "button button__sm button__text-secondary #{order_name == order ? "underline font-bold" : "font-normal"}" %>
|
12
13
|
<% end %>
|
13
14
|
</div>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
4
4
|
<% available_locales.each do |locale| %>
|
5
|
-
<link rel="alternate" href="<%=
|
5
|
+
<link rel="alternate" href="<%= current_url(request.parameters.merge(locale:)) %>" hreflang="<%= locale %>">
|
6
6
|
<% end %>
|
7
7
|
|
8
8
|
<meta name="twitter:card" content="summary_large_image">
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<div class="menu-bar__breadcrumb-mobile__dropdown-trigger">
|
3
3
|
<span>
|
4
4
|
<% breadcrumb_items.last(2).each_with_index do |item, i| %>
|
5
|
-
<% item_label =
|
5
|
+
<% item_label = decidim_escape_translated(item[:label]).html_safe %>
|
6
6
|
<% if i.positive? %>
|
7
7
|
<span>/</span>
|
8
8
|
<% end %>
|
@@ -10,12 +10,12 @@
|
|
10
10
|
|
11
11
|
<div class="vertical-tabs">
|
12
12
|
<nav aria-label="menu-vertical">
|
13
|
-
<button id="dropdown-trigger-profile" data-component="dropdown" data-target="dropdown-menu-profile">
|
13
|
+
<button id="dropdown-trigger-profile" data-open-md="true" data-component="dropdown" data-target="dropdown-menu-profile">
|
14
14
|
<span><%= user_menu.active_item&.label || t("decidim.searches.filters.jump_to") %></span>
|
15
15
|
<%= icon "arrow-down-s-line" %>
|
16
16
|
<%= icon "arrow-up-s-line" %>
|
17
17
|
</button>
|
18
|
-
<ul id="dropdown-menu-profile" class="vertical-tabs__list"
|
18
|
+
<ul id="dropdown-menu-profile" class="vertical-tabs__list">
|
19
19
|
<%= user_menu.render %>
|
20
20
|
</ul>
|
21
21
|
</nav>
|