Release v2.4.2
This commit is contained in:
+33
-20
@@ -90,25 +90,31 @@ function getCanvasSignature(width, height) {
|
||||
return normalizedWidth + 'x' + normalizedHeight;
|
||||
}
|
||||
|
||||
async function fetchPlaylistCanvasSignature(pool, playlistId) {
|
||||
async function fetchPlaylistCanvasId(pool, playlistId) {
|
||||
const [playlistRows] = await pool.query('SELECT canvas_id FROM c_playlists WHERE id = ?', [playlistId]);
|
||||
const storedCanvasId = Number(playlistRows && playlistRows[0] && playlistRows[0].canvas_id);
|
||||
if (Number.isInteger(storedCanvasId) && storedCanvasId > 0) {
|
||||
return storedCanvasId;
|
||||
}
|
||||
|
||||
const [rows] = await pool.query(
|
||||
`SELECT DISTINCT cs.width AS canvas_width, cs.height AS canvas_height
|
||||
`SELECT DISTINCT st.canvas_size_id AS canvas_id
|
||||
FROM c_playlist_slides ps
|
||||
JOIN c_slides sl ON sl.id = ps.slide_id
|
||||
LEFT JOIN c_templates st ON st.id = sl.template_id
|
||||
LEFT JOIN c_canvas_sizes cs ON cs.id = st.canvas_size_id
|
||||
WHERE ps.playlist_id = ?
|
||||
AND cs.width IS NOT NULL
|
||||
AND cs.height IS NOT NULL`,
|
||||
AND st.canvas_size_id IS NOT NULL`,
|
||||
[playlistId]
|
||||
);
|
||||
const signatures = Array.from(new Set(rows.map(function (row) {
|
||||
return getCanvasSignature(row.canvas_width, row.canvas_height);
|
||||
}).filter(Boolean)));
|
||||
if (!signatures.length) {
|
||||
const canvasIds = Array.from(new Set(rows.map(function (row) {
|
||||
return Number(row.canvas_id);
|
||||
}).filter(function (value) {
|
||||
return Number.isInteger(value) && value > 0;
|
||||
})));
|
||||
if (!canvasIds.length) {
|
||||
return null;
|
||||
}
|
||||
return signatures.length === 1 ? signatures[0] : 'mismatch';
|
||||
return canvasIds.length === 1 ? canvasIds[0] : 'mismatch';
|
||||
}
|
||||
|
||||
async function fetchScreensByPlaylistId(connection, playlistId) {
|
||||
@@ -160,19 +166,25 @@ async function fetchOrderedPlaylistSlides(connection, playlistId) {
|
||||
|
||||
function redirectAfterSave(req, res, defaultUrl, options) {
|
||||
const safeOptions = options || {};
|
||||
const action = String((req && req.body && req.body.action) || req.query.action || '').toLowerCase();
|
||||
const action = String((req && req.body && (req.body.action || req.body.save_action)) || req.query.action || '').toLowerCase();
|
||||
const message = safeOptions.message || '';
|
||||
|
||||
function withMessage(url) {
|
||||
if (!message) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const joiner = String(url || '').indexOf('?') === -1 ? '?' : '&';
|
||||
return url + joiner + 'message=' + encodeURIComponent(message);
|
||||
}
|
||||
|
||||
if (action === 'close') {
|
||||
return res.redirect(safeOptions.closeUrl || defaultUrl);
|
||||
return res.redirect(withMessage(safeOptions.closeUrl || defaultUrl));
|
||||
}
|
||||
if (action === 'new') {
|
||||
return res.redirect(safeOptions.newUrl || defaultUrl);
|
||||
return res.redirect(withMessage(safeOptions.newUrl || defaultUrl));
|
||||
}
|
||||
const message = safeOptions.message || '';
|
||||
if (message) {
|
||||
const joiner = defaultUrl.indexOf('?') === -1 ? '?' : '&';
|
||||
return res.redirect(defaultUrl + joiner + 'message=' + encodeURIComponent(message));
|
||||
}
|
||||
return res.redirect(defaultUrl);
|
||||
return res.redirect(withMessage(defaultUrl));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -185,7 +197,8 @@ module.exports = {
|
||||
normalizeScheduleMode: normalizeScheduleMode,
|
||||
getAuditUserId: getAuditUserId,
|
||||
getCanvasSignature: getCanvasSignature,
|
||||
fetchPlaylistCanvasSignature: fetchPlaylistCanvasSignature,
|
||||
fetchPlaylistCanvasId: fetchPlaylistCanvasId,
|
||||
fetchPlaylistCanvasSignature: fetchPlaylistCanvasId,
|
||||
fetchScreensByPlaylistId: fetchScreensByPlaylistId,
|
||||
fetchScreensBySlideId: fetchScreensBySlideId,
|
||||
fetchScreensByTemplateId: fetchScreensByTemplateId,
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
// Shared schedule rule helpers for playlist playback and editor rendering.
|
||||
|
||||
const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
|
||||
function normalizeDateTimeValue(value) {
|
||||
const raw = String(value || '').trim();
|
||||
return raw ? raw : null;
|
||||
}
|
||||
|
||||
function normalizeTimeValue(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const match = raw.match(/^(\d{2}):(\d{2})(?::\d{2})?$/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return match[1] + ':' + match[2];
|
||||
}
|
||||
|
||||
function normalizeDayValues(value) {
|
||||
let days = [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
days = value;
|
||||
} else if (value !== undefined && value !== null && String(value).trim()) {
|
||||
try {
|
||||
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
||||
if (Array.isArray(parsed)) {
|
||||
days = parsed;
|
||||
}
|
||||
} catch (_error) {
|
||||
days = String(value)
|
||||
.split(',')
|
||||
.map(function (item) {
|
||||
return item.trim();
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(new Set(days.map(function (day) {
|
||||
return Number(day);
|
||||
}).filter(function (day) {
|
||||
return Number.isInteger(day) && day >= 0 && day <= 6;
|
||||
}))).sort(function (left, right) {
|
||||
return left - right;
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeScheduleRule(rule) {
|
||||
const input = rule && typeof rule === 'object' ? rule : {};
|
||||
const startDatetime = normalizeDateTimeValue(
|
||||
input.start_datetime || input.startDateTime || input.schedule_start_datetime || input.scheduleStartDatetime
|
||||
);
|
||||
const endDatetime = normalizeDateTimeValue(
|
||||
input.end_datetime || input.endDateTime || input.schedule_end_datetime || input.scheduleEndDatetime
|
||||
);
|
||||
const startTime = normalizeTimeValue(
|
||||
input.start_time || input.startTime || input.schedule_start_time || input.scheduleStartTime
|
||||
);
|
||||
const endTime = normalizeTimeValue(
|
||||
input.end_time || input.endTime || input.schedule_end_time || input.scheduleEndTime
|
||||
);
|
||||
const days = normalizeDayValues(
|
||||
input.days || input.day_list || input.schedule_days || input.schedule_days_json || input.scheduleDays
|
||||
);
|
||||
|
||||
if (startDatetime || endDatetime) {
|
||||
if (!startDatetime || !endDatetime) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (startTime || endTime) {
|
||||
if (!startTime || !endTime) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!startDatetime && !endDatetime && !startTime && !endTime && !days.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalized = {};
|
||||
if (startDatetime) {
|
||||
normalized.start_datetime = startDatetime;
|
||||
}
|
||||
if (endDatetime) {
|
||||
normalized.end_datetime = endDatetime;
|
||||
}
|
||||
if (startTime) {
|
||||
normalized.start_time = startTime;
|
||||
}
|
||||
if (endTime) {
|
||||
normalized.end_time = endTime;
|
||||
}
|
||||
if (days.length) {
|
||||
normalized.days = days;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function parseScheduleRules(value) {
|
||||
let rawRules = [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
rawRules = value;
|
||||
} else if (value && typeof value === 'object' && Array.isArray(value.rules)) {
|
||||
rawRules = value.rules;
|
||||
} else {
|
||||
const rawValue = String(value || '').trim();
|
||||
if (!rawValue) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(rawValue);
|
||||
rawRules = Array.isArray(parsed) ? parsed : [];
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return rawRules
|
||||
.map(function (rule) {
|
||||
return normalizeScheduleRule(rule);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function serializeScheduleRules(value) {
|
||||
const normalized = parseScheduleRules(value);
|
||||
if (normalized === null) {
|
||||
return null;
|
||||
}
|
||||
return JSON.stringify(normalized);
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
return '';
|
||||
}
|
||||
return raw.replace('T', ' ').slice(0, 16);
|
||||
}
|
||||
|
||||
function formatTime(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
return '';
|
||||
}
|
||||
return raw.slice(0, 5);
|
||||
}
|
||||
|
||||
function formatDays(value) {
|
||||
const days = normalizeDayValues(value);
|
||||
return days
|
||||
.map(function (day) {
|
||||
return DAY_NAMES[day] || '';
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
function formatScheduleRuleSummary(rule) {
|
||||
const normalized = normalizeScheduleRule(rule);
|
||||
if (!normalized) {
|
||||
return 'Always visible';
|
||||
}
|
||||
|
||||
const leftParts = [];
|
||||
if (normalized.start_datetime && normalized.end_datetime) {
|
||||
leftParts.push('Dates ' + formatDateTime(normalized.start_datetime) + ' to ' + formatDateTime(normalized.end_datetime));
|
||||
} else {
|
||||
leftParts.push('Any date');
|
||||
}
|
||||
|
||||
const rightParts = [];
|
||||
const days = formatDays(normalized.days || []);
|
||||
if (days) {
|
||||
rightParts.push(days);
|
||||
}
|
||||
if (normalized.start_time && normalized.end_time) {
|
||||
rightParts.push(formatTime(normalized.start_time) + '-' + formatTime(normalized.end_time));
|
||||
}
|
||||
|
||||
if (rightParts.length) {
|
||||
return leftParts[0] + ': ' + rightParts.join(' ');
|
||||
}
|
||||
|
||||
return leftParts[0];
|
||||
}
|
||||
|
||||
function formatScheduleRulesSummary(value) {
|
||||
const rules = parseScheduleRules(value) || [];
|
||||
if (!rules.length) {
|
||||
return 'Always visible';
|
||||
}
|
||||
|
||||
return rules.length === 1 ? '1 rule' : String(rules.length) + ' rules';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normalizeDateTimeValue: normalizeDateTimeValue,
|
||||
normalizeTimeValue: normalizeTimeValue,
|
||||
normalizeDayValues: normalizeDayValues,
|
||||
normalizeScheduleRule: normalizeScheduleRule,
|
||||
parseScheduleRules: parseScheduleRules,
|
||||
serializeScheduleRules: serializeScheduleRules,
|
||||
formatScheduleRuleSummary: formatScheduleRuleSummary,
|
||||
formatScheduleRulesSummary: formatScheduleRulesSummary
|
||||
};
|
||||
Reference in New Issue
Block a user