Uploading duplicate files

In our panels we use multiple file sections. This is great but for the end user it is sometimes confusing that when they upload an image called “building.jpg” to the image section “visuals” they can not upload a “building.jpg” to the image section “footer-images” for example. So we’re creating a plugin to allow duplicate files to be uploaded: https://github.com/reprovinci/kirby-upload-duplicates.

Now the thing is that we tried to use file.create:before hook to rename prefix & suffix the file before upload but that doesn’t seem to work. The before hook is triggered, if we dump it we can see the output. But the file is not renamed. So currently we are using the create:after hook like this:

<?php

Kirby::plugin('reprovinci/kirby-upload-duplicates', [
  'hooks' => [
    'file.create:after' => function ($file, $upload) {
    	$i = 1;
    	$tpl = $file->template();
    	$newname = $tpl . '-' . $file->name() . '-' . $i;
	    $newpath = str_replace('/' . $file->name(), '/' . $newname, $file->root());
    	while (F::exists($newpath)) {
    		$i++;
    		$newname = $tpl . '-' . $file->name() . '-' . $i;
	    	$newpath = str_replace('/' . $file->name(), '/' . $newname, $file->root());
    	}
    		
    	$file->changeName($newname);

    }
  ]
]);

Now our questions are:

  • can we do this on the create:before hook?
  • the :before hook seems to be after the core checks if the file exists, is that right?

Thanks for anyone looking into this :slight_smile:
Have a nice day

Instead of duplicating files, wouldn’t it make more sense to file fields instead of sections for this purpose?

well… It’s kind of experimenting. But it does seem to work. Sometimes the main content for a section is actually an image and then it’s great to be able to just click and supply an image instead of having to upload an image in a box with all sorts of images and then link it somewhere.

We’re currently using image sections with seperate file blueprints for visuals (with the visual title/link and button in the visual blueprint), for teammembers (with their info in the blueprint) and more… It works intuïtive for the client and it also gives a very natural look in the panel. The teammember list for example is a nice list of cards.

:slight_smile:

Ok, was just asking. (And files fields will get upload functionality in 3.2.)

As regards your original question, I’m not sure why the before hook doesn’t work. What is the problem with using an after hook?

when uploading a file it changes it’s name to template-filename-number.ext. But on the after hook it first uploads the file. So if a test.jpg already exists a test.jpg can not be uploaded. It needs to be renamed before it is written to the content dir. I thought doing a $file->changeName() in the before hook would do the trick but that doesn’t do anything… The $file object can be echoed but modifications don’t seem to have any effect.

Oh, ok, now I finally understand (maybe I should increase my coffee input). One way out of this problem would be to always add a timestamp or whatever to any uploaded file so that the issue with duplicate filenames doesn’t come up in the first place if we can’t get the before hook to work. Maybe a route.before hook instead of a file.create:before hook could do the job.

Having upload functionality in the file field might render my original usecase obsolete. But still… I’d like to get a grip on that create:before/after process. :slight_smile:

Well… we usually do things in unorthodox ways, so coffe might not alway do the full trick in trying to get along with our mind-boggles :smile:

How would you add that timestamp? I’ve tried $file->changeName() in the before hook but it’s not giving any result… The file isn’t renamed at all. And in the after hook the file is already uploaded.

What I meant was that if you always rename any file on upload using a file.create:after hook, then the issue with using the after hook should disappear because no two files would ever have the same filename.

ah! like that :man_facepalming:t2: Yeah, in a clean install that would be the case. But we also have a bunch of running websites where we’d need to rename all existing files. I’m nitpicking, I know… but I’d like to make it work even for existing files.

But then again, maybe this all boils down to me trying to get a grip on the file.create:before hook :slight_smile:

Can you (or someone else?) shed some light on the file.create:before hook? It triggers and I can echo the file object, but I can’t seem to do anything with it. Modifications are not stored and for example the changeName() function does not work…

Hi, @mcbrwr I am facing the same issue. I am using this code:

'hooks' => [
  'file.create:after' => function ($file, $upload) {
    $file->changeName(uniqid());
  },
  'file.replace:after' => function ($file, $upload) {
    $file->changeName(uniqid());
  },
],

It works just fine when I am uploading to Files section. The file gets renamed properly. However problems begin when you are trying to upload files directly either to Textarea field or Files field. The file itself gets renamed as expected but the value sent to those fields is still original filename.

Makes [kind of] sense, since the renaming takes place after the file is uploaded.

Tried to change the hook from :after to :before but then the renaming gets completelly ignored.

I would appreciate help with this. Is this a bug or just bad implementation on my end? Thanks!