diff --git a/administrator/components/com_media/controllers/file.json.php b/administrator/components/com_media/controllers/file.json.php index 9a5bb546942b3..4ee056f13f180 100644 --- a/administrator/components/com_media/controllers/file.json.php +++ b/administrator/components/com_media/controllers/file.json.php @@ -34,8 +34,9 @@ public function upload() if (!JSession::checkToken('request')) { $response = array( - 'status' => '0', - 'error' => JText::_('JINVALID_TOKEN') + 'status' => '0', + 'message' => JText::_('JINVALID_TOKEN'), + 'error' => JText::_('JINVALID_TOKEN') ); echo json_encode($response); @@ -59,8 +60,9 @@ public function upload() || $_SERVER['CONTENT_LENGTH'] > $mediaHelper->toBytes(ini_get('memory_limit'))) { $response = array( - 'status' => '0', - 'error' => JText::_('COM_MEDIA_ERROR_WARNFILETOOLARGE') + 'status' => '0', + 'message' => JText::_('COM_MEDIA_ERROR_WARNFILETOOLARGE'), + 'error' => JText::_('COM_MEDIA_ERROR_WARNFILETOOLARGE') ); echo json_encode($response); @@ -70,39 +72,41 @@ public function upload() // Set FTP credentials, if given JClientHelper::setCredentialsFromRequest('ftp'); - // Make the filename safe - $file['name'] = JFile::makeSafe($file['name']); - - if (!isset($file['name'])) + if (isset($file['name'])) { - $response = array( - 'status' => '0', - 'error' => JText::_('COM_MEDIA_ERROR_BAD_REQUEST') - ); + // Make the filename safe + $file['name'] = JFile::makeSafe($file['name']); - echo json_encode($response); + // We need a URL safe name + $fileparts = pathinfo(COM_MEDIA_BASE . '/' . $folder . '/' . $file['name']); - return; - } + // Transform filename to punycode + $fileparts['filename'] = JStringPunycode::toPunycode($fileparts['filename']); + $tempExt = (!empty($fileparts['extension'])) ? strtolower($fileparts['extension']) : ''; - // The request is valid - $err = null; + // Transform filename to punycode, then neglect otherthan non-alphanumeric characters & underscores. Also transform extension to lowercase + $safeFileName = preg_replace(array("/[\\s]/", "/[^a-zA-Z0-9_]/"), array("_", ""), $fileparts['filename']) . '.' . $tempExt; - $filepath = JPath::clean(COM_MEDIA_BASE . '/' . $folder . '/' . strtolower($file['name'])); + // Create filepath with safe-filename + $files['final'] = $fileparts['dirname'] . DIRECTORY_SEPARATOR . $safeFileName; + $file['name'] = $safeFileName; - if (!MediaHelper::canUpload($file, $err)) - { - JLog::add('Invalid: ' . $filepath . ': ' . $err, JLog::INFO, 'upload'); + $filepath = JPath::clean($files['final']); - $response = array( - 'status' => '0', - 'error' => JText::_($err) - ); + if (!$mediaHelper->canUpload($file, 'com_media')) + { + JLog::add('Invalid: ' . $filepath, JLog::INFO, 'upload'); - echo json_encode($response); + $response = array( + 'status' => '0', + 'message' => JText::_('COM_MEDIA_ERROR_UNABLE_TO_UPLOAD_FILE'), + 'error' => JText::_('COM_MEDIA_ERROR_UNABLE_TO_UPLOAD_FILE') + ); - return; - } + echo json_encode($response); + + return; + } // Trigger the onContentBeforeSave event. JPluginHelper::importPlugin('content'); @@ -111,77 +115,100 @@ public function upload() $object_file->filepath = $filepath; $result = $dispatcher->trigger('onContentBeforeSave', array('com_media.file', &$object_file, true)); - if (in_array(false, $result, true)) - { - // There are some errors in the plugins - JLog::add('Errors before save: ' . $object_file->filepath . ' : ' . implode(', ', $object_file->getErrors()), JLog::INFO, 'upload'); - - $response = array( - 'status' => '0', - 'error' => JText::plural('COM_MEDIA_ERROR_BEFORE_SAVE', count($errors = $object_file->getErrors()), implode('
', $errors)) - ); - - echo json_encode($response); - - return; + if (in_array(false, $result, true)) + { + // There are some errors in the plugins + JLog::add('Errors before save: ' . $object_file->filepath . ' : ' . implode(', ', $object_file->getErrors()), JLog::INFO, 'upload'); + + $response = array( + 'status' => '0', + 'message' => JText::plural('COM_MEDIA_ERROR_BEFORE_SAVE', count($errors = $object_file->getErrors()), implode('
', $errors)), + 'error' => JText::plural('COM_MEDIA_ERROR_BEFORE_SAVE', count($errors = $object_file->getErrors()), implode('
', $errors)) + ); + + echo json_encode($response); + + return; + } + + if (JFile::exists($object_file->filepath)) + { + // File exists + JLog::add('File exists: ' . $object_file->filepath . ' by user_id ' . $user->id, JLog::INFO, 'upload'); + + $response = array( + 'status' => '0', + 'message' => JText::_('COM_MEDIA_ERROR_FILE_EXISTS'), + 'error' => JText::_('COM_MEDIA_ERROR_FILE_EXISTS'), + 'location' => str_replace(JPATH_ROOT, '', $filepath) + ); + + echo json_encode($response); + + return; + } + elseif (!$user->authorise('core.create', 'com_media')) + { + // File does not exist and user is not authorised to create + JLog::add('Create not permitted: ' . $object_file->filepath . ' by user_id ' . $user->id, JLog::INFO, 'upload'); + + $response = array( + 'status' => '0', + 'error' => JText::_('COM_MEDIA_ERROR_CREATE_NOT_PERMITTED'), + 'message' => JText::_('COM_MEDIA_ERROR_CREATE_NOT_PERMITTED') + ); + + echo json_encode($response); + + return; + } + + if (!JFile::upload($object_file->tmp_name, $object_file->filepath)) + { + // Error in upload + JLog::add('Error on upload: ' . $object_file->filepath, JLog::INFO, 'upload'); + + $response = array( + 'status' => '0', + 'message' => JText::_('COM_MEDIA_ERROR_UNABLE_TO_UPLOAD_FILE'), + 'error' => JText::_('COM_MEDIA_ERROR_UNABLE_TO_UPLOAD_FILE') + ); + + echo json_encode($response); + + return; + } + else + { + // Trigger the onContentAfterSave event. + $dispatcher->trigger('onContentAfterSave', array('com_media.file', &$object_file, true)); + JLog::add($folder, JLog::INFO, 'upload'); + + $returnUrl = str_replace(JPATH_ROOT, '', $object_file->filepath); + + $response = array( + 'status' => '1', + 'message' => JText::sprintf('COM_MEDIA_UPLOAD_COMPLETE', $returnUrl), + 'error' => JText::sprintf('COM_MEDIA_UPLOAD_COMPLETE', $returnUrl), + 'location' => str_replace('\\', '/', $returnUrl) + ); + + echo json_encode($response); + + return; + } } - - if (JFile::exists($object_file->filepath)) + else { - // File exists - JLog::add('File exists: ' . $object_file->filepath . ' by user_id ' . $user->id, JLog::INFO, 'upload'); - $response = array( - 'status' => '0', - 'error' => JText::_('COM_MEDIA_ERROR_FILE_EXISTS') + 'status' => '0', + 'error' => JText::_('COM_MEDIA_ERROR_BAD_REQUEST'), + 'message' => JText::_('COM_MEDIA_ERROR_BAD_REQUEST') ); echo json_encode($response); return; } - - if (!$user->authorise('core.create', 'com_media')) - { - // File does not exist and user is not authorised to create - JLog::add('Create not permitted: ' . $object_file->filepath . ' by user_id ' . $user->id, JLog::INFO, 'upload'); - - $response = array( - 'status' => '0', - 'error' => JText::_('COM_MEDIA_ERROR_CREATE_NOT_PERMITTED') - ); - - echo json_encode($response); - - return; - } - - if (!JFile::upload($object_file->tmp_name, $object_file->filepath)) - { - // Error in upload - JLog::add('Error on upload: ' . $object_file->filepath, JLog::INFO, 'upload'); - - $response = array( - 'status' => '0', - 'error' => JText::_('COM_MEDIA_ERROR_UNABLE_TO_UPLOAD_FILE') - ); - - echo json_encode($response); - - return; - } - - // Trigger the onContentAfterSave event. - $dispatcher->trigger('onContentAfterSave', array('com_media.file', &$object_file, true)); - JLog::add($folder, JLog::INFO, 'upload'); - - $response = array( - 'status' => '1', - 'error' => JText::sprintf('COM_MEDIA_UPLOAD_COMPLETE', substr($object_file->filepath, strlen(COM_MEDIA_BASE))) - ); - - echo json_encode($response); - - return; } } diff --git a/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini b/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini index c1361ace6bf41..33a967877ecc9 100644 --- a/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini +++ b/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini @@ -7,6 +7,7 @@ PLG_EDITORS_TINYMCE="Editor - TinyMCE" PLG_TINY_BUTTON_TOGGLE_EDITOR="Toggle editor" PLG_TINY_ERR_CUSTOMCSSFILENOTPRESENT="The file name %s was entered in the TinyMCE Custom CSS field. This file could not be found in the default template folder. No styles are available." PLG_TINY_ERR_EDITORCSSFILENOTPRESENT="Could not find the file 'editor.css' in the template or templates/system folder. No styles are available." +PLG_TINY_ERR_UNSUPPORTEDBROWSER="Drag and drop image upload is not available for your your browser. Please consider using a fully HTML5 compatible browser" PLG_TINY_FIELD_ADVIMAGE_DESC="Turn on/off a more advanced image dialog." PLG_TINY_FIELD_ADVIMAGE_LABEL="Advanced Image" PLG_TINY_FIELD_ADVLIST_DESC="Turn on/off to enable to set number formats and bullet types in ordered and unordered lists." @@ -27,10 +28,14 @@ PLG_TINY_FIELD_CUSTOMBUTTON_DESC="Add custom button(s)." PLG_TINY_FIELD_CUSTOMBUTTON_LABEL="Custom Button" PLG_TINY_FIELD_CUSTOMPLUGIN_DESC="Add custom plugin(s)." PLG_TINY_FIELD_CUSTOMPLUGIN_LABEL="Custom Plugin" +PLG_TINY_FIELD_CUSTOM_PATH_DESC="Provide a directory for the images to be uploaded. If nothing provided images will be uploaded at /images." +PLG_TINY_FIELD_CUSTOM_PATH_LABEL="Images directory" PLG_TINY_FIELD_DATE_DESC="Show or hide the Insert Date button. Only works in Extended mode." PLG_TINY_FIELD_DATE_LABEL="Insert Date" PLG_TINY_FIELD_DIRECTION_DESC="Choose default text direction." PLG_TINY_FIELD_DIRECTION_LABEL="Text Direction" +PLG_TINY_FIELD_DRAG_DROP_DESC="Enable drag and drop for uploading images" +PLG_TINY_FIELD_DRAG_DROP_LABEL="Images drag&drop" PLG_TINY_FIELD_ELEMENTS_DESC="Allows the addition of specific valid elements to the existing rule set." PLG_TINY_FIELD_ELEMENTS_LABEL="Extended Valid Elements" PLG_TINY_FIELD_ENCODING_DESC="Controls how HTML entities are encoded. Recommended setting is 'raw'. 'named' = used named entity encoding (for example, '<'). 'numeric' = use numeric HTML encoding (for example, '%03c'). raw = Do not encode HTML entities. Note that searching content may not work properly if setting is not 'raw'." diff --git a/media/editors/tinymce/plugins/jdragdrop/plugin.js b/media/editors/tinymce/plugins/jdragdrop/plugin.js new file mode 100644 index 0000000000000..1fa68610bf63a --- /dev/null +++ b/media/editors/tinymce/plugins/jdragdrop/plugin.js @@ -0,0 +1,121 @@ +tinymce.PluginManager.add('jdragdrop', function(editor) { + + // Reset the drop area border + tinyMCE.DOM.bind(document, 'dragleave', function(e) { + e.stopPropagation(); + e.preventDefault(); + tinyMCE.activeEditor.contentAreaContainer.style.borderWidth=''; + + return false; + }); + + // The upload logic + function UploadFile(file) { + var fd = new FormData(); + fd.append('Filedata', file); + fd.append('folder', mediaUploadPath); + + var xhr = new XMLHttpRequest(); + + xhr.upload.onprogress = function(e) { + var percentComplete = (e.loaded / e.total) * 100; + jQuery('.bar').width(percentComplete + '%'); + }; + + removeProgessBar = function(){ + setTimeout(function(){ + jQuery('#jloader').remove(); + editor.contentAreaContainer.style.borderWidth = ''; + }, 200); + }; + + xhr.onload = function() { + var resp = JSON.parse(xhr.responseText); + + if (xhr.status == 200) { + if (resp.status == '0') { + removeProgessBar(); + + tinyMCE.activeEditor.windowManager.alert(resp.message + ': ' + setCustomDir + resp.location); + + } + + if (resp.status == '1') { + removeProgessBar(); + + // Create the image tag + var newNode = tinyMCE.activeEditor.getDoc().createElement ('img'); + newNode.src= setCustomDir + resp.location; + tinyMCE.activeEditor.execCommand('mceInsertContent', false, newNode.outerHTML); + } + } else { + removeProgessBar(); + } + }; + + xhr.onerror = function() { + removeProgessBar(); + }; + + xhr.open("POST", uploadUri, true); + xhr.send(fd); + + } + + // Listers for drag and drop + if (typeof FormData != 'undefined'){ + + // Fix for Chrome + editor.on('dragenter', function(e) { + e.stopPropagation(); + + return false; + }); + + + // Notify user when file is over the drop area + editor.on('dragover', function(e) { + e.preventDefault(); + tinyMCE.activeEditor.contentAreaContainer.style.borderStyle = 'dashed'; + tinyMCE.activeEditor.contentAreaContainer.style.borderWidth = '5px'; + + return false; + }); + + // Logic for the dropped file + editor.on('drop', function(e) { + + // We override only for files + if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) { + for (var i = 0, f; f = e.dataTransfer.files[i]; i++) { + + // Only images allowed + if (f.name.toLowerCase().match(/\.(jpg|jpeg|png|gif)$/)) { + + // Display a spining Joomla! logo + jQuery('.mce-toolbar-grp').append( + '
' + + '
' + + '
' + + '
' + + '
'); + editor.contentAreaContainer.style.borderWidth = ''; + + // Upload the file(s) + UploadFile(f); + } + + e.preventDefault(); + } + } + editor.contentAreaContainer.style.borderWidth = ''; + }); + } else { + Joomla.renderMessages({'error': [Joomla.JText._("PLG_TINY_ERR_UNSUPPORTEDBROWSER")]}); + editor.on('drop', function(e) { + e.preventDefault(); + + return false; + }); + } +}); diff --git a/media/editors/tinymce/plugins/jdragdrop/plugin.min.js b/media/editors/tinymce/plugins/jdragdrop/plugin.min.js new file mode 100644 index 0000000000000..285f2446bf654 --- /dev/null +++ b/media/editors/tinymce/plugins/jdragdrop/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("jdragdrop",function(e){function t(t){var r=new FormData;r.append("Filedata",t),r.append("folder",mediaUploadPath);var o=new XMLHttpRequest;o.upload.onprogress=function(e){var t=e.loaded/e.total*100;jQuery(".bar").width(t+"%")},removeProgessBar=function(){setTimeout(function(){jQuery("#jloader").remove(),e.contentAreaContainer.style.borderWidth=""},200)},o.onload=function(){var e=JSON.parse(o.responseText);if(200==o.status){if("0"==e.status&&(removeProgessBar(),tinyMCE.activeEditor.windowManager.alert(e.message+": "+setCustomDir+e.location)),"1"==e.status){removeProgessBar();var t=tinyMCE.activeEditor.getDoc().createElement("img");t.src=setCustomDir+e.location,tinyMCE.activeEditor.execCommand("mceInsertContent",!1,t.outerHTML)}}else removeProgessBar()},o.onerror=function(){removeProgessBar()},o.open("POST",uploadUri,!0),o.send(r)}tinyMCE.DOM.bind(document,"dragleave",function(e){return e.stopPropagation(),e.preventDefault(),tinyMCE.activeEditor.contentAreaContainer.style.borderWidth="",!1}),"undefined"!=typeof FormData?(e.on("dragenter",function(e){return e.stopPropagation(),!1}),e.on("dragover",function(e){return e.preventDefault(),tinyMCE.activeEditor.contentAreaContainer.style.borderStyle="dashed",tinyMCE.activeEditor.contentAreaContainer.style.borderWidth="5px",!1}),e.on("drop",function(r){if(r.dataTransfer&&r.dataTransfer.files&&r.dataTransfer.files.length>0)for(var o,n=0;o=r.dataTransfer.files[n];n++)o.name.toLowerCase().match(/\.(jpg|jpeg|png|gif)$/)&&(jQuery(".mce-toolbar-grp").append('
'),e.contentAreaContainer.style.borderWidth="",t(o)),r.preventDefault();e.contentAreaContainer.style.borderWidth=""})):(Joomla.renderMessages({error:[Joomla.JText._("PLG_TINY_ERR_UNSUPPORTEDBROWSER")]}),e.on("drop",function(e){return e.preventDefault(),!1}))}); \ No newline at end of file diff --git a/plugins/editors/tinymce/tinymce.php b/plugins/editors/tinymce/tinymce.php index c9751082b544c..7e41b9734a58a 100644 --- a/plugins/editors/tinymce/tinymce.php +++ b/plugins/editors/tinymce/tinymce.php @@ -616,7 +616,55 @@ public function onInit() $btnsNames = $buttons['names']; $tinyBtns = $buttons['script']; - // Prepare config variables + // Drag and drop Images + $allowImgPaste = "false"; + $dragDropPlg = ''; + $dragdrop = $this->params->get('drag_drop', 1); + $user = JFactory::getUser(); + + if ($dragdrop && $user->authorise('core.create', 'com_media')) + { + $allowImgPaste = "true"; + $isSubDir = ''; + $session = JFactory::getSession(); + $uploadUrl = JUri::base() . 'index.php?option=com_media&task=file.upload&tmpl=component&' + . $session->getName() . '=' . $session->getId() + . '&' . JSession::getFormToken() . '=1' + . '&asset=image&format=json'; + + if (JFactory::getApplication()->isSite()) + { + $uploadUrl = htmlentities($uploadUrl, null, 'UTF-8', null); + } + + // Is Joomla installed in subdirectory + if (JUri::root(true) != '/') + { + $isSubDir = JUri::root(true); + } + + // Get specific path + $tempPath = $this->params->get('path', ''); + + if (!empty($tempPath)) + { + $tempPath = rtrim($tempPath, '/'); + $tempPath = ltrim($tempPath, '/'); + } + + $dragDropPlg = 'jdragdrop'; + + JText::script('PLG_TINY_ERR_UNSUPPORTEDBROWSER'); + JFactory::getDocument()->addScriptDeclaration( + " + var setCustomDir = '" . $isSubDir . "'; + var mediaUploadPath = '" . $tempPath . "'; + var uploadUri = '" . $uploadUrl . "'; + " + ); + } + + // Prepare config variables $plugins = implode(',', $plugins); $elements = implode(',', $elements); @@ -671,7 +719,7 @@ public function onInit() menubar: false, toolbar1: \"bold italics underline strikethrough | undo redo | bullist numlist\", toolbar2: \"$toolbar5 | code\", - plugins: \"code\", + plugins: \"$dragDropPlg code\", // Cleanup/Output inline_styles : true, gecko_spellcheck : true, @@ -686,7 +734,8 @@ public function onInit() document_base_url : \"" . JUri::root() . "\", setup: function (editor) { $tinyBtns - } + }, + paste_data_images: $allowImgPaste }); " ); @@ -718,7 +767,7 @@ public function onInit() $smallButtons invalid_elements : \"$invalid_elements\", // Plugins - plugins : \"table link image code hr charmap autolink lists importcss\", + plugins : \"table link image code hr charmap autolink lists importcss $dragDropPlg\", // Toolbar toolbar1: \"$toolbar1\", toolbar2: \"$toolbar2\", @@ -737,7 +786,8 @@ public function onInit() width : \"$html_width\", setup: function (editor) { $tinyBtns - } + }, + paste_data_images: $allowImgPaste }); " ); @@ -766,7 +816,7 @@ public function onInit() $smallButtons invalid_elements : \"$invalid_elements\", // Plugins - plugins : \"$plugins\", + plugins : \"$plugins $dragDropPlg\", // Toolbar toolbar1: \"$toolbar1\", toolbar2: \"$toolbar2\", @@ -805,7 +855,8 @@ public function onInit() width : \"$html_width\", setup: function (editor) { $tinyBtns - } + }, + paste_data_images: $allowImgPaste }); " ); @@ -962,11 +1013,11 @@ private function tinyButtons() if ($button->get('name')) { // Set some vars - $name = str_replace(" ", "", $button->get('text')); - $title = $button->get('text'); - $onclick = ($button->get('onclick')) ? $button->get('onclick') : null; - $options = $button->get('options'); - $icon = $button->get('name'); + $name = str_replace(" ", "", $button->get('text')); + $title = $button->get('text'); + $onclick = ($button->get('onclick')) ? $button->get('onclick') : null; + $options = $button->get('options'); + $icon = $button->get('name'); if ($button->get('link') != "#") { @@ -1040,7 +1091,7 @@ private function tinyButtons() } return array( - 'names' => $btnsNames, + 'names' => $btnsNames, 'script' => $tinyBtns ); } diff --git a/plugins/editors/tinymce/tinymce.xml b/plugins/editors/tinymce/tinymce.xml index b8ec85ccfe323..829cf279dbaf8 100644 --- a/plugins/editors/tinymce/tinymce.xml +++ b/plugins/editors/tinymce/tinymce.xml @@ -56,6 +56,22 @@ + + + + + +