Moved menu to the DB and started modernizing old code

react_convert
Mark Moffat 2018-01-14 18:02:10 +01:00
parent ceb23e1ce1
commit ecef3b8224
7 changed files with 222 additions and 195 deletions

View File

@ -6,7 +6,7 @@
"promise"
],
"parserOptions": {
"ecmaVersion": 6
"ecmaVersion": 2017
},
"globals": {
"$": true

20
app.js
View File

@ -260,16 +260,22 @@ app.use((err, req, res, next) => {
});
});
// Nodejs version check
if(parseInt(process.version.split('.')[0].replace('v', '')) <= 7){
console.log(colors.red('Please use Node.js version 7.x or above'));
process.exit(2);
}
app.on('uncaughtException', (err) => {
console.error(colors.red(err.stack));
process.exit();
process.exit(2);
});
MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
// On connection error we display then exit
if(err){
console.log(colors.red('Error connecting to MongoDB: ' + err));
process.exit();
process.exit(2);
}
// select DB
@ -281,22 +287,22 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
db.products = db.collection('products');
db.orders = db.collection('orders');
db.pages = db.collection('pages');
db.menu = db.collection('menu');
// add db to app for routes
app.db = db;
// add indexing
common.runIndexing(app)
.then(common.testData(db))
.then(common.testData(db, app))
.then(app.listen(app.get('port')))
.then(() => {
// lift the app
app.listen(app.get('port'), () => {
console.log(colors.green('expressCart running on host: http://localhost:' + app.get('port')));
});
console.log(colors.green('expressCart running on host: http://localhost:' + app.get('port')));
})
.catch(() => {
console.error(colors.red('Error setting up indexes:' + err));
process.exit();
process.exit(2);
});
});

View File

