Added permission validation to non admin users
							parent
							
								
									bf67621a86
								
							
						
					
					
						commit
						4533e23993
					
				
							
								
								
									
										2
									
								
								app.js
								
								
								
								
							
							
						
						
									
										2
									
								
								app.js
								
								
								
								
							|  | @ -235,7 +235,7 @@ app.use('/', customer); | |||
| app.use('/', product); | ||||
| app.use('/', order); | ||||
| app.use('/', user); | ||||
| app.use('/admin', admin); | ||||
| app.use('/', admin); | ||||
| app.use('/paypal', paypal); | ||||
| app.use('/stripe', stripe); | ||||
| app.use('/authorizenet', authorizenet); | ||||
|  |  | |||
|  | @ -11,7 +11,35 @@ const nodemailer = require('nodemailer'); | |||
| const escape = require('html-entities').AllHtmlEntities; | ||||
| let ObjectId = require('mongodb').ObjectID; | ||||
| 
 | ||||
| const restrictedRoutes = [ | ||||
|     {route: '/admin/product/new', response: 'redirect'}, | ||||
|     {route: '/admin/product/insert', response: 'redirect'}, | ||||
|     {route: '/admin/product/edit/:id', response: 'redirect'}, | ||||
|     {route: '/admin/product/update', response: 'redirect'}, | ||||
|     {route: '/admin/product/delete/:id', response: 'redirect'}, | ||||
|     {route: '/admin/product/published_state', response: 'json'}, | ||||
|     {route: '/admin/product/setasmainimage', response: 'json'}, | ||||
|     {route: '/admin/product/deleteimage', response: 'json'}, | ||||
|     {route: '/admin/order/statusupdate', response: 'json'}, | ||||
|     {route: '/admin/settings/update', response: 'json'}, | ||||
|     {route: '/admin/settings/option/remove', response: 'json'}, | ||||
|     {route: '/admin/settings/pages/new', response: 'redirect'}, | ||||
|     {route: '/admin/settings/pages/edit/:page', response: 'redirect'}, | ||||
|     {route: '/admin/settings/pages/update', response: 'json'}, | ||||
|     {route: '/admin/settings/pages/delete/:page', response: 'redirect'}, | ||||
|     {route: '/admin/settings/menu/new', response: 'redirect'}, | ||||
|     {route: '/admin/settings/menu/update', response: 'redirect'}, | ||||
|     {route: '/admin/settings/menu/delete/:menuid', response: 'redirect'}, | ||||
|     {route: '/admin/settings/menu/save_order', response: 'json'}, | ||||
|     {route: '/admin/file/upload', response: 'redirect'}, | ||||
|     {route: '/admin/file/delete', response: 'json'} | ||||
| ]; | ||||
| 
 | ||||
| // common functions
 | ||||
| exports.restrict = (req, res, next) => { | ||||
|     exports.checkLogin(req, res, next); | ||||
| }; | ||||
| 
 | ||||
