From 00d8d7f9fe0b81a823651f9579a1c3f6d1053a9f Mon Sep 17 00:00:00 2001 From: David Arnold Date: Mon, 14 Sep 2020 01:07:17 -0500 Subject: [PATCH] Ref: original & and use fileObj.meta fileObj.meta is part of the ostrio:files API and be passed to the constructor. This is less hacky than trying tu update a persistet object after the fact. --- client/components/cards/attachments.js | 31 +++++---- client/components/main/editor.js | 29 +++----- client/components/users/userAvatar.jade | 2 +- client/components/users/userAvatar.js | 6 +- client/lib/utils.js | 28 ++++---- models/activities.js | 2 +- models/attachments.js | 89 +++++++++---------------- models/exporter.js | 6 +- models/trelloCreator.js | 48 +++++++------ models/wekanCreator.js | 50 +++++++------- 10 files changed, 127 insertions(+), 164 deletions(-) diff --git a/client/components/cards/attachments.js b/client/components/cards/attachments.js index 96255fc40..b17d032dd 100644 --- a/client/components/cards/attachments.js +++ b/client/components/cards/attachments.js @@ -58,20 +58,20 @@ Template.cardAttachmentsPopup.events({ const uploader = Attachments.insert( { file: event.currentTarget.files[0], + meta: Utils.getCommonAttachmentMetaFrom(card), streams: 'dynamic', chunkSize: 'dynamic', }, false, ); - uploader.on('uploaded', (error, fileObj) => { + uploader.on('uploaded', (error, fileRef) => { if (!error) { - if (fileObj.isImage) { - card.setCover(fileObj._id); + if (fileRef.isImage) { + card.setCover(fileRef._id); } - Utils.addCommonMetaToAttachment(card, fileObj); } }); - uploader.on('end', (error, fileObj) => { + uploader.on('end', (error, fileRef) => { Popup.close(); }); uploader.start(); @@ -123,29 +123,28 @@ Template.previewClipboardImagePopup.onRendered(() => { Template.previewClipboardImagePopup.events({ 'click .js-upload-pasted-image'() { - const results = pastedResults; - if (results && results.file) { + const card = this; + if (pastedResults && pastedResults.file) { + const file = pastedResults.file; window.oPasted = pastedResults; - const card = this; const uploader = Attachments.insert( { - file: results.file, - fileName: - results.name || results.file.type.replace('image/', 'clipboard.'), + file, + meta: Utils.getCommonAttachmentMetaFrom(card), + fileName: file.name || file.type.replace('image/', 'clipboard.'), streams: 'dynamic', chunkSize: 'dynamic', }, false, ); - uploader.on('uploaded', (error, fileObj) => { + uploader.on('uploaded', (error, fileRef) => { if (!error) { - if (fileObj.isImage) { - card.setCover(fileObj._id); + if (fileRef.isImage) { + card.setCover(fileRef._id); } - Utils.addCommonMetaToAttachment(card, fileObj); } }); - uploader.on('end', (error, fileObj) => { + uploader.on('end', (error, fileRef) => { pastedResults = null; $(document.body).pasteImageReader(() => {}); Popup.close(); diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 89d0717cf..61daa2bfc 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -140,37 +140,30 @@ Template.editor.onRendered(() => { const $summernote = getSummernote(this); if (files && files.length > 0) { const image = files[0]; - const currentCard = Cards.findOne(Session.get('currentCard')); - const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL; - const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO; - const insertImage = src => { - const img = document.createElement('img'); - img.src = src; - img.setAttribute('width', '100%'); - $summernote.summernote('insertNode', img); - }; + const card = Cards.findOne(Session.get('currentCard')); const processUpload = function(file) { const uploader = Attachments.insert( { file, + meta: Utils.getCommonAttachmentMetaFrom(card), streams: 'dynamic', chunkSize: 'dynamic', }, false, ); - uploader.on('uploaded', (error, fileObj) => { + uploader.on('uploaded', (error, fileRef) => { if (!error) { - if (fileObj.isImage) { - insertImage( - `${location.protocol}//${location.host}${fileObj.path}`, - ); + if (fileRef.isImage) { + const img = document.createElement('img'); + img.src = fileRef.link(); + img.setAttribute('width', '100%'); + $summernote.summernote('insertNode', img); } - Utils.addCommonMetaToAttachment(currentCard, fileObj); } }); uploader.start(); }; - if (MAX_IMAGE_PIXEL) { + if (Utils.MAX_IMAGE_PIXEL) { const reader = new FileReader(); reader.onload = function(e) { const dataurl = e && e.target && e.target.result; @@ -178,8 +171,8 @@ Template.editor.onRendered(() => { // need to shrink image Utils.shrinkImage({ dataurl, - maxSize: MAX_IMAGE_PIXEL, - ratio: COMPRESS_RATIO, + maxSize: Utils.MAX_IMAGE_PIXEL, + ratio: Utils.IMAGE_COMPRESS_RATIO, toBlob: true, callback(blob) { if (blob !== false) { diff --git a/client/components/users/userAvatar.jade b/client/components/users/userAvatar.jade index 7f2067ce8..7ec7d8e13 100644 --- a/client/components/users/userAvatar.jade +++ b/client/components/users/userAvatar.jade @@ -51,7 +51,7 @@ template(name="changeAvatarPopup") unless isSelected a.js-delete-avatar {{_ 'delete'}} | - - = original.name + = name li: a.js-select-initials .member +userAvatarInitials(userId=currentUser._id) diff --git a/client/components/users/userAvatar.js b/client/components/users/userAvatar.js index 6ab636cb4..87b918107 100644 --- a/client/components/users/userAvatar.js +++ b/client/components/users/userAvatar.js @@ -102,13 +102,13 @@ BlazeComponent.extendComponent({ }, false, ); - uploader.on('uploaded', (error, fileObj) => { + uploader.on('uploaded', (error, fileRef) => { if (!error) { - self.setAvatar(fileObj.path); + self.setAvatar(fileRef.path); // self.setAvatar(this.currentData().url(this.avatarUrlOptions())); } }); - uploader.on('error', (error, fileObj) => { + uploader.on('error', (error, fileData) => { // XXX check for actually returned error self.setError('avatar-too-big'); }); diff --git a/client/lib/utils.js b/client/lib/utils.js index 78c2de741..e42e3493a 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -67,23 +67,21 @@ Utils = { }) ); }, + getCommonAttachmentMetaFrom(card) { + let meta; + if (card.isLinkedCard()) { + meta.boardId = Cards.findOne(card.linkedId).boardId; + meta.cardId = card.linkedId; + } else { + meta.boardId = card.boardId; + meta.swimlaneId = card.swimlaneId; + meta.listId = card.listId; + meta.cardId = card._id; + } + return meta; + }, MAX_IMAGE_PIXEL: Meteor.settings.public.MAX_IMAGE_PIXEL, COMPRESS_RATIO: Meteor.settings.public.IMAGE_COMPRESS_RATIO, - addCommonMetaToAttachment(card, file) { - if (card.isLinkedCard()) { - file.boardId = Cards.findOne(card.linkedId).boardId; - file.cardId = card.linkedId; - } else { - file.boardId = card.boardId; - file.swimlaneId = card.swimlaneId; - file.listId = card.listId; - file.cardId = card._id; - } - file.userId = Meteor.userId(); - if (file.original) { - file.original.name = file.name; - } - }, shrinkImage(options) { // shrink image to certain size const dataurl = options.dataurl, diff --git a/models/activities.js b/models/activities.js index b5fcb7d8f..2663dd299 100644 --- a/models/activities.js +++ b/models/activities.js @@ -217,7 +217,7 @@ if (Meteor.isServer) { } if (activity.attachmentId) { const attachment = activity.attachment(); - params.attachment = attachment.original.name; + params.attachment = attachment.name; params.attachmentId = attachment._id; } if (activity.checklistId) { diff --git a/models/attachments.js b/models/attachments.js index 9e5a2e601..a272a159b 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -7,6 +7,21 @@ import { createOnAfterRemove } from './lib/fsHooks/createOnAfterRemove'; const attachmentBucket = createBucket('attachments'); +const insertActivity = (fileObj, activityType) => + Activities.insert({ + userId: fileObj.userId, + type: 'card', + activityType, + attachmentId: fileObj._id, + // this preserves the name so that notifications can be meaningful after + // this file is removed + attachmentName: fileObj.name, + boardId: fileObj.meta.boardId, + cardId: fileObj.meta.cardId, + listId: fileObj.meta.listId, + swimlaneId: fileObj.meta.swimlaneId, + }); + // XXX Enforce a schema for the Attachments FilesCollection // see: https://github.com/VeliovGroup/Meteor-Files/wiki/Schema @@ -14,84 +29,46 @@ export const Attachments = new FilesCollection({ debug: false, // Change to `true` for debugging collectionName: 'attachments', allowClientCode: false, - onAfterUpload(doc) { + onAfterUpload(fileRef) { + createOnAfterUpload(attachmentBucket)(fileRef); // If the attachment doesn't have a source field // or its source is different than import - if (!doc.source || doc.source !== 'import') { + if (!fileRef.meta.source || fileRef.meta.source !== 'import') { // Add activity about adding the attachment - Activities.insert({ - userId, - type: 'card', - activityType: 'addAttachment', - attachmentId: doc._id, - // this preserves the name so that notifications can be meaningful after - // this file is removed - attachmentName: doc.original.name, - boardId: doc.boardId, - cardId: doc.cardId, - listId: doc.listId, - swimlaneId: doc.swimlaneId, - }); - } else { - // Don't add activity about adding the attachment as the activity - // be imported and delete source field - Attachments.update( - { - _id: doc._id, - }, - { - $unset: { - source: '', - }, - }, - ); + insertActivity(fileRef, 'addAttachment'); } - createOnAfterUpload(attachmentBucket)(doc); }, interceptDownload: createInterceptDownload(attachmentBucket), - onAfterRemove(docs) { - docs.forEach(function(doc) { - Activities.insert({ - userId: doc.userId, - type: 'card', - activityType: 'deleteAttachment', - attachmentId: doc._id, - // this preserves the name so that notifications can be meaningful after - // this file is removed - attachmentName: doc.original.name, - boardId: doc.boardId, - cardId: doc.cardId, - listId: doc.listId, - swimlaneId: doc.swimlaneId, - }); + onAfterRemove(files) { + createOnAfterRemove(attachmentBucket)(files); + files.forEach(fileObj => { + insertActivity(fileObj, 'deleteAttachment'); }); - createOnAfterRemove(attachmentBucket)(docs); }, // We authorize the attachment download either: // - if the board is public, everyone (even unconnected) can download it // - if the board is private, only board members can download it - downloadCallback(doc) { - const board = Boards.findOne(doc.boardId); + protected(fileObj) { + const board = Boards.findOne(fileObj.meta.boardId); if (board.isPublic()) { return true; - } else { - return board.hasMember(this.userId); } + return board.hasMember(this.userId); }, }); if (Meteor.isServer) { Attachments.allow({ - insert(userId, doc) { - return allowIsBoardMember(userId, Boards.findOne(doc.boardId)); + insert(userId, fileObj) { + return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId)); }, - update(userId, doc) { - return allowIsBoardMember(userId, Boards.findOne(doc.boardId)); + update(userId, fileObj) { + return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId)); }, - remove(userId, doc) { - return allowIsBoardMember(userId, Boards.findOne(doc.boardId)); + remove(userId, fileObj) { + return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId)); }, - fetch: ['boardId'], + fetch: ['meta'], }); Meteor.startup(() => { diff --git a/models/exporter.js b/models/exporter.js index 26cb556bd..31b020e1b 100644 --- a/models/exporter.js +++ b/models/exporter.js @@ -124,11 +124,11 @@ export class Exporter { return { _id: attachment._id, - cardId: attachment.cardId, + cardId: attachment.meta.cardId, //url: FlowRouter.url(attachment.url()), file: filebase64, - name: attachment.original.name, - type: attachment.original.type, + name: attachment.name, + type: attachment.type, }; }); diff --git a/models/trelloCreator.js b/models/trelloCreator.js index a44c4e308..c22928a7f 100644 --- a/models/trelloCreator.js +++ b/models/trelloCreator.js @@ -360,35 +360,33 @@ export class TrelloCreator { } const attachments = this.attachments[card.id]; const trelloCoverId = card.idAttachmentCover; - // Simulating file.attachData on the client generates multiple errors - // - HEAD returns null, which causes exception down the line - // - the template then tries to display the url to the attachment which causes other errors - // so we make it server only, and let UI catch up once it is done, forget about latency comp. if (attachments && Meteor.isServer) { attachments.forEach(att => { - // Simulating file.attachData on the client generates multiple errors - // - HEAD returns null, which causes exception down the line - // - the template then tries to display the url to the attachment which causes other errors - // so we make it server only, and let UI catch up once it is done, forget about latency comp. const self = this; + const opts = { + type: att.type ? att.type : undefined, + userId: self._user(att.userId), + meta: { + boardId, + cardId, + source: 'import', + }, + }; + const cb = (error, fileObj) => { + if (error) { + throw error; + } + self.attachmentIds[att._id] = fileObj._id; + if (trelloCoverId === att._id) { + Cards.direct.update(cardId, { + $set: { coverId: fileObj._id }, + }); + } + }; if (att.url) { - Attachment.load(att.url, (error, fileObj) => { - if (error) { - throw error; - } - fileObj.boardId = boardId; - fileObj.cardId = cardId; - fileObj.userId = self._user(att.userId); - // The field source will only be used to prevent adding - // attachments' related activities automatically - fileObj.source = 'import'; - self.attachmentIds[att._id] = fileObj._id; - if (trelloCoverId === att.id) { - Cards.direct.update(cardId, { - $set: { coverId: fileObj._id }, - }); - } - }); + Attachment.load(att.url, opts, cb, true); + } else if (att.file) { + Attachment.write(att.file, opts, cb, true); } }); } diff --git a/models/wekanCreator.js b/models/wekanCreator.js index 6e8394c7a..7abada56a 100644 --- a/models/wekanCreator.js +++ b/models/wekanCreator.js @@ -407,33 +407,31 @@ export class WekanCreator { const wekanCoverId = card.coverId; if (attachments && Meteor.isServer) { attachments.forEach(att => { - // Simulating file.attachData on the client generates multiple errors - // - HEAD returns null, which causes exception down the line - // - the template then tries to display the url to the attachment which causes other errors - // so we make it server only, and let UI catch up once it is done, forget about latency comp. const self = this; - if (att.url || att.file) { - Attachment.load( - att.url ? att.url : Buffer.from(att.file, 'base64'), - { type: att.type ? att.ype : undefined }, - (error, fileObj) => { - if (error) { - throw error; - } - fileObj.boardId = boardId; - fileObj.cardId = cardId; - fileObj.userId = self._user(att.userId); - // The field source will only be used to prevent adding - // attachments' related activities automatically - fileObj.source = 'import'; - self.attachmentIds[att._id] = fileObj._id; - if (wekanCoverId === att._id) { - Cards.direct.update(cardId, { - $set: { coverId: fileObj._id }, - }); - } - }, - ); + const opts = { + type: att.type ? att.type : undefined, + userId: self._user(att.userId), + meta: { + boardId, + cardId, + source: 'import', + }, + }; + const cb = (error, fileObj) => { + if (error) { + throw error; + } + self.attachmentIds[att._id] = fileObj._id; + if (wekanCoverId === att._id) { + Cards.direct.update(cardId, { + $set: { coverId: fileObj._id }, + }); + } + }; + if (att.url) { + Attachment.load(att.url, opts, cb, true); + } else if (att.file) { + Attachment.write(att.file, opts, cb, true); } }); }