apostrophe-schemas
Advanced tools
Comparing version 0.5.15 to 0.5.17
121
index.js
@@ -213,15 +213,15 @@ var async = require('async'); | ||
self.converters.csv = { | ||
area: function(req, data, name, snippet, field, cb) { | ||
area: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.textToArea(data[name]); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
string: function(req, data, name, snippet, field, cb) { | ||
string: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeString(data[name], field.def); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
slug: function(req, data, name, snippet, field, cb) { | ||
slug: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.slugify(self._apos.sanitizeString(data[name], field.def)); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
tags: function(req, data, name, snippet, field, cb) { | ||
tags: function(req, data, name, snippet, field, callback) { | ||
var tags; | ||
@@ -231,33 +231,33 @@ tags = self._apos.sanitizeString(data[name]); | ||
snippet[name] = tags; | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
boolean: function(req, data, name, snippet, field, cb) { | ||
boolean: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeBoolean(data[name], field.def); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
select: function(req, data, name, snippet, field, cb) { | ||
select: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeSelect(data[name], field.choices, field.def); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
integer: function(req, data, name, snippet, field, cb) { | ||
integer: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeInteger(data[name], field.def, field.min, field.max); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
float: function(req, data, name, snippet, field, cb) { | ||
float: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeFloat(data[name], field.def, field.min, field.max); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
url: function(req, data, name, snippet, field, cb) { | ||
url: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeUrl(data[name], field.def); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
date: function(req, data, name, snippet, field, cb) { | ||
date: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeDate(data[name], field.def); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
time: function(req, data, name, snippet, field, cb) { | ||
time: function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeTime(data[name], field.def); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
password: function(req, data, name, snippet, field, cb) { | ||
password: function(req, data, name, snippet, field, callback) { | ||
// Change the stored password hash only if a new value | ||
@@ -269,7 +269,11 @@ // has been offered | ||
} | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
group: function(req, data, name, snippet, field, cb) { | ||
group: function(req, data, name, snippet, field, callback) { | ||
// This is a visual grouping element and has no data | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}, | ||
array: function(req, data, name, snippet, field, callback) { | ||
// We don't do arrays in CSV, it would be painful to work with | ||
return setImmediate(callback); | ||
} | ||
@@ -283,3 +287,3 @@ }; | ||
self.converters.form.singleton = self.converters.form.area = function(req, data, name, snippet, field, cb) { | ||
self.converters.form.singleton = self.converters.form.area = function(req, data, name, snippet, field, callback) { | ||
var content = []; | ||
@@ -291,18 +295,45 @@ try { | ||
} | ||
self._apos.sanitizeItems(content); | ||
snippet[name] = { items: content, type: 'area' }; | ||
return setImmediate(cb); | ||
return self._apos.sanitizeItems(req, content, function(err, items) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
snippet[name] = { items: items, type: 'area' }; | ||
return callback(null); | ||
}); | ||
}; | ||
self.converters.form.joinByOne = function(req, data, name, snippet, field, cb) { | ||
// An array of objects with their own schema | ||
self.converters.form.array = function(req, data, name, snippet, field, callback) { | ||
var schema = field.schema; | ||
data = data[name]; | ||
if (!Array.isArray(data)) { | ||
data = []; | ||
} | ||
var results = []; | ||
return async.eachSeries(data, function(datum, callback) { | ||
var result = {}; | ||
return self.convertFields(req, schema, 'form', datum, result, function(err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
results.push(result); | ||
return callback(null); | ||
}); | ||
}, function(err) { | ||
snippet[name] = results; | ||
return callback(err); | ||
}); | ||
}; | ||
self.converters.form.joinByOne = function(req, data, name, snippet, field, callback) { | ||
snippet[field.idField] = self._apos.sanitizeId(data[name]); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}; | ||
self.converters.form.joinByOneReverse = function(req, data, name, snippet, field, cb) { | ||
self.converters.form.joinByOneReverse = function(req, data, name, snippet, field, callback) { | ||
// Not edited on this side of the relation | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}; | ||
self.converters.form.joinByArray = function(req, data, name, snippet, field, cb) { | ||
self.converters.form.joinByArray = function(req, data, name, snippet, field, callback) { | ||
var input = data[name] || []; | ||
@@ -356,13 +387,13 @@ if (!Array.isArray(input)) { | ||
}); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}; | ||
self.converters.form.joinByArrayReverse = function(req, data, name, snippet, field, cb) { | ||
self.converters.form.joinByArrayReverse = function(req, data, name, snippet, field, callback) { | ||
// Not edited on this side of the relation | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}; | ||
self.converters.form.tags = function(req, data, name, snippet, field, cb) { | ||
self.converters.form.tags = function(req, data, name, snippet, field, callback) { | ||
snippet[name] = self._apos.sanitizeTags(data[name]); | ||
return setImmediate(cb); | ||
return setImmediate(callback); | ||
}; | ||
@@ -417,3 +448,3 @@ | ||
var i; | ||
return async.each(schema, function(field, callback) { | ||
return async.eachSeries(schema, function(field, callback) { | ||
// Fields that are contextual are edited in the context of a | ||
@@ -433,4 +464,8 @@ // show page and do not appear in regular schema forms. They are | ||
} | ||
self.converters[from][field.type](req, data, field.name, object, field, callback); | ||
}, callback); | ||
return self.converters[from][field.type](req, data, field.name, object, field, function(err) { | ||
return callback(err); | ||
}); | ||
}, function(err) { | ||
return callback(err); | ||
}); | ||
}; | ||
@@ -437,0 +472,0 @@ |
{ | ||
"version": "0.5.15", | ||
"version": "0.5.17", | ||
"name": "apostrophe-schemas", | ||
@@ -4,0 +4,0 @@ "description": "Schemas for easy editing of properties in Apostrophe objects", |
@@ -15,4 +15,5 @@ function AposSchemas() { | ||
// Not all displayers use this | ||
var $field = $el.findByName(field.name); | ||
// Utilized by simple displayers that use a simple HTML | ||
// element with a name attribute | ||
var $field = self.findField($el, field.name); | ||
@@ -43,22 +44,31 @@ // If this field maps to a plain HTML element set the | ||
self.convertFields = function($el, schema, data, callback) { | ||
$el.find('[data-name]').removeClass('apos-error'); | ||
self.findSafe($el, '[data-name]').removeClass('apos-error'); | ||
var failing; | ||
_.each(schema, function(field) { | ||
// async for loop | ||
var i = 0; | ||
function convertField() { | ||
if (i === schema.length) { | ||
return apos.afterYield(_.partial(callback, failing)); | ||
} | ||
var field = schema[i]; | ||
if (field.contextual) { | ||
return; | ||
i++; | ||
return apos.afterYield(convertField); | ||
} | ||
// This won't be enough for every type of field, so we pass $el too | ||
var $field = $el.findByName(field.name); | ||
var $field = self.findField($el, field.name); | ||
if (!$field.length) { | ||
$field = $el.findByName(field.legacy); | ||
$field = self.findField($el, field.legacy); | ||
} | ||
var result = self.converters[field.type](data, field.name, $field, $el, field); | ||
if (result) { | ||
apos.log(result); | ||
apos.log('addError'); | ||
self.addError($el, field.name); | ||
failing = field; | ||
} | ||
}); | ||
return callback(failing); | ||
return self.converters[field.type](data, field.name, $field, $el, field, function(err) { | ||
if (err) { | ||
self.addError($el, field.name); | ||
failing = field; | ||
} | ||
i++; | ||
return apos.afterYield(convertField); | ||
}); | ||
} | ||
convertField(); | ||
}; | ||
@@ -82,2 +92,3 @@ | ||
var $fieldset = self.findFieldset($el, name); | ||
refreshSingleton(items, callback); | ||
@@ -88,3 +99,3 @@ | ||
$.post('/apos/edit-virtual-singleton', options, function(data) { | ||
var $editView = $el.find('[data-' + name + '-edit-view]'); | ||
var $editView = self.findSafe($fieldset, '[data-' + name + '-edit-view]'); | ||
$editView.html(''); | ||
@@ -102,3 +113,3 @@ $editView.append(data); | ||
var $singleton = $editView.find('.apos-singleton:first'); | ||
var $singleton = self.findSafe($editView, '.apos-singleton:first'); | ||
$singleton.on('aposEdited', function(e, data) { | ||
@@ -109,3 +120,3 @@ refreshSingleton([data], function() { | ||
// called to see the new data | ||
$el.find('[data-name="' + name + '"]').trigger('change'); | ||
$fieldset.trigger('change'); | ||
}); | ||
@@ -131,4 +142,5 @@ }); | ||
} | ||
var $fieldset = self.findFieldset($el, name); | ||
$.post('/apos/edit-virtual-area', { content: JSON.stringify(items), options: JSON.stringify(options) }, function(data) { | ||
var $editView = $el.find('[data-' + name + '-edit-view]'); | ||
var $editView = self.findSafe($fieldset, '[data-' + name + '-edit-view]'); | ||
$editView.append(data); | ||
@@ -141,3 +153,4 @@ return callback(null); | ||
self.getSingletonItem = function($el, name) { | ||
var items = $el.find('[data-' + name + '-edit-view]').data('items'); | ||
var $fieldset = self.findFieldset($el, name); | ||
var items = self.findSafe($fieldset, '[data-' + name + '-edit-view]').data('items'); | ||
items = items || []; | ||
@@ -149,3 +162,4 @@ return items[0]; | ||
self.getSingletonJSON = function($el, name) { | ||
var items = $el.find('[data-' + name + '-edit-view]').data('items'); | ||
var $fieldset = self.findFieldset($el, name); | ||
var items = self.findSafe($fieldset, '[data-' + name + '-edit-view]').data('items'); | ||
items = items || []; | ||
@@ -157,3 +171,4 @@ return JSON.stringify(items); | ||
self.getAreaJSON = function($el, name) { | ||
var $property = $el.find('[data-' + name + '-edit-view]'); | ||
var $fieldset = self.findFieldset($el, name); | ||
var $property = self.findSafe($fieldset, '[data-' + name + '-edit-view]'); | ||
return apos.stringifyArea($property.find('.apos-area:first')); | ||
@@ -169,3 +184,3 @@ }; | ||
// Convert the tough cases | ||
area: function(data, name, $field, $el, field) { | ||
area: function(data, name, $field, $el, field, callback) { | ||
data[name] = self.getAreaJSON($el, name); | ||
@@ -176,26 +191,58 @@ // TODO: this is very lazy and doesn't bother to look for things | ||
if (field.required && ((data[name] === '[]') || (data[name] === '[{"type":"richText","content":""}]'))) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
singleton: function(data, name, $field, $el, field) { | ||
singleton: function(data, name, $field, $el, field, callback) { | ||
data[name] = self.getSingletonJSON($el, name); | ||
if (field.required && ((data[name] === '[]') || (data[name] === '[{"type":"richText","content":""}]'))) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
joinByOne: function(data, name, $field, $el, field) { | ||
array: function(data, name, $field, $el, field, callback) { | ||
var results = []; | ||
var $fieldset = self.findFieldset($el, name); | ||
var $elements = self.findSafe($fieldset, '[data-element]:not(.apos-template)'); | ||
var i = 0; | ||
var err; | ||
function convertElement() { | ||
if (i === $elements.length) { | ||
data[name] = results; | ||
return apos.afterYield(_.partial(callback, err)); | ||
} | ||
var result = {}; | ||
var $element = $($elements[i]); | ||
return self.convertFields($element, field.schema, result, function(_err) { | ||
if (_err) { | ||
err = _err; | ||
} | ||
results.push(result); | ||
i++; | ||
return apos.afterYield(convertElement); | ||
}); | ||
} | ||
convertElement(); | ||
}, | ||
joinByOne: function(data, name, $field, $el, field, callback) { | ||
// Fix $field since we can't use the regular name attribute here | ||
$field = $el.find('[data-name="' + name + '"]'); | ||
$field = self.findSafe($el, '[data-name="' + name + '"]'); | ||
// The server will do the work of moving it to the idField as needed | ||
data[name] = $field.selective('get', { incomplete: true })[0]; | ||
if (field.required && !data[name]) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
joinByOneReverse: function(data, name, $field, $el, field) { | ||
joinByOneReverse: function(data, name, $field, $el, field, callback) { | ||
// Not edited on this side of the relation | ||
return apos.afterYield(callback); | ||
}, | ||
joinByArray: function(data, name, $field, $el, field) { | ||
joinByArray: function(data, name, $field, $el, field, callback) { | ||
// Fix $field since we can't use the regular name attribute here | ||
$field = $el.find('[data-name="' + name + '"]'); | ||
$field = self.findSafe($el, '[data-name="' + name + '"]'); | ||
// The server will do the work of processing it all into | ||
@@ -205,10 +252,15 @@ // the relationshipsField and idsField separately for us if needed | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(function() { | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
}); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
joinByArrayReverse: function(data, name, $field, $el, field) { | ||
joinByArrayReverse: function(data, name, $field, $el, field, callback) { | ||
// Not edited on this side of the relation | ||
return apos.afterYield(callback); | ||
}, | ||
group: function(data, name, $field, $el, field) { | ||
group: function(data, name, $field, $el, field, callback) { | ||
// Just a presentation thing | ||
return apos.afterYield(callback); | ||
}, | ||
@@ -218,68 +270,79 @@ // The rest are very simple because the server does | ||
// is a simple form element | ||
string: function(data, name, $field, $el, field) { | ||
string: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
password: function(data, name, $field, $el, field) { | ||
password: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
slug: function(data, name, $field, $el, field) { | ||
slug: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
tags: function(data, name, $field, $el, field) { | ||
data[name] = $el.find('[data-name="' + name + '"]').selective('get', { incomplete: true }); | ||
tags: function(data, name, $field, $el, field, callback) { | ||
data[name] = self.findSafe($el, '[data-name="' + name + '"]').selective('get', { incomplete: true }); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
boolean: function(data, name, $field, $el, field) { | ||
boolean: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
// Seems odd but sometimes used to mandate an "I agree" box | ||
if (field.required && !data[name]) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
select: function(data, name, $field, $el, field) { | ||
select: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
integer: function(data, name, $field, $el, field) { | ||
integer: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
float: function(data, name, $field, $el, field) { | ||
float: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
url: function(data, name, $field, $el, field) { | ||
url: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
date: function(data, name, $field, $el, field) { | ||
date: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
time: function(data, name, $field, $el, field) { | ||
time: function(data, name, $field, $el, field, callback) { | ||
data[name] = $field.val(); | ||
if (field.required && !data[name].length) { | ||
return 'required'; | ||
return apos.afterYield(_.partial(callback, 'required')); | ||
} | ||
return apos.afterYield(callback); | ||
}, | ||
@@ -296,21 +359,78 @@ }; | ||
}, | ||
array: function(data, name, $field, $el, field, callback) { | ||
var $fieldset = self.findFieldset($el, name); | ||
var $template = self.findSafe($fieldset, '.apos-template[data-element]'); | ||
var $add = self.findSafe($fieldset, '[data-add]'); | ||
var $elements = self.findSafe($fieldset, '[data-elements]'); | ||
// Add the elements via an async for loop without | ||
// the async module. -Tom | ||
var i = 0; | ||
data = data[name] || []; | ||
function nextElement() { | ||
if (i === data.length) { | ||
$elements.sortable({ handle: '[data-move]' }); | ||
return callback(null); | ||
} | ||
var $element = $template.clone(); | ||
$element.removeClass('apos-template'); | ||
addRemoveHandler($element); | ||
$elements.append($element); | ||
return self.populateFields($element, field.schema, data[i], function() { | ||
i++; | ||
return nextElement(); | ||
}); | ||
} | ||
nextElement(); | ||
$add.on('click', function() { | ||
var $element = $template.clone(); | ||
$element.removeClass('apos-template'); | ||
$elements.append($element); | ||
addRemoveHandler($element); | ||
var element = {}; | ||
_.each(field.schema, function(field) { | ||
if (field.def !== undefined) { | ||
element[field.name] = field.def; | ||
} | ||
}); | ||
self.populateFields($element, field.schema, element, function() { | ||
// Make sure lister gets a crack | ||
apos.emit('enhance', $element); | ||
}); | ||
return false; | ||
}); | ||
function addRemoveHandler($element) { | ||
var $remove = self.findSafe($element, '[data-remove]'); | ||
$remove.on('click', function() { | ||
$element.remove(); | ||
return false; | ||
}); | ||
} | ||
}, | ||
string: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name]); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
password: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name]); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
slug: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name]); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
tags: function(data, name, $field, $el, field, callback) { | ||
apos.enableTags($el.find('[data-name="' + name + '"]'), data[name]); | ||
return callback(); | ||
apos.enableTags(self.findSafe($el, '[data-name="' + name + '"]'), data[name]); | ||
return apos.afterYield(callback); | ||
}, | ||
url: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name]); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
@@ -334,19 +454,19 @@ select: function(data, name, $field, $el, field, callback) { | ||
} | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
integer: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name]); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
float: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name]); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
boolean: function(data, name, $field, $el, field, callback) { | ||
$field.val(data[name] ? '1' : '0'); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
joinByOne: function(data, name, $field, $el, field, callback) { | ||
// Since we can't use a regular name attribute for a div | ||
$field = $el.find('[data-name="' + name + '"]'); | ||
$field = self.findSafe($el, '[data-name="' + name + '"]'); | ||
if (!$field.length) { | ||
@@ -369,11 +489,11 @@ apos.log('Error: your new.html template for the ' + self.name + ' module does not have a snippetSelective call for the ' + name + ' join yet'); | ||
$field.selective({ limit: 1, data: selectiveData, source: autocomplete }); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
joinByOneReverse: function(data, name, $field, $el, field, callback) { | ||
// Not edited on the reverse side | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
joinByArray: function(data, name, $field, $el, field, callback) { | ||
// Since we can't use a regular name attribute for a div | ||
$field = $el.find('[data-name="' + name + '"]'); | ||
$field = self.findSafe($el, '[data-name="' + name + '"]'); | ||
if (!$field.length) { | ||
@@ -410,11 +530,11 @@ apos.log('Error: your new.html template for the ' + self.name + ' module does not have a snippetSelective call for the ' + name + ' join yet'); | ||
$field.selective({ preventDuplicates: true, sortable: field.sortable, extras: !!field.relationship, data: selectiveData, source: autocomplete }); | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
joinByArrayReverse: function(data, name, $field, $el, field, callback) { | ||
// Not edited on the reverse side | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
group: function(data, name, $field, $el, field, callback) { | ||
// Just a presentation thing | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
@@ -425,5 +545,5 @@ date: function(data, name, $field, $el, field, callback) { | ||
if (field.legacy) { | ||
apos.enhanceDate($el.findByName(field.legacy)); | ||
apos.enhanceDate(self.findField($el, field.legacy)); | ||
} | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
@@ -435,3 +555,3 @@ time: function(data, name, $field, $el, field, callback) { | ||
} | ||
return callback(); | ||
return apos.afterYield(callback); | ||
}, | ||
@@ -449,3 +569,3 @@ }; | ||
self.addError = function($el, name) { | ||
$el.find('[data-name="' + name + '"]').addClass('apos-error'); | ||
self.findSafe($el, '[data-name="' + name + '"]').addClass('apos-error'); | ||
}; | ||
@@ -459,3 +579,3 @@ | ||
self.scrollToError = function($el) { | ||
var $element = $el.find('.apos-error'); | ||
var $element = self.findSafe($el, '.apos-error'); | ||
if (!$element.length) { | ||
@@ -469,2 +589,33 @@ return; | ||
}; | ||
// Used to search for fieldsets at this level of the schema, | ||
// without false positives for any schemas nested within it | ||
self.findFieldset = function($el, name) { | ||
return self.findSafe($el, '[data-name="' + name + '"]'); | ||
}; | ||
// Used to search for elements without false positives from nested | ||
// schemas in unrelated fieldsets | ||
self.findSafe = function($el, sel) { | ||
return $el.find(sel).filter(function() { | ||
var $parents = $(this).parents(); | ||
var i; | ||
for (i = 0; (i < $parents.length); i++) { | ||
if ($parents[i] === $el[0]) { | ||
return true; | ||
} | ||
if ($($parents[i]).hasClass('apos-fieldset')) { | ||
return false; | ||
} | ||
} | ||
}); | ||
}; | ||
// Used to search for simple elements that have a | ||
// "name" attribute, without false positives from nested | ||
// schemas in unrelated fieldsets. | ||
self.findField = function($el, name) { | ||
$fieldset = self.findFieldset($el, name); | ||
return self.findSafe($fieldset, '[name="' + name + '"]'); | ||
}; | ||
} | ||
@@ -471,0 +622,0 @@ |
@@ -13,2 +13,3 @@ # apostrophe-schemas | ||
* [Grouping fields into tabs](#grouping-fields-into-tabs) | ||
* [Arrays in schemas](#arrays-in-schemas) | ||
* Editing | ||
@@ -121,3 +122,3 @@ * [Schemas in Nunjucks Templates](#editing-schemas-in-nunjucks-templates) | ||
Joins are also supported as described later. | ||
[Joins](#joins-in-schemas) and [arrays](#arrays-in-schemas) are also supported as described later. | ||
@@ -182,2 +183,71 @@ #### Validation | ||
#### Arrays in schemas | ||
Let's say you're managing "companies," and each company has several "offices." Wouldn't it be nice if you could edit a list of offices while editing the company? | ||
This is easy to do with the `array` field type: | ||
```javascript | ||
{ | ||
name: 'offices', | ||
label: 'Offices', | ||
type: 'array', | ||
schema: [ | ||
{ | ||
name: 'city', | ||
label: 'City', | ||
type: 'string' | ||
}, | ||
{ | ||
name: 'zip', | ||
label: 'Zip', | ||
type: 'string', | ||
def: '19147' | ||
}, | ||
{ | ||
name: 'thumbnail', | ||
label: 'Thumbnail', | ||
type: 'singleton', | ||
widgetType: 'slideshow', | ||
options: { | ||
limit: 1 | ||
} | ||
} | ||
] | ||
} | ||
```javascript | ||
Each `array` has its own `schema`, which supports all of the usual field types. You an even nest an `array` in another `array`. | ||
It's easy to access the resulting information in a page template, such as the `show` template for companies. The array property is... you guessed it... an array! Not hard to iterate over at all: | ||
```markup | ||
<h4>Offices</h4> | ||
<ul> | ||
{% for office in item.offices %} | ||
<li>{{ office.city }}, {{ office.zip }}</li> | ||
{% endfor %} | ||
</ul> | ||
``` | ||
Areas and thumbnails are allowed in arrays. In order to display them in a page template, you'll need to use this syntax: | ||
```markup | ||
{% for office in item.offices %} | ||
{{ aposSingleton({ area: office.thumbnail, type: 'slideshow', more options... }) }} | ||
{% endfor %} | ||
``` | ||
For an area you would write: | ||
```markup | ||
{% for office in item.offices %} | ||
{{ aposArea({ area: office.body, more options... }) }} | ||
{% endfor %} | ||
``` | ||
Since the area is not a direct property of the page, we can't use the `(page, areaname)` syntax that is typically more convenient. | ||
Areas and thumbnails in arrays cannot be edited "in context" on a page, they must be updated through the schema editor. | ||
#### Preventing Autocomplete | ||
@@ -184,0 +254,0 @@ |
Sorry, the diff of this file is not supported yet
90183
26
1159
940