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();
@ericwebster
Copy link

incase anyone wants that code

      files.forEach(function(file, i){
        static_path = static_path.replace(new RegExp('/', 'g'), "\\");
        var filename = file.split(static_path)[1],
            isCSS = filename.match(/.css$/);

@hari419
Copy link

hari419 commented Dec 11, 2017

Hey, I'm getting below error when i'm trying to override keystonejs with above code mentioned
Cannot read property 'split' of undefined...
can you resolve this issue?

@hari419
Copy link

hari419 commented Dec 11, 2017

Hey, I'm getting below error when i'm trying to override keystonejs with above code mentioned in view.list.js
TypeError: item.compile is not a function
i.e.., in view.list.js
can you resolve this issue?

@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