Hi, i am currently hashing original file names of image uploads by using File (Field) Paths and File Hash in some fields, and pushing them into the private filesystem, which works well.  e.g.:

[random:hash:tiger128,3].[file:ffp-extension-original]
Path: useruploads
Upload destination: private files
 

But only for file upload fields. 

Using the TinyMCE editor however (drag & drop etc.) stores the file in a custom folder, but retains its original filename without hashing. 

Is there any input filter around or should the TinyMCE Uploader manually be hooked to be processed by the File Hash or Path module(s)?

Most helpful answers

Is there any input filter around or should the TinyMCE Uploader manually be hooked to be processed by the File Hash or Path module(s)?

I'm not aware of any input filter - the src must point to the actual path, otherwise images won't display.

I'm also not aware of any way to define a hash pattern for drag and drop images. In no WYSIWYG editor I know.

What you could try is playing with the TinyMCE option images_reuse_filename via alter hook (set it to false).

The API provides hook_tinymce_options_alter for all sorts of manipulations.

But then you'd still have no control over the actual pattern - the editor uses its own, something like mceclip0 (1, 2, 3)... Backdrop File will take care of uniqueness, but you'll end up with names like "mceclip7_99.jpg".

Another option would be to prevent drag-and-drop upload via paste_block_drop option. (I belief, people could still paste then, though.)

If you really absolutely need a hash in the form you described, and you have to provide drag-and-drop upload, you'd probably have to replace tinymce-uploader.js with something custom (hook_library_alter).

Comments

indigoxela's picture

Is there any input filter around or should the TinyMCE Uploader manually be hooked to be processed by the File Hash or Path module(s)?

I'm not aware of any input filter - the src must point to the actual path, otherwise images won't display.

I'm also not aware of any way to define a hash pattern for drag and drop images. In no WYSIWYG editor I know.

What you could try is playing with the TinyMCE option images_reuse_filename via alter hook (set it to false).

The API provides hook_tinymce_options_alter for all sorts of manipulations.

But then you'd still have no control over the actual pattern - the editor uses its own, something like mceclip0 (1, 2, 3)... Backdrop File will take care of uniqueness, but you'll end up with names like "mceclip7_99.jpg".

Another option would be to prevent drag-and-drop upload via paste_block_drop option. (I belief, people could still paste then, though.)

If you really absolutely need a hash in the form you described, and you have to provide drag-and-drop upload, you'd probably have to replace tinymce-uploader.js with something custom (hook_library_alter).

This was tricky, so i tried to stay minimal and try to snatch the very first procedure after file upload and inject rehashing there. The file is tinymce.pages.inc under the function tinymce_image_upload

To my surprise, the upload and rehashing works without any complications right now. 

Example file for public uploads (uploaded image address):

/files/uploads/b22e3dfbdb3e413688e17f6aa69729ab.jpg

This is a very alternative version (although File Hash < - > Tinymce support would be more handy), but the drag & dropped and inline images seem to get hashed with MD5 now, which is fair enough.

This is the new full tinymce.pages.inc (see triple star "***" comments):

------


<?php
/**
* @file
* Menu callbacks for TinyMCE module.
*/

/**
* Menu callback; Saves images uploaded via copy/paste into the editor.
*
* @param stdClass $format
*   Filter format.
*
* @return array|void
*/
function tinymce_image_upload($format) {
 $upload_settings = $format->editor_settings['image_upload'];
 if (!$upload_settings['status']) {
   backdrop_add_http_header('Status', '403 Forbidden');
   return;
 }

 // Odd structure needed for file_save_upload().
 if (isset($_FILES['file'])) {
   $_FILES['files'] = array();
   foreach ($_FILES['file'] as $key => $value) {
     $_FILES['files'][$key]['upload'] = $value;
   }
 }

 $destination = $upload_settings['scheme'] . '://' .  $upload_settings['directory'];
 $extensions = image_get_supported_extensions();
 $validators = array(
   'file_validate_extensions' => array(implode(' ', $extensions)),
   'tinymce_validate_is_image' => array(),
 );
 if ($upload_settings['max_size']) {
   $validators['file_validate_size'] = array(parse_size($upload_settings['max_size']));
 }
 if ($upload_settings['max_dimensions']['width']) {
   $validators['file_validate_image_resolution'] = array(
     $upload_settings['max_dimensions']['width'] . 'x' . $upload_settings['max_dimensions']['height'],
   );
 }

 file_prepare_directory($destination, FILE_CREATE_DIRECTORY);
 $file = file_save_upload('upload', $validators, $destination);
 if ($file) {
   // *** Generate MD5 hash of the file content ***
   $md5_hash = md5_file($file->uri);
   
   // Get original file extension
   $path_info = pathinfo($file->filename);
   $extension = isset($path_info['extension']) ? '.' . $path_info['extension'] : '';
   
   // *** Create new filename with MD5 hash *** 
   $new_filename = $md5_hash . $extension;
   
   // Get the full destination path for the new file
   $new_destination = $destination . '/' . $new_filename;
   
   // *** Rename the file to use MD5 hash ***
   if (file_move($file, $new_destination)) {
     // Update file object with new filename
     $file->filename = $new_filename;
     $file->uri = $new_destination;
     
     // Try to make a local path if possible for better portability.
     $absolute_path = parse_url($GLOBALS['base_url'], PHP_URL_PATH) . '/';
     $url = file_create_url($file->uri);
     $url = str_replace($GLOBALS['base_url'] . '/', $absolute_path, $url);
     $image_info = image_get_info($file->uri);
     $response = array(
       'uploaded' => 1,
       'location' => $url,
       'fileId' => $file->fid,
       'width' => !empty($image_info['width']) ? $image_info['width'] : '',
       'height' => !empty($image_info['height']) ? $image_info['height'] : '',
     );
   }
   else {
     // If we can't rename, use the original file
     $absolute_path = parse_url($GLOBALS['base_url'], PHP_URL_PATH) . '/';
     $url = file_create_url($file->uri);
     $url = str_replace($GLOBALS['base_url'] . '/', $absolute_path, $url);
     $image_info = image_get_info($file->uri);
     $response = array(
       'uploaded' => 1,
       'location' => $url,
       'fileId' => $file->fid,
       'width' => !empty($image_info['width']) ? $image_info['width'] : '',
       'height' => !empty($image_info['height']) ? $image_info['height'] : '',
     );
   }
 }
 else {
   $response = array('uploaded' => 0);
 }

 // file_save_upload() sets messages via backdrop_set_message(). Pull the
 // responses out and display via TinyMCE's notification system.
 $messages = backdrop_get_messages();
 $message_string = '';
 foreach ($messages as $type) {
   $message_string .= $type[0] . ' ';
 }
 if ($message_string) {
   $response['errors'] = strip_tags($message_string);
 }

 return $response;
}

