apostrophe
Version:
Apostrophe is a user-friendly content management system. You'll need more than this core module. See apostrophenow.org to get started.
169 lines (156 loc) • 5.43 kB
JavaScript
var _ = require('lodash');
var extend = require('extend');
/**
* prune
* @augments Augments the apos object with methods relating to the
* pruning of temporary or oversized properties of page objects.
* @see static
*/
module.exports = function(self) {
// Except for `._id`, no property beginning with `_` should be
// loaded from or stored to the database. These are reserved for dynamically
// determined properties like permissions and joins. This method
// purges all such temporary properties from a page object, or any
// object that obeys this rule. It is called by `apos.putPage` before
// saving a page and is also used in files.js.
//
// This method is recursive and will prune both object properties
// and array properties recursively. (Arrays do not themselves contain
// any keys starting with _ but their values might.)
self.pruneTemporaryProperties = function(page) {
return self.pruneDeep(page, function(o, key, value, dotPath) {
// Don't crash on numbers
key = key.toString();
if ((key.substr(0, 1) === '_') && (key !== '_id')) {
return true;
}
});
};
// perform a recursive prune operation on a page. The second argument must be
// a function that takes an object, a key a value and a "dot path" and returns true if
// that key should be discarded. Remember, keys can be numeric.
//
// If the original object looks like:
//
// { a: { b: 5 } }
//
// Then when the pruner is invoked for b, the key will be 'b' and the
// dotPath will be the string 'a.b'.
//
// You do not need to pass a _dotPath argument to pruneDeep itself, it is used for
// recursive invocation.
self.pruneDeep = function(page, pruner, _dotPath) {
// We do not use underscore here because of performance issues.
// Pruning big nested objects is not something we can afford
// to do slowly. -Tom
var key;
var val;
var __dotPath;
if (_dotPath !== undefined) {
_dotPath += '.';
} else {
_dotPath = '';
}
if (Array.isArray(page)) {
for (key in page) {
var item = page[key];
if (typeof(item) === 'object') {
__dotPath = _dotPath + key.toString();
self.pruneDeep(item, pruner, __dotPath);
}
}
return;
}
var remove = [];
for (key in page) {
__dotPath = _dotPath + key.toString();
if (pruner(page, key, page[key], __dotPath)) {
remove.push(key);
} else {
val = page[key];
if (typeof(val) === 'object') {
self.pruneDeep(val, pruner, __dotPath);
}
}
}
_.each(remove, function(key) {
delete page[key];
});
};
// perform a recursive clone operation on a page. The second argument must
// be a function that takes an object, a key a value and a "dot path" and
// returns true if that key should be discarded rather than copied.
// Remember, keys can be numeric.
//
// If the original object looks like:
//
// { a: { b: 5 } }
//
// Then when the pruner is invoked for b, the key will be 'b' and the
// dotPath will be the string 'a.b'.
//
// You do not need to pass a _dotPath argument to pruneDeep itself, it
// is used for recursive invocation.
//
// The original object is not modified.
self.cloneDeep = function(page, pruner, _dotPath) {
var key;
var val;
var __dotPath;
var _page;
if (_dotPath !== undefined) {
_dotPath += '.';
} else {
_dotPath = '';
}
if (Array.isArray(page)) {
_page = [];
for (key in page) {
var item = page[key];
if (typeof(item) === 'object') {
__dotPath = _dotPath + key.toString();
item = self.cloneDeep(item, pruner, __dotPath);
}
_page[key] = item;
}
return _page;
}
_page = {};
for (key in page) {
__dotPath = _dotPath + key.toString();
if (pruner(page, key, page[key], __dotPath)) {
// Skip
} else {
val = page[key];
if (typeof(val) === 'object') {
val = self.cloneDeep(val, pruner, __dotPath);
}
_page[key] = val;
}
}
return _page;
};
// This method makes a deep copy of an object retaining only
// properties that are considered worth sending to the browser
// as JSON on every page request. A great deal of voluminous
// information is removed to avoid extremely slow page
// renders. Note that the existing object is not modified,
// which differs from the behavior of pruneDeep and pruneTemporaryProperties.
self.prunePage = function(page) {
page = _.omit(page, 'tabs', 'children', 'peers', 'lowSearchText', 'highSearchText', 'searchSummary', 'preMigrationAreas', 'legacyPermissions');
// Limit information about ancestors to avoid
// excessive amounts of data in the page
page.ancestors = _.map(page.ancestors, function(ancestor) {
return _.pick(ancestor, [ 'title', 'slug', '_id', 'type', 'published' ]);
});
return self.cloneDeep(page, function(o, k, v) {
// Drop joins, but watch out for ._id, ._edit and ._publish
// which we actually do want in some cases
if ((k !== '_id') && (k !== '_edit') && (k !== '_publish') && (k.toString().match(/^_/))) {
// No joins encoded in JSON please
return true;
}
return (v && (v.type === 'area'));
});
};
};