@ -1,67 +1,83 @@
[
{
"productPermalink": "duckworth-jacket",
"productTitle": "Duckworth Woolfill Jacket",
"productPrice": "188.00",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">Inspired by the timeless, functional style of your grandfather's work coat, the Foraker features brass buttons and 4 patch pockets. Crafted in Bristol, Tennessee, our 10oz organic duck canvas is light enough for an early summer morning, but rugged enough to handle your days work.<\/p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">100% Organic Duck Canvas.<\/li><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-05-01T11:22:49.442Z"
{
"products":[
{
"productPermalink": "duckworth-jacket",
"productTitle": "Duckworth Woolfill Jacket",
"productPrice": "188.00",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">Inspired by the timeless, functional style of your grandfather's work coat, the Foraker features brass buttons and 4 patch pockets. Crafted in Bristol, Tennessee, our 10oz organic duck canvas is light enough for an early summer morning, but rugged enough to handle your days work.<\/p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">100% Organic Duck Canvas.<\/li><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-05-01T11:22:49.442Z"
},
"productTags": "organic, jacket",
"productOptions": "{\"Size\":{\"optName\":\"Size\",\"optLabel\":\"Select size\",\"optType\":\"select\",\"optOptions\":[\"S\",\"M\",\"L\",\"XL\"]},\"Colour\":{\"optName\":\"Colour\",\"optLabel\":\"Select colour\",\"optType\":\"select\",\"optOptions\":[\"Harvest\",\"Navy\"]}}",
"productImage": "/uploads/duckworth-jacket/woolfill-jacket_6c39ae23-c0c8-4821-85f4-4b5d64333c62_grande.jpg"
},
"productTags": "organic, jacket",
"productOptions": "{\"Size\":{\"optName\":\"Size\",\"optLabel\":\"Select size\",\"optType\":\"select\",\"optOptions\":[\"S\",\"M\",\"L\",\"XL\"]},\"Colour\":{\"optName\":\"Colour\",\"optLabel\":\"Select colour\",\"optType\":\"select\",\"optOptions\":[\"Harvest\",\"Navy\"]}}",
"productImage": "/uploads/duckworth-jacket/woolfill-jacket_6c39ae23-c0c8-4821-85f4-4b5d64333c62_grande.jpg"
},
{
"productPermalink": "5-panel-cap",
"productTitle": "5 Panel Camp Cap",
"productPrice": "48",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">A classic 5 panel hat with our United By Blue logo on the front and an adjustable strap to keep fit and secure. Made with recycled polyester and organic cotton mix.<\/p><ul style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">Made in&nbsp;New Jersey<\/li><li style=\"margin-bottom: 0px;\">7oz Eco-Twill fabric:&nbsp;35% organic cotton, 65% recycled PET&nbsp;(plastic water and soda bottles)&nbsp;<\/li><li style=\"margin-bottom: 0px;\">Embossed leather patch<\/li><\/ul><ul class=\"tabs\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility; color: rgb(28, 29, 29); font-family: Arapey, serif; line-height: 25.008px;\"><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-07-10T05:01:15.744Z"
{
"productPermalink": "5-panel-cap",
"productTitle": "5 Panel Camp Cap",
"productPrice": "48",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">A classic 5 panel hat with our United By Blue logo on the front and an adjustable strap to keep fit and secure. Made with recycled polyester and organic cotton mix.<\/p><ul style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">Made in&nbsp;New Jersey<\/li><li style=\"margin-bottom: 0px;\">7oz Eco-Twill fabric:&nbsp;35% organic cotton, 65% recycled PET&nbsp;(plastic water and soda bottles)&nbsp;<\/li><li style=\"margin-bottom: 0px;\">Embossed leather patch<\/li><\/ul><ul class=\"tabs\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility; color: rgb(28, 29, 29); font-family: Arapey, serif; line-height: 25.008px;\"><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-07-10T05:01:15.744Z"
},
"productTags": "panel, cap",
"productOptions": "{\"colour\":{\"optName\":\"colour\",\"optLabel\":\"Select colour\",\"optType\":\"select\",\"optOptions\":[\"Heather green\",\"Burnt orange\",\"Slate grey\",\"Navy blue\"]}}",
"productImage": "/uploads/5-panel-cap/5-panel-hat_4ee20a27-8d5a-490e-a2fc-1f9c3beb7bf5_grande.jpg"
},
"productTags": "panel, cap",
"productOptions": "{\"colour\":{\"optName\":\"colour\",\"optLabel\":\"Select colour\",\"optType\":\"select\",\"optOptions\":[\"Heather green\",\"Burnt orange\",\"Slate grey\",\"Navy blue\"]}}",
"productImage": "/uploads/5-panel-cap/5-panel-hat_4ee20a27-8d5a-490e-a2fc-1f9c3beb7bf5_grande.jpg"
},
{
"productPermalink": "ranger-boot",
"productTitle": "Red Wing Iron Ranger Boot",
"productPrice": "310",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">The Mesabi Iron Range lies in northern Minnesota, a rugged and remote area known for its iron mines. The local residents who work these mines are proudly known as Iron Rangers, individuals with a sense of adventure and a determined personality. Originally designed to be worn in the iron mines, Iron Ranger work boots had to be as tough as the people who wore them in demanding conditions. Iron Ranger boots are built with a double layer of leaver over the toe to provide an extra measure of safety.<\/p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">6 inch, Amber Harness leather with Nitrile Cork sole.<\/li><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-07-10T05:29:04.941Z"
{
"productPermalink": "ranger-boot",
"productTitle": "Red Wing Iron Ranger Boot",
"productPrice": "310",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">The Mesabi Iron Range lies in northern Minnesota, a rugged and remote area known for its iron mines. The local residents who work these mines are proudly known as Iron Rangers, individuals with a sense of adventure and a determined personality. Originally designed to be worn in the iron mines, Iron Ranger work boots had to be as tough as the people who wore them in demanding conditions. Iron Ranger boots are built with a double layer of leaver over the toe to provide an extra measure of safety.<\/p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">6 inch, Amber Harness leather with Nitrile Cork sole.<\/li><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-07-10T05:29:04.941Z"
},
"productTags": "ranger, boot, leather",
"productImage": "/uploads/ranger-boot/boot_grande.jpg",
"productOptions": "{\"size\":{\"optName\":\"size\",\"optLabel\":\"Select size\",\"optType\":\"select\",\"optOptions\":[\"7.5\",\"8\",\"8.5\",\"9\",\"9.5\",\"10\",\"10.5\",\"11\"]}}"
},
"productTags": "ranger, boot, leather",
"productImage": "/uploads/ranger-boot/boot_grande.jpg",
"productOptions": "{\"size\":{\"optName\":\"size\",\"optLabel\":\"Select size\",\"optType\":\"select\",\"optOptions\":[\"7.5\",\"8\",\"8.5\",\"9\",\"9.5\",\"10\",\"10.5\",\"11\"]}}"
},
{
"productPermalink": "whitney-pullover",
"productTitle": "Whitney pullover",
"productPrice": "138",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">Buttons are fussy. Sometimes you just want to roll out of bed, put on the pull over and get to the days work.&nbsp;Julie is 5'8\" and wearing a size small.<\/p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">100% Wool, Heavy 4 gauge thickness<\/li><li style=\"margin-bottom: 0px;\">Handmade in Nepal.<\/li><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-05-01T11:23:08.374Z"
{
"productPermalink": "whitney-pullover",
"productTitle": "Whitney pullover",
"productPrice": "138",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">Buttons are fussy. Sometimes you just want to roll out of bed, put on the pull over and get to the days work.&nbsp;Julie is 5'8\" and wearing a size small.<\/p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">100% Wool, Heavy 4 gauge thickness<\/li><li style=\"margin-bottom: 0px;\">Handmade in Nepal.<\/li><\/ul>",
"productPublished": "true",
"productAddedDate": {
"$date": "2016-05-01T11:23:08.374Z"
},
"productImage": "/uploads/whitney-pullover/WhitneyPullover_Full_58e7b8d6-b939-4701-9e1d-9d853dff60ed_grande.jpeg",
"productTags": "whitney, pullover",
"productOptions": "{\"size\":{\"optName\":\"size\",\"optLabel\":\"Select size\",\"optType\":\"select\",\"optOptions\":[\"S\",\"M\",\"L\",\"XL\"]}}"
},
"productImage": "/uploads/whitney-pullover/WhitneyPullover_Full_58e7b8d6-b939-4701-9e1d-9d853dff60ed_grande.jpeg",
"productTags": "whitney, pullover",
"productOptions": "{\"size\":{\"optName\":\"size\",\"optLabel\":\"Select size\",\"optType\":\"select\",\"optOptions\":[\"S\",\"M\",\"L\",\"XL\"]}}"
},
{
"productPermalink": "scout-backpack",
"productTitle": "Scout Backpack",
"productPrice": "128.00",
"productDescription": "<p><span style=\"line-height: 1.42857;\">This durable backpack is ready for any adventure, large or small. Features adjustable and padded shoulder pads for comfort. Designed with a storm flap and a secured by two snap-button closure. Made with a waxed downpour proof exterior canvas and a soft cotton interior lining. Finished with brass hardware and genuine leather trimmings.<\/span><\/p><ul><li><span style=\"line-height: 1.42857;\">100% organic waxed 18 oz canvas<\/span><\/li><li><span style=\"line-height: 1.42857;\">Full grain genuine leather accents<\/span><\/li><li>Adjustable shoulder straps<\/li><li>Lifetime&amp;nbsp;Guarantee<\/li><\/ul>",
"productPublished": "true",
"productTags": "backpack, organic",
"productAddedDate": {
"$date": "2016-07-10T05:46:26.974Z"
},
"productImage": "/uploads/scout-backpack/scout-backpack_a035275d-8975-4a05-8456-5e1ec35f020f_grande.jpg",
"productOptions": "{\"colour\":{\"optName\":\"colour\",\"optLabel\":\"Select colour\",\"optType\":\"select\",\"optOptions\":[\"Navy\",\"Moss\",\"Nutmeg\",\"Khaki\"]}}"
{
"productPermalink": "scout-backpack",
"productTitle": "Scout Backpack",
"productPrice": "128.00",
"productDescription": "<p><span style=\"line-height: 1.42857;\">This durable backpack is ready for any adventure, large or small. Features adjustable and padded shoulder pads for comfort. Designed with a storm flap and a secured by two snap-button closure. Made with a waxed downpour proof exterior canvas and a soft cotton interior lining. Finished with brass hardware and genuine leather trimmings.<\/span><\/p><ul><li><span style=\"line-height: 1.42857;\">100% organic waxed 18 oz canvas<\/span><\/li><li><span style=\"line-height: 1.42857;\">Full grain genuine leather accents<\/span><\/li><li>Adjustable shoulder straps<\/li><li>Lifetime&amp;nbsp;Guarantee<\/li><\/ul>",
"productPublished": "true",
"productTags": "backpack, organic",
"productAddedDate": {
"$date": "2016-07-10T05:46:26.974Z"
},
"productImage": "/uploads/scout-backpack/scout-backpack_a035275d-8975-4a05-8456-5e1ec35f020f_grande.jpg",
"productOptions": "{\"colour\":{\"optName\":\"colour\",\"optLabel\":\"Select colour\",\"optType\":\"select\",\"optOptions\":[\"Navy\",\"Moss\",\"Nutmeg\",\"Khaki\"]}}"
}
],
"menu": {
"items": [
{
"title" : "Backpacks",
"link" : "backpack",
"order" : 0
},
{
"title" : "Boots",
"link" : "boots",
"order" : 1
}
]
}
]
}

