/** * A collection of world-level Document objects with a singleton instance per primary Document type. * Each primary Document type has an associated subclass of WorldCollection which contains them. * @extends {DocumentCollection} * @abstract * @see {Game#collections} * * @param {object[]} data An array of data objects from which to create Document instances */ class WorldCollection extends DirectoryCollectionMixin(DocumentCollection) { /* -------------------------------------------- */ /* Collection Properties */ /* -------------------------------------------- */ /** * Reference the set of Folders which contain documents in this collection * @type {Collection} */ get folders() { return game.folders.reduce((collection, folder) => { if (folder.type === this.documentName) { collection.set(folder.id, folder); } return collection; }, new foundry.utils.Collection()); } /** * Return a reference to the SidebarDirectory application for this WorldCollection. * @type {DocumentDirectory} */ get directory() { const doc = getDocumentClass(this.constructor.documentName); return ui[doc.metadata.collection]; } /* -------------------------------------------- */ /** * Return a reference to the singleton instance of this WorldCollection, or null if it has not yet been created. * @type {WorldCollection} */ static get instance() { return game.collections.get(this.documentName); } /* -------------------------------------------- */ /* Collection Methods */ /* -------------------------------------------- */ /** @override */ _getVisibleTreeContents(entry) { return this.contents.filter(c => c.visible); } /* -------------------------------------------- */ /** * Import a Document from a Compendium collection, adding it to the current World. * @param {CompendiumCollection} pack The CompendiumCollection instance from which to import * @param {string} id The ID of the compendium entry to import * @param {object} [updateData] Optional additional data used to modify the imported Document before it is created * @param {object} [options] Optional arguments passed to the {@link WorldCollection#fromCompendium} and * {@link Document.create} methods * @returns {Promise} The imported Document instance */ async importFromCompendium(pack, id, updateData={}, options={}) { const cls = this.documentClass; if (pack.documentName !== cls.documentName) { throw new Error(`The ${pack.documentName} Document type provided by Compendium ${pack.collection} is incorrect for this Collection`); } // Prepare the source data from which to create the Document const document = await pack.getDocument(id); const sourceData = this.fromCompendium(document, options); const createData = foundry.utils.mergeObject(sourceData, updateData); // Create the Document console.log(`${vtt} | Importing ${cls.documentName} ${document.name} from ${pack.collection}`); this.directory.activate(); options.fromCompendium = true; return this.documentClass.create(createData, options); } /* -------------------------------------------- */ /** * @typedef {object} FromCompendiumOptions * @property {boolean} [options.clearFolder=false] Clear the currently assigned folder. * @property {boolean} [options.clearSort=true] Clear the current sort order. * @property {boolean} [options.clearOwnership=true] Clear Document ownership. * @property {boolean} [options.keepId=false] Retain the Document ID from the source Compendium. */ /** * Apply data transformations when importing a Document from a Compendium pack * @param {Document|object} document The source Document, or a plain data object * @param {FromCompendiumOptions} [options] Additional options which modify how the document is imported * @returns {object} The processed data ready for world Document creation */ fromCompendium(document, {clearFolder=false, clearSort=true, clearOwnership=true, keepId=false, ...rest}={}) { /** @deprecated since v12 */ if ( "addFlags" in rest ) { foundry.utils.logCompatibilityWarning("The addFlags option for WorldCompendium#fromCompendium has been removed. ", { since: 12, until: 14 }); } // Prepare the data structure let data = document; if (document instanceof foundry.abstract.Document) { data = document.toObject(); if ( document.pack ) foundry.utils.setProperty(data, "_stats.compendiumSource", document.uuid); } // Eliminate certain fields if ( !keepId ) delete data._id; if ( clearFolder ) delete data.folder; if ( clearSort ) delete data.sort; if ( clearOwnership && ("ownership" in data) ) { data.ownership = { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE, [game.user.id]: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }; } return data; } /* -------------------------------------------- */ /* Sheet Registration Methods */ /* -------------------------------------------- */ /** * Register a Document sheet class as a candidate which can be used to display Documents of a given type. * See {@link DocumentSheetConfig.registerSheet} for details. * @static * @param {Array<*>} args Arguments forwarded to the DocumentSheetConfig.registerSheet method * * @example Register a new ActorSheet subclass for use with certain Actor types. * ```js * Actors.registerSheet("dnd5e", ActorSheet5eCharacter, { types: ["character], makeDefault: true }); * ``` */ static registerSheet(...args) { DocumentSheetConfig.registerSheet(getDocumentClass(this.documentName), ...args); } /* -------------------------------------------- */ /** * Unregister a Document sheet class, removing it from the list of available sheet Applications to use. * See {@link DocumentSheetConfig.unregisterSheet} for detauls. * @static * @param {Array<*>} args Arguments forwarded to the DocumentSheetConfig.unregisterSheet method * * @example Deregister the default ActorSheet subclass to replace it with others. * ```js * Actors.unregisterSheet("core", ActorSheet); * ``` */ static unregisterSheet(...args) { DocumentSheetConfig.unregisterSheet(getDocumentClass(this.documentName), ...args); } /* -------------------------------------------- */ /** * Return an array of currently registered sheet classes for this Document type. * @static * @type {DocumentSheet[]} */ static get registeredSheets() { const sheets = new Set(); for ( let t of Object.values(CONFIG[this.documentName].sheetClasses) ) { for ( let s of Object.values(t) ) { sheets.add(s.cls); } } return Array.from(sheets); } }