Release v2.5.6
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
// Announcement duplication helpers.
|
||||
|
||||
function buildDuplicateAnnouncementName(announcementName) {
|
||||
const baseName = 'Copy of ' + String(announcementName || '').trim();
|
||||
return baseName;
|
||||
}
|
||||
|
||||
function buildDuplicateAnnouncement(announcement, duplicateName) {
|
||||
return Object.assign({}, announcement, {
|
||||
id: null,
|
||||
short_label: duplicateName,
|
||||
expires_at: null,
|
||||
screen_ids: Array.isArray(announcement && announcement.screen_ids) ? announcement.screen_ids.slice() : []
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicateAnnouncementName,
|
||||
buildDuplicateAnnouncement
|
||||
};
|
||||
@@ -24,8 +24,11 @@ function buildAnnouncementFormViewModel(announcement, message, currentUser, opti
|
||||
const announcementColors = Array.isArray(options && options.announcementColors) ? options.announcementColors : [];
|
||||
const announcementIcons = Array.isArray(options && options.announcementIcons) ? options.announcementIcons : [];
|
||||
const announcementScreens = Array.isArray(options && options.announcementScreens) ? options.announcementScreens : [];
|
||||
const viewOptions = options || {};
|
||||
const viewAnnouncement = Object.assign({}, buildDefaultAnnouncement(), announcement || {});
|
||||
const canDelete = Boolean(currentUser && Array.isArray(currentUser.permissionKeys) && currentUser.permissionKeys.indexOf('announcements.delete') !== -1);
|
||||
const hasTargets = Array.isArray(viewAnnouncement.screen_ids) && viewAnnouncement.screen_ids.length > 0;
|
||||
const isActive = !(viewAnnouncement.expires_at && new Date(viewAnnouncement.expires_at).getTime() <= Date.now());
|
||||
|
||||
return {
|
||||
title: isEdit ? 'Edit announcement' : 'Add announcement',
|
||||
@@ -34,9 +37,29 @@ function buildAnnouncementFormViewModel(announcement, message, currentUser, opti
|
||||
currentUser: currentUser || null,
|
||||
announcement: viewAnnouncement,
|
||||
isEdit: Boolean(isEdit),
|
||||
showSaveSecondaryActions: Boolean(isEdit),
|
||||
showDeleteAction: Boolean(isEdit && canDelete),
|
||||
showSaveSecondaryActions: true,
|
||||
messageVariant: viewOptions.messageVariant || '',
|
||||
actionLabel: isActive ? 'Stop' : 'Play',
|
||||
actionIcon: isActive ? 'bi-stop-fill' : 'bi-play-fill',
|
||||
actionDisabled: !isActive && !hasTargets,
|
||||
actionClassName: !isActive && !hasTargets
|
||||
? 'btn-outline-info'
|
||||
: (isActive ? 'btn-outline-warning' : 'btn-info'),
|
||||
actionDisabledTitle: !isActive && !hasTargets
|
||||
? 'Select at least one screen group to play this announcement.'
|
||||
: '',
|
||||
actionConfirmMessage: isActive
|
||||
? 'Stop this announcement on the selected screens now?'
|
||||
: 'Send this announcement to the selected screens now?',
|
||||
actionPath: isEdit && viewAnnouncement.id
|
||||
? (isActive ? '/announcements/' + viewAnnouncement.id + '/stop' : '/announcements/' + viewAnnouncement.id + '/play')
|
||||
: '',
|
||||
showDeleteAction: Boolean(canDelete || !isEdit),
|
||||
deleteDisabled: !isEdit || !Boolean(viewAnnouncement && viewAnnouncement.canDelete),
|
||||
deleteConfirmMessage: isEdit ? 'Delete this announcement?' : '',
|
||||
deleteTitle: !isEdit
|
||||
? 'Delete is available after the announcement has been created.'
|
||||
: (!Boolean(viewAnnouncement && viewAnnouncement.canDelete) ? 'This announcement is currently active and cannot be deleted.' : ''),
|
||||
formAction: isEdit && viewAnnouncement && viewAnnouncement.id ? '/announcements/' + viewAnnouncement.id : '/announcements',
|
||||
formAttrs: 'data-async-save' + (isEdit ? ' data-async-save-close-url="/announcements"' : ' data-async-save-new-redirect="response-url"') + ' data-async-save-new-url="/announcements/new"',
|
||||
cancelUrl: '/announcements',
|
||||
|
||||
@@ -21,10 +21,15 @@ function formatStatusLabel(announcement) {
|
||||
|
||||
function formatActionButton(announcement) {
|
||||
const isActive = formatStatusLabel(announcement) === 'Active';
|
||||
const hasTargets = Math.max(0, Number(announcement && announcement.screen_target_count) || 0) > 0;
|
||||
return {
|
||||
label: isActive ? 'Stop' : 'Play',
|
||||
icon: isActive ? 'bi-stop-fill' : 'bi-play-fill',
|
||||
className: isActive ? 'btn-outline-warning' : 'btn-outline-success',
|
||||
disabled: !isActive && !hasTargets,
|
||||
className: !isActive && !hasTargets
|
||||
? 'btn-outline-info'
|
||||
: (isActive ? 'btn-outline-warning' : 'btn-info'),
|
||||
disabledTitle: !isActive && !hasTargets ? 'Select at least one screen group to play this announcement.' : '',
|
||||
confirmMessage: isActive
|
||||
? 'Stop this announcement on the selected screens now?'
|
||||
: 'Send this announcement to the selected screens now?',
|
||||
@@ -32,6 +37,10 @@ function formatActionButton(announcement) {
|
||||
};
|
||||
}
|
||||
|
||||
function isAnnouncementDeletionBlocked(announcement) {
|
||||
return formatStatusLabel(announcement) === 'Active';
|
||||
}
|
||||
|
||||
function formatIconLabel(iconKey) {
|
||||
const normalized = String(iconKey || '').trim().toLowerCase() || DEFAULT_ANNOUNCEMENT_ICON;
|
||||
return ANNOUNCEMENT_ICON_LABELS[normalized] || normalized;
|
||||
@@ -69,6 +78,7 @@ function formatScreenTargetsLabel(announcement) {
|
||||
module.exports = function renderAnnouncementsPage(data, message, currentUser) {
|
||||
const announcements = (data.announcements || []).map(function (announcement) {
|
||||
const actionButton = formatActionButton(announcement);
|
||||
const deleteDisabled = isAnnouncementDeletionBlocked(announcement);
|
||||
return Object.assign({}, announcement, {
|
||||
shortLabel: String(announcement.short_label || '').trim(),
|
||||
iconKey: String(announcement.icon_key || DEFAULT_ANNOUNCEMENT_ICON).trim().toLowerCase() || DEFAULT_ANNOUNCEMENT_ICON,
|
||||
@@ -77,9 +87,13 @@ module.exports = function renderAnnouncementsPage(data, message, currentUser) {
|
||||
expiresAtLabel: formatExpiresLabel(announcement.expires_at),
|
||||
screenTargetsLabel: formatScreenTargetsLabel(announcement),
|
||||
statusLabel: formatStatusLabel(announcement),
|
||||
deleteDisabled: deleteDisabled,
|
||||
deleteBlockedMessage: deleteDisabled ? 'This announcement is currently active and cannot be deleted.' : '',
|
||||
actionButtonLabel: actionButton.label,
|
||||
actionButtonIcon: actionButton.icon,
|
||||
actionButtonClassName: actionButton.className,
|
||||
actionButtonDisabled: actionButton.disabled,
|
||||
actionButtonDisabledTitle: actionButton.disabledTitle,
|
||||
actionButtonConfirmMessage: actionButton.confirmMessage,
|
||||
actionButtonPath: actionButton.actionPath
|
||||
});
|
||||
|
||||
@@ -11,12 +11,13 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
const { buildPagination } = require('../../../lib/pagination');
|
||||
const announcementData = require('#src/data/announcements');
|
||||
const announcementIcons = require('#src/data/announcement-icons');
|
||||
const { buildDuplicateAnnouncementName, buildDuplicateAnnouncement } = require('./duplicate');
|
||||
|
||||
const LIST_PAGE_SIZE = 25;
|
||||
const ANNOUNCEMENT_TYPES = [
|
||||
{ value: 'lower-third', label: 'Lower third' },
|
||||
{ value: 'fullscreen', label: 'Fullscreen' },
|
||||
{ value: 'top-banner', label: 'Top banner' }
|
||||
{ value: 'lower-third', label: 'Lower third', icon: 'arrow-bar-down' },
|
||||
{ value: 'fullscreen', label: 'Fullscreen', icon: 'arrows-fullscreen' },
|
||||
{ value: 'top-banner', label: 'Top banner', icon: 'arrow-bar-up' }
|
||||
];
|
||||
const ANNOUNCEMENT_COLORS = [
|
||||
{ value: 'primary', label: 'Primary' },
|
||||
@@ -63,8 +64,7 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
}
|
||||
|
||||
function isAnnouncementDeletionBlocked(announcement) {
|
||||
const screenIds = Array.isArray(announcement && announcement.screen_ids) ? announcement.screen_ids : [];
|
||||
return isAnnouncementActive(announcement) || screenIds.length > 0;
|
||||
return isAnnouncementActive(announcement);
|
||||
}
|
||||
|
||||
async function fetchAllScreenSlugs() {
|
||||
@@ -221,6 +221,10 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
const durationMode = String(normalized.durationMode || '').trim() || (durationSeconds > 0 ? 'duration' : 'until_disabled');
|
||||
const isActive = isAnnouncementActive(normalized);
|
||||
const canDelete = Boolean(normalized.id) && !isAnnouncementDeletionBlocked(normalized);
|
||||
const hasTargets = Array.isArray(normalized.screen_ids) && normalized.screen_ids.length > 0;
|
||||
const deleteBlockedMessage = canDelete
|
||||
? ''
|
||||
: 'This announcement is currently active and cannot be deleted.';
|
||||
|
||||
return Object.assign({}, normalized, {
|
||||
durationMode: durationMode,
|
||||
@@ -230,7 +234,11 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
isActive: isActive,
|
||||
actionLabel: isActive ? 'Stop' : 'Play',
|
||||
actionIcon: isActive ? 'bi-stop-fill' : 'bi-play-fill',
|
||||
actionClassName: isActive ? 'btn-outline-warning' : 'btn-outline-success',
|
||||
actionDisabled: !isActive && !hasTargets,
|
||||
actionClassName: !isActive && !hasTargets
|
||||
? 'btn-outline-info'
|
||||
: (isActive ? 'btn-outline-warning' : 'btn-info'),
|
||||
actionDisabledTitle: !isActive && !hasTargets ? 'Select at least one screen group to play this announcement.' : '',
|
||||
actionConfirmMessage: isActive
|
||||
? 'Stop this announcement on the selected screens now?'
|
||||
: 'Send this announcement to the selected screens now?',
|
||||
@@ -238,7 +246,7 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
? `/announcements/${normalized.id}/stop`
|
||||
: `/announcements/${normalized.id}/play`,
|
||||
canDelete: canDelete,
|
||||
deleteBlockedMessage: canDelete ? '' : 'This announcement is still in use by one or more screens.'
|
||||
deleteBlockedMessage: deleteBlockedMessage
|
||||
});
|
||||
}
|
||||
|
||||
@@ -301,6 +309,33 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/announcements/:id/duplicate', requirePermission('announcements.read'), requirePermission('announcements.create'), async function (req, res, next) {
|
||||
try {
|
||||
const announcement = await common.fetchAnnouncementById(pool, Number(req.params.id));
|
||||
if (!announcement) {
|
||||
return res.status(404).send('Announcement not found');
|
||||
}
|
||||
|
||||
const screens = await fetchAnnouncementScreens();
|
||||
let duplicateName = buildDuplicateAnnouncementName(announcement.short_label || announcement.message || 'Announcement');
|
||||
let duplicateIndex = 2;
|
||||
while (await common.fetchDuplicateName(pool, 'd_announcements', duplicateName, null, 'short_label')) {
|
||||
duplicateName = buildDuplicateAnnouncementName(announcement.short_label || announcement.message || 'Announcement') + ' (' + duplicateIndex + ')';
|
||||
duplicateIndex += 1;
|
||||
}
|
||||
|
||||
res.send(pages.renderAnnouncementAddPage(buildAnnouncementFormData(buildDuplicateAnnouncement(announcement, duplicateName)), req.query.message ? String(req.query.message) : 'Review the copied values and save when ready.', req.currentUser, {
|
||||
announcementTypes: ANNOUNCEMENT_TYPES,
|
||||
announcementColors: ANNOUNCEMENT_COLORS,
|
||||
announcementIcons: ANNOUNCEMENT_ICONS,
|
||||
announcementScreens: buildAnnouncementScreenOptions(screens, announcement.screen_ids || []),
|
||||
messageVariant: 'info'
|
||||
}));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/announcements', requirePermission('announcements.create'), async function (req, res, next) {
|
||||
try {
|
||||
const message = String(req.body.message || '').replace(/\r\n/g, '\n').trim();
|
||||
@@ -421,7 +456,7 @@ module.exports = function registerAnnouncementRoutes(app, deps) {
|
||||
}
|
||||
|
||||
if (isAnnouncementDeletionBlocked(announcement)) {
|
||||
return res.redirect('/announcements/' + announcement.id + '/edit?message=' + encodeURIComponent('This announcement is still in use by one or more screens.'));
|
||||
return res.redirect('/announcements/' + announcement.id + '/edit?message=' + encodeURIComponent('This announcement is currently active and cannot be deleted.'));
|
||||
}
|
||||
|
||||
await pool.query('DELETE FROM d_announcements WHERE id = ?', [announcement.id]);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Canvas size duplicate helpers.
|
||||
|
||||
function buildDuplicateCanvasSizeName(name) {
|
||||
const source = String(name || 'Canvas size').trim() || 'Canvas size';
|
||||
return 'Copy of ' + source;
|
||||
}
|
||||
|
||||
function buildDuplicateCanvasSize(canvasSize, duplicateName) {
|
||||
const source = canvasSize || {};
|
||||
return {
|
||||
id: null,
|
||||
name: String(duplicateName || buildDuplicateCanvasSizeName(source.name)).trim(),
|
||||
width: Math.max(1, Number(source.width || 1920)),
|
||||
height: Math.max(1, Number(source.height || 1080))
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicateCanvasSizeName,
|
||||
buildDuplicateCanvasSize
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
// Shared canvas size form view-model builder.
|
||||
|
||||
const { MAX_CANVAS_SIZE_DIMENSION } = require('#src/data');
|
||||
|
||||
function buildDefaultCanvasSize() {
|
||||
return {
|
||||
id: null,
|
||||
@@ -19,6 +21,8 @@ function buildCanvasSizeFormViewModel(canvasSize, message, currentUser, isEdit)
|
||||
currentUser: currentUser || null,
|
||||
canvasSize: viewCanvasSize,
|
||||
inUse: Boolean(viewCanvasSize && viewCanvasSize.inUse),
|
||||
dimensionsLocked: Boolean(viewCanvasSize && viewCanvasSize.inUse),
|
||||
maxDimension: MAX_CANVAS_SIZE_DIMENSION,
|
||||
isEdit: Boolean(isEdit),
|
||||
formAction: isEdit && viewCanvasSize ? '/canvas-sizes/' + viewCanvasSize.id : '/canvas-sizes',
|
||||
formAttrs: 'data-async-save' + (isEdit ? ' data-async-save-close-url="/canvas-sizes"' : ' data-async-save-new-redirect="response-url"') + ' data-async-save-new-url="/canvas-sizes/new"',
|
||||
|
||||
@@ -2,8 +2,15 @@
|
||||
|
||||
const renderPlaylistEditorPage = require('./form-page');
|
||||
|
||||
module.exports = async function renderPlaylistFormPage(message, currentUser, data, pool) {
|
||||
module.exports = async function renderPlaylistFormPage(playlistOrMessage, messageOrCurrentUser, currentUserOrData, dataOrPool, poolMaybe) {
|
||||
const hasPlaylistArgument = arguments.length >= 5;
|
||||
const playlist = hasPlaylistArgument ? playlistOrMessage : null;
|
||||
const message = hasPlaylistArgument ? messageOrCurrentUser : playlistOrMessage;
|
||||
const currentUser = hasPlaylistArgument ? currentUserOrData : messageOrCurrentUser;
|
||||
const data = hasPlaylistArgument ? dataOrPool : currentUserOrData;
|
||||
const pool = hasPlaylistArgument ? poolMaybe : dataOrPool;
|
||||
const adminData = data || (pool ? await require('#src/data').fetchAdminData(pool) : { slides: [] });
|
||||
return renderPlaylistEditorPage(null, adminData, message, currentUser, { isEdit: false });
|
||||
|
||||
return renderPlaylistEditorPage(playlist, adminData, message, currentUser, { isEdit: false });
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Playlist duplicate helpers.
|
||||
|
||||
function buildDuplicatePlaylistName(name) {
|
||||
const source = String(name || 'Playlist').trim() || 'Playlist';
|
||||
return 'Copy of ' + source;
|
||||
}
|
||||
|
||||
function buildDuplicatePlaylist(playlist, duplicateName) {
|
||||
const source = playlist || {};
|
||||
return {
|
||||
id: source.id || null,
|
||||
name: String(duplicateName || buildDuplicatePlaylistName(source.name)).trim(),
|
||||
fade_between_slides: Boolean(source.fade_between_slides),
|
||||
skip_unavailable_rtmp: Boolean(source.skip_unavailable_rtmp),
|
||||
canvas_id: source.canvas_id || null
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicatePlaylistName,
|
||||
buildDuplicatePlaylist
|
||||
};
|
||||
@@ -7,6 +7,7 @@ module.exports = function registerPlaylistsRoutes(app, deps) {
|
||||
const fetchScreensByPlaylistId = deps.fetchScreensByPlaylistId;
|
||||
const { buildPagination } = require('../../../lib/pagination');
|
||||
const requirePermission = deps.requirePermission;
|
||||
const { buildDuplicatePlaylistName, buildDuplicatePlaylist } = require('./duplicate');
|
||||
|
||||
const LIST_PAGE_SIZE = 25;
|
||||
|
||||
@@ -51,6 +52,28 @@ module.exports = function registerPlaylistsRoutes(app, deps) {
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/playlists/:id/duplicate', requirePermission('playlists.read'), requirePermission('playlists.create'), async function (req, res, next) {
|
||||
try {
|
||||
const playlist = await common.fetchPlaylistById(pool, Number(req.params.id));
|
||||
if (!playlist) {
|
||||
return res.status(404).send('Playlist not found');
|
||||
}
|
||||
|
||||
const data = await common.fetchAdminData(pool);
|
||||
let duplicateName = buildDuplicatePlaylistName(playlist.name);
|
||||
let duplicateIndex = 2;
|
||||
while (await common.fetchDuplicateName(pool, 'c_playlists', duplicateName)) {
|
||||
duplicateName = buildDuplicatePlaylistName(playlist.name) + ' (' + duplicateIndex + ')';
|
||||
duplicateIndex += 1;
|
||||
}
|
||||
|
||||
const duplicatePlaylist = buildDuplicatePlaylist(playlist, duplicateName);
|
||||
res.send(await pages.renderPlaylistFormPage(duplicatePlaylist, req.query.message ? String(req.query.message) : 'Review the copied values and save when ready.', req.currentUser, data, pool));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/playlists/:id/edit', requirePermission('playlists.update'), async function (req, res, next) {
|
||||
try {
|
||||
const playlist = await common.fetchPlaylistById(pool, Number(req.params.id));
|
||||
|
||||
@@ -16,8 +16,9 @@ function buildScreenFormViewModel(screen, data, message, currentUser, isEdit) {
|
||||
screen: viewScreen,
|
||||
inUse: Boolean(viewScreen.inUse),
|
||||
isEdit: Boolean(isEdit),
|
||||
showSaveSecondaryActions: Boolean(isEdit),
|
||||
showSaveSecondaryActions: true,
|
||||
deleteDisabled: Boolean(isEdit) ? Boolean(viewScreen.inUse) : true,
|
||||
deleteConfirmMessage: Boolean(isEdit) ? 'Delete this screen?' : '',
|
||||
formAction: Boolean(isEdit) && viewScreen.id ? '/screens/' + viewScreen.id : '/screens',
|
||||
formAttrs: Boolean(isEdit)
|
||||
? 'data-async-save data-async-save-close-url="/screens" data-async-save-new-url="/screens/new"'
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
const { renderView } = require('../../../view');
|
||||
const { buildSlideFormViewModel } = require('./form-view-model');
|
||||
|
||||
module.exports = function renderSlideAddPage(data, message, currentUser) {
|
||||
return renderView('slides/form', buildSlideFormViewModel(data || {}, null, message, currentUser, false));
|
||||
module.exports = function renderSlideAddPage(slideOrData, dataOrMessage, messageOrCurrentUser, currentUserMaybe) {
|
||||
const hasSlideArgument = arguments.length >= 4;
|
||||
const slide = hasSlideArgument ? slideOrData : null;
|
||||
const data = hasSlideArgument ? dataOrMessage : slideOrData;
|
||||
const message = hasSlideArgument ? messageOrCurrentUser : dataOrMessage;
|
||||
const currentUser = hasSlideArgument ? currentUserMaybe : messageOrCurrentUser;
|
||||
return renderView('slides/form', buildSlideFormViewModel(data || {}, slide || null, message, currentUser, false));
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Slide duplicate helpers.
|
||||
|
||||
function buildDuplicateSlideName(title) {
|
||||
const source = String(title || 'Slide').trim() || 'Slide';
|
||||
return 'Copy of ' + source;
|
||||
}
|
||||
|
||||
function buildDuplicateSlide(slide, duplicateName) {
|
||||
const source = slide || {};
|
||||
const content = source.content && typeof source.content === 'object'
|
||||
? JSON.parse(JSON.stringify(source.content))
|
||||
: {};
|
||||
|
||||
return {
|
||||
id: null,
|
||||
title: String(duplicateName || buildDuplicateSlideName(source.title)).trim(),
|
||||
template_id: source.template_id || null,
|
||||
content: content
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicateSlideName,
|
||||
buildDuplicateSlide
|
||||
};
|
||||
@@ -53,7 +53,7 @@ function buildSlideFormViewModel(data, slide, message, currentUser, isEdit) {
|
||||
existingContent: viewSlide && viewSlide.content ? viewSlide.content : {}
|
||||
},
|
||||
assetVersion: assetVersion,
|
||||
slideEditorScripts: ['vendor/qrcode-generator/qrcode.js?v=' + assetVersion].concat(getRegionEditorScripts(assetVersion)),
|
||||
slideEditorScripts: ['vendor/qr-code-styling/qr-code-styling-loader.js?v=' + assetVersion].concat(getRegionEditorScripts(assetVersion)),
|
||||
currentUser: currentUser || null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Template duplicate helpers.
|
||||
|
||||
function buildDuplicateTemplateName(name) {
|
||||
const source = String(name || 'Template').trim() || 'Template';
|
||||
return 'Copy of ' + source;
|
||||
}
|
||||
|
||||
function cloneRegion(region) {
|
||||
const cloned = JSON.parse(JSON.stringify(region || {}));
|
||||
delete cloned.id;
|
||||
delete cloned.template_id;
|
||||
delete cloned.created_at;
|
||||
delete cloned.modified_at;
|
||||
delete cloned.created_by;
|
||||
delete cloned.modified_by;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
function buildDuplicateTemplate(template, duplicateName) {
|
||||
const source = template || {};
|
||||
return {
|
||||
id: null,
|
||||
name: String(duplicateName || buildDuplicateTemplateName(source.name)).trim(),
|
||||
canvas_size_id: source.canvas_size_id || null,
|
||||
canvas_size_width: source.canvas_size_width,
|
||||
canvas_size_height: source.canvas_size_height,
|
||||
background_color: source.background_color,
|
||||
background_image_path: source.background_image_path,
|
||||
region_usage: [],
|
||||
regions: Array.isArray(source.regions) ? source.regions.map(cloneRegion) : []
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicateTemplateName,
|
||||
buildDuplicateTemplate
|
||||
};
|
||||
@@ -55,15 +55,17 @@ function buildTemplateFormViewModel(template, message, canvasSizes, currentUser,
|
||||
template: current,
|
||||
inUse: Boolean(current.inUse),
|
||||
isEdit: Boolean(isEdit),
|
||||
showSaveSecondaryActions: Boolean(isEdit),
|
||||
showSaveSecondaryActions: true,
|
||||
deleteDisabled: !isEdit || Boolean(current.inUse),
|
||||
formAction: isEdit && current.id ? '/templates/' + current.id : '/templates',
|
||||
formAttrs: 'data-template-has-slides="' + (current.slide_count ? 'true' : 'false') + '" data-async-save' + (isEdit ? ' data-async-save-close-url="/templates"' : ' data-async-save-new-redirect="response-url"') + ' data-async-save-new-url="/templates/new"',
|
||||
formAttrs: 'data-template-has-slides="' + (current.slide_count ? 'true' : 'false') + '" data-async-save data-async-save-close-url="/templates"' + (isEdit ? '' : ' data-async-save-new-redirect="response-url"') + ' data-async-save-new-url="/templates/new"',
|
||||
cancelUrl: '/templates',
|
||||
cancelConfirmMessage: "You've made changes. Are you sure you want to leave this page?",
|
||||
deleteUrl: isEdit && current.id ? '/templates/' + current.id + '/delete' : '',
|
||||
deleteConfirmMessage: isEdit ? 'Delete this template?' : '',
|
||||
canvasSizes: canvasSizes || [],
|
||||
animationPresets: animationPresets,
|
||||
scripts: ['js/lib/modal.js?v=' + assetVersion, 'js/templates/animation-presets.js?v=' + assetVersion, 'vendor/qrcode-generator/qrcode.js?v=' + assetVersion].concat(getRegionEditorScripts(assetVersion), ['js/templates/template-designer-utils.js?v=' + assetVersion, 'js/templates/template-designer.js?v=' + assetVersion])
|
||||
scripts: ['js/lib/modal.js?v=' + assetVersion, 'js/templates/animation-presets.js?v=' + assetVersion, 'vendor/qr-code-styling/qr-code-styling-loader.js?v=' + assetVersion].concat(getRegionEditorScripts(assetVersion), ['js/templates/template-designer-utils.js?v=' + assetVersion, 'js/templates/template-designer.js?v=' + assetVersion])
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user