220 lines
6.6 KiB
JavaScript
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},
|
|
"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);
|
|
});
|
|
});
|
|
},
|
|
});
|