Skip to content

Instantly share code, notes, and snippets.

@nikolowry
Last active December 26, 2018 06:24
Show Gist options
  • Save nikolowry/5083ecd1d0499ab5f44c to your computer and use it in GitHub Desktop.
Save nikolowry/5083ecd1d0499ab5f44c to your computer and use it in GitHub Desktop.
Customize Keystone
This GIST illustrates a hack to allow for configurable backend-url and admin styles in Keystone.js
New deps introduced:
- node-dir
- shelljs
New directory structure after running "yo keystone":
.
|____admin
| |____assets
| | |____styles.css
| | |____main.js
| |____routes
| | |____api.list.js
| | |____view.item.js
| | |____view.list.js
| |____render.js
| |____router.js
|____models
|____public
|____routes
|____templates
|____updates
|____.editorconfig
|____.env
|____.jshintrc
|____gulpfile.js
|____keystone.js
|____package.json
|____Procfile
NOTE: Gist does not allow "/" in filenames. The following files paths should be read as:
- admin.assets.main.js : /admin/assets/main.js
- admin.routes.api.list.js : /admin/routes/api.list.js
- admin.routes.view.item.js : /admin/routes/view.item.js
- admin.routes.view.list.js : /admin/routes/view.list.js
- admin.render.js : /admin/render.js
- admin.router.js : /admin/router.js
BACKEND_DIR=/admin
BACKEND_STATIC_PATH=/admin/assets
BACKEND_STATIC_MOUNT=/assets
'use strict';
if(window.jQuery){
var custom_admin = (function(admin, $){
$('a[href^="/keystone"], form[action^="/keystone"]').each(function(){
var $el = $(this),
attr_type = $el.is('form') ? 'action' : 'href',
//catch all for the moment
new_url = $el.attr(attr_type).replace('/keystone', '/admin');
$el.attr(attr_type, new_url);
});
})(custom_admin || {}, jQuery);
}
var _ = require('underscore'),
fs = require('fs'),
jade = require('../node_modules/keystone/node_modules/jade'),
cloudinary = require('cloudinary'),
moment = require('moment'),
numeral = require('../node_modules/keystone/node_modules/numeral'),
utils = require('../node_modules/keystone/node_modules/keystone-utils'),
dir = require('node-dir');
/**
* Renders a Keystone View
*
* @api private
*/
var templateCache = {};
function render(req, res, view, ext) {
var keystone = this;
var templatePath = 'node_modules/keystone/templates/views/' + view + '.jade';
var jadeOptions = {
filename: templatePath,
pretty: keystone.get('env') !== 'production'
};
// TODO: Allow custom basePath for extensions... like this or similar
// if (keystone.get('extensions')) {
// jadeOptions.basedir = keystone.getPath('extensions') + '/templates';
// }
var compileTemplate = function() {
return jade.compile(fs.readFileSync(templatePath, 'utf8'), jadeOptions);
};
var template = keystone.get('viewCache')
? templateCache[view] || (templateCache[view] = compileTemplate())
: compileTemplate();
var flashMessages = {
info: res.req.flash('info'),
success: res.req.flash('success'),
warning: res.req.flash('warning'),
error: res.req.flash('error'),
hilight: res.req.flash('hilight')
};
var locals = {
_: _,
moment: moment,
numeral: numeral,
env: keystone.get('env'),
brand: keystone.get('brand'),
appversion : keystone.get('appversion'),
nav: keystone.nav,
messages: _.any(flashMessages, function(msgs) { return msgs.length; }) ? flashMessages : false,
lists: keystone.lists,
js: 'javascript:;',
utils: utils,
User: keystone.lists[keystone.get('user model')],
user: req.user,
title: 'Keystone',
signout: keystone.get('signout url'),
backUrl: keystone.get('back url') || '/',
section: {},
version: keystone.version,
csrf_token_key: keystone.security.csrf.TOKEN_KEY,
csrf_token_value: keystone.security.csrf.getToken(req, res),
csrf_query: '&' + keystone.security.csrf.TOKEN_KEY + '=' + keystone.security.csrf.getToken(req, res),
ga: {
property: keystone.get('ga property'),
domain: keystone.get('ga domain')
},
wysiwygOptions: {
enableImages: keystone.get('wysiwyg images') ? true : false,
enableCloudinaryUploads: keystone.get('wysiwyg cloudinary images') ? true : false,
additionalButtons: keystone.get('wysiwyg additional buttons') || '',
additionalPlugins: keystone.get('wysiwyg additional plugins') || '',
additionalOptions: keystone.get('wysiwyg additional options') || {},
overrideToolbar: keystone.get('wysiwyg override toolbar'),
skin: keystone.get('wysiwyg skin') || 'keystone',
menubar: keystone.get('wysiwyg menubar')
}
};
// optional extensions to the local scope
_.extend(locals, ext);
// add cloudinary locals if configured
if (keystone.get('cloudinary config')) {
try {
var cloudinaryUpload = cloudinary.uploader.direct_upload();
locals.cloudinary = {
cloud_name: keystone.get('cloudinary config').cloud_name,
api_key: keystone.get('cloudinary config').api_key,
timestamp: cloudinaryUpload.hidden_fields.timestamp,
signature: cloudinaryUpload.hidden_fields.signature,
prefix: keystone.get('cloudinary prefix') || '',
folders: keystone.get('cloudinary folders'),
uploader: cloudinary.uploader
};
locals.cloudinary_js_config = cloudinary.cloudinary_js_config();
} catch(e) {
if (e === 'Must supply api_key') {
throw new Error('Invalid Cloudinary Config Provided\n\n' +
'See http://keystonejs.com/docs/configuration/#services-cloudinary for more information.');
} else {
throw e;
}
}
}
// fieldLocals defines locals that are provided to each field's `render` method
locals.fieldLocals = _.pick(locals, '_', 'moment', 'numeral', 'env', 'js', 'utils', 'user', 'cloudinary');
//build template
var html = template(_.extend(locals, ext));
//Add custom admin tweaks
if(keystone.get('backend static mount') && keystone.get('backend static path')){
//Env variables
var static_mount = keystone.get('backend static mount'),
static_path = keystone.get('backend static path');
//Inject Admin Custom CSS
var head = html.split('</head>')[0],
body = html.split('</head>')[1].split('</body>');
return dir.files(static_path, function(err, files) {
if (err) throw err;
files.forEach(function(file, i){
var filename = file.split(static_path)[1],
isCSS = filename.match(/.css$/);
if(isCSS) {
head +='<link rel="stylesheet" href="'+static_mount+filename+'">';
} else {
body +='<script src="'+static_mount+filename+'"></script>';
}
if(i == files.length - 1){
head += '</head>';
body += '</body></html>';
res.send(head+body);
}
})
})
} else {
res.send(html);
}
}
module.exports = render;
/**
* Adds bindings for the keystone routes
*
* ####Example:
*
* var app = express();
* app.configure(...); // configuration settings
* app.use(...); // middleware, routes, etc. should come before keystone is initialised
* keystone.routes(app);
*
* @param {Express()} app
* @api public
*/
function routes(app) {
this.app = app;
var keystone = this;
//Set backend url
var backend_dir = process.env.BACKEND_DIR ? process.env.BACKEND_DIR : '/keystone';
this.set('backend dir', backend_dir);
//catch all for custom admin directoy url
if(backend_dir != '/keystone'){
app.all('/keystone*', function(req, res, next){
if(req.originalMethod == "GET") {
return res.redirect(backend_dir + req.params[0]);
}
if(req.originalMethod == "POST") {
//need to work this out
//just getting a POC from the existing codebase
//Will probably end up switching to headless and
//using resty-stone or keystone rest
//and will build out the admin UI custom.
}
});
}
//Set backend static path for assets if config variable is declared
if(process.env.BACKEND_STATIC_MOUNT && process.env.BACKEND_STATIC_PATH) {
var express = this.express,
static_mount = process.env.BACKEND_STATIC_MOUNT,
static_path = require("shelljs").pwd() + process.env.BACKEND_STATIC_PATH;
this.set('backend static mount', static_mount);
this.set('backend static path', static_path);
app.use(static_mount, express.static(static_path));
}
/// ensure keystone nav has been initialised
if (!this.nav) {
this.nav = this.initNav();
}
// Cache compiled view templates if we are in Production mode
this.set('view cache', this.get('env') === 'production');
// Bind auth middleware (generic or custom) to /backend_dir* routes, allowing
// access to the generic signin page if generic auth is used
if (this.get('auth') === true) {
if (!this.get('signout url')) {
this.set('signout url', backend_dir+'/signout');
}
if (!this.get('signin url')) {
this.set('signin url', backend_dir+'/signin');
}
if (!this.nativeApp || !this.get('session')) {
app.all(backend_dir+'*', this.session.persist);
}
app.all(backend_dir+'/signin', require('../node_modules/keystone/routes/views/signin'));
app.all(backend_dir+'/signout', require('../node_modules/keystone/routes/views/signout'));
app.all(backend_dir+'*', this.session.keystoneAuth);
} else if ('function' === typeof this.get('auth')) {
app.all(backend_dir+'*', this.get('auth'));
}
// Keystone Admin Route
app.all(backend_dir, require('../node_modules/keystone/routes/views/home.js'));
// Email test routes
if (this.get('email tests')) {
this.bindEmailTestRoutes(app, this.get('email tests'));
}
// Cloudinary API for image uploading (only if Cloudinary is configured)
if (keystone.get('wysiwyg cloudinary images')) {
if (!keystone.get('cloudinary config')) {
throw new Error('KeystoneJS Initialisaton Error:\n\nTo use wysiwyg cloudinary images, the \'cloudinary config\' setting must be configured.\n\n');
}
app.post(backend_dir+'/api/cloudinary/upload', require('.../node_modules/keystone/routes/api/cloudinary').upload);
}
// Cloudinary API for selecting an existing image from the cloud
if (keystone.get('cloudinary config')) {
app.get(backend_dir+'/api/cloudinary/get', require('../node_modules/keystone/routes/api/cloudinary').get);
app.get(backend_dir+'/api/cloudinary/autocomplete', require('../node_modules/keystone/routes/api/cloudinary').autocomplete);
}
var initList = function(protect) {
return function(req, res, next) {
req.list = keystone.list(req.params.list);
if (!req.list || (protect && req.list.get('hidden'))) {
req.flash('error', 'List ' + req.params.list + ' could not be found.');
return res.redirect(backend_dir+'');
}
next();
};
};
// Generic Lists API
app.all(backend_dir+'/api/:list/:action', initList(), require('./routes/api.list'));
// Generic Lists Download Route
app.all(backend_dir+'/download/:list', initList(), require('../node_modules/keystone/routes/download/list'));
// List and Item Details Admin Routes
app.all(backend_dir+'/:list/:page([0-9]{1,5})?', initList(true), require('./routes/view.list'));
app.all(backend_dir+'/:list/:item', initList(true), require('./routes/view.item'));
return this;
}
module.exports = routes;
var _ = require('underscore'),
async = require('async'),
keystone = require('keystone'),
jade = require('jade');
exports = module.exports = function(req, res) {
var sendResponse = function(status) {
res.json(status);
};
var sendError = function(key, err, msg) {
msg = msg || 'API Error';
key = key || 'unknown error';
msg += ' (' + key + ')';
console.log(msg + (err ? ':' : ''));
if (err) {
console.log(err);
}
res.status(500);
sendResponse({ error: key || 'error', detail: err ? err.message : '' });
};
switch (req.params.action) {
case 'autocomplete':
var limit = req.query.limit || 10,
page = req.query.page || 1,
skip = limit * (page - 1);
var filters = req.list.getSearchFilters(req.query.q);
var count = req.list.model.count(filters),
query = req.list.model.find(filters)
.limit(limit)
.skip(skip)
.sort(req.list.defaultSort);
var doQuery = function() {
count.exec(function(err, total) {
if (err) return sendError('database error', err);
query.exec(function(err, items) {
if (err) return sendError('database error', err);
sendResponse({
total: total,
items: items.map(function(i) {
return {
name: req.list.getDocumentName(i, true) || '(' + i.id + ')',
id: i.id
};
})
});
});
});
};
if (req.query.context === 'relationship') {
var srcList = keystone.list(req.query.list);
if (!srcList) return sendError('invalid list provided');
var field = srcList.fields[req.query.field];
if (!field || field.type !== 'relationship') return sendError('invalid field provided');
if (!field.hasFilters) {
return doQuery();
}
_.each(req.query.filters, function(value, key) {
query.where(key).equals(value ? value : null);
count.where(key).equals(value ? value : null);
});
return doQuery();
} else {
return doQuery();
}
break;
case 'get':
req.list.model.findById(req.query.id).exec(function(err, item) {
if (err) return sendError('database error', err);
if (!item) return sendResponse({ name: req.query.id, id: req.query.id });
switch (req.query.dataset) {
case 'simple':
return sendResponse({
name: req.list.getDocumentName(item, true),
id: item.id
});
default:
return sendResponse(item);
}
});
break;
case 'order':
if (!keystone.security.csrf.validate(req)) {
return sendError('invalid csrf');
}
var order = req.query.order || req.body.order,
queue = [];
if ('string' === typeof order) {
order = order.split(',');
}
_.each(order, function(id, i) {
queue.push(function(done) {
req.list.model.update({ _id: id }, { $set: { sortOrder: i }}, done);
});
});
async.parallel(queue, function(err) {
if (err) return sendError('database error', err);
return sendResponse({
success: true
});
});
break;
case 'create':
console.log('creaate')
if (!keystone.security.csrf.validate(req)) {
return sendError('invalid csrf');
}
var item = new req.list.model(),
updateHandler = item.getUpdateHandler(req),
data = (req.method === 'POST') ? req.body : req.query;
if (req.list.nameIsInitial) {
if (req.list.nameField.validateInput(data)) {
req.list.nameField.updateItem(item, data);
} else {
updateHandler.addValidationError(req.list.nameField.path, 'Name is required.');
}
}
updateHandler.process(data, {
flashErrors: true,
logErrors: true,
fields: req.list.initialFields
}, function(err) {
if (err) {
return sendResponse({
success: false,
err: err
});
} else {
return sendResponse({
success: true,
name: req.list.getDocumentName(item, true),
id: item.id
});
}
});
break;
case 'delete':
if (!keystone.security.csrf.validate(req)) {
return sendError('invalid csrf');
}
if (req.list.get('nodelete')) {
return sendError('nodelete');
}
var id = req.body.id || req.query.id;
if (id === req.user.id) {
return sendError('You can not delete yourself');
}
req.list.model.findById(id).exec(function (err, item) {
if (err) return sendError('database error', err);
if (!item) return sendError('not found');
item.remove(function (err) {
if (err) return sendError('database error', err);
return sendResponse({
success: true,
count: 1
});
});
});
break;
case 'fetch':
if (!keystone.security.csrf.validate(req)) {
return sendError('invalid csrf');
}
(function() {
var queryFilters = req.list.getSearchFilters(req.query.search, req.query.filters),
skip = parseInt(req.query.items.last) - 1,
querystring = require('../../node_modules/keystone/node_modules/querystring'),
link_to = function(params) {
var p = params.page || '';
delete params.page;
var queryParams = _.clone(req.query.q);
for (var i in params) {
if (params[i] === undefined) {
delete params[i];
delete queryParams[i];
}
}
params = querystring.stringify(_.defaults(params, queryParams));
return keystone.get('backend dir') + '/' + req.list.path + (p ? '/' + p : '') + (params ? '?' + params : '');
};
var query = req.list.model.find(queryFilters).sort(req.query.sort).skip(skip).limit(1),
columns = req.list.expandColumns(req.query.cols);
req.list.selectColumns(query, columns);
query.exec(function(err, items) {
if (err) return sendError('database error', err);
if (!items) return sendError('not found');
var locals, row, pagination;
req.list.getPages(req.query.items, req.list.pagination.maxPages);
locals = { list: req.list, columns: columns, item: items[0], csrf_query: req.query.csrf_query, _:_ };
row = jade.renderFile(__dirname + '/../../templates/partials/row.jade', locals);
pagination = jade.renderFile(__dirname + '/../../templates/partials/pagination.jade', {items: req.query.items, link_to: link_to });
return sendResponse({
item: items[0],
row: row,
pagination: pagination,
success: true,
count: 1
});
});
})();
break;
}
};
var keystone = require('keystone'),
_ = require('underscore'),
async = require('async');
exports = module.exports = function(req, res) {
console.log('item request')
var itemQuery = req.list.model.findById(req.params.item);
if (req.list.tracking && req.list.tracking.createdBy) {
console.log('first conditional')
itemQuery.populate(req.list.tracking.createdBy);
}
if (req.list.tracking && req.list.tracking.updatedBy) {
console.log('second conditional')
itemQuery.populate(req.list.tracking.updatedBy);
}
itemQuery.exec(function(err, item) {
if (!item) {
req.flash('error', 'Item ' + req.params.item + ' could not be found.');
return res.redirect(keystone.get('backend dir') + '/' + req.list.path);
}
var viewLocals = {
validationErrors: {}
};
var renderView = function() {
var relationships = _.values(_.compact(_.map(req.list.relationships, function(i) {
if (i.isValid) {
return _.clone(i);
} else {
keystone.console.err('Relationship Configuration Error', 'Relationship: ' + i.path + ' on list: ' + req.list.key + ' links to an invalid list: ' + i.ref);
return null;
}
})));
var drilldown = {
def: req.list.get('drilldown'),
data: {},
items: []
};
var loadDrilldown = function(cb) {
if (!drilldown.def)
return cb();
// step back through the drilldown list and load in reverse order to support nested relationships
// TODO: proper support for nested relationships in drilldown
drilldown.def = drilldown.def.split(' ').reverse();
async.eachSeries(drilldown.def, function(path, done) {
var field = req.list.fields[path];
if (!field || field.type !== 'relationship')
throw new Error('Drilldown for ' + req.list.key + ' is invalid: field at path ' + path + ' is not a relationship.');
var refList = field.refList;
if (field.many) {
if (!item.get(field.path).length) {
return done();
}
refList.model.find().where('_id').in(item.get(field.path)).limit(4).exec(function(err, results) {
if (err || !results) {
done(err);
}
var more = (results.length === 4) ? results.pop() : false;
if (results.length) {
drilldown.data[path] = results;
drilldown.items.push({
list: refList,
items: _.map(results, function(i) { return {
label: refList.getDocumentName(i),
href: keystone.get('backend dir') + '/' + refList.path + '/' + i.id
};}),
more: (more) ? true : false
});
}
done();
});
} else {
if (!item.get(field.path)) {
return done();
}
refList.model.findById(item.get(field.path)).exec(function(err, result) {
if (result) {
drilldown.data[path] = result;
drilldown.items.push({
list: refList,
label: refList.getDocumentName(result),
href: keystone.get('backend dir') + '/' + refList.path + '/' + result.id
});
}
done();
});
}
}, function(err) {
// put the drilldown list back in the right order
drilldown.def.reverse();
drilldown.items.reverse();
cb(err);
});
};
var loadRelationships = function(cb) {
async.each(relationships, function(rel, done) {
// TODO: Handle invalid relationship config
rel.list = keystone.list(rel.ref);
rel.sortable = (rel.list.get('sortable') && rel.list.get('sortContext') === req.list.key + ':' + rel.path);
// TODO: Handle relationships with more than 1 page of results
var q = rel.list.paginate({ page: 1, perPage: 100 })
.where(rel.refPath).equals(item.id)
.sort(rel.list.defaultSort);
// rel.columns = _.reject(rel.list.defaultColumns, function(col) { return (col.type == 'relationship' && col.refList == req.list) });
rel.columns = rel.list.defaultColumns;
rel.list.selectColumns(q, rel.columns);
q.exec(function(err, results) {
rel.items = results;
done(err);
});
}, cb);
};
var loadFormFieldTemplates = function(cb){
var onlyFields = function(item) { return item.type === 'field'; };
var compile = function(item, callback) { item.field.compile('form',callback); };
async.eachSeries(req.list.uiElements.filter(onlyFields), compile , cb);
};
/** Render View */
async.parallel([
loadDrilldown,
loadRelationships,
loadFormFieldTemplates
], function(err) {
// TODO: Handle err
var showRelationships = _.some(relationships, function(rel) {
return rel.items.results.length;
});
keystone.render(req, res, 'item', _.extend(viewLocals, {
section: keystone.nav.by.list[req.list.key] || {},
title: 'Keystone: ' + req.list.singular + ': ' + req.list.getDocumentName(item),
page: 'item',
list: req.list,
item: item,
relationships: relationships,
showRelationships: showRelationships,
drilldown: drilldown
}));
});
};
if (req.method === 'POST' && req.body.action === 'updateItem' && !req.list.get('noedit')) {
if (!keystone.security.csrf.validate(req)) {
req.flash('error', 'There was a problem with your request, please try again.');
return renderView();
}
item.getUpdateHandler(req).process(req.body, { flashErrors: true, logErrors: true }, function(err) {
if (err) {
return renderView();
}
req.flash('success', 'Your changes have been saved.');
return res.redirect(keystone.get('backend dir') + '/' + req.list.path + '/' + item.id);
});
} else {
renderView();
}
});
};
var keystone = require('keystone'),
_ = require('underscore'),
querystring = require('querystring'),
async = require('async');
exports = module.exports = function(req, res) {
var viewLocals = {
validationErrors: {},
showCreateForm: _.has(req.query, 'new')
};
var sort = { by: req.query.sort || req.list.defaultSort },
filters = req.list.processFilters(req.query.q),
cleanFilters = {},
queryFilters = req.list.getSearchFilters(req.query.search, filters),
columns = (req.query.cols) ? req.list.expandColumns(req.query.cols) : req.list.defaultColumns;
_.each(filters, function(filter, path) {
cleanFilters[path] = _.omit(filter, 'field');
});
if (sort.by) {
sort.inv = sort.by.charAt(0) === '-';
sort.path = (sort.inv) ? sort.by.substr(1) : sort.by;
sort.field = req.list.fields[sort.path];
var clearSort = function() {
delete req.query.sort;
var qs = querystring.stringify(req.query);
return res.redirect(req.path + ((qs) ? '?' + qs : ''));
};
// clear the sort query value if it is the default sort value for the list
if (req.query.sort === req.list.defaultSort) {
return clearSort();
}
if (sort.field) {
// the sort is set to a field, use its label
sort.label = sort.field.label;
// some fields have custom sort paths
if (sort.field.type === 'name') {
sort.by = sort.by + '.first ' + sort.by + '.last';
}
} else if (req.list.get('sortable') && (sort.by === 'sortOrder' || sort.by === '-sortOrder')) {
// the sort is set to the built-in sort order, set the label correctly
sort.label = 'display order';
} else if (req.query.sort) {
// it looks like an invalid path has been specified (no matching field), so clear the sort
return clearSort();
}
}
var renderView = function() {
var query = req.list.paginate({ filters: queryFilters, page: req.params.page, perPage: req.list.get('perPage') }).sort(sort.by);
req.list.selectColumns(query, columns);
var link_to = function(params) {
var p = params.page || '';
delete params.page;
var queryParams = _.clone(req.query);
for (var i in params) {
if (params[i] === undefined) {
delete params[i];
delete queryParams[i];
}
}
params = querystring.stringify(_.defaults(params, queryParams));
return keystone.get('backend dir') + '/' + req.list.path + (p ? '/' + p : '') + (params ? '?' + params : '');
};
query.exec(function(err, items) {
if (err) {
console.log(err);
return res.status(500).send('Error querying items:<br><br>' + JSON.stringify(err));
}
// if there were results but not on this page, reset the page
if (req.params.page && items.total && !items.results.length) {
return res.redirect(keystone.get('backend dir') + '/' + req.list.path);
}
// go straight to the result if there was a search, and only one result
if (req.query.search && items.total === 1 && items.results.length === 1) {
return res.redirect(keystone.get('backend dir') + '/' + req.list.path + '/' + items.results[0].id);
}
var download_link = keystone.get('backend dir') + '/' + 'download/' + req.list.path,
downloadParams = {};
if (req.query.q) {
downloadParams.q = req.query.q;
}
if (req.query.search) {
downloadParams.search = req.query.search;
}
if (req.query.cols) {
downloadParams.cols = req.query.cols;
}
downloadParams = querystring.stringify(downloadParams);
if (downloadParams) {
download_link += '?' + downloadParams;
}
var compileFields = function(item, callback) { item.compile('initial', callback); };
async.eachSeries(req.list.initialFields, compileFields , function() {
keystone.render(req, res, 'list', _.extend(viewLocals, {
section: keystone.nav.by.list[req.list.key] || {},
title: 'Keystone: ' + req.list.plural,
page: 'list',
link_to: link_to,
download_link: download_link,
list: req.list,
sort: sort,
filters: cleanFilters,
search: req.query.search,
columns: columns,
colPaths: _.pluck(columns, 'path'),
items: items,
submitted: req.body || {},
query: req.query
}));
});
});
};
var checkCSRF = function() {
var pass = keystone.security.csrf.validate(req);
if (!pass) {
req.flash('error', 'There was a problem with your request, please try again.');
}
return pass;
};
var item;
if ('update' in req.query) {
if (!checkCSRF()) return renderView();
(function() {
var data = null;
if (req.query.update) {
try {
data = JSON.parse(req.query.update);
} catch(e) {
req.flash('error', 'There was an error parsing the update data.');
return renderView();
}
}
req.list.updateAll(data, function(err) {
if (err) {
console.log('Error updating all ' + req.list.plural);
console.log(err);
req.flash('error', 'There was an error updating all ' + req.list.plural + ' (logged to console)');
} else {
req.flash('success', 'All ' + req.list.plural + ' updated successfully.');
}
res.redirect(keystone.get('backend dir') + '/' + req.list.path);
});
})();
} else if (!req.list.get('nodelete') && req.query['delete']) {
if (!checkCSRF()) return renderView();
if (req.query['delete'] === req.user.id) {
req.flash('error', 'You can\'t delete your own ' + req.list.singular + '.');
return renderView();
}
req.list.model.findById(req.query['delete']).exec(function (err, item) {
if (err || !item) return res.redirect(keystone.get('backend dir') + '/' + req.list.path);
item.remove(function (err) {
if (err) {
console.log('Error deleting ' + req.list.singular);
console.log(err);
req.flash('error', 'Error deleting the ' + req.list.singular + ': ' + err.message);
} else {
req.flash('success', req.list.singular + ' deleted successfully.');
}
res.redirect(keystone.get('backend dir') + '/' + req.list.path);
});
});
return;
} else if (!req.list.get('nocreate') && req.list.get('autocreate') && _.has(req.query, 'new')) {
if (!checkCSRF()) return renderView();
item = new req.list.model();
item.save(function(err) {
if (err) {
console.log('There was an error creating the new ' + req.list.singular + ':');
console.log(err);
req.flash('error', 'There was an error creating the new ' + req.list.singular + '.');
renderView();
} else {
req.flash('success', 'New ' + req.list.singular + ' ' + req.list.getDocumentName(item) + ' created.');
return res.redirect(keystone.get('backend dir') + '/' + req.list.path + '/' + item.id);
}
});
} else if (!req.list.get('nocreate') && req.method === 'POST' && req.body.action === 'create') {
if (!checkCSRF()) return renderView();
item = new req.list.model();
var updateHandler = item.getUpdateHandler(req);
viewLocals.showCreateForm = true; // always show the create form after a create. success will redirect.
if (req.list.nameIsInitial) {
if (!req.list.nameField.validateInput(req.body, true, item)) {
updateHandler.addValidationError(req.list.nameField.path, req.list.nameField.label + ' is required.');
}
req.list.nameField.updateItem(item, req.body);
}
updateHandler.process(req.body, {
flashErrors: true,
logErrors: true,
fields: req.list.initialFields
}, function(err) {
if (err) {
return renderView();
}
req.flash('success', 'New ' + req.list.singular + ' ' + req.list.getDocumentName(item) + ' created.');
return res.redirect(keystone.get('backend dir') + '/' + req.list.path + '/' + item.id);
});
} else {
renderView();
}
};
// Simulate config options from your production environment by
// customising the .env file in your project's root folder.
require('dotenv').load();
// Require keystone
var keystone = require('keystone'),
handlebars = require('express-handlebars');
// Overrides to non-configurable keystone options
keystone.routes = require('./admin/router');
keystone.render = require('./admin/render');
// Initialise Keystone with your project's configuration.
// See http://keystonejs.com/guide/config for available options
// and documentation.
keystone.init({
'name': 'SapientCMS',
'brand': 'SapientCMS',
'sass': 'public',
'static': 'public',
'favicon': 'public/favicon.ico',
'views': 'templates/views',
'view engine': 'hbs',
'custom engine': handlebars.create({
layoutsDir: 'templates/views/layouts',
partialsDir: 'templates/views/partials',
defaultLayout: 'default',
helpers: new require('./templates/views/helpers')(),
extname: '.hbs'
}).engine,
'auto update': true,
'session': true,
'auth': true,
'user model': 'User',
'cookie secret': '*frh":IcIak-*CTLKEU7MB[WN"a?/Q",Zrs@;y6PK6W2b?gl8,M_>vbEJ`XCH?Ro',
'signin redirect': process.env.BACKEND_DIR
});
// Load your project's Models
keystone.import('models');
// Setup common locals for your templates. The following are required for the
// bundled templates and layouts. Any runtime locals (that should be set uniquely
// for each request) should be added to ./routes/middleware.js
keystone.set('locals', {
_: require('underscore'),
env: keystone.get('env'),
utils: keystone.utils,
editable: keystone.content.editable
});
keystone.set('routes', require('./routes'));
// Configure the navigation bar in Keystone's Admin UI
keystone.set('nav', {
'posts': ['posts', 'post-categories'],
'galleries': 'galleries',
'enquiries': 'enquiries',
'users': 'users'
});
// Start Keystone to connect to your database and initialise the web server
keystone.start();
@DavidMakow
Copy link

Hi
After "yo keystone", i can't see admin folder in my structure.
Please help me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment