/** @module hooks/uploader */
import ReactDOM from 'react-dom';
import { hooks, findDeep, dataUriToBlob, ajax, i18n } from '../util';
import wp from 'wp';
import $ from 'jquery';
import store, { TreeNode } from '../store';
import { latestQueriedFolder } from '../AppTree';
import { message } from 'react-aiot';
import UploadMessage from '../components/UploadMessage';
import { Provider } from 'mobx-react';
import { BROWSER_SELECTOR } from '../others/mediaViews';
import rmlOpts from 'rmlopts';
const UniqueUploadMessage = <Provider store={ store }><UploadMessage /></Provider>,
CLASS_NAME = 'ant-message-bottom';
let uploaderFetchCountsTimeout, currentMessageHide;
/**
* Show the uploading message.
*/
function showMessage() {
currentMessageHide = message.loading(UniqueUploadMessage, 0);
}
/**
* Hide the uploading message.
*/
function hideMessage() {
currentMessageHide && currentMessageHide();
}
/**
* Toggle the placement of the unique uploader message.
*/
function togglePlacement() {
$(this).parents('.ant-message').toggleClass(CLASS_NAME);
setTimeout(() => $(document).one('mouseenter', '.rml-upload', togglePlacement), 10);
}
/**
* Get the current selected node id.
*
* @returns {object}
*/
function getNodeId() {
const selectVal = $(".attachments-filter-preUploadUi:visible:first"),
value = selectVal.val();
if (value) {
const option = selectVal.find('option[value="' + value + '"]'),
{ type, name } = option.data(),
id = parseInt(value, 10);
return store.getTreeItemById(value, false) || TreeNode.create({
id, title: name, properties: { type },
isQueried: false
});
}else{
return latestQueriedFolder.node;
}
}
/**
* When a file is added do general checks.
*/
hooks.register('uploader/add', function(file, node) {
if (node.id === 'all') {
this.node = store.getTreeItemById(+rmlOpts.rootId, false);
}
});
/**
* The media-new.php page. Adds the property to the asyn-upload.php file and
* modifies the output row while uploading a new file.
*
* @see wp-includes/js/plupload/handlers.js
*/
hooks.register('general', () => {
if (!$('body').hasClass('media-new-php')) {
return;
}
// When the file is uploaded, then the original filename is overwritten. Now we
// must add it again to the row after the filename.
if (window.prepareMediaItemInit) {
const copyPrepareMediaItemInit = window.prepareMediaItemInit;
window.prepareMediaItemInit = function(file) {
copyPrepareMediaItemInit.apply(this, arguments);
if (file.rmlFolderHTML) {
const mediaRowFilename = $('#media-item-' + file.id).find(".filename");
if (mediaRowFilename.size()) {
mediaRowFilename.after(file.rmlFolderHTML);
}
}
};
}
// Add event to the uploader so the parameter for the folder id is sent
setTimeout(() => window.uploader && window.uploader.bind('BeforeUpload', function(up, file) {
const rmlFolderNode = getNodeId();
// Set server-side-readable rmlfolder id
if (rmlFolderNode && !isNaN(+rmlFolderNode.id)) {
up.settings.multipart_params.rmlFolder = rmlFolderNode.id;
// Get title as string
const div = document.createElement('div');
let { title } = rmlFolderNode;
typeof title === 'string' ? div.innerText = title : ReactDOM.render(title, div);
title = div.innerText;
const mediaRowFilename = $('#media-item-' + file.id).find('.filename');
if (mediaRowFilename.length > 0) {
file.rmlFolderHTML = '<div class="media-item-rml-folder">' + title + '</div>';
mediaRowFilename.after(file.rmlFolderHTML);
}
}
}), 500);
});
/**
* Load data to a dropdown or show label that the folder is inherited from the AppTree.
* This RFC is placed in the upload UI where you can select your files.
*/
hooks.register('wprfc/preUploadUi', async function(data) {
const attachmentsBrowser = $(this).parents('.attachments-browser');
if (attachmentsBrowser.size()) {
$(this).parent().hide().prev().html(rmlOpts.lang.uploaderUsesLeftTree);
}else{
const { html } = await ajax('tree/dropdown');
$(this).addClass('attachments-filter-preUploadUi').html(html);
}
});
/**
* The default backbone uploader.
*/
hooks.register('general', () => {
if (!findDeep(window, 'wp.media') || !findDeep(window, 'wp.Uploader')) {
return;
}
$(document).one('mouseenter', '.rml-upload', togglePlacement);
// Initialize
const oldP = wp.Uploader.prototype, oldInit = oldP.init, oldSucess = oldP.success;
oldP.init = function() {
oldInit.apply(this, arguments);
/**
* The uploader gets initialized.
*
* @event module:util/hooks#uploader/init
* @this wp.Uploader
*/
hooks.call('uploader/init', [], this);
// Bind the last selected node to the uploaded file
this.uploader.bind('FileFiltered', function(up, file) {
file.rmlFolderNode = getNodeId();
});
// Remove files from queue if upload is a folder
/*this.uploader.bind('FilesAdded', function(up, files) {
files.forEach(file => {
console.log(file.getSource().relativePath);
up.removeFile(file);
});
console.log(files);
console.log(up);
// TODO: Disable upload until here and show dialog with folder preview
}, undefined, 10);*/
// A new file is added, add it to the store so it can be rendered
this.uploader.bind('FilesAdded', function(up, files) {
showMessage();
files.forEach(file => {
const { attachment: { cid }, name, percent, loaded, size, rmlFolderNode } = file,
previewObj = {
cid, name, percent, loaded, size,
node: rmlFolderNode
};
/**
* A new file is added.
*
* @event module:util/hooks#uploader/add
* @param {object} file The file
* @param {module:store/TreeNode~TreeNode} folder The folder node
* @param {module:store~Store} store The store
* @this object
*/
hooks.call('uploader/add', [ file, rmlFolderNode, store ], previewObj);
const upload = file.rmlUpload = store.addUploading(previewObj);
// Generate preview url
const preloader = new window.mOxie.Image();
preloader.onload = () => {
preloader.downsize(89, 89);
let finalUrl;
try {
finalUrl = preloader.getAsDataURL();
finalUrl = dataUriToBlob(finalUrl);
finalUrl = window.URL.createObjectURL(finalUrl);
finalUrl && upload.setter(u => u.previewSrc = finalUrl);
}catch (e) {
// Silence is golden.
}
};
preloader.load(file.getSource());
});
});
// Set server-side-readable RML folder id
this.uploader.bind('BeforeUpload', function(up, file) {
const { multipart_params } = up.settings;
let { rmlFolderNode } = file;
!rmlFolderNode && (rmlFolderNode = getNodeId()); // Lazy node
if (rmlFolderNode && !isNaN(+rmlFolderNode.id)) {
multipart_params.rmlFolder = rmlFolderNode.id;
}
});
// The upload progress
this.uploader.bind('UploadProgress', function({ total }, { rmlUpload, percent, loaded }) {
rmlUpload.setter(u => {
u.percent = percent;
u.loaded = loaded;
});
store.setUploadTotal(total);
});
// All files are completed
this.uploader.bind('UploadComplete', function(up, files) {
// Update queue and counter
up.splice();
up.total.reset();
clearTimeout(uploaderFetchCountsTimeout);
uploaderFetchCountsTimeout = setTimeout(() => store.fetchCounts(), 500); // Avoid too many requests
// Hide uploader message
hideMessage();
});
};
/**
* A single file is completed successfully.
*/
oldP.success = function(file_attachment) {
oldSucess.apply(this, arguments);
// Remove file from queue
const upload = store.removeUploading(file_attachment.cid),
uploadId = upload.node;
store.addFoldersNeedsRefresh(uploadId);
// Update all available backbone view
const rmlGalleryOrder = file_attachment.get('rmlGalleryOrder'),
at = rmlGalleryOrder === -1 ? 0 : rmlGalleryOrder;
$(BROWSER_SELECTOR).each(function() {
const backboneView = $(this).data('backboneView');
if (backboneView) {
const { $RmlAppTree } = backboneView.controller,
activeNode = $RmlAppTree.getTreeItemById(undefined, false);
if (uploadId === activeNode.id || activeNode.id === 'all') {
backboneView.collection.add(file_attachment, { at: activeNode.id === 'all' ? 0 : at });
}
}
});
};
});
const GALLERY_ALLOWED_EXT = ['jpg', 'jpeg', 'jpe', 'gif', 'png'];
/**
* Checks, if the uploading folder is a collection or gallery and restrict the upload,
* move the file to unorganized folder.
*/
hooks.register('uploader/add', function({ name }, { properties }, store) {
// May only contain image files
if (properties && properties.type) {
const ext = name.substr(name.lastIndexOf('.') + 1).toLowerCase(),
isCollection = +properties.type === 1;
if ($.inArray(ext, GALLERY_ALLOWED_EXT) === -1 || isCollection) {
this.node = store.getTreeItemById(+rmlOpts.rootId, false);
this.deny = i18n(isCollection ? 'uploadingCollection' : 'uploadingGallery');
}
}
});