From 0fc380788157779a39fe630b2464280eea9f5088 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Wed, 8 Apr 2015 14:06:30 -0400 Subject: Add resource instance concept Can now define resource classes bound to particular data points, in particular they can be static data defined by app config and not even in DB --- app/index.js | 56 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 12 deletions(-) (limited to 'app/index.js') diff --git a/app/index.js b/app/index.js index c8dceb5..ca09079 100644 --- a/app/index.js +++ b/app/index.js @@ -53,12 +53,13 @@ function OKCMS(options) { } }; var schemas = this._schemas = this._createSchemas(schemaConfig); - var resources = this._resources = + var resourceCache = this._resourceCache = this._createResources(resourceConfig, db, schemas); // Create view instances from config var views = this._views = - this._createViews(viewConfig, db, meta, resources, templateProvider); + this._createViews(viewConfig, db, meta, resourceCache, templateProvider); + var server = this._server = new OKServer({ express: express, app: app, @@ -85,22 +86,24 @@ OKCMS.prototype._createSchemas = function(schemaConfig) { OKCMS.prototype._createResources = function(resourceConfig, db, schemaCache) { resourceConfig = resourceConfig || {}; - var typeCache = {}; - return resourceConfig.reduce(function(cache, config) { + var resources = resourceConfig.map(function(config) { var type = config.type; var schema = schemaCache[type]; if (!schema) throw new Error('Resource config references nonexistent schema'); - // If we already created resource class, just skip - if (cache[type]) - return cache; - cache[type] = OKResource({ + var resource = OKResource({ type: type, db: db, schema: schema - }) - return cache; - }, {}); + }); + // Static resources have some data defined by configuration and + // are a special case + if (config.static) { + resource = resource.instance({static: config.static}); + } + return resource; + }); + return ResourceCache(resources); }; OKCMS.prototype._createViews = function(viewConfig, db, @@ -150,7 +153,7 @@ OKCMS.prototype._createQueries = function(queryConfig, resourceCache) { queryConfig = [queryConfig]; return queryConfig.map(function(config) { var type = config.type; - var resource = resourceCache[type]; + var resource = resourceCache.get(type); if (!resource) throw new Error('Query configured with nonexistent resource'); // Default to "select all" query @@ -162,6 +165,35 @@ OKCMS.prototype._createQueries = function(queryConfig, resourceCache) { }); }; +/** + * Stupid lil cache to help deal with the fact that + * resources can be indexed by either type or a type + id combo. + */ +function ResourceCache(resources) { + if (!(this instanceof ResourceCache)) return new ResourceCache(resources); + resources = resources || []; + var cache = this._cache = {}; + resources.forEach(function(resource) { + if (!resource) + throw new Error('Undefined resource given to ResourceCache'); + if (resource.bound) { + cache[resource.type] = resource.parent; + cache[resource.type + ':' + resource.id] = resource; + } else { + cache[resource.type] = resource; + } + }); +} + +ResourceCache.prototype.get = function(type, id) { + if (!type) return; + if (id && this._cache[type + ':' + id]) { + return this._cache[type + ':' + id]; + } else { + return this._cache[type]; + } +}; + module.exports = { createApp: function(options) { -- cgit v1.2.3-70-g09d2 From 5d676437f64791b435d7554d9ec4f4628d0abcc3 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Wed, 8 Apr 2015 15:07:50 -0400 Subject: Remove unused REST endpoint module --- app/index.js | 5 +---- app/node_modules/okrest/index.js | 20 -------------------- app/node_modules/okrest/package.json | 11 ----------- 3 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 app/node_modules/okrest/index.js delete mode 100644 app/node_modules/okrest/package.json (limited to 'app/index.js') diff --git a/app/index.js b/app/index.js index ca09079..0e83363 100644 --- a/app/index.js +++ b/app/index.js @@ -10,7 +10,6 @@ var OKDB = require('okdb'); var OKResource = require('okresource') var OKTemplate = require('oktemplate'); var OKServer = require('okserver'); -var OKRestEndpoint = require('okrest'); var OKSchema = require('okschema'); /** @@ -202,8 +201,6 @@ module.exports = { OKResource: OKResource, - OKView: OKView, - - OKRestEndpoint: OKRestEndpoint + OKView: OKView }; diff --git a/app/node_modules/okrest/index.js b/app/node_modules/okrest/index.js deleted file mode 100644 index 169626d..0000000 --- a/app/node_modules/okrest/index.js +++ /dev/null @@ -1,20 +0,0 @@ -var OKView = require('okview'); - -/** - * OKRestEndpoint! - * Takes a resources and creates a CRUD endpoint. - */ -function OKRestEndpoint(resource, options) { - if (!(this instanceof OKRestEndpoint)) return new OKRestEndpoint(resource, options); - options = options || {}; - this._resource = resource; -} - -OKRestEndpoint.prototype.middleware = function() { - var self = this; - return function handleREST(req, res, next) { - res.send(self._resource.name); - }; -} - -module.exports = OKRestEndpoint; diff --git a/app/node_modules/okrest/package.json b/app/node_modules/okrest/package.json deleted file mode 100644 index 462c890..0000000 --- a/app/node_modules/okrest/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "okrest", - "version": "1.0.0", - "description": "nice", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "OKFocus", - "license": "None" -} -- cgit v1.2.3-70-g09d2 From bb3d8da23279dc2e4cf275b08b03148b3980fb01 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Wed, 8 Apr 2015 18:28:26 -0400 Subject: Fix bug where views didn't resolve resource correctly --- app/index.js | 2 +- app/node_modules/okview/index.js | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'app/index.js') diff --git a/app/index.js b/app/index.js index 0e83363..792c10a 100644 --- a/app/index.js +++ b/app/index.js @@ -152,7 +152,7 @@ OKCMS.prototype._createQueries = function(queryConfig, resourceCache) { queryConfig = [queryConfig]; return queryConfig.map(function(config) { var type = config.type; - var resource = resourceCache.get(type); + var resource = resourceCache.get(type, config.query); if (!resource) throw new Error('Query configured with nonexistent resource'); // Default to "select all" query diff --git a/app/node_modules/okview/index.js b/app/node_modules/okview/index.js index e9c0bfe..c245b0c 100644 --- a/app/node_modules/okview/index.js +++ b/app/node_modules/okview/index.js @@ -51,8 +51,11 @@ function OKView(options) { this._fetchTemplateData = unbound ? fetchUnbound : fetchBound; function fetchUnbound(id) { - // TODO Janky - return fetchTemplateData(meta, [queries[0].get(id)]); + var resource = queries[0].resource; + return fetchTemplateData(meta, [OKQuery({ + resource: resource, + query: id + })]); } function fetchBound() { @@ -150,9 +153,10 @@ function fetchTemplateData(meta, queries) { .then(function(results) { var metadata = results.shift(); var normalized = results.reduce(function(cache, result, i) { - // Huh? Bail - if (!result) + // Could be just some rogue request + if (!result) { return cache; + } var resource = queries[i].resource; var type = queries[i].type; var manyResult = isarray(result); -- cgit v1.2.3-70-g09d2 From 51296eb133599d98dd05d04399898c42b1662ab5 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Wed, 8 Apr 2015 19:53:43 -0400 Subject: Add static root to meta template info --- app/index.js | 36 +++++++++++++++++++++++++++++------- package.json | 3 ++- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'app/index.js') diff --git a/app/index.js b/app/index.js index 792c10a..94d87dc 100644 --- a/app/index.js +++ b/app/index.js @@ -4,6 +4,7 @@ var withTrailingSlash = require('okutil').withTrailingSlash; var withoutTrailingSlash = require('okutil').withoutTrailingSlash; var assign = require('object-assign'); var express = require('express'); +var Q = require('q'); var OKQuery = require('okquery'); var OKView = require('okview'); var OKDB = require('okdb'); @@ -32,6 +33,34 @@ function OKCMS(options) { var adminTemplateRoot = options.templateRoot || path.join(__dirname, '../themes/okadmin/templates'); + // Set metadata defaults + // TODO Abstract this out somewhere else + var meta = { + type: 'meta', + get: function() { + return Q.promise(function(resolve, reject) { + db.getMeta().then(function(metadata) { + resolve(assign({}, { + static: '' + }, metadata)); + }).fail(reject); + }); + } + }; + + var adminMeta ={ + type: 'meta', + get: function() { + return Q.promise(function(resolve, reject) { + db.getMeta().then(function(metadata) { + resolve(assign({}, { + static: withoutTrailingSlash(adminPath) + }, metadata)); + }).fail(reject); + }); + } + }; + var schemaConfig = options.schemas || {}; var resourceConfig = options.resources || []; var viewConfig = options.views || { @@ -44,13 +73,6 @@ function OKCMS(options) { new OKTemplate({root: adminTemplateRoot}); var db = new OKDB(options.db || 'fs'); - // Special query to get project wide meta data - var meta = this._meta = { - type: 'meta', - get: function() { - return db.getMeta(); - } - }; var schemas = this._schemas = this._createSchemas(schemaConfig); var resourceCache = this._resourceCache = this._createResources(resourceConfig, db, schemas); diff --git a/package.json b/package.json index bb8a39b..58f04e1 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "author": "OKFocus", "license": "None", "dependencies": { - "express": "^4.12.3" + "express": "^4.12.3", + "q": "^1.2.0" } } -- cgit v1.2.3-70-g09d2 From 32f87fcb58466e0e311349f96751c18e2a2cd7ea Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Wed, 8 Apr 2015 19:55:34 -0400 Subject: Administrate! --- app/index.js | 42 +++- app/node_modules/okadminview/index.js | 320 ++++++++++++++++++++++++ app/node_modules/okadminview/package.json | 17 ++ package.json | 1 + themes/okadmin/public/css/main.css | 103 ++++++++ themes/okadmin/templates/index.liquid | 26 ++ themes/okadmin/templates/partials/head.liquid | 13 + themes/okadmin/templates/partials/inputs.liquid | 19 ++ themes/okadmin/templates/partials/tail.liquid | 3 + themes/okadmin/templates/resource.liquid | 15 ++ themes/okadmin/templates/resource_new.liquid | 15 ++ 11 files changed, 571 insertions(+), 3 deletions(-) create mode 100644 app/node_modules/okadminview/index.js create mode 100644 app/node_modules/okadminview/package.json create mode 100644 themes/okadmin/public/css/main.css create mode 100644 themes/okadmin/templates/index.liquid create mode 100644 themes/okadmin/templates/partials/head.liquid create mode 100644 themes/okadmin/templates/partials/inputs.liquid create mode 100644 themes/okadmin/templates/partials/tail.liquid create mode 100644 themes/okadmin/templates/resource.liquid create mode 100644 themes/okadmin/templates/resource_new.liquid (limited to 'app/index.js') diff --git a/app/index.js b/app/index.js index 94d87dc..4ec8730 100644 --- a/app/index.js +++ b/app/index.js @@ -7,6 +7,7 @@ var express = require('express'); var Q = require('q'); var OKQuery = require('okquery'); var OKView = require('okview'); +var OKAdminView = require('okadminview'); var OKDB = require('okdb'); var OKResource = require('okresource') var OKTemplate = require('oktemplate'); @@ -80,11 +81,16 @@ function OKCMS(options) { // Create view instances from config var views = this._views = this._createViews(viewConfig, db, meta, resourceCache, templateProvider); + var adminViews = this._adminViews = + this._createAdminViews(adminPath, app, express, resourceConfig, + resourceCache, adminTemplateProvider, adminMeta); var server = this._server = new OKServer({ express: express, app: app, - views: views, + // Merge admin views with normal views + views: assign(views, adminViews), + // Specify root folders and paths for serving static assets root: root, adminRoot: adminRoot, adminPath: adminPath @@ -166,7 +172,37 @@ OKCMS.prototype._createViews = function(viewConfig, db, else return '404'; } -} +}; + +OKCMS.prototype._createAdminViews = function(path, app, express, + resourceConfig, resourceCache, templateProvider, meta) { + var views = {}; + var withTrail = withTrailingSlash(path); + var withoutTrail = withoutTrailingSlash(path); + // Stoopid fix for a bug in Express. Need to do this + // to ensure strict routing is not broken for the nested + // admin router. + // See: https://github.com/strongloop/express/issues/2281 + // TODO Get rid of this crap + views[withoutTrail] = { + mount: 'get', + middleware: function() { + return function(req, res) { + res.redirect(301, withTrail); + } + } + }; + // Add real view at trailing slash route + views[withTrail] = OKAdminView({ + app: app, + express: express, + resourceConfig: resourceConfig, + resourceCache: resourceCache, + templateProvider: templateProvider, + meta: meta + }); + return views; +}; OKCMS.prototype._createQueries = function(queryConfig, resourceCache) { queryConfig = queryConfig || {}; @@ -218,7 +254,7 @@ ResourceCache.prototype.get = function(type, id) { module.exports = { createApp: function(options) { - return new OKCMS(options); + return OKCMS(options); }, OKResource: OKResource, diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js new file mode 100644 index 0000000..42e7f36 --- /dev/null +++ b/app/node_modules/okadminview/index.js @@ -0,0 +1,320 @@ +var bodyParser = require('body-parser'); +var methodOverride = require('method-override'); +var Q = require('q'); +var pluralize = require('pluralize'); +var OKQuery = require('okquery'); + +/** + * OKAdminView! + */ +function OKAdminView(options) { + if (!(this instanceof OKAdminView)) return new OKAdminView(options); + if (!options.app) + throw new Error('No Express app provided to OKAdminView'); + if (!options.express) + throw new Error('No Express provided to OKAdminView'); + if (!options.resourceConfig) + throw new Error('No resourceConfig provided to OKAdminView'); + if (!options.resourceCache) + throw new Error('No resourceCache provided to OKAdminView'); + if (!options.templateProvider) + throw new Error('No templateProvider provided to OKAdminView'); + if (!options.meta) + throw new Error('No meta query provided to OKAdminView'); + var app = options.app; + var express = options.express; + var meta = options.meta; + var resourceCache = this._resourceCache = options.resourceCache; + var resourceConfig = this._resourceConfig = options.resourceConfig; + var provider = options.templateProvider; + // Load templates + var templates = this._templates = + ['index', 'resource', 'resource_new'].reduce(function(cache, name) { + var template = provider.getTemplate(name); + if (!template) + throw new Error('Admin theme does not have needed template: "' + name + '"'); + cache[name] = template; + return cache; + }, {}); + // OKAdmin middleware is a router, so mounts on 'use' + Object.defineProperty(this, 'mount', { + value: 'use', + writable: false, + enumerable: true + }); + + // Resources which apper on the index are a function of + // the resource configuration. + var indexQueries = this._indexQueries = Object.keys(resourceConfig) + .map(function(key) { + var config = resourceConfig[key]; + var type = config.type; + var staticData = config.static || {}; + var resource = resourceCache.get(config.type); + if (!resource) + throw new Error('Something weird is going on'); + var id = staticData[resource.idField]; + resource = resourceCache.get(type, id) || resource; + if (resource.bound) { + // Resource instances implement the query API + return OKQuery({resource: resource});; + } else { + return OKQuery({resource: resource, query: config.query}) + } + }); + + this._middleware = createMiddleware(this); + + function createMiddleware(view) { + var router = express.Router({ + strict: app.get('strict routing') + }); + + // Parse form data + router.use(bodyParser.urlencoded({extended: true})); + // HTML forms only support POST and GET methods + // We extend this by adding hidden input fields to the forms which + // specify the actual method desired. + router.use(methodOverride(function(req, res) { + // Parse out the hidden field + if (req.body && typeof req.body === 'object' && '_method' in req.body) { + var method = req.body._method; + delete req.body._method + return method + } + })); + + router.get('/', function readIndex(req, res, next) { + fetchIndexTemplateData(meta, indexQueries).then(function(data) { + view.renderIndex(req, res, data); + }).fail(errorHandler(req, res)); + }); + + 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)); + } else { + meta.get().then(function(metadata) { + view.renderResourceNew(req, res, { + meta: metadata, + resource: { + type: resource.type, + spec: resource.spec + } + }); + }).fail(errorHandler(req, res)); + } + }); + + + router.get('/:type/:id/', function readResource(req, res, next) { + 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, transform).then(function(data) { + if (!data) { + resourceMissingHandler(req, res)() + } else { + view.renderResource(req, res, data); + } + }).fail(errorHandler(req, res)); + } + + function transform(meta, resource, data) { + meta = meta || {}; + resource = resource || {}; + data = data || {}; + var spec = Object.keys(resource.spec).reduce(function(cache, prop) { + var propSpec = resource.spec[prop]; + var value = data[prop]; + cache[prop].id = data[resource.idField]; + cache[prop].value = value; + return cache; + }, resource.spec); + return { + meta: meta, + resource: { + type: resource.type, + spec: spec + } + }; + } + }); + + router.post('/:type/', function createResource(req, res, next) { + var type = req.params.type; + var resource = resourceCache.get(type); + var data = req.body; + if (!resource) { + errorHandler(req, res)(new Error('No such resource ' + type)); + } else { + meta.get().then(function(metadata) { + var templateData = { + meta: metadata, + resource: { + type: resource.type, + spec: resource.spec, + data: data + } + }; + try { + resource.assertValid(data); + resource.create(data).then(function(created) { + res.redirect(303, data[resource.idField]); + }).fail(errorHandler(req, res)); + } catch (errors) { + view.renderResource(req, res, templateData); + } + }).fail(errorHandler(req, res));; + } + }); + + router.put('/:type/:id/', function updateResource(req, res, next) { + var type = req.params.type; + var id = req.params.id; + var data = req.body; + var resource = resourceCache.get(type, id); + if (!resource) { + errorHandler(req, res)(new Error('No such resource ' + type)); + } else { + // TODO Maybe should make metadata synchronous... + meta.get().then(function(metadata) { + var templateData = { + meta: metadata, + resource: { + spec: resource.spec, + data: data + } + }; + try { + resource.assertValid(data); + resource.update(id, data).then(function(updated) { + res.redirect(303, '../' + updated[resource.idField]); + }).fail(errorHandler(req, res)); + } catch (errors) { + view.renderResource(req, res, templateData); + } + }).fail(errorHandler(req, res)); + } + }); + + return router; + } +} + +OKAdminView.prototype.middleware = function() { + return this._middleware; +}; + +OKAdminView.prototype.renderIndex = function(req, res, data) { + data = data || {}; + this._templates['index'].render(data).then(function(rendered) { + res.send(rendered); + }).fail(errorHandler(req, res)); +}; + +OKAdminView.prototype.renderResource = function(req, res, data) { + data = data || {}; + this._templates['resource'].render(data).then(function(rendered) { + res.send(rendered); + }).fail(errorHandler(req, res)); +}; + +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)); +}; + +/** + * Annotate template data with schema info + */ +function fetchIndexTemplateData(meta, queries) { + return Q.Promise(function(resolve, reject) { + Q.all([meta.get()].concat(queries.map(function(query) { + return query.get(); + }))).then(function(results) { + var meta = results.shift(); + var resources = results.reduce(function(cache, result, i) { + if (!result) + return cache; + var resource = queries[i].resource; + // We want the raw object spec + var spec = resource.spec; + var key = pluralize(resource.type); + if (!cache[key]) { + cache[key] = { + type: resource.type, + spec: spec, + data: [] + }; + } + + if (result.length) { + result.forEach(addToCache) + } else { + addToCache(result); + } + + function addToCache(data) { + // Report id to template under standard name + data.id = data[resource.idField]; + cache[key].data.push(data); + } + + return cache; + }, {}); + + resolve({ + meta: meta, + resources: resources + }); + }).fail(reject); + }); +} + +/** + * Annotate template data with schema info + */ +function fetchResourceTemplateData(meta, query, fn) { + fn = fn || function(m, r, d) { return {meta: m, resource: d}; }; + 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)); + }).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/okadminview/package.json b/app/node_modules/okadminview/package.json new file mode 100644 index 0000000..b07cb1a --- /dev/null +++ b/app/node_modules/okadminview/package.json @@ -0,0 +1,17 @@ +{ + "name": "okadminview", + "version": "1.0.0", + "description": "administrate!", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "OKFocus", + "license": "None", + "dependencies": { + "body-parser": "^1.12.2", + "method-override": "^2.3.2", + "pluralize": "^1.1.2", + "q": "^1.2.0" + } +} diff --git a/package.json b/package.json index 58f04e1..1e7c1cd 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "license": "None", "dependencies": { "express": "^4.12.3", + "object-assign": "^2.0.0", "q": "^1.2.0" } } diff --git a/themes/okadmin/public/css/main.css b/themes/okadmin/public/css/main.css new file mode 100644 index 0000000..88628e3 --- /dev/null +++ b/themes/okadmin/public/css/main.css @@ -0,0 +1,103 @@ +html, body { + margin: 0; + padding: 0; + font-family: "Helvetica", sans-serif; + font-size: 16px; +} + +ul { + padding: 0; + list-style: none; +} + +a { + color: #0000ff; + text-decoration: none; +} + +a:hover { + border-bottom: 3px solid #0000ff; +} + +a:visited { + color: #0000ff; +} + +.admin-header { + height: 50px; + background-color: rgb(233, 233, 233); +} + +.admin-header .breadcrumb { + margin-left: 2em; + font-size: 2em; + color: rgba(0, 0, 0, 0.25); + line-height: 50px; +} + +.admin-header .site-link { + font-size: 1.5em; + float: right; + margin-right: 10%; + line-height: 50px; +} + +.main.index .resource-category { + float: left; + min-width: 200px; + margin: 1em; + padding: 1em; + background-color: rgba(0, 0, 0, 0.1); +} + +.main.index .resource-category a.add-new { + border-bottom: 3px solid rgba(0, 0, 0, 0); + float: right; + font-size: 1.5em; + color: rgba(0, 0, 0, 0.25); +} + +.main.index .resource-category li { + margin: 1em 0; +} + +.main.index .resource-category a.add-new:hover { + border-bottom: 3px solid rgba(0, 0, 0, 0.25); +} + +.main.resource > * { + margin: 1em 1em; +} + +.main.resource form { + background-color: rgba(0, 0, 0, 0.1); + max-width: 500px; + padding: 1em; + font-size: 1.25em; +} + +.main.resource form label { + display: block; + margin-bottom: 0.25em; + color: rgba(0, 0, 0, 0.75); +} + +.main.resource form .property { + margin: 1em 0; +} + +.main.resource form input { + display: block; + font-size: 1.25em; + min-height: 2em; + padding: 0 0.5em; +} + +.main.resource form button { + font-size: 1.25em; + float: right; +} + +.clear { + clear: both; +} diff --git a/themes/okadmin/templates/index.liquid b/themes/okadmin/templates/index.liquid new file mode 100644 index 0000000..95c64dd --- /dev/null +++ b/themes/okadmin/templates/index.liquid @@ -0,0 +1,26 @@ +{% include 'partials/head' %} + +
+ {% for pair in resources %} + {% assign name = pair[0] %} + {% assign resource = pair[1] %} + {% assign spec = resource.spec %} + +
+
+

