Release v2.2.0
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
// Settings route registration for managed fonts.
|
||||
|
||||
const fs = require('fs');
|
||||
const { hasAnyPermission } = require('../../../rbac');
|
||||
const fontLibrary = require('../../lib/media/font-library');
|
||||
|
||||
function normalizeFontFamilyName(value) {
|
||||
return String(value || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
||||
}
|
||||
|
||||
async function fetchUsedFontFamilies(pool, fonts) {
|
||||
const usedFamilies = new Set();
|
||||
|
||||
for (let i = 0; i < (Array.isArray(fonts) ? fonts.length : 0); i += 1) {
|
||||
const family = normalizeFontFamilyName(fonts[i] && (fonts[i].family || fonts[i].name));
|
||||
if (!family) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [rows] = await pool.query(
|
||||
'SELECT COUNT(*) AS match_count FROM c_slides WHERE LOCATE(LOWER(?), LOWER(COALESCE(content_json, ""))) > 0',
|
||||
[family]
|
||||
);
|
||||
if (Number(rows && rows[0] && rows[0].match_count) > 0) {
|
||||
usedFamilies.add(family);
|
||||
}
|
||||
}
|
||||
|
||||
return usedFamilies;
|
||||
}
|
||||
|
||||
async function isFontUsed(pool, family) {
|
||||
const normalizedFamily = normalizeFontFamilyName(family);
|
||||
if (!normalizedFamily) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [rows] = await pool.query(
|
||||
'SELECT COUNT(*) AS match_count FROM c_slides WHERE LOCATE(LOWER(?), LOWER(COALESCE(content_json, ""))) > 0',
|
||||
[normalizedFamily]
|
||||
);
|
||||
|
||||
return Number(rows && rows[0] && rows[0].match_count) > 0;
|
||||
}
|
||||
|
||||
function redirectToLogin(res, setAuthMessageCookie) {
|
||||
if (typeof setAuthMessageCookie === 'function') {
|
||||
setAuthMessageCookie(res, 'Please sign in to continue.');
|
||||
return res.redirect('/login');
|
||||
}
|
||||
|
||||
return res.redirect('/login');
|
||||
}
|
||||
|
||||
function requireFontsAccess(setAuthMessageCookie) {
|
||||
return function (req, res, next) {
|
||||
if (!req.currentUser) {
|
||||
return redirectToLogin(res, setAuthMessageCookie);
|
||||
}
|
||||
|
||||
if (hasAnyPermission(req.currentUser, ['fonts.read', 'fonts.create', 'fonts.delete'])) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const error = new Error('You do not have permission to access this area.');
|
||||
error.statusCode = 403;
|
||||
error.expose = true;
|
||||
next(error);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function registerFontRoutes(app, deps) {
|
||||
const pool = deps.pool;
|
||||
const pages = deps.pages;
|
||||
const upload = deps.upload;
|
||||
const uploadDir = deps.uploadDir;
|
||||
const backgroundTaskQueue = deps.backgroundTaskQueue;
|
||||
const setAuthMessageCookie = deps.setAuthMessageCookie;
|
||||
|
||||
if (!pool || !pages || !upload || !uploadDir) {
|
||||
throw new Error('registerFontRoutes requires pool, pages, upload, and uploadDir.');
|
||||
}
|
||||
|
||||
app.get('/settings/fonts', requireFontsAccess(setAuthMessageCookie), async function (req, res, next) {
|
||||
try {
|
||||
const library = fontLibrary.loadFontLibrary(uploadDir);
|
||||
const usedFamilies = await fetchUsedFontFamilies(pool, library.fonts);
|
||||
const fonts = library.fonts.map(function (font) {
|
||||
const family = normalizeFontFamilyName(font.family || font.name);
|
||||
return Object.assign({}, font, {
|
||||
inUse: usedFamilies.has(family)
|
||||
});
|
||||
});
|
||||
res.send(pages.renderFontsPage({
|
||||
fonts: fonts,
|
||||
stylesheetHref: library.stylesheetHref
|
||||
}, req.query.message ? String(req.query.message) : '', req.currentUser));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/settings/fonts', requireFontsAccess(setAuthMessageCookie), upload.single('font_file'), async function (req, res, next) {
|
||||
try {
|
||||
if (!req.file) {
|
||||
return res.status(400).send('No font file was uploaded.');
|
||||
}
|
||||
|
||||
const result = await fontLibrary.addFontFile(uploadDir, req.file, String(req.body && req.body.font_family || '').trim());
|
||||
if (backgroundTaskQueue && typeof backgroundTaskQueue.enqueueTask === 'function') {
|
||||
await backgroundTaskQueue.enqueueTask({
|
||||
key: `font-sync:${result && result.font && result.font.id ? result.font.id : Date.now()}`,
|
||||
title: 'Font sync',
|
||||
category: 'fonts',
|
||||
taskType: 'font-sync',
|
||||
payload: {
|
||||
uploadDir: uploadDir,
|
||||
operations: fontLibrary.collectFontLibrarySyncOperations(uploadDir)
|
||||
},
|
||||
persist: true
|
||||
});
|
||||
}
|
||||
|
||||
res.redirect('/settings/fonts?message=' + encodeURIComponent('Font uploaded.'));
|
||||
} catch (error) {
|
||||
if (req.file && req.file.path) {
|
||||
try {
|
||||
await fs.promises.unlink(req.file.path);
|
||||
} catch (_cleanupError) {}
|
||||
}
|
||||
|
||||
if (error && /already exists/i.test(String(error.message || ''))) {
|
||||
const library = fontLibrary.loadFontLibrary(uploadDir);
|
||||
const usedFamilies = await fetchUsedFontFamilies(pool, library.fonts);
|
||||
const fonts = library.fonts.map(function (font) {
|
||||
const family = normalizeFontFamilyName(font.family || font.name);
|
||||
return Object.assign({}, font, {
|
||||
inUse: usedFamilies.has(family)
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(400).send(pages.renderFontsPage({
|
||||
fonts: fonts,
|
||||
stylesheetHref: library.stylesheetHref
|
||||
}, String(error.message || 'Font already exists.'), req.currentUser));
|
||||
}
|
||||
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/settings/fonts/:id/toggle', requireFontsAccess(setAuthMessageCookie), async function (req, res, next) {
|
||||
try {
|
||||
const enabled = String(req.body && req.body.enabled || '').trim() !== '0';
|
||||
const updated = await fontLibrary.setFontEnabled(uploadDir, String(req.params.id || '').trim(), enabled);
|
||||
if (!updated) {
|
||||
return res.redirect('/settings/fonts?message=' + encodeURIComponent('That font was not found.'));
|
||||
}
|
||||
|
||||
if (backgroundTaskQueue && typeof backgroundTaskQueue.enqueueTask === 'function') {
|
||||
await backgroundTaskQueue.enqueueTask({
|
||||
key: `font-sync:toggle:${updated && updated.id ? updated.id : Date.now()}`,
|
||||
title: 'Font sync',
|
||||
category: 'fonts',
|
||||
taskType: 'font-sync',
|
||||
payload: {
|
||||
uploadDir: uploadDir,
|
||||
operations: fontLibrary.collectFontLibrarySyncOperations(uploadDir)
|
||||
},
|
||||
persist: true
|
||||
});
|
||||
}
|
||||
|
||||
res.redirect('/settings/fonts?message=' + encodeURIComponent(enabled ? 'Font enabled.' : 'Font disabled.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/settings/fonts/:id/delete', requireFontsAccess(setAuthMessageCookie), async function (req, res, next) {
|
||||
try {
|
||||
const fontId = String(req.params.id || '').trim();
|
||||
const currentFont = fontLibrary.loadFontLibrary(uploadDir).fonts.find(function (font) {
|
||||
return String(font.id || font.fileName || '').trim() === fontId || String(font.fileName || '').trim() === fontId;
|
||||
});
|
||||
const family = currentFont ? String(currentFont.family || currentFont.name || '').trim() : '';
|
||||
if (family && await isFontUsed(pool, family)) {
|
||||
return res.redirect('/settings/fonts?message=' + encodeURIComponent('That font is still used by one or more slides.'));
|
||||
}
|
||||
|
||||
const removed = await fontLibrary.deleteFontFile(uploadDir, fontId);
|
||||
if (!removed) {
|
||||
return res.redirect('/settings/fonts?message=' + encodeURIComponent('That font was not found.'));
|
||||
}
|
||||
|
||||
if (backgroundTaskQueue && typeof backgroundTaskQueue.enqueueTask === 'function') {
|
||||
await backgroundTaskQueue.enqueueTask({
|
||||
key: `font-sync:delete:${removed && removed.id ? removed.id : Date.now()}`,
|
||||
title: 'Font sync',
|
||||
category: 'fonts',
|
||||
taskType: 'font-sync',
|
||||
payload: {
|
||||
uploadDir: uploadDir,
|
||||
operations: [
|
||||
removed.fileName ? { type: 'delete', uploadPath: '/media/fonts/' + removed.fileName } : null
|
||||
].concat(fontLibrary.collectFontLibrarySyncOperations(uploadDir)).filter(Boolean)
|
||||
},
|
||||
persist: true
|
||||
});
|
||||
}
|
||||
|
||||
res.redirect('/settings/fonts?message=' + encodeURIComponent('Font removed.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user