0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-04 10:22:38 +00:00

Merge branch 'remove-google-get-during-update' into disable-changes-from-non-authors

This commit is contained in:
Charlie Humphreys
2022-11-16 23:15:55 -06:00
25 changed files with 12471 additions and 5806 deletions

View File

@@ -27,7 +27,7 @@ jobs:
# fallback to using the latest cache if no exact match is found # fallback to using the latest cache if no exact match is found
- v1-dependencies- - v1-dependencies-
- node/install-npm - run: sudo npm install -g npm@8.10.0
- node/install-packages: - node/install-packages:
app-dir: ~/homebrewery app-dir: ~/homebrewery
cache-path: node_modules cache-path: node_modules
@@ -55,13 +55,13 @@ jobs:
at: . at: .
# run tests! # run tests!
- run: - run:
name: Test - Basic name: Test - Basic
command: npm run test:basic command: npm run test:basic
- run: - run:
name: Test - Mustache Spans name: Test - Mustache Spans
command: npm run test:mustache-span command: npm run test:mustache-span
- run: - run:
name: Test - Routes name: Test - Routes
command: npm run test:route command: npm run test:route
@@ -71,4 +71,4 @@ workflows:
- build - build
- test: - test:
requires: requires:
- build - build

View File

@@ -12,10 +12,6 @@ body:
description: The best feature requests provide an explanation of the current issue and then an explanation of how it could be improved. Screenshots/images can be pasted right in as well! description: The best feature requests provide an explanation of the current issue and then an explanation of how it could be improved. Screenshots/images can be pasted right in as well!
validations: validations:
required: true required: true
- type: checkboxes - type: markdown
id: terms
attributes: attributes:
label: "Please confirm:" value: "Please be sure to search for any close matches to your request in the GitHub Issues tracker before opening a new request, thanks!"
options:
- label: I have searched the Issues tracker for any duplicate requests and found none.
required: true

View File

