Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented user media manager #262

Merged
merged 8 commits into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

return [
(new Extend\Routes('api'))
->get('/fof/uploads', 'fof-upload.list', Api\Controllers\ListUploadsController::class)
->post('/fof/upload', 'fof-upload.upload', Api\Controllers\UploadController::class)
->post('/fof/watermark', 'fof-upload.watermark', Api\Controllers\WatermarkUploadController::class)
->get('/fof/download/{uuid}/{post}/{csrf}', 'fof-upload.download', Api\Controllers\DownloadController::class),
Expand All @@ -30,6 +31,8 @@
(new Extend\Frontend('forum'))
->css(__DIR__.'/resources/less/forum/download.less')
->css(__DIR__.'/resources/less/forum/upload.less')
->css(__DIR__.'/resources/less/forum/fileManagerModal.less')
->css(__DIR__.'/resources/less/forum/fileList.less')
->js(__DIR__.'/js/dist/forum.js'),
new Extend\Locales(__DIR__.'/resources/locale'),

Expand Down
707 changes: 706 additions & 1 deletion js/dist/admin.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/admin.js.map

Large diffs are not rendered by default.

1,479 changes: 1,478 additions & 1 deletion js/dist/forum.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/forum.js.map

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions js/src/common/fileToBBcode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export default function fileToBBcode(file) {
switch (file.tag()) {
// File
case 'file':
return `[upl-file uuid=${file.uuid()} size=${file.humanSize()}]${file.baseName()}[/upl-file]`;

// Image template
case 'image':
return `[upl-image uuid=${file.uuid()} size=${file.humanSize()} url=${file.url()}]${file.baseName()}[/upl-image]`;

// Image preview
case 'image-preview':
return `[upl-image-preview url=${file.url()}]`;

// 'just-url' or unknown
default:
return file.url();
}
}
41 changes: 41 additions & 0 deletions js/src/common/mimeToIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const image = ['image/png', 'image/jpg', 'image/jpeg', 'image/svg+xml'];
const archive = ['application/zip', 'application/x-7z-compressed', 'application/gzip', 'application/vnd.rar', 'application/x-rar-compressed'];
const code = ['text/html', 'text/css', 'text/javascript', 'application/json', 'application/ld+json', 'text/javascript', 'application/x-httpd-php'];
const word = ['application/x-abiword', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];

export default function mimeToIcon(fileType) {
// Display image (do not display for)
if (image.indexOf(fileType) >= 0) {
return 'image';
}
// Display image icon for other types
else if (fileType.includes('image/')) {
return 'far fa-file-image';
}
// Video icon
else if (fileType.includes('video/')) {
return 'far fa-file-video';
}
// Archive icon
else if (archive.indexOf(fileType) >= 0) {
return 'far fa-file-archive';
}
// PDF icon
else if (fileType === 'application/pdf') {
return 'far fa-file-pdf';
}
// Word
else if (word.indexOf(fileType) >= 0) {
return 'far fa-file-word';
}
// Audio icon
else if (fileType.includes('audio/')) {
return 'far fa-file-audio';
}
// Code files
else if (code.indexOf(fileType) >= 0) {
return 'far fa-file-code';
}

return 'far fa-file';
}
29 changes: 29 additions & 0 deletions js/src/common/models/File.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Model from 'flarum/Model';
import mixin from 'flarum/utils/mixin';
import fileToBBcode from '../fileToBBcode';

export default class File extends mixin(Model, {
baseName: Model.attribute('baseName'),
path: Model.attribute('path'),
url: Model.attribute('url'),
type: Model.attribute('type'),
size: Model.attribute('size'),
humanSize: Model.attribute('humanSize'),
createdAt: Model.attribute('createdAt'),
uuid: Model.attribute('uuid'),
tag: Model.attribute('tag'),
}) {
/**
* Use FoF Uploads endpoint
*/
apiEndpoint() {
return '/fof/uploads' + (this.exists ? '/' + this.data.id : '');
}

/**
* Generate bbcode for this file
*/
bbcode() {
return fileToBBcode(this);
}
}
16 changes: 14 additions & 2 deletions js/src/forum/addUploadButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import UploadButton from './components/UploadButton';
import DragAndDrop from './components/DragAndDrop';
import PasteClipboard from './components/PasteClipboard';
import Uploader from './handler/Uploader';
import FileManagerButton from './components/FileManagerButton';

export default function () {
extend(TextEditor.prototype, 'oninit', function () {
Expand All @@ -13,6 +14,15 @@ export default function () {
extend(TextEditor.prototype, 'controlItems', function (items) {
if (!app.forum.attribute('fof-upload.canUpload')) return;

// Add media button
items.add(
'fof-upload-media',
FileManagerButton.component({
uploader: this.uploader,
})
);

// Add upload button
items.add(
'fof-upload',
UploadButton.component({
Expand All @@ -24,8 +34,10 @@ export default function () {
extend(TextEditor.prototype, 'oncreate', function (f_, vnode) {
if (!app.forum.attribute('fof-upload.canUpload')) return;

this.uploader.on('success', (image) => {
this.attrs.composer.editor.insertAtCursor(image + '\n');
this.uploader.on('success', ({ file, addBBcode }) => {
if (!addBBcode) return;

this.attrs.composer.editor.insertAtCursor(file.bbcode() + '\n');

// We wrap this in a typeof check to prevent it running when a user
// is creating a new discussion. There's nothing to preview in a new
Expand Down
12 changes: 2 additions & 10 deletions js/src/forum/components/DragAndDrop.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ export default class DragAndDrop {
this.composerElement.addEventListener('dragend', this.handlers.out);

this.composerElement.addEventListener('drop', (this.handlers.dropping = this.dropping.bind(this)));

this.isDropping = this.over = false;
}

supportsFileDragging() {
Expand Down Expand Up @@ -88,14 +86,8 @@ export default class DragAndDrop {

event.preventDefault();

if (!this.isDropping) {
this.isDropping = true;
this.composerElement.classList.add('fof-upload-dropping');
this.upload(event.dataTransfer.files);

this.upload(event.dataTransfer.files, () => {
this.composerElement.classList.remove('fof-upload-dropping');
this.isDropping = false;
});
}
this.composerElement.classList.remove('fof-upload-dragging');
}
}
41 changes: 41 additions & 0 deletions js/src/forum/components/FileManagerButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import app from 'flarum/app';
import Component from 'flarum/Component';
import Button from 'flarum/components/Button';
import FileManagerModal from './FileManagerModal';

export default class FileManagerButton extends Component {
view() {
return Button.component({
className: 'Button fof-upload-button Button--icon',
onclick: this.fileManagerButtonClicked.bind(this),
icon: 'fas fa-photo-video',
title: app.translator.trans('fof-upload.forum.buttons.media'),
});
}

/**
* Show tooltip on hover
*
* @param {*} vnode
*/
oncreate(vnode) {
super.oncreate(vnode);

// Add tooltip
this.$().tooltip();
}

/**
* Event handler for upload button being clicked
*
* @param {PointerEvent} e
*/
fileManagerButtonClicked(e) {
e.preventDefault();

// Open dialog
app.modal.show(FileManagerModal, {
uploader: this.attrs.uploader
});
}
}
163 changes: 163 additions & 0 deletions js/src/forum/components/FileManagerModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import Modal from 'flarum/components/Modal';
import Button from 'flarum/components/Button';
import UploadButton from './UploadButton';
import UserFileList from './UserFileList';
import DragAndDrop from './DragAndDrop';

export default class FileManagerModal extends Modal {
oninit(vnode) {
super.oninit(vnode);

// Initialize upload managers
this.uploader = vnode.attrs.uploader;

// Current selected files
this.selectedFiles = [];

// Allow multiselect
this.multiSelect = vnode.attrs.multiSelect || true;

// Restrict file selection to specific types
this.restrictFileType = vnode.attrs.restrictFileType || null;

// Drag & drop
this.dragDrop = null;

// Initialize uploads
this.onUpload();
}

className() {
return 'Modal--large fof-file-manager-modal';
}

/**
* Initialize drag & drop
*/
oncreate(vnode) {
super.oncreate(vnode);

this.dragDrop = new DragAndDrop((files) => this.uploader.upload(files, false), this.$().find('.Modal-content')[0]);
}

/**
* Remove events from modal content
*/
onremove() {
if (this.dragDrop) {
this.dragDrop.unload();
}
}

view() {
return (
<div className={'Modal modal-dialog ' + this.className()}>
<div className="Modal-content">
<div className="fof-modal-buttons App-backControl">
{UploadButton.component({
uploader: this.uploader,
disabled: app.fileListState.isLoading(),
isMediaUploadButton: true,
})}
</div>

<div className={'fof-drag-and-drop'}>
jaspervriends marked this conversation as resolved.
Show resolved Hide resolved
<div className={'fof-drag-and-drop-release'}>
<i className={'fas fa-cloud-upload-alt'}></i>

{app.translator.trans('fof-upload.forum.file_list.release_to_upload')}
</div>
</div>

<div className="Modal-header">
<h3 className="App-titleControl App-titleControl--text">{app.translator.trans('fof-upload.forum.media_manager')}</h3>
</div>

{this.alertAttrs ? <div className="Modal-alert">{Alert.component(this.alertAttrs)}</div> : ''}

<div className={'Modal-body'}>
{UserFileList.component({
user: this.attrs.user,
selectable: true,
onFileSelect: this.onFileSelect.bind(this),
selectedFiles: this.selectedFiles,
restrictFileType: this.restrictFileType
})}
</div>

<div className={'Modal-footer'}>
{Button.component(
{
onclick: this.hide.bind(this),
className: 'Button',
},
app.translator.trans('fof-upload.forum.buttons.cancel')
)}

{Button.component(
{
onclick: this.onSelect.bind(this),
disabled: this.selectedFiles.length === 0 || (!this.multiSelect && this.selectedFiles.length > 1),
className: 'Button Button--primary',
},
app.translator.transChoice('fof-upload.forum.buttons.select_file', this.selectedFiles.length)
)}
</div>
</div>
</div>
);
}

/**
* Add or remove file from selected files
*
* @param {File} file
*/
onFileSelect(file) {
const itemPosition = this.selectedFiles.indexOf(file.id());

if (itemPosition >= 0) {
this.selectedFiles.splice(itemPosition, 1);
} else {
if (this.multiSelect) {
this.selectedFiles.push(file.id());
} else {
this.selectedFiles = [file.id()];
}
}
}

/**
* Add files to file list after upload
*/
onUpload() {
this.uploader.on('success', ({ file }) => {
if (this.multiSelect) {
this.selectedFiles.push(file.id());
} else {
this.selectedFiles = [file.id()];
}
});
}

/**
* Add selected files to the composer
*/
onSelect() {
this.hide();

// Custom callback
if (this.attrs.onSelect) {
this.attrs.onSelect(this.selectedFiles);

return;
}

// Add selected files to composer
this.selectedFiles.map((fileId) => {
const file = app.store.getById('files', fileId);

app.composer.editor.insertAtCursor(file.bbcode() + '\n');
});
}
}
Loading