mirror of
https://github.com/wekan/wekan.git
synced 2026-02-04 15:41:52 +01:00
Merge branch 'feature-ostrio-files' of https://github.com/majus/wekan
This commit is contained in:
commit
d00596f88a
42 changed files with 786 additions and 1577 deletions
|
|
@ -196,14 +196,14 @@ BlazeComponent.extendComponent({
|
|||
// trying to display url before file is stored generates js errors
|
||||
return (
|
||||
(attachment &&
|
||||
attachment.url({ download: true }) &&
|
||||
attachment.path &&
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: attachment.url({ download: true }),
|
||||
href: `${attachment.link()}?download=true`,
|
||||
target: '_blank',
|
||||
},
|
||||
DOMPurify.sanitize(attachment.name()),
|
||||
DOMPurify.sanitize(attachment.name),
|
||||
),
|
||||
)) ||
|
||||
DOMPurify.sanitize(this.currentData().activity.attachmentName)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ template(name="previewClipboardImagePopup")
|
|||
img.preview-clipboard-image()
|
||||
button.primary.js-upload-pasted-image {{_ 'upload'}}
|
||||
|
||||
template(name="previewAttachedImagePopup")
|
||||
img.preview-large-image.js-large-image-clicked(src="{{url}}")
|
||||
|
||||
template(name="attachmentDeletePopup")
|
||||
p {{_ "attachment-delete-pop"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
|
@ -22,31 +19,31 @@ template(name="attachmentsGalery")
|
|||
.attachments-galery
|
||||
each attachments
|
||||
.attachment-item
|
||||
a.attachment-thumbnail.swipebox(href="{{url}}" title="{{name}}")
|
||||
a.attachment-thumbnail.swipebox(href="{{link}}" title="{{name}}")
|
||||
if isUploaded
|
||||
if isImage
|
||||
img.attachment-thumbnail-img(src="{{url}}")
|
||||
img.attachment-thumbnail-img(src="{{link}}")
|
||||
else if($eq extension 'mp3')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="audio/mpeg")
|
||||
source(src="{{link}}" type="audio/mpeg")
|
||||
else if($eq extension 'ogg')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/ogg")
|
||||
source(src="{{link}}" type="video/ogg")
|
||||
else if($eq extension 'webm')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/webm")
|
||||
source(src="{{link}}" type="video/webm")
|
||||
else if($eq extension 'mp4')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/mp4")
|
||||
source(src="{{link}}" type="video/mp4")
|
||||
else
|
||||
span.attachment-thumbnail-ext= extension
|
||||
else
|
||||
+spinner
|
||||
span.attachment-thumbnail-ext= extension
|
||||
p.attachment-details
|
||||
= name
|
||||
span.file-size ({{fileSize size}} KB)
|
||||
span.attachment-details-actions
|
||||
a.js-download(href="{{url download=true}}")
|
||||
a.js-download(href="{{link}}?download=true", download="{{name}}")
|
||||
i.fa.fa-download
|
||||
| {{_ 'download'}}
|
||||
if currentUser.isBoardMember
|
||||
|
|
|
|||
|
|
@ -13,35 +13,10 @@ Template.attachmentsGalery.events({
|
|||
event.stopPropagation();
|
||||
},
|
||||
'click .js-add-cover'() {
|
||||
Cards.findOne(this.cardId).setCover(this._id);
|
||||
Cards.findOne(this.meta.cardId).setCover(this._id);
|
||||
},
|
||||
'click .js-remove-cover'() {
|
||||
Cards.findOne(this.cardId).unsetCover();
|
||||
},
|
||||
'click .js-preview-image'(event) {
|
||||
Popup.open('previewAttachedImage').call(this, event);
|
||||
// when multiple thumbnails, if click one then another very fast,
|
||||
// we might get a wrong width from previous img.
|
||||
// when popup reused, onRendered() won't be called, so we cannot get there.
|
||||
// here make sure to get correct size when this img fully loaded.
|
||||
const img = $('img.preview-large-image')[0];
|
||||
if (!img) return;
|
||||
const rePosPopup = () => {
|
||||
const w = img.width;
|
||||
const h = img.height;
|
||||
// if the image is too large, we resize & center the popup.
|
||||
if (w > 300) {
|
||||
$('div.pop-over').css({
|
||||
width: w + 20,
|
||||
position: 'absolute',
|
||||
left: (window.innerWidth - w) / 2,
|
||||
top: (window.innerHeight - h) / 2,
|
||||
});
|
||||
}
|
||||
};
|
||||
const url = $(event.currentTarget).attr('src');
|
||||
if (img.src === url && img.complete) rePosPopup();
|
||||
else img.onload = rePosPopup;
|
||||
Cards.findOne(this.meta.cardId).unsetCover();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -54,59 +29,30 @@ Template.attachmentsGalery.helpers({
|
|||
},
|
||||
});
|
||||
|
||||
Template.previewAttachedImagePopup.events({
|
||||
'click .js-large-image-clicked'() {
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAttachmentsPopup.events({
|
||||
'change .js-attach-file'(event) {
|
||||
const card = this;
|
||||
const processFile = f => {
|
||||
Utils.processUploadedAttachment(card, f, attachment => {
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
if (event.currentTarget.files && event.currentTarget.files[0]) {
|
||||
const uploader = Attachments.insert(
|
||||
{
|
||||
file: event.currentTarget.files[0],
|
||||
meta: Utils.getCommonAttachmentMetaFrom(card),
|
||||
chunkSize: 'dynamic',
|
||||
},
|
||||
false,
|
||||
);
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
if (fileRef.isImage) {
|
||||
card.setCover(fileRef._id);
|
||||
}
|
||||
}
|
||||
});
|
||||
uploader.on('end', (error, fileRef) => {
|
||||
Popup.back();
|
||||
});
|
||||
};
|
||||
|
||||
FS.Utility.eachFile(event, f => {
|
||||
if (
|
||||
MAX_IMAGE_PIXEL > 0 &&
|
||||
typeof f.type === 'string' &&
|
||||
f.type.match(/^image/)
|
||||
) {
|
||||
// is image
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const dataurl = e && e.target && e.target.result;
|
||||
if (dataurl !== undefined) {
|
||||
Utils.shrinkImage({
|
||||
dataurl,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
toBlob: true,
|
||||
callback(blob) {
|
||||
if (blob === false) {
|
||||
processFile(f);
|
||||
} else {
|
||||
blob.name = f.name;
|
||||
processFile(blob);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// couldn't process it let other function handle it?
|
||||
processFile(f);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(f);
|
||||
} else {
|
||||
processFile(f);
|
||||
}
|
||||
});
|
||||
uploader.start();
|
||||
}
|
||||
},
|
||||
'click .js-computer-upload'(event, templateInstance) {
|
||||
templateInstance.find('.js-attach-file').click();
|
||||
|
|
@ -154,30 +100,32 @@ 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 file = new FS.File(results.file);
|
||||
if (!results.name) {
|
||||
// if no filename, it's from clipboard. then we give it a name, with ext name from MIME type
|
||||
if (typeof results.file.type === 'string') {
|
||||
file.name(results.file.type.replace('image/', 'clipboard.'));
|
||||
const uploader = Attachments.insert(
|
||||
{
|
||||
file,
|
||||
meta: Utils.getCommonAttachmentMetaFrom(card),
|
||||
fileName: file.name || file.type.replace('image/', 'clipboard.'),
|
||||
chunkSize: 'dynamic',
|
||||
},
|
||||
false,
|
||||
);
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
if (fileRef.isImage) {
|
||||
card.setCover(fileRef._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
file.updatedAt(new Date());
|
||||
file.boardId = card.boardId;
|
||||
file.cardId = card._id;
|
||||
file.userId = Meteor.userId();
|
||||
const attachment = Attachments.insert(file);
|
||||
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
}
|
||||
|
||||
pastedResults = null;
|
||||
$(document.body).pasteImageReader(() => {});
|
||||
Popup.back();
|
||||
});
|
||||
uploader.on('end', (error, fileRef) => {
|
||||
pastedResults = null;
|
||||
$(document.body).pasteImageReader(() => {});
|
||||
Popup.back();
|
||||
});
|
||||
uploader.start();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -51,11 +51,6 @@
|
|||
display: block
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
.preview-large-image
|
||||
max-width: 1000px
|
||||
display: block
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
.preview-clipboard-image
|
||||
width: 280px
|
||||
max-width: 100%;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ template(name="minicard")
|
|||
.handle
|
||||
.fa.fa-arrows
|
||||
if cover
|
||||
.minicard-cover(style="background-image: url('{{cover.url}}');")
|
||||
.minicard-cover(style="background-image: url('{{cover.link 'original' '/'}}?dummyReloadAfterSessionEstablished={{sess}}');")
|
||||
if labels
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
|
|
|
|||
|
|
@ -114,6 +114,12 @@ Template.minicard.helpers({
|
|||
return false;
|
||||
}
|
||||
},
|
||||
// XXX resolve this nasty hack for https://github.com/veliovgroup/Meteor-Files/issues/763
|
||||
sess() {
|
||||
return Meteor.connection && Meteor.connection._lastSessionId
|
||||
? Meteor.connection._lastSessionId
|
||||
: null;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ BlazeComponent.extendComponent({
|
|||
});
|
||||
}
|
||||
},
|
||||
|
||||
onImageUpload(files) {
|
||||
const $summernote = getSummernote(this);
|
||||
if (files && files.length > 0) {
|
||||
|
|
@ -161,46 +160,26 @@ BlazeComponent.extendComponent({
|
|||
const currentCard = Utils.getCurrentCard();
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
const insertImage = src => {
|
||||
// process all image upload types to the description/comment window
|
||||
const img = document.createElement('img');
|
||||
img.src = src;
|
||||
img.setAttribute('width', '100%');
|
||||
$summernote.summernote('insertNode', img);
|
||||
};
|
||||
const processData = function(fileObj) {
|
||||
Utils.processUploadedAttachment(
|
||||
currentCard,
|
||||
fileObj,
|
||||
attachment => {
|
||||
if (
|
||||
attachment &&
|
||||
attachment._id &&
|
||||
attachment.isImage()
|
||||
) {
|
||||
attachment.one('uploaded', function() {
|
||||
const maxTry = 3;
|
||||
const checkItvl = 500;
|
||||
let retry = 0;
|
||||
const checkUrl = function() {
|
||||
// even though uploaded event fired, attachment.url() is still null somehow //TODO
|
||||
const url = attachment.url();
|
||||
if (url) {
|
||||
insertImage(
|
||||
`${location.protocol}//${location.host}${url}`,
|
||||
);
|
||||
} else {
|
||||
retry++;
|
||||
if (retry < maxTry) {
|
||||
setTimeout(checkUrl, checkItvl);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkUrl();
|
||||
});
|
||||
}
|
||||
const processUpload = function(file) {
|
||||
const uploader = Attachments.insert(
|
||||
{
|
||||
file,
|
||||
meta: Utils.getCommonAttachmentMetaFrom(card),
|
||||
chunkSize: 'dynamic',
|
||||
},
|
||||
false,
|
||||
);
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
if (fileRef.isImage) {
|
||||
const img = document.createElement('img');
|
||||
img.src = fileRef.link();
|
||||
img.setAttribute('width', '100%');
|
||||
$summernote.summernote('insertNode', img);
|
||||
}
|
||||
}
|
||||
});
|
||||
uploader.start();
|
||||
};
|
||||
if (MAX_IMAGE_PIXEL) {
|
||||
const reader = new FileReader();
|
||||
|
|
@ -216,7 +195,7 @@ BlazeComponent.extendComponent({
|
|||
callback(blob) {
|
||||
if (blob !== false) {
|
||||
blob.name = image.name;
|
||||
processData(blob);
|
||||
processUpload(blob);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -224,7 +203,7 @@ BlazeComponent.extendComponent({
|
|||
};
|
||||
reader.readAsDataURL(image);
|
||||
} else {
|
||||
processData(image);
|
||||
processUpload(image);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ template(name="changeAvatarPopup")
|
|||
each uploadedAvatars
|
||||
li: a.js-select-avatar
|
||||
.member
|
||||
img.avatar.avatar-image(src="{{url avatarUrlOptions}}")
|
||||
img.avatar.avatar-image(src="{{link}}?auth=false&brokenIsFine=true")
|
||||
| {{_ 'uploaded-avatar'}}
|
||||
if isSelected
|
||||
i.fa.fa-check
|
||||
|
|
@ -93,7 +93,7 @@ template(name="changeAvatarPopup")
|
|||
unless isSelected
|
||||
a.js-delete-avatar {{_ 'delete'}}
|
||||
| -
|
||||
= original.name
|
||||
= name
|
||||
li: a.js-select-initials
|
||||
.member
|
||||
+userAvatarInitials(userId=currentUser._id)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import Avatars from '/models/avatars';
|
|||
import Users from '/models/users';
|
||||
import Org from '/models/org';
|
||||
import Team from '/models/team';
|
||||
import { formatFleURL } from 'meteor/ostrio:files/lib';
|
||||
|
||||
Template.userAvatar.helpers({
|
||||
userData() {
|
||||
|
|
@ -181,21 +182,14 @@ BlazeComponent.extendComponent({
|
|||
Meteor.subscribe('my-avatars');
|
||||
},
|
||||
|
||||
avatarUrlOptions() {
|
||||
return {
|
||||
auth: false,
|
||||
brokenIsFine: true,
|
||||
};
|
||||
},
|
||||
|
||||
uploadedAvatars() {
|
||||
return Avatars.find({ userId: Meteor.userId() });
|
||||
return Avatars.find({ userId: Meteor.userId() }).each();
|
||||
},
|
||||
|
||||
isSelected() {
|
||||
const userProfile = Meteor.user().profile;
|
||||
const avatarUrl = userProfile && userProfile.avatarUrl;
|
||||
const currentAvatarUrl = this.currentData().url(this.avatarUrlOptions());
|
||||
const currentAvatarUrl = `${this.currentData().link()}?auth=false&brokenIsFine=true`;
|
||||
return avatarUrl === currentAvatarUrl;
|
||||
},
|
||||
|
||||
|
|
@ -220,32 +214,30 @@ BlazeComponent.extendComponent({
|
|||
this.$('.js-upload-avatar-input').click();
|
||||
},
|
||||
'change .js-upload-avatar-input'(event) {
|
||||
let file, fileUrl;
|
||||
|
||||
FS.Utility.eachFile(event, f => {
|
||||
try {
|
||||
file = Avatars.insert(new FS.File(f));
|
||||
fileUrl = file.url(this.avatarUrlOptions());
|
||||
} catch (e) {
|
||||
this.setError('avatar-too-big');
|
||||
}
|
||||
});
|
||||
|
||||
if (fileUrl) {
|
||||
this.setError('');
|
||||
const fetchAvatarInterval = window.setInterval(() => {
|
||||
$.ajax({
|
||||
url: fileUrl,
|
||||
success: () => {
|
||||
this.setAvatar(file.url(this.avatarUrlOptions()));
|
||||
window.clearInterval(fetchAvatarInterval);
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
const self = this;
|
||||
if (event.currentTarget.files && event.currentTarget.files[0]) {
|
||||
const uploader = Avatars.insert(
|
||||
{
|
||||
file: event.currentTarget.files[0],
|
||||
chunkSize: 'dynamic',
|
||||
},
|
||||
false,
|
||||
);
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
self.setAvatar(
|
||||
`${formatFleURL(fileRef)}?auth=false&brokenIsFine=true`,
|
||||
);
|
||||
}
|
||||
});
|
||||
uploader.on('error', (error, fileData) => {
|
||||
self.setError(error.reason);
|
||||
});
|
||||
uploader.start();
|
||||
}
|
||||
},
|
||||
'click .js-select-avatar'() {
|
||||
const avatarUrl = this.currentData().url(this.avatarUrlOptions());
|
||||
const avatarUrl = `${this.currentData().link()}?auth=false&brokenIsFine=true`;
|
||||
this.setAvatar(avatarUrl);
|
||||
},
|
||||
'click .js-select-initials'() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue