From ce294f2591279e1fc342b6c3da4a2e0c22c805a2 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 14:59:30 -0400 Subject: Don't expose object refs in public APIs ya dummy! Make sure to deep clone them input/output objects to maintain immutability y'hear --- app/node_modules/okschema/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 8871a99..4b215d1 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -1,4 +1,4 @@ -var assign = require('object-assign'); +var cloneDeep = require('lodash.clonedeep'); var mschema = require('mschema'); var v = require('validator'); @@ -68,7 +68,7 @@ function OKSchema(spec) { if (!(this instanceof OKSchema)) return new OKSchema(spec); if (!spec) throw new Error('No spec provided to OKSchema'); - spec = assign({}, spec); + spec = cloneDeep(spec); // Cache the mschema version of our spec this._mschemaSpec = Object.keys(spec).reduce(function(cache, prop) { // If custom type, return its parent spec @@ -83,8 +83,10 @@ function OKSchema(spec) { }, {}); Object.defineProperty(this, 'spec', { - value: spec, - writable: false + get: function() { + return cloneDeep(spec); + }, + enumerable: true }); } -- cgit v1.2.3-70-g09d2 From 5abbdf600396144fbbe32b97e83beaabf6ed5c39 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Fri, 10 Apr 2015 21:40:38 -0400 Subject: Support autoincrement and custom ID field in OKSchema --- app/node_modules/okschema/index.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 4b215d1..1528eab 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -69,8 +69,9 @@ function OKSchema(spec) { if (!spec) throw new Error('No spec provided to OKSchema'); spec = cloneDeep(spec); + var specKeys = Object.keys(spec); // Cache the mschema version of our spec - this._mschemaSpec = Object.keys(spec).reduce(function(cache, prop) { + this._mschemaSpec = specKeys.reduce(function(cache, prop) { // If custom type, return its parent spec var type = spec[prop].type; if (types[type]) { @@ -82,12 +83,45 @@ function OKSchema(spec) { return cache; }, {}); + // Find ID field + var idField; + specKeys.every(function(prop) { + if (prop === 'id' || spec[prop].id) { + idField = prop; + return false; + } else { + return true; + } + }); + + // Register autoincrement fields + // NOTE Does not work for nested fields + var autoIncrementFields = specKeys.reduce(function(arr, prop) { + if (spec[prop] === 'autoincrement') { + arr.push(prop); + } + return arr; + }, []); + Object.defineProperty(this, 'spec', { get: function() { return cloneDeep(spec); }, enumerable: true }); + + Object.defineProperty(this, 'idField', { + value: idField, + writable: true, + enumerable: true + }); + + Object.defineProperty(this, 'autoIncrementFields',{ + get: function() { + return cloneDeep(autoIncrementFields); + }, + enumerable: true + }); } OKSchema.prototype.assertValid = function(data) { -- cgit v1.2.3-70-g09d2 From fd0d777ee81219577ef9416fab7f985920c3ae29 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Fri, 10 Apr 2015 22:35:24 -0400 Subject: Fix autoincrement algo bug --- app/node_modules/okdb/index.js | 8 +++++--- app/node_modules/okdb/package.json | 1 + app/node_modules/okschema/index.js | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okdb/index.js b/app/node_modules/okdb/index.js index c9dca99..7639ec6 100644 --- a/app/node_modules/okdb/index.js +++ b/app/node_modules/okdb/index.js @@ -1,8 +1,10 @@ -var Q = require('q'); +var assign = require('object-assign') var cloneDeep = require('lodash.clonedeep'); var isobject = require('lodash.isobject'); var format = require('util').format; var low = require('lowdb'); +var Q = require('q'); + low.mixin(low.mixin(require('underscore-db'))); /** @@ -146,10 +148,10 @@ FSDB.prototype.getMeta = function() { */ function autoincrement(wrapper, schema, data) { return schema.autoIncrementFields.reduce(function(data, field) { - var last = wrapper.chain().sort(field).last().value(); + var last = wrapper.chain().sort(field).first().value(); var index = last ? last[field] : -1; var incremented = {}; - incremented[field] = index + 1; + incremented[field] = (parseInt(index) + 1) % Number.MAX_SAFE_INT; return assign(data, incremented); }, data); } diff --git a/app/node_modules/okdb/package.json b/app/node_modules/okdb/package.json index a6d13ff..5184cb6 100644 --- a/app/node_modules/okdb/package.json +++ b/app/node_modules/okdb/package.json @@ -12,6 +12,7 @@ "lodash.clonedeep": "^3.0.0", "lodash.isobject": "^3.0.1", "lowdb": "^0.7.2", + "object-assign": "^2.0.0", "q": "^1.2.0", "underscore-db": "^0.8.1" } diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 1528eab..4633e7a 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -97,7 +97,8 @@ function OKSchema(spec) { // Register autoincrement fields // NOTE Does not work for nested fields var autoIncrementFields = specKeys.reduce(function(arr, prop) { - if (spec[prop] === 'autoincrement') { + var specProp = spec[prop]; + if (specProp.autoincrement) { arr.push(prop); } return arr; -- cgit v1.2.3-70-g09d2 From 1eff07c9c2ca5b61a28a1037a586d25c3791d67b Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Fri, 10 Apr 2015 22:37:33 -0400 Subject: Add autoincrementing index to resources --- app/index.js | 2 ++ app/node_modules/okschema/index.js | 5 +++++ 2 files changed, 7 insertions(+) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/index.js b/app/index.js index fc38b0a..e462b48 100644 --- a/app/index.js +++ b/app/index.js @@ -122,6 +122,8 @@ OKCMS.prototype._createSchemas = function(schemaConfig) { schemaConfig = schemaConfig || {}; return Object.keys(schemaConfig).reduce(function(cache, key) { var spec = schemaConfig[key]; + // All resources have an autoincrementing index so we can order them suckas + spec.__index = {type: 'meta', autoincrement: true}; cache[key] = OKSchema(spec); return cache; }, {}); diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 4633e7a..d53ed7b 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -56,6 +56,11 @@ var types = { }]; } } + }, + // Special type for resource meta information + 'meta': { + parent: 'string', + assertValid: function(spec, value) {} } } -- cgit v1.2.3-70-g09d2 From 43063c3242ccaf95e39fff240d501de38131f7c5 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 16 Apr 2015 23:38:16 -0400 Subject: Add number type support --- app/node_modules/okschema/index.js | 12 +++++++++++- themes/okadmin/templates/partials/inputs.liquid | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index d53ed7b..c5a56c4 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -68,6 +68,9 @@ var types = { * OKSchema! * Meant as a thin wrapper around some existing schema validation * module, mostly to allow for the extension of types. + * + * NOTE: Currently just assumes spec is valid. If you give a bad spec + * strange things may or may not happen */ function OKSchema(spec) { if (!(this instanceof OKSchema)) return new OKSchema(spec); @@ -138,11 +141,18 @@ OKSchema.prototype.assertValid = function(data) { var type = spec[prop].type; if (types[type]) { types[type].assertValid(spec[prop], data[prop]); + // Also check if it's a number type and try to cast it + // otherwise pass and let mschema handle + } else if (type === 'number') { + try { + data[prop] = parseFloat(data[prop]); + } catch (err) {} } }); var result = mschema.validate(data, this.toMschema()); - if (!result.valid) + if (!result.valid) { throw result.errors; + } }; /** diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index c9a4d92..4d31413 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -12,6 +12,10 @@ {% elsif type == 'text' %} + {% elsif type == 'number' %} + {% elsif type == 'enum' %} + {% elsif type == 'media-list' %}
-- cgit v1.2.3-70-g09d2 From bcca89ebfd9ea93356d934edd5218335430746b6 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Tue, 2 Jun 2015 17:29:57 -0400 Subject: Add 'date' field type --- app/node_modules/okschema/index.js | 4 ++++ themes/okadmin/public/css/main.css | 4 ++++ themes/okadmin/public/js/app.js | 19 +++++++++++++++++++ themes/okadmin/templates/partials/inputs.liquid | 17 +++++++++++++++-- 4 files changed, 42 insertions(+), 2 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 0079f51..67f6e42 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -65,6 +65,10 @@ var types = { 'tag-list': { parent: 'string', assertValid: function(spec, value) {} + }, + 'date': { + parent: 'string', + assertValid: function(spec, value) {} } } diff --git a/themes/okadmin/public/css/main.css b/themes/okadmin/public/css/main.css index 094e66b..a5fbdeb 100644 --- a/themes/okadmin/public/css/main.css +++ b/themes/okadmin/public/css/main.css @@ -211,6 +211,10 @@ label { button, input[type=submit] { cursor: pointer; } +.main.resource .date input { + /* date inputs need font family override */ + font-family: "Helvetica", sans-serif; +} .main.resource form .group { display: block; float: left; diff --git a/themes/okadmin/public/js/app.js b/themes/okadmin/public/js/app.js index f989c7b..d58cb2a 100644 --- a/themes/okadmin/public/js/app.js +++ b/themes/okadmin/public/js/app.js @@ -162,6 +162,25 @@ var OKAdmin = function(){ $id.val( slug ) } + // Parse date input + $('.property .date').each(function(i, el) { + var name = $(el).parent('.property').data('name') + var $input = $(el).find('input') + var date = new Date($input.val()) + // Set to middle of day so it is the same date + // for all locales + date.setUTCHours(12) + var dateString = date.toUTCString() + var normalizedInput = document.createElement('input') + $(normalizedInput).attr({ + name: name, + type: 'text', + value: dateString + }) + $input.remove() + $(el).append(normalizedInput) + }) + $("ol").each(function(){ $("li", this).each(function(index){ $(this).find("input,textarea").each(function(){ diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index 0d5321b..e766558 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -50,10 +50,23 @@
+ + {% elsif type =='date' %} + +
+ +
+ {% elsif type == 'tag-list' %}
- +
{% elsif type == 'link-list' %} -- cgit v1.2.3-70-g09d2 From 27abfdd15714895894565124bcc660b9d6774494 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Fri, 19 Jun 2015 15:29:19 -0400 Subject: Add 'flag' data type --- app/node_modules/okschema/index.js | 4 ++++ package.json | 2 +- themes/okadmin/public/js/app.js | 11 +++++++++++ themes/okadmin/templates/partials/inputs.liquid | 13 ++++++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 67f6e42..89b59cc 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -69,6 +69,10 @@ var types = { 'date': { parent: 'string', assertValid: function(spec, value) {} + }, + 'flag': { + parent: 'string', + assertValid: function(spec, value) {} } } diff --git a/package.json b/package.json index a8a602f..50ec3e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "okcms", - "version": "0.1.0", + "version": "0.1.10", "description": "great", "main": "app/index.js", "scripts": { diff --git a/themes/okadmin/public/js/app.js b/themes/okadmin/public/js/app.js index d58cb2a..4be0afc 100644 --- a/themes/okadmin/public/js/app.js +++ b/themes/okadmin/public/js/app.js @@ -181,6 +181,17 @@ var OKAdmin = function(){ $(el).append(normalizedInput) }) + // Modify flags checkboxes such that unchecked ones return "false" + // instead of nothing + $('.property .flag').each(function(i, el) { + var input = el.querySelector('input') + var checked = !!input.checked + if (!checked) { + $(input).attr('value', 'false') + input.checked = true + } + }) + $("ol").each(function(){ $("li", this).each(function(index){ $(this).find("input,textarea").each(function(){ diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index e766558..9c1a26b 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -51,7 +51,7 @@ - {% elsif type =='date' %} + {% elsif type == 'date' %}
+ {% elsif type == 'flag' %} + +
+ +
+ {% elsif type == 'tag-list' %}
Date: Fri, 10 Jul 2015 17:31:40 -0400 Subject: Implement basic foreign key support --- app/index.js | 23 ++++++ app/node_modules/okadminview/index.js | 100 ++++++++++++++++++------ app/node_modules/okquery/index.js | 1 + app/node_modules/okresource/index.js | 66 ++++++++++++++++ app/node_modules/okschema/index.js | 4 + themes/okadmin/templates/partials/inputs.liquid | 2 +- 6 files changed, 169 insertions(+), 27 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/index.js b/app/index.js index d514f10..2507dd2 100644 --- a/app/index.js +++ b/app/index.js @@ -78,6 +78,7 @@ function OKCMS(options) { }); var resourceCache = this._resourceCache = this._createResources(resourceConfig, db, schemas); + this._resolveForeignKeys(resourceCache) var errorHandler = createErrorHandlerProducer( templateProvider, adminTemplateProvider, debug); // Create view instances from config @@ -154,6 +155,21 @@ OKCMS.prototype._createResources = function(resourceConfig, db, schemaCache) { return ResourceCache(resources); }; +OKCMS.prototype._resolveForeignKeys = function(resourceCache) { + resourceCache.forEach(function(resource) { + Object.keys(resource.foreignKeys).forEach(function(field) { + var foreignKeyType = resource.foreignKeys[field] + var keyedResource = resourceCache.get(foreignKeyType) + if (!keyedResource) { + throw new Error(format( + "Foreign key field '%s' in '%s' resource references unknown" + + "resource of type '%s'", field, resource.type, foreignKeyType)) + } + resource._linkForeignKey(field, resourceCache.get(foreignKeyType)) + }) + }) +} + OKCMS.prototype._createViews = function(viewConfig, db, meta, resourceCache, templateProvider, errorHandler) { viewConfig = viewConfig || {}; @@ -279,6 +295,13 @@ ResourceCache.prototype.get = function(type, id) { } }; +ResourceCache.prototype.forEach = function(cb) { + cb = cb || function() {} + Object.keys(this._cache).forEach(function(key) { + cb(this._cache[key]) + }.bind(this)) +} + /** * Higher order function implementing customizable error handling */ diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js index 273d541..06656dc 100644 --- a/app/node_modules/okadminview/index.js +++ b/app/node_modules/okadminview/index.js @@ -154,11 +154,12 @@ function OKAdminView(options) { if (!resource) { error(req, res, 404)(new Error('No such resource ' + type)); } else { - var templateData = transformData(meta, resource, {}); - view.renderResourceNew(req, res, assign(templateData, { - success: req.flash('success'), - errors: req.flash('errors'), - })); + fetchNewTemplateData(meta, resource, transformData).then(function(data) { + view.renderResourceNew(req, res, assign(data, { + success: req.flash('success'), + errors: req.flash('errors'), + })) + }).fail(error(req, res, 500)) } }); @@ -280,11 +281,11 @@ function OKAdminView(options) { /** * Yields formatted template data for a single resource */ -function transformData(meta, resource, data) { +function transformData(meta, spec, resource, data) { meta = meta || {}; resource = resource || {}; data = data || {}; - var spec = Object.keys(resource.spec).reduce(function(cache, prop) { + var spec = Object.keys(spec).reduce(function(cache, prop) { var value = data[prop]; var propSpec = cache[prop]; // Decorate spec with actual resource values @@ -294,7 +295,7 @@ function transformData(meta, resource, data) { propSpec.hidden = true; } return cache; - }, resource.spec); + }, spec); return { meta: meta, resource: { @@ -340,47 +341,94 @@ function fetchIndexTemplateData(meta, queries, dashboardConfig) { Q.all(queries.map(function(query) { return query.get(); })).then(function(results) { - var resources = results.reduce(function(cache, result, i) { - if (!result) - return cache; + var templateData = results.reduce(function(acc, result, i) { var resource = queries[i].resource; // We want the raw object spec var spec = resource.spec; var dashConf = resourceConfig[resource.type] || {} var groupBy = dashConf.groupBy var key = pluralize(resource.type) - cache[key] = { + acc[key] = { type: resource.type, spec: spec, data: result, groupBy: groupBy } - return cache; - }, {}); - + return acc + }, {}) resolve({ meta: meta, - resources: resources - }); - }).fail(reject); + resources: templateData + }) + }).fail(reject) }); } +function fetchNewTemplateData(meta, resource, transformFn) { + return Q.promise(function(resolve, reject) { + if (!resource.hasForeignKey) { + done({spec: resource.spec, resource: resource}) + } else { + fetchForeignKeyOptions(resource).then(done).fail(reject) + } + + function done(results) { + resolve(transformFn(meta, results.spec, results.resource, {})) + } + }) +} + /** * Annotate template data with schema info */ -function fetchResourceTemplateData(meta, query, fn) { - fn = fn || function(m, r, d) { return {meta: m, resource: d}; }; +function fetchResourceTemplateData(meta, query, transformFn) { return Q.promise(function(resolve, reject) { query.get().then(function(data) { - if (!data) { - reject(new Error('No resource data')); + if (!data) + return reject(new Error('No resource data')) + + var resource = query.resource + + if (resource.hasForeignKey) { + fetchForeignKeyOptions(resource).then(done).fail(reject) } else { - var resource = query.resource; - resolve(fn(meta, resource, data)); + done({spec: resource.spec, resource: resource}) } - }).fail(reject); - }); + + function done(results) { + resolve(transformFn(meta, results.spec, results.resource, data)) + } + }).fail(reject) + }) +} + +function fetchForeignKeyOptions(resource) { + var promises = Object.keys(resource.foreignKeys) + .map(fetchOptionsForKey) + var spec = resource.spec + + return Q.all(promises).then(done) + + function done() { + return Q.promise(function(resolve, reject) { + resolve({spec: spec, resource: resource}) + }) + } + + function fetchOptionsForKey(field) { + var relatedResourceType = resource.foreignKeys[field] + return resource.related(relatedResourceType).then(fillOptions) + + function fillOptions(results) { + return Q.promise(function(resolve, reject) { + spec[field].options = results.map(function(result) { + return result.id + }) + resolve() + }) + } + } } + module.exports = OKAdminView; diff --git a/app/node_modules/okquery/index.js b/app/node_modules/okquery/index.js index 09d867d..d4cb905 100644 --- a/app/node_modules/okquery/index.js +++ b/app/node_modules/okquery/index.js @@ -23,6 +23,7 @@ function OKQuery(options) { // Queries are ordered by index by default var sortField = options.sortBy || '__index'; + // TODO Make descending by default var descending = options.descending || false; Object.defineProperty(this, 'resource', { diff --git a/app/node_modules/okresource/index.js b/app/node_modules/okresource/index.js index c3f9adb..c1f2509 100644 --- a/app/node_modules/okresource/index.js +++ b/app/node_modules/okresource/index.js @@ -1,3 +1,4 @@ +var format = require('util').format var assign = require('object-assign'); var cloneDeep = require('lodash.clonedeep'); var Q = require('q'); @@ -22,10 +23,36 @@ function OKResource(options) { var spec = schema.spec; var type = options.type; + var hasForeignKey = false this._db = options.db; this._schema = schema; + var foreignKeys = Object.keys(spec).reduce(function(acc, field) { + var fieldSpec = spec[field] + if (fieldSpec.type === 'foreign-key') { + hasForeignKey = true + acc[field] = fieldSpec.key + } + return acc + }, {}) + + // Will store references to other resources referenced via foreign keys + this._foreignKeyedResources = {} + // Define properties which are part of the API + + // Should be treated as read-only + Object.defineProperty(this, 'foreignKeys', { + get: function() { + return foreignKeys + } + }) + + Object.defineProperty(this, 'hasForeignKey', { + get: function() { + return hasForeignKey + } + }) Object.defineProperty(this, 'spec', { get: function() { @@ -49,6 +76,30 @@ function OKResource(options) { }); } +OKResource.prototype._linkForeignKey = function(field, resource) { + this._foreignKeyedResources[field] = resource +} + +/** + * Fetch all related resources for the given field + */ +OKResource.prototype.related = function(field) { + var resource = this._foreignKeyedResources[field] + return Q.promise(function(resolve, reject) { + if (!resource) { + return error(reject, new Error(format( + "No related resource for field '%s'", field))) + } + resource.all().then(resolve).fail(reject) + }) + + function error(reject, err) { + setTimeout(function() { + reject(err) + }, 0) + } +} + /** * Throws an error if data does not conform to schema */ @@ -180,6 +231,9 @@ OKResource.prototype.instance = function(options) { }); }; +/** + * TODO This class is such bullshit. Refactor out + */ function OKResourceInstance(resource, options) { if (!(this instanceof OKResourceInstance)) return new OKResourceInstance(options); // Only support static data instances for now @@ -279,6 +333,18 @@ function OKResourceInstance(resource, options) { resource.assertValid(data); }; + Object.defineProperty(this, 'foreignKeys', { + get: function() { + return [] + } + }) + + Object.defineProperty(this, 'hasForeignKey', { + get: function() { + return false + } + }) + Object.defineProperty(this, 'parent', { value: resource, writable: false, diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 89b59cc..330ad6b 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -73,6 +73,10 @@ var types = { 'flag': { parent: 'string', assertValid: function(spec, value) {} + }, + 'foreign-key': { + parent: 'enum', + assertValid: function(spec, value) {} } } diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index 9c1a26b..c6efc68 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -17,7 +17,7 @@ - {% elsif type == 'enum' %} + {% elsif type == 'enum' or type == 'foreign-key' %} - + {% for link in spec.value %} +
  • + + + +
  • {% endfor %}
    + + + + {% elsif type == 'media-list' %} -- cgit v1.2.3-70-g09d2 From ea9a94f3e1f980153588da1aee3fc7e456e292b5 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 28 Mar 2016 14:39:44 -0400 Subject: okschema: replace array-type data with empty list if unspecified (due to constraint of querystring body-parser) --- app/node_modules/okschema/index.js | 74 ++++++++++++++++++++++++++------------ examples/db.json | 23 ++++++++---- 2 files changed, 68 insertions(+), 29 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 82aa13f..cf041fb 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -41,43 +41,25 @@ var types = { } }, 'captioned-image-list': { + isArray: true, parent: [{ uri: { type: 'string' }, // TODO Implement URI type caption: { type: 'string' } }], - assertValid: function(spec, value) { - var message; - var actual; - if (!value || !value.length) { - throw [{ - message: 'Not an array', - expected: JSON.stringify(this.parent), - actual: value - }]; - } - } + assertValid: function(spec, value) {} }, // Special type for resource meta information 'meta': { parent: 'string', assertValid: function(spec, value) {} }, - 'tag-list': { + 'link-list': { + isArray: true, parent: [{ uri: { type: 'string' }, text: { type: 'string' } }], - assertValid: function(spec, value) { - var message; - var actual; - if (!value || !value.length) { - throw [{ - message: 'Not an array', - expected: JSON.stringify(this.parent), - actual: value - }]; - } - } + assertValid: function(spec, value) {} }, 'date': { parent: 'string', @@ -90,8 +72,38 @@ var types = { 'foreign-key': { parent: 'enum', assertValid: function(spec, value) {} + }, + 'media-list': { + isArray: true, + parent: [], + assertValid: function(spec, value) {} + }, + 'double-captioned-image-list': { + isArray: true, + parent: [], + assertValid: function(spec, value) {} + }, + 'triple-captioned-image-list': { + isArray: true, + parent: [], + assertValid: function(spec, value) {} + }, +} + +/* +function checkArrayLength (spec, value) { + var message; + var actual; + if (!value || !value.length) { + throw [{ + message: 'Not an array', + expected: JSON.stringify(this.parent), + actual: value + }]; } } +*/ + /** * OKSchema! @@ -162,9 +174,25 @@ function OKSchema(spec) { }); } +OKSchema.prototype.checkDataForMissingArrays = function(data) { + data = data || {}; + var spec = this.spec; + + // The qs body-parser module does not have a way to represent + // empty lists. If you delete all elements from a list, + // check against the spec so we know to replace with an empty list. + Object.keys(spec).forEach(function(prop){ + var type = spec[prop].type; + if (types[type] && types[type].isArray && ! data[prop]) { + data[prop] = [] + } + }) +} + OKSchema.prototype.assertValid = function(data) { data = data || {}; var spec = this.spec; + this.checkDataForMissingArrays(data) // Run through custom validators, they'll throw if invalid Object.keys(data).forEach(function(prop) { var type = spec[prop].type; diff --git a/examples/db.json b/examples/db.json index 4319237..5e10d60 100644 --- a/examples/db.json +++ b/examples/db.json @@ -92,6 +92,22 @@ }, "__index": 3, "id": "croissant" + }, + { + "type": "Test", + "title": "Testing", + "description": "", + "color": "red", + "video": { + "url": "", + "type": "", + "token": "", + "title": "", + "thumb": "" + }, + "__index": "5", + "dateCreated": "Mon, 28 Mar 2016 18:49:32 GMT", + "images": [] } ], "page": [ @@ -100,12 +116,7 @@ "body": "Just a small bakery", "id": "about", "__index": "1", - "links": [ - { - "text": "asdf2", - "uri": "asdf" - } - ], + "links": [], "dateCreated": "" }, { -- cgit v1.2.3-70-g09d2 From cd929e1c38c02c1d048abe702c04c7d4d6188011 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 5 Apr 2016 11:46:47 -0400 Subject: Fix sorting bug when saving --- app/node_modules/okschema/index.js | 23 ++++++++++++++++++++--- examples/db.json | 20 +++++++++++++++++--- examples/index.js | 2 +- themes/okadmin/public/js/app.js | 4 ++++ themes/okadmin/public/js/parser.js | 2 +- themes/okadmin/templates/partials/inputs.liquid | 2 ++ 6 files changed, 45 insertions(+), 8 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index cf041fb..8162fd4 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -174,8 +174,7 @@ function OKSchema(spec) { }); } -OKSchema.prototype.checkDataForMissingArrays = function(data) { - data = data || {}; +OKSchema.prototype.fixMissingLists = function(data) { var spec = this.spec; // The qs body-parser module does not have a way to represent @@ -189,10 +188,22 @@ OKSchema.prototype.checkDataForMissingArrays = function(data) { }) } +OKSchema.prototype.fixIndexField = function(data) { + // Likewise numbers always come in as strings. The field used to sort + // records, __index, is of type "meta", so the parseFloat in + // assertValid (below) never fires and we end up with sorting issues. + if (data.__index && typeof data.__index == "string") { + var __index = parseInt(data.__index) + if (! isNaN(__index)) { + data.__index = __index + } + } +} + OKSchema.prototype.assertValid = function(data) { data = data || {}; var spec = this.spec; - this.checkDataForMissingArrays(data) + // Run through custom validators, they'll throw if invalid Object.keys(data).forEach(function(prop) { var type = spec[prop].type; @@ -210,6 +221,12 @@ OKSchema.prototype.assertValid = function(data) { if (!result.valid) { throw result.errors; } + + // Fix various issues with our data, having to do + // with use of the "qs" body-parser module. + // TODO: just send JSON? + this.fixMissingLists(data) + this.fixIndexField(data) }; /** diff --git a/examples/db.json b/examples/db.json index bdad6c8..1d045c3 100644 --- a/examples/db.json +++ b/examples/db.json @@ -150,8 +150,8 @@ ], "test": [ { - "id": "test", - "title": "tEst", + "id": "red", + "title": "Red", "media": [ { "uri": "http://asdf.us/", @@ -159,8 +159,22 @@ "type": "link" } ], - "__index": "0", + "__index": 0, "dateCreated": "Mon, 28 Mar 2016 23:02:45 GMT" + }, + { + "id": "blue", + "title": "Blue", + "__index": 2, + "dateCreated": "Tue, 05 Apr 2016 15:17:54 GMT", + "media": [] + }, + { + "id": "green", + "title": "Green", + "__index": 1, + "dateCreated": "Tue, 05 Apr 2016 15:17:57 GMT", + "media": [] } ] } \ No newline at end of file diff --git a/examples/index.js b/examples/index.js index 4bf385f..81d9241 100644 --- a/examples/index.js +++ b/examples/index.js @@ -4,7 +4,7 @@ var app = okcms.createApp({ root: 'public', - debug: false, + debug: true, schemas: { page: { diff --git a/themes/okadmin/public/js/app.js b/themes/okadmin/public/js/app.js index e7f902d..e79f704 100644 --- a/themes/okadmin/public/js/app.js +++ b/themes/okadmin/public/js/app.js @@ -56,6 +56,7 @@ var OKAdmin = function(){ $el.addClass("loaded") $el.find(".video-type").val( media.type ) $el.find(".video-token").val( media.token ) + $el.find(".video-uri").val( media.uri ) $el.find(".video-title").val( media.title ) $el.find(".video-thumb").val( media.thumbnail ) $el.find(".video-width").val( media.width ) @@ -125,8 +126,11 @@ var OKAdmin = function(){ $el.parent().addClass("loaded") $el.parent().find(".video-type").val( media.type ) $el.parent().find(".video-token").val( media.token ) + $el.parent().find(".video-uri").val( media.uri ) $el.parent().find(".video-title").val( media.title ) $el.parent().find(".video-thumb").val( media.thumbnail ) + $el.parent().find(".video-width").val( media.width ) + $el.parent().find(".video-height").val( media.height ) }) })) diff --git a/themes/okadmin/public/js/parser.js b/themes/okadmin/public/js/parser.js index 6c964f7..b4a087d 100644 --- a/themes/okadmin/public/js/parser.js +++ b/themes/okadmin/public/js/parser.js @@ -39,7 +39,7 @@ var Parser = { done({ url: url, type: "video", - token: "", + token: url, thumbnail: "http://okfocus.s3.amazonaws.com/misc/okcms/video.png", title: filename, width: width, diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index fa03e48..e618c61 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -164,6 +164,7 @@
    + @@ -192,6 +193,7 @@
    + -- cgit v1.2.3-70-g09d2 From 654053736a74aa8cd4344e9c4f5e5a7ebeefc33e Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 6 Apr 2016 19:11:22 -0400 Subject: Store booleans as type boolean --- app/index.js | 2 +- app/node_modules/okadminview/index.js | 3 ++- app/node_modules/okschema/index.js | 13 ++++++++----- examples/db.json | 3 ++- examples/index.js | 1 + 5 files changed, 14 insertions(+), 8 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/index.js b/app/index.js index d478a03..1a8f97e 100644 --- a/app/index.js +++ b/app/index.js @@ -151,7 +151,7 @@ OKCMS.prototype._createResources = function(resourceConfig, db, schemaCache) { var schema = schemaCache[type]; if (!schema) throw new Error('Resource config references nonexistent schema ' + type); - var resource = OKResource({ + var resource = new OKResource({ type: type, db: db, schema: schema diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js index ec3cd86..b94c814 100644 --- a/app/node_modules/okadminview/index.js +++ b/app/node_modules/okadminview/index.js @@ -247,6 +247,7 @@ function OKAdminView(options) { if (!resource) { error(req, res, 400)(new Error('No such resource ' + type)); } else { + var spec = resource.spec try { resource.assertValid(data); resource.update(id, data).then(function(updated) { @@ -254,7 +255,7 @@ function OKAdminView(options) { res.redirect(303, '../' + resource.getID(updated)); }).fail(error(req, res, 500)); } catch (errors) { - var templateData = transformData(meta, resource, data); + var templateData = transformData(meta, spec, resource, data); view.renderResource(req, res, assign(templateData, {errors: errors})); } } diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 8162fd4..b3d4cc2 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -66,7 +66,7 @@ var types = { assertValid: function(spec, value) {} }, 'flag': { - parent: 'string', + parent: 'boolean', assertValid: function(spec, value) {} }, 'foreign-key': { @@ -207,14 +207,17 @@ OKSchema.prototype.assertValid = function(data) { // Run through custom validators, they'll throw if invalid Object.keys(data).forEach(function(prop) { var type = spec[prop].type; - if (types[type]) { - types[type].assertValid(spec[prop], data[prop]); - // Also check if it's a number type and try to cast it + + // Check if it's a number/boolean and try to cast it // otherwise pass and let mschema handle - } else if (type === 'number') { + if (type === 'number') { try { data[prop] = parseFloat(data[prop]); } catch (err) {} + } else if (type === 'flag') { + data[prop] = data[prop] == "true" ? true : false + } else if (types[type]) { + types[type].assertValid(spec[prop], data[prop]); } }); var result = mschema.validate(data, this.toMschema()); diff --git a/examples/db.json b/examples/db.json index da7c19a..8805342 100644 --- a/examples/db.json +++ b/examples/db.json @@ -207,7 +207,8 @@ "title": "clouds35.mp3", "thumb": "http://okfocus.s3.amazonaws.com/misc/okcms/video.png" } - ] + ], + "flagged": true } ] } \ No newline at end of file diff --git a/examples/index.js b/examples/index.js index 57bce4b..fe37954 100644 --- a/examples/index.js +++ b/examples/index.js @@ -24,6 +24,7 @@ var app = okcms.createApp({ test: { id: {type: 'string', hidden: true}, title: {type: 'string'}, + flagged: {type: 'flag'}, media: {type: 'media-list'}, } }, -- cgit v1.2.3-70-g09d2 From ef64682b40be479ecff91c19d406fff10f9cc688 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 2 Sep 2016 11:29:01 -0400 Subject: rename captioned-image-list to gallery, closes #3 --- app/node_modules/okschema/index.js | 13 +++++++++++++ examples/index.js | 4 ++-- themes/okadmin/templates/partials/inputs.liquid | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'app/node_modules/okschema/index.js') diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index b3d4cc2..0048fc5 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -48,6 +48,14 @@ var types = { }], assertValid: function(spec, value) {} }, + 'gallery': { + isArray: true, + parent: [{ + uri: { type: 'string' }, // TODO Implement URI type + caption: { type: 'string' } + }], + assertValid: function(spec, value) {} + }, // Special type for resource meta information 'meta': { parent: 'string', @@ -78,6 +86,11 @@ var types = { parent: [], assertValid: function(spec, value) {} }, + 'media': { + isArray: true, + parent: [], + assertValid: function(spec, value) {} + }, 'double-captioned-image-list': { isArray: true, parent: [], diff --git a/examples/index.js b/examples/index.js index fa3281e..711246a 100644 --- a/examples/index.js +++ b/examples/index.js @@ -23,13 +23,13 @@ var app = okcms.createApp({ description: {type: 'text'}, color: {type: 'enum', options: ["red","blue","green"]}, video: {type: 'video'}, - images: {type: 'captioned-image-list'} + images: {type: 'gallery'} }, test: { id: {type: 'string', hidden: true}, title: {type: 'string'}, flagged: {type: 'flag'}, - media: {type: 'media-list'}, + media: {type: 'media'}, }, flour: { id: {type: 'string', hidden: true}, diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index bca397f..0fee61e 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -140,7 +140,7 @@
    - {% elsif type == 'media-list' %} + {% elsif type == 'media-list' or type == 'media' %}
    @@ -274,7 +274,7 @@ {% endfor %}
    - {% elsif type == 'captioned-image-list' %} + {% elsif type == 'captioned-image-list' or type == 'gallery' %}
    -- cgit v1.2.3-70-g09d2