How to Set a 'Default Image' for a Page Listing in The Panel, from Assets

I have an image inside my assets/images folder which should be used as the ‘default page image’ within the Panel, if the user hasn’t yet uploaded another image for the page. In order to do that, I used a page model to create a default ‘main_img’ calculated property: if the page has an image, it will use that, otherwise it should use the new ‘Asset’ class - or the asset() helper - to create a default image on-the-fly. The code I have for the model, and which doesn’t seem to work, is:

<?php 

class TestPage extends Page {
    public function main_img() {
        if ($this->images()->findBy('template','preview_image')) {
            return $this->images()->findBy('template','preview_image');
        } else {
            return new Asset("assets/images/default.png");
        }
    }
}

In the blueprint of the parent page, I have created a Pages section, which should list all my child ‘Test’ pages, using the “main_img” as a cover image - like this:

        sections:
          site_areas:
            type: pages 
            headline: Test Pages 
            status: all
            layout: cards
            templates:
              - test
            image:
              query: page.main_img
              cover: true
              back: white

If the user hasn’t uploaded an image, nothing is shown - i.e., it seems that the Asset class is not finding the image, or not creating the image object. I’ve checked that the image is has the right filename, and is in the right location, and I can access it directly with the browser. I also don’t get any errors or exceptions - I just don’t get the default image in the Panel, ever… Same thing happens if I use the asset() helper.

What am I doing wrong?

An Asset object is not a File object and that’s what is probably expected here.

You’re probably right, @texnixe! Is there a ready-made method to convert an Asset to a File?..

I would probably tackle it another way - use a page create hook to copy the default image to it. You could use a file upload hook to auto delete it the first time someone uploads a better image.

Another apporach: Override $page->panelImage(): https://github.com/getkirby/kirby/blob/3.1.1/src/Cms/Page.php#L923

Basically get the original output with parent::panelImage() and then just alter url.

Or I might be completely wrong with this, but what would happen if you use a File object instead of Asset?

new File([
  'filename' => 'default.png',
  'url' => ...,
  'root'=> ...
])

@distantnative both your solutions sound neat. It might be a good idea to add some info in the Guide about these methods, so it’s easier for ‘slow-brains’ like me to see the the different types of use-cases when they would be used.

It feels a bit odd to me that Asset does not inherit from File - I (obviously wrongly) assumed that an Asset would imply a FILE Asset. For most use-cases, I’m guessing that the developer wants to use the Asset class to access files in the Assets folder, so they can be used as ‘normal’ files everywhere on the site, as needed. If that is not how the Asset class was designed, then I’m curious: what is the typical use-case for it?

Yes, and this is the case, you can use an asset in your templates in the same way as a file object.

The reason you can’t use the Assets in your case is because the panelImage() method want a Kirby\Cms\File object explicitly:

if ($image = $this->query($settings['query'] ?? 'page.image', 'Kirby\Cms\File')) {
    $settings['url'] = $image->thumb($thumbSettings)->url(true) . '?t=' . $image->modified();

    unset($settings['query']);
}

(Kirby/src/Cms/page.php, #942ff)

Providing an asset through a page model will be possible in 3.2.