mirror of
https://github.com/wekan/wekan.git
synced 2025-12-17 07:50:12 +01:00
261 lines
9.2 KiB
JavaScript
261 lines
9.2 KiB
JavaScript
|
|
/** @method FS.Collection.prototype.insert Insert `File` or `FS.File` or remote URL into collection
|
||
|
|
* @public
|
||
|
|
* @param {File|Blob|Buffer|ArrayBuffer|Uint8Array|String} fileRef File, FS.File, or other data to insert
|
||
|
|
* @param {function} [callback] Callback `function(error, fileObj)`
|
||
|
|
* @returns {FS.File|undefined} The `file object`
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#insert)
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.insert = function(fileRef, callback) {
|
||
|
|
var self = this;
|
||
|
|
|
||
|
|
if (Meteor.isClient && !callback) {
|
||
|
|
callback = FS.Utility.defaultCallback;
|
||
|
|
}
|
||
|
|
|
||
|
|
// XXX:
|
||
|
|
// We should out factor beginStorage to FS.File.beginStorage
|
||
|
|
// the client side storage adapters should be the one providing
|
||
|
|
// the upload either via http/ddp or direct upload
|
||
|
|
// Could be cool to have a streaming api on the client side
|
||
|
|
// having a createReadStream etc. on the client too...
|
||
|
|
function beginStorage(fileObj) {
|
||
|
|
|
||
|
|
// If on client, begin uploading the data
|
||
|
|
if (Meteor.isClient) {
|
||
|
|
self.options.uploader && self.options.uploader(fileObj);
|
||
|
|
}
|
||
|
|
|
||
|
|
// If on the server, save the binary to a single chunk temp file,
|
||
|
|
// so that it is available when FileWorker calls saveCopies.
|
||
|
|
// This will also trigger file handling from collection observes.
|
||
|
|
else if (Meteor.isServer) {
|
||
|
|
fileObj.createReadStream().pipe(FS.TempStore.createWriteStream(fileObj));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// XXX: would be great if this function could be simplyfied - if even possible?
|
||
|
|
function checkAndInsert(fileObj) {
|
||
|
|
// Check filters. This is called in deny functions, too, but we call here to catch
|
||
|
|
// server inserts and to catch client inserts early, allowing us to call `onInvalid` on
|
||
|
|
// the client and save a trip to the server.
|
||
|
|
if (!self.allowsFile(fileObj)) {
|
||
|
|
return FS.Utility.handleError(callback, 'FS.Collection insert: file does not pass collection filters');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set collection name
|
||
|
|
fileObj.collectionName = self.name;
|
||
|
|
|
||
|
|
// Insert the file into db
|
||
|
|
// We call cloneFileRecord as an easy way of extracting the properties
|
||
|
|
// that need saving.
|
||
|
|
if (callback) {
|
||
|
|
fileObj._id = self.files.insert(FS.Utility.cloneFileRecord(fileObj), function(err, id) {
|
||
|
|
if (err) {
|
||
|
|
if (fileObj._id) {
|
||
|
|
delete fileObj._id;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Set _id, just to be safe, since this could be before or after the insert method returns
|
||
|
|
fileObj._id = id;
|
||
|
|
// Pass to uploader or stream data to the temp store
|
||
|
|
beginStorage(fileObj);
|
||
|
|
}
|
||
|
|
callback(err, err ? void 0 : fileObj);
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
fileObj._id = self.files.insert(FS.Utility.cloneFileRecord(fileObj));
|
||
|
|
// Pass to uploader or stream data to the temp store
|
||
|
|
beginStorage(fileObj);
|
||
|
|
}
|
||
|
|
return fileObj;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parse, adjust fileRef
|
||
|
|
if (fileRef instanceof FS.File) {
|
||
|
|
return checkAndInsert(fileRef);
|
||
|
|
} else {
|
||
|
|
// For convenience, allow File, Blob, Buffer, data URI, filepath, URL, etc. to be passed as first arg,
|
||
|
|
// and we will attach that to a new fileobj for them
|
||
|
|
var fileObj = new FS.File(fileRef);
|
||
|
|
if (callback) {
|
||
|
|
fileObj.attachData(fileRef, function attachDataCallback(error) {
|
||
|
|
if (error) {
|
||
|
|
callback(error);
|
||
|
|
} else {
|
||
|
|
checkAndInsert(fileObj);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
// We ensure there's a callback on the client, so if there isn't one at this point,
|
||
|
|
// we must be on the server expecting synchronous behavior.
|
||
|
|
fileObj.attachData(fileRef);
|
||
|
|
checkAndInsert(fileObj);
|
||
|
|
}
|
||
|
|
return fileObj;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @method FS.Collection.prototype.update Update the file record
|
||
|
|
* @public
|
||
|
|
* @param {FS.File|object} selector
|
||
|
|
* @param {object} modifier
|
||
|
|
* @param {object} [options]
|
||
|
|
* @param {function} [callback]
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#update)
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.update = function(selector, modifier, options, callback) {
|
||
|
|
var self = this;
|
||
|
|
if (selector instanceof FS.File) {
|
||
|
|
// Make sure the file belongs to this FS.Collection
|
||
|
|
if (selector.collectionName === self.files._name) {
|
||
|
|
return selector.update(modifier, options, callback);
|
||
|
|
} else {
|
||
|
|
// Tried to save a file in the wrong FS.Collection
|
||
|
|
throw new Error('FS.Collection cannot update file belongs to: "' + selector.collectionName + '" not: "' + self.files._name + '"');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return self.files.update(selector, modifier, options, callback);
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @method FS.Collection.prototype.remove Remove the file from the collection
|
||
|
|
* @public
|
||
|
|
* @param {FS.File|object} selector
|
||
|
|
* @param {Function} [callback]
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#remove)
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.remove = function(selector, callback) {
|
||
|
|
var self = this;
|
||
|
|
if (selector instanceof FS.File) {
|
||
|
|
|
||
|
|
// Make sure the file belongs to this FS.Collection
|
||
|
|
if (selector.collectionName === self.files._name) {
|
||
|
|
return selector.remove(callback);
|
||
|
|
} else {
|
||
|
|
// Tried to remove a file from the wrong FS.Collection
|
||
|
|
throw new Error('FS.Collection cannot remove file belongs to: "' + selector.collectionName + '" not: "' + self.files._name + '"');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//doesn't work correctly on the client without a callback
|
||
|
|
callback = callback || FS.Utility.defaultCallback;
|
||
|
|
return self.files.remove(selector, callback);
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @method FS.Collection.prototype.findOne
|
||
|
|
* @public
|
||
|
|
* @param {[selector](http://docs.meteor.com/#selectors)} selector
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#findone)
|
||
|
|
* Example:
|
||
|
|
```js
|
||
|
|
var images = new FS.Collection( ... );
|
||
|
|
// Get the file object
|
||
|
|
var fo = images.findOne({ _id: 'NpnskCt6ippN6CgD8' });
|
||
|
|
```
|
||
|
|
*/
|
||
|
|
// Call findOne on files collection
|
||
|
|
FS.Collection.prototype.findOne = function(selector) {
|
||
|
|
var self = this;
|
||
|
|
return self.files.findOne.apply(self.files, arguments);
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @method FS.Collection.prototype.find
|
||
|
|
* @public
|
||
|
|
* @param {[selector](http://docs.meteor.com/#selectors)} selector
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#find)
|
||
|
|
* Example:
|
||
|
|
```js
|
||
|
|
var images = new FS.Collection( ... );
|
||
|
|
// Get the all file objects
|
||
|
|
var files = images.find({ _id: 'NpnskCt6ippN6CgD8' }).fetch();
|
||
|
|
```
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.find = function(selector) {
|
||
|
|
var self = this;
|
||
|
|
return self.files.find.apply(self.files, arguments);
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @method FS.Collection.prototype.allow
|
||
|
|
* @public
|
||
|
|
* @param {object} options
|
||
|
|
* @param {function} options.download Function that checks if the file contents may be downloaded
|
||
|
|
* @param {function} options.insert
|
||
|
|
* @param {function} options.update
|
||
|
|
* @param {function} options.remove Functions that look at a proposed modification to the database and return true if it should be allowed
|
||
|
|
* @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
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#allow)
|
||
|
|
* Example:
|
||
|
|
```js
|
||
|
|
var images = new FS.Collection( ... );
|
||
|
|
// Get the all file objects
|
||
|
|
var files = images.allow({
|
||
|
|
insert: function(userId, doc) { return true; },
|
||
|
|
update: function(userId, doc, fields, modifier) { return true; },
|
||
|
|
remove: function(userId, doc) { return true; },
|
||
|
|
download: function(userId, fileObj) { return true; },
|
||
|
|
});
|
||
|
|
```
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.allow = function(options) {
|
||
|
|
var self = this;
|
||
|
|
|
||
|
|
// Pull out the custom "download" functions
|
||
|
|
if (options.download) {
|
||
|
|
if (!(options.download instanceof Function)) {
|
||
|
|
throw new Error("allow: Value for `download` must be a function");
|
||
|
|
}
|
||
|
|
self._validators.download.allow.push(options.download);
|
||
|
|
delete options.download;
|
||
|
|
}
|
||
|
|
|
||
|
|
return self.files.allow.call(self.files, options);
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @method FS.Collection.prototype.deny
|
||
|
|
* @public
|
||
|
|
* @param {object} options
|
||
|
|
* @param {function} options.download Function that checks if the file contents may be downloaded
|
||
|
|
* @param {function} options.insert
|
||
|
|
* @param {function} options.update
|
||
|
|
* @param {function} options.remove Functions that look at a proposed modification to the database and return true if it should be denyed
|
||
|
|
* @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
|
||
|
|
* [Meteor docs](http://docs.meteor.com/#deny)
|
||
|
|
* Example:
|
||
|
|
```js
|
||
|
|
var images = new FS.Collection( ... );
|
||
|
|
// Get the all file objects
|
||
|
|
var files = images.deny({
|
||
|
|
insert: function(userId, doc) { return true; },
|
||
|
|
update: function(userId, doc, fields, modifier) { return true; },
|
||
|
|
remove: function(userId, doc) { return true; },
|
||
|
|
download: function(userId, fileObj) { return true; },
|
||
|
|
});
|
||
|
|
```
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.deny = function(options) {
|
||
|
|
var self = this;
|
||
|
|
|
||
|
|
// Pull out the custom "download" functions
|
||
|
|
if (options.download) {
|
||
|
|
if (!(options.download instanceof Function)) {
|
||
|
|
throw new Error("deny: Value for `download` must be a function");
|
||
|
|
}
|
||
|
|
self._validators.download.deny.push(options.download);
|
||
|
|
delete options.download;
|
||
|
|
}
|
||
|
|
|
||
|
|
return self.files.deny.call(self.files, options);
|
||
|
|
};
|
||
|
|
|
||
|
|
// TODO: Upsert?
|
||
|
|
|
||
|
|
/**
|
||
|
|
* We provide a default implementation that doesn't do anything.
|
||
|
|
* Can be changed by user or packages, such as the default cfs-collection-filters pkg.
|
||
|
|
* @param {FS.File} fileObj File object
|
||
|
|
* @return {Boolean} Should we allow insertion of this file?
|
||
|
|
*/
|
||
|
|
FS.Collection.prototype.allowsFile = function fsColAllowsFile(fileObj) {
|
||
|
|
return true;
|
||
|
|
};
|