@@ -4,14 +4,15 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: Please include as much information as possible. value: Please include as much information as possible.
- type: checkboxes - type: dropdown
id: renderer id: renderer
attributes: attributes:
label: Renderer label: Renderer
description: Which renderer does this issue occur on? If you are unsure, you can check the renderer in the Properties Editor (click the "i" in the Snippet Menu bar above the editor). description: Which renderer does this issue occur on? If you are unsure, you can check the renderer in the Properties Editor (click the "i" in the Snippet Menu bar above the editor).
options: options:
- label: Legacy - v3
- label: v3 - Legacy
- Both
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@@ -45,6 +45,21 @@ pre {
## changelog ## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
### Thursday 28/10/2022 - v3.3.1
{{taskList
##### Calculuschild
* [x] Fixes to several broken CSS styles from v3.3.0
Fixes issues [#2468](https://github.com/naturalcrit/homebrewery/issues/2468)
##### Jeddai
* [x] Reduce size of thumbnails on social media links
}}
### Friday 19/10/2022 - v3.3.0 ### Friday 19/10/2022 - v3.3.0
{{taskList {{taskList
@@ -76,8 +91,6 @@ Fixes issues [#2135](https://github.com/naturalcrit/homebrewery/issues/2135)
Fixes issues [#2427](https://github.com/naturalcrit/homebrewery/issues/2427) Fixes issues [#2427](https://github.com/naturalcrit/homebrewery/issues/2427)
##### Gazook: ##### Gazook:
* [x] Several updates to bug reporting and error popups * [x] Several updates to bug reporting and error popups

View File

@@ -137,9 +137,17 @@ const Editor = createClass({
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
} }
// Highlight injectors {style}
if(line.includes('{') && line.includes('}')){
const regex = /(?<!{){(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1}/g;
let match;
while ((match = regex.exec(line)) != null) {
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'injection' });
}
}
// Highlight inline spans {{content}} // Highlight inline spans {{content}}
if(line.includes('{{') && line.includes('}}')){ if(line.includes('{{') && line.includes('}}')){
const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g; const regex = /{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *|}}/g;
let match; let match;
let blockCount = 0; let blockCount = 0;
while ((match = regex.exec(line)) != null) { while ((match = regex.exec(line)) != null) {
@@ -158,7 +166,7 @@ const Editor = createClass({
// Highlight block divs {{\n Content \n}} // Highlight block divs {{\n Content \n}}
let endCh = line.length+1; let endCh = line.length+1;
const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/); const match = line.match(/^ *{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *$|^ *}}$/);
if(match) if(match)
endCh = match.index+match[0].length; endCh = match.index+match[0].length;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' }); codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });

View File

@@ -29,6 +29,10 @@
font-weight : bold; font-weight : bold;
//font-style: italic; //font-style: italic;
} }
.injection{
color : green;
font-weight : bold;
}
} }
.brewJump{ .brewJump{

View File

@@ -11,6 +11,7 @@ const SharePage = require('./pages/sharePage/sharePage.jsx');
const NewPage = require('./pages/newPage/newPage.jsx'); const NewPage = require('./pages/newPage/newPage.jsx');
//const ErrorPage = require('./pages/errorPage/errorPage.jsx'); //const ErrorPage = require('./pages/errorPage/errorPage.jsx');
const PrintPage = require('./pages/printPage/printPage.jsx'); const PrintPage = require('./pages/printPage/printPage.jsx');
const AccountPage = require('./pages/accountPage/accountPage.jsx');
const WithRoute = (props)=>{ const WithRoute = (props)=>{
const params = useParams(); const params = useParams();
@@ -61,24 +62,27 @@ const Homebrew = createClass({
}, },
render : function (){ render : function (){
return <Router location={this.props.url}> return (
<div className='homebrew'> <Router location={this.props.url}>
<Routes> <div className='homebrew'>
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} />} /> <Routes>
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={this.props.brew} />} /> <Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} />} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} />} /> <Route path='/share/:id' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/new' element={<WithRoute el={NewPage}/>} /> <Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} />} />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} /> <Route path='/new' element={<WithRoute el={NewPage}/>} />
<Route path='/print/:id' element={<WithRoute el={PrintPage} brew={this.props.brew} />} /> <Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} />
<Route path='/print' element={<WithRoute el={PrintPage} />} /> <Route path='/print/:id' element={<WithRoute el={PrintPage} brew={this.props.brew} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} /> <Route path='/print' element={<WithRoute el={PrintPage} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} /> <Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} /> <Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} /> <Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} uiItems={this.props.brew.uiItems} />} />
<Route path='/*' element={<WithRoute el={HomePage} brew={this.props.brew} />} /> <Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
</Routes> <Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
</div> <Route path='/*' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
</Router>; </Routes>
</div>
</Router>
);
} }
}); });

View File

