Moved menu to the DB and started modernizing old code
parent
ceb23e1ce1
commit
ecef3b8224
|
@ -6,7 +6,7 @@
|
|||
"promise"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6
|
||||
"ecmaVersion": 2017
|
||||
},
|
||||
"globals": {
|
||||
"$": true
|
||||
|
|
20
app.js
20
app.js
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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 New Jersey<\/li><li style=\"margin-bottom: 0px;\">7oz Eco-Twill fabric: 35% organic cotton, 65% recycled PET (plastic water and soda bottles) <\/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 New Jersey<\/li><li style=\"margin-bottom: 0px;\">7oz Eco-Twill fabric: 35% organic cotton, 65% recycled PET (plastic water and soda bottles) <\/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. 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. 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&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&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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
164
routes/common.js
164
routes/common.js
|
@ -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));
|
||||
|
|
|
@ -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))
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue