Release v2.5.6
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// API source duplication helpers.
|
||||
|
||||
function buildDuplicateApiSourceName(sourceName) {
|
||||
const baseName = 'Copy of ' + String(sourceName || '').trim();
|
||||
return baseName;
|
||||
}
|
||||
|
||||
function buildDuplicateApiSource(apiSource, duplicateName) {
|
||||
return Object.assign({}, apiSource, {
|
||||
id: null,
|
||||
name: duplicateName,
|
||||
apiUrl: apiSource.api_url || '',
|
||||
lastPulledAt: null,
|
||||
lastPullError: '',
|
||||
lastResponseStatus: null,
|
||||
lastResponseContentType: '',
|
||||
lastResponseJson: '',
|
||||
authMethod: apiSource.auth_method || 'none',
|
||||
authUsername: apiSource.auth_username || '',
|
||||
authPassword: apiSource.auth_password || '',
|
||||
authBearerToken: apiSource.auth_bearer_token || '',
|
||||
authHeaderName: apiSource.auth_header_name || 'X-API-Key',
|
||||
authHeaderValue: apiSource.auth_header_value || '',
|
||||
itemsPath: apiSource.items_path || ''
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicateApiSourceName,
|
||||
buildDuplicateApiSource
|
||||
};
|
||||
@@ -35,8 +35,9 @@ function buildApiSourceFormViewModel(apiSource, message, currentUser, options, i
|
||||
inUse: Boolean(viewApiSource.inUse),
|
||||
lastResponseJson: viewOptions.lastResponseJson || viewApiSource.lastResponseJson || '',
|
||||
lastPullError: viewOptions.lastPullError || viewApiSource.lastPullError || '',
|
||||
messageVariant: viewOptions.messageVariant || '',
|
||||
isEdit: Boolean(isEdit),
|
||||
showSaveSecondaryActions: Boolean(isEdit),
|
||||
showSaveSecondaryActions: true,
|
||||
deleteDisabled: !isEdit || Boolean(viewApiSource.inUse),
|
||||
formAction: isEdit && viewApiSource.id ? '/data-sources/api-sources/' + viewApiSource.id : '/data-sources/api-sources',
|
||||
formAttrs: isEdit && viewApiSource.id ? 'data-async-save data-async-save-refresh-target="#api-source-response-panel" data-async-save-refresh-state-url="/data-sources/api-sources/' + viewApiSource.id + '/state" data-async-save-close-url="/data-sources/api-sources" data-async-save-new-url="/data-sources/api-sources/new"' : 'data-async-save data-async-save-close-url="/data-sources/api-sources" data-async-save-new-redirect="response-url" data-async-save-new-url="/data-sources/api-sources/new"',
|
||||
|
||||
@@ -4,6 +4,7 @@ const { buildPagination } = require('../../../lib/pagination');
|
||||
const renderApiSourcesPage = require('./list');
|
||||
const renderApiSourceAddPage = require('./add');
|
||||
const renderApiSourceEditPage = require('./edit');
|
||||
const { buildDuplicateApiSourceName, buildDuplicateApiSource } = require('./duplicate');
|
||||
|
||||
function toIsoTimestamp(value) {
|
||||
if (!value) {
|
||||
@@ -185,6 +186,29 @@ module.exports = function registerApiSourceRoutes(app, deps) {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/data-sources/api-sources/:id/duplicate', requirePermission('api-sources.read'), requirePermission('api-sources.create'), async function (req, res, next) {
|
||||
try {
|
||||
const apiSource = await common.fetchApiSourceById(pool, Number(req.params.id));
|
||||
if (!apiSource) {
|
||||
return res.status(404).send('API source not found');
|
||||
}
|
||||
|
||||
let duplicateName = buildDuplicateApiSourceName(apiSource.name);
|
||||
let duplicateIndex = 2;
|
||||
while (await common.fetchDuplicateName(pool, 'i_api_sources', duplicateName)) {
|
||||
duplicateName = buildDuplicateApiSourceName(apiSource.name) + ' (' + duplicateIndex + ')';
|
||||
duplicateIndex += 1;
|
||||
}
|
||||
|
||||
res.send(renderApiSourceAddPage(buildDuplicateApiSource(apiSource, duplicateName), req.query.message ? String(req.query.message) : 'Review the copied values and save when ready.', req.currentUser, {
|
||||
messageVariant: 'info',
|
||||
showSaveSecondaryActions: true
|
||||
}));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/data-sources/api-sources/:id/state', requirePermission('api-sources.update'), function (req, res, next) {
|
||||
sendRefreshTaskState(req, res, dataSourceTasks, Number(req.params.id)).catch(next);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// RSS feed duplication helpers.
|
||||
|
||||
function buildDuplicateRssFeedName(feedName) {
|
||||
const baseName = 'Copy of ' + String(feedName || '').trim();
|
||||
return baseName;
|
||||
}
|
||||
|
||||
function buildDuplicateRssFeed(rssFeed, duplicateName) {
|
||||
return Object.assign({}, rssFeed, {
|
||||
id: null,
|
||||
name: duplicateName,
|
||||
feedUrl: rssFeed.feed_url || '',
|
||||
lastPulledAt: null,
|
||||
pullError: '',
|
||||
pulledItems: [],
|
||||
updateIntervalValue: rssFeed.update_interval_value,
|
||||
updateIntervalUnit: rssFeed.update_interval_unit || 'minutes',
|
||||
itemLimit: rssFeed.item_limit,
|
||||
feedUrlValue: rssFeed.feed_url || ''
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDuplicateRssFeedName,
|
||||
buildDuplicateRssFeed
|
||||
};
|
||||
@@ -24,8 +24,9 @@ function buildRssFeedFormViewModel(rssFeed, message, currentUser, options, isEdi
|
||||
inUse: Boolean(viewRssFeed.inUse),
|
||||
pulledItems: viewOptions.pulledItems || viewRssFeed.pulledItems || [],
|
||||
pullError: viewOptions.pullError || viewRssFeed.pullError || '',
|
||||
messageVariant: viewOptions.messageVariant || '',
|
||||
isEdit: Boolean(isEdit),
|
||||
showSaveSecondaryActions: Boolean(isEdit),
|
||||
showSaveSecondaryActions: true,
|
||||
deleteDisabled: !isEdit || Boolean(viewRssFeed.inUse),
|
||||
formAction: isEdit && viewRssFeed.id ? '/data-sources/rss-feeds/' + viewRssFeed.id : '/data-sources/rss-feeds',
|
||||
formAttrs: isEdit && viewRssFeed.id ? 'data-async-save data-async-save-refresh-target="#rss-feed-items-panel" data-async-save-refresh-state-url="/data-sources/rss-feeds/' + viewRssFeed.id + '/state" data-async-save-close-url="/data-sources/rss-feeds" data-async-save-new-url="/data-sources/rss-feeds/new"' : 'data-async-save data-async-save-close-url="/data-sources/rss-feeds" data-async-save-new-redirect="response-url" data-async-save-new-url="/data-sources/rss-feeds/new"',
|
||||
|
||||
@@ -4,6 +4,7 @@ const { buildPagination } = require('../../../lib/pagination');
|
||||
const renderRssFeedsPage = require('./list');
|
||||
const renderRssFeedAddPage = require('./add');
|
||||
const renderRssFeedEditPage = require('./edit');
|
||||
const { buildDuplicateRssFeedName, buildDuplicateRssFeed } = require('./duplicate');
|
||||
|
||||
async function getDataSourceUsageMaps(pool, common) {
|
||||
const [slides] = await pool.query('SELECT content_json FROM c_slides WHERE content_json IS NOT NULL');
|
||||
@@ -155,6 +156,29 @@ module.exports = function registerRssFeedRoutes(app, deps) {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/data-sources/rss-feeds/:id/duplicate', requirePermission('rss-feeds.read'), requirePermission('rss-feeds.create'), async function (req, res, next) {
|
||||
try {
|
||||
const rssFeed = await common.fetchRssFeedById(pool, Number(req.params.id));
|
||||
if (!rssFeed) {
|
||||
return res.status(404).send('RSS feed not found');
|
||||
}
|
||||
|
||||
let duplicateName = buildDuplicateRssFeedName(rssFeed.name);
|
||||
let duplicateIndex = 2;
|
||||
while (await common.fetchDuplicateName(pool, 'i_rss_feeds', duplicateName)) {
|
||||
duplicateName = buildDuplicateRssFeedName(rssFeed.name) + ' (' + duplicateIndex + ')';
|
||||
duplicateIndex += 1;
|
||||
}
|
||||
|
||||
res.send(renderRssFeedAddPage(buildDuplicateRssFeed(rssFeed, duplicateName), req.query.message ? String(req.query.message) : 'Review the copied values and save when ready.', req.currentUser, {
|
||||
messageVariant: 'info',
|
||||
showSaveSecondaryActions: true
|
||||
}));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/data-sources/rss-feeds/:id/state', requirePermission('rss-feeds.update'), function (req, res, next) {
|
||||
sendRefreshTaskState(req, res, dataSourceTasks, Number(req.params.id)).catch(next);
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user