Merge feature-nested-sets into develop

This commit is contained in:
ThaumRystra
2023-12-18 18:27:17 +02:00
523 changed files with 5492 additions and 3763 deletions

View File

@@ -1,13 +1,13 @@
import SimpleSchema from 'simpl-schema';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';
import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties';
import { CreatureSchema } from '/imports/api/creature/creatures/Creatures';
let createS3FilesCollection;
if (Meteor.isServer) {
createS3FilesCollection = require('/imports/api/files/server/s3FileStorage.js').createS3FilesCollection
createS3FilesCollection = require('/imports/api/files/server/s3FileStorage').createS3FilesCollection
} else {
createS3FilesCollection = require('/imports/api/files/client/s3FileStorage.js').createS3FilesCollection
createS3FilesCollection = require('/imports/api/files/client/s3FileStorage').createS3FilesCollection
}
const ArchiveCreatureFiles = createS3FilesCollection({

View File

@@ -1,21 +1,22 @@
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION';
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
import Experiences from '/imports/api/creature/experience/Experiences.js';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions';
import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
import Experiences from '/imports/api/creature/experience/Experiences';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
import { getFilter } from '/imports/api/parenting/parentingFunctions';
export function getArchiveObj(creatureId){
export function getArchiveObj(creatureId) {
// Build the archive document
const creature = Creatures.findOne(creatureId);
const properties = CreatureProperties.find({'ancestors.id': creatureId}).fetch();
const experiences = Experiences.find({creatureId}).fetch();
const logs = CreatureLogs.find({creatureId}).fetch();
const properties = CreatureProperties.find({ ...getFilter.descendantsOfRoot(creatureId) }).fetch();
const experiences = Experiences.find({ creatureId }).fetch();
const logs = CreatureLogs.find({ creatureId }).fetch();
let archiveCreature = {
meta: {
type: 'DiceCloud V2 Creature Archive',
@@ -31,7 +32,7 @@ export function getArchiveObj(creatureId){
return archiveCreature;
}
export function archiveCreature(creatureId){
export function archiveCreature(creatureId) {
const archive = getArchiveObj(creatureId);
const buffer = Buffer.from(JSON.stringify(archive, null, 2));
ArchiveCreatureFiles.write(buffer, {
@@ -44,7 +45,7 @@ export function archiveCreature(creatureId){
creatureName: archive.creature.name,
},
}, (error) => {
if (error){
if (error) {
throw error;
} else {
removeCreatureWork(creatureId);
@@ -65,10 +66,10 @@ const archiveCreatureToFile = new ValidatedMethod({
numRequests: 10,
timeInterval: 5000,
},
async run({creatureId}) {
async run({ creatureId }) {
assertOwnership(creatureId, this.userId);
if (Meteor.isServer){
archiveCreature(creatureId, this.userId);
if (Meteor.isServer) {
archiveCreature(creatureId);
} else {
removeCreatureWork(creatureId);
}

View File

@@ -1,3 +1,3 @@
import '/imports/api/creature/archive/methods/archiveCreatureToFile.js';
import '/imports/api/creature/archive/methods/restoreCreatureFromFile.js';
import '/imports/api/creature/archive/methods/removeArchiveCreature.js';
import '/imports/api/creature/archive/methods/archiveCreatureToFile';
import '/imports/api/creature/archive/methods/restoreCreatureFromFile';
import '/imports/api/creature/archive/methods/removeArchiveCreature';

View File

@@ -1,8 +1,8 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';
const removeArchiveCreature = new ValidatedMethod({
name: 'ArchiveCreatureFiles.methods.removeArchiveCreature',

View File

@@ -1,20 +1,20 @@
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION';
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
import Experiences from '/imports/api/creature/experience/Experiences.js';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
import verifyArchiveSafety from '/imports/api/creature/archive/methods/verifyArchiveSafety.js';
import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
import Experiences from '/imports/api/creature/experience/Experiences';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';
import verifyArchiveSafety from '/imports/api/creature/archive/methods/verifyArchiveSafety';
let migrateArchive;
if (Meteor.isServer) {
migrateArchive = require('/imports/migrations/archive/migrateArchive.js').default;
migrateArchive = require('/imports/migrations/archive/migrateArchive').default;
}
function restoreCreature(archive, userId) {

View File

@@ -1,7 +1,7 @@
import { slice } from 'lodash';
import PER_CREATURE_LOG_LIMIT from '/imports/api/creature/log/CreatureLogs.js';
import PER_CREATURE_LOG_LIMIT from '/imports/api/creature/log/CreatureLogs';
export default function verifyArchiveSafety({ meta, creature, properties, experiences, logs }){
export default function verifyArchiveSafety({ meta, creature, properties, experiences, logs }) {
const creatureId = creature._id;
// Check lengths of arrays
@@ -21,8 +21,14 @@ export default function verifyArchiveSafety({ meta, creature, properties, experi
}
});
properties.forEach(prop => {
if (prop.ancestors[0].id !== creatureId) {
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
if (meta.schemaVersion.schemaVersion >= 3) {
if (prop.root?.id !== creatureId) {
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
}
} else {
if (prop.ancestors?.[0]?.id !== creatureId) {
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
}
}
});
}

View File

@@ -1,5 +1,5 @@
import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
let CreatureFolders = new Mongo.Collection('creatureFolders');
@@ -35,5 +35,5 @@ let creatureFolderSchema = new SimpleSchema({
CreatureFolders.attachSchema(creatureFolderSchema);
import '/imports/api/creature/creatureFolders/methods.js/index.js';
import '/imports/api/creature/creatureFolders/methods.js/index';
export default CreatureFolders;

View File

@@ -1,4 +1,4 @@
import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName.js';
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder';
import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName';
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder';
import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder';

View File

@@ -1,4 +1,4 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
@@ -15,23 +15,23 @@ const insertCreatureFolder = new ValidatedMethod({
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.insert.denied',
'You need to be logged in to insert a folder');
'You need to be logged in to insert a folder');
}
// Limit folders to 50 per user
let existingFolders = CreatureFolders.find({
owner: userId
}, {
fields: {order: 1},
sort: {order :-1}
fields: { order: 1 },
sort: { order: -1 }
});
if (existingFolders.count() >= 50){
if (existingFolders.count() >= 50) {
throw new Meteor.Error('creatureFolders.methods.insert.denied',
'You can not have more than 50 folders');
'You can not have more than 50 folders');
}
// Make the new folder the last in the order
let order = 0;
let lastFolder = existingFolders.fetch()[0];
if (lastFolder){
if (lastFolder) {
order = (lastFolder.order || 0) + 1;
}
// Insert

View File

@@ -1,4 +1,4 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
@@ -10,33 +10,33 @@ const moveCreatureToFolder = new ValidatedMethod({
numRequests: 5,
timeInterval: 5000,
},
run({creatureId, folderId}) {
run({ creatureId, folderId }) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to remove a folder');
'You need to be logged in to remove a folder');
}
// Check that this folder is owned by the user
if (folderId){
if (folderId) {
let existingFolder = CreatureFolders.findOne(folderId);
if (existingFolder.owner !== userId){
if (existingFolder.owner !== userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
'This folder does not belong to you');
}
}
// Remove from other folders
CreatureFolders.update({
owner: userId
}, {
$pull: {creatures: creatureId},
$pull: { creatures: creatureId },
}, {
multi: true,
});
if (folderId){
if (folderId) {
// Add to this folder
CreatureFolders.update(folderId, {
$addToSet: {creatures: creatureId},
$addToSet: { creatures: creatureId },
});
}
},

View File

@@ -1,4 +1,4 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
@@ -10,18 +10,18 @@ const removeCreatureFolder = new ValidatedMethod({
numRequests: 5,
timeInterval: 5000,
},
run({_id}) {
run({ _id }) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to remove a folder');
'You need to be logged in to remove a folder');
}
// Check that this folder is owned by the user
let existingFolder = CreatureFolders.findOne(_id);
if (existingFolder.owner !== userId){
if (existingFolder.owner !== userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
'This folder does not belong to you');
}
// Remove
return CreatureFolders.remove(_id);

View File

@@ -1,4 +1,4 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
@@ -10,31 +10,31 @@ const reorderCreatureFolder = new ValidatedMethod({
numRequests: 5,
timeInterval: 5000,
},
run({_id, order}) {
run({ _id, order }) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.reorder.denied',
'You need to be logged in to reorder a folder');
'You need to be logged in to reorder a folder');
}
// Check that this folder is owned by the user
let existingFolder = CreatureFolders.findOne(_id);
if (existingFolder.owner !== userId){
if (existingFolder.owner !== userId) {
throw new Meteor.Error('creatureFolders.methods.reorder.denied',
'This folder does not belong to you');
'This folder does not belong to you');
}
// First give it the new order, it should end in 0.5 putting it between two other docs
CreatureFolders.update(_id, {$set: {order}});
CreatureFolders.update(_id, { $set: { order } });
this.unblock();
// Reorder all the folders with integer numbers in this new order
CreatureFolders.find({
owner: userId
}, {
fields: {order: 1,},
sort: {order: -1}
fields: { order: 1, },
sort: { order: -1 }
}).forEach((folder, index) => {
if (folder.order !== index){
CreatureFolders.update(_id, {$set: {order: index}})
if (folder.order !== index) {
CreatureFolders.update(_id, { $set: { order: index } })
}
});
},

View File

@@ -1,4 +1,4 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
@@ -10,21 +10,21 @@ const updateCreatureFolderName = new ValidatedMethod({
numRequests: 5,
timeInterval: 5000,
},
run({_id, name}) {
run({ _id, name }) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to update a folder');
'You need to be logged in to update a folder');
}
// Check that this folder is owned by the user
let existingFolder = CreatureFolders.findOne(_id);
if (existingFolder.owner !== userId){
if (existingFolder.owner !== userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
'This folder does not belong to you');
}
// Update
return CreatureFolders.update(_id, {$set: {name}});
return CreatureFolders.update(_id, { $set: { name } });
},
});

View File

@@ -1,15 +1,35 @@
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js';
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema';
import ChildSchema, { TreeDoc } from '/imports/api/parenting/ChildSchema';
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema';
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex';
import { storedIconsSchema } from '/imports/api/icons/Icons';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
let CreatureProperties = new Mongo.Collection('creatureProperties');
const CreatureProperties: Mongo.Collection<CreatureProperty> = new Mongo.Collection('creatureProperties');
let CreaturePropertySchema = new SimpleSchema({
export interface CreatureProperty extends TreeDoc {
_id: string
_migrationError?: string
type: string
tags: string[]
disabled?: boolean
icon?: {
name: string
shape: string
},
libraryNodeId?: string
slotQuantityFilled?: number
inactive?: boolean
deactivatedByAncestor?: boolean
deactivatedBySelf?: boolean
deactivatedByToggle?: boolean
deactivatingToggleId?: boolean
dirty?: boolean
}
const CreaturePropertySchema = new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
@@ -56,7 +76,7 @@ let CreaturePropertySchema = new SimpleSchema({
const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
// Denormalised flag if this property is inactive on the sheet for any reason
// Including being disabled, or a decendent of a disabled property
// Including being disabled, or a descendant of a disabled property
inactive: {
type: Boolean,
optional: true,
@@ -135,13 +155,14 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
for (let key in propertySchemasIndex) {
let schema = new SimpleSchema({});
for (const key in propertySchemasIndex) {
const schema = new SimpleSchema({});
schema.extend(propertySchemasIndex[key]);
schema.extend(CreaturePropertySchema);
schema.extend(ColorSchema);
schema.extend(ChildSchema);
schema.extend(SoftRemovableSchema);
// @ts-expect-error don't have types for .attachSchema
CreatureProperties.attachSchema(schema, {
selector: { type: key }
});

View File

@@ -1,5 +1,5 @@
import { getCreature } from '/imports/api/engine/loadCreatures';
export default function getRootCreatureAncestor(property) {
return getCreature(property.ancestors[0].id);
return getCreature(property.root.id);
}

View File

@@ -1,9 +1,9 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
const adjustQuantity = new ValidatedMethod({
name: 'creatureProperties.adjustQuantity',

View File

@@ -1,22 +1,21 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import LibraryNodes from '/imports/api/library/LibraryNodes';
import { RefSchema } from '/imports/api/parenting/ChildSchema';
import {
assertEditPermission,
assertDocEditPermission,
assertCopyPermission
} from '/imports/api/sharing/sharingPermissions.js';
} from '/imports/api/sharing/sharingPermissions';
import {
setLineageOfDocs,
getAncestry,
fetchDocByRef,
getFilter,
renewDocIds
} from '/imports/api/parenting/parenting.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
import Libraries from '/imports/api/library/Libraries.js';
} from '/imports/api/parenting/parentingFunctions';
import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
import Libraries from '/imports/api/library/Libraries';
const DUPLICATE_CHILDREN_LIMIT = 500;
const copyPropertyToLibrary = new ValidatedMethod({
@@ -41,33 +40,30 @@ const copyPropertyToLibrary = new ValidatedMethod({
},
run({ propId, parentRef, order }) {
// get the new ancestry for the properties
let { parentDoc, ancestors } = getAncestry({ parentRef });
const parentDoc = fetchDocByRef(parentRef);
// Check permission to edit the destination
let rootLibrary;
if (parentRef.collection === 'libraries') {
rootLibrary = parentDoc;
} else if (parentRef.collection === 'libraryNodes') {
rootLibrary = Libraries.findOne(parentDoc.ancestors[0].id)
rootLibrary = Libraries.findOne(parentDoc.root.id)
} else {
throw `${parentRef.collection} is not a valid parent collection`
}
assertEditPermission(rootLibrary, this.userId);
const insertedRootNode = insertNodeFromProperty(propId, ancestors, order, this);
const insertedRootNode = insertNodeFromProperty(propId, order, this);
// Tree structure changed by inserts, reorder the tree
reorderDocs({
collection: LibraryNodes,
ancestorId: rootLibrary._id,
});
rebuildNestedSets(LibraryNodes, rootLibrary._id);
// Return the docId of the inserted root property
return insertedRootNode?._id;
},
});
function insertNodeFromProperty(propId, ancestors, order, method) {
function insertNodeFromProperty(propId, order, method) {
// Fetch the property and its descendants, provided they have not been
// removed
let prop = CreatureProperties.findOne({
@@ -87,9 +83,9 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
// Make sure we can edit this property
assertDocEditPermission(prop, method.userId);
let oldParent = prop.parent;
let oldParentId = prop.parentId;
const propCursor = CreatureProperties.find({
'ancestors.id': propId,
...getFilter.descendants(prop),
removed: { $ne: true },
});
@@ -109,13 +105,6 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
// properties
assertSourceLibraryCopyPermission(props, method);
// re-map all the ancestors
setLineageOfDocs({
docArray: props,
newAncestry: ancestors,
oldParent,
});
// Give the docs new IDs without breaking internal references
renewDocIds({
docArray: props,
@@ -123,14 +112,8 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
});
// Order the root node
if (order === undefined) {
setDocToLastOrder({
collection: LibraryNodes,
doc: prop,
});
} else {
prop.order = order;
}
prop.left = Number.MAX_SAFE_INTEGER - 1;
prop.right = Number.MAX_SAFE_INTEGER;
// Clean the props
props = cleanProps(props);
@@ -142,8 +125,8 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
/**
*
* @param {[Property]} props The properties to check
* @param {String} userId The userId trying to copy these properties to a library
* @param props The properties to check
* @param userId The userId trying to copy these properties to a library
* Checks that every property can be copied out of the library that originated it by this user
*/
function assertSourceLibraryCopyPermission(props, method) {
@@ -162,9 +145,9 @@ function assertSourceLibraryCopyPermission(props, method) {
LibraryNodes.find({
_id: { $in: libraryNodeIds }
}, {
fields: { ancestors: 1 }
fields: { root: 1 }
}).forEach(node => {
sourceLibIds.add(node.ancestors?.[0]?.id);
sourceLibIds.add(node.root.id);
});
// Assert copy permission on each of those libraries

View File

@@ -1,10 +1,10 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
import ActionContext from '/imports/api/engine/actions/ActionContext';
const damageProperty = new ValidatedMethod({
name: 'creatureProperties.damage',
@@ -28,7 +28,7 @@ const damageProperty = new ValidatedMethod({
if (!prop) throw new Meteor.Error(
'Damage property failed', 'Property doesn\'t exist'
);
const creatureId = prop.ancestors[0].id;
const creatureId = prop.root.id;
const actionContext = new ActionContext(creatureId, [creatureId], this);
// Check permissions
@@ -59,7 +59,7 @@ const damageProperty = new ValidatedMethod({
},
});
export function damagePropertyWork({ prop, operation, value, actionContext, logFunction }) {
export function damagePropertyWork({ prop, operation, value, actionContext, logFunction = undefined }) {
// Save the value to the scope before applying the before triggers
if (operation === 'increment') {

View File

@@ -1,18 +1,18 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import {
setLineageOfDocs,
getFilter,
renewDocIds
} from '/imports/api/parenting/parenting.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
} from '/imports/api/parenting/parentingFunctions';
import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
var snackbar;
if (Meteor.isClient) {
snackbar = require(
'/imports/client/ui/components/snackbars/SnackbarQueue.js'
'/imports/client/ui/components/snackbars/SnackbarQueue'
).snackbar
}
@@ -33,6 +33,8 @@ const duplicateProperty = new ValidatedMethod({
},
run({ _id }) {
let property = CreatureProperties.findOne(_id);
if (!property) throw new Meteor.Error('not-found', 'The source property was not found');
let creature = getRootCreatureAncestor(property);
assertEditPermission(creature, this.userId);
@@ -49,7 +51,7 @@ const duplicateProperty = new ValidatedMethod({
// Get all the descendants
let nodes = CreatureProperties.find({
'ancestors.id': _id,
...getFilter.descendants(property),
removed: { $ne: true },
}, {
limit: DUPLICATE_CHILDREN_LIMIT + 1,
@@ -66,22 +68,13 @@ const duplicateProperty = new ValidatedMethod({
}
}
// re-map all the ancestors
setLineageOfDocs({
docArray: nodes,
newAncestry: [
...property.ancestors,
{ id: propertyId, collection: 'creatureProperties' }
],
oldParent: { id: _id, collection: 'creatureProperties' },
});
// Give the docs new IDs without breaking internal references
const allNodes = [property, ...nodes];
renewDocIds({ docArray: allNodes });
// Order the root node
property.order += 0.5;
property.left = Number.MAX_SAFE_INTEGER - 1;
property.right = Number.MAX_SAFE_INTEGER;
// Mark the sheet as needing recompute
property.dirty = true;
@@ -90,10 +83,7 @@ const duplicateProperty = new ValidatedMethod({
CreatureProperties.batchInsert(allNodes);
// Tree structure changed by inserts, reorder the tree
reorderDocs({
collection: CreatureProperties,
ancestorId: property.ancestors[0].id,
});
rebuildNestedSets(CreatureProperties, property.root.id);
return propertyId;
},

View File

@@ -1,11 +1,11 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { organizeDoc } from '/imports/api/parenting/organizeMethods';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
// Equipping or unequipping an item will also change its parent
const equipItem = new ValidatedMethod({

View File

@@ -1,8 +1,8 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
const flipToggle = new ValidatedMethod({
name: 'creatureProperties.flipToggle',
@@ -17,7 +17,7 @@ const flipToggle = new ValidatedMethod({
run({ _id }) {
// Permission
let property = CreatureProperties.findOne(_id, {
fields: { type: 1, ancestors: 1, enabled: 1, disabled: 1 }
fields: { type: 1, root: 1, enabled: 1, disabled: 1 }
});
if (property.type !== 'toggle') {
throw new Meteor.Error('wrong property',

View File

@@ -1,13 +1,14 @@
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { getFilter } from '/imports/api/parenting/parentingFunctions';
export default function getParentRefByTag(creatureId, tag){
export default function getParentRefByTag(creatureId, tag) {
let prop = CreatureProperties.findOne({
'ancestors.id': creatureId,
removed: {$ne: true},
inactive: {$ne: true},
...getFilter.descendantsOfRoot(creatureId),
removed: { $ne: true },
inactive: { $ne: true },
tags: tag,
}, {
sort: {order: 1},
sort: { order: 1 },
});
return prop && {id: prop._id, collection: 'creatureProperties'};
return prop && { id: prop._id, collection: 'creatureProperties' };
}

View File

@@ -1,3 +1,5 @@
import { getFilter } from "/imports/api/parenting/parentingFunctions";
export default function getSlotFillFilter({ slot, libraryIds }) {
if (!slot) throw 'Slot is required for getSlotFillFilter';
@@ -6,9 +8,14 @@ export default function getSlotFillFilter({ slot, libraryIds }) {
let filter = {
fillSlots: true,
removed: { $ne: true },
$and: []
$and: [],
};
filter['ancestors.id'] = { $in: libraryIds };
if (libraryIds.length) {
Object.assign(
filter,
getFilter.descendantsOfAllRoots(libraryIds)
);
}
if (slot.slotType) {
filter.$and.push({
$or: [{

View File

@@ -1,5 +1,5 @@
import { assert } from 'chai';
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js';
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter';
describe('Slot fill filter', function () {
@@ -33,7 +33,7 @@ describe('Slot fill filter', function () {
$or: [{
libraryTags: { $all: ['tag1', 'tag2'] }
}],
'ancestors.id': { $in: ['libraryId1', 'libraryId2'] },
'root.id': { $in: ['libraryId1', 'libraryId2'] },
removed: { $ne: true },
fillSlots: true,
});
@@ -76,7 +76,7 @@ describe('Slot fill filter', function () {
$and: [
{ libraryTags: { $nin: ['tag5', 'tag6', 'tag7', 'tag8'] } },
],
'ancestors.id': { $in: ['libraryId1', 'libraryId2'] },
'root.id': { $in: ['libraryId1', 'libraryId2'] },
removed: { $ne: true },
fillSlots: true,
});

View File

@@ -1,14 +1,14 @@
import '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
import '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary.js';
import '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import '/imports/api/creature/creatureProperties/methods/duplicateProperty.js';
import '/imports/api/creature/creatureProperties/methods/equipItem.js';
import '/imports/api/creature/creatureProperties/methods/insertProperty.js';
import '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
import '/imports/api/creature/creatureProperties/methods/pullFromProperty.js';
import '/imports/api/creature/creatureProperties/methods/pushToProperty.js';
import '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
import '/imports/api/creature/creatureProperties/methods/selectAmmoItem.js';
import '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
import '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
import '/imports/api/creature/creatureProperties/methods/flipToggle.js';
import '/imports/api/creature/creatureProperties/methods/adjustQuantity';
import '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary';
import '/imports/api/creature/creatureProperties/methods/damageProperty';
import '/imports/api/creature/creatureProperties/methods/duplicateProperty';
import '/imports/api/creature/creatureProperties/methods/equipItem';
import '/imports/api/creature/creatureProperties/methods/insertProperty';
import '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode';
import '/imports/api/creature/creatureProperties/methods/pullFromProperty';
import '/imports/api/creature/creatureProperties/methods/pushToProperty';
import '/imports/api/creature/creatureProperties/methods/restoreProperty';
import '/imports/api/creature/creatureProperties/methods/selectAmmoItem';
import '/imports/api/creature/creatureProperties/methods/softRemoveProperty';
import '/imports/api/creature/creatureProperties/methods/updateCreatureProperty';
import '/imports/api/creature/creatureProperties/methods/flipToggle';

View File

@@ -1,14 +1,12 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
import { getAncestry } from '/imports/api/parenting/parenting.js';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import { getHighestOrder } from '/imports/api/parenting/order.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { fetchDocByRef, rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
import { RefSchema } from '/imports/api/parenting/ChildSchema';
const insertProperty = new ValidatedMethod({
name: 'creatureProperties.insert',
@@ -25,27 +23,23 @@ const insertProperty = new ValidatedMethod({
timeInterval: 5000,
},
run({ creatureProperty, parentRef }) {
// get the new ancestry for the properties
let { parentDoc, ancestors } = getAncestry({ parentRef });
let rootCreature;
const parentDoc = fetchDocByRef(parentRef);
// Check permission to edit
let rootCreature;
if (parentRef.collection === 'creatures') {
rootCreature = parentDoc;
} else if (parentRef.collection === 'creatureProperties') {
rootCreature = getRootCreatureAncestor(parentDoc);
creatureProperty.parentId = parentDoc._id;
} else {
throw `${parentRef.collection} is not a valid parent collection`
}
assertEditPermission(rootCreature, this.userId);
creatureProperty.parent = parentRef;
creatureProperty.ancestors = ancestors;
creatureProperty.root = { collection: 'creatures', id: rootCreature._id };
return insertPropertyWork({
property: creatureProperty,
creature: rootCreature,
});
return insertPropertyWork(creatureProperty);
},
});
@@ -77,18 +71,17 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
},
run({ creatureProperty, creatureId, tag, tagDefaultName }) {
let parentRef = getParentRefByTag(creatureId, tag);
let insertFolderFirst = false;
if (!parentRef) {
// Use the creature as the parent and mark that we need to insert the folder first later
var insertFolderFirst = true;
insertFolderFirst = true;
parentRef = { id: creatureId, collection: 'creatures' };
}
// get the new ancestry for the properties
let { parentDoc, ancestors } = getAncestry({ parentRef });
// Check permission to edit
let rootCreature;
const parentDoc = fetchDocByRef(parentRef);
if (parentRef.collection === 'creatures') {
rootCreature = parentDoc;
} else if (parentRef.collection === 'creatureProperties') {
@@ -98,46 +91,34 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
}
assertEditPermission(rootCreature, this.userId);
const root = { collection: 'creatures', id: rootCreature._id };
// Add the folder first if we need to
if (insertFolderFirst) {
let order = getHighestOrder({
collection: CreatureProperties,
ancestorId: parentRef.id,
}) + 1;
let id = CreatureProperties.insert({
type: 'folder',
name: tagDefaultName || (tag.charAt(0).toUpperCase() + tag.slice(1)),
tags: [tag],
parent: parentRef,
ancestors: [parentRef],
order,
// parentId: undefined,
root,
});
// Make the folder our new parent
let newParentRef = { id, collection: 'creatureProperties' };
ancestors = [parentRef, newParentRef];
parentRef = newParentRef;
creatureProperty.order = order + 1;
parentRef = { id, collection: 'creatureProperties' };
}
creatureProperty.parent = parentRef;
creatureProperty.ancestors = ancestors;
creatureProperty.root = root;
creatureProperty.parentId = parentRef.id;
return insertPropertyWork({
property: creatureProperty,
creature: rootCreature,
});
return insertPropertyWork(creatureProperty);
},
});
export function insertPropertyWork({ property, creature }) {
export function insertPropertyWork(property) {
delete property._id;
property.dirty = true;
let _id = CreatureProperties.insert(property);
// Tree structure changed by insert, reorder the tree
reorderDocs({
collection: CreatureProperties,
ancestorId: creature._id,
});
rebuildNestedSets(CreatureProperties, property.root.id);
return _id;
}

View File

@@ -1,19 +1,17 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import LibraryNodes from '/imports/api/library/LibraryNodes';
import { RefSchema } from '/imports/api/parenting/ChildSchema';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import {
setLineageOfDocs,
getAncestry,
renewDocIds
} from '/imports/api/parenting/parenting.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
renewDocIds,
fetchDocByRef,
rebuildNestedSets,
getFilter
} from '/imports/api/parenting/parentingFunctions';
import { union } from 'lodash';
const insertPropertyFromLibraryNode = new ValidatedMethod({
@@ -30,19 +28,15 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
parentRef: {
type: RefSchema,
},
order: {
type: Number,
optional: true,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({ nodeIds, parentRef, order }) {
run({ nodeIds, parentRef }) {
// get the new ancestry for the properties
let { parentDoc, ancestors } = getAncestry({ parentRef });
const parentDoc = fetchDocByRef(parentRef);
// Check permission to edit
let rootCreature;
@@ -55,37 +49,32 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
}
assertEditPermission(rootCreature, this.userId);
// {libraryId: hasViewPermission}
//let libraryPermissionMemoir = {};
const root = { collection: 'creatures', id: rootCreature._id };
const parentId = parentRef.id;
let node;
nodeIds.forEach(nodeId => {
// TODO: Check library view permission for each node before starting
node = insertPropertyFromNode(nodeId, ancestors, order);
node = insertPropertyFromNode(nodeId, root, parentId);
});
// get one of the root inserted docs
let rootId = node._id;
// Tree structure changed by inserts, reorder the tree
reorderDocs({
collection: CreatureProperties,
ancestorId: rootCreature._id,
});
// Return the docId of the last property, the inserted root property
return rootId;
rebuildNestedSets(CreatureProperties, rootCreature._id);
// get one of the root inserted docs
const lastInsertedId = node?._id;
return lastInsertedId;
},
});
function insertPropertyFromNode(nodeId, ancestors, order) {
// Fetch the library node and its decendents, provided they have not been
function insertPropertyFromNode(nodeId, root, parentId) {
// Fetch the library node and its descendants, provided they have not been
// removed
// TODO: Check permission to read the library this node is in
let node = LibraryNodes.findOne({
_id: nodeId,
removed: { $ne: true },
});
if (!node) {
if (Meteor.isClient) return;
if (Meteor.isClient) return {};
else {
throw new Meteor.Error(
'Insert property from library failed',
@@ -93,13 +82,12 @@ function insertPropertyFromNode(nodeId, ancestors, order) {
);
}
}
let oldParent = node.parent;
let nodes = LibraryNodes.find({
'ancestors.id': nodeId,
...getFilter.descendants(node),
removed: { $ne: true },
}).fetch();
// The root node is first in the array of nodes
// It must get the first generated ID to prevent flickering
nodes = [node, ...nodes];
@@ -112,31 +100,17 @@ function insertPropertyFromNode(nodeId, ancestors, order) {
// set libraryNodeIds
storeLibraryNodeReferences(nodes);
// re-map all the ancestors
setLineageOfDocs({
docArray: nodes,
newAncestry: ancestors,
oldParent,
});
// Give the docs new IDs without breaking internal references
renewDocIds({
docArray: nodes,
collectionMap: { 'libraryNodes': 'creatureProperties' }
});
// Order the root node
if (order === undefined) {
setDocToLastOrder({
collection: CreatureProperties,
doc: node,
});
} else {
node.order = order;
}
// Mark root node as dirty
node.dirty = true;
// Mark all nodes as dirty
dirtyNodes(nodes);
// Move the root node to the end of the order
node.left = Number.MAX_SAFE_INTEGER;
// Insert the creature properties
CreatureProperties.batchInsert(nodes);
@@ -150,12 +124,6 @@ function storeLibraryNodeReferences(nodes) {
});
}
function dirtyNodes(nodes) {
nodes.forEach(node => {
node.dirty = true;
});
}
// Covert node references into actual nodes
// TODO: check permissions for each library a reference node references
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
@@ -178,7 +146,6 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
let referencedNode
try {
referencedNode = fetchDocByRef(node.ref);
referencedNode.order = node.order;
referencedNode.tags = union(node.tags, referencedNode.tags);
// We are definitely replacing this node, so add it to the list
visitedRefs.add(node._id);
@@ -188,23 +155,15 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
}
// Get all the descendants of the referenced node
let descendents = LibraryNodes.find({
'ancestors.id': referencedNode._id,
let descendants = LibraryNodes.find({
...getFilter.descendants(referencedNode),
removed: { $ne: true },
}, {
sort: { order: 1 },
}).fetch();
// We are adding the referenced node and its descendants
let addedNodes = [referencedNode, ...descendents];
// re-map all the ancestors to parent the new sub-tree into our existing
// node tree
setLineageOfDocs({
docArray: addedNodes,
newAncestry: node.ancestors,
oldParent: referencedNode.parent,
});
let addedNodes = [referencedNode, ...descendants];
// Filter all the looped references
addedNodes = addedNodes.filter(addedNode => {

View File

@@ -1,8 +1,8 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
const pullFromProperty = new ValidatedMethod({
name: 'creatureProperties.pull',

View File

@@ -1,8 +1,8 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import { get } from 'lodash';
const pushToProperty = new ValidatedMethod({

View File

@@ -1,10 +1,10 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { restore } from '/imports/api/parenting/softRemove.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { restore } from '/imports/api/parenting/softRemove';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
const restoreProperty = new ValidatedMethod({
name: 'creatureProperties.restore',

View File

@@ -1,9 +1,9 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
const selectAmmoItem = new ValidatedMethod({
name: 'creatureProperties.selectAmmoItem',

View File

@@ -1,10 +1,10 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { softRemove } from '/imports/api/parenting/softRemove.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { softRemove } from '/imports/api/parenting/softRemove';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
const softRemoveProperty = new ValidatedMethod({
name: 'creatureProperties.softRemove',

View File

@@ -1,8 +1,8 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
const updateCreatureProperty = new ValidatedMethod({
name: 'creatureProperties.update',
@@ -14,6 +14,8 @@ const updateCreatureProperty = new ValidatedMethod({
case 'order':
case 'parent':
case 'ancestors':
case 'root':
case 'parentId':
case 'damage':
throw new Meteor.Error('Permission denied',
'This property can\'t be updated directly');
@@ -27,7 +29,7 @@ const updateCreatureProperty = new ValidatedMethod({
run({ _id, path, value }) {
// Permission
let property = CreatureProperties.findOne(_id, {
fields: { type: 1, ancestors: 1 }
fields: { type: 1, root: 1 }
});
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);

View File

@@ -1,12 +1,9 @@
import computeCreature from '/imports/api/engine/computeCreature.js';
import computeCreature from '/imports/api/engine/computeCreature';
/**
* Recomputes all ancestor creatures of this property
* @deprecated
*/
export default function recomputeCreaturesByProperty(property){
for (let ref of property.ancestors){
if (ref.collection === 'creatures') {
computeCreature.call(ref.id);
}
}
export default function recomputeCreaturesByProperty(property) {
computeCreature.call(property.root.id);
}

View File

@@ -1,8 +1,8 @@
import SimpleSchema from 'simpl-schema';
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js'
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema'
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema';
import SharingSchema from '/imports/api/sharing/SharingSchema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
//set up the collection for creatures
let Creatures = new Mongo.Collection('creatures');
@@ -195,4 +195,4 @@ Creatures.attachSchema(CreatureSchema);
export default Creatures;
export { CreatureSchema };
import '/imports/api/engine/actions/doAction.js';
import '/imports/api/engine/actions/doAction';

View File

@@ -1,29 +1,29 @@
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import Creatures from '/imports/api/creature/creatures/Creatures';
import {
assertEditPermission as editPermission,
assertViewPermission as viewPermission,
assertOwnership as ownership
} from '/imports/api/sharing/sharingPermissions.js';
} from '/imports/api/sharing/sharingPermissions';
function getCreature(creature, fields){
if (typeof creature === 'string'){
return Creatures.findOne(creature, {fields});
function getCreature(creature, fields) {
if (typeof creature === 'string') {
return Creatures.findOne(creature, { fields });
} else {
return creature;
}
}
export function assertOwnership(creature, userId){
creature = getCreature(creature, {owner: 1});
export function assertOwnership(creature, userId) {
creature = getCreature(creature, { owner: 1 });
ownership(creature, userId);
}
export function assertEditPermission(creature, userId) {
creature = getCreature(creature, {owner: 1, writers: 1});
creature = getCreature(creature, { owner: 1, writers: 1 });
editPermission(creature, userId);
}
export function assertViewPermission(creature, userId) {
creature = getCreature(creature, {owner: 1, readers:1, writers: 1, public: 1});
creature = getCreature(creature, { owner: 1, readers: 1, writers: 1, public: 1 });
viewPermission(creature, userId);
}

View File

@@ -1,47 +1,50 @@
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
export default function defaultCharacterProperties(creatureId){
export default function defaultCharacterProperties(creatureId) {
if (!creatureId) throw 'creatureId is required';
const creatureRef = {collection: 'creatures', id: creatureId};
const creatureRef = { collection: 'creatures', id: creatureId };
let randomSrc = DDP.randomStream('defaultProperties');
const inventoryId = randomSrc.id();
const inventoryRef = {collection: 'creatureProperties', id: inventoryId};
return [
{
type: 'propertySlot',
name: 'Ruleset',
description: {text: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base ruleset, your sheet will be empty.'},
description: { text: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base ruleset, your sheet will be empty.' },
slotTags: ['base'],
tags: [],
quantityExpected: {calculation: '1'},
quantityExpected: { calculation: '1' },
hideWhenFull: true,
spaceLeft: 1,
totalFilled: 0,
order: 0,
parent: creatureRef,
ancestors: [creatureRef],
left: 1,
right: 2,
parentId: creatureId,
root: creatureRef,
}, {
_id: inventoryId,
type: 'folder',
name: 'Inventory',
tags: [BUILT_IN_TAGS.inventory],
order: 1,
parent: creatureRef,
ancestors: [creatureRef],
left: 3,
right: 8,
parentId: creatureId,
root: creatureRef,
}, {
type: 'folder',
name: 'Equipment',
tags: [BUILT_IN_TAGS.equipment],
order: 2,
parent: inventoryRef,
ancestors: [creatureRef, inventoryRef],
left: 4,
right: 5,
parentId: inventoryId,
root: creatureRef,
}, {
type: 'folder',
name: 'Carried',
tags: [BUILT_IN_TAGS.carried],
order: 3,
parent: inventoryRef,
ancestors: [creatureRef, inventoryRef],
left: 6,
right: 7,
parent: inventoryId,
root: creatureRef,
},
];
}

View File

@@ -1,5 +1,5 @@
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import { getUserTier } from '/imports/api/users/patreon/tiers';
import Creatures from '/imports/api/creature/creatures/Creatures';
export default function assertHasCharactersSlots(userId) {
if (characterSlotsRemaining(userId) <= 0) {

View File

@@ -1,9 +1,9 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import SimpleSchema from 'simpl-schema';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin';
const changeAllowedLibraries = new ValidatedMethod({
name: 'creatures.changeAllowedLibraries',

View File

@@ -1,5 +1,5 @@
import '/imports/api/creature/creatures/methods/insertCreature.js';
import '/imports/api/creature/creatures/methods/removeCreature.js';
import '/imports/api/creature/creatures/methods/restCreature.js';
import '/imports/api/creature/creatures/methods/updateCreature.js';
import '/imports/api/creature/creatures/methods/changeAllowedLibraries.js';
import '/imports/api/creature/creatures/methods/insertCreature';
import '/imports/api/creature/creatures/methods/removeCreature';
import '/imports/api/creature/creatures/methods/restCreature';
import '/imports/api/creature/creatures/methods/updateCreature';
import '/imports/api/creature/creatures/methods/changeAllowedLibraries';

View File

@@ -1,15 +1,15 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
import Creatures, { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import defaultCharacterProperties from '/imports/api/creature/creatures/defaultCharacterProperties.js';
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js';
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js';
import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { insertExperienceForCreature } from '/imports/api/creature/experience/Experiences.js';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin';
import Creatures, { CreatureSchema } from '/imports/api/creature/creatures/Creatures';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import defaultCharacterProperties from '/imports/api/creature/creatures/defaultCharacterProperties';
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode';
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots';
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter';
import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds';
import LibraryNodes from '/imports/api/library/LibraryNodes';
import { insertExperienceForCreature } from '/imports/api/creature/experience/Experiences';
import SimpleSchema from 'simpl-schema';
const insertCreature = new ValidatedMethod({

View File

@@ -1,18 +1,19 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
import Experiences from '/imports/api/creature/experience/Experiences.js';
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions';
import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
import Experiences from '/imports/api/creature/experience/Experiences';
import { getFilter } from '/imports/api/parenting/parentingFunctions';
function removeRelatedDocuments(creatureId){
CreatureVariables.remove({_creatureId: creatureId});
CreatureProperties.remove({'ancestors.id': creatureId});
CreatureLogs.remove({creatureId});
Experiences.remove({creatureId});
function removeRelatedDocuments(creatureId) {
CreatureVariables.remove({ _creatureId: creatureId });
CreatureProperties.remove(getFilter.descendantsOfRoot(creatureId));
CreatureLogs.remove({ creatureId });
Experiences.remove({ creatureId });
}
const removeCreature = new ValidatedMethod({
@@ -28,14 +29,14 @@ const removeCreature = new ValidatedMethod({
numRequests: 5,
timeInterval: 5000,
},
run({charId}) {
run({ charId }) {
assertOwnership(charId, this.userId)
this.unblock();
removeCreatureWork(charId)
},
});
export function removeCreatureWork(creatureId){
export function removeCreatureWork(creatureId) {
Creatures.remove(creatureId);
removeRelatedDocuments(creatureId);
}

View File

@@ -1,12 +1,13 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
import { union } from 'lodash';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import ActionContext from '/imports/api/engine/actions/ActionContext';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
import { getFilter } from '/imports/api/parenting/parentingFunctions';
const restCreature = new ValidatedMethod({
name: 'creature.methods.rest',
@@ -74,7 +75,7 @@ function doRestWork(restType, actionContext) {
export function resetProperties(creatureId, resetFilter, actionContext) {
// Only apply to active properties
const filter = {
'ancestors.id': creatureId,
...getFilter.descendantsOfRoot(creatureId),
reset: resetFilter,
removed: { $ne: true },
inactive: { $ne: true },
@@ -128,7 +129,7 @@ export function resetProperties(creatureId, resetFilter, actionContext) {
function resetHitDice(creatureId, actionContext) {
let hitDice = CreatureProperties.find({
'ancestors.id': creatureId,
...getFilter.descendantsOfRoot(creatureId),
type: 'attribute',
attributeType: 'hitDice',
removed: { $ne: true },

View File

@@ -1,7 +1,7 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
const updateCreature = new ValidatedMethod({
name: 'creatures.update',

View File

@@ -1,9 +1,9 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
import Creatures from '/imports/api/creature/creatures/Creatures';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
let Experiences = new Mongo.Collection('experiences');

View File

@@ -1,5 +1,5 @@
import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
let ExperienceSchema = new SimpleSchema({
title: {

View File

@@ -1,18 +1,18 @@
import SimpleSchema from 'simpl-schema';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js';
import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
import LogContentSchema from '/imports/api/creature/log/LogContentSchema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { parse, prettifyParseError } from '/imports/parser/parser.js';
import resolve, { toString } from '/imports/parser/resolve.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
import { parse, prettifyParseError } from '/imports/parser/parser';
import resolve, { toString } from '/imports/parser/resolve';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
const PER_CREATURE_LOG_LIMIT = 100;
if (Meteor.isServer) {
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook').sendWebhookAsCreature;
}
let CreatureLogs = new Mongo.Collection('creatureLogs');

View File

@@ -1,7 +1,18 @@
import SimpleSchema from 'simpl-schema';
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema';
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
export interface LogContent {
name?: string
value?: string
inline?: boolean
context?: {
errors: any[]
rolls: any[]
doubleRolls?: boolean
}
}
let LogContentSchema = new SimpleSchema({
// The name of the field, included in discord webhook message

View File

@@ -2,7 +2,7 @@ import {
assertEditPermission,
assertViewPermission,
assertOwnership,
} from '/imports/api/creature/creatures/creaturePermissions.js';
} from '/imports/api/creature/creatures/creaturePermissions';
// Checks if the method has permission to run on the document. If the document
// has a charId, that creature is checked, otherwise if it has an _id and the
@@ -13,36 +13,36 @@ import {
// Because this mixin injects the charId into argument objects that don't
// already contain it, it should always come last in the mixin list, so that it
// the outermost wrapper of the run function
export default function creaturePermissionMixin(methodOptions){
export default function creaturePermissionMixin(methodOptions) {
let assertPermission;
if (methodOptions.permission === 'owner'){
if (methodOptions.permission === 'owner') {
assertPermission = assertOwnership;
} else if (methodOptions.permission === 'edit'){
} else if (methodOptions.permission === 'edit') {
assertPermission = assertEditPermission;
} else if (methodOptions.permission === 'view'){
} else if (methodOptions.permission === 'view') {
assertPermission = assertViewPermission;
} else {
throw "`permission` missing in method options";
}
let getCharId;
if (methodOptions.getCharId){
if (methodOptions.getCharId) {
getCharId = methodOptions.getCharId;
} else if (methodOptions.collection) {
getCharId = function({_id}){
getCharId = function ({ _id }) {
return methodOptions.collection.findOne(_id, {
fields: {charId: 1}
fields: { charId: 1 }
}).charId;
};
} else {
getCharId = function(){
getCharId = function () {
throw "`getCharId` or `collection` missing in method options," +
" or {charId} missing in call";
};
}
let runFunc = methodOptions.run;
methodOptions.run = function(doc, ...rest){
methodOptions.run = function (doc, ...rest) {
// Store the charId on the doc for other mixins if it had to be fetched
doc.charId = doc.charId || getCharId.apply(this, arguments);
assertPermission(doc.charId, this.userId);

View File

@@ -1,59 +0,0 @@
import {
updateChildren,
updateDescendants,
} from '/imports/api/parenting/parenting.js';
import { inheritedFields } from '/imports/api/parenting/ChildSchema.js';
import MONGO_OPERATORS from '/imports/constants/MONGO_OPERATORS.js';
// This mixin can be safely applied to all update methods which are validated
// with the updateSchemaMixin. It will propagate updates to fields which
// are inherited and normalised on the parent or ancestor docs
// It should have neglible performance impact for updates that aren't inherited
function propagateInheritanceUpdate({_id, update}){
let childModifier = {};
let descendantModifier = {};
// For each operator
for (let operator of MONGO_OPERATORS){
// If the operator is in the update, for each field
if (update[operator]) for (let field in update[operator]){
// Get the top level field that was actually modified
const indexOfDot = field.indexOf('.');
let modifiedField;
if (indexOfDot !== -1) {
modifiedField = field.substring(0, indexOfDot);
} else {
modifiedField = field;
}
// If that field is updated and inherited
if (inheritedFields.has(modifiedField)){
// Perform the same update on the descendants
if (!childModifier[operator]) childModifier[operator] = {};
if (!descendantModifier[operator]) descendantModifier[operator] = {};
childModifier[operator][`parent.${field}`] = update[operator][field];
descendantModifier[operator][`ancestors.$.${field}`] = update[operator][field];
}
}
}
// Update the parent object of its children
updateChildren({
parentId: _id,
modifier: childModifier,
});
// Update the ancestors object of its descendants
updateDescendants({
ancestorId: _id,
modifier: descendantModifier,
});
}
export default function propagateInheritanceUpdateMixin(methodOptions){
let runFunc = methodOptions.run;
methodOptions.run = function({_id, update}){
const result = runFunc.apply(this, arguments);
propagateInheritanceUpdate({_id, update});
return result;
};
return methodOptions;
}

View File

@@ -1,8 +1,8 @@
import computeCreature from '/imports/api/engine/computeCreature.js';
import computeCreature from '/imports/api/engine/computeCreature';
export default function recomputeCreatureMixin(methodOptions){
export default function recomputeCreatureMixin(methodOptions) {
let runFunc = methodOptions.run;
methodOptions.run = function({charId}){
methodOptions.run = function ({ charId }) {
const result = runFunc.apply(this, arguments);
if (
methodOptions.skipRecompute &&

View File

@@ -1,27 +0,0 @@
import SimpleSchema from 'simpl-schema';
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
export function setDocToLastMixin(methodOptions){
// Make sure the doc has a charId
// This mixin should come before simpleSchemaMixin so that it can extend the
// schema before it is turned into a validate function
if (methodOptions.validate){
throw new Meteor.Error(`setDocToLastMixin should come before simpleSchemaMixin`);
}
methodOptions.schema = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).extend(methodOptions.schema);
let collection = methodOptions.collection;
if (!collection){
throw new Meteor.Error("`collection` required in method options for setDocToLastMixin");
}
let runFunc = methodOptions.run;
methodOptions.run = function(doc){
setDocToLastOrder({collection, doc});
return runFunc.apply(this, arguments);
};
return methodOptions;
}