| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 | /** * * @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 eventsFS.Collection.prototype = new EventEmitter();
 |