{{name | capitalize}}

+
+
    + {% for data in resource.data %} +
  • {{data.id}}
  • + {% endfor %} +
+
+ + +
+
+ + {% endfor %} +
+ +{% include 'partials/tail' %} diff --git a/themes/okadmin/templates/partials/head.liquid b/themes/okadmin/templates/partials/head.liquid new file mode 100644 index 0000000..86915a4 --- /dev/null +++ b/themes/okadmin/templates/partials/head.liquid @@ -0,0 +1,13 @@ + + + + + {{meta.title}} + + + +
+ Admin + View Site +
+
diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid new file mode 100644 index 0000000..4dd600d --- /dev/null +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -0,0 +1,19 @@ +{% for pair in resource.spec %} + {% assign name = pair[0] %} + {% assign spec = pair[1] %} + {% assign type = spec.type %} + +
+ {% if type == 'string' %} + + + {% else %} +

Admin template doesn't support '{{type}}' properties!

+ {% endif %} +
+ +{% endfor %} diff --git a/themes/okadmin/templates/partials/tail.liquid b/themes/okadmin/templates/partials/tail.liquid new file mode 100644 index 0000000..773c8d4 --- /dev/null +++ b/themes/okadmin/templates/partials/tail.liquid @@ -0,0 +1,3 @@ +
{% comment %} closes container tag {% endcomment %} + + diff --git a/themes/okadmin/templates/resource.liquid b/themes/okadmin/templates/resource.liquid new file mode 100644 index 0000000..9c1b71c --- /dev/null +++ b/themes/okadmin/templates/resource.liquid @@ -0,0 +1,15 @@ +{% include 'partials/head' %} + +
+ +
+ + {% include 'partials/inputs' %} + +
+
+
+ +{% include 'partials/tail' %} diff --git a/themes/okadmin/templates/resource_new.liquid b/themes/okadmin/templates/resource_new.liquid new file mode 100644 index 0000000..1e414be --- /dev/null +++ b/themes/okadmin/templates/resource_new.liquid @@ -0,0 +1,15 @@ +{% include 'partials/head' %} + +
+ +
+ + {% include 'partials/inputs' %} + +
+
+
+ +{% include 'partials/tail' %} -- cgit v1.2.3-70-g09d2