If check and fallback in Kirby Meta plugin

Note that this will still throw an error if the fallback image does not exist.

@pedroborges Thanks but unfortunately this doesnt work.

    'og' => [
        'title' => function($page) {
            return $page->isHomePage()
            ? site()->seotitle()
            : $page->seotitle().' | '.site()->seotitle();
        },
        'image' => function($page) {
          $image = $page->isHomePage()
                      ? site()->feature()->toFile()
                      : $page->feature()->toFile();

          if (! $image) {
            $image = new asset('assets/images/fallback.png');
          }

          return $image->focusCrop(1200, 630)->url();
        },
        'type' => 'website',
        'site_name' => site()->seotitle(),
        'url' => function($page) { return $page->url(); },
        'description' => function($page) {
            return $page->isHomePage()
            ? site()->seometa()
            : $page->seometa();
        },
    ], 

I get a Call to undefined method Asset::focusCrop() error.

Any other ideas?

focusCrop() probably doesn’t work with an instance of the Asset class; maybe use standard crop() instead (for the asset, this will not have a cropping point anyway). You can still continue using focusCrop() for the homepage image.

I see, well the fallback image i guess doesnt need that, its a fixed image as a placeholder. its only the real ones that need focus crop because they may or may not be art directed.

 'image' => function($page) {
          $image = $page->isHomePage()
                      ? site()->feature()->toFile()
                      : $page->feature()->toFile();

          if($image) {
            return $image->focusCrop(1200, 630)->url();        
          } else {
            $image = new Asset('assets/images/fallback.png');
            if($image) {
               return $image->url();
            } else {
               return '';
            }
          }

          
        },

Thanks @texnixe, it looks like it might work but its doing the oppisite of what i want. The field has a value and its deciding to use the fallback regardless. If i switch the if statement round, im back at the old error again with focus crop.

Does the field have a valid value, i.e. one that returns an image?

Yup. Lower down i have this code that is unaltered that generates an image for twitter:

        'image' => function($page) {
          return $page->isHomePage()
          ? site()->shareimage()->toFile()->focusCrop(800,400)->url()
          : $page->shareimage()->toFile()->focusCrop(800,400)->url();
        },

the result is using the same field,

<meta name="twitter:image" content="//localhost:3000/thumbs/shareimagehome-800x400-12-18.png">

With your last code in the facebook rule, the result is:

<meta property="og:image" content="//localhost:3000/assets/images/fallback.png">

Here the field is called shareimage, not feature!

Oh for goodness sake! that was @pedroborges. I copied his example. Sorry, he changed the field name :frowning:

Hmmm. Still not working. I set the feild name correctly:

'image' => function($page) {
         $image = $page->isHomePage()
                     ? site()->shareimage()->toFile()
                     : $page->shareimage()->toFile();
         if($image) {
           return $image->focusCrop(1200, 630)->url();
         } else {
           $image = new Asset('assets/images/fallback.png');
           if($image) {
              return $image->url();
           } else {
              return '';
           }
         }

},

But the result is:

<meta property="og:image" content="//localhost:3000/content/shareimagehome.png">

Which i think means the toFile() part isnt working.

If i empty the field out, to trigger the fallback, i get the following whoops page:

Call to a member function focusCrop() on null

Do you really get the whoops page from this bit of code or because you are trying to get the image from the same field in another part of the code, where you don’t use the check if the image exists?

If i understand what you mean correctly, i think so yes, because the other bit of code doesnt use the $image variabe

Yes, but in the above code, you are calling the focusCrop() method without even checking if the image exists :roll_eyes:! And if you deleted the field content - as you said above - then this code will throw an error. So please check which part of your code is causing the error message (Whoops will tell you that).

Ok, I am not getting the whoops page any more, but its still not right.

Both the site.en.txt and home.en.txt file has a value for the shareimage() field. It does not seem to be paying any attention to home.en.txt, instead its reading the value from site.en.txt. If i empty the field in site.en.txt, it does fall back to the fallback.png. Why is it skipping the current page and going straight to the site.en.txt for a value? It should only do that if home.en.txt has an empty field.

Heres the code im using

'image' => function($page) {
   $image = $page->isHomePage()
   ? site()->shareimage()->toFile()
   : $page->shareimage()->toFile();
   if($image) {
     return $image->focusCrop(1200, 630)->url();
   } else {
     $image = new Asset('assets/images/fallback.png');
     if($image) {
        return $image->url();
     } else {
        return '';
     }
   }
},

