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": "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>- 100% organic waxed 18 oz canvas<\/span><\/li>
- Full grain genuine leather accents<\/span><\/li>
- Adjustable shoulder straps<\/li>
- Lifetime 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": "
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>- 100% organic waxed 18 oz canvas<\/span><\/li>
- Full grain genuine leather accents<\/span><\/li>
- Adjustable shoulder straps<\/li>
- Lifetime 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
+ }
+ ]
}
-]
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/routes/admin.js b/routes/admin.js
index 3efb5ef..9e8a2cf 100644
--- a/routes/admin.js
+++ b/routes/admin.js
@@ -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
+ });
}
});
});
diff --git a/routes/common.js b/routes/common.js
index 1e49774..d286abc 100644
--- a/routes/common.js
+++ b/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));
diff --git a/routes/index.js b/routes/index.js
index 15623be..2824668 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -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))
+ });
}
});
}
diff --git a/views/settings_menu.hbs b/views/settings_menu.hbs
index 351f560..d6ca80a 100644
--- a/views/settings_menu.hbs
+++ b/views/settings_menu.hbs
@@ -10,7 +10,7 @@
Link |
|
- {{#each menu}}
+ {{#each menu.items}}
-
+
- 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.
-
+