Prechádzať zdrojové kódy

Done attachments activities operating

Romulus Urakagi Tsai 5 rokov pred
rodič
commit
4448488767

+ 4 - 3
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'),
         ),
       )
     );

+ 5 - 1
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) {

+ 61 - 180
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;