api.common.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /** @method FS.Collection.prototype.insert Insert `File` or `FS.File` or remote URL into collection
  2. * @public
  3. * @param {File|Blob|Buffer|ArrayBuffer|Uint8Array|String} fileRef File, FS.File, or other data to insert
  4. * @param {function} [callback] Callback `function(error, fileObj)`
  5. * @returns {FS.File|undefined} The `file object`
  6. * [Meteor docs](http://docs.meteor.com/#insert)
  7. */
  8. FS.Collection.prototype.insert = function(fileRef, callback) {
  9. var self = this;
  10. if (Meteor.isClient && !callback) {
  11. callback = FS.Utility.defaultCallback;
  12. }
  13. // XXX:
  14. // We should out factor beginStorage to FS.File.beginStorage
  15. // the client side storage adapters should be the one providing
  16. // the upload either via http/ddp or direct upload
  17. // Could be cool to have a streaming api on the client side
  18. // having a createReadStream etc. on the client too...
  19. function beginStorage(fileObj) {
  20. // If on client, begin uploading the data
  21. if (Meteor.isClient) {
  22. self.options.uploader && self.options.uploader(fileObj);
  23. }
  24. // If on the server, save the binary to a single chunk temp file,
  25. // so that it is available when FileWorker calls saveCopies.
  26. // This will also trigger file handling from collection observes.
  27. else if (Meteor.isServer) {
  28. fileObj.createReadStream().pipe(FS.TempStore.createWriteStream(fileObj));
  29. }
  30. }
  31. // XXX: would be great if this function could be simplyfied - if even possible?
  32. function checkAndInsert(fileObj) {
  33. // Check filters. This is called in deny functions, too, but we call here to catch
  34. // server inserts and to catch client inserts early, allowing us to call `onInvalid` on
  35. // the client and save a trip to the server.
  36. if (!self.allowsFile(fileObj)) {
  37. return FS.Utility.handleError(callback, 'FS.Collection insert: file does not pass collection filters');
  38. }
  39. // Set collection name
  40. fileObj.collectionName = self.name;
  41. // Insert the file into db
  42. // We call cloneFileRecord as an easy way of extracting the properties
  43. // that need saving.
  44. if (callback) {
  45. fileObj._id = self.files.insert(FS.Utility.cloneFileRecord(fileObj), function(err, id) {
  46. if (err) {
  47. if (fileObj._id) {
  48. delete fileObj._id;
  49. }
  50. } else {
  51. // Set _id, just to be safe, since this could be before or after the insert method returns
  52. fileObj._id = id;
  53. // Pass to uploader or stream data to the temp store
  54. beginStorage(fileObj);
  55. }
  56. callback(err, err ? void 0 : fileObj);
  57. });
  58. } else {
  59. fileObj._id = self.files.insert(FS.Utility.cloneFileRecord(fileObj));
  60. // Pass to uploader or stream data to the temp store
  61. beginStorage(fileObj);
  62. }
  63. return fileObj;
  64. }
  65. // Parse, adjust fileRef
  66. if (fileRef instanceof FS.File) {
  67. return checkAndInsert(fileRef);
  68. } else {
  69. // For convenience, allow File, Blob, Buffer, data URI, filepath, URL, etc. to be passed as first arg,
  70. // and we will attach that to a new fileobj for them
  71. var fileObj = new FS.File(fileRef);
  72. if (callback) {
  73. fileObj.attachData(fileRef, function attachDataCallback(error) {
  74. if (error) {
  75. callback(error);
  76. } else {
  77. checkAndInsert(fileObj);
  78. }
  79. });
  80. } else {
  81. // We ensure there's a callback on the client, so if there isn't one at this point,
  82. // we must be on the server expecting synchronous behavior.
  83. fileObj.attachData(fileRef);
  84. checkAndInsert(fileObj);
  85. }
  86. return fileObj;
  87. }
  88. };
  89. /** @method FS.Collection.prototype.update Update the file record
  90. * @public
  91. * @param {FS.File|object} selector
  92. * @param {object} modifier
  93. * @param {object} [options]
  94. * @param {function} [callback]
  95. * [Meteor docs](http://docs.meteor.com/#update)
  96. */
  97. FS.Collection.prototype.update = function(selector, modifier, options, callback) {
  98. var self = this;
  99. if (selector instanceof FS.File) {
  100. // Make sure the file belongs to this FS.Collection
  101. if (selector.collectionName === self.files._name) {
  102. return selector.update(modifier, options, callback);
  103. } else {
  104. // Tried to save a file in the wrong FS.Collection
  105. throw new Error('FS.Collection cannot update file belongs to: "' + selector.collectionName + '" not: "' + self.files._name + '"');
  106. }
  107. }
  108. return self.files.update(selector, modifier, options, callback);
  109. };
  110. /** @method FS.Collection.prototype.remove Remove the file from the collection
  111. * @public
  112. * @param {FS.File|object} selector
  113. * @param {Function} [callback]
  114. * [Meteor docs](http://docs.meteor.com/#remove)
  115. */
  116. FS.Collection.prototype.remove = function(selector, callback) {
  117. var self = this;
  118. if (selector instanceof FS.File) {
  119. // Make sure the file belongs to this FS.Collection
  120. if (selector.collectionName === self.files._name) {
  121. return selector.remove(callback);
  122. } else {
  123. // Tried to remove a file from the wrong FS.Collection
  124. throw new Error('FS.Collection cannot remove file belongs to: "' + selector.collectionName + '" not: "' + self.files._name + '"');
  125. }
  126. }
  127. //doesn't work correctly on the client without a callback
  128. callback = callback || FS.Utility.defaultCallback;
  129. return self.files.remove(selector, callback);
  130. };
  131. /** @method FS.Collection.prototype.findOne
  132. * @public
  133. * @param {[selector](http://docs.meteor.com/#selectors)} selector
  134. * [Meteor docs](http://docs.meteor.com/#findone)
  135. * Example:
  136. ```js
  137. var images = new FS.Collection( ... );
  138. // Get the file object
  139. var fo = images.findOne({ _id: 'NpnskCt6ippN6CgD8' });
  140. ```
  141. */
  142. // Call findOne on files collection
  143. FS.Collection.prototype.findOne = function(selector) {
  144. var self = this;
  145. return self.files.findOne.apply(self.files, arguments);
  146. };
  147. /** @method FS.Collection.prototype.find
  148. * @public
  149. * @param {[selector](http://docs.meteor.com/#selectors)} selector
  150. * [Meteor docs](http://docs.meteor.com/#find)
  151. * Example:
  152. ```js
  153. var images = new FS.Collection( ... );
  154. // Get the all file objects
  155. var files = images.find({ _id: 'NpnskCt6ippN6CgD8' }).fetch();
  156. ```
  157. */
  158. FS.Collection.prototype.find = function(selector) {
  159. var self = this;
  160. return self.files.find.apply(self.files, arguments);
  161. };
  162. /** @method FS.Collection.prototype.allow
  163. * @public
  164. * @param {object} options
  165. * @param {function} options.download Function that checks if the file contents may be downloaded
  166. * @param {function} options.insert
  167. * @param {function} options.update
  168. * @param {function} options.remove Functions that look at a proposed modification to the database and return true if it should be allowed
  169. * @param {[string]} [options.fetch] Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your update and remove functions
  170. * [Meteor docs](http://docs.meteor.com/#allow)
  171. * Example:
  172. ```js
  173. var images = new FS.Collection( ... );
  174. // Get the all file objects
  175. var files = images.allow({
  176. insert: function(userId, doc) { return true; },
  177. update: function(userId, doc, fields, modifier) { return true; },
  178. remove: function(userId, doc) { return true; },
  179. download: function(userId, fileObj) { return true; },
  180. });
  181. ```
  182. */
  183. FS.Collection.prototype.allow = function(options) {
  184. var self = this;
  185. // Pull out the custom "download" functions
  186. if (options.download) {
  187. if (!(options.download instanceof Function)) {
  188. throw new Error("allow: Value for `download` must be a function");
  189. }
  190. self._validators.download.allow.push(options.download);
  191. delete options.download;
  192. }
  193. return self.files.allow.call(self.files, options);
  194. };
  195. /** @method FS.Collection.prototype.deny
  196. * @public
  197. * @param {object} options
  198. * @param {function} options.download Function that checks if the file contents may be downloaded
  199. * @param {function} options.insert
  200. * @param {function} options.update
  201. * @param {function} options.remove Functions that look at a proposed modification to the database and return true if it should be denyed
  202. * @param {[string]} [options.fetch] Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your update and remove functions
  203. * [Meteor docs](http://docs.meteor.com/#deny)
  204. * Example:
  205. ```js
  206. var images = new FS.Collection( ... );
  207. // Get the all file objects
  208. var files = images.deny({
  209. insert: function(userId, doc) { return true; },
  210. update: function(userId, doc, fields, modifier) { return true; },
  211. remove: function(userId, doc) { return true; },
  212. download: function(userId, fileObj) { return true; },
  213. });
  214. ```
  215. */
  216. FS.Collection.prototype.deny = function(options) {
  217. var self = this;
  218. // Pull out the custom "download" functions
  219. if (options.download) {
  220. if (!(options.download instanceof Function)) {
  221. throw new Error("deny: Value for `download` must be a function");
  222. }
  223. self._validators.download.deny.push(options.download);
  224. delete options.download;
  225. }
  226. return self.files.deny.call(self.files, options);
  227. };
  228. // TODO: Upsert?
  229. /**
  230. * We provide a default implementation that doesn't do anything.
  231. * Can be changed by user or packages, such as the default cfs-collection-filters pkg.
  232. * @param {FS.File} fileObj File object
  233. * @return {Boolean} Should we allow insertion of this file?
  234. */
  235. FS.Collection.prototype.allowsFile = function fsColAllowsFile(fileObj) {
  236. return true;
  237. };