mirror of https://github.com/wekan/wekan
The Open Source kanban (built with Meteor). Keep variable/table/field names camelCase. For translations, only add Pull Request changes to wekan/i18n/en.i18n.json , other translations are done at https://transifex.com/wekan/wekan only.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
6.9 KiB
171 lines
6.9 KiB
/**
|
|
*
|
|
* @constructor
|
|
* @param {string} name A name for the collection
|
|
* @param {Object} options
|
|
* @param {FS.StorageAdapter[]} options.stores An array of stores in which files should be saved. At least one is required.
|
|
* @param {Object} [options.filter] Filter definitions
|
|
* @param {Number} [options.chunkSize=2MB] Override the chunk size in bytes for uploads
|
|
* @param {Function} [options.uploader] A function to pass FS.File instances after inserting, which will begin uploading them. By default, `FS.HTTP.uploadQueue.uploadFile` is used if the `cfs-upload-http` package is present, or `FS.DDP.uploadQueue.uploadFile` is used if the `cfs-upload-ddp` package is present. You can override with your own, or set to `null` to prevent automatic uploading.
|
|
* @returns {undefined}
|
|
*/
|
|
FS.Collection = function(name, options) {
|
|
var self = this;
|
|
|
|
self.storesLookup = {};
|
|
|
|
self.primaryStore = {};
|
|
|
|
self.options = {
|
|
filter: null, //optional
|
|
stores: [], //required
|
|
chunkSize: null
|
|
};
|
|
|
|
// Define a default uploader based on which upload packages are present,
|
|
// preferring HTTP. You may override with your own function or
|
|
// set to null to skip automatic uploading of data after file insert/update.
|
|
if (FS.HTTP && FS.HTTP.uploadQueue) {
|
|
self.options.uploader = FS.HTTP.uploadQueue.uploadFile;
|
|
} else if (FS.DDP && FS.DDP.uploadQueue) {
|
|
self.options.uploader = FS.DDP.uploadQueue.uploadFile;
|
|
}
|
|
|
|
// Extend and overwrite options
|
|
FS.Utility.extend(self.options, options || {});
|
|
|
|
// Set the FS.Collection name
|
|
self.name = name;
|
|
|
|
// Make sure at least one store has been supplied.
|
|
// Usually the stores aren't used on the client, but we need them defined
|
|
// so that we can access their names and use the first one as the default.
|
|
if (FS.Utility.isEmpty(self.options.stores)) {
|
|
throw new Error("You must specify at least one store. Please consult the documentation.");
|
|
}
|
|
|
|
FS.Utility.each(self.options.stores, function(store, i) {
|
|
// Set the primary store
|
|
if (i === 0) {
|
|
self.primaryStore = store;
|
|
}
|
|
|
|
// Check for duplicate naming
|
|
if (typeof self.storesLookup[store.name] !== 'undefined') {
|
|
throw new Error('FS.Collection store names must be uniq, duplicate found: ' + store.name);
|
|
}
|
|
|
|
// Set the lookup
|
|
self.storesLookup[store.name] = store;
|
|
|
|
if (Meteor.isServer) {
|
|
|
|
// Emit events based on store events
|
|
store.on('stored', function (storeName, fileObj) {
|
|
// This is weird, but currently there is a bug where each store will emit the
|
|
// events for all other stores, too, so we need to make sure that this event
|
|
// is truly for this store.
|
|
if (storeName !== store.name)
|
|
return;
|
|
// When a file is successfully stored into the store, we emit a "stored" event on the FS.Collection only if the file belongs to this collection
|
|
if (fileObj.collectionName === name) {
|
|
var emitted = self.emit('stored', fileObj, store.name);
|
|
if (FS.debug && !emitted) {
|
|
console.log(fileObj.name({store: store.name}) + ' was successfully saved to the ' + store.name + ' store. You are seeing this informational message because you enabled debugging and you have not defined any listeners for the "stored" event on the ' + name + ' collection.');
|
|
}
|
|
}
|
|
fileObj.emit('stored', store.name);
|
|
});
|
|
|
|
store.on('error', function (storeName, error, fileObj) {
|
|
// This is weird, but currently there is a bug where each store will emit the
|
|
// events for all other stores, too, so we need to make sure that this event
|
|
// is truly for this store.
|
|
if (storeName !== store.name)
|
|
return;
|
|
// When a file has an error while being stored into the temp store, we emit an "error" event on the FS.Collection only if the file belongs to this collection
|
|
if (fileObj.collectionName === name) {
|
|
error = new Error('Error storing file to the ' + store.name + ' store: ' + error.message);
|
|
var emitted = self.emit('error', error, fileObj, store.name);
|
|
if (FS.debug && !emitted) {
|
|
console.log(error.message);
|
|
}
|
|
}
|
|
fileObj.emit('error', store.name);
|
|
});
|
|
|
|
}
|
|
});
|
|
|
|
var _filesOptions = {
|
|
transform: function(doc) {
|
|
// This should keep the filerecord in the file object updated in reactive
|
|
// context
|
|
var result = new FS.File(doc, true);
|
|
result.collectionName = name;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
if(self.options.idGeneration) _filesOptions.idGeneration = self.options.idGeneration;
|
|
|
|
// Enable specifying an alternate driver, to change where the filerecord is stored
|
|
// Drivers can be created with MongoInternals.RemoteCollectionDriver()
|
|
if(self.options._driver){
|
|
_filesOptions._driver = self.options._driver;
|
|
}
|
|
|
|
// Create the 'cfs.' ++ ".filerecord" and use fsFile
|
|
var collectionName = 'cfs.' + name + '.filerecord';
|
|
self.files = new Mongo.Collection(collectionName, _filesOptions);
|
|
|
|
// For storing custom allow/deny functions
|
|
self._validators = {
|
|
download: {allow: [], deny: []}
|
|
};
|
|
|
|
// Set up filters
|
|
// XXX Should we deprecate the filter option now that this is done with a separate pkg, or just keep it?
|
|
if (self.filters) {
|
|
self.filters(self.options.filter);
|
|
}
|
|
|
|
// Save the collection reference (we want it without the 'cfs.' prefix and '.filerecord' suffix)
|
|
FS._collections[name] = this;
|
|
|
|
// Set up observers
|
|
Meteor.isServer && FS.FileWorker && FS.FileWorker.observe(this);
|
|
|
|
// Emit "removed" event on collection
|
|
self.files.find().observe({
|
|
removed: function(fileObj) {
|
|
self.emit('removed', fileObj);
|
|
}
|
|
});
|
|
|
|
// Emit events based on TempStore events
|
|
if (FS.TempStore) {
|
|
FS.TempStore.on('stored', function (fileObj, result) {
|
|
// When a file is successfully stored into the temp store, we emit an "uploaded" event on the FS.Collection only if the file belongs to this collection
|
|
if (fileObj.collectionName === name) {
|
|
var emitted = self.emit('uploaded', fileObj);
|
|
if (FS.debug && !emitted) {
|
|
console.log(fileObj.name() + ' was successfully uploaded. You are seeing this informational message because you enabled debugging and you have not defined any listeners for the "uploaded" event on the ' + name + ' collection.');
|
|
}
|
|
}
|
|
});
|
|
|
|
FS.TempStore.on('error', function (error, fileObj) {
|
|
// When a file has an error while being stored into the temp store, we emit an "error" event on the FS.Collection only if the file belongs to this collection
|
|
if (fileObj.collectionName === name) {
|
|
self.emit('error', new Error('Error storing uploaded file to TempStore: ' + error.message), fileObj);
|
|
}
|
|
});
|
|
} else if (Meteor.isServer) {
|
|
throw new Error("FS.Collection constructor: FS.TempStore must be defined before constructing any FS.Collections.")
|
|
}
|
|
|
|
};
|
|
|
|
// An FS.Collection can emit events
|
|
FS.Collection.prototype = new EventEmitter();
|
|
|