decidim-core 0.31.7 → 0.31.8
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/participatory_space_main_data/title.erb +1 -14
- data/app/cells/decidim/following_cell.rb +1 -1
- data/app/cells/decidim/nav_links/show.erb +3 -1
- data/app/cells/decidim/participatory_space_private_user/show.erb +1 -1
- data/app/cells/decidim/share_text_widget/modal.erb +1 -0
- data/app/cells/decidim/share_widget/modal.erb +1 -0
- data/app/cells/decidim/share_widget/qr_code_modal.erb +2 -4
- data/app/commands/decidim/create_omniauth_registration.rb +17 -2
- data/app/controllers/concerns/decidim/devise_authentication_methods.rb +4 -0
- data/app/controllers/concerns/decidim/devise_controllers.rb +2 -0
- data/app/controllers/concerns/decidim/disable_redirection_to_external_host.rb +2 -0
- data/app/controllers/concerns/decidim/filter_resource.rb +2 -0
- data/app/controllers/concerns/decidim/impersonate_users.rb +2 -2
- data/app/controllers/concerns/decidim/locale_switcher.rb +62 -11
- data/app/controllers/concerns/decidim/needs_permission.rb +22 -10
- data/app/controllers/concerns/decidim/onboarding_action_methods.rb +2 -0
- data/app/controllers/concerns/decidim/paginable.rb +7 -3
- data/app/controllers/concerns/decidim/payload_info.rb +2 -0
- data/app/controllers/concerns/decidim/resource_versions_concern.rb +14 -0
- data/app/controllers/decidim/application_controller.rb +0 -6
- data/app/controllers/decidim/authorization_modals_controller.rb +2 -0
- data/app/controllers/decidim/components/base_controller.rb +2 -0
- data/app/controllers/decidim/devise/passwords_controller.rb +8 -0
- data/app/controllers/decidim/favicon_controller.rb +9 -0
- data/app/controllers/decidim/free_resource_authorization_modals_controller.rb +2 -0
- data/app/controllers/decidim/gamification/badges_controller.rb +2 -0
- data/app/controllers/decidim/homepage_controller.rb +4 -1
- data/app/controllers/decidim/likes_controller.rb +1 -1
- data/app/controllers/decidim/links_controller.rb +2 -0
- data/app/controllers/decidim/manifests_controller.rb +2 -0
- data/app/controllers/decidim/messaging/conversations_controller.rb +10 -4
- data/app/controllers/decidim/newsletters_controller.rb +4 -0
- data/app/controllers/decidim/notifications_subscriptions_controller.rb +4 -0
- data/app/controllers/decidim/offline_controller.rb +5 -1
- data/app/controllers/decidim/open_data_controller.rb +5 -1
- data/app/controllers/decidim/pages_controller.rb +31 -3
- data/app/controllers/decidim/private_downloads_controller.rb +2 -0
- data/app/controllers/decidim/profiles_controller.rb +10 -0
- data/app/controllers/decidim/qr_controller.rb +3 -1
- data/app/controllers/decidim/resource_autocomplete_controller.rb +2 -0
- data/app/controllers/decidim/searches_controller.rb +2 -0
- data/app/controllers/decidim/short_links_controller.rb +4 -0
- data/app/controllers/decidim/static_map_controller.rb +2 -0
- data/app/controllers/decidim/timeouts_controller.rb +4 -0
- data/app/controllers/decidim/tos_controller.rb +2 -0
- data/app/controllers/decidim/upload_validations_controller.rb +4 -2
- data/app/controllers/decidim/user_activities_controller.rb +2 -0
- data/app/forms/decidim/upload_validation_form.rb +16 -10
- data/app/helpers/concerns/decidim/user_role_checker.rb +35 -10
- data/app/helpers/decidim/sanitize_helper.rb +11 -1
- data/app/jobs/decidim/application_job.rb +4 -0
- data/app/models/decidim/authorization.rb +24 -0
- data/app/models/decidim/oauth_application.rb +1 -0
- data/app/models/decidim/user.rb +1 -0
- data/app/models/decidim/user_base_entity.rb +1 -1
- data/app/packs/src/decidim/callout.js +10 -0
- data/app/packs/src/decidim/callout.test.js +32 -0
- data/app/packs/src/decidim/controllers/accordion/accordion.test.js +64 -0
- data/app/packs/src/decidim/controllers/accordion/controller.js +43 -0
- data/app/packs/src/decidim/datepicker/datepicker_functions.js +34 -23
- data/app/packs/src/decidim/datepicker/generate_datepicker.js +4 -4
- data/app/packs/src/decidim/datepicker/test/time.test.js +39 -24
- data/app/packs/src/decidim/utilities/dom.js +2 -2
- data/app/packs/stylesheets/decidim/_content_blocks.scss +4 -10
- data/app/packs/stylesheets/decidim/_conversations.scss +0 -8
- data/app/packs/stylesheets/decidim/_modal.scss +16 -8
- data/app/packs/stylesheets/decidim/_modal_share.scss +0 -22
- data/app/packs/stylesheets/decidim/_participatory_spaces.scss +11 -1
- data/app/packs/stylesheets/decidim/_rich_text.scss +12 -9
- data/app/packs/stylesheets/decidim/_typography.scss +5 -2
- data/app/packs/stylesheets/decidim/legacy/email.scss +0 -5
- data/app/permissions/decidim/permissions.rb +20 -0
- data/app/presenters/decidim/admin_log/component_presenter.rb +1 -1
- data/app/presenters/decidim/log/base_presenter.rb +10 -1
- data/app/presenters/decidim/log/user_presenter.rb +2 -2
- data/app/serializers/decidim/exporters/open_data_blocked_user_serializer.rb +11 -2
- data/app/serializers/decidim/exporters/open_data_moderation_serializer.rb +25 -1
- data/app/services/decidim/svg_content_validator.rb +554 -0
- data/app/uploaders/decidim/application_uploader.rb +2 -2
- data/app/validators/uploader_image_content_validator.rb +278 -0
- data/app/views/decidim/application/_radio_accordion.html.erb +1 -5
- data/app/views/decidim/messaging/conversations/create.js.erb +1 -3
- data/app/views/decidim/shared/_login_modal.html.erb +12 -14
- data/app/views/layouts/decidim/_mailer_logo.html.erb +1 -1
- data/config/initializers/rack_attack.rb +10 -0
- data/config/locales/bg.yml +0 -1
- data/config/locales/ca-IT.yml +3 -1
- data/config/locales/ca.yml +3 -1
- data/config/locales/cs.yml +7 -1
- data/config/locales/de.yml +61 -3
- data/config/locales/en.yml +3 -1
- data/config/locales/es-MX.yml +3 -1
- data/config/locales/es-PY.yml +3 -1
- data/config/locales/es.yml +7 -5
- data/config/locales/eu.yml +155 -156
- data/config/locales/fi-plain.yml +1 -1
- data/config/locales/fi.yml +1 -1
- data/config/locales/fr-CA.yml +3 -1
- data/config/locales/fr.yml +3 -1
- data/config/locales/gl.yml +1441 -33
- data/config/locales/hu.yml +0 -1
- data/config/locales/ja.yml +1 -1
- data/config/locales/pl.yml +0 -1
- data/config/locales/pt-BR.yml +0 -1
- data/config/locales/ro-RO.yml +0 -1
- data/config/locales/sk.yml +2 -1
- data/config/locales/sv.yml +47 -10
- data/config/locales/tr-TR.yml +0 -2
- data/db/migrate/20260909000000_add_failed_attempts_and_locked_at_to_decidim_authorizations.rb +8 -0
- data/lib/decidim/core/test/shared_examples/participatory_space_members_page_examples.rb +77 -0
- data/lib/decidim/core/test/shared_examples/participatory_space_members_shared_examples.rb +85 -6
- data/lib/decidim/core/test/shared_examples/versions_controller_examples.rb +23 -1
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/core.rb +9 -0
- data/lib/decidim/has_upload_validations.rb +1 -0
- data/lib/decidim/legacy_form_builder.rb +4 -0
- data/lib/decidim/query_extensions.rb +2 -0
- metadata +10 -6
|
@@ -50,10 +50,7 @@ export const setMinute = (value) => {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
export const formatInputDate = (date, formats) => {
|
|
53
|
-
const
|
|
54
|
-
const year = dateList[0];
|
|
55
|
-
const month = dateList[1];
|
|
56
|
-
const day = dateList[2];
|
|
53
|
+
const [year, month, day] = date.split("-");
|
|
57
54
|
|
|
58
55
|
if (formats.order === "m-d-y") {
|
|
59
56
|
return `${month}${formats.separator}${day}${formats.separator}${year}`;
|
|
@@ -63,10 +60,34 @@ export const formatInputDate = (date, formats) => {
|
|
|
63
60
|
return `${day}${formats.separator}${month}${formats.separator}${year}`;
|
|
64
61
|
};
|
|
65
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Splits a time string into two elements: hours and minutes. Ensures a returned
|
|
65
|
+
* array of size 2 for any kind of strings or `null` values.
|
|
66
|
+
*
|
|
67
|
+
* Examples:
|
|
68
|
+
* splitTime("") => ["00", "00"]
|
|
69
|
+
* splitTime(null) => ["00", "00"]
|
|
70
|
+
* splitTime("12") => ["12", "00"]
|
|
71
|
+
* splitTime("12:30") => ["12", "30"]
|
|
72
|
+
* splitTime("2:4") => ["02", "04"]
|
|
73
|
+
* splitTime("12:30:45") => ["12", "30"]
|
|
74
|
+
*
|
|
75
|
+
* @param {String} value The original time string, typically hh:ss.
|
|
76
|
+
* @returns {Array} An array with two elements where the first element is the
|
|
77
|
+
* hour part and the second element is the minute part.
|
|
78
|
+
*/
|
|
79
|
+
const splitTime = (value) => {
|
|
80
|
+
return [...(value || "").split(":"), "00"].splice(0, 2).map((part) => part.padStart(2, "0"));
|
|
81
|
+
};
|
|
82
|
+
|
|
66
83
|
export const formatInputTime = (time, format, input) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
if (!time) {
|
|
85
|
+
return "";
|
|
86
|
+
} else if (time.length < 1) {
|
|
87
|
+
return time;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let [hour, minute] = splitTime(time);
|
|
70
91
|
|
|
71
92
|
if (format === 12) {
|
|
72
93
|
if (Number(hour) === 12) {
|
|
@@ -80,11 +101,9 @@ export const formatInputTime = (time, format, input) => {
|
|
|
80
101
|
} else if (Number(hour) === 0) {
|
|
81
102
|
hour = "12";
|
|
82
103
|
}
|
|
83
|
-
|
|
84
|
-
return `${hour}:${minute}`;
|
|
85
104
|
};
|
|
86
105
|
|
|
87
|
-
return
|
|
106
|
+
return `${hour}:${minute}`;
|
|
88
107
|
};
|
|
89
108
|
|
|
90
109
|
export const changeHourDisplay = (change, hour, format) => {
|
|
@@ -199,29 +218,21 @@ export const formatDate = (value, formats) => {
|
|
|
199
218
|
};
|
|
200
219
|
|
|
201
220
|
export const formatTime = (value, format, inputname) => {
|
|
221
|
+
let [hour, minute] = splitTime(value);
|
|
222
|
+
|
|
202
223
|
if (format === 12) {
|
|
203
|
-
const splitValue = value.split(":");
|
|
204
|
-
let hour = splitValue[0];
|
|
205
|
-
const minute = splitValue[1];
|
|
206
224
|
if (document.getElementById(`period_am_${inputname}`).checked) {
|
|
207
|
-
|
|
208
|
-
case "12":
|
|
225
|
+
if (hour === "12") {
|
|
209
226
|
hour = "00";
|
|
210
|
-
|
|
211
|
-
return `${hour}:${minute}`;
|
|
212
|
-
default:
|
|
213
|
-
return value;
|
|
214
|
-
};
|
|
227
|
+
}
|
|
215
228
|
} else if (document.getElementById(`period_pm_${inputname}`).checked) {
|
|
216
229
|
if (Number(hour) > 0 && Number(hour) < 12) {
|
|
217
230
|
hour = `${Number(hour) + 12}`;
|
|
218
231
|
};
|
|
219
|
-
|
|
220
|
-
return `${hour}:${minute}`
|
|
221
232
|
};
|
|
222
233
|
};
|
|
223
234
|
|
|
224
|
-
return
|
|
235
|
+
return `${hour}:${minute}`;
|
|
225
236
|
};
|
|
226
237
|
|
|
227
238
|
export const updateInputValue = (input, formats, time) => {
|
|
@@ -58,7 +58,7 @@ export default function generateDatePicker(input, row, formats) {
|
|
|
58
58
|
dateColumn.appendChild(datePickerContainer);
|
|
59
59
|
|
|
60
60
|
let prevDate = null;
|
|
61
|
-
let defaultTime = input.
|
|
61
|
+
let defaultTime = input.dataset.defaultTime || "00:00"
|
|
62
62
|
|
|
63
63
|
const datePickerDisplay = (event) => {
|
|
64
64
|
if (!dateColumn.contains(event.target)) {
|
|
@@ -79,7 +79,7 @@ export default function generateDatePicker(input, row, formats) {
|
|
|
79
79
|
if (input.type === "date") {
|
|
80
80
|
input.value = `${formatDate(date.value, formats)}`;
|
|
81
81
|
} else if (input.type === "datetime-local") {
|
|
82
|
-
input.value = `${formatDate(date.value, formats)}T${formatTime(document.querySelector(`#${input.id}_time`).value, formats.time, input.id)
|
|
82
|
+
input.value = `${formatDate(date.value, formats)}T${formatTime(document.querySelector(`#${input.id}_time`).value || defaultTime, formats.time, input.id)}`;
|
|
83
83
|
};
|
|
84
84
|
};
|
|
85
85
|
});
|
|
@@ -95,7 +95,7 @@ export default function generateDatePicker(input, row, formats) {
|
|
|
95
95
|
if (input.type === "date") {
|
|
96
96
|
input.value = `${formatDate(date.value, formats)}`;
|
|
97
97
|
} else if (input.type === "datetime-local") {
|
|
98
|
-
input.value = `${formatDate(date.value, formats)}T${formatTime(document.querySelector(`#${input.id}_time`).value, formats.time, input.id)
|
|
98
|
+
input.value = `${formatDate(date.value, formats)}T${formatTime(document.querySelector(`#${input.id}_time`).value || defaultTime, formats.time, input.id)}`;
|
|
99
99
|
};
|
|
100
100
|
};
|
|
101
101
|
});
|
|
@@ -115,7 +115,7 @@ export default function generateDatePicker(input, row, formats) {
|
|
|
115
115
|
if (input.type === "date") {
|
|
116
116
|
input.value = `${pickedDate}`;
|
|
117
117
|
} else if (input.type === "datetime-local") {
|
|
118
|
-
input.value = `${pickedDate}T${formatTime(document.querySelector(`#${input.id}_time`).value, formats.time, input.id)
|
|
118
|
+
input.value = `${pickedDate}T${formatTime(document.querySelector(`#${input.id}_time`).value || defaultTime, formats.time, input.id)}`;
|
|
119
119
|
};
|
|
120
120
|
datePickerContainer.style.display = "none";
|
|
121
121
|
});
|
|
@@ -49,34 +49,42 @@ describe("setMinute", () => {
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
describe("formatInputTime", () => {
|
|
52
|
-
const setupDOM = () => {
|
|
53
|
-
document.body.innerHTML = `
|
|
54
|
-
<input type="radio" id="period_am_exampleInput" name="period">
|
|
55
|
-
<input type="radio" id="period_pm_exampleInput" name="period">
|
|
56
|
-
`;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
52
|
describe("24-hour format", () => {
|
|
53
|
+
const format = 24;
|
|
54
|
+
const input = { id: "exampleInput" };
|
|
55
|
+
|
|
60
56
|
it("returns the value in correct format", () => {
|
|
61
|
-
|
|
62
|
-
const time = "15:30";
|
|
63
|
-
const format = 24;
|
|
64
|
-
const input = { id: "exampleInput" };
|
|
57
|
+
const result = formatInputTime("15:30", format, input);
|
|
65
58
|
|
|
66
|
-
|
|
59
|
+
expect(result).toBe("15:30");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("returns the value in correct format if seconds are provided", () => {
|
|
63
|
+
const result = formatInputTime("15:30:04", format, input);
|
|
67
64
|
|
|
68
65
|
expect(result).toBe("15:30");
|
|
69
66
|
});
|
|
67
|
+
|
|
68
|
+
it("returns an empty string if an empty string is provided", () => {
|
|
69
|
+
const result = formatInputTime("", 24, { id: "exampleInput" });
|
|
70
|
+
|
|
71
|
+
expect(result).toBe("");
|
|
72
|
+
});
|
|
70
73
|
})
|
|
71
74
|
|
|
72
75
|
describe("12-hour format", () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
document.body.innerHTML = `
|
|
78
|
+
<input type="radio" id="period_am_exampleInput" name="period">
|
|
79
|
+
<input type="radio" id="period_pm_exampleInput" name="period">
|
|
80
|
+
`;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const format = 12;
|
|
84
|
+
const input = { id: "exampleInput" };
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
it("returns the value in correct format and checks the PM radio-button", () => {
|
|
87
|
+
const result = formatInputTime("12:45", format, input);
|
|
80
88
|
|
|
81
89
|
expect(result).toBe("12:45");
|
|
82
90
|
expect(document.getElementById("period_pm_exampleInput").checked).toBeTruthy();
|
|
@@ -84,17 +92,24 @@ describe("formatInputTime", () => {
|
|
|
84
92
|
});
|
|
85
93
|
|
|
86
94
|
it("returns the value in correct format", () => {
|
|
87
|
-
|
|
88
|
-
const time = "00:25";
|
|
89
|
-
const format = 12;
|
|
90
|
-
const input = { id: "exampleInput" };
|
|
91
|
-
|
|
92
|
-
const result = formatInputTime(time, format, input);
|
|
95
|
+
const result = formatInputTime("00:25", format, input);
|
|
93
96
|
|
|
94
97
|
expect(result).toBe("12:25");
|
|
95
98
|
// We are not expecting a radio button to be selected here since in a real environment the selected button is not
|
|
96
99
|
// handled by this method for values < 12
|
|
97
100
|
});
|
|
101
|
+
|
|
102
|
+
it("returns the value in correct format if seconds are provided", () => {
|
|
103
|
+
const result = formatInputTime("00:25:04", format, input);
|
|
104
|
+
|
|
105
|
+
expect(result).toBe("12:25");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("returns an empty string if an empty string is provided", () => {
|
|
109
|
+
const result = formatInputTime("", 24, { id: "exampleInput" });
|
|
110
|
+
|
|
111
|
+
expect(result).toBe("");
|
|
112
|
+
});
|
|
98
113
|
})
|
|
99
114
|
});
|
|
100
115
|
|
|
@@ -77,7 +77,7 @@ const createUnloadPreventer = () => {
|
|
|
77
77
|
ev.preventDefault();
|
|
78
78
|
ev.stopPropagation();
|
|
79
79
|
|
|
80
|
-
confirmAction(confirmMessage, link).then((answer) => {
|
|
80
|
+
confirmAction(confirmMessage, link, { iconName: "alert-line" }).then((answer) => {
|
|
81
81
|
if (!answer) {
|
|
82
82
|
return;
|
|
83
83
|
}
|
|
@@ -109,7 +109,7 @@ const createUnloadPreventer = () => {
|
|
|
109
109
|
ev.stopImmediatePropagation();
|
|
110
110
|
ev.stopPropagation();
|
|
111
111
|
|
|
112
|
-
confirmAction(confirmMessage, button).then((answer) => {
|
|
112
|
+
confirmAction(confirmMessage, button, { iconName: "alert-line" }).then((answer) => {
|
|
113
113
|
if (!answer) {
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
@@ -13,18 +13,12 @@
|
|
|
13
13
|
@apply block max-h-40 overflow-hidden relative first:[&_*]:mt-0 before:content-[''] before:absolute before:inset-0 before:h-full before:w-full before:bg-gradient-to-b before:from-transparent before:to-white after:content-[''] after:absolute after:inset-0 after:h-full after:w-full after:bg-gradient-to-b after:from-transparent after:to-white;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
[aria-expanded
|
|
17
|
-
|
|
18
|
-
[aria-expanded="true"] > span:first-of-type,
|
|
19
|
-
[aria-expanded="true"] > svg:first-of-type {
|
|
20
|
-
@apply hidden;
|
|
16
|
+
[aria-expanded] > svg {
|
|
17
|
+
@apply transition-transform;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
[aria-expanded="true"] > svg
|
|
24
|
-
|
|
25
|
-
[aria-expanded="false"] > span:first-of-type,
|
|
26
|
-
[aria-expanded="false"] > svg:first-of-type {
|
|
27
|
-
@apply block;
|
|
20
|
+
[aria-expanded="true"] > svg {
|
|
21
|
+
@apply rotate-180;
|
|
28
22
|
}
|
|
29
23
|
}
|
|
30
24
|
|
|
@@ -92,14 +92,6 @@
|
|
|
92
92
|
[data-dialog-closable] {
|
|
93
93
|
@apply top-6 right-6;
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
[data-dialog-container] {
|
|
97
|
-
@apply inline grid-cols-none;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
[data-dialog-title] {
|
|
101
|
-
@apply col-span-2 md:col-span-1 md:col-start-2 grid grid-cols-[auto_1fr] items-start md:items-center gap-2 pb-4 text-left text-xl text-gray-2 md:border-b md:border-gray-3;
|
|
102
|
-
}
|
|
103
95
|
}
|
|
104
96
|
|
|
105
97
|
&-results {
|
|
@@ -22,19 +22,27 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
[data-dialog-container] {
|
|
25
|
-
@apply
|
|
25
|
+
@apply block text-left;
|
|
26
26
|
|
|
27
|
-
> svg {
|
|
28
|
-
@apply
|
|
29
|
-
|
|
27
|
+
&:has(> svg) {
|
|
28
|
+
@apply grid grid-cols-[auto_1fr] items-start md:items-center gap-2;
|
|
29
|
+
|
|
30
|
+
> svg {
|
|
31
|
+
@apply w-6 h-6 text-gray fill-current flex-none mb-6;
|
|
32
|
+
}
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
> :last-child {
|
|
35
|
+
@apply col-span-2 md:col-span-1 md:col-start-2;
|
|
36
|
+
}
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
[data-dialog-title] {
|
|
37
|
-
@apply text-
|
|
41
|
+
@apply flex items-center gap-2 text-xl text-gray-2 font-semibold focus:outline-none pb-2 mb-4 md:border-b md:border-gray-3;
|
|
42
|
+
|
|
43
|
+
> svg {
|
|
44
|
+
@apply w-6 h-6 text-gray fill-current flex-none;
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
[data-dialog-actions] {
|
|
@@ -59,7 +67,7 @@
|
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
[data-dialog-container] {
|
|
62
|
-
@apply grid-cols-none;
|
|
70
|
+
@apply grid grid-cols-none;
|
|
63
71
|
|
|
64
72
|
&.layout-content {
|
|
65
73
|
@apply bg-white;
|
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
.qr-modal {
|
|
2
2
|
@apply md:p-6 lg:p-8;
|
|
3
3
|
|
|
4
|
-
[data-dialog-title] {
|
|
5
|
-
@apply flex justify-start items-center text-2xl text-black font-semibold focus:outline-none;
|
|
6
|
-
|
|
7
|
-
> svg {
|
|
8
|
-
@apply w-10 h-10 text-gray fill-current flex-none;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
> :last-child {
|
|
12
|
-
@apply ml-1;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
4
|
[data-dialog-container] {
|
|
17
|
-
@apply inline grid-cols-none;
|
|
18
|
-
|
|
19
5
|
.content {
|
|
20
6
|
@apply md:grid md:grid-cols-2 gap-5 md:py-4;
|
|
21
7
|
|
|
@@ -59,14 +45,6 @@
|
|
|
59
45
|
@apply top-6 right-6;
|
|
60
46
|
}
|
|
61
47
|
|
|
62
|
-
[data-dialog-container] {
|
|
63
|
-
@apply inline grid-cols-none;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
[data-dialog-title] {
|
|
67
|
-
@apply col-span-2 md:col-span-1 md:col-start-2 grid grid-cols-[auto_1fr] items-start md:items-center gap-2 pb-4 text-left text-xl text-gray-2 md:border-b md:border-gray-3;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
48
|
&__list {
|
|
71
49
|
@apply my-6 flex flex-wrap justify-center items-center md:grid grid-cols-4 md:grid-cols-5 gap-3 md:gap-6 max-w-96 mx-auto text-secondary;
|
|
72
50
|
|
|
@@ -111,7 +111,17 @@
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
&-item {
|
|
114
|
-
@apply flex items-center gap-1 text-white font-semibold hover:underline
|
|
114
|
+
@apply flex items-center gap-1 text-white font-semibold hover:underline;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&-label {
|
|
118
|
+
@apply flex-1 min-w-0 overflow-hidden text-ellipsis;
|
|
119
|
+
|
|
120
|
+
overflow-wrap: anywhere;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
&-item > svg {
|
|
124
|
+
@apply shrink-0 fill-current;
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
127
|
|
|
@@ -7,18 +7,21 @@
|
|
|
7
7
|
.editor-content-videoEmbed {
|
|
8
8
|
@apply my-10 mx-auto;
|
|
9
9
|
}
|
|
10
|
+
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
// Resource mentions are rendered both inside rich text (`.rich-text-display`)
|
|
13
|
+
// and inside comments (`.editor-content`), so they cannot be scoped to one of
|
|
14
|
+
// those wrappers.
|
|
15
|
+
.editor-mention {
|
|
16
|
+
@apply inline-flex items-center align-bottom font-semibold underline text-secondary;
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
img,
|
|
19
|
+
svg {
|
|
20
|
+
@apply w-5 h-5 m-0 rounded-full;
|
|
21
|
+
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
23
|
+
span {
|
|
24
|
+
@apply ml-1;
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.title-decorator {
|
|
30
|
-
@apply h2 md:h1 decorator;
|
|
30
|
+
@apply h2 md:h1 decorator break-normal;
|
|
31
|
+
|
|
32
|
+
hyphens: auto;
|
|
33
|
+
overflow-wrap: anywhere;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
.text-with-links {
|
|
@@ -38,7 +41,7 @@
|
|
|
38
41
|
|
|
39
42
|
/* default styles for those dynamic text blocks */
|
|
40
43
|
.editor-content {
|
|
41
|
-
@apply prose max-w-full text-gray-2 text-lg prose-a:text-secondary break-words prose-ul:pl-
|
|
44
|
+
@apply prose max-w-full text-gray-2 text-lg prose-a:text-secondary break-words prose-ul:pl-8 prose-ol:pl-8;
|
|
42
45
|
|
|
43
46
|
iframe {
|
|
44
47
|
@apply w-full block max-w-[600px] max-h-[70vh] h-[450px] my-10 mx-auto;
|
|
@@ -1725,7 +1725,6 @@ body.outlook p {
|
|
|
1725
1725
|
table.body th.decidim-bar,
|
|
1726
1726
|
table.body td.decidim-bar {
|
|
1727
1727
|
padding: 10px 0;
|
|
1728
|
-
background-color: #1a181d;
|
|
1729
1728
|
}
|
|
1730
1729
|
|
|
1731
1730
|
table.body {
|
|
@@ -1742,10 +1741,6 @@ table.body {
|
|
|
1742
1741
|
height: 30px;
|
|
1743
1742
|
}
|
|
1744
1743
|
|
|
1745
|
-
.cityhall-bar {
|
|
1746
|
-
background-color: #2c2930;
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
1744
|
.cityhall-logo {
|
|
1750
1745
|
width: 150px;
|
|
1751
1746
|
margin-top: 16px;
|
|
@@ -9,6 +9,7 @@ module Decidim
|
|
|
9
9
|
locales_action?
|
|
10
10
|
component_public_action?
|
|
11
11
|
search_scope_action?
|
|
12
|
+
public_content_action?
|
|
12
13
|
|
|
13
14
|
public_report_content_action?
|
|
14
15
|
|
|
@@ -66,6 +67,23 @@ module Decidim
|
|
|
66
67
|
toggle_allow([:search, :pick].include?(permission_action.action))
|
|
67
68
|
end
|
|
68
69
|
|
|
70
|
+
# Public, non-resource content that any visitor is allowed to reach. The
|
|
71
|
+
# controllers using these subjects enforce the actual data visibility
|
|
72
|
+
# themselves (profile publication, private download tokens, newsletter
|
|
73
|
+
# tokens, etc.).
|
|
74
|
+
def public_content_action?
|
|
75
|
+
case permission_action.subject
|
|
76
|
+
when :open_data, :search, :profile, :private_download, :short_link,
|
|
77
|
+
:calendar, :badges, :static_map, :qr_code, :homepage, :manifest,
|
|
78
|
+
:offline, :link, :authorization, :error, :timeout, :page
|
|
79
|
+
allow! if permission_action.action == :read
|
|
80
|
+
when :newsletter
|
|
81
|
+
allow! if [:read, :unsubscribe].include?(permission_action.action)
|
|
82
|
+
when :upload_validation
|
|
83
|
+
allow! if permission_action.action == :create
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
69
87
|
def manage_self_user_action?
|
|
70
88
|
return unless permission_action.subject == :user
|
|
71
89
|
|
|
@@ -78,6 +96,8 @@ module Decidim
|
|
|
78
96
|
authorization = context.fetch(:authorization, nil)
|
|
79
97
|
|
|
80
98
|
case permission_action.action
|
|
99
|
+
when :read
|
|
100
|
+
allow!
|
|
81
101
|
when :create
|
|
82
102
|
toggle_allow(authorization.user == user && not_already_active?(authorization))
|
|
83
103
|
when :update, :destroy
|
|
@@ -29,7 +29,7 @@ module Decidim
|
|
|
29
29
|
|
|
30
30
|
def action_string
|
|
31
31
|
case action
|
|
32
|
-
when "export_component", "soft_delete", "restore"
|
|
32
|
+
when "export_component", "soft_delete", "restore", "menu_hidden"
|
|
33
33
|
"decidim.admin_log.component.#{action}"
|
|
34
34
|
when "create", "delete", "publish", "unpublish", "update_permissions", "update_filters"
|
|
35
35
|
generate_action_string(action)
|
|
@@ -111,9 +111,18 @@ module Decidim
|
|
|
111
111
|
# Returns an HTML-safe String.
|
|
112
112
|
def present_explanation
|
|
113
113
|
h.content_tag(:div, class: "logs__log__explanation") do
|
|
114
|
+
escaped_params = i18n_params.transform_values do |value|
|
|
115
|
+
if value.is_a?(String) && !value.html_safe?
|
|
116
|
+
ERB::Util.html_escape(value)
|
|
117
|
+
else
|
|
118
|
+
value
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
114
122
|
I18n.t(
|
|
115
123
|
action_string,
|
|
116
|
-
|
|
124
|
+
escape: false,
|
|
125
|
+
**escaped_params
|
|
117
126
|
).html_safe
|
|
118
127
|
end
|
|
119
128
|
end
|
|
@@ -66,9 +66,9 @@ module Decidim
|
|
|
66
66
|
|
|
67
67
|
# Private: Presents the nickname of the user performing the action.
|
|
68
68
|
#
|
|
69
|
-
# Returns
|
|
69
|
+
# Returns a String.
|
|
70
70
|
def present_user_nickname
|
|
71
|
-
extra["nickname"]
|
|
71
|
+
extra["nickname"]
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
# Private: Calculates the path for the user. Returns the path of the
|
|
@@ -16,10 +16,19 @@ module Decidim
|
|
|
16
16
|
about: resource.user.about,
|
|
17
17
|
reasons: resource.reports.map(&:reason),
|
|
18
18
|
details: resource.reports.map(&:details),
|
|
19
|
-
block_reasons:
|
|
20
|
-
blocking_user:
|
|
19
|
+
block_reasons: blocking&.justification,
|
|
20
|
+
blocking_user: blocking_user&.presenter&.name
|
|
21
21
|
}
|
|
22
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# The collection filters on `decidim_users.blocked_at`, but this
|
|
27
|
+
# association is resolved through `decidim_users.block_id`. The two can
|
|
28
|
+
# disagree, in which case there is no block to read the details from.
|
|
29
|
+
def blocking = resource.blocking
|
|
30
|
+
|
|
31
|
+
def blocking_user = blocking&.blocking_user
|
|
23
32
|
end
|
|
24
33
|
end
|
|
25
34
|
end
|
|
@@ -14,7 +14,7 @@ module Decidim
|
|
|
14
14
|
id: resource.id,
|
|
15
15
|
hidden_at: resource.hidden_at,
|
|
16
16
|
report_count: resource.report_count,
|
|
17
|
-
reported_url
|
|
17
|
+
reported_url:,
|
|
18
18
|
reportable_type: resource.decidim_reportable_type,
|
|
19
19
|
reportable_id: resource.decidim_reportable_id,
|
|
20
20
|
reported_content: resource.reported_content,
|
|
@@ -25,6 +25,30 @@ module Decidim
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# `Decidim::Moderation#reportable` is polymorphic, so the database cannot
|
|
32
|
+
# enforce the reference, and the moderation row outlives the resource it
|
|
33
|
+
# points at. Both of the cases below leave the moderation in the export
|
|
34
|
+
# collection with no URL to build.
|
|
35
|
+
def reported_url
|
|
36
|
+
reportable = resource.reportable
|
|
37
|
+
|
|
38
|
+
# The resource is gone. Reportables such as proposals and meetings are
|
|
39
|
+
# soft-deletable while `belongs_to :reportable` does not use
|
|
40
|
+
# `with_deleted`, so moving one to the trash is enough for this.
|
|
41
|
+
return if reportable.nil?
|
|
42
|
+
|
|
43
|
+
# The component is gone but the resource is not: trashing a component
|
|
44
|
+
# does not trash its contents. `ResourceLocatorPresenter#route_proxy`
|
|
45
|
+
# falls back to the resource itself when `component` is nil, and
|
|
46
|
+
# `EngineRouter.main_proxy` then calls `mounted_engine` on it, which
|
|
47
|
+
# only components and participatory spaces respond to.
|
|
48
|
+
return if reportable.is_a?(Decidim::HasComponent) && reportable.component.nil?
|
|
49
|
+
|
|
50
|
+
reportable.reported_content_url
|
|
51
|
+
end
|
|
28
52
|
end
|
|
29
53
|
end
|
|
30
54
|
end
|