diff options
| -rw-r--r-- | Readme.md | 1 | ||||
| -rw-r--r-- | app/index.js | 66 | ||||
| -rw-r--r-- | app/node_modules/okadminview/index.js | 131 | ||||
| -rw-r--r-- | app/node_modules/okdb/index.js | 2 | ||||
| -rw-r--r-- | app/node_modules/okserver/index.js | 7 | ||||
| -rw-r--r-- | app/node_modules/okview/index.js | 133 | ||||
| -rw-r--r-- | examples/index.js | 2 | ||||
| -rw-r--r-- | site/index.js | 8 | ||||
| -rw-r--r-- | themes/okadmin/public/css/main.css | 39 | ||||
| -rw-r--r-- | themes/okadmin/public/js/app.js | 11 | ||||
| -rw-r--r-- | themes/okadmin/templates/404.liquid | 54 | ||||
| -rw-r--r-- | themes/okadmin/templates/5xx.liquid | 54 | ||||
| -rw-r--r-- | themes/okadmin/templates/index.liquid | 6 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/flash.liquid | 9 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/inputs.liquid | 4 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/tail.liquid | 8 | ||||
| -rw-r--r-- | themes/okadmin/templates/resource.liquid | 2 |
17 files changed, 357 insertions, 180 deletions
@@ -10,4 +10,5 @@ * node index Server will be running on http://lvh.me:1337/ +Admin area available on http://lvh.me:1337/admin/ diff --git a/app/index.js b/app/index.js index 708943c..419adfc 100644 --- a/app/index.js +++ b/app/index.js @@ -32,10 +32,11 @@ function OKCMS(options) { var adminConfig = options.admin || {}; var adminRoot = this._adminRoot = adminConfig.root || path.join(__dirname, '../themes/okadmin/public'); - var adminPath = this._adminPath = '/_admin'; + var adminPath = this._adminPath = '/admin'; var templateRoot = options.templateRoot || 'templates'; var adminTemplateRoot = options.templateRoot || path.join(__dirname, '../themes/okadmin/templates'); + var debug = options.debug || false; // Set metadata defaults // TODO Abstract this out somewhere else @@ -84,13 +85,17 @@ function OKCMS(options) { }); var resourceCache = this._resourceCache = this._createResources(resourceConfig, db, schemas); + var errorHandler = createErrorHandlerProducer( + templateProvider, adminTemplateProvider, debug); // Create view instances from config var views = this._views = - this._createViews(viewConfig, db, meta, resourceCache, templateProvider); + this._createViews(viewConfig, db, meta, resourceCache, templateProvider, + errorHandler); var adminViews = this._adminViews = this._createAdminViews(adminPath, app, express, resourceConfig, - resourceCache, adminTemplateProvider, adminMeta); + resourceCache, adminTemplateProvider, adminMeta, + errorHandler); // Create services var imageService = OKImageService({ @@ -109,7 +114,8 @@ function OKCMS(options) { adminPath: adminPath, services: { image: imageService - } + }, + errorHandler: errorHandler }); } @@ -152,7 +158,7 @@ OKCMS.prototype._createResources = function(resourceConfig, db, schemaCache) { }; OKCMS.prototype._createViews = function(viewConfig, db, - meta, resourceCache, templateProvider) { + meta, resourceCache, templateProvider, errorHandler) { viewConfig = viewConfig || {}; var self = this; var createQueries = this._createQueries.bind(this); @@ -171,7 +177,8 @@ OKCMS.prototype._createViews = function(viewConfig, db, route: route, template: template, queries: queries, - meta: meta + meta: meta, + errorHandler: errorHandler }); return cache; }, {}); @@ -193,7 +200,8 @@ OKCMS.prototype._createViews = function(viewConfig, db, }; OKCMS.prototype._createAdminViews = function(path, app, express, - resourceConfig, resourceCache, templateProvider, meta) { + resourceConfig, resourceCache, templateProvider, meta, + errorHandler) { var views = {}; var withTrail = withTrailingSlash(path); var withoutTrail = withoutTrailingSlash(path); @@ -217,7 +225,8 @@ OKCMS.prototype._createAdminViews = function(path, app, express, resourceConfig: resourceConfig, resourceCache: resourceCache, templateProvider: templateProvider, - meta: meta + meta: meta, + errorHandler: errorHandler }); return views; }; @@ -269,6 +278,47 @@ ResourceCache.prototype.get = function(type, id) { } }; +/** + * Higher order function implementing customizable error handling + */ +function createErrorHandlerProducer(templateProvider, adminTemplateProvider, debugMode) { + + var template404 = templateProvider.getTemplate('404') || + adminTemplateProvider.getTemplate('404'); + var template5xx = templateProvider.getTemplate('5xx') || + adminTemplateProvider.getTemplate('5xx'); + + if (!template404 || !template5xx) + throw new Error('No error templates provided by admin theme or user') + + return debugMode ? createDebugHandler : createErrorHandler; + + function createErrorHandler(req, res, status) { + return function handleError(err) { + switch (status) { + case 404: + template404.render().then(function(rendered) { + res.status(status); + res.send(rendered); + }); + break; + default: + template5xx.render().then(function(rendered) { + res.status(status); + res.send(rendered); + }); + } + + }; + } + + function createDebugHandler(req, res, status) { + return function handleError(err) { + res.send(err.stack); + }; + } +} + module.exports = { createApp: function(options) { diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js index ac633e8..161a195 100644 --- a/app/node_modules/okadminview/index.js +++ b/app/node_modules/okadminview/index.js @@ -37,6 +37,8 @@ function OKAdminView(options) { throw new Error('No templateProvider provided to OKAdminView'); if (!options.meta) throw new Error('No meta query provided to OKAdminView'); + if (!options.errorHandler) + throw new Error('No error handler provided to OKAdminView'); var app = options.app; var express = options.express; @@ -44,6 +46,7 @@ function OKAdminView(options) { var resourceCache = this._resourceCache = options.resourceCache; var resourceConfig = this._resourceConfig = cloneDeep(options.resourceConfig); var provider = options.templateProvider; + var error = this._error = options.errorHandler; // Load templates var templates = this._templates = @@ -112,12 +115,10 @@ function OKAdminView(options) { } })); - var auth = passport.authenticate('digest', {session: false}); - // This should really be mounted on the router, but can't be due to // https://github.com/jaredhanson/passport-http/pull/16 - app.use('/_admin/', passport.initialize()); - app.all('/_admin/:path*', auth); + app.use('/admin/', passport.initialize()); + app.all('/admin/:path*', passport.authenticate('digest', {session: false})); router.get('/', function readIndex(req, res, next) { fetchIndexTemplateData(meta, indexQueries).then(function(data) { @@ -125,22 +126,22 @@ function OKAdminView(options) { success: req.flash('success'), errors: req.flash('errors') })); - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); }); - router.get('/:type/new/', function createResourceView(req, res, next) { + router.get('/:type/__new__/', function createResourceView(req, res, next) { var type = req.params.type || ''; var resource = resourceCache.get(type); if (!resource) { - errorHandler(req, res)(new Error('No such resource ' + type)); + error(req, res, 404)(new Error('No such resource ' + type)); } else { meta.get().then(function(metadata) { - var templateData = getResourceTemplateData(metadata, resource, {}); + var templateData = transformData(metadata, resource, {}); view.renderResourceNew(req, res, assign(templateData, { success: req.flash('success'), errors: req.flash('errors'), })); - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); } }); @@ -148,26 +149,27 @@ function OKAdminView(options) { var type = req.params.type || ''; var id = req.params.id || ''; var resource = resourceCache.get(type, id); - if (!resource) { - errorHandler(req, res)(new Error('No such resource')); - } else { - var query = OKQuery({ - resource: resource, - query: id - }); - fetchResourceTemplateData(meta, query, getResourceTemplateData) - .then(function(data) { - if (!data) { - resourceMissingHandler(req, res)() - } else { - view.renderResource(req, res, assign(data, { - success: req.flash('success'), - errors: req.flash('errors') - })); - } - }).fail(errorHandler(req, res)); - } - + var query = OKQuery({ + resource: resource, + query: id + }); + fetchResourceTemplateData(meta, query, transformData) + .then(function(data) { + if (!data) { + resourceMissingHandler(req, res)() + } else { + view.renderResource(req, res, assign(data, { + success: req.flash('success'), + errors: req.flash('errors') + })); + } + }).fail(function(err) { + if (err.message === 'No resource data') { + error(req, res, 404)(new Error('No such resource')); + } else { + error(req, res, 500)(err); + } + }); }); router.post('/:type/', function createResource(req, res, next) { @@ -175,7 +177,7 @@ function OKAdminView(options) { var resource = resourceCache.get(type); var data = req.body; if (!resource) { - errorHandler(req, res)(new Error('No such resource ' + type)); + error(req, res, 400)(new Error('No such resource ' + type)); } else { meta.get().then(function(metadata) { try { @@ -183,26 +185,24 @@ function OKAdminView(options) { resource.create(data).then(function(created) { req.flash('success', {action: 'create'}); res.redirect(303, resource.getID(data)); - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); } catch (errors) { - var templateData = getResourceTemplateData(metadata, resource, data); + var templateData = transformData(metadata, resource, data); view.renderResource(req, res, assign(templateData, {errors: errors})); } - }).fail(errorHandler(req, res));; + }).fail(error(req, res, 500));; } }); - router.put('/:type/__batch/', function putBatch(req, res, next) { + router.put('/:type/__batch__/', function putBatch(req, res, next) { var type = req.params.type; var body = req.body || {}; var resourcesJSON = body[type]; var resource = resourceCache.get(type); if (!resourcesJSON || !resourcesJSON.length) { - res.status(400); - errorHandler(req, res)(new Error('Bad request')); + error(req, res, 400)(new Error('Bad request')); } else if (!resource) { - res.status(404); - errorHandler(req, res)(new Error('No such resource')); + error(req, res, 404)(new Error('No such resource')); } else { try { var ids = []; @@ -212,7 +212,7 @@ function OKAdminView(options) { return data; }); } catch (e) { - errorHandler(req, res)(new Error('Resource batch contains invalid JSON')); + error(req, res, 500)(new Error('Resource batch contains invalid JSON')); return; } Q.all([ @@ -222,7 +222,7 @@ function OKAdminView(options) { var metadata = results.shift(); req.flash('success', {action: 'batch_update'}); res.redirect(303, '../..'); - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); } }); @@ -232,7 +232,7 @@ function OKAdminView(options) { var data = req.body; var resource = resourceCache.get(type, id); if (!resource) { - errorHandler(req, res)(new Error('No such resource ' + type)); + error(req, res, 400)(new Error('No such resource ' + type)); } else { // TODO Prob should make metadata synchronous... meta.get().then(function(metadata) { @@ -241,12 +241,12 @@ function OKAdminView(options) { resource.update(id, data).then(function(updated) { req.flash('success', {action: 'update'}); res.redirect(303, '../' + resource.getID(updated)); - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); } catch (errors) { - var templateData = getResourceTemplateData(metadata, resource, data); + var templateData = transformData(metadata, resource, data); view.renderResource(req, res, assign(templateData, {errors: errors})); } - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); } }); @@ -255,14 +255,14 @@ function OKAdminView(options) { var id = req.params.id; var resource = resourceCache.get(type, id); if (!resource) { - errorHandler(req, res)(new Error('No such resource ' + type)); + error(req, res, 500)(new Error('No such resource ' + type)); } else { meta.get().then(function(metadata) { resource.destroy(id).then(function() { req.flash('success', {action: 'delete'}); res.redirect(303, '../..'); - }).fail(errorHandler(req, res)); - }).fail(errorHandler(req, res)); + }).fail(error(req, res, 500)); + }).fail(error(req, res, 500)); } }); @@ -271,9 +271,9 @@ function OKAdminView(options) { } /** - * Get template data for a single resource + * Yields formatted template data for a single resource */ -function getResourceTemplateData(meta, resource, data) { +function transformData(meta, resource, data) { meta = meta || {}; resource = resource || {}; data = data || {}; @@ -306,21 +306,21 @@ OKAdminView.prototype.renderIndex = function(req, res, data) { data = data || {}; this._templates['index'].render(data).then(function(rendered) { res.send(rendered); - }).fail(errorHandler(req, res)); + }).fail(this._error(req, res, 500)); }; OKAdminView.prototype.renderResource = function(req, res, data) { data = data || {}; this._templates['resource'].render(data).then(function(rendered) { res.send(rendered); - }).fail(errorHandler(req, res)); + }).fail(this._error(req, res, 500)); }; OKAdminView.prototype.renderResourceNew = function(req, res, data) { data = data || {meta: {}, resource: {}}; this._templates['resource_new'].render(data).then(function(rendered) { res.send(rendered); - }).fail(errorHandler(req, res)); + }).fail(this._error(req, res, 500)); }; /** @@ -378,30 +378,15 @@ function fetchResourceTemplateData(meta, query, fn) { return Q.promise(function(resolve, reject) { meta.get().then(function(metadata) { query.get().then(function(data) { - var resource = query.resource; - resolve(fn(metadata, resource, data)); + if (!data) { + reject(new Error('No resource data')); + } else { + var resource = query.resource; + resolve(fn(metadata, resource, data)); + } }).fail(reject); }).fail(reject) }); } -/** - * TODO Real error handling - */ -function errorHandler(req, res) { - return function(err) { - res.send(err.stack); - }; -} - -/** - * TODO Real 404 handling - */ -function resourceMissingHandler(req, res) { - return function() { - res.status(404); - res.send('404'); - } -} - module.exports = OKAdminView; diff --git a/app/node_modules/okdb/index.js b/app/node_modules/okdb/index.js index 85156d1..5aa22a2 100644 --- a/app/node_modules/okdb/index.js +++ b/app/node_modules/okdb/index.js @@ -180,7 +180,7 @@ FSDB.prototype.getMeta = function() { */ function autoincrement(wrapper, schema, data) { return schema.autoIncrementFields.reduce(function(data, field) { - var last = wrapper.chain().sort(field).first().value(); + var last = wrapper.chain().sortByOrder([field], [true]).last().value(); var index = last ? last[field] : -1; var incremented = {}; incremented[field] = (parseInt(index) + 1); diff --git a/app/node_modules/okserver/index.js b/app/node_modules/okserver/index.js index cf06b3c..abca8b5 100644 --- a/app/node_modules/okserver/index.js +++ b/app/node_modules/okserver/index.js @@ -16,6 +16,8 @@ function OKServer(options) { throw new Error('No admin root directory provided to OKServer'); if (!options.adminPath) throw new Error('No admin path provided to OKServer'); + if (!options.errorHandler) + throw new Error('No error handler provided to OKServer'); var root = options.root; var adminRoot = options.adminRoot; var adminPath = options.adminPath; @@ -25,6 +27,7 @@ function OKServer(options) { var router = express.Router({ strict: app.get('strict routing') }); + var error = options.errorHandler; var services = options.services || {}; Object.keys(views) // Sort such that more general routes are matched last @@ -67,6 +70,10 @@ function OKServer(options) { // Make sure this lady is last. Checks whether the desired // route has a trailing-slash counterpart and redirects there app.use(slash()); + // Otherwise it's a 404 + app.use(function(req, res) { + error(req, res, 404)(new Error('No matching route')); + }); } OKServer.prototype.listen = function listen(port) { diff --git a/app/node_modules/okview/index.js b/app/node_modules/okview/index.js index 2d1c0a0..63f22b5 100644 --- a/app/node_modules/okview/index.js +++ b/app/node_modules/okview/index.js @@ -24,8 +24,11 @@ function OKView(options) { throw new Error('No meta resource provided to OKView'); if (!options.route) throw new Error('No route provided to OKView'); + if (!options.errorHandler) + throw new Error('No error handler provided to OKView'); var route = options.route; var mount = options.mount || 'get'; + var error = this._error = options.errorHandler; this._template = options.template; var meta = this._meta = options.meta; var queries = this._queries = options.queries || []; @@ -47,16 +50,9 @@ function OKView(options) { enumerable: true }); - this._middleware = createMiddleware(this); - this._fetchTemplateData = unbound ? fetchUnbound : fetchBound; - - function fetchUnbound(id) { - return fetchTemplateData(meta, queries, id) - } - - function fetchBound() { - return fetchTemplateData(meta, queries); - } + this._middleware = unbound + ? unboundMiddleware(this, meta, queries, error) + : boundMiddleware(this, meta, queries, error); } OKView.prototype.middleware = function() { @@ -66,39 +62,20 @@ OKView.prototype.middleware = function() { OKView.prototype.render = function(req, res, data) { this._template.render(data).then(function(html) { res.send(html); - }).fail(errorHandler(req, res, data)); -}; - -OKView.prototype.fetchTemplateData = function() { - return this._fetchTemplateData.apply(this, arguments); + }).fail(this._error(req, res, 500)); }; /** - * Unbound views need different middleware to resolve requests - */ -function createMiddleware(view) { - if (view.unbound) { - return unboundMiddleware(view); - } else { - return boundMiddleware(view); - } -} - -// Note that these middleware do not call next -// and should thus always be added at the end of the -// middleware chain. - -/** * Creates middleware for a view which does not * yet have a resource id associated with it */ -function unboundMiddleware(view) { +function unboundMiddleware(view, meta, queries, error) { var paramName = getParamName(view.route); return function(req, res, next) { var id = req.params[paramName]; - view.fetchTemplateData(id).then(function(data) { + fetchTemplateData(meta, queries, id).then(function(data) { view.render(req, res, data); - }).fail(errorHandler(req, res, next)); + }).fail(failHandler(req, res, error)); }; } @@ -106,20 +83,22 @@ function unboundMiddleware(view) { * Creates middleware for a view which already * has a resource id associated with it */ -function boundMiddleware(view) { +function boundMiddleware(view, meta, queries, error) { return function(req, res, next) { - view.fetchTemplateData().then(function(data) { + fetchTemplateData(meta, queries).then(function(data) { view.render(req, res, data); - }).fail(errorHandler(req, res, next)); + }).fail(failHandler(req, res, error)); }; } -/** - * TODO BS error handling for now - */ -function errorHandler(req, res, next) { - return function(err) { - res.send(err.stack); +function failHandler(req, res, error) { + return function (err) { + // TODO Use custom exception type + if (err.message === 'No resource found') { + error(req, res, 404)(err); + } else { + error(req, res, 500)(err); + } } } @@ -138,9 +117,12 @@ function getParamName(route) { * and returns a promise for an object merging all queried * data, pluralizing keys where necessary. * - * Lil bit convoluted, sorry. + * Pretty convoluted, sorry. */ function fetchTemplateData(meta, queries, id) { + // If there's only one query, we assume it is for a single + // resource and will resolve errors if no data is found + var single = queries && queries.length === 1; return Q.promise(function(resolve, reject) { return Q.all( [meta.get()].concat(queries.map(function(query) { @@ -148,39 +130,44 @@ function fetchTemplateData(meta, queries, id) { }))) .then(function(results) { var metadata = results.shift(); - var normalized = results.reduce(function(cache, result, i) { - // Could be just some rogue request - if (!result) { - return cache; - } - var resource = queries[i].resource; - var type = queries[i].type; - var manyResult = isarray(result); - // Inform template of ID in generic field - if (manyResult) { - result = result.map(function(data) { - return assign({}, data, {id: resource.getID(data)}) - }); - } else { - result = assign({}, result, {id: resource.getID(result)}); - } - // If we have a lot of results for a certain type, - // we pluralize the key and yield an array of results - if (cache[type] || manyResult) { - var plural = pluralize(type); - delete cache[type]; - cache[plural] = []; + if (single && !results[0]) { + reject(new Error('No resource found')); + } else { + var normalized = results.reduce(function(cache, result, i) { + // Could be just some rogue request + if (!result) { + return cache; + } + var resource = queries[i].resource; + var type = queries[i].type; + var manyResult = isarray(result); + // Inform template of ID in generic field if (manyResult) { - cache[plural] = cache[plural].concat(result); + result = result.map(function(data) { + return assign({}, data, {id: resource.getID(data)}) + }); + } else { + result = assign({}, result, {id: resource.getID(result)}); + } + // If we have a lot of results for a certain type, + // we pluralize the key and yield an array of results + if (cache[type] || manyResult) { + var plural = pluralize(type); + delete cache[type]; + cache[plural] = []; + if (manyResult) { + cache[plural] = cache[plural].concat(result); + } else { + cache[plural].push(result); + } } else { - cache[plural].push(result); + cache[type] = result; } - } else { - cache[type] = result; - } - return cache; - }, {meta: metadata}); - resolve(normalized); + return cache; + }, {meta: metadata}); + + resolve(normalized); + } }).fail(reject); }); } diff --git a/examples/index.js b/examples/index.js index 95d2bcf..3e9f509 100644 --- a/examples/index.js +++ b/examples/index.js @@ -4,6 +4,8 @@ var app = okcms.createApp({ root: 'public', + debug: false, + schemas: { page: { id: {type: 'string'}, diff --git a/site/index.js b/site/index.js index a59e74e..dace900 100644 --- a/site/index.js +++ b/site/index.js @@ -15,15 +15,15 @@ var app = okcms.createApp({ id: {type: 'string', id: true}, title: {type: 'string'}, shortname: {type: 'string'}, - description: {type: 'text'}, - video: {type: 'video'}, - images: {type: 'captioned-image-list'}, category: {type: 'enum', options: [ 'retail', 'advertising', 'experiential', 'content'] - } + }, + description: {type: 'text'}, + video: {type: 'video'}, + images: {type: 'captioned-image-list'}, } }, diff --git a/themes/okadmin/public/css/main.css b/themes/okadmin/public/css/main.css index 3762fd4..67271bc 100644 --- a/themes/okadmin/public/css/main.css +++ b/themes/okadmin/public/css/main.css @@ -109,7 +109,7 @@ h2 { pointer-events: none; } - /* Makes the button look like a link */ +/* Makes the button look like a link */ .main.index .resource-category button { background: none !important; height: 1.5em; @@ -117,6 +117,8 @@ h2 { padding: 0 !important; font: inherit; cursor: pointer; + font-family: Monaco, monospace; + text-transform: uppercase; } .main.index .resource-category .btn { @@ -134,7 +136,7 @@ h2 { } .main.index .resource-category .btn:hover { - border-bottom: 3px solid rgba(0, 0, 0, 0.25); + border-bottom: 1px solid rgba(0, 0, 0, 0.25); } .main.index .resource-category .btn { @@ -203,6 +205,12 @@ label { padding: 0 0.5em; margin-bottom: 1em; } +.main.resource form input[name=id] { + width: 15em; +} +button, input[type=submit] { + cursor: pointer; +} .main.resource form .group { display: block; float: left; @@ -280,18 +288,26 @@ label { background: #ddd; clear: left; text-align: left; - padding: 10px; - width: 15em; + float: left; + margin-right: 1em; position: relative; + overflow: hidden; + cursor: pointer; } .add-image-button:hover { background: #def; } +.main.resource form .add-image-button button { + margin: 0; + pointer-events: none; + width: 100%; height: 100%; +} .add-image-button input[type=file] { opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; + margin: 0; padding: 0; cursor: pointer; } li.image-element:hover .remove-image { @@ -314,13 +330,20 @@ li.image-element .remove-image:hover { } -.errors { +.success, .errors { background: white; - padding: 10px; - width: 100%; + padding: 9px 8px 7px; + width: 50%; line-height: 1.4em; + border: 1px solid; + margin: 1em; + border-radius: 2px; +} + +.success { + color: green; } -.errors .message { +.errors { color: red; } diff --git a/themes/okadmin/public/js/app.js b/themes/okadmin/public/js/app.js index 1ab9956..91a8e1a 100644 --- a/themes/okadmin/public/js/app.js +++ b/themes/okadmin/public/js/app.js @@ -28,7 +28,7 @@ var OKAdmin = function(){ $(".captioned-image-list ol").disableSelection() // delete image - $(document).on("click", ".remove-image", function(){ + $(document).on("mousedown", ".remove-image", function(){ if (confirm("Delete this image?")) { $(this).parent().remove() } @@ -50,7 +50,14 @@ var OKAdmin = function(){ })) // fix post indexing in list-driven inputs - $(".main.resource form").submit(function(){ + $(".main.resource form").submit(function(e){ + var $id = $("[name=id]") + if ($id.length && ! $id.val()) { + alert("Please enter an ID") + $id.focus() + e.preventDefault() + return + } $(".image-element").each(function(index){ $(this).find("input,textarea").each(function(){ var field = $(this).attr("name").replace(/\[[0-9]*\]/, "[" + index + "]") diff --git a/themes/okadmin/templates/404.liquid b/themes/okadmin/templates/404.liquid new file mode 100644 index 0000000..87f5342 --- /dev/null +++ b/themes/okadmin/templates/404.liquid @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <title>404</title> + <style type="text/css"> + html, body { + margin: 0; + padding: 0; + font-family: "Helvetica", sans-serif; + background-image: url('http://okfoc.us/assets/images/photocopy.png'); + background-position: bottom center; + background-repeat: repeat; + background-attachment: scroll; + height: 100%; + font-size: 1.75em; + font-weight: bold; + color: #FFFFFF; + } + + a { + color: #8888FF; + text-decoration: none; + } + + a:hover { + border-bottom: 3px solid #8888FF; + } + + a:visited { + color: #8888FF; + } + + .message { + width: 700px; + padding: 1em 1em 1em 1em; + background-color: #0000FF; + margin: 0 auto; + margin-top: 1em; + } + + .message p:first-child { + margin-top: 0; + } + </style> + </head> + <body> + <div class="message"> + <p>¯\_(ツ)_/¯</p> + <p>We couldn't find that page.</p> + <p>Sure you have the right URL?</p> + <a href="javascript:history.back()">Back</a> + </div> + </body> +</html> diff --git a/themes/okadmin/templates/5xx.liquid b/themes/okadmin/templates/5xx.liquid new file mode 100644 index 0000000..f245545 --- /dev/null +++ b/themes/okadmin/templates/5xx.liquid @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <title>404</title> + <style type="text/css"> + html, body { + margin: 0; + padding: 0; + font-family: "Helvetica", sans-serif; + background-image: url('http://okfoc.us/assets/images/photocopy.png'); + background-position: bottom center; + background-repeat: repeat; + background-attachment: scroll; + height: 100%; + font-size: 1.75em; + font-weight: bold; + color: #FFFFFF; + } + + a { + color: #8888FF; + text-decoration: none; + } + + a:hover { + border-bottom: 3px solid #8888FF; + } + + a:visited { + color: #8888FF; + } + + .message { + width: 700px; + padding: 0 1em 1em 1em; + background-color: #0000FF; + margin: 0 auto; + margin-top: 1em; + } + + .message p:first-child { + margin-top: 0; + } + </style> + </head> + <body> + <div class="message"> + <p>(;一_一)</p> + <p>Looks like we experienced an error.</p> + <p>Sorry about that. Maybe try again later.</p> + <a href="javascript:history.back()">Back</a> + </div> + </body> +</html> diff --git a/themes/okadmin/templates/index.liquid b/themes/okadmin/templates/index.liquid index 0672613..330ed89 100644 --- a/themes/okadmin/templates/index.liquid +++ b/themes/okadmin/templates/index.liquid @@ -8,7 +8,7 @@ {% assign resource = pair[1] %} <section class="resource-category {{name}}"> - <form action="{{resource.type}}/__batch/" method="POST"> + <form action="{{resource.type}}/__batch__/" method="POST"> <header> <h2>{{name | capitalize}}</h2> </header> @@ -26,8 +26,8 @@ <a class="btn cancel-btn" href="#">cancel</a> <button type="submit" class="btn save-btn" href="#">save</button> - <a class="btn edit-btn active" href="#">edit</a> - <a class="btn add-btn active" href="{{resource.type}}/new/">+</a> + <a class="btn edit-btn active" href="#">sort</a> + <a class="btn add-btn active" href="{{resource.type}}/__new__/">+</a> </nav> </footer> </form> diff --git a/themes/okadmin/templates/partials/flash.liquid b/themes/okadmin/templates/partials/flash.liquid index 1980ab5..e51a86b 100644 --- a/themes/okadmin/templates/partials/flash.liquid +++ b/themes/okadmin/templates/partials/flash.liquid @@ -1,8 +1,15 @@ +{% if success.length > 0 %} <div class="success"> + <div class="message">Changes saved.</div> + <!-- {% for info in success %} <div class="message">{{info.action}}</div> {% endfor %} + --> </div> +{% endif %} + +{% if errors.length > 0 %} <div class="errors"> {% for error in errors %} <div class="error"> @@ -10,4 +17,4 @@ </div> {% endfor %} </div> - +{% endif %}
\ No newline at end of file diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index 99258f3..b9cf7a3 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -29,7 +29,7 @@ {% endif %} name="{{name}}"> {% for option in spec.options %} - <option value="{{option}}" {% if option == spec.value %}selected{% endif %}>{{option}}</option> + <option value="{{option}}" {% if option == spec.value %}selected{% endif %}>{{option | capitalize}}</option> {% endfor %} </select> {% elsif type == 'video' %} @@ -56,7 +56,7 @@ </ol> <div class="add-image-button"> <input id="file" type="file" accept="image/*" multiple> - <span>+ Add images</span> + <button>+ Add images</button> </div> <input id="add-image-url" type="text" placeholder="+ Add URL"> <script type="text/html" id="captioned-image-template"> diff --git a/themes/okadmin/templates/partials/tail.liquid b/themes/okadmin/templates/partials/tail.liquid index 88764a6..b3c575d 100644 --- a/themes/okadmin/templates/partials/tail.liquid +++ b/themes/okadmin/templates/partials/tail.liquid @@ -2,8 +2,8 @@ </body> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.6.0/lodash.min.js"></script> - <script src="/_admin/js/jqueryui-draggable.js"></script> - <script src="/_admin/js/upload.js"></script> - <script src="/_admin/js/parser.js"></script> - <script src="/_admin/js/app.js"></script> + <script src="/admin/js/jqueryui-draggable.js"></script> + <script src="/admin/js/upload.js"></script> + <script src="/admin/js/parser.js"></script> + <script src="/admin/js/app.js"></script> </html> diff --git a/themes/okadmin/templates/resource.liquid b/themes/okadmin/templates/resource.liquid index 8078778..abc59e9 100644 --- a/themes/okadmin/templates/resource.liquid +++ b/themes/okadmin/templates/resource.liquid @@ -16,7 +16,7 @@ </form> <form action="." method="POST" id="delete_form"> <input type="hidden" name="_method" value="DELETE"> - <button type="submit">Delete</button> + <button type="submit">Delete Record</button> </form> </section> |
