1
public function init()
{
    craft()->on('assets.onBeforeSaveAsset', function(Event $event) {
        $asset = $event->params['asset'];
        $asset->setAttribute('filename', 'new name');
        craft()->assets->storeFile($asset);
    }
    );
}

Here is the code I use in the primary plugin class. This is just a test as i'm learning. I know it should better be in service. This is supposed to change the filename upon uploading.

Anyway this does nothing, when I upload an asset in the cp, the dialog with progress bars pops up but get stuck. How should I modify this for it to work ?

6

First, when you attach event listeners using craft.on, the naming convention for the event name is assets.beforeSaveEntry, not assets.onBeforeSaveEntry (i.e., skip the on part). Both names appear to work, but the former is the one used in the docs and I'd recommend using that.

Second, the reason why your upload "gets stuck" is your call to craft()->assets->storeFile inside the event listener. Basically, that call results in a loop, because craft->assets->storeFile() will fire another beforeSaveAsset event – making your script execute continually before timing out.

As for actually changing the filename on upload; unfortunately, if there's a reliable way to change the asset filename before the asset is saved, I haven't been able to find it.

However, if you can work with the assets.saveAsset event (which is fired after the asset is uploaded and saved on the server), I found a way:

First, set up an event listener for assets.saveAsset. Inside the event listener, you need to check the $event object's isNewAsset property; making sure the filename is only changed the first time the asset is saved and preventing the loop mentioned above.

Concerning the actual filename, there's a couple of issues to consider. For one, the filename needs to be unique (to that particular folder, as least). If you attempt to store an asset with an existing filename, Craft will use the original filename for your upload (I'd expect Craft to either ask if you wanted to replace the existing file, or suffix the filename with a number, but that doesn't appear to be the case). Secondly, the filename needs to include the file's extension (i.e. jpg or pdf). And finally; because this code runs after the asset has been saved and the file is already stored on the server, it isn't enough to set the filename attribute to the AssetFileModel object (i.e. $asset), you'll also need to rename the actual file.

Keeping these issues in mind, here's a working example:

craft()->on('assets.saveAsset', function (Event $event) {

    if ($event->params['isNewAsset']) {

        $asset = $event->params['asset'];

        // Set the filename to a variable, appending the file extension
        $filename = 'filename-'.md5(time()).'.'.$asset->getExtension();

        // Rename the file on the server
        craft()->assets->renameFile($asset, $filename);

        // Set filename attribute and re-save asset
        $asset->setAttribute('filename', $filename);
        craft()->assets->storeFile($asset);

    }
});

Note that in the above example, I'm hashing the current timestamp and adding it to the filename as an easy fix to ensure that it's unique. I don't know how you want your filenames to look or if duplicate filenames would even be an issue in your case, but you could of course implement a solution that tests for duplicate filenames and/or generates unique filenames in a different manner.

To be honest, although the above should work I do feel this solution is a bit convoluted and I wouldn't be surprised if there's a better way – if there is, hopefully you'll have another answer.

  • Thanks for your answer and pointing the link to the docs. Unfortunately, although this code is non blocking anymore, it doesn't change the filename. Also what if I wanted to display the content of $asset, what would be the best way to do this with php ? – Benj Jan 11 '16 at 9:30
  • So this is extremely odd – I did some more testing w/ the above, and for some reason, it appears to work sometimes – i.e. for certain files, not all. No idea why, but I'll do some more digging and update the answer if I can figure it out. – Mats Mikkel Rummelhoff Jan 11 '16 at 9:48
  • @Benj Phew, this turned out to be a lot more complex than I first thought – just updated my answer with a solution that actually works. Hopefully it helps you out. – Mats Mikkel Rummelhoff Jan 11 '16 at 11:29
  • Also, if you want to dump the contents of $asset, you could do it like this (inside the event listener): var_dump($asset); die();. That'll kill your script, but you should be able to see the dumped contents in your browser's dev tools (see cly.mmikkel.no/013Z1u1p102J for an example). Another option is to serialize the object and log it, e.g. MyPluginHandlePlugin::log(serialize($asset));, which would save the contents of $asset to /craft/storage/runtime/logs/mypluginhandle.log. – Mats Mikkel Rummelhoff Jan 11 '16 at 11:46
  • Mats, thanks for the great answer. It now works indeed. Maybe there is another way, but at least this one works fine. – Benj Jan 11 '16 at 13:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.