diff options
author | Romulus Urakagi Tsai <urakagi@gmail.com> | 2020-05-08 09:32:19 +0800 |
---|---|---|
committer | Romulus Urakagi Tsai <urakagi@gmail.com> | 2020-05-08 09:32:19 +0800 |
commit | 444848876759173ad80203129250d2f0311f30fc (patch) | |
tree | 14cc7c5f9c2695cc506110d1e1acb5bedc02fc7d | |
parent | 7dc0bbd7b26ef50ebd6c0a4d719658c2d288c2d3 (diff) | |
download | wekan-444848876759173ad80203129250d2f0311f30fc.tar.gz wekan-444848876759173ad80203129250d2f0311f30fc.tar.bz2 wekan-444848876759173ad80203129250d2f0311f30fc.zip |
Done attachments activities operating
-rw-r--r-- | client/components/activities/activities.js | 7 | ||||
-rw-r--r-- | models/activities.js | 6 | ||||
-rw-r--r-- | models/attachments.js | 241 |
3 files changed, 70 insertions, 184 deletions
diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js index b082273a..9697d28c 100644 --- a/client/components/activities/activities.js +++ b/client/components/activities/activities.js @@ -152,17 +152,18 @@ BlazeComponent.extendComponent({ attachmentLink() { const attachment = this.currentData().attachment(); + const link = attachment.link('original', '/'); // trying to display url before file is stored generates js errors return ( attachment && - attachment.url({ download: true }) && + link && Blaze.toHTML( HTML.A( { - href: attachment.url({ download: true }), + href: link, target: '_blank', }, - attachment.name(), + attachment.get('name'), ), ) ); diff --git a/models/activities.js b/models/activities.js index 19e3fb7d..3f8a0d35 100644 --- a/models/activities.js +++ b/models/activities.js @@ -214,7 +214,11 @@ if (Meteor.isServer) { } if (activity.attachmentId) { const attachment = activity.attachment(); - params.attachment = attachment.original.name; + if (attachment.original) { + params.attachment = attachment.original.name; + } else { + params.attachment = attachment.versions.original.name; + } params.attachmentId = attachment._id; } if (activity.checklistId) { diff --git a/models/attachments.js b/models/attachments.js index c35d3d4c..798d04be 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -1,13 +1,15 @@ import { FilesCollection } from 'meteor/ostrio:files'; +const collectionName = 'attachments2'; + Attachments = new FilesCollection({ storagePath: storagePath(), - debug: false, // FIXME: Remove debug mode + debug: false, + allowClientCode: true, collectionName: 'attachments2', - allowClientCode: true, // FIXME: Permissions - onAfterUpload: (fileRef) => { - Attachments.update({_id:fileRef._id}, {$set: {"meta.uploaded": true}}); - } + onAfterUpload: onAttachmentUploaded, + onBeforeRemove: onAttachmentRemoving, + onAfterRemove: onAttachmentRemoved }); if (Meteor.isServer) { @@ -17,194 +19,73 @@ if (Meteor.isServer) { // TODO: Permission related // TODO: Add Activity update - // TODO: publish and subscribe - Meteor.publish('attachments2', function() { + Meteor.publish(collectionName, function() { return Attachments.find().cursor; }); } else { - Meteor.subscribe('attachments2'); + Meteor.subscribe(collectionName); } -// ---------- Deprecated fallback ---------- // - -const localFSStore = process.env.ATTACHMENTS_STORE_PATH; -const storeName = 'attachments2'; -const defaultStoreOptions = { - beforeWrite: fileObj => { - if (!fileObj.isImage()) { - return { - type: 'application/octet-stream', - }; - } - return {}; - }, -}; -let store; -if (localFSStore) { - // have to reinvent methods from FS.Store.GridFS and FS.Store.FileSystem - const fs = Npm.require('fs'); - const path = Npm.require('path'); - const mongodb = Npm.require('mongodb'); - const Grid = Npm.require('gridfs-stream'); - // calulate the absolute path here, because FS.Store.FileSystem didn't expose the aboslutepath or FS.Store didn't expose api calls :( - let pathname = localFSStore; - /*eslint camelcase: ["error", {allow: ["__meteor_bootstrap__"]}] */ +function storagePath(defaultPath) { + const storePath = process.env.ATTACHMENTS_STORE_PATH; + return storePath ? storePath : defaultPath; +} - if (!pathname && __meteor_bootstrap__ && __meteor_bootstrap__.serverDir) { - pathname = path.join( - __meteor_bootstrap__.serverDir, - `../../../cfs/files/${storeName}`, +function onAttachmentUploaded(fileRef) { + Attachments.update({_id:fileRef._id}, {$set: {"meta.uploaded": true}}); + if (!fileRef.meta.source || fileRef.meta.source !== 'import') { + // Add activity about adding the attachment + Activities.insert({ + userId: fileRef.userId, + type: 'card', + activityType: 'addAttachment', + attachmentId: fileRef._id, + boardId: fileRef.meta.boardId, + cardId: fileRef.meta.cardId, + listId: fileRef.meta.listId, + swimlaneId: fileRef.meta.swimlaneId, + }); + } else { + // Don't add activity about adding the attachment as the activity + // be imported and delete source field + CFSAttachments.update( + { + _id: fileRef._id, + }, + { + $unset: { + source: '', + }, + }, ); } +} - if (!pathname) - throw new Error('FS.Store.FileSystem unable to determine path'); - - // Check if we have '~/foo/bar' - if (pathname.split(path.sep)[0] === '~') { - const homepath = - process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; - if (homepath) { - pathname = pathname.replace('~', homepath); - } else { - throw new Error('FS.Store.FileSystem unable to resolve "~" in path'); - } - } - - // Set absolute path - const absolutePath = path.resolve(pathname); - - const _FStore = new FS.Store.FileSystem(storeName, { - path: localFSStore, - ...defaultStoreOptions, - }); - const GStore = { - fileKey(fileObj) { - const key = { - _id: null, - filename: null, - }; - - // If we're passed a fileObj, we retrieve the _id and filename from it. - if (fileObj) { - const info = fileObj._getInfo(storeName, { - updateFileRecordFirst: false, - }); - key._id = info.key || null; - key.filename = - info.name || - fileObj.name({ updateFileRecordFirst: false }) || - `${fileObj.collectionName}-${fileObj._id}`; - } - - // If key._id is null at this point, createWriteStream will let GridFS generate a new ID - return key; - }, - db: undefined, - mongoOptions: { useNewUrlParser: true }, - mongoUrl: process.env.MONGO_URL, - init() { - this._init(err => { - this.inited = !err; - }); - }, - _init(callback) { - const self = this; - mongodb.MongoClient.connect(self.mongoUrl, self.mongoOptions, function( - err, - db, - ) { - if (err) { - return callback(err); - } - self.db = db; - return callback(null); - }); - return; - }, - createReadStream(fileKey, options) { - const self = this; - if (!self.inited) { - self.init(); - return undefined; - } - options = options || {}; - - // Init GridFS - const gfs = new Grid(self.db, mongodb); - - // Set the default streamning settings - const settings = { - _id: new mongodb.ObjectID(fileKey._id), - root: `cfs_gridfs.${storeName}`, - }; - - // Check if this should be a partial read - if ( - typeof options.start !== 'undefined' && - typeof options.end !== 'undefined' - ) { - // Add partial info - settings.range = { - startPos: options.start, - endPos: options.end, - }; - } - return gfs.createReadStream(settings); - }, - }; - GStore.init(); - const CRS = 'createReadStream'; - const _CRS = `_${CRS}`; - const FStore = _FStore._transform; - FStore[_CRS] = FStore[CRS].bind(FStore); - FStore[CRS] = function(fileObj, options) { - let stream; - try { - const localFile = path.join( - absolutePath, - FStore.storage.fileKey(fileObj), - ); - const state = fs.statSync(localFile); - if (state) { - stream = FStore[_CRS](fileObj, options); - } - } catch (e) { - // file is not there, try GridFS ? - stream = undefined; - } - if (stream) return stream; - else { - try { - const stream = GStore[CRS](GStore.fileKey(fileObj), options); - return stream; - } catch (e) { - return undefined; - } - } - }.bind(FStore); - store = _FStore; -} else { - store = new FS.Store.GridFS(localFSStore ? `G${storeName}` : storeName, { - // XXX Add a new store for cover thumbnails so we don't load big images in - // the general board view - // If the uploaded document is not an image we need to enforce browser - // download instead of execution. This is particularly important for HTML - // files that the browser will just execute if we don't serve them with the - // appropriate `application/octet-stream` MIME header which can lead to user - // data leaks. I imagine other formats (like PDF) can also be attack vectors. - // See https://github.com/wekan/wekan/issues/99 - // XXX Should we use `beforeWrite` option of CollectionFS instead of - // collection-hooks? - // We should use `beforeWrite`. - ...defaultStoreOptions, +function onAttachmentRemoving(cursor) { + const file = cursor.get()[0]; + const meta = file.meta; + Activities.insert({ + userId: this.userId, + type: 'card', + activityType: 'deleteAttachment', + attachmentId: file._id, + boardId: meta.boardId, + cardId: meta.cardId, + listId: meta.listId, + swimlaneId: meta.swimlaneId, }); + return true; } -function storagePath(defaultPath) { - const storePath = process.env.ATTACHMENTS_STORE_PATH; - return storePath ? storePath : defaultPath; +function onAttachmentRemoved(files) { + // Don't know why we need to remove the activity +/* for (let i in files) { + let doc = files[i]; + Activities.remove({ + attachmentId: doc._id, + }); + }*/ } export default Attachments; |