| exports.checkLogin = (req, res, next) => { | ||||
|     // if not protecting we check for public pages and don't checkLogin
 | ||||
|     if(req.session.needsSetup === true){ | ||||
|  | @ -26,6 +54,26 @@ exports.checkLogin = (req, res, next) => { | |||
|     res.redirect('/admin/login'); | ||||
| }; | ||||
| 
 | ||||
| // Middleware to check for admin access for certain route
 | ||||
| exports.checkAccess = (req, res, next) => { | ||||
|     const routeCheck = _.find(restrictedRoutes, {'route': req.route.path}); | ||||
| 
 | ||||
|     // If the user is not an admin and route is restricted, show message and redirect to /admin
 | ||||
|     if(req.session.isAdmin === 'false' && routeCheck){ | ||||
|         if(routeCheck.response === 'redirect'){ | ||||
|             req.session.message = 'Unauthorised. Please refer to administrator.'; | ||||
|             req.session.messageType = 'danger'; | ||||
|             res.redirect('/admin'); | ||||
|             return; | ||||
|         } | ||||
|         if(routeCheck.response === 'json'){ | ||||
|             res.status(400).json({message: 'Unauthorised. Please refer to administrator.'}); | ||||
|         } | ||||
|     }else{ | ||||
|         next(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| exports.showCartCloseBtn = (page) => { | ||||
|     let showCartCloseButton = true; | ||||
|     if(page === 'checkout' || page === 'pay'){ | ||||
|  | @ -64,10 +112,6 @@ exports.addSitemapProducts = (req, res, cb) => { | |||
|     }); | ||||
| }; | ||||
| 
 | ||||
| exports.restrict = (req, res, next) => { | ||||
|     exports.checkLogin(req, res, next); | ||||
| }; | ||||
| 
 | ||||
| exports.clearSessionValue = (session, sessionVar) => { | ||||
|     let temp; | ||||
|     if(session){ | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ $(document).ready(function (){ | |||
|             showNotification(msg, 'success'); | ||||
|         }) | ||||
|         .fail(function(msg){ | ||||
|             showNotification(msg.responseText, 'danger'); | ||||
|             showNotification(msg.responseJSON.message, 'danger'); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|  | @ -118,10 +118,10 @@ $(document).ready(function (){ | |||
|             data: {id: this.id, state: this.checked} | ||||
|         }) | ||||
| 		.done(function(msg){ | ||||
|             showNotification(msg, 'success'); | ||||
|             showNotification(msg.message, 'success'); | ||||
|         }) | ||||
|         .fail(function(msg){ | ||||
|             showNotification(msg.responseText, 'danger'); | ||||
|             showNotification(msg.responseJSON.message, 'danger'); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|  | @ -540,7 +540,7 @@ $(document).ready(function (){ | |||
|                 showNotification(msg, 'success'); | ||||
|             }) | ||||
|             .fail(function(msg){ | ||||
|                 showNotification(msg.responseText, 'danger'); | ||||
|                 showNotification(msg.responseJSON.message, 'danger'); | ||||
|             }); | ||||
|         }else{ | ||||
|             showNotification('Please enter a permalink to validate', 'danger'); | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -10,12 +10,12 @@ const glob = require('glob'); | |||
| const router = express.Router(); | ||||
| 
 | ||||
| // Admin section
 | ||||
| router.get('/', common.restrict, (req, res, next) => { | ||||
| router.get('/admin', common.restrict, (req, res, next) => { | ||||
|     res.redirect('/admin/orders'); | ||||
| }); | ||||
| 
 | ||||
| // logout
 | ||||
| router.get('/logout', (req, res) => { | ||||
| router.get('/admin/logout', (req, res) => { | ||||
|     req.session.user = null; | ||||
|     req.session.message = null; | ||||
|     req.session.messageType = null; | ||||
|  | @ -23,7 +23,7 @@ router.get('/logout', (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // login form
 | ||||
| router.get('/login', (req, res) => { | ||||
| router.get('/admin/login', (req, res) => { | ||||
|     let db = req.app.db; | ||||
| 
 | ||||
|     db.users.count({}, (err, userCount) => { | ||||
|  | @ -54,7 +54,7 @@ router.get('/login', (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // login the user and check the password
 | ||||
| router.post('/login_action', (req, res) => { | ||||
| router.post('/admin/login_action', (req, res) => { | ||||
|     let db = req.app.db; | ||||
| 
 | ||||
|     db.users.findOne({userEmail: req.body.email}, (err, user) => { | ||||
|  | @ -92,7 +92,7 @@ router.post('/login_action', (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // setup form is shown when there are no users setup in the DB
 | ||||
| router.get('/setup', (req, res) => { | ||||
| router.get('/admin/setup', (req, res) => { | ||||
|     let db = req.app.db; | ||||
| 
 | ||||
|     db.users.count({}, (err, userCount) => { | ||||
|  | @ -119,7 +119,7 @@ router.get('/setup', (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // insert a user
 | ||||
| router.post('/setup_action', (req, res) => { | ||||
| router.post('/admin/setup_action', (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     let doc = { | ||||
|  | @ -156,7 +156,7 @@ router.post('/setup_action', (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings update
 | ||||
| router.get('/settings', common.restrict, (req, res) => { | ||||
| router.get('/admin/settings', common.restrict, (req, res) => { | ||||
|     res.render('settings', { | ||||
|         title: 'Cart settings', | ||||
|         session: req.session, | ||||
|  | @ -172,7 +172,7 @@ router.get('/settings', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings update
 | ||||
| router.post('/settings/update', common.restrict, (req, res) => { | ||||
| router.post('/admin/settings/update', common.restrict, common.checkAccess, (req, res) => { | ||||
|     let result = common.updateConfig(req.body); | ||||
|     if(result === true){ | ||||
|         res.status(200).json({message: 'Settings successfully updated'}); | ||||
|  | @ -182,7 +182,7 @@ router.post('/settings/update', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings update
 | ||||
| router.post('/settings/option/remove', common.restrict, (req, res) => { | ||||
| router.post('/admin/settings/option/remove', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
|     db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => { | ||||
|         if(err){ | ||||
|  | @ -209,7 +209,7 @@ router.post('/settings/option/remove', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings update
 | ||||
| router.get('/settings/menu', common.restrict, async (req, res) => { | ||||
| router.get('/admin/settings/menu', common.restrict, async (req, res) => { | ||||
|     const db = req.app.db; | ||||
|     res.render('settings_menu', { | ||||
|         title: 'Cart menu', | ||||
|  | @ -224,7 +224,7 @@ router.get('/settings/menu', common.restrict, async (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings page list
 | ||||
| router.get('/settings/pages', common.restrict, (req, res) => { | ||||
| router.get('/admin/settings/pages', common.restrict, (req, res) => { | ||||
|     const db = req.app.db; | ||||
|     db.pages.find({}).toArray(async (err, pages) => { | ||||
|         if(err){ | ||||
|  | @ -246,7 +246,7 @@ router.get('/settings/pages', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings pages new
 | ||||
| router.get('/settings/pages/new', common.restrict, async (req, res) => { | ||||
| router.get('/admin/settings/pages/new', common.restrict, common.checkAccess, async (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     res.render('settings_page_edit', { | ||||
|  | @ -263,7 +263,7 @@ router.get('/settings/pages/new', common.restrict, async (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings pages editor
 | ||||
| router.get('/settings/pages/edit/:page', common.restrict, (req, res) => { | ||||
| router.get('/admin/settings/pages/edit/:page', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
|     db.pages.findOne({_id: common.getId(req.params.page)}, async (err, page) => { | ||||
|         if(err){ | ||||
|  | @ -299,7 +299,7 @@ router.get('/settings/pages/edit/:page', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings update page
 | ||||
| router.post('/settings/pages/update', common.restrict, (req, res) => { | ||||
| router.post('/admin/settings/pages/update', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     let doc = { | ||||
|  | @ -339,7 +339,7 @@ router.post('/settings/pages/update', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // settings delete page
 | ||||
| router.get('/settings/pages/delete/:page', common.restrict, (req, res) => { | ||||
| router.get('/admin/settings/pages/delete/:page', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
|     db.pages.remove({_id: common.getId(req.params.page)}, {}, (err, numRemoved) => { | ||||
|         if(err){ | ||||
|  | @ -355,7 +355,7 @@ router.get('/settings/pages/delete/:page', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // new menu item
 | ||||
| router.post('/settings/menu/new', common.restrict, (req, res) => { | ||||
| router.post('/admin/settings/menu/new', common.restrict, common.checkAccess, (req, res) => { | ||||
|     let result = common.newMenu(req, res); | ||||
|     if(result === false){ | ||||
|         req.session.message = 'Failed creating menu.'; | ||||
|  | @ -365,7 +365,7 @@ router.post('/settings/menu/new', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // update existing menu item
 | ||||
| router.post('/settings/menu/update', common.restrict, (req, res) => { | ||||
| router.post('/admin/settings/menu/update', common.restrict, common.checkAccess, (req, res) => { | ||||
|     let result = common.updateMenu(req, res); | ||||
|     if(result === false){ | ||||
|         req.session.message = 'Failed updating menu.'; | ||||
|  | @ -375,7 +375,7 @@ router.post('/settings/menu/update', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // delete menu item
 | ||||
| router.get('/settings/menu/delete/:menuid', common.restrict, (req, res) => { | ||||
| router.get('/admin/settings/menu/delete/:menuid', common.restrict, common.checkAccess, (req, res) => { | ||||
|     let result = common.deleteMenu(req, res, req.params.menuid); | ||||
|     if(result === false){ | ||||
|         req.session.message = 'Failed deleting menu.'; | ||||
|  | @ -385,7 +385,7 @@ router.get('/settings/menu/delete/:menuid', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // We call this via a Ajax call to save the order from the sortable list
 | ||||
| router.post('/settings/menu/save_order', common.restrict, (req, res) => { | ||||
| router.post('/admin/settings/menu/save_order', common.restrict, common.checkAccess, (req, res) => { | ||||
|     let result = common.orderMenu(req, res); | ||||
|     if(result === false){ | ||||
|         res.status(400).json({message: 'Failed saving menu order'}); | ||||
|  | @ -395,7 +395,7 @@ router.post('/settings/menu/save_order', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // validate the permalink
 | ||||
| router.post('/api/validate_permalink', (req, res) => { | ||||
| router.post('/admin/api/validate_permalink', (req, res) => { | ||||
|     // if doc id is provided it checks for permalink in any products other that one provided,
 | ||||
|     // else it just checks for any products with that permalink
 | ||||
|     const db = req.app.db; | ||||
|  | @ -412,18 +412,16 @@ router.post('/api/validate_permalink', (req, res) => { | |||
|             console.info(err.stack); | ||||
|         } | ||||
|         if(products > 0){ | ||||
|             res.writeHead(400, {'Content-Type': 'application/text'}); | ||||
|             res.end('Permalink already exists'); | ||||
|             res.status(400).json({message: 'Permalink already exists'}); | ||||
|         }else{ | ||||
|             res.writeHead(200, {'Content-Type': 'application/text'}); | ||||
|             res.end('Permalink validated successfully'); | ||||
|             res.status(200).json({message: 'Permalink validated successfully'}); | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| // upload the file
 | ||||
| let upload = multer({dest: 'public/uploads/'}); | ||||
| router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, res, next) => { | ||||
| router.post('/admin/file/upload', common.restrict, common.checkAccess, upload.single('upload_file'), (req, res, next) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     if(req.file){ | ||||
|  | @ -479,15 +477,15 @@ router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, | |||
| }); | ||||
| 
 | ||||
| // delete a file via ajax request
 | ||||
| router.post('/testEmail', common.restrict, (req, res) => { | ||||
| router.post('/admin/testEmail', common.restrict, (req, res) => { | ||||
|     let config = common.getConfig(); | ||||
|     // TODO: Should fix this to properly handle result
 | ||||
|     common.sendEmail(config.emailAddress, 'expressCart test email', 'Your email settings are working'); | ||||
|     res.status(200).json('Test email sent'); | ||||
|     res.status(200).json({message: 'Test email sent'}); | ||||
| }); | ||||
| 
 | ||||
| // delete a file via ajax request
 | ||||
| router.post('/file/delete', common.restrict, (req, res) => { | ||||
| router.post('/admin/file/delete', common.restrict, common.checkAccess, (req, res) => { | ||||
|     req.session.message = null; | ||||
|     req.session.messageType = null; | ||||
| 
 | ||||
|  | @ -503,7 +501,7 @@ router.post('/file/delete', common.restrict, (req, res) => { | |||
|     }); | ||||
| }); | ||||
| 
 | ||||
| router.get('/files', common.restrict, (req, res) => { | ||||
| router.get('/admin/files', common.restrict, (req, res) => { | ||||
|     // loop files in /public/uploads/
 | ||||
|     glob('public/uploads/**', {nosort: true}, (er, files) => { | ||||
|         // sort array
 | ||||
|  |  | |||
|  | @ -131,7 +131,7 @@ router.get('/admin/order/delete/:id', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // update order status
 | ||||
| router.post('/admin/order/statusupdate', common.restrict, (req, res) => { | ||||
| router.post('/admin/order/statusupdate', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
|     db.orders.update({_id: common.getId(req.body.order_id)}, {$set: {orderStatus: req.body.status}}, {multi: false}, (err, numReplaced) => { | ||||
|         if(err){ | ||||
|  |  | |||
|  | @ -74,7 +74,7 @@ router.get('/admin/product/new', common.restrict, common.checkAccess, (req, res) | |||
| }); | ||||
| 
 | ||||
| // insert new product form action
 | ||||
| router.post('/admin/product/insert', common.restrict, (req, res) => { | ||||
| router.post('/admin/product/insert', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     let doc = { | ||||
|  | @ -145,7 +145,7 @@ router.post('/admin/product/insert', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // render the editor
 | ||||
| router.get('/admin/product/edit/:id', common.restrict, (req, res) => { | ||||
| router.get('/admin/product/edit/:id', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     common.getImages(req.params.id, req, res, (images) => { | ||||
|  | @ -176,7 +176,7 @@ router.get('/admin/product/edit/:id', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // Update an existing product form action
 | ||||
| router.post('/admin/product/update', common.restrict, (req, res) => { | ||||
| router.post('/admin/product/update', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => { | ||||
|  | @ -256,7 +256,7 @@ router.post('/admin/product/update', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // delete product
 | ||||
| router.get('/admin/product/delete/:id', common.restrict, (req, res) => { | ||||
| router.get('/admin/product/delete/:id', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     // remove the article
 | ||||
|  | @ -283,23 +283,21 @@ router.get('/admin/product/delete/:id', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // update the published state based on an ajax call from the frontend
 | ||||
| router.post('/admin/product/published_state', common.restrict, (req, res) => { | ||||
| router.post('/admin/product/published_state', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     db.products.update({_id: common.getId(req.body.id)}, {$set: {productPublished: req.body.state}}, {multi: false}, (err, numReplaced) => { | ||||
|         if(err){ | ||||
|             console.error(colors.red('Failed to update the published state: ' + err)); | ||||
|             res.writeHead(400, {'Content-Type': 'application/text'}); | ||||
|             res.end('Published state not updated'); | ||||
|             res.status(400).json('Published state not updated'); | ||||
|         }else{ | ||||
|             res.writeHead(200, {'Content-Type': 'application/text'}); | ||||
|             res.end('Published state updated'); | ||||
|             res.status(200).json('Published state updated'); | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| // set as main product image
 | ||||
| router.post('/admin/product/setasmainimage', common.restrict, (req, res) => { | ||||
| router.post('/admin/product/setasmainimage', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     // update the productImage to the db
 | ||||
|  | @ -313,7 +311,7 @@ router.post('/admin/product/setasmainimage', common.restrict, (req, res) => { | |||
| }); | ||||
| 
 | ||||
| // deletes a product image
 | ||||
| router.post('/admin/product/deleteimage', common.restrict, (req, res) => { | ||||
| router.post('/admin/product/deleteimage', common.restrict, common.checkAccess, (req, res) => { | ||||
|     const db = req.app.db; | ||||
| 
 | ||||
|     // get the productImage from the db
 | ||||
|  |  | |||
|  | @ -2,15 +2,19 @@ | |||
|     <h2>Menu</h2> | ||||
|     <ul class="list-group"> | ||||
|         <li class="list-group-item"><strong>Products</strong></li> | ||||
|         {{#ifCond session.isAdmin '===' 'true'}} | ||||
|         <li class="list-group-item"><i class="fa fa-plus-square-o fa-icon" aria-hidden="true"></i>   <a href="/admin/product/new">New</a></li> | ||||
|         {{/ifCond}} | ||||
|         <li class="list-group-item"><i class="fa fa-list fa-icon" aria-hidden="true"></i>   <a href="/admin/products">List</a></li> | ||||
|         <li class="list-group-item"><strong>Orders</strong></li> | ||||
|         <li class="list-group-item"><i class="fa fa-cube fa-icon" aria-hidden="true"></i>   <a href="/admin/orders">List</a></li> | ||||
|         <li class="list-group-item"><strong>Customers</strong></li> | ||||
|         <li class="list-group-item"><i class="fa fa-users fa-icon" aria-hidden="true"></i>   <a href="/admin/customers">List</a></li> | ||||
|         <li class="list-group-item"><strong>Users</strong></li> | ||||
|         {{#ifCond session.isAdmin '===' 'true'}} | ||||
|         <li class="list-group-item"><i class="fa fa-user-plus fa-icon" aria-hidden="true"></i>   <a href="/admin/user/new">New</a></li> | ||||
|         <li class="list-group-item"><i class="fa fa-user fa-icon" aria-hidden="true"></i>   <a href="/admin/users">Edit</a></li> | ||||
|         {{/ifCond}} | ||||
|         <li class="list-group-item"><i class="fa fa-user-circle-o fa-icon" aria-hidden="true"></i>   <a href="/admin/user/edit/{{session.userId}}">My Account</a></li> | ||||
|         <li class="list-group-item"><strong>Settings</strong></li> | ||||
|         <li class="list-group-item"><i class="fa fa-cog fa-icon" aria-hidden="true"></i>   <a href="/admin/settings">General settings</a></li> | ||||
|  |  | |||
|  | @ -1,145 +1,159 @@ | |||
| {{> menu}} | ||||
| <div class="col-lg-9"> | ||||
| 	<div class="row"> | ||||
| 		<div class="col-md-10"> | ||||
| 			<form method="post" id="settingsForm" action="/admin/settings/update" data-toggle="validator"> | ||||
|             <h2 class="clearfix">General Settings <div class="pull-right"><button type="submit" id="btnSettingsUpdate" class="btn btn-success">Update</button></h2> | ||||
|             <div class="form-group"> | ||||
| 		    	<label>Cart name *</label> | ||||
| 		    	<input type="text" class="form-control" name="cartTitle" value="{{config.cartTitle}}" required> | ||||
|                 <p class="help-block"> | ||||
|                     This element is critical for search engine optimisation. Cart title is displayed if your logo is hidden. | ||||
|                 </p> | ||||
| 		  	</div> | ||||
|               <div class="form-group"> | ||||
| 		    	<label>Cart description *</label> | ||||
| 		    	<input type="text" class="form-control" name="cartDescription" value="{{config.cartDescription}}" required> | ||||
|                 <p class="help-block">This description shows when your website is listed in search engine results.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 		    	<label>Cart image/logo</label> | ||||
| 		    	<input type="text" class="form-control" name="cartLogo" value="{{config.cartLogo}}"> | ||||
| 		  	</div> | ||||
| 			<div class="form-group"> | ||||
| 		    	<label>Cart URL *</label> | ||||
| 		    	<input type="text" class="form-control" name="baseUrl" value="{{config.baseUrl}}" required> | ||||
|                 <p class="help-block">This URL is used in sitemaps and when your customer returns from completing their payment.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 		    	<label>Cart Email *</label> | ||||
| 		    	<input type="email" class="form-control" name="emailAddress" value="{{config.emailAddress}}" required> | ||||
|                 <p class="help-block">This is used as the "from" email when sending receipts to your customers.</p> | ||||
| 		  	</div> | ||||
| 		  	<div class="form-group"> | ||||
| 			    <label>Flat shipping rate *</label> | ||||
| 			    <input type="text" class="form-control" name="flatShipping" value="{{config.flatShipping}}" required> | ||||
|                 <p class="help-block">A flat shipping rate applied to all orders.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Free shipping threshold</label> | ||||
| 			    <input type="text" class="form-control" name="freeShippingAmount" value="{{config.freeShippingAmount}}"> | ||||
|                 <p class="help-block">Orders over this value will mean the shipped will the FREE. Set to high value if you always want to charge shipping.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Payment gateway</label> | ||||
|                 <select class="form-control" name="paymentGateway"> | ||||
|                     <option {{selectState 'paypal' config.paymentGateway}} value="paypal">Paypal</option> | ||||
|                     <option {{selectState 'stripe' config.paymentGateway}} value="stripe">Stripe</option> | ||||
|                 </select> | ||||
|                 <p class="help-block">You will also need to configure your payment gateway credentials in the `/config/<gateway_name>.json` file.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Currency symbol</label> | ||||
| 			    <input type="text" class="form-control" name="currencySymbol" value="{{currencySymbol config.currencySymbol}}"> | ||||
|                 <p class="help-block">Set this to your currency symbol. Eg: $, £, €</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Theme</label> | ||||
|                 <select class="form-control" name="theme"> | ||||
|                     <option {{selectState '' config.theme}} value="">Default</option> | ||||
|                     {{#each themes}} | ||||
|     <div class="row"> | ||||
|         <div class="col-md-10"> | ||||
|             <form method="post" id="settingsForm" action="/admin/settings/update" data-toggle="validator"> | ||||
|                 <h2 class="clearfix">General Settings | ||||
|                     <div class="pull-right"> | ||||
|                     <button type="submit" id="btnSettingsUpdate" class="btn btn-success">Update</button> | ||||
|                 </h2> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Cart name *</label> | ||||
|                     <input type="text" class="form-control" name="cartTitle" value="{{config.cartTitle}}" required> | ||||
|                     <p class="help-block"> | ||||
|                         This element is critical for search engine optimisation. Cart title is displayed if your logo is hidden. | ||||
|                     </p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Cart description *</label> | ||||
|                     <input type="text" class="form-control" name="cartDescription" value="{{config.cartDescription}}" required> | ||||
|                     <p class="help-block">This description shows when your website is listed in search engine results.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Cart image/logo</label> | ||||
|                     <input type="text" class="form-control" name="cartLogo" value="{{config.cartLogo}}"> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Cart URL *</label> | ||||
|                     <input type="text" class="form-control" name="baseUrl" value="{{config.baseUrl}}" required> | ||||
|                     <p class="help-block">This URL is used in sitemaps and when your customer returns from completing their payment.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Cart Email *</label> | ||||
|                     <input type="email" class="form-control" name="emailAddress" value="{{config.emailAddress}}" required> | ||||
|                     <p class="help-block">This is used as the "from" email when sending receipts to your customers.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Flat shipping rate *</label> | ||||
|                     <input type="text" class="form-control" name="flatShipping" value="{{config.flatShipping}}" required> | ||||
|                     <p class="help-block">A flat shipping rate applied to all orders.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Free shipping threshold</label> | ||||
|                     <input type="text" class="form-control" name="freeShippingAmount" value="{{config.freeShippingAmount}}"> | ||||
|                     <p class="help-block">Orders over this value will mean the shipped will the FREE. Set to high value if you always want to charge | ||||
|                         shipping.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Payment gateway</label> | ||||
|                     <select class="form-control" name="paymentGateway"> | ||||
|                         <option {{selectState 'paypal' config.paymentGateway}} value="paypal">Paypal</option> | ||||
|                         <option {{selectState 'stripe' config.paymentGateway}} value="stripe">Stripe</option> | ||||
|                     </select> | ||||
|                     <p class="help-block">You will also need to configure your payment gateway credentials in the `/config/<gateway_name>.json` | ||||
|                         file.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Currency symbol</label> | ||||
|                     <input type="text" class="form-control" name="currencySymbol" value="{{currencySymbol config.currencySymbol}}"> | ||||
|                     <p class="help-block">Set this to your currency symbol. Eg: $, £, €</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Theme</label> | ||||
|                     <select class="form-control" name="theme"> | ||||
|                         <option {{selectState '' config.theme}} value="">Default</option> | ||||
|                         {{#each themes}} | ||||
|                         <option {{selectState this ../config.theme}} value="{{this}}">{{this}}</option> | ||||
|                     {{/each}} | ||||
|                 </select> | ||||
|                 <p class="help-block">Themes are loaded from `/public/themes/`</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Products per row</label> | ||||
|                 <select class="form-control" name="productsPerRow"> | ||||
|                     <option value="{{config.productsPerRow}}" hidden="hidden" selected="selected">{{config.productsPerRow}}</option> | ||||
|                     <option {{selectState '1' config.productsPerRow}}>1</option> | ||||
|                     <option {{selectState '2' config.productsPerRow}}>2</option> | ||||
|                     <option {{selectState '3' config.productsPerRow}}>3</option> | ||||
|                     <option {{selectState '4' config.productsPerRow}}>4</option> | ||||
|                 </select> | ||||
|                 <p class="help-block">The number of products to be displayed across the page.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Products per page</label> | ||||
| 			    <input type="number" class="form-control" name="productsPerPage" value="{{config.productsPerPage}}"> | ||||
|                 <p class="help-block">The number of products to be displayed on each page.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Menu Enabled: </label> | ||||
|                 <div class="checkbox"> | ||||
|                     <label><input class="settingsMenuEnabled" type="checkbox" {{checkedState config.menuEnabled}} id="menuEnabled" name="menuEnabled"></label> | ||||
|                         {{/each}} | ||||
|                     </select> | ||||
|                     <p class="help-block">Themes are loaded from `/public/themes/`</p> | ||||
|                 </div> | ||||
|                 <p class="help-block">If a menu is set you can set it up <a href="/admin/settings/menu">here</a>.</p> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Menu header</label> | ||||
| 			    <input type="text" class="form-control" name="menuTitle" value="{{config.menuTitle}}" placeholder="Menu"> | ||||
|                 <p class="help-block">The heading text for your menu.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Menu location: </label> | ||||
|                 <select class="form-control" name="menuLocation"> | ||||
|                     <option {{selectState 'top' config.menuLocation}}>top</option> | ||||
|                     <option {{selectState 'side' config.menuLocation}}>side</option> | ||||
|                 </select> | ||||
|                 <p class="help-block">The location of your menu.</p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Footer HTML</label> | ||||
| 			    <textarea class="form-control codemirrorArea" rows="5" id="footerHtml" name="footerHtml">{{footerHtml}}</textarea> | ||||
|                 <input type="hidden" id="footerHtml_input" name="footerHtml_input"> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Google analytics</label> | ||||
| 			    <textarea class="form-control" rows="3" id="googleAnalytics" name="googleAnalytics">{{googleAnalytics}}</textarea> | ||||
|                 <input type="hidden" id="googleAnalytics_input" name="googleAnalytics_input"> | ||||
|                 <p class="help-block">Your Google Analytics code. Please also inlude the "script" tags - <a href="https://support.google.com/analytics/answer/1032385?hl=en" target="_blank">Help</a></p> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Custom CSS</label> | ||||
| 			    <textarea class="form-control" rows="10" id="customCss" name="customCss">{{config.customCss}}</textarea> | ||||
|                 <input type="hidden" id="customCss_input" name="customCss_input"> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Email SMTP Host</label> | ||||
| 			    <input type="text" class="form-control" name="emailHost" value="{{config.emailHost}}" autocomplete="off" required> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Email SMTP Port</label> | ||||
| 			    <input type="text" class="form-control" name="emailPort" value="{{config.emailPort}}" autocomplete="off" required> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Email SMTP secure </label> | ||||
|                 <div class="checkbox"> | ||||
|                     <label><input class="settingsMenuEnabled" type="checkbox" {{checkedState config.emailSecure}} name="emailSecure"></label> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Products per row</label> | ||||
|                     <select class="form-control" name="productsPerRow"> | ||||
|                         <option value="{{config.productsPerRow}}" hidden="hidden" selected="selected">{{config.productsPerRow}}</option> | ||||
|                         <option {{selectState '1' config.productsPerRow}}>1</option> | ||||
|                         <option {{selectState '2' config.productsPerRow}}>2</option> | ||||
|                         <option {{selectState '3' config.productsPerRow}}>3</option> | ||||
|                         <option {{selectState '4' config.productsPerRow}}>4</option> | ||||
|                     </select> | ||||
|                     <p class="help-block">The number of products to be displayed across the page.</p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Email SMTP Username</label> | ||||
| 			    <input type="text" class="form-control" name="emailUser" value="{{config.emailUser}}" autocomplete="off" required> | ||||
| 		  	</div> | ||||
|             <div class="form-group"> | ||||
| 			    <label>Email SMTP Password</label> | ||||
| 			    <input type="password" class="form-control" name="emailPassword" value="{{config.emailPassword}}" autocomplete="off" required> | ||||
| 		  	</div> | ||||
|         <div class="form-group"> | ||||
|                 <button id="sendTestEmail" class="btn btn-success">Send test email</button> | ||||
| 		  	</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Products per page</label> | ||||
|                     <input type="number" class="form-control" name="productsPerPage" value="{{config.productsPerPage}}"> | ||||
|                     <p class="help-block">The number of products to be displayed on each page.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Menu Enabled: </label> | ||||
|                     <div class="checkbox"> | ||||
|                         <label> | ||||
|                             <input class="settingsMenuEnabled" type="checkbox" {{checkedState config.menuEnabled}} id="menuEnabled" | ||||
|                                 name="menuEnabled"> | ||||
|                         </label> | ||||
|                     </div> | ||||
|                     <p class="help-block">If a menu is set you can set it up | ||||
|                         <a href="/admin/settings/menu">here</a>.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Menu header</label> | ||||
|                     <input type="text" class="form-control" name="menuTitle" value="{{config.menuTitle}}" placeholder="Menu"> | ||||
|                     <p class="help-block">The heading text for your menu.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Menu location: </label> | ||||
|                     <select class="form-control" name="menuLocation"> | ||||
|                         <option {{selectState 'top' config.menuLocation}}>top</option> | ||||
|                         <option {{selectState 'side' config.menuLocation}}>side</option> | ||||
|                     </select> | ||||
|                     <p class="help-block">The location of your menu.</p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Footer HTML</label> | ||||
|                     <textarea class="form-control codemirrorArea" rows="5" id="footerHtml" name="footerHtml">{{footerHtml}}</textarea> | ||||
|                     <input type="hidden" id="footerHtml_input" name="footerHtml_input"> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Google analytics</label> | ||||
|                     <textarea class="form-control" rows="3" id="googleAnalytics" name="googleAnalytics">{{googleAnalytics}}</textarea> | ||||
|                     <input type="hidden" id="googleAnalytics_input" name="googleAnalytics_input"> | ||||
|                     <p class="help-block">Your Google Analytics code. Please also inlude the "script" tags - | ||||
|                         <a href="https://support.google.com/analytics/answer/1032385?hl=en" | ||||
|                             target="_blank">Help</a> | ||||
|                     </p> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Custom CSS</label> | ||||
|                     <textarea class="form-control" rows="10" id="customCss" name="customCss">{{config.customCss}}</textarea> | ||||
|                     <input type="hidden" id="customCss_input" name="customCss_input"> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Email SMTP Host</label> | ||||
|                     <input type="text" class="form-control" name="emailHost" value="{{config.emailHost}}" autocomplete="off" required> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Email SMTP Port</label> | ||||
|                     <input type="text" class="form-control" name="emailPort" value="{{config.emailPort}}" autocomplete="off" required> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Email SMTP secure </label> | ||||
|                     <div class="checkbox"> | ||||
|                         <label> | ||||
|                             <input class="settingsMenuEnabled" type="checkbox" {{checkedState config.emailSecure}} name="emailSecure"> | ||||
|                         </label> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Email SMTP Username</label> | ||||
|                     <input type="text" class="form-control" name="emailUser" value="{{config.emailUser}}" autocomplete="off" required> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <label>Email SMTP Password</label> | ||||
|                     <input type="password" class="form-control" name="emailPassword" value="{{config.emailPassword}}" autocomplete="off" required> | ||||
|                 </div> | ||||
|                 <div class="form-group"> | ||||
|                     <button id="sendTestEmail" class="btn btn-success">Send test email</button> | ||||
|                 </div> | ||||
|                 </div> | ||||
|         </div> | ||||
|     </div> | ||||
		Loading…
	
		Reference in New Issue