The logic is like this:

If the page is the homepage, use the shareimage file from site.txt
If it is any other page, use the shareimage file from page.txt

That means, home.txt is never used (unless you have changed your homepage to something else and still have a home folder)

Duh. Of course. So i got rid of the check for the home page and amended it to this, and its working now. Thanks all.

'image' => function($page) {
   $image = $page->shareimage()->toFile();
   if($image) {
     return $image->focusCrop(1200, 630)->url();
   } else {
     $image = site()->shareimage()->toFile();
     if($image) {
        return $image->url();
     } else {
        $image = new Asset('assets/images/fallback.png');
        return $image->url();
     }
   }
},

Ok so now its the future and the Meta Tags Plugin has been updated for Kirby 2.5.7 which has meant some slight changes to the syntax it uses because I found an issue.

Heres the updated version of the fallback above, using the new syntax, in case its useful to somebody.

// SEO Settings
// Global
c::set('meta-tags.default', function(Page $page, Site $site) {
    return [
        'title' => $page->isHomePage()
          ? $site->seotitle()
          : $page->seotitle().' | '.$site->seotitle(),
          'meta' => [
              'robots' => 'index,follow,noodp',
              'description' => $page->isHomePage()
                ? $site->seometa()
                : $page->seometa(),
              'keywords' => $page->isHomePage()
                ? $site->seokeywords()
                : $page->seokeywords(),
              'robots' => 'index,follow,noodp',
          ],
        'link' => [
            'canonical' => $page->url()
        ],
        'og' => [
            'title' => $page->isHomePage()
                ? $site->seotitle()
                : $page->seotitle().' | '.$site->seotitle(),
            'type' => 'website',
            'site_name' => $site->title(),
            'url' => $page->url(),

            'image' => function($page) {
               $image = $page->shareimage()->toFile();
               if($image) {
                 return $image->focusCrop(1200, 630)->url();
               } else {
                 $image = site()->shareimage()->toFile();
                 if($image) {
                    return $image->url();
                 } else {
                    $image = new Asset('assets/images/fallback.png');
                    return $image->url();
                 }
               }
            },

            'description' => $page->isHomePage()
                ? $site->seometa()
                : $page->seometa().' | '.$site->seotitle(),
        ],
        'twitter' => [
            'title' => $page->isHomePage()
                ? $site->seotitle()
                : $page->seotitle().' | '.$site->seotitle(),
            'card' => 'summary',
            'site' => $site->twitterusername(),
            'creator' => $site->twitterhandle(),
            'image' => function($page) {
               $image = $page->shareimage()->toFile();
               if($image) {
                 return $image->focusCrop(800, 400)->url();
               } else {
                 $image = site()->shareimage()->toFile();
                 if($image) {
                    return $image->url();
                 } else {
                    $image = new Asset('assets/images/fallback.png');
                    return $image->url();
                 }
               }
            },
            'url' => $page->url(),
            'description' => $page->isHomePage()
                ? $site->seometa()
                : $page->seometa().' | '.$site->seotitle(),
        ]
    ];
});

// Specific Pages
c::set('meta-tags.templates', function(Page $page, Site $site) {
    return [
      'newsarticle' => [
          'og' => [
              'type' => 'article',
          ],
          'twitter' => [
              'card' => 'summary_large_image',
              'image' => function($page) {
                 $image = $page->shareimage()->toFile();
                 if($image) {
                   return $image->focusCrop(800, 400)->url();
                 } else {
                   $image = site()->shareimage()->toFile();
                   if($image) {
                      return $image->url();
                   } else {
                      $image = new Asset('assets/images/fallback.png');
                      return $image->url();
                   }
                 }
              },
          ],
      ],
    ];
});

Well done @jimbobrjames!

I’ll give you a little tip that I use a lot to simplify this configuration. With the new syntax, you can store repeated values in variables to reuse them in other places.

c::set('meta-tags.default', function(Page $page, Site $site) {
    $title = $page->isHomePage()
          ? $site->seotitle()
          : $page->seotitle().' | '.$site->seotitle();

    return [
        'title' => $title,
        // ...

Then you can just use $title in the other places you repeated that bit of code.

Nice, thanks for the tip @pedroborges!