@@ -76,6 +76,14 @@ const Account = createClass({
> >
brews brews
</Nav.item> </Nav.item>
<Nav.item
className='account'
color='orange'
icon='fas fa-user'
href='/account'
>
account
</Nav.item>
<Nav.item <Nav.item
className='logout' className='logout'
color='red' color='red'

View File

@@ -20,6 +20,12 @@ module.exports = function(props){
rel='noopener noreferrer'> rel='noopener noreferrer'>
report issue report issue
</Nav.item> </Nav.item>
<Nav.item color='green' icon='fas fa-question-circle'
href='/faq'
newTab={true}
rel='noopener noreferrer'>
FAQ
</Nav.item>
<Nav.item color='blue' icon='fas fa-fw fa-file-import' <Nav.item color='blue' icon='fas fa-fw fa-file-import'
href='/migrate' href='/migrate'
newTab={true} newTab={true}

View File

@@ -115,8 +115,36 @@
color : white; color : white;
text-decoration : none; text-decoration : none;
border-top : 1px solid #888; border-top : 1px solid #888;
overflow : clip;
.clear{
display : none;
position : absolute;
top : 50%;
transform : translateY(-50%);
right : 0px;
width : 20px;
height : 100%;
background-color : #333;
opacity : 70%;
border-radius : 3px;
&:hover {
opacity : 100%;
}
i {
text-align : center;
font-size : 10px;
margin : 0;
height :100%;
width :100%;
}
}
&:hover{ &:hover{
background-color : @blue; background-color : @blue;
.clear{
display : grid;
place-content : center;
}
} }
.title{ .title{
display : inline-block; display : inline-block;

View File

@@ -119,6 +119,25 @@ const RecentItems = createClass({
}); });
}, },
removeItem : function(url, evt){
evt.preventDefault();
let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]');
let viewed = JSON.parse(localStorage.getItem(VIEW_KEY) || '[]');
edited = edited.filter((item)=>{ return (item.url !== url);});
viewed = viewed.filter((item)=>{ return (item.url !== url);});
localStorage.setItem(EDIT_KEY, JSON.stringify(edited));
localStorage.setItem(VIEW_KEY, JSON.stringify(viewed));
this.setState({
edit : edited,
view : viewed
});
},
renderDropdown : function(){ renderDropdown : function(){
if(!this.state.showDropdown) return null; if(!this.state.showDropdown) return null;
@@ -127,6 +146,7 @@ const RecentItems = createClass({
return <a href={brew.url} className='item' key={`${brew.id}-${i}`} target='_blank' rel='noopener noreferrer' title={brew.title || '[ no title ]'}> return <a href={brew.url} className='item' key={`${brew.id}-${i}`} target='_blank' rel='noopener noreferrer' title={brew.title || '[ no title ]'}>
<span className='title'>{brew.title || '[ no title ]'}</span> <span className='title'>{brew.title || '[ no title ]'}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span> <span className='time'>{Moment(brew.ts).fromNow()}</span>
<div className='clear' title='Remove from Recents' onClick={(e)=>{this.removeItem(`${brew.url}`, e);}}><i className='fas fa-times'></i></div>
</a>; </a>;
}); });
}; };

View File

@@ -0,0 +1,71 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const moment = require('moment');
const UIPage = require('../basePages/uiPage/uiPage.jsx');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const Account = require('../../navbar/account.navitem.jsx');
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../navbar/help.navitem.jsx');
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
const AccountPage = createClass({
displayName : 'AccountPage',
getDefaultProps : function() {
return {
brew : {},
uiItems : {}
};
},
getInitialState : function() {
return {
uiItems : this.props.uiItems
};
},
renderNavItems : function() {
return <Navbar>
<Nav.section>
<NewBrew />
<HelpNavItem />
<RecentNavItem />
<Account />
</Nav.section>
</Navbar>;
},
renderUiItems : function() {
// console.log(this.props.uiItems);
return <>
<div className='dataGroup'>
<h1>Account Information <i className='fas fa-user'></i></h1>
<p><strong>Username: </strong> {this.props.uiItems.username || 'No user currently logged in'}</p>
<p><strong>Last Login: </strong> {moment(this.props.uiItems.issued).format('dddd, MMMM Do YYYY, h:mm:ss a ZZ') || '-'}</p>
</div>
<div className='dataGroup'>
<h3>Homebrewery Information <NaturalCritIcon /></h3>
<p><strong>Brews on Homebrewery: </strong> {this.props.uiItems.mongoCount || '-'}</p>
</div>
<div className='dataGroup'>
<h3>Google Information <i className='fab fa-google-drive'></i></h3>
<p><strong>Linked to Google: </strong> {this.props.uiItems.googleId ? 'YES' : 'NO'}</p>
{this.props.uiItems.googleId ? <p><strong>Brews on Google Drive: </strong> {this.props.uiItems.fileCount || '-'}</p> : '' }
</div>
</>;
},
render : function(){
return <UIPage brew={this.props.brew}>
{this.renderUiItems()}
</UIPage>;
}
});
module.exports = AccountPage;

View File

