Release v2.6.19
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ const { fetchAdminData, fetchPlaylistsPage, fetchSlidesPage, fetchTemplatesPage,
|
||||
const { ANNOUNCEMENT_TYPES, ANNOUNCEMENT_COLORS, ANNOUNCEMENT_ICONS, DEFAULT_ANNOUNCEMENT_ICON, normalizeAnnouncementType, normalizeAnnouncementColor, normalizeAnnouncementIcon, fetchAnnouncementsPage, fetchAnnouncementById, fetchActiveAnnouncement, buildAnnouncementPayload } = require('./announcements');
|
||||
const { ANNOUNCEMENT_ICON_OPTIONS, ANNOUNCEMENT_ICON_LABELS } = require('./announcement-icons');
|
||||
const { fetchPlaylistById } = require('./playlists');
|
||||
const { normalizeDisplayMode, fetchTimetablesData, fetchTimetableGroupsPage, fetchTimetableGroupById, fetchTimetableEntriesByGroupId, buildTimetableGroupPayload } = require('./schedules');
|
||||
const { normalizeDisplayMode, fetchTimetablesData, fetchTimetableGroupsPage, fetchTimetableGroupById, fetchTimetableEntriesByGroupId, buildTimetableGroupPayload } = require('./timetables');
|
||||
const { fetchApiSourcesData, fetchApiSourcesPage, fetchApiSourceById, fetchApiSourceResponse, buildApiSourcePayload } = require('./api-sources');
|
||||
const { fetchRssFeedsData, fetchRssFeedsPage, fetchRssFeedById, fetchRssFeedItemsByFeedId, normalizeRssFeedItem, buildRssFeedPayload, fetchRssFeedItems, replaceRssFeedItems } = require('./rss-feeds');
|
||||
const { slugify, uniqueScreenSlug, fetchScreenById, fetchScreenEditData, fetchScreenPlayerUrls, fetchPlayerPublicBaseUrl, fetchScreenPlayerRecord, fetchPlayerRecordByIdentifier } = require('./screens');
|
||||
|
||||
@@ -308,6 +308,49 @@ async function buildTemplateContent(pool, template, body, filesByField, existing
|
||||
value: submitted === undefined ? String(current.value !== undefined ? current.value : current.text !== undefined ? current.text : '') : String(submitted || ''),
|
||||
timezone: timezoneValue === undefined || timezoneValue === null ? String(current.timezone || current.time_zone || '') : String(timezoneValue || '').trim()
|
||||
};
|
||||
} else if (region.region_type === 'timetable') {
|
||||
const current = existingContent && existingContent[region.region_key] && typeof existingContent[region.region_key] === 'object' ? existingContent[region.region_key] : {};
|
||||
const suffix = '_' + region.id;
|
||||
const generic = {};
|
||||
const submittedText = body[`region_text_${region.id}`];
|
||||
const normalizedText = submittedText === undefined ? String(current.text !== undefined ? current.text : current.value !== undefined ? current.value : '') : String(submittedText || '');
|
||||
|
||||
Object.keys(body || {}).forEach((key) => {
|
||||
if (!key.startsWith('region_') || !key.endsWith(suffix)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const field = key.slice('region_'.length, -suffix.length);
|
||||
if (!field || field === 'type' || field === 'key' || field === 'name' || field === 'label') {
|
||||
return;
|
||||
}
|
||||
|
||||
generic[field] = body[key];
|
||||
});
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(generic, 'timetable_display_mode')) {
|
||||
generic.display_mode = generic.timetable_display_mode;
|
||||
delete generic.timetable_display_mode;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(generic, 'timetable_max_items')) {
|
||||
generic.max_items = generic.timetable_max_items;
|
||||
delete generic.timetable_max_items;
|
||||
}
|
||||
|
||||
Object.keys(current).forEach((key) => {
|
||||
if (generic[key] === undefined) {
|
||||
generic[key] = current[key];
|
||||
}
|
||||
});
|
||||
|
||||
delete generic.timetable_display_mode;
|
||||
delete generic.timetable_max_items;
|
||||
|
||||
generic.text = normalizedText;
|
||||
generic.value = normalizedText;
|
||||
generic.type = region.region_type;
|
||||
content[region.region_key] = generic;
|
||||
} else if (region.region_type === 'rss') {
|
||||
const submitted = body[`region_text_${region.id}`];
|
||||
const current = existingContent && existingContent[region.region_key] ? existingContent[region.region_key] : {};
|
||||
|
||||
@@ -4,6 +4,23 @@ const { fetchPagedRows, validateMaxLength } = require('./utils');
|
||||
|
||||
const NAME_MAX_LENGTH = 255;
|
||||
const DESCRIPTION_MAX_LENGTH = 255;
|
||||
const DEFAULT_TIME_ZONE = 'Europe/London';
|
||||
|
||||
function normalizeTimeZone(value, fallback) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
return String(fallback || DEFAULT_TIME_ZONE).trim() || DEFAULT_TIME_ZONE;
|
||||
}
|
||||
|
||||
try {
|
||||
new Intl.DateTimeFormat('en-GB', { timeZone: raw }).format(new Date());
|
||||
return raw;
|
||||
} catch (_error) {
|
||||
const error = new Error('Timetable time zone is invalid.');
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeDisplayMode(value) {
|
||||
const mode = String(value || 'upcoming').trim().toLowerCase();
|
||||
@@ -15,15 +32,15 @@ function normalizeDisplayMode(value) {
|
||||
|
||||
async function fetchTimetablesData(pool) {
|
||||
const [timetableGroups] = await pool.query(`
|
||||
SELECT g.id, g.name, g.short_description, g.created_at, g.modified_at, g.created_by, g.modified_by,
|
||||
(SELECT COUNT(*) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id) AS entry_count,
|
||||
(SELECT MIN(e.start_datetime) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id AND e.start_datetime IS NOT NULL) AS next_start_datetime
|
||||
FROM i_schedule_groups g
|
||||
SELECT g.id, g.name, g.short_description, g.timezone, g.created_at, g.modified_at, g.created_by, g.modified_by,
|
||||
(SELECT COUNT(*) FROM i_timetable_entries e WHERE e.schedule_group_id = g.id) AS entry_count,
|
||||
(SELECT MIN(e.start_datetime) FROM i_timetable_entries e WHERE e.schedule_group_id = g.id AND e.start_datetime IS NOT NULL) AS next_start_datetime
|
||||
FROM i_timetable_groups g
|
||||
ORDER BY g.modified_at DESC, g.id DESC
|
||||
`);
|
||||
const [timetableEntries] = await pool.query(`
|
||||
SELECT id, schedule_group_id, title, short_description, start_datetime, end_datetime, created_at, modified_at, created_by, modified_by
|
||||
FROM i_schedule_entries
|
||||
FROM i_timetable_entries
|
||||
ORDER BY schedule_group_id ASC, start_datetime ASC, id ASC
|
||||
`);
|
||||
|
||||
@@ -50,17 +67,18 @@ async function fetchTimetablesData(pool) {
|
||||
|
||||
async function fetchTimetableGroupsPage(pool, page, pageSize, searchTerm, sortKey, sortDirection) {
|
||||
const paged = await fetchPagedRows(pool, {
|
||||
selectSql: `SELECT g.id, g.name, g.short_description, g.created_at, g.modified_at, g.created_by, g.modified_by,
|
||||
(SELECT COUNT(*) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id) AS entry_count,
|
||||
(SELECT MIN(e.start_datetime) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id AND e.start_datetime IS NOT NULL) AS next_start_datetime
|
||||
FROM i_schedule_groups g
|
||||
selectSql: `SELECT g.id, g.name, g.short_description, g.timezone, g.created_at, g.modified_at, g.created_by, g.modified_by,
|
||||
(SELECT COUNT(*) FROM i_timetable_entries e WHERE e.schedule_group_id = g.id) AS entry_count,
|
||||
(SELECT MIN(e.start_datetime) FROM i_timetable_entries e WHERE e.schedule_group_id = g.id AND e.start_datetime IS NOT NULL) AS next_start_datetime
|
||||
FROM i_timetable_groups g
|
||||
ORDER BY g.modified_at DESC, g.id DESC`,
|
||||
countSql: 'SELECT COUNT(*) AS count FROM i_schedule_groups',
|
||||
countSql: 'SELECT COUNT(*) AS count FROM i_timetable_groups',
|
||||
searchColumns: ['g.name', 'g.short_description'],
|
||||
searchTerm: searchTerm,
|
||||
sortColumns: {
|
||||
name: 'g.name',
|
||||
description: 'g.short_description',
|
||||
timezone: 'g.timezone',
|
||||
entries: 'entry_count',
|
||||
next_start: 'next_start_datetime',
|
||||
created: 'g.created_at',
|
||||
@@ -77,7 +95,7 @@ async function fetchTimetableGroupsPage(pool, page, pageSize, searchTerm, sortKe
|
||||
|
||||
async function fetchTimetableGroupById(pool, id) {
|
||||
const [rows] = await pool.query(
|
||||
'SELECT id, name, short_description, created_at, modified_at, created_by, modified_by FROM i_schedule_groups WHERE id = ?',
|
||||
'SELECT id, name, short_description, timezone, created_at, modified_at, created_by, modified_by FROM i_timetable_groups WHERE id = ?',
|
||||
[id]
|
||||
);
|
||||
|
||||
@@ -87,7 +105,7 @@ async function fetchTimetableGroupById(pool, id) {
|
||||
async function fetchTimetableEntriesByGroupId(pool, timetableGroupId) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT id, schedule_group_id, title, short_description, start_datetime, end_datetime, created_at, modified_at, created_by, modified_by
|
||||
FROM i_schedule_entries
|
||||
FROM i_timetable_entries
|
||||
WHERE schedule_group_id = ?
|
||||
ORDER BY start_datetime ASC, id ASC`,
|
||||
[timetableGroupId]
|
||||
@@ -100,6 +118,7 @@ function buildTimetableGroupPayload(req, existingTimetableGroup) {
|
||||
const fallback = existingTimetableGroup || {};
|
||||
const name = validateMaxLength(req.body.name || fallback.name || '', NAME_MAX_LENGTH, 'Timetable group name');
|
||||
const shortDescription = validateMaxLength(req.body.short_description || req.body.shortDescription || fallback.short_description || '', DESCRIPTION_MAX_LENGTH, 'Timetable group description');
|
||||
const timezone = normalizeTimeZone(req.body.timezone || req.body.time_zone || fallback.timezone || DEFAULT_TIME_ZONE, fallback.timezone || DEFAULT_TIME_ZONE);
|
||||
|
||||
if (!name) {
|
||||
const error = new Error('Timetable group name is required.');
|
||||
@@ -109,7 +128,8 @@ function buildTimetableGroupPayload(req, existingTimetableGroup) {
|
||||
|
||||
return {
|
||||
name: name,
|
||||
shortDescription: shortDescription
|
||||
shortDescription: shortDescription,
|
||||
timezone: timezone
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,5 +139,6 @@ module.exports = {
|
||||
fetchTimetableGroupsPage,
|
||||
fetchTimetableGroupById,
|
||||
fetchTimetableEntriesByGroupId,
|
||||
buildTimetableGroupPayload
|
||||
buildTimetableGroupPayload,
|
||||
normalizeTimeZone
|
||||
};
|
||||
Reference in New Issue
Block a user