diff --git a/library.js b/library.js index dfb73d5..ef6bb42 100644 --- a/library.js +++ b/library.js @@ -2,7 +2,6 @@ const nconf = nodebb.require('nconf'); -const validator = require('validator'); const plugins = nodebb.require('./src/plugins'); const topics = nodebb.require('./src/topics'); @@ -43,27 +42,20 @@ plugin.addAdminNavigation = async function (header) { return header; }; -plugin.getFormattingOptions = async function () { +plugin.getFormattingOptions = async function (uid) { const defaultVisibility = { - mobile: true, - desktop: true, + // use d-none d-lg-block, visible on desktop + // use d-block d-lg-none, visible on mobile + class: 'd-block', // op or reply main: true, reply: true, }; let payload = { + uid, defaultVisibility, options: [ - { - name: 'tags', - title: '[[global:tags.tags]]', - className: 'fa fa-tags', - visibility: { - ...defaultVisibility, - desktop: false, - }, - }, { name: 'zen', title: '[[modules:composer.zen-mode]]', @@ -72,16 +64,36 @@ plugin.getFormattingOptions = async function () { }, ], }; - if (parseInt(meta.config.allowTopicsThumbnail, 10) === 1) { + const [canUploadImage, canUploadFile] = await privileges.global.can( + ['upload:post:image', 'upload:post:file'], uid + ); + if (canUploadImage) { + if (meta.config.allowTopicsThumbnail) { + payload.options.push({ + name: 'thumbs', + title: '[[topic:composer.thumb-title]]', + className: 'fa fa-address-card-o', + badge: true, + visibility: { + ...defaultVisibility, + reply: false, + }, + }); + } payload.options.push({ - name: 'thumbs', - title: '[[topic:composer.thumb-title]]', - className: 'fa fa-address-card-o', - badge: true, - visibility: { - ...defaultVisibility, - reply: false, - }, + name: 'picture', + title: '[[modules:composer.upload-picture]]', + className: 'fa fa-file-image-o', + visibility: defaultVisibility, + }); + } + + if (canUploadFile) { + payload.options.push({ + name: 'upload', + title: '[[modules:composer.upload-file]]', + className: 'fa fa-file-o', + visibility: defaultVisibility, }); } @@ -135,7 +147,7 @@ plugin.filterComposerBuild = async function (hookData) { ]), user.isAdministrator(req.uid), isModerator(req), - plugin.getFormattingOptions(), + plugin.getFormattingOptions(req.uid), getTagWhitelist(req.query, req.uid), privileges.global.get(req.uid), canTag(req), @@ -161,7 +173,8 @@ plugin.filterComposerBuild = async function (hookData) { const cid = req.query.cid || ''; const topicTitle = topicData && topicData.title ? topicData.title : - validator.escape(String(req.query.title || '')); + String(req.query.title || ''); + return { req: req, res: res, @@ -180,12 +193,6 @@ plugin.filterComposerBuild = async function (hookData) { // can't use title property as that is used for page title topicTitle: topicTitle, titleLength: topicTitle ? topicTitle.length : 0, - titleLabel: translator.compile( - isEditing ? - 'topic:composer.editing-in' : - 'topic:composer.replying-to', - `"${topicTitle}"` - ), topic: topicData, thumb: topicData ? topicData.thumb : '', @@ -231,7 +238,7 @@ async function checkPrivileges(req, res) { function generateDiscardRoute(req, topicData) { if (req.query.cid) { - return `${nconf.get('relative_path')}/category/${validator.escape(String(req.query.cid))}`; + return `${nconf.get('relative_path')}/category/${req.query.cid}`; } else if ((req.query.tid || req.query.pid)) { if (topicData) { return `${nconf.get('relative_path')}/topic/${topicData.slug}`; @@ -242,6 +249,7 @@ function generateDiscardRoute(req, topicData) { async function generateBody(req, postData) { let body; + console.log('generate body', req.query, postData); // Quoted reply if (req.query.toPid && parseInt(req.query.quoted, 10) === 1 && postData) { const username = await user.getUserField(postData.uid, 'username'); @@ -249,11 +257,11 @@ async function generateBody(req, postData) { body = `${translated}\n` + `> ${postData ? `${postData.content.replace(/\n/g, '\n> ')}\n\n` : ''}`; } else if (req.query.body || req.query.content) { - body = validator.escape(String(req.query.body || req.query.content)); + body = req.query.body || req.query.content; } else { body = postData ? postData.content : ''; } - return translator.escape(body); + return body; } async function getPostData(req) { diff --git a/package.json b/package.json index 200814f..5f51727 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,10 @@ }, "readmeFilename": "README.md", "nbbpm": { - "compatibility": "^4.13.0" + "compatibility": "^4.14.0" }, "dependencies": { - "screenfull": "^5.0.2", - "validator": "^13.7.0" + "screenfull": "^5.0.2" }, "devDependencies": { "eslint": "^10.0.0", diff --git a/static/lib/composer.js b/static/lib/composer.js index 8990041..51023d3 100644 --- a/static/lib/composer.js +++ b/static/lib/composer.js @@ -106,7 +106,9 @@ define('composer', [ // Find a match for (const uuid of Object.keys(composer.posts)) { - if (composer.posts[uuid].hasOwnProperty(type) && id === String(composer.posts[uuid][type])) { + const openPost = composer.posts[uuid]; + // make sure dom element exists too + if (openPost.hasOwnProperty(type) && id === String(openPost[type]) && $(`.composer[data-uuid="${uuid}"]`).length) { return uuid; } } @@ -124,7 +126,7 @@ define('composer', [ if (existingUUID) { taskbar.updateActive(existingUUID); if (post.body && !post.fromDraft) { - const postContainer = $('.composer[data-uuid="' + existingUUID + '"]'); + const postContainer = $(`.composer[data-uuid="${existingUUID}"]`); const bodyEl = postContainer.find('textarea'); const prevText = bodyEl.val(); composer.posts[existingUUID].body = (prevText.length ? prevText + '\n\n' : '') + post.body; @@ -203,7 +205,7 @@ define('composer', [ push(pushData); }; - composer.addQuote = function (data) { + composer.addQuote = async function (data) { // tid, toPid, selectedPid, title, username, text, uuid data.uuid = data.uuid || composer.active; @@ -227,13 +229,14 @@ define('composer', [ const quoteKey = useTopicLink ? `> ${translator.compile('modules:composer.user-said-in', data.username, topicLink)}\n>\n` : `> ${translator.compile('modules:composer.user-said', data.username, postHref)}\n>\n`; + const quoteText = await translator.translateKey(quoteKey, config.defaultLang); if (data.uuid === undefined) { composer.newReply({ tid: data.tid, toPid: data.toPid, title: data.title, - body: quoteKey + data.body, + body: quoteText + data.body, }); return; } else if (data.uuid !== composer.active) { @@ -245,16 +248,13 @@ define('composer', [ const bodyEl = postContainer.find('textarea'); const prevText = bodyEl.val(); - translator.translate(quoteKey, config.defaultLang, function (translated) { - composer.posts[data.uuid].body = (prevText.length ? prevText + '\n\n' : '') + translated + data.body; - bodyEl.val(composer.posts[data.uuid].body); - focusElements(postContainer); - preview.render(postContainer); - }); + composer.posts[data.uuid].body = (prevText.length ? prevText + '\n\n' : '') + quoteText + data.body; + bodyEl.val(composer.posts[data.uuid].body); + focusElements(postContainer); + preview.render(postContainer); }; composer.newReply = async function (data) { - const translated = await translator.translate(data.body, config.defaultLang); let pushData = { fromDraft: data.fromDraft, save_id: data.save_id, @@ -262,8 +262,8 @@ define('composer', [ tid: data.tid, toPid: data.toPid, title: data.title, - body: translated, - modified: !!(translated && translated.length), + body: data.body, + modified: !!(data.body && data.body.length), isMain: false, }; ({ pushData } = await hooks.fire('filter:composer.reply.push', { @@ -446,15 +446,10 @@ define('composer', [ const topicTemplate = isTopic && postData.category ? postData.category.topicTemplate : ''; let data = { + action: postData.action, topicTitle: postData.title, titleLength: postData.title.length, - titleLabel: translator.compile( - isEditing ? - 'topic:composer.editing-in' : - 'topic:composer.replying-to', - `"${postData.title}"` - ), - body: utils.escapeHTML(translator.escape(postData.body) || topicTemplate), + body: postData.body || topicTemplate, mobile: composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm', resizable: true, thumb: postData.thumb, @@ -492,7 +487,7 @@ define('composer', [ app.toggleNavbar(false); } - postData.mobile = composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm'; + postData.mobile = data.mobile; ({ postData, createData: data } = await hooks.fire('filter:composer.create', { postData: postData, @@ -505,10 +500,6 @@ define('composer', [ } composerTemplate = $(composerTemplate); - composerTemplate.find('.title, textarea.write').each(function () { - $(this).text(translator.unescape($(this).text())); - }); - composerTemplate.attr('data-uuid', post_uuid); $(document.body).append(composerTemplate); diff --git a/static/lib/composer/drafts.js b/static/lib/composer/drafts.js index f2f3a63..9ad7076 100644 --- a/static/lib/composer/drafts.js +++ b/static/lib/composer/drafts.js @@ -66,14 +66,14 @@ define('composer/drafts', ['api', 'hooks'], function (api, hooks) { } $(window).trigger('action:composer.drafts.get', { save_id: save_id, - draft: draft, + draft: { ...draft }, storage: storage, }); return draft; } catch (e) { console.warn(`[composer/drafts] Could not get draft ${save_id}, removing`); - drafts.removeFromDraftList('available'); - drafts.removeFromDraftList('open'); + drafts.removeFromDraftList('available', save_id); + drafts.removeFromDraftList('open', save_id); return null; } }; @@ -263,8 +263,8 @@ define('composer/drafts', ['api', 'hooks'], function (api, hooks) { fromDraft: true, save_id: draft.save_id, cid: draft.cid, - handle: app.user && app.user.uid ? undefined : utils.escapeHTML(draft.handle), - title: utils.escapeHTML(draft.title), + handle: app.user && app.user.uid ? undefined : draft.handle, + title: draft.title, body: draft.text, tags: String(draft.tags || '').split(','), thumbs: draft.thumbs || [], @@ -288,7 +288,7 @@ define('composer/drafts', ['api', 'hooks'], function (api, hooks) { fromDraft: true, save_id: draft.save_id, pid: draft.pid, - title: draft.title ? utils.escapeHTML(draft.title) : undefined, + title: draft.title || undefined, body: draft.text, thumbs: draft.thumbs || [], }; diff --git a/static/templates/compose.tpl b/static/templates/compose.tpl index 9fb4300..caa18cc 100644 --- a/static/templates/compose.tpl +++ b/static/templates/compose.tpl @@ -1,5 +1,5 @@