View File

@ -359,7 +359,6 @@ router.get('/product/new', common.restrict, (req, res) => {
// insert new product form action
router.post('/product/insert', common.restrict, (req, res) => {
let db = req.app.db;
let config = common.getConfig();
let doc = {
productPermalink: req.body.frmProductPermalink,
@ -852,7 +851,8 @@ router.post('/settings/option/remove', common.restrict, (req, res) => {
});
// settings update
router.get('/settings/menu', common.restrict, (req, res) => {
router.get('/settings/menu', common.restrict, async (req, res) => {
let db = req.app.db;
res.render('settings_menu', {
title: 'Cart menu',
session: req.session,
@ -861,17 +861,18 @@ router.get('/settings/menu', common.restrict, (req, res) => {
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
menu: common.getMenu().items
menu: common.sortMenu(await common.getMenu(db))
});
});
// settings page list
router.get('/settings/pages', common.restrict, (req, res) => {
let db = req.app.db;
common.dbQuery(db.pages, {}, null, null, (err, pages) => {
common.dbQuery(db.pages, {}, null, null, async (err, pages) => {
if(err){
console.info(err.stack);
}
res.render('settings_pages', {
title: 'Static pages',
pages: pages,
@ -881,13 +882,15 @@ router.get('/settings/pages', common.restrict, (req, res) => {
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
menu: common.getMenu().items
menu: common.sortMenu(await common.getMenu(db))
});
});
});
// settings pages new
router.get('/settings/pages/new', common.restrict, (req, res) => {
router.get('/settings/pages/new', common.restrict, async (req, res) => {
let db = req.app.db;
res.render('settings_page_edit', {
title: 'Static pages',
session: req.session,
@ -897,18 +900,19 @@ router.get('/settings/pages/new', common.restrict, (req, res) => {
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
menu: common.getMenu().items
menu: common.sortMenu(await common.getMenu(db))
});
});
// settings pages editor
router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
let db = req.app.db;
db.pages.findOne({_id: common.getId(req.params.page)}, (err, page) => {
db.pages.findOne({_id: common.getId(req.params.page)}, async (err, page) => {
if(err){
console.info(err.stack);
}
// page found
const menu = common.sortMenu(await common.getMenu(db));
if(page){
res.render('settings_page_edit', {
title: 'Static pages',
@ -920,7 +924,7 @@ router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
menu: common.getMenu().items
menu
});
}else{
// 404 it!
@ -930,9 +934,8 @@ router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
message: '404 Error - Page not found',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
}
);
menu
});
}
});
});

View File

@ -4,6 +4,7 @@ const colors = require('colors');
const lunr = require('lunr');
const fs = require('fs');
const escape = require('html-entities').AllHtmlEntities;
let ObjectId = require('mongodb').ObjectID;
// common functions
exports.checkLogin = function(req, res, next){
@ -229,77 +230,99 @@ exports.updateConfig = function(fields){
}
};
exports.getMenu = function(){
let fs = require('fs');
let path = require('path');
let menuFile = JSON.parse(fs.readFileSync(path.join(__dirname, '../config/menu.json'), 'utf8'));
menuFile.items = _.sortBy(menuFile.items, 'order');
return menuFile;
exports.getMenu = function(db){
return db.menu.findOne({});
};
// creates a new menu item
exports.newMenu = function(req, res){
let fs = require('fs');
let path = require('path');
let menuJson = '../config/menu.json';
let menuFile = require(menuJson);
const db = req.app.db;
return exports.getMenu(db)
.then((menu) => {
// if no menu present
if(!menu){
menu = {};
menu.items = [];
}
let newNav = {
title: req.body.navMenu,
link: req.body.navLink,
order: Object.keys(menu.items).length + 1
};
let newNav = {
title: req.body.navMenu,
link: req.body.navLink,
order: Object.keys(menuFile.items).length + 1
};
// add new menu item
menuFile.items.push(newNav);
// write file
try{
fs.writeFileSync(path.join(__dirname, '../config/menu.json'), JSON.stringify(menuFile));
return true;
}catch(e){
menu.items.push(newNav);
return db.menu.updateOne({}, {$set: {items: menu.items}}, {upsert: true})
.then(() => {
return true;
});
})
.catch((err) => {
console.log('Error creating new menu', err);
return false;
}
});
};
// delete a menu item
exports.deleteMenu = function(req, res, menuIndex){
let fs = require('fs');
let path = require('path');
let menuJson = '../config/menu.json';
let menuFile = require(menuJson);
delete menuFile.items[menuIndex];
// write file
try{
fs.writeFileSync(path.join(__dirname, '../config/menu.json'), JSON.stringify(menuFile));
return true;
}catch(e){
const db = req.app.db;
return exports.getMenu(db)
.then((menu) => {
// Remove menu item
menu.items.splice(menuIndex, 1);
return db.menu.updateOne({}, {$set: {items: menu.items}}, {upsert: true})
.then(() => {
return true;
});
})
.catch(() => {
return false;
}
});
};
// updates and existing menu item
exports.updateMenu = function(req, res){
let fs = require('fs');
let path = require('path');
let menuJson = '../config/menu.json';
let menuFile = require(menuJson);
// find menu item and update it
let menuIndex = _.findIndex(menuFile.items, ['title', req.body.navId]);
menuFile.items[menuIndex].title = req.body.navMenu;
menuFile.items[menuIndex].link = req.body.navLink;
// write file
try{
fs.writeFileSync(path.join(__dirname, '../config/menu.json'), JSON.stringify(menuFile));
return true;
}catch(e){
const db = req.app.db;
return exports.getMenu(db)
.then((menu) => {
// find menu item and update it
let menuIndex = _.findIndex(menu.items, ['title', req.body.navId]);
menu.items[menuIndex].title = req.body.navMenu;
menu.items[menuIndex].link = req.body.navLink;
return db.menu.updateOne({}, {$set: {items: menu.items}}, {upsert: true})
.then(() => {
return true;
});
})
.catch(() => {
return false;
});
};
exports.sortMenu = function(menu){
if(menu && menu.items){
menu.items = _.sortBy(menu.items, 'order');
return menu;
}
return{};
};
// orders the menu
exports.orderMenu = function(req, res){
const db = req.app.db;
return exports.getMenu(db)
.then((menu) => {
// update the order
for(let i = 0; i < req.body.navId.length; i++){
_.find(menu.items, ['title', req.body.navId[i]]).order = i;
}
return db.menu.updateOne({}, {$set: {items: menu.items}}, {upsert: true})
.then(() => {
return true;
});
})
.catch(() => {
return false;
});
};
exports.getEmailTemplate = function(result){
@ -360,36 +383,14 @@ exports.sendEmail = function(to, subject, body){
});
};
// orders the menu
exports.orderMenu = function(req, res){
let fs = require('fs');
let path = require('path');
let menuJson = '../config/menu.json';
let menuFile = require(menuJson);
// update the order
for(let i = 0; i < req.body.navId.length; i++){
_.find(menuFile.items, ['title', req.body.navId[i]]).order = i;
}
// write file
try{
fs.writeFileSync(path.join(__dirname, '../config/menu.json'), JSON.stringify(menuFile));
return true;
}catch(e){
return false;
}
};
// gets the correct type of index ID
exports.getId = function(id){
let ObjectID = require('mongodb').ObjectID;
if(id){
if(id.length !== 24){
return id;
}
}
return ObjectID(id);
return ObjectId(id);
};
// run the DB query
@ -496,7 +497,7 @@ exports.runIndexing = (app) => {
});
};
exports.testData = (db) => {
exports.testData = (db, app) => {
db.products.count({})
.then((products) => {
if(products > 0){
@ -505,9 +506,12 @@ exports.testData = (db) => {
console.info(colors.cyan('No products, inserting test data'));
const testdata = fs.readFileSync('./bin/testdata.json', 'utf-8');
const testData = fs.readFileSync('./bin/testdata.json', 'utf-8');
const jsonData = JSON.parse(testData);
return Promise.all([
db.products.insertMany(JSON.parse(testdata))
db.products.insertMany(jsonData.products),
db.menu.insertOne(jsonData.menu),
exports.runIndexing(app)
])
.catch((err) => {
console.info(colors.red('Error inserting test data. Check `/bin/testdata.json` is correctly formatted.', err));

View File

@ -4,12 +4,12 @@ const colors = require('colors');
const _ = require('lodash');
const common = require('./common');
router.get('/payment/:orderId', (req, res, next) => {
router.get('/payment/:orderId', async (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
// render the payment complete message
db.orders.findOne({_id: common.getId(req.params.orderId)}, (err, result) => {
db.orders.findOne({_id: common.getId(req.params.orderId)}, async (err, result) => {
if(err){
console.info(err.stack);
}
@ -23,12 +23,12 @@ router.get('/payment/:orderId', (req, res, next) => {
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
menu: common.sortMenu(await common.getMenu(db))
});
});
});
router.get('/checkout', (req, res, next) => {
router.get('/checkout', async (req, res, next) => {
let config = common.getConfig();
// if there is no items in the cart then render a failure
@ -50,12 +50,11 @@ router.get('/checkout', (req, res, next) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
showFooter: 'showFooter'
});
});
router.get('/pay', (req, res, next) => {
router.get('/pay', async (req, res, next) => {
let config = common.getConfig();
// if there is no items in the cart then render a failure
@ -78,8 +77,7 @@ router.get('/pay', (req, res, next) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
showFooter: 'showFooter'
});
});
@ -113,7 +111,7 @@ router.get('/product/:id', (req, res) => {
}
// show the view
common.getImages(result._id, req, res, (images) => {
common.getImages(result._id, req, res, async (images) => {
res.render(config.themeViews + 'product', {
title: result.productTitle,
result: result,
@ -129,7 +127,7 @@ router.get('/product/:id', (req, res) => {
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
menu: common.sortMenu(await common.getMenu(db))
});
});
}
@ -240,6 +238,7 @@ router.post('/login_action', (req, res) => {
// search products
router.get('/search/:searchTerm/:pageNum?', (req, res) => {
let db = req.app.db;
let searchTerm = req.params.searchTerm;
let productsIndex = req.app.productsIndex;
let config = common.getConfig();
@ -256,7 +255,7 @@ router.get('/search/:searchTerm/:pageNum?', (req, res) => {
}
// we search on the lunr indexes
getData(req, pageNum, {_id: {$in: lunrIdArray}}, (err, results) => {
getData(req, pageNum, {_id: {$in: lunrIdArray}}, async (err, results) => {
if(err){
console.error(colors.red('Error searching for products', err));
}
@ -276,7 +275,7 @@ router.get('/search/:searchTerm/:pageNum?', (req, res) => {
pageNum: pageNum,
paginateUrl: 'search',
config: config,
menu: common.getMenu(),
menu: common.sortMenu(await common.getMenu(db)),
helpers: req.handlebars.helpers,
showFooter: 'showFooter'
});
@ -285,6 +284,7 @@ router.get('/search/:searchTerm/:pageNum?', (req, res) => {
// search products
router.get('/category/:cat/:pageNum?', (req, res) => {
let db = req.app.db;
let searchTerm = req.params.cat;
let productsIndex = req.app.productsIndex;
let config = common.getConfig();
@ -292,18 +292,16 @@ router.get('/category/:cat/:pageNum?', (req, res) => {
let lunrIdArray = [];
productsIndex.search(searchTerm).forEach((id) => {
lunrIdArray.push(common.getId(id.ref))
lunrIdArray.push(common.getId(id.ref));
});
let menuLink = _.find(common.getMenu().items, (obj) => { return obj.link === searchTerm; });
let pageNum = 1;
if(req.params.pageNum){
pageNum = req.params.pageNum;
}
// we search on the lunr indexes
getData(req, pageNum, {_id: {$in: lunrIdArray}}, (err, results) => {
getData(req, pageNum, {_id: {$in: lunrIdArray}}, async (err, results) => {
if(err){
console.error(colors.red('Error getting products for category', err));
}
@ -321,10 +319,10 @@ router.get('/category/:cat/:pageNum?', (req, res) => {
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: pageNum,
menuLink: menuLink,
menuLink: _.find(common.sortMenu(await common.getMenu(db)).items, (obj) => { return obj.link === searchTerm; }),
paginateUrl: 'category',
config: config,
menu: common.getMenu(),
menu: common.sortMenu(await common.getMenu(db)),
helpers: req.handlebars.helpers,
showFooter: 'showFooter'
});
@ -365,10 +363,11 @@ router.get('/sitemap.xml', (req, res, next) => {
});
router.get('/page/:pageNum', (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
getData(req, req.params.pageNum, {}, (err, results) => {
getData(req, req.params.pageNum, {}, async (err, results) => {
if(err){
console.error(colors.red('Error getting products for page', err));
}
@ -388,7 +387,7 @@ router.get('/page/:pageNum', (req, res, next) => {
paginateUrl: 'page',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
menu: common.sortMenu(await common.getMenu(db))
});
});
});
@ -400,7 +399,7 @@ router.get('/:page?', (req, res, next) => {
// if no page is specified, just render page 1 of the cart
if(!req.params.page){
getData(req, 1, {}, (err, results) => {
getData(req, 1, {}, async (err, results) => {
if(err){
console.error(colors.red('Error getting products for page', err));
}
@ -420,7 +419,7 @@ router.get('/:page?', (req, res, next) => {
paginateUrl: 'page',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
menu: common.sortMenu(await common.getMenu(db))
});
});
}else{
@ -429,7 +428,7 @@ router.get('/:page?', (req, res, next) => {
return;
}
// lets look for a page
db.pages.findOne({pageSlug: req.params.page, pageEnabled: 'true'}, (err, page) => {
db.pages.findOne({pageSlug: req.params.page, pageEnabled: 'true'}, async (err, page) => {
if(err){
console.error(colors.red('Error getting page', err));
}
@ -446,7 +445,7 @@ router.get('/:page?', (req, res, next) => {
metaDescription: common.getConfig().cartTitle + ' - ' + page,
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
menu: common.sortMenu(await common.getMenu(db))
});
}else{
res.status(404).render('error', {
@ -455,9 +454,8 @@ router.get('/:page?', (req, res, next) => {
message: '404 Error - Page not found',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.getMenu()
}
);
menu: common.sortMenu(await common.getMenu(db))
});
}
});
}

View File

@ -10,7 +10,7 @@
<th>Link</th>
<th></th>
<tbody id="draggable_list">
{{#each menu}}
{{#each menu.items}}
<tr class="drag-row">
<form method="post" action="/admin/settings/menu/update">
<input type="hidden" class="navId" name="navId" value="{{title}}">
@ -24,7 +24,7 @@
<td class="col-md-2">
<a class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');" href="/admin/settings/menu/delete/{{@key}}">
<i class="fa fa-trash-o"></i>
</a>
</a>
<button type="submit" class="btn btn-success btn-sm">
<i class="fa fa-floppy-o"></i>
</button>
@ -47,14 +47,14 @@
</button>
</td>
</form>
</tr>
</tr>
</tbody>
</table>
<p class="text-muted">
Here you can setup a menu to displayed on your shopping cart. You can use this menu to filter your products by specifying a keyword in the
Here you can setup a menu to displayed on your shopping cart. You can use this menu to filter your products by specifying a keyword in the
"link" field. Eg: To show products with a keyword (or tag) of boots you would set the menu field to "Backpacks" and a link value "backpack".
You can also use this menu to link to static pages, Eg: shipping, returns, help, about, contact etc.
</p>
</p>
</div>
</div>
</div>