Files
DiceCloud/rpg-docs/lib/functions/parenting.js
2017-09-24 04:02:15 +02:00

220 lines
6.6 KiB
JavaScript

var childSchema = new SimpleSchema({
parent: {type: Object},
"parent.collection": {type: String},
"parent.id": {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
"parent.group": {type: String, optional: true},
"removedWith": {
optional: true,
type: String,
regEx: SimpleSchema.RegEx.Id,
},
});
var joinWithDefaultKeys = function(keys){
var defaultKeys = [
"charId",
];
return _.union(keys, defaultKeys);
};
var limitModifierToKeys = function(modifier, keys){
if (!modifier) return;
modifier = _.pick(modifier, ["$set", "$unset"]);
if (modifier.$set) modifier.$set = _.pick(modifier.$set, keys);
if (modifier.$unset) modifier.$unset = _.pick(modifier.$unset, keys);
if (_.isEmpty(modifier.$set)) delete modifier.$set;
if (_.isEmpty(modifier.$unset)) delete modifier.$unset;
return modifier;
};
var getParent = function(doc){
if (!doc || !doc.parent) return;
var parentCol = Meteor.isClient ?
window[doc.parent.collection] : global[doc.parent.collection];
if (parentCol)
return parentCol.findOne(doc.parent.id, {removed: true});
};
var inheritParentProperties = function(doc, collection){
var parent = getParent(doc);
if (!parent) throw new Meteor.Error(
"Parenting Error",
"Document's parent does not exist"
);
var handMeDowns = _.pick(parent, collection.inheritedKeys);
if (
_.contains(collection.inheritedKeys, "charId") &&
doc.parent.collection === "Characters"
){
handMeDowns.charId = doc.parent.id;
}
if (_.isEmpty(handMeDowns)) return;
collection.update(doc._id, {$set: handMeDowns});
};
var childCollections = [];
makeChild = function(collection, inheritedKeys){
inheritedKeys = inheritedKeys || [];
if (inheritedKeys) {
collection.inheritedKeys = joinWithDefaultKeys(inheritedKeys);
}
collection.helpers({
//returns the parent even if it's removed
getParent: function(){
return getParent(this);
},
getParentCollection: function(){
return Meteor.isClient ?
window[this.parent.collection] : global[this.parent.collection];
},
});
//when created, inherit parent properties
collection.after.insert(function(userId, doc){
inheritParentProperties(doc, collection);
});
collection.before.update(function(userId, doc, fieldNames, modifier, options){
//if we are restoring this asset, unmark that it was removed with its parent, we no longer care
if (modifier && modifier.$unset && modifier.$unset.removed) {
modifier.$unset.removedWith = "";
}
});
collection.after.update(function(userId, doc, fieldNames, modifier, options) {
if (modifier && modifier.$set && modifier.$set["parent.id"]){
//when we change parents, inherit its properties
inheritParentProperties(doc, collection);
}
});
collection.softRemoveNode = collection.softRemoveNode || function(id){
collection.softRemove(id);
};
collection.restoreNode = collection.restoreNode || function(id){
collection.restore(id);
};
collection.attachSchema(childSchema);
childCollections.push(collection);
};
makeParent = function(collection, donatedKeys){
donatedKeys = joinWithDefaultKeys(donatedKeys);
var collectionName = collection._collection.name;
//after changing, push the changes to all children
collection.after.update(function(userId, doc, fieldNames, modifier, options) {
modifier = limitModifierToKeys(modifier, donatedKeys);
doc = _.pick(doc, ["_id", "charId"]);
if (!modifier) return;
Meteor.call("updateChildren", doc, modifier, true);
});
collection.softRemoveNode = function(id){
Meteor.call("softRemoveNode", collectionName, id);
};
collection.restoreNode = function(id){
Meteor.call("restoreNode", collectionName, id);
};
if (Meteor.isServer) collection.after.remove(function(userId, doc) {
_.each(childCollections, function(collection){
collection.remove(
{"parent.id": doc._id}
);
});
});
};
var checkPermission = function(userId, charId){
var char = Characters.findOne(charId, {fields: {owner: 1, writers: 1}});
if (!char)
throw new Meteor.Error("Access Denied, no charId",
"Character " + charId + " does not exist");
if (!userId)
throw new Meteor.Error("Access Denied, no userId",
"No UserId set when trying to update character asset.");
if (char.owner !== userId && !_.contains(char.writers, userId))
throw new Meteor.Error("Access Denied, not permitted",
"Not permitted to update assets of this character.");
return true;
};
var cascadeSoftRemove = function(id, removedWithId){
_.each(childCollections, function(treeCollection){
treeCollection.update(
{"parent.id": id},
{$set: {
removed: true,
removedWith: removedWithId,
}},
{multi: true}
);
treeCollection.find({"parent.id": id}).forEach(function(doc){
cascadeSoftRemove(doc._id, removedWithId);
});
});
};
var checkRemovePermission = function(collectionName, id, self){
check(collectionName, String);
check(id, String);
var collection = Mongo.Collection.get(collectionName);
var node = collection.findOne(id);
var charId = node && node.charId;
checkPermission(self.userId, charId);
};
Meteor.methods({
softRemoveNode: function(collectionName, id){
checkRemovePermission(collectionName, id, this);
var collection = Mongo.Collection.get(collectionName);
collection.softRemove(id);
cascadeSoftRemove(id, id);
},
restoreNode: function(collectionName, id){
checkRemovePermission(collectionName, id, this);
var collection = Mongo.Collection.get(collectionName);
collection.restore(id);
_.each(childCollections, function(treeCollection){
treeCollection.update(
{removedWith: id, removed: true},
{$unset: {removed: true, removedWith: ""}},
{multi: true}
);
});
},
updateChildren: function(parent, modifier, limitToInheritance) {
check(parent, {_id: String, charId: String});
check(modifier, Object);
checkPermission(this.userId, parent.charId);
var selector = {"parent.id": parent._id};
_.each(childCollections, function(collection){
var thisModifier;
if (limitToInheritance){
thisModifier = limitModifierToKeys(modifier, collection.inheritedKeys);
} else {
thisModifier = _.clone(modifier);
}
if (_.isEmpty(thisModifier)) return;
collection.update(selector, thisModifier, {multi: true, removed: true});
});
},
cloneChildren: function(objectId, newParent){
check(objectId, String);
check(newParent, {id: String, collection: String});
_.each(childCollections, function(collection){
var keys = collection.simpleSchema().objectKeys();
collection.find({"parent.id": objectId}).forEach(function(doc){
var newDoc = _.pick(doc, keys);
newDoc.parent = newParent;
collection.insert(newDoc);
});
});
},
});