From eb719e34a8b42672dfebf59564a0c4c6681bc618 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 21 Apr 2024 14:35:51 +1200 Subject: [PATCH 001/128] Add aggregate query to HB model --- server/homebrew.model.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/homebrew.model.js b/server/homebrew.model.js index 36c9aa192..57e57d9e8 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -60,6 +60,12 @@ HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, f return brews; }; +HomebrewSchema.statics.getAggregate = async function(aggregate, options={}){ + const output = await Homebrew.aggregate(aggregate, options) + .catch((error)=>{throw 'Can not get aggregate';}); + return output; +}; + const Homebrew = mongoose.model('Homebrew', HomebrewSchema); module.exports = { From 770025da0473aadd15bf450b1112e5c8cc50480b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 21 Apr 2024 14:42:20 +1200 Subject: [PATCH 002/128] Add lock count route, update pipelines --- server/admin.api.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 5363ecc08..93c7771a7 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -27,10 +27,12 @@ const mw = { }; const junkBrewPipeline = [ - { $match : { - updatedAt : { $lt: Moment().subtract(30, 'days').toDate() }, - lastViewed : { $lt: Moment().subtract(30, 'days').toDate() } - }}, + { $match : + { + updatedAt : { $lt: Moment().subtract(30, 'days').toDate() }, + lastViewed : { $lt: Moment().subtract(30, 'days').toDate() } + } + }, { $project: { textBinSize: { $binarySize: '$textBin' } } }, { $match: { textBinSize: { $lt: 140 } } }, { $limit: 100 } @@ -138,6 +140,31 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ } }); +router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ + try { + const countLocksPipeline = [ + { + $match : + { + 'lock.locked' : true, + }, + }, + { + $count : + 'count', + } + ]; + const totalLockCount = await HomebrewModel.getAggregate(countLocksPipeline); + const count = totalLockCount[0].count; + return res.json({ + count + }); + } catch (error) { + console.error(error); + return res.status(500).json({ error: 'Unable to get lock count' }); + } +}); + router.get('/admin', mw.adminOnly, (req, res)=>{ templateFn('admin', { url : req.originalUrl From 8c5f2ff61cb12f44d00399f19fdd6e97b024b24e Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 21 Apr 2024 22:17:10 +1200 Subject: [PATCH 003/128] Add route to get locked documents with review requested --- server/admin.api.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index 93c7771a7..b6f72efa1 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -165,6 +165,27 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ } }); +router.get('/admin/lockreviews', mw.adminOnly, async (req, res)=>{ + try { + const countReviewsPipeline = [ + { + $match : + { + 'lock.locked' : true, + 'lock.reviewRequested' : { '$exists': 1 } + }, + } + ]; + const reviewDocuments = await HomebrewModel.getAggregate(countReviewsPipeline); + return res.json({ + reviewDocuments + }); + } catch (error) { + console.error(error); + return res.status(500).json({ error: 'Unable to get lock count' }); + } +}); + router.get('/admin', mw.adminOnly, (req, res)=>{ templateFn('admin', { url : req.originalUrl From b4a7dc0cbd13bf9145e4398ecad89bed08f5c1b6 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 21 Apr 2024 22:48:11 +1200 Subject: [PATCH 004/128] Update lock review route --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index b6f72efa1..552b8a30f 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -165,7 +165,7 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ } }); -router.get('/admin/lockreviews', mw.adminOnly, async (req, res)=>{ +router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ try { const countReviewsPipeline = [ { From 14c7a1152802da1e8eba5570efeb2495cab18be3 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 21 Apr 2024 22:51:53 +1200 Subject: [PATCH 005/128] Fix error message --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 552b8a30f..48a05438d 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -182,7 +182,7 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ }); } catch (error) { console.error(error); - return res.status(500).json({ error: 'Unable to get lock count' }); + return res.status(500).json({ error: 'Unable to get review collection' }); } }); From 874cbe1da43d9da27e732249b92fd2ed2bf32213 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 22 Apr 2024 00:26:34 +1200 Subject: [PATCH 006/128] Don't forget to update the schema! --- server/homebrew.model.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/homebrew.model.js b/server/homebrew.model.js index 57e57d9e8..81f31f1eb 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -26,7 +26,9 @@ const HomebrewSchema = mongoose.Schema({ updatedAt : { type: Date, default: Date.now }, lastViewed : { type: Date, default: Date.now }, views : { type: Number, default: 0 }, - version : { type: Number, default: 1 } + version : { type: Number, default: 1 }, + + lock : { type: Object } }, { versionKey: false }); HomebrewSchema.statics.increaseView = async function(query) { From 8feae7efb622f1e2727bead01e83e7951244339b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 22 Apr 2024 00:32:32 +1200 Subject: [PATCH 007/128] Function request review route --- server/admin.api.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index 48a05438d..846757f23 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -186,6 +186,33 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ } }); +router.get('/admin/lock/requestreview/:id', mw.adminOnly, async (req, res)=>{ + try { + const filter = { + shareId : req.params.id, + 'lock.locked' : true + }; + + const brew = await HomebrewModel.findOne(filter).exec(); + if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} is not locked!` }); }; + + brew.lock.reviewRequested = new Date(); + brew.markModified('lock'); + + console.log(brew); + + await brew.save() + .catch((err)=>{ + return err; + }); + + return res.json(brew); + } catch (error) { + console.error(error); + return res.status(500).json({ error: `Unable to set request for review on brew ID ${req.params.id}` }); + } +}); + router.get('/admin', mw.adminOnly, (req, res)=>{ templateFn('admin', { url : req.originalUrl From 99f5aad94238030207bd0a349bb4ea902aefd822 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 14:32:04 +1200 Subject: [PATCH 008/128] Stop review request if it has already been logged --- server/admin.api.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 846757f23..dfaa6d03f 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -193,19 +193,20 @@ router.get('/admin/lock/requestreview/:id', mw.adminOnly, async (req, res)=>{ 'lock.locked' : true }; - const brew = await HomebrewModel.findOne(filter).exec(); + const brew = await HomebrewModel.findOne(filter); if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} is not locked!` }); }; + if(brew.lock.reviewRequested){ return res.status(500).json({ error: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; + brew.lock.reviewRequested = new Date(); brew.markModified('lock'); - console.log(brew); - await brew.save() .catch((err)=>{ return err; }); + console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); return res.json(brew); } catch (error) { console.error(error); From 71b84e1aba73c38bce5d08348747aa7e7e813ccd Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 14:33:29 +1200 Subject: [PATCH 009/128] Log message when review already requested --- server/admin.api.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index dfaa6d03f..e28b2fff8 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -196,7 +196,10 @@ router.get('/admin/lock/requestreview/:id', mw.adminOnly, async (req, res)=>{ const brew = await HomebrewModel.findOne(filter); if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} is not locked!` }); }; - if(brew.lock.reviewRequested){ return res.status(500).json({ error: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; + if(brew.lock.reviewRequested){ + console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); + return res.status(500).json({ error: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + }; brew.lock.reviewRequested = new Date(); brew.markModified('lock'); From e8b9b3d583b2496bd42a2b547854be4d6bc4faaa Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 15:27:40 +1200 Subject: [PATCH 010/128] Add lock route --- server/admin.api.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index e28b2fff8..cbde256f7 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -165,6 +165,27 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ } }); +router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ + const lock = req.body; + + try { + const filter = { + shareId : req.params.id + }; + + const brew = await HomebrewModel.findOne(filter); + + brew.lock = lock; + brew.markModified('lock'); + + await brew.save(); + + console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); + } catch {} + + return; +}); + router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ try { const countReviewsPipeline = [ From 1c2ae8392c56a79598ec2b7519382ca8cad9c719 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 15:32:26 +1200 Subject: [PATCH 011/128] Increase max lines in Linter --- server/admin.api.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/admin.api.js b/server/admin.api.js index cbde256f7..2034a0e68 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ const HomebrewModel = require('./homebrew.model.js').model; const router = require('express').Router(); const Moment = require('moment'); From e9db7d1bb92014bc14b69209f30f4911ccdab048 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 15:37:50 +1200 Subject: [PATCH 012/128] Add return on success to lock route --- server/admin.api.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 2034a0e68..5a8b19acb 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -182,9 +182,12 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); - } catch {} + } catch (error) { + console.error(error); + return res.status(500).json({ error: `Unable to set lock on brew ${req.params.id}` }); + } - return; + return res.status(200).json({ status: 'SUCCESS', lock }); }); router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ From 4b1d6ebd7c6537ad700b42078772f0595368c186 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 15:58:43 +1200 Subject: [PATCH 013/128] Rename review request route --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 5a8b19acb..620ddebae 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -211,7 +211,7 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ } }); -router.get('/admin/lock/requestreview/:id', mw.adminOnly, async (req, res)=>{ +router.get('/admin/lock/review/request/:id', mw.adminOnly, async (req, res)=>{ try { const filter = { shareId : req.params.id, From 20d48d7dc2d74def10e3e760cedf2f3b544661bd Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Apr 2024 15:59:11 +1200 Subject: [PATCH 014/128] Add review removal route --- server/admin.api.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index 620ddebae..9f72a5ea2 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -242,6 +242,30 @@ router.get('/admin/lock/review/request/:id', mw.adminOnly, async (req, res)=>{ } }); +router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ + try { + const filter = { + shareId : req.params.id, + 'lock.locked' : true, + 'lock.reviewRequested' : { '$exists': 1 } + }; + + const brew = await HomebrewModel.findOne(filter); + if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} does not have a review pending!` }); }; + + delete brew.lock.reviewRequested; + brew.markModified('lock'); + + await brew.save(); + + console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); + return res.json(brew); + } catch (error) { + console.error(error); + return res.status(500).json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); + } +}); + router.get('/admin', mw.adminOnly, (req, res)=>{ templateFn('admin', { url : req.originalUrl From b4ce621630c39e03be9f1737ba83a287d473d470 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 9 May 2024 07:37:37 +1200 Subject: [PATCH 015/128] Request review no longer Admin only --- server/admin.api.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 9f72a5ea2..455258555 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -211,7 +211,9 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ } }); -router.get('/admin/lock/review/request/:id', mw.adminOnly, async (req, res)=>{ +router.get('/admin/lock/review/request/:id', async (req, res)=>{ + // === This route is NOT Admin only === + // Any user can request a review of their document try { const filter = { shareId : req.params.id, From 1fe2a26e8336a03b5730e7b03441e8cd52411296 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 10 May 2024 12:38:31 +1200 Subject: [PATCH 016/128] Tabify Admin page --- client/admin/admin.jsx | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 92e0b2aee..c20b6abed 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -8,11 +8,38 @@ const BrewLookup = require('./brewLookup/brewLookup.jsx'); const BrewCompress = require ('./brewCompress/brewCompress.jsx'); const Stats = require('./stats/stats.jsx'); +const tabGroups = ['brews']; + const Admin = createClass({ getDefaultProps : function() { return {}; }, + getInitialState : function() { + return { + currentTab : 'brews' + }; + }, + + handleClick : function(newTab) { + if(this.state.currentTab === newTab) return; + this.setState({ + currentTab : newTab + }); + }, + + renderBrewTools : function(){ + return <> + +
+ +
+ +
+ + ; + }, + render : function(){ return
@@ -23,13 +50,12 @@ const Admin = createClass({
- -
- -
- -
- +
+ {tabGroups.map((name, idx)=>{ + return ; + })} +
+ {this.state.currentTab == 'brews' && this.renderBrewTools()}
; } From e7dc757293f00d7f78c21b4aad4a3f22d5447051 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 11 May 2024 23:58:37 +1200 Subject: [PATCH 017/128] Initial UI functionality for most Lock functions --- client/admin/admin.jsx | 7 +- client/admin/admin.less | 18 +++ client/admin/lockTools/lockTools.jsx | 176 ++++++++++++++++++++++++++ client/admin/lockTools/lockTools.less | 3 + server/admin.api.js | 48 +++++-- 5 files changed, 240 insertions(+), 12 deletions(-) create mode 100644 client/admin/lockTools/lockTools.jsx create mode 100644 client/admin/lockTools/lockTools.less diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index c20b6abed..088847a66 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -8,7 +8,9 @@ const BrewLookup = require('./brewLookup/brewLookup.jsx'); const BrewCompress = require ('./brewCompress/brewCompress.jsx'); const Stats = require('./stats/stats.jsx'); -const tabGroups = ['brews']; +const LockTools = require('./lockTools/lockTools.jsx'); + +const tabGroups = ['brews', 'locks']; const Admin = createClass({ getDefaultProps : function() { @@ -52,10 +54,11 @@ const Admin = createClass({
{tabGroups.map((name, idx)=>{ - return ; + return ; })}
{this.state.currentTab == 'brews' && this.renderBrewTools()} + {this.state.currentTab == 'locks' && }
; } diff --git a/client/admin/admin.less b/client/admin/admin.less index a61335835..39a66bd54 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -40,5 +40,23 @@ body{ margin : 30px 0px; } + .tabs { + border-bottom: 1px solid black; + } + + button.tab { + margin: 2px 2px 0px; + border-width: 1px 1px 0px; + border-style: solid; + border-color: black; + border-radius: 5px 5px 0px 0px; + color: black; + background-color: transparent; + &.active { + background-color: #000000a0; + color: white; + text-decoration: underline; + } + } } diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx new file mode 100644 index 000000000..710a29681 --- /dev/null +++ b/client/admin/lockTools/lockTools.jsx @@ -0,0 +1,176 @@ +require('./lockTools.less'); +const React = require('react'); +const createClass = require('create-react-class'); + +const request = require('superagent'); + +const LockTools = createClass({ + getInitialState : function() { + return { + fetching : false, + reviewCount : 0 + }; + }, + + componentDidMount : function() { + this.updateReviewCount(); + }, + + updateReviewCount : async function() { + const newCount = await request.get('/admin/lock') + .then((res)=>{return res.body?.count || 'Unknown';}); + if(newCount != this.state.reviewCount){ + this.setState({ + reviewCount : newCount + }); + } + }, + + render : function() { + return
+

Lock Count

+

Number of brews currently locked: {this.state.reviewCount}

+ +
+ +
+

Lock Brew

+ NYI +
+ +
+ +
; + } +}); + +const LockTable = createClass({ + getDefaultProps : function() { + return { + title : '', + fetchURL : '/admin/locks', + resultName : '', + propertyNames : ['shareId'] + }; + }, + + getInitialState : function() { + return { + result : '', + error : '', + searching : false + }; + }, + + clickFn(){ + this.setState({ searching: true, error: null }); + + request.get(this.props.fetchURL) + .then((res)=>this.setState({ result: res.body })) + .catch((err)=>this.setState({ error: err })) + .finally(()=>{ + this.setState({ searching: false }); + }); + }, + + render : function () { + return <> +

{this.props.title}

+ + {this.state.result[this.props.resultName] && + <> +

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

+ + + + {this.props.propertyNames.map((name, idx)=>{ + return ; + })} + + + + {this.state.result[this.props.resultName].map((result, resultIdx)=>{ + return {navigator.clipboard.writeText(result.shareId.toString());}}> + {this.props.propertyNames.map((name, nameIdx)=>{ + return ; + })} + ; + })} + +
{name}
+ {result[name].toString()} +
+ + } + ; + } +}); + +const LockLookup = createClass({ + getDefaultProps : function() { + return { + fetchURL : '/admin/lookup' + }; + }, + + getInitialState : function() { + return { + query : '', + result : '', + error : '', + searching : false + }; + }, + + handleChange(e){ + this.setState({ query: e.target.value }); + }, + + clickFn(){ + this.setState({ searching: true, error: null }); + + request.get(`${this.props.fetchURL}/${this.state.query}`) + .then((res)=>this.setState({ result: res.body })) + .catch((err)=>this.setState({ error: err })) + .finally(()=>{ + this.setState({ searching: false }); + }); + }, + + renderResult : function(){ + return <> +

Result:

+ + + {Object.keys(this.state.result).map((key, idx)=>{ + return + + + ; + })} + +
{key}{this.state.result[key].toString()} +
+ ; + }, + + render : function() { + return
+

{this.props.title}

+ + + + {this.state.error + &&
{this.state.error.toString()}
+ } + + {this.state.result && this.renderResult()} +
; + } +}); + +module.exports = LockTools; \ No newline at end of file diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less new file mode 100644 index 000000000..8f0ec04bd --- /dev/null +++ b/client/admin/lockTools/lockTools.less @@ -0,0 +1,3 @@ +.lockTools { + display: block; +} \ No newline at end of file diff --git a/server/admin.api.js b/server/admin.api.js index 455258555..e1d33f029 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -162,12 +162,13 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ }); } catch (error) { console.error(error); - return res.status(500).json({ error: 'Unable to get lock count' }); + return res.json({ status: 'ERROR', detail: 'Unable to get lock count', error }); } }); router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ const lock = req.body; + lock.applied = new Date; try { const filter = { @@ -176,6 +177,8 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ const brew = await HomebrewModel.findOne(filter); + if(brew.lock) return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); + brew.lock = lock; brew.markModified('lock'); @@ -184,10 +187,34 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); } catch (error) { console.error(error); - return res.status(500).json({ error: `Unable to set lock on brew ${req.params.id}` }); + return res.json({ error, message: `Unable to set lock on brew ${req.params.id}` }); } - return res.status(200).json({ status: 'SUCCESS', lock }); + return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); +}); + +router.get('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ + try { + const filter = { + shareId : req.params.id + }; + + const brew = await HomebrewModel.findOne(filter); + + if(!brew.lock) return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); + + brew.lock = undefined; + brew.markModified('lock'); + + await brew.save(); + + console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); + } catch (error) { + console.error(error); + return res.json({ status: 'ERROR', detail: `Unable to clear lock on brew ${req.params.id}`, error }); + } + + return res.json({ status: 'UNLOCKED', detail: `Lock removed from brew ID ${req.params.id}` }); }); router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ @@ -202,12 +229,13 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ } ]; const reviewDocuments = await HomebrewModel.getAggregate(countReviewsPipeline); + console.log(reviewDocuments); return res.json({ reviewDocuments }); } catch (error) { console.error(error); - return res.status(500).json({ error: 'Unable to get review collection' }); + return res.json({ status: 'ERROR', detail: 'Unable to get review collection', error }); } }); @@ -221,11 +249,11 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} is not locked!` }); }; + if(!brew) { return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); }; if(brew.lock.reviewRequested){ console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.status(500).json({ error: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + return res.json({ status: 'NOT REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; brew.lock.reviewRequested = new Date(); @@ -237,10 +265,10 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ }); console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json(brew); + return res.json({ status: 'REVIEW REQUESTED', brew }); } catch (error) { console.error(error); - return res.status(500).json({ error: `Unable to set request for review on brew ID ${req.params.id}` }); + return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); } }); @@ -253,7 +281,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} does not have a review pending!` }); }; + if(!brew) { return res.json({ status: 'NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; delete brew.lock.reviewRequested; brew.markModified('lock'); @@ -264,7 +292,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ return res.json(brew); } catch (error) { console.error(error); - return res.status(500).json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); + return res.json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); } }); From 2a91d3ddbdaab7387d7c5c07a0eb356173f5edbc Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 16:07:28 +1200 Subject: [PATCH 018/128] Additional styling --- client/admin/lockTools/lockTools.less | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 8f0ec04bd..9554ffa19 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -1,3 +1,13 @@ .lockTools { - display: block; + .lockBrew label { + width: 50%; + display: inline-block; + text-align: right; + line-height: 1.5em; + input { + float: right; + width: 70%; + margin-left: 10px; + } + } } \ No newline at end of file From 2502c0e87ca04e35f01987ffb3a60d969488b383 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 16:08:26 +1200 Subject: [PATCH 019/128] Add missing return status messages --- server/admin.api.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index e1d33f029..eb025a686 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -170,14 +170,18 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ const lock = req.body; lock.applied = new Date; + let brew; try { const filter = { shareId : req.params.id }; - const brew = await HomebrewModel.findOne(filter); + brew = await HomebrewModel.findOne(filter); - if(brew.lock) return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); + if(brew.lock) { + console.log('ALREADY LOCKED'); + return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); + } brew.lock = lock; brew.markModified('lock'); @@ -187,7 +191,7 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); } catch (error) { console.error(error); - return res.json({ error, message: `Unable to set lock on brew ${req.params.id}` }); + return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); } return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); @@ -244,8 +248,8 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ // Any user can request a review of their document try { const filter = { - shareId : req.params.id, - 'lock.locked' : true + shareId : req.params.id, + lock : { $exists: 1 } }; const brew = await HomebrewModel.findOne(filter); @@ -283,7 +287,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ const brew = await HomebrewModel.findOne(filter); if(!brew) { return res.json({ status: 'NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; - delete brew.lock.reviewRequested; + brew.lock.reviewRequested = undefined; brew.markModified('lock'); await brew.save(); From 65f1c19721b9a8f9eeeb930253a17c026a28674b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 16:14:09 +1200 Subject: [PATCH 020/128] Add functional Lock Brew component --- client/admin/lockTools/lockTools.jsx | 75 +++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 710a29681..6e5d7f90e 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -34,8 +34,7 @@ const LockTools = createClass({

-

Lock Brew

- NYI +

@@ -44,6 +43,77 @@ const LockTools = createClass({ } }); +const LockBrew = createClass({ + getInitialState : function() { + return { + brewId : '', + code : 1000, + editMessage : '', + shareMessage : '' + }; + }, + + handleChange : function(e, varName) { + const output = {}; + output[varName] = e.target.value; + this.setState(output); + }, + + submit : function(e){ + e.preventDefault(); + if(!this.state.editMessage) return; + const newLock = { + code : parseInt(this.state.code) || 100, + editMessage : this.state.editMessage, + shareMessage : this.state.shareMessage, + applied : new Date, + locked : true + }; + + request.post(`/admin/lock/${this.state.brewId}`) + .send(newLock) + .set('Content-Type', 'application/json') + .then((response)=>{ + console.log(response.body); + }); + }, + + renderInput : function (name) { + return this.handleChange(e, name)} autoComplete='off' required/>; + }, + + render : function() { + return
+

Lock Brew

+
+ +
+ +
+ +
+ +
+ +
+
; + } +}); + const LockTable = createClass({ getDefaultProps : function() { return { @@ -140,6 +210,7 @@ const LockLookup = createClass({ }, renderResult : function(){ + console.log(this.state.result); return <>

Result:

From b093be52a25321e2f0148c148b8c830371839d20 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 16:57:28 +1200 Subject: [PATCH 021/128] Update esLint rules --- client/admin/lockTools/lockTools.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 6e5d7f90e..9e53e31fd 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./lockTools.less'); const React = require('react'); const createClass = require('create-react-class'); From 7c4721932dcd6c1adea242aa0bf80fca77b5e4a3 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 19:50:27 +1200 Subject: [PATCH 022/128] Remove unnecessary function from HB.model --- server/admin.api.js | 45 ++++++++++++++++++---------------------- server/homebrew.model.js | 6 ------ 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index eb025a686..625e44f22 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -147,16 +147,16 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ { $match : { - 'lock.locked' : true, - }, + lock : { $exists: 1 } + } }, { - $count : - 'count', + $count : 'count' } ]; - const totalLockCount = await HomebrewModel.getAggregate(countLocksPipeline); + const totalLockCount = await HomebrewModel.aggregate(countLocksPipeline); const count = totalLockCount[0].count; + console.log(totalLockCount); return res.json({ count }); @@ -188,10 +188,10 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); - console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); + // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); } catch (error) { console.error(error); - return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); + return res.json({ status: 'ERROR', error, detail: `Unable to set lock on brew ${req.params.id}` }); } return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); @@ -212,7 +212,7 @@ router.get('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); - console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); + // console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); } catch (error) { console.error(error); return res.json({ status: 'ERROR', detail: `Unable to clear lock on brew ${req.params.id}`, error }); @@ -227,13 +227,11 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ { $match : { - 'lock.locked' : true, 'lock.reviewRequested' : { '$exists': 1 } }, } ]; - const reviewDocuments = await HomebrewModel.getAggregate(countReviewsPipeline); - console.log(reviewDocuments); + const reviewDocuments = await HomebrewModel.aggregate(countReviewsPipeline); return res.json({ reviewDocuments }); @@ -256,20 +254,17 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ if(!brew) { return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); }; if(brew.lock.reviewRequested){ - console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'NOT REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'ALREADY REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; brew.lock.reviewRequested = new Date(); brew.markModified('lock'); - await brew.save() - .catch((err)=>{ - return err; - }); + await brew.save(); - console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUESTED', brew }); + // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested for ${brew.shareId} - ${brew.title}` }); } catch (error) { console.error(error); return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); @@ -277,14 +272,14 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ }); router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ + let brew; try { const filter = { shareId : req.params.id, - 'lock.locked' : true, - 'lock.reviewRequested' : { '$exists': 1 } + 'lock.reviewRequested' : { $exists: 1 } }; - const brew = await HomebrewModel.findOne(filter); + brew = await HomebrewModel.findOne(filter); if(!brew) { return res.json({ status: 'NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; brew.lock.reviewRequested = undefined; @@ -292,11 +287,11 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); - console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); - return res.json(brew); + // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Request for Review removed from brew ID ${brew.shareId} - ${brew.title}` }); } catch (error) { console.error(error); - return res.json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); + return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); } }); diff --git a/server/homebrew.model.js b/server/homebrew.model.js index 81f31f1eb..ea32265ff 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -62,12 +62,6 @@ HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, f return brews; }; -HomebrewSchema.statics.getAggregate = async function(aggregate, options={}){ - const output = await Homebrew.aggregate(aggregate, options) - .catch((error)=>{throw 'Can not get aggregate';}); - return output; -}; - const Homebrew = mongoose.model('Homebrew', HomebrewSchema); module.exports = { From c0c674d8626abcc74169e2f4f548327831e73966 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 19:52:08 +1200 Subject: [PATCH 023/128] Comment out debug logging --- server/admin.api.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 625e44f22..8c29791ce 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -147,16 +147,16 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ { $match : { - lock : { $exists: 1 } + lock : true } }, { - $count : 'count' + $count : + 'count' } ]; const totalLockCount = await HomebrewModel.aggregate(countLocksPipeline); const count = totalLockCount[0].count; - console.log(totalLockCount); return res.json({ count }); @@ -179,7 +179,7 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ brew = await HomebrewModel.findOne(filter); if(brew.lock) { - console.log('ALREADY LOCKED'); + // console.log('ALREADY LOCKED'); return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); } @@ -191,7 +191,7 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); } catch (error) { console.error(error); - return res.json({ status: 'ERROR', error, detail: `Unable to set lock on brew ${req.params.id}` }); + return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); } return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); @@ -227,6 +227,7 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ { $match : { + 'lock.locked' : true, 'lock.reviewRequested' : { '$exists': 1 } }, } @@ -255,16 +256,19 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ if(brew.lock.reviewRequested){ // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'ALREADY REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + return res.json({ status: 'NOT REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; brew.lock.reviewRequested = new Date(); brew.markModified('lock'); - await brew.save(); + await brew.save() + .catch((err)=>{ + return err; + }); // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested for ${brew.shareId} - ${brew.title}` }); + return res.json({ status: 'REVIEW REQUESTED', brew }); } catch (error) { console.error(error); return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); @@ -272,14 +276,14 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ }); router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ - let brew; try { const filter = { shareId : req.params.id, - 'lock.reviewRequested' : { $exists: 1 } + 'lock.locked' : true, + 'lock.reviewRequested' : { '$exists': 1 } }; - brew = await HomebrewModel.findOne(filter); + const brew = await HomebrewModel.findOne(filter); if(!brew) { return res.json({ status: 'NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; brew.lock.reviewRequested = undefined; @@ -288,10 +292,10 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Request for Review removed from brew ID ${brew.shareId} - ${brew.title}` }); + return res.json(brew); } catch (error) { console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); + return res.json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); } }); From b237f29636bea20f7486915e0883b62ed8a3da2b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 19:58:29 +1200 Subject: [PATCH 024/128] Clean up API functions --- server/admin.api.js | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 8c29791ce..13d3cb78c 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -155,8 +155,7 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ 'count' } ]; - const totalLockCount = await HomebrewModel.aggregate(countLocksPipeline); - const count = totalLockCount[0].count; + const count = await HomebrewModel.aggregate(countLocksPipeline); return res.json({ count }); @@ -167,16 +166,15 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ }); router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ - const lock = req.body; - lock.applied = new Date; - - let brew; try { + const lock = req.body; + lock.applied = new Date; + const filter = { shareId : req.params.id }; - brew = await HomebrewModel.findOne(filter); + const brew = await HomebrewModel.findOne(filter); if(brew.lock) { // console.log('ALREADY LOCKED'); @@ -189,12 +187,11 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); } catch (error) { console.error(error); return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); } - - return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); }); router.get('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ @@ -256,19 +253,16 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ if(brew.lock.reviewRequested){ // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'NOT REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + return res.json({ status: 'ALREADY REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; brew.lock.reviewRequested = new Date(); brew.markModified('lock'); - await brew.save() - .catch((err)=>{ - return err; - }); + await brew.save(); // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUESTED', brew }); + return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); } catch (error) { console.error(error); return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); @@ -279,12 +273,11 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ try { const filter = { shareId : req.params.id, - 'lock.locked' : true, - 'lock.reviewRequested' : { '$exists': 1 } + 'lock.reviewRequested' : { $exists: 1 } }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.json({ status: 'NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; + if(!brew) { return res.json({ status: 'REVIEW REQUEST NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; brew.lock.reviewRequested = undefined; brew.markModified('lock'); @@ -292,10 +285,10 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); - return res.json(brew); + return res.json({status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); } catch (error) { console.error(error); - return res.json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); + return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); } }); From f5014f29c3973c1442c76d497fa78c4cad01b5ad Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 15 May 2024 23:13:53 +1200 Subject: [PATCH 025/128] Linter fix --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 13d3cb78c..f0714f1ce 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -285,7 +285,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); - return res.json({status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); + return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); } catch (error) { console.error(error); return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); From 96b955f2fe5b885cabb4e73b96b6c03422d6f55d Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 16 May 2024 17:00:26 +1200 Subject: [PATCH 026/128] Change lock check to stub.lock from stub.lock.locked --- server/homebrew.api.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e73a704a8..ef3af9a21 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -54,7 +54,7 @@ const api = { }); stub = stub?.toObject(); - if(stub?.lock?.locked && accessType != 'edit') { + if(stub?.lock && accessType != 'edit') { throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.message, brewId: stub.shareId, brewTitle: stub.title }; } @@ -83,9 +83,9 @@ const api = { if(accessType === 'edit' && (authorsExist && !(isAuthor || isInvited))) { const accessError = { name: 'Access Error', status: 401 }; if(req.account){ - throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId}; + throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId }; } - throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId}; + throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId }; } // If after all of that we still don't have a brew, throw an exception From 4e11a0c737e0ad3332ba7dba6effae7a455d01c3 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 16 May 2024 17:02:00 +1200 Subject: [PATCH 027/128] Fix error message --- server/homebrew.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index ef3af9a21..4baab48ce 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -55,7 +55,7 @@ const api = { stub = stub?.toObject(); if(stub?.lock && accessType != 'edit') { - throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.message, brewId: stub.shareId, brewTitle: stub.title }; + throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title }; } // If there is a google id, try to find the google brew From 7b287fb0f438b5d16e0a670a497a46048bb6f42a Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 16 May 2024 17:04:37 +1200 Subject: [PATCH 028/128] Fix test --- server/homebrew.api.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 8a4748e38..c8539bf63 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -300,7 +300,7 @@ describe('Tests for api', ()=>{ }); it('access is denied to a locked brew', async()=>{ - const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, message: 'brew locked' } }; + const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, shareMessage: 'brew locked' } }; model.get = jest.fn(()=>toBrewPromise(lockBrew)); api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); From d751addf9d8224be02f56d661ef1c7661f6fe344 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 16 May 2024 17:18:50 +1200 Subject: [PATCH 029/128] Change lock static error message --- client/homebrew/pages/errorPage/errors/errorIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index f9d52c109..85df6d286 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -140,7 +140,7 @@ const errorIndex = (props)=>{ '100' : dedent` ## This brew has been locked. - Please contact the Administrators to unlock this document. + Once the author has taken corrective action, they may request an administrative review to have this document unlocked. : From 6314672109a4fdf9e2b16dce32fc08e3c0b7cf5c Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 14:59:08 +1200 Subject: [PATCH 030/128] Pass the entire lock object to LockNotification --- client/homebrew/pages/editPage/editPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 48d0f3fe5..a9652a3ad 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -396,7 +396,7 @@ const EditPage = createClass({ {this.renderNavbar()}
- {this.props.brew.lock && } + {this.props.brew.lock && } Date: Sat, 8 Jun 2024 15:31:46 +1200 Subject: [PATCH 031/128] Shift API call for REQUEST REVIEW to PUT --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index bc4f1fe73..9f25d74c8 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -237,7 +237,7 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ } }); -router.get('/admin/lock/review/request/:id', async (req, res)=>{ +router.put('/admin/lock/review/request/:id', async (req, res)=>{ // === This route is NOT Admin only === // Any user can request a review of their document try { From f81d16309c872cffb4b91310abdc0a3d2e35a16c Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 15:53:01 +1200 Subject: [PATCH 032/128] Add functionality to REQUEST REVIEW button --- .../lockNotification/lockNotification.jsx | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx index c5eeaee47..da79ff265 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx @@ -1,17 +1,28 @@ require('./lockNotification.less'); const React = require('react'); +const request = require('../../../utils/request-middleware.js'); import Dialog from '../../../../components/dialog.jsx'; function LockNotification(props) { props = { shareId : 0, disableLock : ()=>{}, - message : '', + lock : {}, ...props }; - const removeLock = ()=>{ - alert(`Not yet implemented - ID ${props.shareId}`); + const [reviewState, setReviewState] = React.useState(false); + + const removeLock = async ()=>{ + await request.put(`/admin/lock/review/request/${props.shareId}`) + .then(()=>{ + setReviewState(true); + }); + }; + + const renderReviewButton = function(){ + if(reviewState || props.lock.reviewRequested){ return ; }; + return ; }; return @@ -19,11 +30,11 @@ function LockNotification(props) {

This brew been locked by the Administrators. It will not be accessible by any method other than the Editor until the lock is removed.


LOCK REASON

-

{props.message || 'Unable to retrieve Lock Message'}

+

{props.lock.editMessage || 'Unable to retrieve Lock Message'}


Once you have resolved this issue, click REQUEST LOCK REMOVAL to notify the Administrators for review.

Click CONTINUE TO EDITOR to temporarily hide this notification; it will reappear the next time the page is reloaded.

- + {renderReviewButton()}
; }; From 00fd1e415c8ac350f30759f4ecceb57894321d16 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 15:53:28 +1200 Subject: [PATCH 033/128] Change styling for inactive REQUEST REVIEW button --- .../pages/editPage/lockNotification/lockNotification.less | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.less b/client/homebrew/pages/editPage/lockNotification/lockNotification.less index 54f1a9569..de2692ec0 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.less +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.less @@ -15,7 +15,10 @@ color : white; background-color : #333333; - &:hover { background-color : #777777; } + &.inactive, + &:hover { + background-color : #777777; + } } h1, h3 { From bc7731b819b9e7d5e8534e739a007043b00c6372 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 16:08:18 +1200 Subject: [PATCH 034/128] Fix review requested aggregation pipeline --- server/admin.api.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 9f25d74c8..498c1bff3 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -222,7 +222,6 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ { $match : { - 'lock.locked' : true, 'lock.reviewRequested' : { '$exists': 1 } }, } From 240342007b955c269b1e092614963e3429a120bd Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 16:36:20 +1200 Subject: [PATCH 035/128] Fix lock count function --- server/admin.api.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 498c1bff3..8559143c2 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -141,19 +141,13 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ try { - const countLocksPipeline = [ - { - $match : - { - lock : true - } - }, - { - $count : - 'count' - } - ]; - const count = await HomebrewModel.aggregate(countLocksPipeline); + const countLocksQuery = { + lock : { $exists: true } + }; + const count = await HomebrewModel.countDocuments(countLocksQuery) + .then((result)=>{ + return result; + }); return res.json({ count }); From 634b099ade26c18e122c83e9d25a0c024f6883ce Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 16:48:16 +1200 Subject: [PATCH 036/128] Add styling to LockTable --- client/admin/lockTools/lockTools.jsx | 2 +- client/admin/lockTools/lockTools.less | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 9e53e31fd..808f9058e 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -153,7 +153,7 @@ const LockTable = createClass({ {this.state.result[this.props.resultName] && <>

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

-
+
{this.props.propertyNames.map((name, idx)=>{ diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 9554ffa19..765c3cc5d 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -10,4 +10,16 @@ margin-left: 10px; } } + + .lockTable{ + td{ + border: 1px solid #333333; + padding: 4px 10px; + text-align: center; + + &:hover { + cursor: pointer; + } + } + } } \ No newline at end of file From 01ae858a1461a068eedbb10077ec6407190dc5b1 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 17:08:05 +1200 Subject: [PATCH 037/128] Update LockLookup placeholder value --- client/admin/lockTools/lockTools.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 808f9058e..d9448fc14 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -231,7 +231,7 @@ const LockLookup = createClass({ render : function() { return

{this.props.title}

- + From deef5998c54199ade997cb06e0629c6a49262d4a Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 18:55:14 +1200 Subject: [PATCH 038/128] Update LockBrew text and styling --- client/admin/lockTools/lockTools.jsx | 80 ++++++++++++++++++--------- client/admin/lockTools/lockTools.less | 45 ++++++++++++--- 2 files changed, 90 insertions(+), 35 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index d9448fc14..58829eacf 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -85,32 +85,60 @@ const LockBrew = createClass({ render : function() { return
-

Lock Brew

-
- -
- -
- -
- -
- - +
+

Lock Brew

+
+ +
+ +
+ +
+ +
+ + +
+
+

Suggestions

+
+

Codes

+
    +
  • 455 - Generic Lock
  • +
  • 456 - Copyright issues
  • +
  • 457 - Confidential Information Leakage
  • +
  • 458 - Sensitive Personal Information
  • +
  • 459 - Defamation or Libel
  • +
  • 460 - Hate Speech or Discrimination
  • +
  • 461 - Illegal Activities
  • +
  • 462 - Malware or Phishing
  • +
  • 463 - Plagiarism
  • +
  • 465 - Misrepresentation
  • +
  • 466 - Inappropriate Content
  • +
+
+
+

Messages

+
    +
  • Edit Message: This is the private message that is ONLY displayed to the authors of the locked brew. This message MUST specify exactly what actions must be taken in order to have the brew unlocked.
  • +
  • Share Message: This is the public message that is displayed to the EVERYONE that attempts to view the locked brew.
  • +
+
+
; } }); diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 765c3cc5d..c34a69485 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -1,13 +1,40 @@ .lockTools { - .lockBrew label { - width: 50%; - display: inline-block; - text-align: right; - line-height: 1.5em; - input { - float: right; - width: 70%; - margin-left: 10px; + .lockBrew { + columns: 2; + + .lockForm { + break-inside: avoid; + + label { + width: 100%; + display: inline-block; + text-align: right; + line-height: 1.5em; + input { + float: right; + width: 70%; + margin-left: 10px; + } + } + } + + .lockSuggestions { + break-inside: avoid; + columns: 2; + line-height: 1.2em; + h2 { + column-span: all; + } + h3 { + margin-top: 0px; + } + b { + font-weight: 600; + } + + .lockCodes { + break-inside: avoid; + } } } From 7830c7e2eb6bd0fc4e6af5cdac23eee4e5cfc15a Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 19:59:01 +1200 Subject: [PATCH 039/128] Add divs for styling of brewsAwaitingReview --- client/admin/lockTools/lockTools.jsx | 65 +++++++++++++++------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 58829eacf..7f92cdd0e 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -174,35 +174,42 @@ const LockTable = createClass({ render : function () { return <> -

{this.props.title}

- - {this.state.result[this.props.resultName] && - <> -

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

-
- - - {this.props.propertyNames.map((name, idx)=>{ - return ; - })} - - - - {this.state.result[this.props.resultName].map((result, resultIdx)=>{ - return {navigator.clipboard.writeText(result.shareId.toString());}}> - {this.props.propertyNames.map((name, nameIdx)=>{ - return ; - })} - ; - })} - -
{name}
- {result[name].toString()} -
- - } +
+
+

{this.props.title}

+ +
+ {this.state.result[this.props.resultName] && + <> +

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

+
+

Click a row to copy the Share ID to the clipboard

+ + + + {this.props.propertyNames.map((name, idx)=>{ + return ; + })} + + + + {this.state.result[this.props.resultName].map((result, resultIdx)=>{ + return {navigator.clipboard.writeText(result.shareId.toString());}}> + {this.props.propertyNames.map((name, nameIdx)=>{ + return ; + })} + ; + })} + +
{name}
+ {result[name].toString()} +
+ + } +
; } }); From 44f2e3838735868203129b142f93298c79cb3d17 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 20:00:10 +1200 Subject: [PATCH 040/128] Shift unlock and removeReview functions to use PUT --- client/admin/lockTools/lockTools.jsx | 2 +- server/admin.api.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 7f92cdd0e..710d08ff2 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -237,7 +237,7 @@ const LockLookup = createClass({ clickFn(){ this.setState({ searching: true, error: null }); - request.get(`${this.props.fetchURL}/${this.state.query}`) + request.put(`${this.props.fetchURL}/${this.state.query}`) .then((res)=>this.setState({ result: res.body })) .catch((err)=>this.setState({ error: err })) .finally(()=>{ diff --git a/server/admin.api.js b/server/admin.api.js index 8559143c2..d02cdb67b 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -186,7 +186,7 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ } }); -router.get('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ +router.put('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ try { const filter = { shareId : req.params.id @@ -260,7 +260,7 @@ router.put('/admin/lock/review/request/:id', async (req, res)=>{ } }); -router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ +router.put('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ try { const filter = { shareId : req.params.id, From 7d37602d433895c063857df733c0ae2dbdce8e30 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 20:01:53 +1200 Subject: [PATCH 041/128] Remove debugging line --- client/admin/lockTools/lockTools.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 710d08ff2..9a1d1d0ce 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -246,7 +246,6 @@ const LockLookup = createClass({ }, renderResult : function(){ - console.log(this.state.result); return <>

Result:

From 17aa564c57c27174d847676efc86f83efb7e5f24 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 8 Jun 2024 21:05:44 +1200 Subject: [PATCH 042/128] Add styling for result tables --- client/admin/lockTools/lockTools.less | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index c34a69485..272646ce2 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -39,14 +39,14 @@ } .lockTable{ - td{ - border: 1px solid #333333; - padding: 4px 10px; - text-align: center; - - &:hover { - cursor: pointer; - } + td:hover { + cursor: pointer; } } + + td { + border: 1px solid #333333; + padding: 4px 10px; + text-align: center; + } } \ No newline at end of file From 24cf78bc033b565ccde43b78c3837d2c132cbfdf Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 12 Jun 2024 20:44:55 +1200 Subject: [PATCH 043/128] Add lock result --- client/admin/lockTools/lockTools.jsx | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 9a1d1d0ce..bf77be018 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -50,7 +50,8 @@ const LockBrew = createClass({ brewId : '', code : 1000, editMessage : '', - shareMessage : '' + shareMessage : '', + result : {} }; }, @@ -75,7 +76,7 @@ const LockBrew = createClass({ .send(newLock) .set('Content-Type', 'application/json') .then((response)=>{ - console.log(response.body); + this.setState({ result: response.body }); }); }, @@ -83,6 +84,23 @@ const LockBrew = createClass({ return this.handleChange(e, name)} autoComplete='off' required/>; }, + renderResult : function(){ + return <> +

Result:

+
+ + {Object.keys(this.state.result).map((key, idx)=>{ + return + + + ; + })} + +
{key}{this.state.result[key].toString()} +
+ ; + }, + render : function() { return
@@ -112,6 +130,7 @@ const LockBrew = createClass({ + {this.state.result && this.renderResult()}

Suggestions

From c594fc58b35ee76d00770e1a8d885523d29246fa Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 12 Jun 2024 21:47:52 +1200 Subject: [PATCH 044/128] Spread lock object in /lock response --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index d02cdb67b..8f997aed9 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -179,7 +179,7 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ await brew.save(); // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); + return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); } catch (error) { console.error(error); return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); From fc99328459b1aec2df0aa90cb8890a23a112faa4 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 12 Jun 2024 21:48:55 +1200 Subject: [PATCH 045/128] Tweak styling, remove obsolete locked property --- client/admin/lockTools/lockTools.jsx | 10 +++++----- client/admin/lockTools/lockTools.less | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index bf77be018..2135425eb 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -37,9 +37,11 @@ const LockTools = createClass({

- +
+ + +

-
; } }); @@ -68,8 +70,7 @@ const LockBrew = createClass({ code : parseInt(this.state.code) || 100, editMessage : this.state.editMessage, shareMessage : this.state.shareMessage, - applied : new Date, - locked : true + applied : new Date }; request.post(`/admin/lock/${this.state.brewId}`) @@ -204,7 +205,6 @@ const LockTable = createClass({ {this.state.result[this.props.resultName] && <>

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

-

Click a row to copy the Share ID to the clipboard

diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 272646ce2..d78462d35 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -49,4 +49,10 @@ padding: 4px 10px; text-align: center; } + + .brewLookup{ + h2 { + margin-top: 0px; + } + } } \ No newline at end of file From 7e8078767982710bd05285ff2138a0abd60b793d Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 12 Jun 2024 22:18:12 +1200 Subject: [PATCH 046/128] Tweak styling --- client/admin/lockTools/lockTools.less | 1 + 1 file changed, 1 insertion(+) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index d78462d35..9bf2b39ef 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -51,6 +51,7 @@ } .brewLookup{ + min-height: 175px; h2 { margin-top: 0px; } From 0803362a504e5c2c4bacc8d58533cd598e8a54e0 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 12 Jun 2024 22:29:47 +1200 Subject: [PATCH 047/128] More styling tweaks --- client/admin/lockTools/lockTools.less | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 9bf2b39ef..82341eddb 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -44,11 +44,13 @@ } } - td { - border: 1px solid #333333; + th, td { padding: 4px 10px; text-align: center; } + td{ + border: 1px solid #333333; + } .brewLookup{ min-height: 175px; From 7094d43ee5169d725b48e74a64cb8e9a821bbc1a Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 29 Jun 2024 15:29:02 +1200 Subject: [PATCH 048/128] Shift API calls to /api from /admin --- client/admin/lockTools/lockTools.jsx | 22 +++++++++++---------- client/homebrew/utils/request-middleware.js | 3 ++- server/admin.api.js | 10 +++++----- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 2135425eb..31520b88c 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -3,7 +3,8 @@ require('./lockTools.less'); const React = require('react'); const createClass = require('create-react-class'); -const request = require('superagent'); +// const request = require('superagent'); +const request = require('../../homebrew/utils/request-middleware.js'); const LockTools = createClass({ getInitialState : function() { @@ -18,7 +19,7 @@ const LockTools = createClass({ }, updateReviewCount : async function() { - const newCount = await request.get('/admin/lock') + const newCount = await request.get('/api/lock/count') .then((res)=>{return res.body?.count || 'Unknown';}); if(newCount != this.state.reviewCount){ this.setState({ @@ -33,13 +34,13 @@ const LockTools = createClass({

Number of brews currently locked: {this.state.reviewCount}


- +

- - + +

; @@ -48,11 +49,12 @@ const LockTools = createClass({ const LockBrew = createClass({ getInitialState : function() { + // Default values return { brewId : '', - code : 1000, + code : 455, editMessage : '', - shareMessage : '', + shareMessage : 'This Brew has been locked.', result : {} }; }, @@ -73,7 +75,7 @@ const LockBrew = createClass({ applied : new Date }; - request.post(`/admin/lock/${this.state.brewId}`) + request.post(`/api/lock/${this.state.brewId}`) .send(newLock) .set('Content-Type', 'application/json') .then((response)=>{ @@ -167,7 +169,7 @@ const LockTable = createClass({ getDefaultProps : function() { return { title : '', - fetchURL : '/admin/locks', + fetchURL : '/api/locks', resultName : '', propertyNames : ['shareId'] }; @@ -236,7 +238,7 @@ const LockTable = createClass({ const LockLookup = createClass({ getDefaultProps : function() { return { - fetchURL : '/admin/lookup' + fetchURL : '/api/lookup' }; }, diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js index f6bc2571b..80ee58f5d 100644 --- a/client/homebrew/utils/request-middleware.js +++ b/client/homebrew/utils/request-middleware.js @@ -1,6 +1,7 @@ const request = require('superagent'); +const version = require('../../../package.json').version; -const addHeader = (request)=>request.set('Homebrewery-Version', global.version); +const addHeader = (request)=>request.set('Homebrewery-Version', version); const requestMiddleware = { get : (path)=>addHeader(request.get(path)), diff --git a/server/admin.api.js b/server/admin.api.js index 8f997aed9..1bf597bb4 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -139,7 +139,7 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ } }); -router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ +router.get('/api/lock/count', mw.adminOnly, async (req, res)=>{ try { const countLocksQuery = { lock : { $exists: true } @@ -157,7 +157,7 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ } }); -router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ +router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ try { const lock = req.body; lock.applied = new Date; @@ -186,7 +186,7 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ } }); -router.put('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ +router.put('/api/unlock/:id', mw.adminOnly, async (req, res)=>{ try { const filter = { shareId : req.params.id @@ -210,7 +210,7 @@ router.put('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ return res.json({ status: 'UNLOCKED', detail: `Lock removed from brew ID ${req.params.id}` }); }); -router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ +router.get('/api/lock/reviews', mw.adminOnly, async (req, res)=>{ try { const countReviewsPipeline = [ { @@ -260,7 +260,7 @@ router.put('/admin/lock/review/request/:id', async (req, res)=>{ } }); -router.put('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ +router.put('/api/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ try { const filter = { shareId : req.params.id, From b0687493803eda0e8500a8223c2d34ec01b141ec Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 25 Jul 2024 22:11:14 +1200 Subject: [PATCH 049/128] Move copy ID to clipboard to separate button --- client/admin/lockTools/lockTools.jsx | 7 +++++-- client/admin/lockTools/lockTools.less | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 31520b88c..b5c94399a 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -207,23 +207,26 @@ const LockTable = createClass({ {this.state.result[this.props.resultName] && <>

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

-

Click a row to copy the Share ID to the clipboard

{this.props.propertyNames.map((name, idx)=>{ return ; })} + + {this.state.result[this.props.resultName].map((result, resultIdx)=>{ - return {navigator.clipboard.writeText(result.shareId.toString());}}> + return {this.props.propertyNames.map((name, nameIdx)=>{ return ; })} + + ; })} diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 82341eddb..5737a32e0 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -39,8 +39,16 @@ } .lockTable{ - td:hover { + cursor: default; + .row:hover { + background-color: #ccc; + color: #000; + } + .icon { cursor: pointer; + &:hover{ + text-shadow: 0px 0px 6px black; + } } } From 4448410c3ea263e1ac2cc10d9caacbc571645e42 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 1 Nov 2024 14:02:27 -0500 Subject: [PATCH 050/128] Partial implementation --- .circleci/config.yml | 2 +- .../homebrew/editor/snippetbar/snippetbar.jsx | 4 ++ client/homebrew/pages/editPage/editPage.jsx | 2 + package.json | 4 +- server/homebrew.api.js | 6 ++ shared/helpers.js | 60 +++++++++++++++++++ 6 files changed, 75 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f18f84943..abd8faacc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ orbs: jobs: build: docker: - - image: cimg/node:20.17.0 + - image: cimg/node:20.18.0 - image: mongo:4.4 working_directory: ~/homebrewery diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 50237b914..1f786fd93 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -6,6 +6,7 @@ const _ = require('lodash'); const cx = require('classnames'); import { loadHistory } from '../../utils/versionHistory.js'; +import { brewSnippetsToJSON } from '../../../../shared/helpers.js'; //Import all themes const ThemeSnippets = {}; @@ -114,6 +115,9 @@ const Snippetbar = createClass({ oldSnippets = _.keyBy(compiledSnippets, 'groupName'); } + const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title, this.props.brew.snippets, this.props.snippetsBundle); + compiledSnippets.push(userSnippetsasJSON); + return compiledSnippets; }, diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 744e187a6..a98b16717 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -61,6 +61,7 @@ const EditPage = createClass({ currentEditorCursorPageNum : 1, currentBrewRendererPageNum : 1, displayLockMessage : this.props.brew.lock || false, + snippetsBundle : {}, themeBundle : {} }; }, @@ -440,6 +441,7 @@ const EditPage = createClass({ reportError={this.errorReported} renderer={this.state.brew.renderer} userThemes={this.props.userThemes} + snippets={this.props.snippets} snippetBundle={this.state.themeBundle.snippets} updateBrew={this.updateBrew} onCursorPageChange={this.handleEditorCursorPageChange} diff --git a/package.json b/package.json index 94d0122ab..3fe3ce5a6 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "description": "Create authentic looking D&D homebrews using only markdown", "version": "3.16.0", "engines": { - "npm": "^10.2.x", - "node": "^20.17.x" + "npm": "^10.8.x", + "node": "^20.18.x" }, "repository": { "type": "git", diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 213b341ca..2abbf9485 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -170,6 +170,12 @@ const api = { `\`\`\`\n\n` + `${text}`; } + if(brew.snippets !== undefined) { + text = `\`\`\`snippets\n` + + `${brew.snippets || ''}\n` + + `\`\`\`\n\n` + + `${text}`; + } const metadata = _.pick(brew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme']); text = `\`\`\`metadata\n` + `${yaml.dump(metadata)}\n` + diff --git a/shared/helpers.js b/shared/helpers.js index ac684b06f..e75dcdb28 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,6 +1,65 @@ const _ = require('lodash'); const yaml = require('js-yaml'); const request = require('../client/homebrew/utils/request-middleware.js'); +const dedent = require('dedent'); + +// Convert the templates from a brew to a Snippets Structure. +const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ + const textSplit = /^\\page/gm; + const mpAsSnippets = []; + // Snippets from Themes first. + if(themeBundleSnippets) { + for (let themes of themeBundleSnippets) { + const userSnippets = []; + for (let snips of themes.snippets.split(textSplit)) { + const name = snips.split('\n')[0]; + if(name.length != 0) { + userSnippets.push({ + name : name, + icon : '', + gen : snips.split('\n').slice(0), + }); + } + } + if(userSnippets.length > 0) { + mpAsSnippets.push({ + name : themes.name, + icon : '', + gen : '', + subsnippets : userSnippets + }); + } + } + } + // Local Snippets + if(userBrewSnippets) { + const userSnippets = []; + for (let snips of userBrewSnippets.split(textSplit)) { + let name = mp.split('\n')[0]; + if(name.length != 0) { + userSnippets.push({ + name : name, + icon : '', + gen : snips.split('\n').slice(0), + }); + } + } + if(userSnippets.length) { + mpAsSnippets.push({ + name : menuTitle, + icon : '', + subsnippets : userSnippets + }); + } + } + + return { + groupName : 'Brew Snippets', + icon : 'fas fa-th-list', + view : 'text', + snippets : mpAsSnippets + }; +}; const splitTextStyleAndMetadata = (brew)=>{ brew.text = brew.text.replaceAll('\r\n', '\n'); @@ -55,4 +114,5 @@ module.exports = { splitTextStyleAndMetadata, printCurrentBrew, fetchThemeBundle, + brewSnippetsToJSON }; From 7cd82ffc4edaf2ee47b38cb6ca4298957483ed47 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 2 Nov 2024 17:37:43 -0500 Subject: [PATCH 051/128] WOrking snippets insertion from local. --- shared/helpers.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index e75dcdb28..b84cb0332 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -35,12 +35,12 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ if(userBrewSnippets) { const userSnippets = []; for (let snips of userBrewSnippets.split(textSplit)) { - let name = mp.split('\n')[0]; + let name = snips.split('\n')[0]; if(name.length != 0) { userSnippets.push({ - name : name, + name : name.slice('\snippet '.length), icon : '', - gen : snips.split('\n').slice(0), + gen : snips.slice(name.length + 1), }); } } @@ -70,16 +70,19 @@ const splitTextStyleAndMetadata = (brew)=>{ Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])); brew.text = brew.text.slice(index + 5); } - if(brew.text.startsWith('```css')) { - const index = brew.text.indexOf('```\n\n'); - brew.style = brew.text.slice(7, index - 1); - brew.text = brew.text.slice(index + 5); - } if(brew.text.startsWith('```snippets')) { const index = brew.text.indexOf('```\n\n'); brew.snippets = brew.text.slice(11, index - 1); brew.text = brew.text.slice(index + 5); } + if(brew.text.startsWith('```css')) { + const index = brew.text.indexOf('```\n\n'); + brew.style = brew.text.slice(7, index - 1); + brew.text = brew.text.slice(index + 5); + } + // if(!brew?.snippets) { + brew.snippets = `\\snippet Example\nI am an example user snippet\n`; + // } }; const printCurrentBrew = ()=>{ From 4f240bf1100c46fca948b00899d71a2db2bb4205 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 2 Nov 2024 22:30:18 -0500 Subject: [PATCH 052/128] WIP --- client/homebrew/editor/editor.jsx | 2 +- .../homebrew/editor/snippetbar/snippetbar.jsx | 21 ++++++++++++------- server/homebrew.api.js | 4 +--- server/homebrew.api.spec.js | 2 -- shared/helpers.js | 7 +++++-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 9fef72cbb..27737fcba 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -499,7 +499,7 @@ const Editor = createClass({ historySize={this.historySize()} currentEditorTheme={this.state.editorTheme} updateEditorTheme={this.updateEditorTheme} - snippetBundle={this.props.snippetBundle} + themeBundle={this.props.themeBundle} cursorPos={this.codeEditor.current?.getCursorPosition() || {}} updateBrew={this.props.updateBrew} /> diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 1f786fd93..111b4fd38 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -41,7 +41,6 @@ const Snippetbar = createClass({ unfoldCode : ()=>{}, updateEditorTheme : ()=>{}, cursorPos : {}, - snippetBundle : [], updateBrew : ()=>{} }; }, @@ -102,20 +101,26 @@ const Snippetbar = createClass({ }, compileSnippets : function() { + console.log('compileSnippets'); let compiledSnippets = []; let oldSnippets = _.keyBy(compiledSnippets, 'groupName'); - for (let snippets of this.props.snippetBundle) { - if(typeof(snippets) == 'string') // load staticThemes as needed; they were sent as just a file name - snippets = ThemeSnippets[snippets]; + console.log(this.props.themesBundle); - const newSnippets = _.keyBy(_.cloneDeep(snippets), 'groupName'); - compiledSnippets = _.values(_.mergeWith(oldSnippets, newSnippets, this.mergeCustomizer)); + if( this.props.themesBundle) { + for (let snippets of this.props?.themesBundle?.snippets) { + if(typeof(snippets) == 'string') // load staticThemes as needed; they were sent as just a file name + snippets = ThemeSnippets[snippets]; - oldSnippets = _.keyBy(compiledSnippets, 'groupName'); + const newSnippets = _.keyBy(_.cloneDeep(snippets), 'groupName'); + compiledSnippets = _.values(_.mergeWith(oldSnippets, newSnippets, this.mergeCustomizer)); + + oldSnippets = _.keyBy(compiledSnippets, 'groupName'); + } } - const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title, this.props.brew.snippets, this.props.snippetsBundle); + console.log(this.props.themesBundle); + const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title, this.props.brew.snippets, this.props?.themesBundle?.snippets); compiledSnippets.push(userSnippetsasJSON); return compiledSnippets; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 2abbf9485..685415c14 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -296,7 +296,7 @@ const api = { splitTextStyleAndMetadata(currentTheme); // If there is anything in the snippets or style members, append them to the appropriate array - if(currentTheme?.snippets) completeSnippets.push(JSON.parse(currentTheme.snippets)); + if(currentTheme?.snippets) completeSnippets.push({ name: currentTheme.title, snippets: currentTheme.snippets }); if(currentTheme?.style) completeStyles.push(`/* From Brew: ${req.protocol}://${req.get('host')}/share/${req.params.id} */\n\n${currentTheme.style}`); req.params.id = currentTheme.theme; @@ -304,9 +304,7 @@ const api = { } //=== Static Themes ===// else { - const localSnippets = `${req.params.renderer}_${req.params.id}`; // Just log the name for loading on client const localStyle = `@import url(\"/themes/${req.params.renderer}/${req.params.id}/style.css\");`; - completeSnippets.push(localSnippets); completeStyles.push(`/* From Theme ${req.params.id} */\n\n${localStyle}`); req.params.id = Themes[req.params.renderer][req.params.id].baseTheme; diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index a1222cb57..3c86e4208 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -627,8 +627,6 @@ brew`); `/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");` ], snippets : [ - 'V3_Blank', - 'V3_5ePHB' ] }); }); diff --git a/shared/helpers.js b/shared/helpers.js index b84cb0332..d5a4512a6 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -8,9 +8,12 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ const textSplit = /^\\page/gm; const mpAsSnippets = []; // Snippets from Themes first. + //console.log(themeBundleSnippets); if(themeBundleSnippets) { + console.log('Looping!'); for (let themes of themeBundleSnippets) { const userSnippets = []; + console.log(themes); for (let snips of themes.snippets.split(textSplit)) { const name = snips.split('\n')[0]; if(name.length != 0) { @@ -80,9 +83,9 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.style = brew.text.slice(7, index - 1); brew.text = brew.text.slice(index + 5); } - // if(!brew?.snippets) { + if(!brew?.snippets) { brew.snippets = `\\snippet Example\nI am an example user snippet\n`; - // } + } }; const printCurrentBrew = ()=>{ From b9b3d284cf3d89a62b671789047cd7c0ea0defde Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 3 Nov 2024 11:14:31 -0600 Subject: [PATCH 053/128] WOrking snippet editor - menu population regression --- client/homebrew/editor/editor.jsx | 19 +++++++++++- .../homebrew/editor/snippetbar/snippetbar.jsx | 18 +++++++----- .../editor/snippetbar/snippetbar.less | 3 ++ client/homebrew/pages/editPage/editPage.jsx | 17 +++++++++-- server/homebrew.api.js | 2 ++ shared/helpers.js | 29 +++++++++---------- 6 files changed, 61 insertions(+), 27 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 27737fcba..dedfeaebb 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -21,6 +21,7 @@ const DEFAULT_STYLE_TEXT = dedent` color: black; }`; +const DEFAULT_SNIPPET_TEXT = ``; let isJumping = false; const Editor = createClass({ @@ -35,6 +36,7 @@ const Editor = createClass({ onTextChange : ()=>{}, onStyleChange : ()=>{}, onMetaChange : ()=>{}, + onSnipChange : ()=>{}, reportError : ()=>{}, onCursorPageChange : ()=>{}, @@ -51,7 +53,7 @@ const Editor = createClass({ getInitialState : function() { return { editorTheme : this.props.editorTheme, - view : 'text' //'text', 'style', 'meta' + view : 'text' //'text', 'style', 'meta', 'snip' }; }, @@ -61,6 +63,7 @@ const Editor = createClass({ isText : function() {return this.state.view == 'text';}, isStyle : function() {return this.state.view == 'style';}, isMeta : function() {return this.state.view == 'meta';}, + isSnip : function() {return this.state.view == 'snip';}, componentDidMount : function() { @@ -459,6 +462,20 @@ const Editor = createClass({ userThemes={this.props.userThemes}/> ; } + + if(this.isSnip()){ + return <> + + ; + } }, redo : function(){ diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 111b4fd38..bb932c0fe 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -41,6 +41,7 @@ const Snippetbar = createClass({ unfoldCode : ()=>{}, updateEditorTheme : ()=>{}, cursorPos : {}, + themeBundle : [], updateBrew : ()=>{} }; }, @@ -64,7 +65,7 @@ const Snippetbar = createClass({ }, componentDidUpdate : async function(prevProps, prevState) { - if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme || prevProps.snippetBundle != this.props.snippetBundle) { + if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme || prevProps.themeBundle != this.props.themeBundle) { this.setState({ snippets : this.compileSnippets() }); @@ -101,15 +102,12 @@ const Snippetbar = createClass({ }, compileSnippets : function() { - console.log('compileSnippets'); let compiledSnippets = []; let oldSnippets = _.keyBy(compiledSnippets, 'groupName'); - console.log(this.props.themesBundle); - - if( this.props.themesBundle) { - for (let snippets of this.props?.themesBundle?.snippets) { + if(this.props.themeBundle.snippets) { + for (let snippets of this.props.themeBundle.snippets) { if(typeof(snippets) == 'string') // load staticThemes as needed; they were sent as just a file name snippets = ThemeSnippets[snippets]; @@ -119,8 +117,8 @@ const Snippetbar = createClass({ oldSnippets = _.keyBy(compiledSnippets, 'groupName'); } } - console.log(this.props.themesBundle); - const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title, this.props.brew.snippets, this.props?.themesBundle?.snippets); + + const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title, this.props.brew.snippets, this.props.themeBundle.snippets); compiledSnippets.push(userSnippetsasJSON); return compiledSnippets; @@ -266,6 +264,10 @@ const Snippetbar = createClass({ onClick={()=>this.props.onViewChange('meta')}> +
this.props.onViewChange('snip')}> + +
; diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 319cd0cad..a7202c428 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -48,6 +48,9 @@ &.meta { .tooltipLeft('Properties'); } + &.snip { + .tooltipLeft('Snippets'); + } &.undo { .tooltipLeft('Undo'); font-size : 0.75em; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index a98b16717..df840bc48 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -61,7 +61,6 @@ const EditPage = createClass({ currentEditorCursorPageNum : 1, currentBrewRendererPageNum : 1, displayLockMessage : this.props.brew.lock || false, - snippetsBundle : {}, themeBundle : {} }; }, @@ -143,6 +142,19 @@ const EditPage = createClass({ }), ()=>{if(this.state.autoSave) this.trySave();}); }, + handleSnipChange : function(snippet){ + console.log('Snip Change!'); + //If there are errors, run the validator on every change to give quick feedback + let htmlErrors = this.state.htmlErrors; + if(htmlErrors.length) htmlErrors = Markdown.validate(snippet); + + this.setState((prevState)=>({ + brew : { ...prevState.brew, snippets: snippet }, + isPending : true, + htmlErrors : htmlErrors, + }), ()=>{if(this.state.autoSave) this.trySave();}); + }, + handleStyleChange : function(style){ this.setState((prevState)=>({ brew : { ...prevState.brew, style: style }, @@ -438,11 +450,12 @@ const EditPage = createClass({ onTextChange={this.handleTextChange} onStyleChange={this.handleStyleChange} onMetaChange={this.handleMetaChange} + onSnipChange={this.handleSnipChange} reportError={this.errorReported} renderer={this.state.brew.renderer} userThemes={this.props.userThemes} snippets={this.props.snippets} - snippetBundle={this.state.themeBundle.snippets} + themeBundle={this.state.themeBundle} updateBrew={this.updateBrew} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 685415c14..9c4f55ef7 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -304,7 +304,9 @@ const api = { } //=== Static Themes ===// else { + const localSnippets = `${req.params.renderer}_${req.params.id}`; // Just log the name for loading on client const localStyle = `@import url(\"/themes/${req.params.renderer}/${req.params.id}/style.css\");`; + completeSnippets.push(localSnippets); completeStyles.push(`/* From Theme ${req.params.id} */\n\n${localStyle}`); req.params.id = Themes[req.params.renderer][req.params.id].baseTheme; diff --git a/shared/helpers.js b/shared/helpers.js index d5a4512a6..27c372dd7 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -8,29 +8,26 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ const textSplit = /^\\page/gm; const mpAsSnippets = []; // Snippets from Themes first. - //console.log(themeBundleSnippets); if(themeBundleSnippets) { - console.log('Looping!'); for (let themes of themeBundleSnippets) { - const userSnippets = []; - console.log(themes); - for (let snips of themes.snippets.split(textSplit)) { - const name = snips.split('\n')[0]; + if(typeof themes !== 'string') { + const userSnippets = []; + const name = themes.snippets.split('\n')[0]; if(name.length != 0) { userSnippets.push({ - name : name, + name : name.slice('\snippets '.length), icon : '', - gen : snips.split('\n').slice(0), + gen : themes.snippets.slice(name.length + 1), + }); + } + if(userSnippets.length > 0) { + mpAsSnippets.push({ + name : themes.name, + icon : '', + gen : '', + subsnippets : userSnippets }); } - } - if(userSnippets.length > 0) { - mpAsSnippets.push({ - name : themes.name, - icon : '', - gen : '', - subsnippets : userSnippets - }); } } } From 7f7f3557b36e29575e3703137445149d015f49fd Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 3 Nov 2024 12:30:14 -0600 Subject: [PATCH 054/128] Mostly working. Needs tweakages. Presentable --- client/homebrew/editor/editor.jsx | 2 +- .../homebrew/editor/snippetbar/snippetbar.jsx | 5 +++- client/homebrew/pages/editPage/editPage.jsx | 1 - shared/helpers.js | 24 ++++++++++--------- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index dedfeaebb..6c74bfdf7 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -158,7 +158,7 @@ const Editor = createClass({ highlightCustomMarkdown : function(){ if(!this.codeEditor.current) return; - if(this.state.view === 'text') { + if(this.state.view === 'text') { const codeMirror = this.codeEditor.current.codeMirror; codeMirror.operation(()=>{ // Batch CodeMirror styling diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index bb932c0fe..066711c41 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -65,7 +65,10 @@ const Snippetbar = createClass({ }, componentDidUpdate : async function(prevProps, prevState) { - if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme || prevProps.themeBundle != this.props.themeBundle) { + if(prevProps.renderer != this.props.renderer || + prevProps.theme != this.props.theme || + prevProps.themeBundle != this.props.themeBundle || + prevProps.brew.snippets != this.props.brew.snippets) { this.setState({ snippets : this.compileSnippets() }); diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index df840bc48..be5af729b 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -143,7 +143,6 @@ const EditPage = createClass({ }, handleSnipChange : function(snippet){ - console.log('Snip Change!'); //If there are errors, run the validator on every change to give quick feedback let htmlErrors = this.state.htmlErrors; if(htmlErrors.length) htmlErrors = Markdown.validate(snippet); diff --git a/shared/helpers.js b/shared/helpers.js index 27c372dd7..750f533cd 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -5,20 +5,22 @@ const dedent = require('dedent'); // Convert the templates from a brew to a Snippets Structure. const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ - const textSplit = /^\\page/gm; + const textSplit = /^\\snippet /gm; const mpAsSnippets = []; // Snippets from Themes first. if(themeBundleSnippets) { for (let themes of themeBundleSnippets) { if(typeof themes !== 'string') { const userSnippets = []; - const name = themes.snippets.split('\n')[0]; - if(name.length != 0) { - userSnippets.push({ - name : name.slice('\snippets '.length), - icon : '', - gen : themes.snippets.slice(name.length + 1), - }); + for (let snips of themes.snippets.trim().split(textSplit)) { + const name = snips.trim().split('\n')[0]; + if(name.length != 0) { + userSnippets.push({ + name : name.slice('\snippets'.length), + icon : '', + gen : snips.slice(name.length + 1), + }); + } } if(userSnippets.length > 0) { mpAsSnippets.push({ @@ -34,11 +36,11 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ // Local Snippets if(userBrewSnippets) { const userSnippets = []; - for (let snips of userBrewSnippets.split(textSplit)) { + for (let snips of userBrewSnippets.trim().split(textSplit)) { let name = snips.split('\n')[0]; if(name.length != 0) { userSnippets.push({ - name : name.slice('\snippet '.length), + name : name, icon : '', gen : snips.slice(name.length + 1), }); @@ -72,7 +74,7 @@ const splitTextStyleAndMetadata = (brew)=>{ } if(brew.text.startsWith('```snippets')) { const index = brew.text.indexOf('```\n\n'); - brew.snippets = brew.text.slice(11, index - 1); + brew.snippets = brew.text.slice(12, index - 1); brew.text = brew.text.slice(index + 5); } if(brew.text.startsWith('```css')) { From f4e951623348728d243f5097e532b459a8cee638 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 3 Nov 2024 12:41:04 -0600 Subject: [PATCH 055/128] Remove testing helper --- shared/helpers.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 750f533cd..8b94704bd 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -82,9 +82,6 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.style = brew.text.slice(7, index - 1); brew.text = brew.text.slice(index + 5); } - if(!brew?.snippets) { - brew.snippets = `\\snippet Example\nI am an example user snippet\n`; - } }; const printCurrentBrew = ()=>{ From f43a155e6efad0e267ebfb0196d94546923062fc Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 3 Nov 2024 12:52:54 -0600 Subject: [PATCH 056/128] Fix Test --- server/homebrew.api.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 3c86e4208..a1222cb57 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -627,6 +627,8 @@ brew`); `/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");` ], snippets : [ + 'V3_Blank', + 'V3_5ePHB' ] }); }); From e0400c0425be5d00c3541fca64bda5f2ed43bf8f Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 12 Nov 2024 18:41:31 -0600 Subject: [PATCH 057/128] Snippets should go after existing tab sections --- shared/helpers.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 8b94704bd..e603abaf0 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -72,16 +72,16 @@ const splitTextStyleAndMetadata = (brew)=>{ Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])); brew.text = brew.text.slice(index + 5); } - if(brew.text.startsWith('```snippets')) { - const index = brew.text.indexOf('```\n\n'); - brew.snippets = brew.text.slice(12, index - 1); - brew.text = brew.text.slice(index + 5); - } if(brew.text.startsWith('```css')) { const index = brew.text.indexOf('```\n\n'); brew.style = brew.text.slice(7, index - 1); brew.text = brew.text.slice(index + 5); } + if(brew.text.startsWith('```snippets')) { + const index = brew.text.indexOf('```\n\n'); + brew.snippets = brew.text.slice(12, index - 1); + brew.text = brew.text.slice(index + 5); + } }; const printCurrentBrew = ()=>{ From f7561b78247ea98c85a5f65f0452bf578ef0890d Mon Sep 17 00:00:00 2001 From: David Bolack Date: Wed, 13 Nov 2024 20:53:04 -0600 Subject: [PATCH 058/128] Insert a CR --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index df7f41503..5206f4cbf 100644 --- a/README.md +++ b/README.md @@ -144,3 +144,4 @@ your contribution to the project, please join our [gitter chat][gitter-url]. [github-mark-duplicate-url]: https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/about-duplicate-issues-and-pull-requests [github-pr-docs-url]: https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request [gitter-url]: https://gitter.im/naturalcrit/Lobby + From d541a70da54ee4f59f72255518cfe3380e6690ab Mon Sep 17 00:00:00 2001 From: David Bolack Date: Thu, 14 Nov 2024 06:54:36 -0600 Subject: [PATCH 059/128] Remove unneeded dedent --- shared/helpers.js | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/helpers.js b/shared/helpers.js index e603abaf0..f5411f501 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,7 +1,6 @@ const _ = require('lodash'); const yaml = require('js-yaml'); const request = require('../client/homebrew/utils/request-middleware.js'); -const dedent = require('dedent'); // Convert the templates from a brew to a Snippets Structure. const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ From c5935ec262c3c59fd5db441df868655eb0232232 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 23 Nov 2024 01:57:07 -0600 Subject: [PATCH 060/128] Add Snippets to /new --- client/homebrew/pages/homePage/homePage.jsx | 3 ++- client/homebrew/pages/newPage/newPage.jsx | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 00d0c801d..0a598556a 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -108,7 +108,8 @@ const HomePage = createClass({ onTextChange={this.handleTextChange} renderer={this.state.brew.renderer} showEditButtons={false} - snippetBundle={this.state.themeBundle.snippets} + snippets={this.props.snippets} + themeBundle={this.state.themeBundle} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} currentEditorViewPageNum={this.state.currentEditorViewPageNum} diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index ee2c67d5f..ca31e03a5 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -141,6 +141,18 @@ const NewPage = createClass({ localStorage.setItem(STYLEKEY, style); }, + handleSnipChange : function(snippet){ + //If there are errors, run the validator on every change to give quick feedback + let htmlErrors = this.state.htmlErrors; + if(htmlErrors.length) htmlErrors = Markdown.validate(snippet); + + this.setState((prevState)=>({ + brew : { ...prevState.brew, snippets: snippet }, + isPending : true, + htmlErrors : htmlErrors, + }), ()=>{if(this.state.autoSave) this.trySave();}); + }, + handleMetaChange : function(metadata, field=undefined){ if(field == 'theme' || field == 'renderer') // Fetch theme bundle only if theme or renderer was changed fetchThemeBundle(this, metadata.renderer, metadata.theme); @@ -231,9 +243,11 @@ const NewPage = createClass({ onTextChange={this.handleTextChange} onStyleChange={this.handleStyleChange} onMetaChange={this.handleMetaChange} + onSnipChange={this.handleSnipChange} renderer={this.state.brew.renderer} userThemes={this.props.userThemes} - snippetBundle={this.state.themeBundle.snippets} + snippets={this.props.snippets} + themeBundle={this.state.themeBundle} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} currentEditorViewPageNum={this.state.currentEditorViewPageNum} From b6e445c445355d60bc272fd20628fd0d7ab14cc3 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 24 Nov 2024 18:13:32 -0600 Subject: [PATCH 061/128] Convert storage of snippets in Brew to yaml by request. --- server/homebrew.api.js | 16 ++++++----- shared/helpers.js | 64 ++++++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 42d4efca3..b0532e392 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -8,9 +8,11 @@ import Markdown from '../shared/naturalcrit/markdown.js'; import yaml from 'js-yaml'; import asyncHandler from 'express-async-handler'; import { nanoid } from 'nanoid'; -import { splitTextStyleAndMetadata } from '../shared/helpers.js'; +import { splitTextStyleAndMetadata, + brewSnippetsToJSON } from '../shared/helpers.js'; import checkClientVersion from './middleware/check-client-version.js'; + const router = express.Router(); import { DEFAULT_BREW, DEFAULT_BREW_LOAD } from './brewDefaults.js'; @@ -176,15 +178,15 @@ const api = { mergeBrewText : (brew)=>{ let text = brew.text; - if(brew.style !== undefined) { - text = `\`\`\`css\n` + - `${brew.style || ''}\n` + + if(brew.snippets !== undefined) { + text = `\`\`\`snippets\n` + + `${yaml.dump(brewSnippetsToJSON('brew_snippets', brew.snippets, null, false))}` + `\`\`\`\n\n` + `${text}`; } - if(brew.snippets !== undefined) { - text = `\`\`\`snippets\n` + - `${brew.snippets || ''}\n` + + if(brew.style !== undefined) { + text = `\`\`\`css\n` + + `${brew.style || ''}\n` + `\`\`\`\n\n` + `${text}`; } diff --git a/shared/helpers.js b/shared/helpers.js index be3c3331d..b6cbe2df8 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -3,7 +3,7 @@ import yaml from 'js-yaml'; import request from '../client/homebrew/utils/request-middleware.js'; // Convert the templates from a brew to a Snippets Structure. -const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ +const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=null, full=true)=>{ const textSplit = /^\\snippet /gm; const mpAsSnippets = []; // Snippets from Themes first. @@ -17,7 +17,7 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ userSnippets.push({ name : name.slice('\snippets'.length), icon : '', - gen : snips.slice(name.length + 1), + gen : snips.slice(name.length + 1).trim(), }); } } @@ -37,49 +37,71 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets)=>{ const userSnippets = []; for (let snips of userBrewSnippets.trim().split(textSplit)) { let name = snips.split('\n')[0]; + let justSnippet = snips.slice(name.length + 1); + if(justSnippet.slice(-1) === '\n') { + justSnippet = justSnippet.slice(0, -1); + } if(name.length != 0) { - userSnippets.push({ + const subSnip = { name : name, - icon : '', - gen : snips.slice(name.length + 1), - }); + gen : justSnippet, + }; + // if(full) subSnip.icon = ''; + userSnippets.push(subSnip); } } if(userSnippets.length) { mpAsSnippets.push({ name : menuTitle, - icon : '', + // icon : '', subsnippets : userSnippets }); } } - return { - groupName : 'Brew Snippets', - icon : 'fas fa-th-list', - view : 'text', - snippets : mpAsSnippets + const returnObj = { + snippets : mpAsSnippets }; + + if(full) { + returnObj.groupName = 'Brew Snippets'; + returnObj.icon = 'fas fa-th-list'; + returnObj.view = 'text'; + } + + return returnObj; +}; + +const yamlSnippetsToText = (yamlObj)=>{ + if(typeof yamlObj == 'string') return yamlObj; + + let snippetsText = ''; + for (let snippet of yamlObj.snippets) { + for (let subSnippet of snippet.subsnippets) { + snippetsText = `${snippetsText}\\snippet ${subSnippet.name}\n${subSnippet.gen || ''}\n`; + } + } + return snippetsText; }; const splitTextStyleAndMetadata = (brew)=>{ brew.text = brew.text.replaceAll('\r\n', '\n'); if(brew.text.startsWith('```metadata')) { - const index = brew.text.indexOf('```\n\n'); - const metadataSection = brew.text.slice(12, index - 1); + const index = brew.text.indexOf('\n```\n\n'); + const metadataSection = brew.text.slice(11, index - 1); const metadata = yaml.load(metadataSection); Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])); - brew.text = brew.text.slice(index + 5); + brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```css')) { - const index = brew.text.indexOf('```\n\n'); - brew.style = brew.text.slice(7, index - 1); - brew.text = brew.text.slice(index + 5); + const index = brew.text.indexOf('\n```\n\n'); + brew.style = brew.text.slice(6, index - 1); + brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```snippets')) { - const index = brew.text.indexOf('```\n\n'); - brew.snippets = brew.text.slice(12, index - 1); - brew.text = brew.text.slice(index + 5); + const index = brew.text.indexOf('\n```\n\n'); + brew.snippets = yamlSnippetsToText(yaml.load(brew.text.slice(11, index - 1))).slice(0, -1); + brew.text = brew.text.slice(index + 6); } }; From 008b31e530166abbe7d9646af19b7a57747ef5cd Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 24 Nov 2024 18:58:50 -0600 Subject: [PATCH 062/128] Correct failing test. --- server/homebrew.api.spec.js | 2 +- shared/helpers.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 84ffc3052..2da940e40 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -908,7 +908,7 @@ brew`); }); describe('Get CSS', ()=>{ it('should return brew style content as CSS text', async ()=>{ - const testBrew = { title: 'test brew', text: '```css\n\nI Have a style!\n````\n\n' }; + const testBrew = { title: 'test brew', text: '```css\n\nI Have a style!\n```\n\n' }; const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); diff --git a/shared/helpers.js b/shared/helpers.js index b6cbe2df8..9b0b43084 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -88,19 +88,19 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.text = brew.text.replaceAll('\r\n', '\n'); if(brew.text.startsWith('```metadata')) { const index = brew.text.indexOf('\n```\n\n'); - const metadataSection = brew.text.slice(11, index - 1); + const metadataSection = brew.text.slice(11, index + 1); const metadata = yaml.load(metadataSection); Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])); brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```css')) { const index = brew.text.indexOf('\n```\n\n'); - brew.style = brew.text.slice(6, index - 1); + brew.style = brew.text.slice(7, index + 1); brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```snippets')) { const index = brew.text.indexOf('\n```\n\n'); - brew.snippets = yamlSnippetsToText(yaml.load(brew.text.slice(11, index - 1))).slice(0, -1); + brew.snippets = yamlSnippetsToText(yaml.load(brew.text.slice(11, index + 1))).slice(0, -1); brew.text = brew.text.slice(index + 6); } }; From e763ae16313ff03713ea1f42f2c30af004d26929 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 24 Nov 2024 21:57:59 -0600 Subject: [PATCH 063/128] t shouldn't have been saved... --- package-lock.json | 39 ++++++++++++++++++++++++++++++++------- package.json | 2 +- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index b20286889..589e7b661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "lodash": "^4.17.21", "marked": "11.2.0", "marked-emoji": "^1.4.3", - "marked-extended-tables": "^1.0.10", + "marked-extended-tables": "file:../marked-extended-tables", "marked-gfm-heading-id": "^3.2.0", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", @@ -71,6 +71,35 @@ "npm": "^10.8.x" } }, + "../marked-extended-tables": { + "version": "1.0.10", + "license": "MIT", + "devDependencies": { + "@babel/core": "^7.24.6", + "@babel/preset-env": "^7.25.4", + "@rollup/plugin-commonjs": "^26.0.1", + "@rollup/plugin-node-resolve": "^15.2.3", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^12.0.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^10.1.7", + "@semantic-release/npm": "^12.0.1", + "@semantic-release/release-notes-generator": "^13.0.0", + "babel-jest": "^29.5.0", + "eslint": "^8.57.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.1.1", + "jest-cli": "^29.7.0", + "marked": "^14.1.0", + "rollup": "^4.18.0", + "semantic-release": "^24.1.0" + }, + "peerDependencies": { + "marked": ">=3 <15" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -10543,12 +10572,8 @@ } }, "node_modules/marked-extended-tables": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.10.tgz", - "integrity": "sha512-zvRS0GPTkxq8UWawSDecd1Rxd2KD8crrmq2QALGDdrgkcgRNQzHlbnlujBGuXxdgDJg7f6UTv+JpcfejBpKdSg==", - "peerDependencies": { - "marked": ">=3 <15" - } + "resolved": "../marked-extended-tables", + "link": true }, "node_modules/marked-gfm-heading-id": { "version": "3.2.0", diff --git a/package.json b/package.json index 8755d0699..87390b48d 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "lodash": "^4.17.21", "marked": "11.2.0", "marked-emoji": "^1.4.3", - "marked-extended-tables": "^1.0.10", + "marked-extended-tables": "file:../marked-extended-tables", "marked-gfm-heading-id": "^3.2.0", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", From 74b4cb2afde0f97b02aa60781378670651bfe212 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 25 Nov 2024 13:59:24 -0600 Subject: [PATCH 064/128] Revert local error in package.json --- package-lock.json | 40 ++++++++-------------------------------- package.json | 2 +- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 589e7b661..e5f637e46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "lodash": "^4.17.21", "marked": "11.2.0", "marked-emoji": "^1.4.3", - "marked-extended-tables": "file:../marked-extended-tables", + "marked-extended-tables": "^1.0.10", "marked-gfm-heading-id": "^3.2.0", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", @@ -71,35 +71,6 @@ "npm": "^10.8.x" } }, - "../marked-extended-tables": { - "version": "1.0.10", - "license": "MIT", - "devDependencies": { - "@babel/core": "^7.24.6", - "@babel/preset-env": "^7.25.4", - "@rollup/plugin-commonjs": "^26.0.1", - "@rollup/plugin-node-resolve": "^15.2.3", - "@semantic-release/changelog": "^6.0.3", - "@semantic-release/commit-analyzer": "^12.0.0", - "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^10.1.7", - "@semantic-release/npm": "^12.0.1", - "@semantic-release/release-notes-generator": "^13.0.0", - "babel-jest": "^29.5.0", - "eslint": "^8.57.0", - "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.1.1", - "jest-cli": "^29.7.0", - "marked": "^14.1.0", - "rollup": "^4.18.0", - "semantic-release": "^24.1.0" - }, - "peerDependencies": { - "marked": ">=3 <15" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -10572,8 +10543,13 @@ } }, "node_modules/marked-extended-tables": { - "resolved": "../marked-extended-tables", - "link": true + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.10.tgz", + "integrity": "sha512-zvRS0GPTkxq8UWawSDecd1Rxd2KD8crrmq2QALGDdrgkcgRNQzHlbnlujBGuXxdgDJg7f6UTv+JpcfejBpKdSg==", + "license": "MIT", + "peerDependencies": { + "marked": ">=3 <15" + } }, "node_modules/marked-gfm-heading-id": { "version": "3.2.0", diff --git a/package.json b/package.json index 87390b48d..8755d0699 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "lodash": "^4.17.21", "marked": "11.2.0", "marked-emoji": "^1.4.3", - "marked-extended-tables": "file:../marked-extended-tables", + "marked-extended-tables": "^1.0.10", "marked-gfm-heading-id": "^3.2.0", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", From 43222b7651040e981ee09ffc3207b8e3027588ab Mon Sep 17 00:00:00 2001 From: David Bolack Date: Wed, 4 Dec 2024 21:41:04 -0600 Subject: [PATCH 065/128] Fix test --- server/homebrew.api.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index de0d35e40..0af9863aa 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -1005,7 +1005,7 @@ brew`); expect(testBrew.theme).toEqual('5ePHB'); expect(testBrew.lang).toEqual('en'); // Style - expect(testBrew.style).toEqual('style\nstyle\nstyle'); + expect(testBrew.style).toEqual('style\nstyle\nstyle\n'); // Text expect(testBrew.text).toEqual('text\n'); }); From 6e5f071f2273fec3450464c83d349c3e52f758af Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 10 Dec 2024 21:21:41 -0600 Subject: [PATCH 066/128] Move Properties icon to the end of the snippets bar. --- client/homebrew/editor/snippetbar/snippetbar.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index d252c7120..00a826bc1 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -262,14 +262,14 @@ const Snippetbar = createClass({ onClick={()=>this.props.onViewChange('style')}> -
this.props.onViewChange('meta')}> - -
this.props.onViewChange('snip')}>
+
this.props.onViewChange('meta')}> + +
From dae297e0f54aa23333dbb3a9c1e19d3feabae223 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 10 Dec 2024 21:52:38 -0600 Subject: [PATCH 067/128] Partially implement disabled BrewSnippets --- client/homebrew/editor/snippetbar/snippetbar.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 00a826bc1..1809d8f7b 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -217,8 +217,6 @@ const Snippetbar = createClass({ renderEditorButtons : function(){ if(!this.props.showEditButtons) return; - - return (
{this.props.view !== 'meta' && <>
@@ -324,10 +322,11 @@ const SnippetGroup = createClass({ }, render : function(){ + const groupName = `groupName ${this.props.snippets.length === 0 ? 'disabled' : ''}`; return
- {this.props.groupName} + {this.props.groupName}
{this.renderSnippets(this.props.snippets)} From 86856605b94d50eef6aced21447d00d8ecf583aa Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 10 Dec 2024 23:08:51 -0600 Subject: [PATCH 068/128] Add editor highlighting --- client/homebrew/editor/editor.jsx | 22 +++++++++++++++++++--- client/homebrew/editor/editor.less | 8 ++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 95581e5d8..46fcf46c7 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -158,7 +158,7 @@ const Editor = createClass({ highlightCustomMarkdown : function(){ if(!this.codeEditor.current) return; - if(this.state.view === 'text') { + if((this.state.view === 'text') ||(this.state.view === 'snip')) { const codeMirror = this.codeEditor.current.codeMirror; codeMirror.operation(()=>{ // Batch CodeMirror styling @@ -178,8 +178,10 @@ const Editor = createClass({ for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear(); let editorPageCount = 2; // start page count from page 2 + let userSnippetCount = 1; // start snippet count from page 2 - _.forEach(this.props.brew.text.split('\n'), (line, lineNumber)=>{ + const whichSource = this.state.view === 'text' ? this.props.brew.text : this.props.brew.snippets; + _.forEach(whichSource.split('\n'), (line, lineNumber)=>{ //reset custom line styles codeMirror.removeLineClass(lineNumber, 'background', 'pageLine'); @@ -193,7 +195,7 @@ const Editor = createClass({ // Styling for \page breaks if((this.props.renderer == 'legacy' && line.includes('\\page')) || - (this.props.renderer == 'V3' && line.match(/^\\page$/))) { + (this.props.renderer == 'V3' && line.match(/^\\page$/) && this.state.view === 'text')) { // add back the original class 'background' but also add the new class '.pageline' codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); @@ -206,12 +208,26 @@ const Editor = createClass({ editorPageCount += 1; }; + // New Codemirror styling for V3 renderer if(this.props.renderer == 'V3') { if(line.match(/^\\column$/)){ codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); } + // Styling for \snippet breaks + if(this.state.view === 'snip' && line.match(/^\\snippet\ .*$/)) { + + // add back the original class 'background' but also add the new class '.snippetLine' + codeMirror.addLineClass(lineNumber, 'background', 'snippetLine'); + const userSnippetCountElement = Object.assign(document.createElement('span'), { + className : 'editor-snippet-count', + textContent : userSnippetCount + }); + codeMirror.setBookmark({ line: lineNumber, ch: line.length }, userSnippetCountElement); + + userSnippetCount += 1; + }; // definition lists if(line.includes('::')){ if(/^:*$/.test(line) == true){ return; }; diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index b2e96683e..a73b596d7 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -10,10 +10,18 @@ background : #33333328; border-top : #333399 solid 1px; } + .snippetLine { + background : #33333328; + border-top : #333399 solid 1px; + } .editor-page-count { float : right; color : grey; } + .editor-snippet-count { + float : right; + color : grey; + } .columnSplit { font-style : italic; color : grey; From ed099aa061bef4ad783d64a459232beed0c12602 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 20 Dec 2024 21:47:05 -0600 Subject: [PATCH 069/128] Disable the BrewSnippets menu if empty. --- client/homebrew/editor/snippetbar/snippetbar.jsx | 6 +++--- client/homebrew/editor/snippetbar/snippetbar.less | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 1809d8f7b..b1fd02fd2 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -322,11 +322,11 @@ const SnippetGroup = createClass({ }, render : function(){ - const groupName = `groupName ${this.props.snippets.length === 0 ? 'disabled' : ''}`; - return
+ const snippetGroup = `snippetGroup snippetBarButton ${this.props.snippets.length === 0 ? 'disabledSnippets' : ''}`; + return
- {this.props.groupName} + {this.props.groupName}
{this.renderSnippets(this.props.snippets)} diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 33242174b..3478dfde0 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -231,6 +231,13 @@ } } } + .disabledSnippets { + color: grey; + cursor: not-allowed; + + &:hover { background-color: #DDDDDD;} + } + } @container editor (width < 553px) { .snippetBar { From 662f039daa9437317cd128271d2962ecfbd32fb0 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 12 Jan 2025 14:13:27 -0600 Subject: [PATCH 070/128] Merge conflict clearing --- package-lock.json | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 072108b1f..d05e97f62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "classnames": "^2.5.1", "codemirror": "^5.65.6", "cookie-parser": "^1.4.7", - "core-js": "^3.39.0", + "core-js": "^3.40.0", "cors": "^2.8.5", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", @@ -36,17 +36,17 @@ "lodash": "^4.17.21", "marked": "11.2.0", "marked-emoji": "^1.4.3", - "marked-extended-tables": "^1.0.10", + "marked-extended-tables": "^1.1.0", "marked-gfm-heading-id": "^3.2.0", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.9.2", + "mongoose": "^8.9.4", "nanoid": "5.0.9", "nconf": "^0.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-frame-component": "^4.1.3", + "react-frame-component": "^5.2.7", "react-router": "^7.1.1", "sanitize-filename": "1.6.3", "superagent": "^10.1.1", @@ -54,7 +54,7 @@ }, "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.1", - "babel-plugin-transform-import-meta": "^2.2.1", + "babel-plugin-transform-import-meta": "^2.3.2", "eslint": "^9.17.0", "eslint-plugin-jest": "^28.10.0", "eslint-plugin-react": "^7.37.3", @@ -3678,14 +3678,14 @@ } }, "node_modules/babel-plugin-transform-import-meta": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz", - "integrity": "sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.3.2.tgz", + "integrity": "sha512-902o4GiQqI1GqAXfD5rEoz0PJamUfJ3VllpdWaNsFTwdaNjFSFHawvBO+cp5K2j+g2h3bZ4lnM1Xb6yFYGihtA==", "dev": true, "license": "BSD", "dependencies": { - "@babel/template": "^7.4.4", - "tslib": "^2.4.0" + "@babel/template": "^7.25.9", + "tslib": "^2.8.1" }, "peerDependencies": { "@babel/core": "^7.10.0" @@ -4645,9 +4645,9 @@ } }, "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -9868,12 +9868,12 @@ } }, "node_modules/marked-extended-tables": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.10.tgz", - "integrity": "sha512-zvRS0GPTkxq8UWawSDecd1Rxd2KD8crrmq2QALGDdrgkcgRNQzHlbnlujBGuXxdgDJg7f6UTv+JpcfejBpKdSg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.1.0.tgz", + "integrity": "sha512-xPQlnmr/mpIJEjwg9/guSKzxoKu7hyCD/sM59mcZc+nMIh2JuVM2se+kCa1Jo1UH+BEHcNlZ221ziJ/cOxAgCA==", "license": "MIT", "peerDependencies": { - "marked": ">=3 <15" + "marked": ">=3 <16" } }, "node_modules/marked-gfm-heading-id": { @@ -10254,9 +10254,9 @@ } }, "node_modules/mongoose": { - "version": "8.9.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.2.tgz", - "integrity": "sha512-mLWynmZS1v8HTeMxyLhskQncS1SkrjW1eLNuFDYGQMQ/5QrFrxTLNwWXeCRZeKT2lXyaxW8bnJC9AKPT9jYMkw==", + "version": "8.9.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.4.tgz", + "integrity": "sha512-DndoI01aV/q40P9DiYDXsYjhj8vZjmmuFwcC3Tro5wFznoE1z6Fe2JgMnbLR6ghglym5ziYizSfAJykp+UPZWg==", "license": "MIT", "dependencies": { "bson": "^6.10.1", @@ -11675,9 +11675,9 @@ } }, "node_modules/react-frame-component": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/react-frame-component/-/react-frame-component-4.1.3.tgz", - "integrity": "sha512-4PurhctiqnmC1F5prPZ+LdsalH7pZ3SFA5xoc0HBe8mSHctdLLt4Cr2WXfXOoajHBYq/yiipp9zOgx+vy8GiEA==", + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/react-frame-component/-/react-frame-component-5.2.7.tgz", + "integrity": "sha512-ROjHtSLoSVYUBfTieazj/nL8jIX9rZFmHC0yXEU+dx6Y82OcBEGgU9o7VyHMrBFUN9FuQ849MtIPNNLsb4krbg==", "license": "MIT", "peerDependencies": { "prop-types": "^15.5.9", From 004729b2a4f0ad27711a1c60a9ba415c4ad47141 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 28 Jan 2025 19:37:02 -0600 Subject: [PATCH 071/128] Fix editor regression. --- client/homebrew/editor/editor.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 46fcf46c7..2d349252a 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -181,7 +181,7 @@ const Editor = createClass({ let userSnippetCount = 1; // start snippet count from page 2 const whichSource = this.state.view === 'text' ? this.props.brew.text : this.props.brew.snippets; - _.forEach(whichSource.split('\n'), (line, lineNumber)=>{ + _.forEach(whichSource?.split('\n'), (line, lineNumber)=>{ //reset custom line styles codeMirror.removeLineClass(lineNumber, 'background', 'pageLine'); From b605346c7dab4b66696e038139e336466a018f44 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 14 Mar 2025 18:36:18 -0500 Subject: [PATCH 072/128] Fix regeression in snippets --- client/homebrew/pages/editPage/editPage.jsx | 1 - client/homebrew/pages/homePage/homePage.jsx | 1 - client/homebrew/pages/newPage/newPage.jsx | 1 - shared/helpers.js | 4 ++-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 494de6c88..b209e7ef7 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -469,7 +469,6 @@ const EditPage = createClass({ renderer={this.state.brew.renderer} userThemes={this.props.userThemes} themeBundle={this.state.themeBundle} - snippetBundle={this.state.themeBundle.snippets} updateBrew={this.updateBrew} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 0a598556a..45efedf6a 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -108,7 +108,6 @@ const HomePage = createClass({ onTextChange={this.handleTextChange} renderer={this.state.brew.renderer} showEditButtons={false} - snippets={this.props.snippets} themeBundle={this.state.themeBundle} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index cb6ab5c76..6daa881f6 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -246,7 +246,6 @@ const NewPage = createClass({ onSnipChange={this.handleSnipChange} renderer={this.state.brew.renderer} userThemes={this.props.userThemes} - snippets={this.props.snippets} themeBundle={this.state.themeBundle} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} diff --git a/shared/helpers.js b/shared/helpers.js index 1f6ccc936..efe7eb008 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -4,7 +4,7 @@ import request from '../client/homebrew/utils/request-middleware.js'; // Convert the templates from a brew to a Snippets Structure. const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=null, full=true)=>{ - const textSplit = /^\\snippet /gm; + const textSplit = /^\\snippet +/gm; const mpAsSnippets = []; // Snippets from Themes first. if(themeBundleSnippets) { @@ -15,7 +15,7 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=nul const name = snips.trim().split('\n')[0]; if(name.length != 0) { userSnippets.push({ - name : name.slice('\snippets'.length), + name : name, icon : '', gen : snips.slice(name.length + 1).trim(), }); From 99efe7f06bf2406e5ac43267c64c985c5fa47a14 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 14 Mar 2025 19:26:37 -0500 Subject: [PATCH 073/128] Add default value for document name in snippets menu --- client/homebrew/editor/snippetbar/snippetbar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index b1fd02fd2..197461b52 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -121,7 +121,7 @@ const Snippetbar = createClass({ } } - const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title, this.props.brew.snippets, this.props.themeBundle.snippets); + const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title || 'New Document', this.props.brew.snippets, this.props.themeBundle.snippets); compiledSnippets.push(userSnippetsasJSON); return compiledSnippets; From a62588a4c9e495f7281b4178b05a02c30046739c Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 24 Mar 2025 14:58:14 -0500 Subject: [PATCH 074/128] Fix fouled up regex that only worked by accident --- shared/helpers.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index efe7eb008..997d77cec 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -4,20 +4,22 @@ import request from '../client/homebrew/utils/request-middleware.js'; // Convert the templates from a brew to a Snippets Structure. const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=null, full=true)=>{ - const textSplit = /^\\snippet +/gm; + const textSplit = /^(\\snippet +.+\n)/gm; const mpAsSnippets = []; // Snippets from Themes first. if(themeBundleSnippets) { for (let themes of themeBundleSnippets) { if(typeof themes !== 'string') { const userSnippets = []; - for (let snips of themes.snippets.trim().split(textSplit)) { - const name = snips.trim().split('\n')[0]; - if(name.length != 0) { + const snipSplit = themes.snippets.trim().split(textSplit).slice(1); + for (let snips = 0; snips < snipSplit.length; snips+=2) { + if(!snipSplit[snips].startsWith('\\snippet ')) break; + const snippetName = snipSplit[snips].split(/\\snippet +/)[1].split('\n')[0].trim(); + if(snippetName.length != 0) { userSnippets.push({ - name : name, + name : snippetName, icon : '', - gen : snips.slice(name.length + 1).trim(), + gen : snipSplit[snips + 1], }); } } @@ -35,16 +37,14 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=nul // Local Snippets if(userBrewSnippets) { const userSnippets = []; - for (let snips of userBrewSnippets.trim().split(textSplit)) { - let name = snips.split('\n')[0]; - let justSnippet = snips.slice(name.length + 1); - if(justSnippet.slice(-1) === '\n') { - justSnippet = justSnippet.slice(0, -1); - } - if(name.length != 0) { + const snipSplit = userBrewSnippets.trim().split(textSplit).slice(1); + for (let snips = 0; snips < snipSplit.length; snips+=2) { + if(!snipSplit[snips].startsWith('\\snippet ')) break; + const snippetName = snipSplit[snips].split(/\\snippet +/)[1].split('\n')[0].trim(); + if(snippetName.length != 0) { const subSnip = { - name : name, - gen : justSnippet, + name : snippetName, + gen : snipSplit[snips + 1], }; // if(full) subSnip.icon = ''; userSnippets.push(subSnip); From 565d58bb31e77d75742057966a322bdea8ed80b8 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 24 Mar 2025 21:06:55 -0500 Subject: [PATCH 075/128] Add clearing for snippets --- client/homebrew/editor/editor.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index cdad4abb3..7800717d5 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -186,6 +186,7 @@ const Editor = createClass({ //reset custom line styles codeMirror.removeLineClass(lineNumber, 'background', 'pageLine'); + codeMirror.removeLineClass(lineNumber, 'background', 'snippetLine'); codeMirror.removeLineClass(lineNumber, 'text'); codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash'); From ac89f428b2824425a0ee9e3106d7d6127c167ac8 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 27 Mar 2025 18:25:56 +1300 Subject: [PATCH 076/128] Post merge fixes --- client/admin/admin.jsx | 6 +++--- client/admin/lockTools/lockTools.jsx | 4 ++-- client/homebrew/utils/request-middleware.js | 4 ++-- server/admin.api.js | 2 ++ 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 025d78abf..f59162e1d 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -3,9 +3,9 @@ import React, { useEffect, useState } from 'react'; const BrewUtils = require('./brewUtils/brewUtils.jsx'); const NotificationUtils = require('./notificationUtils/notificationUtils.jsx'); import AuthorUtils from './authorUtils/authorUtils.jsx'; -const LockTools = require('./lockTools/lockTools.jsx'); +import LockTools from './lockTools/lockTools.jsx'; -const tabGroups = ['brew', 'notifications', 'authors']; +const tabGroups = ['brew', 'notifications', 'authors', 'locks']; const Admin = ()=>{ const [currentTab, setCurrentTab] = useState('brew'); @@ -41,7 +41,7 @@ const Admin = ()=>{ {currentTab === 'brew' && } {currentTab === 'notifications' && } {currentTab === 'authors' && } - {currentTab === 'locks' && } + {currentTab === 'locks' && }
); diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index b5c94399a..5f8a4ba39 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -4,7 +4,7 @@ const React = require('react'); const createClass = require('create-react-class'); // const request = require('superagent'); -const request = require('../../homebrew/utils/request-middleware.js'); +import request from '../../homebrew/utils/request-middleware.js'; const LockTools = createClass({ getInitialState : function() { @@ -226,7 +226,7 @@ const LockTable = createClass({ ; })}
- + ; })} diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js index 22609dadb..deb08d265 100644 --- a/client/homebrew/utils/request-middleware.js +++ b/client/homebrew/utils/request-middleware.js @@ -1,8 +1,8 @@ -const version = require('../../../package.json').version; +import packageJSON from '../../../package.json' with { type: 'json' }; import request from 'superagent'; -const addHeader = (request)=>request.set('Homebrewery-Version', version); +const addHeader = (request)=>request.set('Homebrewery-Version', packageJSON.version); const requestMiddleware = { get : (path)=>addHeader(request.get(path)), diff --git a/server/admin.api.js b/server/admin.api.js index 3685492d5..96a21c8a7 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -305,6 +305,8 @@ router.put('/api/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ } catch (error) { console.error(error); return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); + }; +}); // ####################### NOTIFICATIONS From 7525e087ff0264f9ae28508b2b3f3619a3528511 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 29 Mar 2025 18:47:03 -0500 Subject: [PATCH 077/128] Regression Fix WIP --- client/homebrew/editor/snippetbar/snippetbar.jsx | 9 +++------ client/homebrew/pages/editPage/editPage.jsx | 1 + client/homebrew/pages/homePage/homePage.jsx | 3 ++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index c8c8fb590..ac3062ecf 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -101,10 +101,11 @@ const Snippetbar = createClass({ if(key == 'snippets') { const result = _.reverse(_.unionBy(_.reverse(newValue), _.reverse(oldValue), 'name')); // Join snippets together, with preference for the child theme over the parent theme return result.filter((snip)=>snip.gen || snip.subsnippets); - } + }; }, compileSnippets : function() { + console.log('compileSnippets'); let compiledSnippets = []; let oldSnippets = _.keyBy(compiledSnippets, 'groupName'); @@ -122,6 +123,7 @@ const Snippetbar = createClass({ } const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title || 'New Document', this.props.brew.snippets, this.props.themeBundle.snippets); + console.log(userSnippetsasJSON); compiledSnippets.push(userSnippetsasJSON); return compiledSnippets; @@ -283,11 +285,6 @@ const Snippetbar = createClass({ module.exports = Snippetbar; - - - - - const SnippetGroup = createClass({ displayName : 'SnippetGroup', getDefaultProps : function() { diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index b209e7ef7..a4d8a1ed4 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -451,6 +451,7 @@ const EditPage = createClass({ }, render : function(){ + console.log(this.state.themeBundle); return
{this.renderNavbar()} diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index b9c1b7371..47cb23d50 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -97,6 +97,7 @@ const HomePage = createClass({ }, render : function(){ + console.log(this.state.themeBundle); return
{this.renderNavbar()} @@ -108,7 +109,7 @@ const HomePage = createClass({ onTextChange={this.handleTextChange} renderer={this.state.brew.renderer} showEditButtons={false} - snippetBundle={this.state.themeBundle.snippets} + themeBundle={this.state.themeBundle} onCursorPageChange={this.handleEditorCursorPageChange} onViewPageChange={this.handleEditorViewPageChange} currentEditorViewPageNum={this.state.currentEditorViewPageNum} From 9d1601f4244813407321ba6bfc24946644e530a6 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 29 Mar 2025 19:14:10 -0500 Subject: [PATCH 078/128] Seems to be working - no idea why... --- client/homebrew/editor/snippetbar/snippetbar.jsx | 2 -- client/homebrew/pages/editPage/editPage.jsx | 1 - client/homebrew/pages/homePage/homePage.jsx | 1 - 3 files changed, 4 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index ac3062ecf..8c6872ab4 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -105,7 +105,6 @@ const Snippetbar = createClass({ }, compileSnippets : function() { - console.log('compileSnippets'); let compiledSnippets = []; let oldSnippets = _.keyBy(compiledSnippets, 'groupName'); @@ -123,7 +122,6 @@ const Snippetbar = createClass({ } const userSnippetsasJSON = brewSnippetsToJSON(this.props.brew.title || 'New Document', this.props.brew.snippets, this.props.themeBundle.snippets); - console.log(userSnippetsasJSON); compiledSnippets.push(userSnippetsasJSON); return compiledSnippets; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index a4d8a1ed4..b209e7ef7 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -451,7 +451,6 @@ const EditPage = createClass({ }, render : function(){ - console.log(this.state.themeBundle); return
{this.renderNavbar()} diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 47cb23d50..d03e30c91 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -97,7 +97,6 @@ const HomePage = createClass({ }, render : function(){ - console.log(this.state.themeBundle); return
{this.renderNavbar()} From 2ce7c6c2be86e89b22e794472e5da344d1234459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 31 Mar 2025 00:14:03 +0200 Subject: [PATCH 079/128] css breakpoint changed --- client/homebrew/editor/snippetbar/snippetbar.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 7a39173e6..5c98ccfda 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -237,7 +237,7 @@ } } -@container editor (width < 553px) { +@container editor (width < 681px) { .snippetBar { .editors { flex : 1; From b9b45632b0ac714ab948268f37ad2facd728c92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 31 Mar 2025 00:19:43 +0200 Subject: [PATCH 080/128] fix package-lock --- package-lock.json | 123 +++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d060d210..450d4e364 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,11 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.9", - "@babel/plugin-transform-runtime": "^7.26.9", + "@babel/core": "^7.26.10", + "@babel/plugin-transform-runtime": "^7.26.10", "@babel/preset-env": "^7.26.9", "@babel/preset-react": "^7.26.3", - "@googleapis/drive": "^8.16.0", + "@googleapis/drive": "^11.0.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", @@ -33,7 +33,7 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "14.0.0", + "marked": "15.0.0", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", @@ -41,21 +41,21 @@ "marked-subsuper-text": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.12.1", - "nanoid": "5.1.3", + "mongoose": "^8.13.0", + "nanoid": "5.1.5", "nconf": "^0.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router": "^7.3.0", + "react-router": "^7.4.0", "sanitize-filename": "1.6.3", - "superagent": "^10.1.1", + "superagent": "^10.2.0", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" }, "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.2", "babel-plugin-transform-import-meta": "^2.3.2", - "eslint": "^9.22.0", + "eslint": "^9.23.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.4", "globals": "^16.0.0", @@ -63,10 +63,10 @@ "jest-expect-message": "^1.1.3", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.16.0", + "stylelint": "^16.17.0", "stylelint-config-recess-order": "^6.0.0", "stylelint-config-recommended": "^15.0.0", - "supertest": "^7.0.0" + "supertest": "^7.1.0" }, "engines": { "node": "^20.18.x", @@ -1863,9 +1863,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", - "integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1886,9 +1886,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", - "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1923,9 +1923,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", - "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", "dev": true, "license": "MIT", "engines": { @@ -1957,9 +1957,9 @@ } }, "node_modules/@googleapis/drive": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.16.0.tgz", - "integrity": "sha512-Xi2mMrUTQ+gsfyouRGd0pfnL+jjg4n4sjKsJruM1y4DknuRfdSBTk5E//WrL0YJ/CqpcBgyd7L8DvaPRtxZD3Q==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-11.0.0.tgz", + "integrity": "sha512-vhkl6/MZ8k5h5XOyenWOD4ys+SNdb8wKYvzU6OBGFx/TzJyHRm4JwgvE8uVDFU6efzNRS0mOiNRfY6nrmHOTtg==", "license": "Apache-2.0", "dependencies": { "googleapis-common": "^7.0.0" @@ -5690,19 +5690,19 @@ "license": "MIT" }, "node_modules/eslint": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", - "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.1.0", + "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "9.22.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -9915,9 +9915,9 @@ } }, "node_modules/marked": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", - "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.0.tgz", + "integrity": "sha512-0mouKmBROJv/WSHJBPZZyYofUgawMChnD5je/g+aOBXsHDjb/IsnTQj7mnhQZu+qPJmRQ0ecX3mLGEUm3BgwYA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -10297,9 +10297,9 @@ } }, "node_modules/mongodb-connection-string-url/node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", + "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -10318,12 +10318,12 @@ } }, "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", - "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "license": "MIT", "dependencies": { - "tr46": "^5.0.0", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { @@ -10331,14 +10331,14 @@ } }, "node_modules/mongoose": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.12.1.tgz", - "integrity": "sha512-UW22y8QFVYmrb36hm8cGncfn4ARc/XsYWQwRTaj0gxtQk1rDuhzDO1eBantS+hTTatfAIS96LlRCJrcNHvW5+Q==", + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.13.1.tgz", + "integrity": "sha512-sRqlXI+6jhr9/KicCOjet1VVPONFsOxTrh14tfueX5y3GJ2ihswc5ewUUojuwdSS/5koGXLIPmGivDSApVXflA==", "license": "MIT", "dependencies": { "bson": "^6.10.3", "kareem": "2.6.3", - "mongodb": "~6.14.0", + "mongodb": "~6.15.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10414,9 +10414,9 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.2.tgz", - "integrity": "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz", + "integrity": "sha512-ifBhQ0rRzHDzqp9jAQP6OwHSH7dbYIQjD3SbJs9YYk9AikKEettW/9s/tbSFDTpXcRbF+u1aLrhHxDFaYtZpFQ==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", @@ -10493,9 +10493,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz", - "integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", + "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", "funding": [ { "type": "github", @@ -11770,9 +11770,9 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.3.0.tgz", - "integrity": "sha512-466f2W7HIWaNXTKM5nHTqNxLrHTyXybm7R0eBlVSt0k/u55tTCDO194OIx/NrYD4TS5SXKTNekXfT37kMKUjgw==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.4.1.tgz", + "integrity": "sha512-Vmizn9ZNzxfh3cumddqv3kLOKvc7AskUT0dC1prTabhiEi0U4A33LmkDOJ79tXaeSqCqMBXBU/ySX88W85+EUg==", "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", @@ -13169,9 +13169,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.16.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.16.0.tgz", - "integrity": "sha512-40X5UOb/0CEFnZVEHyN260HlSSUxPES+arrUphOumGWgXERHfwCD0kNBVILgQSij8iliYVwlc0V7M5bcLP9vPg==", + "version": "16.17.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.17.0.tgz", + "integrity": "sha512-I9OwVIWRMqVm2Br5iTbrfSqGRPWQUlvm6oXO1xZuYYu0Gpduy67N8wXOZv15p6E/JdlZiAtQaIoLKZEWk5hrjw==", "dev": true, "funding": [ { @@ -13428,9 +13428,10 @@ } }, "node_modules/superagent": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.1.1.tgz", - "integrity": "sha512-9pIwrHrOj3uAnqg9gDlW7EA2xv+N5au/dSM0kM22HTqmUu8jBxNT+8uA7tA3UoCnmiqzpSbu8rasIUZvbyamMQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.0.tgz", + "integrity": "sha512-IKeoGox6oG9zyDeizaezkJ2/aK0wc5la9st7WsAKyrAkfJ56W3whVbVtF68k6wuc87/y9T85NyON5FLz7Mrzzw==", + "license": "MIT", "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", @@ -13459,9 +13460,9 @@ } }, "node_modules/supertest": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", - "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz", + "integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==", "dev": true, "license": "MIT", "dependencies": { From de1773361ae0d0f2977e221c0e3a7fe9feb597e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 31 Mar 2025 00:40:00 +0200 Subject: [PATCH 081/128] final css fix --- client/homebrew/editor/snippetbar/snippetbar.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 5c98ccfda..d7c8d3847 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -14,13 +14,13 @@ .snippets { display : flex; justify-content : flex-start; - min-width : 327.58px; + min-width : 432.18px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied } .editors { display : flex; justify-content : flex-end; - min-width : 225px; + min-width : 250px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied &:only-child {min-width : unset; margin-left : auto;} @@ -237,7 +237,7 @@ } } -@container editor (width < 681px) { +@container editor (width < 682px) { .snippetBar { .editors { flex : 1; From f5fc106d0184660fea8a2c13c99d6e1289789963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 31 Mar 2025 00:52:41 +0200 Subject: [PATCH 082/128] pixel frame fix --- client/homebrew/editor/snippetbar/snippetbar.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index d7c8d3847..a0691f8b6 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -237,7 +237,7 @@ } } -@container editor (width < 682px) { +@container editor (width < 683px) { .snippetBar { .editors { flex : 1; From 4eeaa7c650e7becbca773833c0a1efcea0779e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 31 Mar 2025 17:11:25 +0200 Subject: [PATCH 083/128] default text for snippet tab --- client/homebrew/editor/editor.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 7800717d5..411476131 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -22,7 +22,13 @@ const DEFAULT_STYLE_TEXT = dedent` color: black; }`; -const DEFAULT_SNIPPET_TEXT = ``; +const DEFAULT_SNIPPET_TEXT = dedent` + \snippet example snippet + + The text between \`\snippet title\` lines will become a snippet of name \`title\` as this example provides. + + This snippet is accessible in the brew tab, and will be inherited if the brew is used as a theme. +`; let isJumping = false; const Editor = createClass({ From 8e8f520eaaed2b8929b8e7a19905890912ea4399 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 31 Mar 2025 20:50:54 -0500 Subject: [PATCH 084/128] Fix Highlighting issue with new brew sample snippets --- client/homebrew/editor/editor.jsx | 32 +++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 411476131..7112aa4b9 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -13,6 +13,7 @@ const MetadataEditor = require('./metadataEditor/metadataEditor.jsx'); const EDITOR_THEME_KEY = 'HOMEBREWERY-EDITOR-THEME'; const PAGEBREAK_REGEX_V3 = /^(?=\\page(?: *{[^\n{}]*})?$)/m; +const SNIPPETBREAK_REGEX_V3 = /^\\snippet\ .*$/; const SNIPPETBAR_HEIGHT = 25; const DEFAULT_STYLE_TEXT = dedent` /*=======--- Example CSS styling ---=======*/ @@ -155,6 +156,7 @@ const Editor = createClass({ handleViewChange : function(newView){ this.props.setMoveArrows(newView === 'text'); + this.setState({ view : newView }, ()=>{ @@ -190,6 +192,9 @@ const Editor = createClass({ const whichSource = this.state.view === 'text' ? this.props.brew.text : this.props.brew.snippets; _.forEach(whichSource?.split('\n'), (line, lineNumber)=>{ + const tabHighlight = this.state.view === 'text' ? 'pageLine' : 'snippetLine'; + const textOrSnip = this.state.view === 'text'; + //reset custom line styles codeMirror.removeLineClass(lineNumber, 'background', 'pageLine'); codeMirror.removeLineClass(lineNumber, 'background', 'snippetLine'); @@ -203,40 +208,28 @@ const Editor = createClass({ // Styling for \page breaks if((this.props.renderer == 'legacy' && line.includes('\\page')) || - (this.props.renderer == 'V3' && line.match(PAGEBREAK_REGEX_V3))) { + (this.props.renderer == 'V3' && line.match(textOrSnip ? PAGEBREAK_REGEX_V3 : SNIPPETBREAK_REGEX_V3))) { - if(lineNumber > 0) // Since \page is optional on first line of document, + if((lineNumber > 0) && (textOrSnip)) // Since \page is optional on first line of document, editorPageCount += 1; // don't use it to increment page count; stay at 1 + else if(this.state.view !== 'text') userSnippetCount += 1; // add back the original class 'background' but also add the new class '.pageline' - codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); + codeMirror.addLineClass(lineNumber, 'background', tabHighlight); const pageCountElement = Object.assign(document.createElement('span'), { className : 'editor-page-count', - textContent : editorPageCount + textContent : textOrSnip ? editorPageCount : userSnippetCount }); codeMirror.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement); }; // New Codemirror styling for V3 renderer - if(this.props.renderer == 'V3') { + if(this.props.renderer === 'V3') { if(line.match(/^\\column$/)){ codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); } - // Styling for \snippet breaks - if(this.state.view === 'snip' && line.match(/^\\snippet\ .*$/)) { - - // add back the original class 'background' but also add the new class '.snippetLine' - codeMirror.addLineClass(lineNumber, 'background', 'snippetLine'); - const userSnippetCountElement = Object.assign(document.createElement('span'), { - className : 'editor-snippet-count', - textContent : userSnippetCount - }); - codeMirror.setBookmark({ line: lineNumber, ch: line.length }, userSnippetCountElement); - - userSnippetCount += 1; - }; // definition lists if(line.includes('::')){ if(/^:*$/.test(line) == true){ return; }; @@ -490,12 +483,13 @@ const Editor = createClass({ } if(this.isSnip()){ + if(!this.props.brew.snippets) { this.props.brew.snippets = DEFAULT_SNIPPET_TEXT; } return <> Date: Sun, 6 Apr 2025 19:09:11 +1200 Subject: [PATCH 085/128] Shift request calls to import --- .../pages/editPage/lockNotification/lockNotification.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx index da79ff265..ed3802c4c 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx @@ -1,6 +1,6 @@ -require('./lockNotification.less'); -const React = require('react'); -const request = require('../../../utils/request-middleware.js'); +import './lockNotification.less'; +import * as React from 'react'; +import request from '../../../utils/request-middleware.js'; import Dialog from '../../../../components/dialog.jsx'; function LockNotification(props) { From cb060ae8b11525db420053f1cfb964458096917b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 6 Apr 2025 19:26:39 +1200 Subject: [PATCH 086/128] Fix state of "Request Review" button on page load --- client/homebrew/pages/editPage/editPage.jsx | 2 +- .../pages/editPage/lockNotification/lockNotification.jsx | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index dc2520373..856a0e347 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -443,7 +443,7 @@ const EditPage = createClass({ {this.renderNavbar()} - {this.props.brew.lock && } + {this.props.brew.lock && }
{}, - lock : {}, + shareId : 0, + disableLock : ()=>{}, + lock : {}, + reviewRequested : false, ...props }; - const [reviewState, setReviewState] = React.useState(false); + const [reviewState, setReviewState] = React.useState(props.reviewRequested); const removeLock = async ()=>{ await request.put(`/admin/lock/review/request/${props.shareId}`) From 0a4ac7a35a01acd47b545167a1e3dbff40fac137 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 6 Apr 2025 19:48:32 +1200 Subject: [PATCH 087/128] Adjust location of package.json --- server/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index 6bd0c7c9a..fc0954efd 100644 --- a/server/app.js +++ b/server/app.js @@ -2,10 +2,11 @@ // Set working directory to project root import { dirname } from 'path'; import { fileURLToPath } from 'url'; -import packageJSON from './../package.json' with { type: 'json' }; const __dirname = dirname(fileURLToPath(import.meta.url)); process.chdir(`${__dirname}/..`); + +import packageJSON from '../package.json' with { type: 'json' }; const version = packageJSON.version; import _ from 'lodash'; From e2b38829f2ac7db54b551f5019cd63a8f4ed87bf Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 08:25:42 +1200 Subject: [PATCH 088/128] Refactor /api/lock/count --- server/admin.api.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 96a21c8a7..9207daf71 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -165,21 +165,18 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ }); router.get('/api/lock/count', mw.adminOnly, async (req, res)=>{ - try { - const countLocksQuery = { - lock : { $exists: true } - }; - const count = await HomebrewModel.countDocuments(countLocksQuery) - .then((result)=>{ - return result; - }); - return res.json({ - count + + const countLocksQuery = { + lock : { $exists: true } + }; + const count = await HomebrewModel.countDocuments(countLocksQuery) + .catch((error)=>{ + console.error(error); + return res.json({ status: 'ERROR', detail: 'Unable to get lock count', error }); }); - } catch (error) { - console.error(error); - return res.json({ status: 'ERROR', detail: 'Unable to get lock count', error }); - } + + return res.json({ count }); + }); router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ From fa4b2ae0e362895b888337da96a9b6775aa41c38 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 08:27:32 +1200 Subject: [PATCH 089/128] Refactor /api/lock --- server/admin.api.js | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 9207daf71..30396d1db 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -180,32 +180,33 @@ router.get('/api/lock/count', mw.adminOnly, async (req, res)=>{ }); router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ - try { - const lock = req.body; - lock.applied = new Date; - const filter = { - shareId : req.params.id - }; + const lock = req.body; + lock.applied = new Date; - const brew = await HomebrewModel.findOne(filter); + const filter = { + shareId : req.params.id + }; - if(brew.lock) { - // console.log('ALREADY LOCKED'); - return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); - } + const brew = await HomebrewModel.findOne(filter); - brew.lock = lock; - brew.markModified('lock'); - - await brew.save(); - - // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); - } catch (error) { - console.error(error); - return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); + if(brew.lock) { + // console.log('ALREADY LOCKED'); + return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); } + + brew.lock = lock; + brew.markModified('lock'); + + await brew.save() + .catch((error)=>{ + console.error(error); + return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); + }); + + // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); + }); router.put('/api/unlock/:id', mw.adminOnly, async (req, res)=>{ From 3cf98617f5b8ee0f4a1c82f5f8a8b247ba19ed82 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 08:33:39 +1200 Subject: [PATCH 090/128] Refactor /api/lock/reviews --- server/admin.api.js | 53 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 30396d1db..45e8ecc57 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -210,47 +210,48 @@ router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ }); router.put('/api/unlock/:id', mw.adminOnly, async (req, res)=>{ - try { - const filter = { - shareId : req.params.id - }; - const brew = await HomebrewModel.findOne(filter); + const filter = { + shareId : req.params.id + }; - if(!brew.lock) return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); + const brew = await HomebrewModel.findOne(filter); - brew.lock = undefined; - brew.markModified('lock'); + if(!brew.lock) return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); - await brew.save(); + brew.lock = undefined; + brew.markModified('lock'); + + await brew.save() + .catch((error)=>{ + console.error(error); + return res.json({ status: 'ERROR', detail: `Unable to clear lock on brew ${req.params.id}`, error }); + }); + + // console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); - // console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); - } catch (error) { - console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to clear lock on brew ${req.params.id}`, error }); - } return res.json({ status: 'UNLOCKED', detail: `Lock removed from brew ID ${req.params.id}` }); }); router.get('/api/lock/reviews', mw.adminOnly, async (req, res)=>{ - try { - const countReviewsPipeline = [ - { + const countReviewsPipeline = [ + { $match : { 'lock.reviewRequested' : { '$exists': 1 } }, - } - ]; - const reviewDocuments = await HomebrewModel.aggregate(countReviewsPipeline); - return res.json({ - reviewDocuments + } + ]; + const reviewDocuments = await HomebrewModel.aggregate(countReviewsPipeline) + .catch((error)=>{ + console.error(error); + return res.json({ status: 'ERROR', detail: 'Unable to get review collection', error }); }); - } catch (error) { - console.error(error); - return res.json({ status: 'ERROR', detail: 'Unable to get review collection', error }); - } + return res.json({ + reviewDocuments + }); + }); router.put('/admin/lock/review/request/:id', async (req, res)=>{ From 30430cb8cbcf43032df3a14b9c6283f647d9f339 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 08:36:33 +1200 Subject: [PATCH 091/128] Refactor /admin/lock/review/request --- server/admin.api.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 45e8ecc57..dbfe4bc5c 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -257,31 +257,31 @@ router.get('/api/lock/reviews', mw.adminOnly, async (req, res)=>{ router.put('/admin/lock/review/request/:id', async (req, res)=>{ // === This route is NOT Admin only === // Any user can request a review of their document - try { - const filter = { - shareId : req.params.id, - lock : { $exists: 1 } - }; + const filter = { + shareId : req.params.id, + lock : { $exists: 1 } + }; - const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); }; + const brew = await HomebrewModel.findOne(filter); + if(!brew) { return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); }; - if(brew.lock.reviewRequested){ - // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'ALREADY REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); - }; + if(brew.lock.reviewRequested){ + // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'ALREADY REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + }; - brew.lock.reviewRequested = new Date(); - brew.markModified('lock'); + brew.lock.reviewRequested = new Date(); + brew.markModified('lock'); - await brew.save(); + await brew.save() + .catch((error)=>{ + console.error(error); + return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); + }); + + // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); - // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); - } catch (error) { - console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); - } }); router.put('/api/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ From 41bd27b573d25b3dda910b1fca95646eaf271b31 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 08:37:35 +1200 Subject: [PATCH 092/128] Refactor /api/lock/review/remove --- server/admin.api.js | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index dbfe4bc5c..2526a5951 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -285,26 +285,27 @@ router.put('/admin/lock/review/request/:id', async (req, res)=>{ }); router.put('/api/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ - try { - const filter = { - shareId : req.params.id, - 'lock.reviewRequested' : { $exists: 1 } - }; - const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.json({ status: 'REVIEW REQUEST NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; - - brew.lock.reviewRequested = undefined; - brew.markModified('lock'); - - await brew.save(); - - // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); - } catch (error) { - console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); + const filter = { + shareId : req.params.id, + 'lock.reviewRequested' : { $exists: 1 } }; + + const brew = await HomebrewModel.findOne(filter); + if(!brew) { return res.json({ status: 'REVIEW REQUEST NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; + + brew.lock.reviewRequested = undefined; + brew.markModified('lock'); + + await brew.save() + .catch((error)=>{ + console.error(error); + return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); + }); + + // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); + return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); + }); // ####################### NOTIFICATIONS From 0bca3393d4f70cfd8caa080372e31771dd41d362 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 08:39:33 +1200 Subject: [PATCH 093/128] Add LOCK header in comment --- server/admin.api.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index 2526a5951..a2dc76498 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -164,6 +164,8 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ } }); +// ####################### LOCKS + router.get('/api/lock/count', mw.adminOnly, async (req, res)=>{ const countLocksQuery = { From 99c342f19b89c777875c725f2fabd54024e2a479 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 14:38:25 +1200 Subject: [PATCH 094/128] Use throw in Lock API calls --- client/admin/lockTools/lockTools.jsx | 15 ++++-- .../pages/errorPage/errors/errorIndex.js | 22 ++++++++ server/admin.api.js | 52 ++++++++++--------- 3 files changed, 61 insertions(+), 28 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 5f8a4ba39..a1cffce24 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -7,6 +7,7 @@ const createClass = require('create-react-class'); import request from '../../homebrew/utils/request-middleware.js'; const LockTools = createClass({ + displayName : 'LockTools', getInitialState : function() { return { fetching : false, @@ -48,6 +49,7 @@ const LockTools = createClass({ }); const LockBrew = createClass({ + displayName : 'LockBrew', getInitialState : function() { // Default values return { @@ -80,6 +82,9 @@ const LockBrew = createClass({ .set('Content-Type', 'application/json') .then((response)=>{ this.setState({ result: response.body }); + }) + .catch((err)=>{ + this.setState({ result: err.response.body }); }); }, @@ -166,6 +171,7 @@ const LockBrew = createClass({ }); const LockTable = createClass({ + displayName : 'LockTable', getDefaultProps : function() { return { title : '', @@ -188,7 +194,7 @@ const LockTable = createClass({ request.get(this.props.fetchURL) .then((res)=>this.setState({ result: res.body })) - .catch((err)=>this.setState({ error: err })) + .catch((err)=>this.setState({ result: err.response.body })) .finally(()=>{ this.setState({ searching: false }); }); @@ -239,6 +245,7 @@ const LockTable = createClass({ }); const LockLookup = createClass({ + displayName : 'LockLookup', getDefaultProps : function() { return { fetchURL : '/api/lookup' @@ -263,14 +270,14 @@ const LockLookup = createClass({ request.put(`${this.props.fetchURL}/${this.state.query}`) .then((res)=>this.setState({ result: res.body })) - .catch((err)=>this.setState({ error: err })) + .catch((err)=>this.setState({ result: err.response.body })) .finally(()=>{ this.setState({ searching: false }); }); }, renderResult : function(){ - return <> + return

Result:

{name}clipview
{result[name].toString()} {navigator.clipboard.writeText(result.shareId.toString());}}>
{navigator.clipboard.writeText(result.shareId.toString());}}>
@@ -283,7 +290,7 @@ const LockLookup = createClass({ })}
- ; +
; }, render : function() { diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index 9584a14b9..d00aec8d5 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -201,6 +201,28 @@ const errorIndex = (props)=>{ ## Access Denied You need to provide correct administrator credentials to access this page.`, + // ####### Lock Errors + + '60' : dedent`General Lock Error`, + + '61' : dedent`Lock Error: Unable to get lock count`, + + '62' : dedent`Lock Error: Cannot lock`, + + '63' : dedent`Lock Error: Cannot lock - brew not found`, + + '64' : dedent`Lock Error: Cannot lock - already locked`, + + '65' : dedent`Lock Error: Cannot unlock`, + + '66' : dedent`Lock Error: Cannot unlock - brew not found`, + + '67' : dedent`Lock Error: Cannot unlock - not locked`, + + '68' : dedent`Lock Error: Cannot get pending reviews`, + + // ####### Other Errors + '90' : dedent` An unexpected error occurred while looking for these brews. Try again in a few minutes.`, diff --git a/server/admin.api.js b/server/admin.api.js index a2dc76498..12a35cf89 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -166,22 +166,25 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ // ####################### LOCKS -router.get('/api/lock/count', mw.adminOnly, async (req, res)=>{ +router.get('/api/lock/throw', asyncHandler(async ()=>{ + throw { HBErrorCode: '60', code: 500, message: 'Thrown deliberately' }; +})); + +router.get('/api/lock/count', mw.adminOnly, asyncHandler(async (req, res)=>{ const countLocksQuery = { lock : { $exists: true } }; const count = await HomebrewModel.countDocuments(countLocksQuery) .catch((error)=>{ - console.error(error); - return res.json({ status: 'ERROR', detail: 'Unable to get lock count', error }); + throw { name: 'Lock Count Error', message: 'Unable to get lock count', status: 500, HBErrorCode: '61', error }; }); return res.json({ count }); -}); +})); -router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ +router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ const lock = req.body; lock.applied = new Date; @@ -192,9 +195,11 @@ router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ const brew = await HomebrewModel.findOne(filter); + if(!brew) throw { name: 'Brew Not Found', message: 'Cannot find brew to lock', shareId: req.params.id, status: 500, HBErrorCode: '63' }; + if(brew.lock) { // console.log('ALREADY LOCKED'); - return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); + throw { name: 'Already Locked', message: 'Lock already exists on brew', shareId: req.params.id, title: brew.title, status: 500, HBErrorCode: '64' }; } brew.lock = lock; @@ -202,16 +207,15 @@ router.post('/api/lock/:id', mw.adminOnly, async (req, res)=>{ await brew.save() .catch((error)=>{ - console.error(error); - return res.json({ status: 'ERROR', error, message: `Unable to set lock on brew ${req.params.id}` }); + throw { name: 'Already Locked', message: 'Unable to set lock', shareId: req.params.id, status: 500, HBErrorCode: '62', error }; }); // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); + return res.json({ name: 'LOCKED', message: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); -}); +})); -router.put('/api/unlock/:id', mw.adminOnly, async (req, res)=>{ +router.put('/api/unlock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ const filter = { shareId : req.params.id @@ -219,24 +223,25 @@ router.put('/api/unlock/:id', mw.adminOnly, async (req, res)=>{ const brew = await HomebrewModel.findOne(filter); - if(!brew.lock) return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); + if(!brew) throw { name: 'Brew Not Found', message: 'Cannot find brew to unlock', shareId: req.params.id, status: 500, HBErrorCode: '66' }; + + if(!brew.lock) throw { name: 'Not Locked', message: 'Cannot unlock as brew is not locked', shareId: req.params.id, status: 500, HBErrorCode: '67' }; brew.lock = undefined; brew.markModified('lock'); await brew.save() .catch((error)=>{ - console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to clear lock on brew ${req.params.id}`, error }); + throw { name: 'Cannot Unlock', message: 'Unable to clear lock', shareId: req.params.id, status: 500, HBErrorCode: '65', error }; }); // console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'UNLOCKED', detail: `Lock removed from brew ID ${req.params.id}` }); -}); + return res.json({ name: 'Unlocked', message: `Lock removed from brew ID ${req.params.id}` }); +})); -router.get('/api/lock/reviews', mw.adminOnly, async (req, res)=>{ +router.get('/api/lock/reviews', mw.adminOnly, asyncHandler(async (req, res)=>{ const countReviewsPipeline = [ { $match : @@ -247,16 +252,15 @@ router.get('/api/lock/reviews', mw.adminOnly, async (req, res)=>{ ]; const reviewDocuments = await HomebrewModel.aggregate(countReviewsPipeline) .catch((error)=>{ - console.error(error); - return res.json({ status: 'ERROR', detail: 'Unable to get review collection', error }); + throw { name: 'Unable to get reviews', message: 'Unable to get review collection', status: 500, HBErrorCode: '68', error }; }); return res.json({ reviewDocuments }); -}); +})); -router.put('/admin/lock/review/request/:id', async (req, res)=>{ +router.put('/admin/lock/review/request/:id', asyncHandler(async (req, res)=>{ // === This route is NOT Admin only === // Any user can request a review of their document const filter = { @@ -284,9 +288,9 @@ router.put('/admin/lock/review/request/:id', async (req, res)=>{ // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); -}); +})); -router.put('/api/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ +router.put('/api/lock/review/remove/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ const filter = { shareId : req.params.id, @@ -308,7 +312,7 @@ router.put('/api/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); -}); +})); // ####################### NOTIFICATIONS From bd145f17da5b7af3cfd0106e8d38a0e1c2cfb8f8 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 14:38:37 +1200 Subject: [PATCH 095/128] Tweak styling --- client/admin/lockTools/lockTools.less | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 5737a32e0..0b6cb615d 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -39,6 +39,7 @@ } .lockTable{ + break-inside: avoid; cursor: default; .row:hover { background-color: #ccc; @@ -56,11 +57,12 @@ padding: 4px 10px; text-align: center; } - td{ + table, td{ border: 1px solid #333333; } .brewLookup{ + break-inside: avoid; min-height: 175px; h2 { margin-top: 0px; From 1e35e1096fd426197b712b84faec82e9d049eba0 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 14:39:33 +1200 Subject: [PATCH 096/128] Reduce data retrieved for brews with requested reviews --- server/admin.api.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index 12a35cf89..6a3a943d5 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -248,6 +248,12 @@ router.get('/api/lock/reviews', mw.adminOnly, asyncHandler(async (req, res)=>{ { 'lock.reviewRequested' : { '$exists': 1 } }, + }, + { + $project : { + shareId : 1, + title : 1 + } } ]; const reviewDocuments = await HomebrewModel.aggregate(countReviewsPipeline) From 4c4a023f34305073f8677331aca0fe6e475ac7a6 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 15:59:06 +1200 Subject: [PATCH 097/128] Fix lock notification message --- .../pages/editPage/lockNotification/lockNotification.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx index cc77e6204..9c8bb2c47 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx @@ -8,6 +8,7 @@ function LockNotification(props) { shareId : 0, disableLock : ()=>{}, lock : {}, + message : 'Unable to retrieve Lock Message', reviewRequested : false, ...props }; @@ -31,7 +32,7 @@ function LockNotification(props) {

This brew been locked by the Administrators. It will not be accessible by any method other than the Editor until the lock is removed.


LOCK REASON

-

{props.lock.editMessage || 'Unable to retrieve Lock Message'}

+

{props.message}


Once you have resolved this issue, click REQUEST LOCK REMOVAL to notify the Administrators for review.

Click CONTINUE TO EDITOR to temporarily hide this notification; it will reappear the next time the page is reloaded.

From a594d456116dfb069ae9c750605884c072474fc0 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 15:59:34 +1200 Subject: [PATCH 098/128] Remove unnecessary default option --- .../pages/editPage/lockNotification/lockNotification.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx index 9c8bb2c47..0750a599c 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx @@ -23,7 +23,7 @@ function LockNotification(props) { }; const renderReviewButton = function(){ - if(reviewState || props.lock.reviewRequested){ return ; }; + if(reviewState){ return ; }; return ; }; From ef6f022ea3fb6781488c6a8aed311a8ca008aa72 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 15:59:54 +1200 Subject: [PATCH 099/128] Tweak lock notification styling --- .../pages/editPage/lockNotification/lockNotification.less | 1 + 1 file changed, 1 insertion(+) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.less b/client/homebrew/pages/editPage/lockNotification/lockNotification.less index de2692ec0..88133ea98 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.less +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.less @@ -14,6 +14,7 @@ margin : 10px; color : white; background-color : #333333; + padding : 2px 15px; &.inactive, &:hover { From a218b87215305e4828454191859f0eda63796cf7 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 16:32:33 +1200 Subject: [PATCH 100/128] Shift remaining lock API functions to use throw --- .../pages/errorPage/errors/errorIndex.js | 28 +++++++++++++------ server/admin.api.js | 18 ++++++------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index d00aec8d5..2f0d4599b 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -203,23 +203,33 @@ const errorIndex = (props)=>{ // ####### Lock Errors - '60' : dedent`General Lock Error`, + '60' : dedent`Lock Error: General`, - '61' : dedent`Lock Error: Unable to get lock count`, + '61' : dedent`Lock Get Error: Unable to get lock count`, - '62' : dedent`Lock Error: Cannot lock`, + '62' : dedent`Lock Set Error: Cannot lock`, - '63' : dedent`Lock Error: Cannot lock - brew not found`, + '63' : dedent`Lock Set Error: Brew not found`, - '64' : dedent`Lock Error: Cannot lock - already locked`, + '64' : dedent`Lock Set Error: Already locked`, - '65' : dedent`Lock Error: Cannot unlock`, + '65' : dedent`Lock Remove Error: Cannot unlock`, - '66' : dedent`Lock Error: Cannot unlock - brew not found`, + '66' : dedent`Lock Remove Error: Brew not found`, - '67' : dedent`Lock Error: Cannot unlock - not locked`, + '67' : dedent`Lock Remove Error: Not locked`, - '68' : dedent`Lock Error: Cannot get pending reviews`, + '68' : dedent`Lock Get Review Error: Cannot get review requests`, + + '69' : dedent`Lock Set Review Error: Cannot set review request`, + + '70' : dedent`Lock Set Review Error: Brew not found`, + + '71' : dedent`Lock Set Review Error: Review already requested`, + + '72' : dedent`Lock Remove Review Error: Cannot clear review request`, + + '73' : dedent`Lock Remove Review Error: Brew not found`, // ####### Other Errors diff --git a/server/admin.api.js b/server/admin.api.js index 6a3a943d5..77de3fd52 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -258,7 +258,7 @@ router.get('/api/lock/reviews', mw.adminOnly, asyncHandler(async (req, res)=>{ ]; const reviewDocuments = await HomebrewModel.aggregate(countReviewsPipeline) .catch((error)=>{ - throw { name: 'Unable to get reviews', message: 'Unable to get review collection', status: 500, HBErrorCode: '68', error }; + throw { name: 'Can Not Get Reviews', message: 'Unable to get review collection', status: 500, HBErrorCode: '68', error }; }); return res.json({ reviewDocuments @@ -275,11 +275,11 @@ router.put('/admin/lock/review/request/:id', asyncHandler(async (req, res)=>{ }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); }; + if(!brew) { throw { name: 'Brew Not Found', message: `Cannot find a locked brew with ID ${req.params.id}`, code: 500, HBErrorCode: '70' }; }; if(brew.lock.reviewRequested){ // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'ALREADY REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + throw { name: 'Review Already Requested', message: `Review already requested for brew ${brew.shareId} - ${brew.title}`, code: 500, HBErrorCode: '71' }; }; brew.lock.reviewRequested = new Date(); @@ -287,12 +287,11 @@ router.put('/admin/lock/review/request/:id', asyncHandler(async (req, res)=>{ await brew.save() .catch((error)=>{ - console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); + throw { name: 'Can Not Set Review Request', message: `Unable to set request for review on brew ID ${req.params.id}`, code: 500, HBErrorCode: '69', error }; }); // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUESTED', detail: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); + return res.json({ name: 'Review Requested', message: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); })); @@ -304,19 +303,18 @@ router.put('/api/lock/review/remove/:id', mw.adminOnly, asyncHandler(async (req, }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.json({ status: 'REVIEW REQUEST NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; + if(!brew) { throw { name: 'Can Not Clear Review Request', message: `Brew ID ${req.params.id} does not have a review pending!`, HBErrorCode: '73' }; }; brew.lock.reviewRequested = undefined; brew.markModified('lock'); await brew.save() .catch((error)=>{ - console.error(error); - return res.json({ status: 'ERROR', detail: `Unable to remove request for review on brew ID ${req.params.id}`, error }); + throw { name: 'Can Not Clear Review Request', message: `Unable to remove request for review on brew ID ${req.params.id}`, HBErrorCode: '72', error }; }); // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); - return res.json({ status: 'REVIEW REQUEST REMOVED', detail: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); + return res.json({ name: 'Review Request Cleared', message: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); })); From e2f2b2962f47532df8079e2294718f0b62a0cc27 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 17:34:31 +1200 Subject: [PATCH 101/128] Revert request middleware change as it is no longer necessary --- client/homebrew/utils/request-middleware.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js index deb08d265..01a9d2571 100644 --- a/client/homebrew/utils/request-middleware.js +++ b/client/homebrew/utils/request-middleware.js @@ -1,8 +1,6 @@ -import packageJSON from '../../../package.json' with { type: 'json' }; import request from 'superagent'; - -const addHeader = (request)=>request.set('Homebrewery-Version', packageJSON.version); +const addHeader = (request)=>request.set('Homebrewery-Version', global.version); const requestMiddleware = { get : (path)=>addHeader(request.get(path)), From 26aa302714ad9364ee34008741543f1e6a1f9399 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 20:04:45 +1200 Subject: [PATCH 102/128] Remove debugging test route --- server/admin.api.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 77de3fd52..8d8500c37 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -166,10 +166,6 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ // ####################### LOCKS -router.get('/api/lock/throw', asyncHandler(async ()=>{ - throw { HBErrorCode: '60', code: 500, message: 'Thrown deliberately' }; -})); - router.get('/api/lock/count', mw.adminOnly, asyncHandler(async (req, res)=>{ const countLocksQuery = { From ab9b151b8a583e85806b2ebde33b17c99335f0e3 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 20:52:35 +1200 Subject: [PATCH 103/128] Add API route to return all locked brews --- server/admin.api.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index 8d8500c37..f770e4adf 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -180,6 +180,31 @@ router.get('/api/lock/count', mw.adminOnly, asyncHandler(async (req, res)=>{ })); +router.get('/api/locks', mw.adminOnly, asyncHandler(async (req, res)=>{ + const countLocksPipeline = [ + { + $match : + { + 'lock' : { '$exists': 1 } + }, + }, + { + $project : { + shareId : 1, + title : 1 + } + } + ]; + const lockedDocuments = await HomebrewModel.aggregate(countLocksPipeline) + .catch((error)=>{ + throw { name: 'Can Not Get Locked Brews', message: 'Unable to get locked brew collection', status: 500, HBErrorCode: '68', error }; + }); + return res.json({ + lockedDocuments + }); + +})); + router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ const lock = req.body; From 7451dda632415a875360d4ca6ba0e9fc29eca1cf Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 20:54:21 +1200 Subject: [PATCH 104/128] Add locked brews table --- client/admin/lockTools/lockTools.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index a1cffce24..253d5f006 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -35,7 +35,9 @@ const LockTools = createClass({

Number of brews currently locked: {this.state.reviewCount}


- + +
+

@@ -175,6 +177,7 @@ const LockTable = createClass({ getDefaultProps : function() { return { title : '', + text : '', fetchURL : '/api/locks', resultName : '', propertyNames : ['shareId'] @@ -212,7 +215,7 @@ const LockTable = createClass({ {this.state.result[this.props.resultName] && <> -

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

+

{this.props.text}: {this.state.result[this.props.resultName].length}

From f74c2049a767e4d751b9f279c7a93e37bf9b5609 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 20:54:44 +1200 Subject: [PATCH 105/128] Rename Edit and Share page message fields --- client/admin/lockTools/lockTools.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 253d5f006..7c7580edf 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -127,12 +127,12 @@ const LockBrew = createClass({


From 61efc2d1522aba1fa24118a68bf431f3d7a86f23 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 21:12:37 +1200 Subject: [PATCH 106/128] Tweak styling --- client/admin/lockTools/lockTools.less | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 0b6cb615d..2666b4bb0 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -12,7 +12,7 @@ line-height: 1.5em; input { float: right; - width: 70%; + width: 65%; margin-left: 10px; } } @@ -68,4 +68,8 @@ margin-top: 0px; } } + + button i { + padding-left: 5px; + } } \ No newline at end of file From 7a1042fedd7abec5161783bb668d14a3d87f7fef Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 22:02:08 +1200 Subject: [PATCH 107/128] Stylelint fixes --- client/admin/lockTools/lockTools.less | 118 ++++++++---------- .../lockNotification/lockNotification.less | 6 +- 2 files changed, 55 insertions(+), 69 deletions(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index 2666b4bb0..f6e7ea9dd 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -1,75 +1,63 @@ .lockTools { - .lockBrew { - columns: 2; + .lockBrew { + columns : 2; - .lockForm { - break-inside: avoid; + .lockForm { + break-inside : avoid; - label { - width: 100%; - display: inline-block; - text-align: right; - line-height: 1.5em; - input { - float: right; - width: 65%; - margin-left: 10px; - } - } - } + label { + display : inline-block; + width : 100%; + line-height : 1.5em; + text-align : right; + input { + float : right; + width : 65%; + margin-left : 10px; + } + input.checkbox { + width : 1.5em; + height : 1.5em; + } + } + } - .lockSuggestions { - break-inside: avoid; - columns: 2; - line-height: 1.2em; - h2 { - column-span: all; - } - h3 { - margin-top: 0px; - } - b { - font-weight: 600; - } + .lockSuggestions { + line-height : 1.2em; + break-inside : avoid; + columns : 2; + h2 { column-span : all; } + h3 { margin-top : 0px; } + b { font-weight : 600; } - .lockCodes { - break-inside: avoid; - } - } - } + .lockCodes { break-inside : avoid; } + } + } - .lockTable{ - break-inside: avoid; - cursor: default; - .row:hover { - background-color: #ccc; - color: #000; - } - .icon { - cursor: pointer; - &:hover{ - text-shadow: 0px 0px 6px black; - } - } - } + .lockTable { + cursor : default; + break-inside : avoid; + .row:hover { + color : #000000; + background-color : #CCCCCC; + } + .icon { + cursor : pointer; + &:hover { text-shadow : 0px 0px 6px black; } + } + } - th, td { - padding: 4px 10px; - text-align: center; - } - table, td{ - border: 1px solid #333333; - } + th, td { + padding : 4px 10px; + text-align : center; + } + table, td { border : 1px solid #333333; } - .brewLookup{ - break-inside: avoid; - min-height: 175px; - h2 { - margin-top: 0px; - } - } + .brewLookup { + min-height : 175px; + break-inside : avoid; + h2 { margin-top : 0px; } + } - button i { - padding-left: 5px; - } + button i { padding-left : 5px; } } \ No newline at end of file diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.less b/client/homebrew/pages/editPage/lockNotification/lockNotification.less index 88133ea98..930b070c4 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.less +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.less @@ -11,15 +11,13 @@ &::backdrop { background-color : #000000AA; } button { + padding : 2px 15px; margin : 10px; color : white; background-color : #333333; - padding : 2px 15px; &.inactive, - &:hover { - background-color : #777777; - } + &:hover { background-color : #777777; } } h1, h3 { From f8566392f6ec9fcf8e12cfb07eefbfd7693cd747 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 22:02:28 +1200 Subject: [PATCH 108/128] Add overwrite option for updating locks --- client/admin/lockTools/lockTools.jsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index 7c7580edf..c528fb13d 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -59,7 +59,8 @@ const LockBrew = createClass({ code : 455, editMessage : '', shareMessage : 'This Brew has been locked.', - result : {} + result : {}, + overwrite : false }; }, @@ -73,6 +74,7 @@ const LockBrew = createClass({ e.preventDefault(); if(!this.state.editMessage) return; const newLock = { + overwrite : this.state.overwrite, code : parseInt(this.state.code) || 100, editMessage : this.state.editMessage, shareMessage : this.state.shareMessage, @@ -136,6 +138,10 @@ const LockBrew = createClass({ {this.renderInput('shareMessage')}
+ @@ -163,8 +169,8 @@ const LockBrew = createClass({

Messages

    -
  • Edit Message: This is the private message that is ONLY displayed to the authors of the locked brew. This message MUST specify exactly what actions must be taken in order to have the brew unlocked.
  • -
  • Share Message: This is the public message that is displayed to the EVERYONE that attempts to view the locked brew.
  • +
  • Private Message: This is the private message that is ONLY displayed to the authors of the locked brew. This message MUST specify exactly what actions must be taken in order to have the brew unlocked.
  • +
  • Public Message: This is the public message that is displayed to the EVERYONE that attempts to view the locked brew.
From ec6258a2a5b219f518f2bc4b4672431e64391fff Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 7 Apr 2025 22:02:53 +1200 Subject: [PATCH 109/128] Add overwrite option to API function --- server/admin.api.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index f770e4adf..8942a4675 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -208,6 +208,10 @@ router.get('/api/locks', mw.adminOnly, asyncHandler(async (req, res)=>{ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ const lock = req.body; + + const overwrite = lock.overwrite || false; + lock.overwrite = undefined; + lock.applied = new Date; const filter = { @@ -218,7 +222,7 @@ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ if(!brew) throw { name: 'Brew Not Found', message: 'Cannot find brew to lock', shareId: req.params.id, status: 500, HBErrorCode: '63' }; - if(brew.lock) { + if(brew.lock && !overwrite) { // console.log('ALREADY LOCKED'); throw { name: 'Already Locked', message: 'Lock already exists on brew', shareId: req.params.id, title: brew.title, status: 500, HBErrorCode: '64' }; } From 1ded1cad5a123ac7fb4f74a7a89eadba21e293b0 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Tue, 8 Apr 2025 09:28:54 +1200 Subject: [PATCH 110/128] Change accessType check --- server/homebrew.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 055d72eba..c59c6c4d7 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -118,7 +118,7 @@ const api = { throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04' }; } - if(stub?.lock && accessType != 'edit') { + if(stub?.lock && accessType === 'share') { throw { HBErrorCode: '51', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title }; } From d0c3765f8f82959bf7bffd597448dc66f18a0836 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 7 Apr 2025 23:22:47 -0500 Subject: [PATCH 111/128] Move Snippets store to metadata block. Note this still stores the snippets as a string for the passed about brew object. --- server/homebrew.api.js | 7 +------ shared/helpers.js | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 4534c5cb7..84e338ef4 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -170,12 +170,6 @@ const api = { mergeBrewText : (brew)=>{ let text = brew.text; - if(brew.snippets !== undefined) { - text = `\`\`\`snippets\n` + - `${yaml.dump(brewSnippetsToJSON('brew_snippets', brew.snippets, null, false))}` + - `\`\`\`\n\n` + - `${text}`; - } if(brew.style !== undefined) { text = `\`\`\`css\n` + `${brew.style || ''}\n` + @@ -183,6 +177,7 @@ const api = { `${text}`; } const metadata = _.pick(brew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme']); + metadata.snippets = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false); text = `\`\`\`metadata\n` + `${yaml.dump(metadata)}\n` + `\`\`\`\n\n` + diff --git a/shared/helpers.js b/shared/helpers.js index 997d77cec..e4dc9eba8 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -91,6 +91,7 @@ const splitTextStyleAndMetadata = (brew)=>{ const metadataSection = brew.text.slice(11, index + 1); const metadata = yaml.load(metadataSection); Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])); + brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets); brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```css')) { @@ -98,11 +99,6 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.style = brew.text.slice(7, index + 1); brew.text = brew.text.slice(index + 6); } - if(brew.text.startsWith('```snippets')) { - const index = brew.text.indexOf('\n```\n\n'); - brew.snippets = yamlSnippetsToText(yaml.load(brew.text.slice(11, index + 1))).slice(0, -1); - brew.text = brew.text.slice(index + 6); - } // Handle old brews that still have empty strings in the tags metadata if(typeof brew.tags === 'string') brew.tags = brew.tags ? [brew.tags] : []; From 9f56d100aaf5a250715fc70872e3410237378625 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 8 Apr 2025 00:32:11 -0500 Subject: [PATCH 112/128] change tab --- client/homebrew/editor/editor.jsx | 6 +++--- client/homebrew/editor/snippetbar/snippetbar.jsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 7112aa4b9..77da1fcd9 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -61,7 +61,7 @@ const Editor = createClass({ getInitialState : function() { return { editorTheme : this.props.editorTheme, - view : 'text' //'text', 'style', 'meta', 'snip' + view : 'text' //'text', 'style', 'meta', 'snippet' }; }, @@ -71,7 +71,7 @@ const Editor = createClass({ isText : function() {return this.state.view == 'text';}, isStyle : function() {return this.state.view == 'style';}, isMeta : function() {return this.state.view == 'meta';}, - isSnip : function() {return this.state.view == 'snip';}, + isSnip : function() {return this.state.view == 'snippet';}, componentDidMount : function() { @@ -167,7 +167,7 @@ const Editor = createClass({ highlightCustomMarkdown : function(){ if(!this.codeEditor.current) return; - if((this.state.view === 'text') ||(this.state.view === 'snip')) { + if((this.state.view === 'text') ||(this.state.view === 'snippet')) { const codeMirror = this.codeEditor.current.codeMirror; codeMirror.operation(()=>{ // Batch CodeMirror styling diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 8c6872ab4..5e2051a86 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -259,8 +259,8 @@ const Snippetbar = createClass({ onClick={()=>this.props.onViewChange('style')}> -
this.props.onViewChange('snip')}> +
this.props.onViewChange('snippet')}>
Date: Wed, 9 Apr 2025 09:59:34 +1200 Subject: [PATCH 113/128] Add authors to locked brew error message --- client/homebrew/pages/errorPage/errors/errorIndex.js | 4 +++- server/homebrew.api.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index 2f0d4599b..0315c021c 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -194,7 +194,9 @@ const errorIndex = (props)=>{ **Brew ID:** ${props.brew.brewId} - **Brew Title:** ${escape(props.brew.brewTitle)}`, + **Brew Title:** ${escape(props.brew.brewTitle)} + + **Brew Authors:** ${escape(props.brew.brewAuthors.length ? props.brew.brewAuthors.join(',') : 'No listed authors')}`, // ####### Admin page error ####### '52' : dedent` diff --git a/server/homebrew.api.js b/server/homebrew.api.js index c59c6c4d7..3c7566af7 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -119,7 +119,7 @@ const api = { } if(stub?.lock && accessType === 'share') { - throw { HBErrorCode: '51', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title }; + throw { HBErrorCode: '51', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title, brewAuthors: stub.authors }; } // If there's a google id, get it if requesting the full brew or if no stub found yet From e3de7b9f012770719aaf9933d2bcab1cfda540e6 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 10:17:35 +1200 Subject: [PATCH 114/128] Tweak lock styling --- client/admin/lockTools/lockTools.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less index f6e7ea9dd..94a27ac8e 100644 --- a/client/admin/lockTools/lockTools.less +++ b/client/admin/lockTools/lockTools.less @@ -8,7 +8,7 @@ label { display : inline-block; width : 100%; - line-height : 1.5em; + line-height : 2.25em; text-align : right; input { float : right; From dc724492efbf6debebbf6ae3584846fdf1c3c9b6 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 10:26:10 +1200 Subject: [PATCH 115/128] Prevent BrewUtils from loading when it is not the current Admin tab --- client/admin/admin.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index f59162e1d..29973d221 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -8,10 +8,10 @@ import LockTools from './lockTools/lockTools.jsx'; const tabGroups = ['brew', 'notifications', 'authors', 'locks']; const Admin = ()=>{ - const [currentTab, setCurrentTab] = useState('brew'); + const [currentTab, setCurrentTab] = useState(''); useEffect(()=>{ - setCurrentTab(localStorage.getItem('hbAdminTab')); + setCurrentTab(localStorage.getItem('hbAdminTab') || 'brew'); }, []); useEffect(()=>{ From b19d05fbf78d08955ccfa3a82ed79918e2303ad7 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 10:41:58 +1200 Subject: [PATCH 116/128] Remove commented out console.logs --- server/admin.api.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 8942a4675..bced73c83 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -223,7 +223,6 @@ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ if(!brew) throw { name: 'Brew Not Found', message: 'Cannot find brew to lock', shareId: req.params.id, status: 500, HBErrorCode: '63' }; if(brew.lock && !overwrite) { - // console.log('ALREADY LOCKED'); throw { name: 'Already Locked', message: 'Lock already exists on brew', shareId: req.params.id, title: brew.title, status: 500, HBErrorCode: '64' }; } @@ -235,7 +234,6 @@ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ throw { name: 'Already Locked', message: 'Unable to set lock', shareId: req.params.id, status: 500, HBErrorCode: '62', error }; }); - // console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); return res.json({ name: 'LOCKED', message: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); })); @@ -260,9 +258,6 @@ router.put('/api/unlock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ throw { name: 'Cannot Unlock', message: 'Unable to clear lock', shareId: req.params.id, status: 500, HBErrorCode: '65', error }; }); - // console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); - - return res.json({ name: 'Unlocked', message: `Lock removed from brew ID ${req.params.id}` }); })); @@ -303,7 +298,6 @@ router.put('/admin/lock/review/request/:id', asyncHandler(async (req, res)=>{ if(!brew) { throw { name: 'Brew Not Found', message: `Cannot find a locked brew with ID ${req.params.id}`, code: 500, HBErrorCode: '70' }; }; if(brew.lock.reviewRequested){ - // console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); throw { name: 'Review Already Requested', message: `Review already requested for brew ${brew.shareId} - ${brew.title}`, code: 500, HBErrorCode: '71' }; }; @@ -315,7 +309,6 @@ router.put('/admin/lock/review/request/:id', asyncHandler(async (req, res)=>{ throw { name: 'Can Not Set Review Request', message: `Unable to set request for review on brew ID ${req.params.id}`, code: 500, HBErrorCode: '69', error }; }); - // console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); return res.json({ name: 'Review Requested', message: `Review requested on brew ID ${brew.shareId} - ${brew.title}` }); })); @@ -338,7 +331,6 @@ router.put('/api/lock/review/remove/:id', mw.adminOnly, asyncHandler(async (req, throw { name: 'Can Not Clear Review Request', message: `Unable to remove request for review on brew ID ${req.params.id}`, HBErrorCode: '72', error }; }); - // console.log(`Review request removed on brew ID ${brew.shareId} - ${brew.title}`); return res.json({ name: 'Review Request Cleared', message: `Review request removed for brew ID ${brew.shareId} - ${brew.title}` }); })); From bd68b9c0cb376f83e56960b5c60ac2de5fd9e0e9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 10:48:02 +1200 Subject: [PATCH 117/128] Remove unnecessary variable --- server/admin.api.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index bced73c83..8327dfd49 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -209,9 +209,6 @@ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ const lock = req.body; - const overwrite = lock.overwrite || false; - lock.overwrite = undefined; - lock.applied = new Date; const filter = { @@ -222,10 +219,12 @@ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ if(!brew) throw { name: 'Brew Not Found', message: 'Cannot find brew to lock', shareId: req.params.id, status: 500, HBErrorCode: '63' }; - if(brew.lock && !overwrite) { + if(brew.lock && !lock.overwrite) { throw { name: 'Already Locked', message: 'Lock already exists on brew', shareId: req.params.id, title: brew.title, status: 500, HBErrorCode: '64' }; } + lock.overwrite = undefined; + brew.lock = lock; brew.markModified('lock'); From 95f44f446036f9f182d9d63e315d660d223e3f98 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 11:04:57 +1200 Subject: [PATCH 118/128] Revert unnecessary change in app.js --- server/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/app.js b/server/app.js index fc0954efd..b7d990baf 100644 --- a/server/app.js +++ b/server/app.js @@ -2,11 +2,10 @@ // Set working directory to project root import { dirname } from 'path'; import { fileURLToPath } from 'url'; +import packageJSON from './../package.json' with { type: 'json' }; const __dirname = dirname(fileURLToPath(import.meta.url)); process.chdir(`${__dirname}/..`); - -import packageJSON from '../package.json' with { type: 'json' }; const version = packageJSON.version; import _ from 'lodash'; From 5e7e314baacd437685ae8b31050da8ac288b7dd9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 11:38:57 +1200 Subject: [PATCH 119/128] Update fields returned for Lock and Review Tables --- server/admin.api.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/admin.api.js b/server/admin.api.js index 8327dfd49..ee859a3e0 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -191,7 +191,9 @@ router.get('/api/locks', mw.adminOnly, asyncHandler(async (req, res)=>{ { $project : { shareId : 1, - title : 1 + editId : 1, + title : 1, + lock : 1 } } ]; @@ -271,7 +273,9 @@ router.get('/api/lock/reviews', mw.adminOnly, asyncHandler(async (req, res)=>{ { $project : { shareId : 1, - title : 1 + editId : 1, + title : 1, + lock : 1 } } ]; From c6cd6e9864fb1911eea84d2602ff52cefb008bf9 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 8 Apr 2025 20:29:32 -0500 Subject: [PATCH 120/128] A little bit of cleanup and structure flattening Fixes failed tests. --- server/homebrew.api.js | 3 ++- shared/helpers.js | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 84e338ef4..8a98d50a8 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -177,7 +177,8 @@ const api = { `${text}`; } const metadata = _.pick(brew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme']); - metadata.snippets = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false); + const snippetsArray = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false).snippets; + metadata.snippets = snippetsArray.length > 0 ? snippetsArray : undefined; text = `\`\`\`metadata\n` + `${yaml.dump(metadata)}\n` + `\`\`\`\n\n` + diff --git a/shared/helpers.js b/shared/helpers.js index e4dc9eba8..0ca681dfb 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -76,7 +76,8 @@ const yamlSnippetsToText = (yamlObj)=>{ if(typeof yamlObj == 'string') return yamlObj; let snippetsText = ''; - for (let snippet of yamlObj.snippets) { + + for (let snippet of yamlObj) { for (let subSnippet of snippet.subsnippets) { snippetsText = `${snippetsText}\\snippet ${subSnippet.name}\n${subSnippet.gen || ''}\n`; } @@ -91,7 +92,7 @@ const splitTextStyleAndMetadata = (brew)=>{ const metadataSection = brew.text.slice(11, index + 1); const metadata = yaml.load(metadataSection); Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])); - brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets); + brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets || ''); brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```css')) { From da4f6c9307d8e2853713529b7e6c5c729372de1e Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 17:54:51 +1200 Subject: [PATCH 121/128] Load lock details to the Lock Brew form --- client/admin/lockTools/lockTools.jsx | 50 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index c528fb13d..c639adaca 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -1,9 +1,8 @@ -/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ +/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ require('./lockTools.less'); const React = require('react'); const createClass = require('create-react-class'); -// const request = require('superagent'); import request from '../../homebrew/utils/request-middleware.js'; const LockTools = createClass({ @@ -29,17 +28,23 @@ const LockTools = createClass({ } }, + updateLockData : function(lock){ + this.setState({ + lock : lock + }); + }, + render : function() { return

Lock Count

Number of brews currently locked: {this.state.reviewCount}


- +
- +
- +
@@ -55,12 +60,12 @@ const LockBrew = createClass({ getInitialState : function() { // Default values return { - brewId : '', - code : 455, - editMessage : '', - shareMessage : 'This Brew has been locked.', + brewId : this.props.lock?.shareId || '', + code : this.props.lock?.code || 455, + editMessage : this.props.lock?.editMessage || '', + shareMessage : this.props.lock?.shareMessage || 'This Brew has been locked.', result : {}, - overwrite : false + overwrite : false, }; }, @@ -186,7 +191,8 @@ const LockTable = createClass({ text : '', fetchURL : '/api/locks', resultName : '', - propertyNames : ['shareId'] + propertyNames : ['shareId'], + loadBrew : ()=>{} }; }, @@ -198,7 +204,9 @@ const LockTable = createClass({ }; }, - clickFn(){ + lockKey : React.createRef(0), + + clickFn : function (){ this.setState({ searching: true, error: null }); request.get(this.props.fetchURL) @@ -209,6 +217,18 @@ const LockTable = createClass({ }); }, + updateBrewLockData : function (lockData){ + this.lockKey.current++; + const brewData = { + key : this.lockKey.current, + shareId : lockData.shareId, + code : lockData.lock.code, + editMessage : lockData.lock.editMessage, + shareMessage : lockData.lock.shareMessage + }; + this.props.loadBrew(brewData); + }, + render : function () { return <>
@@ -229,7 +249,7 @@ const LockTable = createClass({ return
; })} - + @@ -240,8 +260,8 @@ const LockTable = createClass({ {result[name].toString()} ; })} - - + + ; })} From 04defb97b09e99def4a56e35b1f154cf71e89b92 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 9 Apr 2025 20:10:48 +1200 Subject: [PATCH 122/128] Tweak styling for Overwrite checkbox --- client/admin/lockTools/lockTools.jsx | 2 +- client/admin/lockTools/lockTools.less | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx index c639adaca..9a28d330f 100644 --- a/client/admin/lockTools/lockTools.jsx +++ b/client/admin/lockTools/lockTools.jsx @@ -143,7 +143,7 @@ const LockBrew = createClass({ {this.renderInput('shareMessage')}
-
{name}clipviewload
{navigator.clipboard.writeText(result.shareId.toString());}}>{navigator.clipboard.writeText(result.shareId.toString());}}>{this.updateBrewLockData(result);}}>