/**
* Custom validation callback for image upload.
*
* Currently file_validate_is_image() fails for newly uploaded files, as the uri
* is something like "/tmp/phpAbCde" - without file extension.
* @see https://github.com/backdrop-backdrop-issues/issues/6497
*/
function tinymce_validate_is_image(File $file) {
 $errors = array();

 // SVG have their own validator on upload (file_validate_svg).
 // If SVG aren't supported, yet, TinyMCE would refuse to upload in the first
 // place and even if upload was attempted, the server-side extension based
 // validation would fail.
 if ($file->filemime == 'image/svg+xml') {
   return $errors;
 }
 // Validate that the image toolkit can handle this raster image.
 if (!$info = image_get_info($file->uri)) {
   $errors[] = t('The image appears to be broken.');
 }

 return $errors;
}

------

(In Text editors and formats,  i had to turn off "Inline Image Styles" for the inline button to match drag & drop file insertion). Images are now full size in the editor however.

This implementation is somewhat MacGyver and will hash literally all files going through TinyMCE,  but it's essentially doing its thing. 

You can try the above file somewhere, what do you think? I could safely replace this file on my running site. 

indigoxela's picture

You can try the above file somewhere, what do you think? I could safely replace this file on my running site. 

This is indeed "MacGyver" ;-)

You don't have to replace the file in the module - which would hinder updates. (The editor module receives an update roughly once per month.) I belief, you can use hook_menu_alter to replace the callback with a custom one.

Not sure, how familiar you are with Backdrop's hook system.

What I had in mind initially was a smaller change in JS, but the PHP approach seems to work for you, so... ;-)

although File Hash < - > Tinymce support would be more handy

Sure, would be. You could try to open an issue in the TinyMCE module's issue queue. I have no idea, how the File Hash module works, so can't promise anything.

The way you've done it now, isn't generic and not backwards compatible. It just fixes one problem for one site. I couldn't use it as-is in the module. But with a custom module implementing hook_menu_alter you should - at least - be able to do this in a way, that's easier to maintain for you.

Hi, i understand the updating obstacle - not worth mentioning - hence it is a wacky (but working) patch until i know what to do. :-)

One of my biggest wishes is to understand the hook system during this steep learning curve, so i can help to contribute. My brain is still not braining trying to learn it. A lot of front-end stuff is going on, regarding the look & feel of various sites.

I could request hashing support, but that's the lazy way - I would appreciate it if you as an active maintainer would 'officially' add a hashing support - i think it's legitimate to conceal original file names for privacy or aesthetic reasons. (Maybe not by default, but as a boolean option, if that makes sense).

I now have a PC build that runs offline coder LLM's quite quickly, using the Continue extension in VSCode, so i am currently feeding every documentation possible into it, including Contrib Examples,  a Sample Module, and get VSCode burning hopefully with a formula for clean module implementations with high accuracy. 

 

indigoxela's picture

I could request hashing support, but that's the lazy way

🤣

I would appreciate it if you as an active maintainer would 'officially' add a hashing support...

You probably understand, that I can't invest so much spare time for something only one person asks for. Hashing, or let's call it image name obfuscation seems interesting and feasible, but also requires some effort.

Again, I'd recommend to look at hook_menu_alter - if you stick with your current approach. And have a look, how the tinymce module defines the callback here.

You'd basically only have to override "page callback" and "file" in your own (custom) module. Didn't actually test, though.

No problem, let's see how it evolves. 

I'd be happy to provide a suggested version if i could. 

I will keep that in mind and try learn this hook chain hopefully very soon. I will return for updates (if there are any). 

Thank you!