From b4f47ca60bdd1ed15d06a5e7b88ea7c6476bceff Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Tue, 9 Jan 2024 13:45:58 -0800 Subject: [PATCH 1/3] Disable mail sending for development --- .env.dist | 2 +- src/mailer.js | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.env.dist b/.env.dist index 00a9cdac2..1852a66af 100644 --- a/.env.dist +++ b/.env.dist @@ -5,7 +5,7 @@ SECRET=changeMeOnProd! STATS_FILE=%projectdir%/stats.json -MAILER_TRANSPORT=smtps://username:password@smtp.example.com/?pool=true +MAILER_TRANSPORT= MAILER_FROM=test@example.com TWITTER_KEY= diff --git a/src/mailer.js b/src/mailer.js index 6321b7afb..4aa0a4510 100644 --- a/src/mailer.js +++ b/src/mailer.js @@ -12,7 +12,11 @@ const loadSuml = name => new Suml().parse(fs.readFileSync(`${__dirname}/../${nam const translations = loadSuml('data/translations'); const fallbackTranslations = loadSuml('locale/_base/translations'); -const transporter = nodemailer.createTransport(process.env.MAILER_TRANSPORT, {from: process.env.MAILER_FROM}); +let transport = process.env.MAILER_TRANSPORT; +if (!transport && process.env.NODE_ENV === 'development') { + transport = {streamTransport: true}; +} +const transporter = nodemailer.createTransport(transport, {from: process.env.MAILER_FROM}); const sendEmail = (to, subject, text = undefined, html = undefined) => { transporter.sendMail({ @@ -20,7 +24,14 @@ const sendEmail = (to, subject, text = undefined, html = undefined) => { subject, text, html, - }, function(err) { if (err) { console.error(err); } }) + }, function(err, info) { + if (process.env.NODE_ENV === 'development' && info.message) { + info.message.pipe(process.stdout); + } + if (err) { + console.error(err); + } + }) }; const findTranslation = (key) => { From 8f8ce79de32cf2a001086f0ac08a0b0c75d5d68d Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Tue, 9 Jan 2024 13:47:13 -0800 Subject: [PATCH 2/3] Always mark new translations as awaiting approval It's useful to have approval as a separate step in the workflow even when the translator is also able to do the approval. --- server/routes/translations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routes/translations.js b/server/routes/translations.js index a0e9b079a..301407c64 100644 --- a/server/routes/translations.js +++ b/server/routes/translations.js @@ -25,7 +25,7 @@ router.post('/translations/propose', handleErrorAsync(async (req, res) => { await req.db.get(SQL`INSERT INTO translations (id, locale, tKey, tValue, status, author_id) VALUES ( ${ulid()}, ${global.config.locale}, ${tKey}, ${JSON.stringify(req.body.changes[tKey])}, - ${req.isGranted('translations') ? TRANSLATION_STATUS.APPROVED : TRANSLATION_STATUS.AWAITING}, ${req.user.id} + ${TRANSLATION_STATUS.AWAITING}, ${req.user.id} )`); } From 4de49a182443bafec10962f6dcdcdeb4c7317df4 Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Tue, 9 Jan 2024 18:34:08 -0800 Subject: [PATCH 3/3] Add unapprove button for translations Meant for misclicks, especially since there's no confirmation on accept. --- routes/adminTranslationsAwaiting.vue | 13 ++++++++++++- server/routes/translations.js | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/routes/adminTranslationsAwaiting.vue b/routes/adminTranslationsAwaiting.vue index cbd420114..d4381fffe 100644 --- a/routes/adminTranslationsAwaiting.vue +++ b/routes/adminTranslationsAwaiting.vue @@ -39,7 +39,10 @@ - Approved + @@ -147,6 +150,14 @@ export default { await this.$post(`/translations/reject-proposal`, {id}) this.translationProposals = this.translationProposals.filter(tp => tp.id !== id); }, + async unapproveTranslationProposal(id) { + await this.$confirm('Do you want to unmark this translation proposal as approved?', 'danger'); + await this.$post(`/translations/unapprove-proposal`, {id}); + this.translationProposals = this.translationProposals.map(tp => { + if (tp.id === id) { tp.status = 0; } + return tp; + }); + }, async markTranslationProposalsDone() { await this.$confirm(`This will mark all approved translations as done and they'll disappear from here – but they'll only actually show up on production if they are added to the translations.suml file diff --git a/server/routes/translations.js b/server/routes/translations.js index 301407c64..6faf3d15e 100644 --- a/server/routes/translations.js +++ b/server/routes/translations.js @@ -102,6 +102,21 @@ router.post('/translations/accept-proposal', handleErrorAsync(async (req, res) = return res.json('OK'); })); +router.post('/translations/unapprove-proposal', handleErrorAsync(async (req, res) => { + if (!req.isGranted('translations')) { + return res.status(401).json({error: 'Unauthorised'}); + } + + await req.db.get(SQL`UPDATE translations SET status = ${TRANSLATION_STATUS.AWAITING} WHERE id = ${req.body.id}`) + + await auditLog(req, 'translations/unapproved', { + locale: global.config.locale, + id: req.body.id, + }); + + return res.json('OK'); +})); + router.post('/translations/proposals-done', handleErrorAsync(async (req, res) => { if (!req.isGranted('translations')) {