@@ -26,7 +26,29 @@
font-size : 1.3em; font-size : 1.3em;
font-style : italic; font-style : italic;
} }
.brewCollection {
h1:hover{
cursor: pointer;
}
.active::before, .inactive::before {
font-family: 'Font Awesome 5 Free';
font-weight: 900;
font-size: 0.6cm;
padding-right: 0.5em;
}
.active {
color: var(--HB_Color_HeaderText);
}
.active::before {
content: '\f107';
}
.inactive {
color: #707070;
}
.inactive::before {
content: '\f105';
}
}
} }
} }
.sort-container{ .sort-container{

View File

@@ -0,0 +1,38 @@
require('./uiPage.less');
const React = require('react');
const createClass = require('create-react-class');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../../navbar/navbar.jsx');
const NewBrewItem = require('../../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../../navbar/help.navitem.jsx');
const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both;
const Account = require('../../../navbar/account.navitem.jsx');
const UIPage = createClass({
displayName : 'UIPage',
render : function(){
return <div className='uiPage sitePage'>
<Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{this.props.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
<NewBrewItem />
<HelpNavItem />
<RecentNavItem />
<Account />
</Nav.section>
</Navbar>
<div className='content'>
{this.props.children}
</div>
</div>;
}
});
module.exports = UIPage;

View File

@@ -0,0 +1,47 @@
.uiPage{
.content{
overflow-y : hidden;
width : 90vw;
background-color: #f0f0f0;
font-family: 'Open Sans';
margin-left: auto;
margin-right: auto;
margin-top: 25px;
padding: 2% 4%;
font-size: 0.8em;
line-height: 1.8em;
.dataGroup{
padding: 6px 20px 15px;
border: 2px solid black;
border-radius: 5px;
margin: 5px 0px;
}
h1, h2, h3, h4{
font-weight: 900;
text-transform: uppercase;
margin: 0.5em 30% 0.25em 0;
border-bottom: 2px solid slategrey;
}
h1 {
font-size: 2em;
border-bottom: 2px solid darkslategrey;
margin-bottom: 0.5em;
margin-right: 0;
}
h2 {
font-size: 1.75em;
}
h3 {
font-size: 1.5em;
svg {
width: 19px;
}
}
h4 {
font-size: 1.25em;
}
strong {
font-weight: bold;
}
}
}

View File

@@ -78,7 +78,7 @@ const EditPage = createClass({
this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy
this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) }, ()=>{ this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) ?? true }, ()=>{
if(this.state.autoSave){ if(this.state.autoSave){
this.trySave(); this.trySave();
} else { } else {

View File

@@ -1,28 +1,31 @@
module.exports = async(name, title = '', props = {})=>{ const template = async function(name, title='', props = {}){
const HOMEBREWERY_PUBLIC_URL=props.config.publicUrl; const ogTags = [];
const ogMeta = props.ogMeta ?? {};
Object.entries(ogMeta).forEach(([key, value])=>{
if(!value) return;
const tag = `<meta property="og:${key}" content="${value}">`;
ogTags.push(tag);
});
const ogMetaTags = ogTags.join('\n');
return ` return `<!DOCTYPE html>
<!DOCTYPE html> <html>
<html> <head>
<head> <link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" /> <link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" /> <link href=${`/${name}/bundle.css`} rel='stylesheet' />
<link href=${`/${name}/bundle.css`} rel='stylesheet' /> <link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" /> ${ogMetaTags}
<meta property="og:title" content="${props.brew?.title || 'Homebrewery - Untitled Brew'}"> <meta name="twitter:card" content="summary">
<meta property="og:url" content="${HOMEBREWERY_PUBLIC_URL}/${props.brew?.shareId ? `share/${props.brew.shareId}` : ''}"> <title>${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}</title>
<meta property="og:image" content="${props.brew?.thumbnail || `${HOMEBREWERY_PUBLIC_URL}/thumbnail.png`}"> </head>
<meta property="og:description" content="${props.brew?.description || 'No description.'}"> <body>
<meta property="og:site_name" content="The Homebrewery - Make your Homebrew content look legit!"> <main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main>
<meta property="og:type" content="article"> <script src=${`/${name}/bundle.js`}></script>
<meta name="twitter:card" content="summary_large_image"> <script>start_app(${JSON.stringify(props)})</script>
<title>${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}</title> </body>
</head> </html>
<body> `;
<main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main>
<script src=${`/${name}/bundle.js`}></script>
<script>start_app(${JSON.stringify(props)})</script>
</body>
</html>
`;
}; };
module.exports = template;

17680
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "homebrewery", "name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown", "description": "Create authentic looking D&D homebrews using only markdown",
"version": "3.3.0", "version": "3.3.1",
"engines": { "engines": {
"node": "16.11.x" "node": "16.11.x"
}, },
@@ -60,27 +60,28 @@
"codemirror": "^5.65.6", "codemirror": "^5.65.6",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"create-react-class": "^15.7.0", "create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.1", "dedent-tabs": "^0.10.2",
"express": "^4.18.2", "express": "^4.18.2",
"express-async-handler": "^1.2.0", "express-async-handler": "^1.2.0",
"express-static-gzip": "2.1.7", "express-static-gzip": "2.1.7",
"fs-extra": "10.1.0", "fs-extra": "10.1.0",
"googleapis": "108.0.0", "googleapis": "109.0.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"less": "^3.13.1", "less": "^3.13.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"marked": "4.1.1", "marked": "4.2.2",
"marked-extended-tables": "^1.0.5", "marked-extended-tables": "^1.0.5",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.4", "moment": "^2.29.4",
"mongoose": "^6.7.0", "mongoose": "^6.7.0",
"nanoid": "3.3.4", "nanoid": "3.3.4",
"nconf": "^0.12.0", "nconf": "^0.12.0",
"npm": "^8.10.0",
"react": "^16.14.0", "react": "^16.14.0",
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-frame-component": "4.1.3", "react-frame-component": "4.1.3",
"react-router-dom": "6.4.2", "react-router-dom": "6.4.3",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"superagent": "^6.1.0", "superagent": "^6.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git" "vitreum": "git+https://git@github.com/calculuschild/vitreum.git"

View File

@@ -1,4 +1,4 @@
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ /*eslint max-lines: ["warn", {"max": 400, "skipBlankLines": true, "skipComments": true}]*/
// Set working directory to project root // Set working directory to project root
process.chdir(`${__dirname}/..`); process.chdir(`${__dirname}/..`);
@@ -87,16 +87,32 @@ app.get('/', (req, res, next)=>{
req.brew = { req.brew = {
text : welcomeText, text : welcomeText,
renderer : 'V3' renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'Homepage',
description : 'Homepage',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
}; };
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
return next(); return next();
}); });
//Home page v3 //Home page Legacy
app.get('/legacy', (req, res, next)=>{ app.get('/legacy', (req, res, next)=>{
req.brew = { req.brew = {
text : welcomeTextLegacy, text : welcomeTextLegacy,
renderer : 'legacy' renderer : 'legacy'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'Homepage (Legacy)',
description : 'Homepage',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
}; };
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
return next(); return next();
@@ -107,6 +123,14 @@ app.get('/migrate', (req, res, next)=>{
req.brew = { req.brew = {
text : migrateText, text : migrateText,
renderer : 'V3' renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'v3 Migration Guide',
description : 'A brief guide to converting Legacy documents to the v3 renderer.',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
}; };
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
return next(); return next();
@@ -118,6 +142,14 @@ app.get('/changelog', async (req, res, next)=>{
title : 'Changelog', title : 'Changelog',
text : changelogText, text : changelogText,
renderer : 'V3' renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'Changelog',
description : 'Development changelog.',
thumbnail : null,
type : 'website'
}; };
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
return next(); return next();
@@ -129,7 +161,16 @@ app.get('/faq', async (req, res, next)=>{
title : 'FAQ', title : 'FAQ',
text : faqText, text : faqText,
renderer : 'V3' renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'FAQ',
description : 'Frequently Asked Questions',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
}; };
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
return next(); return next();
}); });
@@ -167,6 +208,14 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
app.get('/user/:username', async (req, res, next)=>{ app.get('/user/:username', async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username); const ownAccount = req.account && (req.account.username == req.params.username);
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : `${req.params.username}'s Collection`,
description : 'View my collection of homebrew on the Homebrewery.',
image : null,
type : 'website' // or 'profile'?
};
const fields = [ const fields = [
'googleId', 'googleId',
'title', 'title',
@@ -224,6 +273,14 @@ app.get('/user/:username', async (req, res, next)=>{
//Edit Page //Edit Page
app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{ app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{
req.brew = req.brew.toObject ? req.brew.toObject() : req.brew; req.brew = req.brew.toObject ? req.brew.toObject() : req.brew;
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
image : req.brew.thumbnail || null,
type : 'article'
};
sanitizeBrew(req.brew, 'edit'); sanitizeBrew(req.brew, 'edit');
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
@@ -235,6 +292,13 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
sanitizeBrew(req.brew, 'share'); sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew); splitTextStyleAndMetadata(req.brew);
req.brew.title = `CLONE - ${req.brew.title}`; req.brew.title = `CLONE - ${req.brew.title}`;
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'New',
description : 'Start crafting your homebrew on the Homebrewery!',
image : null,
type : 'website'
};
return next(); return next();
}); });
@@ -242,6 +306,14 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
const { brew } = req; const { brew } = req;
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
image : req.brew.thumbnail || null,
type : 'article'
};
if(req.params.id.length > 12 && !brew._id) { if(req.params.id.length > 12 && !brew._id) {
const googleId = req.params.id.slice(0, -12); const googleId = req.params.id.slice(0, -12);
const shareId = req.params.id.slice(-12); const shareId = req.params.id.slice(-12);
@@ -262,6 +334,54 @@ app.get('/print/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
next(); next();
}); });
//Account Page
app.get('/account', asyncHandler(async (req, res, next)=>{
const data = {};
data.title = 'Account Information Page';
let auth;
let files;
if(req.account) {
if(req.account.googleId) {
try {
auth = await GoogleActions.authCheck(req.account, res);
} catch (e) {
auth = undefined;
console.log('Google auth check failed!');
console.log(e);
}
if(auth.credentials.access_token) {
try {
files = await GoogleActions.listGoogleBrews(auth);
} catch (e) {
files = undefined;
console.log('List Google files failed!');
console.log(e);
}
}
}
const query = { authors: req.account.username, googleId: { $exists: false } };
const brews = await HomebrewModel.find(query, 'id')
.catch((err)=>{
console.log(err);
});
data.uiItems = {
username : req.account.username,
issued : req.account.issued,
mongoCount : brews.length,
googleId : Boolean(req.account.googleId),
authCheck : Boolean(req.account.googleId && auth.credentials.access_token),
fileCount : files?.length || '-'
};
}
req.brew = data;
return next();
}));
const nodeEnv = config.get('node_env'); const nodeEnv = config.get('node_env');
const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); const isLocalEnvironment = config.get('local_environments').includes(nodeEnv);
// Local only // Local only
@@ -276,8 +396,6 @@ if(isLocalEnvironment){
}); });
} }
//Render the page //Render the page
const templateFn = require('./../client/template.js'); const templateFn = require('./../client/template.js');
app.use(asyncHandler(async (req, res, next)=>{ app.use(asyncHandler(async (req, res, next)=>{
@@ -296,7 +414,8 @@ app.use(asyncHandler(async (req, res, next)=>{
account : req.account, account : req.account,
enable_v3 : config.get('enable_v3'), enable_v3 : config.get('enable_v3'),
enable_themes : config.get('enable_themes'), enable_themes : config.get('enable_themes'),
config : configuration config : configuration,
ogMeta : req.ogMeta
}; };
const title = req.brew ? req.brew.title : ''; const title = req.brew ? req.brew.title : '';
const page = await templateFn('homebrew', title, props) const page = await templateFn('homebrew', title, props)

View File

@@ -32,7 +32,7 @@ const mustacheSpans = {
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) { tokenizer(src, tokens) {
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
const inlineRegex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g; const inlineRegex = /{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *|}}/g;
const match = completeSpan.exec(src); const match = completeSpan.exec(src);
if(match) { if(match) {
//Find closing delimiter //Find closing delimiter
@@ -82,7 +82,7 @@ const mustacheDivs = {
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) { tokenizer(src, tokens) {
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
const blockRegex = /^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/gm; const blockRegex = /^ *{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *$|^ *}}$/gm;
const match = completeBlock.exec(src); const match = completeBlock.exec(src);
if(match) { if(match) {
//Find closing delimiter //Find closing delimiter
@@ -130,7 +130,7 @@ const mustacheInjectInline = {
level : 'inline', level : 'inline',
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) { tokenizer(src, tokens) {
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/g; const inlineRegex = /^ *{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1}/g;
const match = inlineRegex.exec(src); const match = inlineRegex.exec(src);
if(match) { if(match) {
const lastToken = tokens[tokens.length - 1]; const lastToken = tokens[tokens.length - 1];
@@ -165,7 +165,7 @@ const mustacheInjectBlock = {
level : 'block', level : 'block',
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) { tokenizer(src, tokens) {
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/ym; const inlineRegex = /^ *{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1}/ym;
const match = inlineRegex.exec(src); const match = inlineRegex.exec(src);
if(match) { if(match) {
const lastToken = tokens[tokens.length - 1]; const lastToken = tokens[tokens.length - 1];

View File

@@ -9,8 +9,8 @@ describe('Tests for static pages', ()=>{
return app.get('/').expect(200); return app.get('/').expect(200);
}); });
it('Home page v3 works', ()=>{ it('Home page legacy works', ()=>{
return app.get('/v3_preview').expect(200); return app.get('/legacy').expect(200);
}); });
it('Changelog page works', ()=>{ it('Changelog page works', ()=>{

View File

@@ -82,7 +82,7 @@ body {
// * BASE // * BASE
// *****************************/ // *****************************/
:where(.page){ .page{
p{ p{
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
display : block; display : block;
@@ -214,6 +214,7 @@ body {
table{ table{
.useSansSerif(); .useSansSerif();
width : 100%; width : 100%;
line-height : 16px;
& + * { & + * {
margin-top : 0.325cm; margin-top : 0.325cm;
} }
@@ -224,7 +225,7 @@ body {
vertical-align : bottom; vertical-align : bottom;
//padding : 0.14em 0.4em; //padding : 0.14em 0.4em;
padding : 0px 1.5px; // Both of these are temporary, just to force padding : 0px 1.5px; // Both of these are temporary, just to force
line-height : 16px; // PDF to render at same height until Chrome 108 //line-height : 16px; // PDF to render at same height until Chrome 108
} }
} }
tbody{ tbody{
@@ -232,7 +233,7 @@ body {
td{ td{
//padding : 0.14em 0.4em; //padding : 0.14em 0.4em;
padding : 0px 1.5px; // Both of these are temporary, just to force padding : 0px 1.5px; // Both of these are temporary, just to force
line-height : 16px; // PDF to render at same height until Chrome 108 //line-height : 16px; // PDF to render at same height until Chrome 108
} }
&:nth-child(odd){ &:nth-child(odd){
background-color : var(--HB_Color_Accent); background-color : var(--HB_Color_Accent);
@@ -240,9 +241,6 @@ body {
} }
} }
} }
div table:has(~table) { // Divs with side-by-side tables add an extra line
margin-bottom: -0.325cm; // of vertical space at bottom. This works around it.
}
//***************************** //*****************************
// * NOTE // * NOTE
// *****************************/ // *****************************/
@@ -598,7 +596,7 @@ body {
//***************************** //*****************************
// * SPELL LIST // * SPELL LIST
// *****************************/ // *****************************/
:where(.page) .spellList{ .page .spellList{
.useSansSerif(); .useSansSerif();
column-count : 2; column-count : 2;
ul+h5{ ul+h5{
@@ -625,7 +623,7 @@ body {
//***************************** //*****************************
// * CLASS TABLE // * CLASS TABLE
// *****************************/ // *****************************/
:where(.page) .classTable{ .page .classTable{
th[colspan]:not([rowspan]) { th[colspan]:not([rowspan]) {
white-space : nowrap; white-space : nowrap;
} }
@@ -682,7 +680,7 @@ body {
//***************************** //*****************************
// * TABLE OF CONTENTS // * TABLE OF CONTENTS
// *****************************/ // *****************************/
:where(.page) { .page {
&:has(.toc):after { &:has(.toc):after {
display: none; display: none;
} }
@@ -759,7 +757,7 @@ body {
//***************************** //*****************************
// * DEFINITION LISTS // * DEFINITION LISTS
// *****************************/ // *****************************/
:where(.page) { .page {
dl { dl {
line-height : 1.25em; line-height : 1.25em;
padding-left : 1em; padding-left : 1em;
@@ -789,10 +787,10 @@ body {
//***************************** //*****************************
// * WIDE // * WIDE
// *****************************/ // *****************************/
:where(.page) .wide{ .page .wide{
margin-bottom : 0.325cm; margin-bottom : 0.325cm;
} }
:where(.page) h1 + *{ .page h1 + *{
margin-top : 0; margin-top : 0;
} }

View File

@@ -43,7 +43,7 @@ body {
//***************************** //*****************************
// * BASE // * BASE
// *****************************/ // *****************************/
:where(.page){ .page{
p{ p{
overflow-wrap : break-word; overflow-wrap : break-word;
display : block; display : block;
@@ -77,13 +77,7 @@ body {
img{ img{
z-index : -1; z-index : -1;
} }
:not(:where(.wide,.columnSplit,.blank,hr,h1)) + :where(h2,h3,h4,h5,h6,table,dl,.block) {
margin-top : 0.325cm; //NOTE: MAKE ALL MARGINS TOP-ONLY FOR BEST RESULTS WITH COLUMN BREAKS. USE * + * STYLE SELECTORS
}
:is(h1,h3,h3,h4,h5,h6) + * {
margin-top : 0;
}
//***************************** //*****************************
// * HEADERS // * HEADERS
// *****************************/ // *****************************/
@@ -116,6 +110,9 @@ body {
font-weight : bold; font-weight : bold;
} }
} }
div:not(.columnWrapper) > table + table { // Side-by-side tables should not
margin-top : 0; // have vertical spacing.
}
/* Watermark */ /* Watermark */
.watermark { .watermark {
@@ -218,7 +215,7 @@ body {
//***************************** //*****************************
// * MUSTACHE DIVS/SPANS // * MUSTACHE DIVS/SPANS
// *****************************/ // *****************************/
:where(.page) { .page {
.block { .block {
break-inside : avoid; break-inside : avoid;
display : inline-block; display : inline-block;
@@ -233,7 +230,7 @@ body {
//***************************** //*****************************
// * DEFINITION LISTS // * DEFINITION LISTS
// *****************************/ // *****************************/
:where(.page) { .page {
dl { dl {
padding-left : 1em; padding-left : 1em;
white-space : pre-line; white-space : pre-line;
@@ -253,17 +250,20 @@ body {
//***************************** //*****************************
// * BLANK LINE // * BLANK LINE
// *****************************/ // *****************************/
:where(.page) { .page {
.blank { .blank {
height : 1em; height : 1em;
margin-top : 0; margin-top : 0;
& + * {
margin-top : 0;
}
} }
} }
//***************************** //*****************************
// * WIDE // * WIDE
// *****************************/ // *****************************/
:where(.page) { .page {
.wide{ .wide{
column-span : all; column-span : all;
display : block; display : block;

View File

@@ -62,7 +62,7 @@
//***************************** //*****************************
// * BASE // * BASE
// *****************************/ // *****************************/
:where(.page){ .page{
color : var(--HB_Color_Text); color : var(--HB_Color_Text);
font-family : ReenieBeanie; font-family : ReenieBeanie;
font-size : 0.53cm; font-size : 0.53cm;
@@ -554,6 +554,6 @@
//***************************** //*****************************
// * WIDE // * WIDE
// *****************************/ // *****************************/
:where(.page) .wide { .page .wide {
margin-bottom : 0.45cm; margin-bottom : 0.45cm;
} }