Release v2.5.6

This commit is contained in:
2026-08-05 19:38:16 +01:00
parent a8ef35b287
commit 9f831f04bc
161 changed files with 8487 additions and 1295 deletions
@@ -3,6 +3,6 @@
const { renderView } = require('../../../view');
const { buildTimetableGroupFormViewModel } = require('./form-view-model');
module.exports = function renderTimetableGroupAddPage(timetableGroup, timetableEntries, message, currentUser) {
return renderView('data-sources/timetables/form', buildTimetableGroupFormViewModel(timetableGroup, timetableEntries, message, currentUser, false));
module.exports = function renderTimetableGroupAddPage(timetableGroup, timetableEntries, message, currentUser, options) {
return renderView('data-sources/timetables/form', buildTimetableGroupFormViewModel(timetableGroup, timetableEntries, message, currentUser, false, options));
};
@@ -0,0 +1,27 @@
// Timetable duplication helpers.
function buildDuplicateTimetableGroupName(groupName) {
const baseName = 'Copy of ' + String(groupName || '').trim();
return baseName;
}
function buildDuplicateTimetableGroup(timetableGroup, duplicateName) {
return Object.assign({}, timetableGroup, {
id: null,
name: duplicateName,
shortDescription: timetableGroup.short_description || ''
});
}
function buildDuplicateTimetableEntry(timetableEntry) {
return Object.assign({}, timetableEntry, {
id: null,
schedule_group_id: null
});
}
module.exports = {
buildDuplicateTimetableGroupName,
buildDuplicateTimetableGroup,
buildDuplicateTimetableEntry
};
@@ -26,8 +26,9 @@ function formatDateTimeLocalValue(value) {
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
function buildTimetableGroupFormViewModel(timetableGroup, timetableEntries, message, currentUser, isEdit) {
function buildTimetableGroupFormViewModel(timetableGroup, timetableEntries, message, currentUser, isEdit, options) {
const viewTimetableGroup = Object.assign(buildDefaultTimetableGroup(), timetableGroup || {});
const viewOptions = options || {};
const viewTimetableEntries = Array.isArray(timetableEntries) && timetableEntries.length
? timetableEntries.map(function (entry) {
return Object.assign({}, entry, {
@@ -42,11 +43,14 @@ function buildTimetableGroupFormViewModel(timetableGroup, timetableEntries, mess
active: 'timetables',
message: message,
currentUser: currentUser || null,
messageVariant: viewOptions.messageVariant || '',
isEdit: Boolean(isEdit),
timetableGroup: viewTimetableGroup,
timetableEntries: viewTimetableEntries,
inUse: Boolean(viewTimetableGroup.inUse),
showSaveSecondaryActions: Boolean(isEdit),
showSaveSecondaryActions: true,
cancelConfirmMessage: "You've made changes. Are you sure you want to leave this page?",
deleteConfirmMessage: isEdit ? 'Delete this timetable group?' : '',
deleteDisabled: !isEdit || Boolean(viewTimetableGroup.inUse),
formAction: isEdit && viewTimetableGroup.id ? '/data-sources/timetables/' + viewTimetableGroup.id : '/data-sources/timetables',
formAttrs: 'data-async-save' + (isEdit ? ' data-async-save-close-url="/data-sources/timetables"' : ' data-async-save-new-redirect="response-url"') + ' data-async-save-new-url="/data-sources/timetables/new"',
@@ -4,6 +4,7 @@ const { buildPagination } = require('../../../lib/pagination');
const renderTimetableGroupsPage = require('./list');
const renderTimetableGroupAddPage = require('./add');
const renderTimetableGroupEditPage = require('./edit');
const { buildDuplicateTimetableGroupName, buildDuplicateTimetableGroup, buildDuplicateTimetableEntry } = require('./duplicate');
async function getDataSourceUsageMaps(pool, common) {
const [slides] = await pool.query('SELECT content_json FROM c_slides WHERE content_json IS NOT NULL');
@@ -25,8 +26,8 @@ async function getDataSourceUsageMaps(pool, common) {
return;
}
if (Object.prototype.hasOwnProperty.call(value, 'schedule_group_id')) {
const timetableGroupId = Number(value.schedule_group_id);
if (Object.prototype.hasOwnProperty.call(value, 'timetable_group_id')) {
const timetableGroupId = Number(value.timetable_group_id);
if (Number.isFinite(timetableGroupId)) {
timetableGroupIds.add(timetableGroupId);
}
@@ -176,6 +177,32 @@ module.exports = function registerTimetableRoutes(app, deps) {
}
});
app.get('/data-sources/timetables/:id/duplicate', requirePermission('timetables.read'), requirePermission('timetables.create'), async function (req, res, next) {
try {
const timetableGroup = await common.fetchTimetableGroupById(pool, Number(req.params.id));
if (!timetableGroup) {
return res.status(404).send('Timetable group not found');
}
const timetableEntries = typeof common.fetchTimetableEntriesByGroupId === 'function'
? await common.fetchTimetableEntriesByGroupId(pool, timetableGroup.id)
: [];
let duplicateName = buildDuplicateTimetableGroupName(timetableGroup.name);
let duplicateIndex = 2;
while (await common.fetchDuplicateName(pool, 'i_schedule_groups', duplicateName)) {
duplicateName = buildDuplicateTimetableGroupName(timetableGroup.name) + ' (' + duplicateIndex + ')';
duplicateIndex += 1;
}
res.send(renderTimetableGroupAddPage(buildDuplicateTimetableGroup(timetableGroup, duplicateName), timetableEntries.map(buildDuplicateTimetableEntry), req.query.message ? String(req.query.message) : 'Review the copied values and save when ready.', req.currentUser, {
messageVariant: 'info'
}));
} catch (error) {
next(error);
}
});
app.post('/data-sources/timetables', requirePermission('timetables.create'), async function (req, res, next) {
const connection = await pool.getConnection();
try {