Rename image/file on upload ModX 3

When uploading files, the file name is cleaned up. However, a unique ID should also be appended to the file name. Can I set this? This would avoid duplicate file names. FileSluggy no longer works under ModX3.

ModX 3.0.5
PHP 8.2
ContentBlocks 1.13.5

There is an event OnFileManagerBeforeUpload that you could probably use for that in a plugin.

This event runs before the file name is cleaned up though. So you might have to do that in the plugin yourself, before checking if a file with the same name already exists.

Thanks! Is there already a plugin that does this?

I don’t know of any extra.


Here is some plugin code that might do what you want:

<?php
switch ($modx->event->name) {
    case 'OnFileManagerBeforeUpload':
        $name = $file['name'];
        if ((boolean)$this->xpdo->getOption('upload_translit')) {
            // clean up the file name | This might has to be changed for future MODX versions -> https://github.com/modxcms/revolution/pull/16582
            $name = $this->xpdo->filterPathSegment($name);
        }
        $filePath = $directory . $source->sanitizePath($name);
        try {
            if ($source->getFilesystem()->fileExists($filePath)) {
                // Add something unique to the file name
                $fileData = pathinfo($name);
                $fileName = $fileData['filename'];
                $fileExt = $fileData['extension'];
                $newName = $fileName . "-" . uniqid() . "." . $fileExt;
                $f = &$scriptProperties['file'];
                $f['name'] = $newName;
            }
        } catch (Throwable $e) {
            $this->xpdo->log(modX::LOG_LEVEL_ERROR, $e->getMessage());
        }
        break;
}
return;

Thank you very much. The plugin works perfectly when I upload images via the media browser.
When images are uploaded via ContentBlocks, they are renamed correctly and saved in the correct directory (with a unique ID), but I get an error message and the image is not displayed in ContentBlocks.

Error Log:

[2024-07-18 06:14:57] (ERROR @ /.../core/components/tinymcerte/src/Plugins/Events/OnRichTextEditorInit.php : 143) PHP warning: foreach() argument must be of type array|object, null given
[2024-07-18 06:14:59] (ERROR @ /.../core/cache/includes/elements/modx/revolution/modsnippet/20.include.cache.php : 210) PHP warning: Undefined variable $showLog
[2024-07-18 06:15:08] (ERROR @ /.../core/components/contentblocks/processors/content/image/upload.class.php : 98) PHP warning: Undefined array key "content"
[2024-07-18 06:15:08] (ERROR @ /.../core/components/contentblocks/processors/content/image/upload.class.php : 104) PHP warning: Undefined array key "content"
[2024-07-18 06:15:08] (ERROR @ /.../core/components/contentblocks/processors/content/image/upload.class.php : 141) Exception Imagine\Exception\RuntimeException while processing image upload /assets/upload/Acrocalyptica_01.jpg  for crop into imagine: [Imagine\Exception\RuntimeException] An image could not be created from the given input #0 /.../core/components/contentblocks/vendor/imagine/imagine/src/Gd/Imagine.php(122): Imagine\Gd\Imagine->doLoad(NULL, Object(Imagine\Image\Metadata\MetadataBag))
#1 /.../core/components/contentblocks/processors/content/image/upload.class.php(104): Imagine\Gd\Imagine->load(NULL)
#2 /.../core/src/Revolution/Processors/Processor.php(189): ContentBlocksImageUploadProcessor->process()
#3 /.../core/src/Revolution/modX.php(1816): MODX\Revolution\Processors\Processor->run()
#4 /.../core/src/Revolution/modConnectorResponse.php(151): MODX\Revolution\modX->runProcessor('content/image/u...', Array, Array)
#5 /.../core/src/Revolution/modConnectorRequest.php(89): MODX\Revolution\modConnectorResponse->outputContent(Array)
#6 /.../core/src/Revolution/modConnectorRequest.php(77): MODX\Revolution\modConnectorRequest->prepareResponse(Array)
#7 /.../assets/components/contentblocks/connector.php(22): MODX\Revolution\modConnectorRequest->handleRequest(Array)
#8 {main}


It seems that the extra “ContentBlocks” doesn’t know that the file name changed and therefore looks for it using the wrong path.

As far as I can tell, “ContentBlocks” listens to the event OnFileManagerFileRename to detect file name changes. (With these paid extras it’s hard to say exactly, because I don’t have access to the current code.)
So invoking this event in the custom plugin might solve the issue. (Unfortunately I can’t test this myself currently.)


New code with the added part that invokes the event OnFileManagerFileRename:

<?php
switch ($modx->event->name) {
    case 'OnFileManagerBeforeUpload':
        $name = $file['name'];
        if ((boolean)$this->xpdo->getOption('upload_translit')) {
            // clean up the file name | This might has to be changed for future MODX versions -> https://github.com/modxcms/revolution/pull/16582
            $name = $this->xpdo->filterPathSegment($name);
        }
        $filePath = $directory . $source->sanitizePath($name);
        try {
            if ($source->getFilesystem()->fileExists($filePath)) {
                // Add something unique to the file name
                $fileData = pathinfo($name);
                $fileName = $fileData['filename'];
                $fileExt = $fileData['extension'];
                $newName = $fileName . "-" . uniqid() . "." . $fileExt;
                $f = &$scriptProperties['file'];
                $f['name'] = $newName;
                
                // Invoke the event 'OnFileManagerFileRename', so that extras like ContentBlocks know that the file name has changed
                $newPath = $directory . $source->sanitizePath($newName);
                $this->xpdo->invokeEvent('OnFileManagerFileRename', [
                    'path' => $newPath,
                    'source' => &$source,
                ]);
            }
        } catch (Throwable $e) {
            $this->xpdo->log(modX::LOG_LEVEL_ERROR, $e->getMessage());
        }
        break;
}
return;

Great! Now it works! Thank you!