fabianmichael/kirby-meta

Your all-in-one powerhouse for any SEO and metadata needs imaginable.

Fund package maintenance!
fabianmichael

Installs: 6 378

Dependents: 2

Suggesters: 1

Security: 0

Stars: 65

Watchers: 8

Forks: 8

Open Issues: 13

Type:kirby-plugin

2.0.0-alpha.1 2024-03-22 17:45 UTC

README

⚠️ Warning: This plugin is in beta state. Documentation and implementation are still incomplete.

This piece of code handles the generation of meta tags for search engines, social networks, browsers and beyond.

Screenshot 2022-01-14 at 09-57-24 Mirin Avo’s Kitchen

Key features:

  • 🔎 All-in-one solution for SEO and social media optimization
  • 📱 Support for OpenGraph and Schema.org (JSON-LD) markup
  • 🚀 Customizable metadata for auto-generated metadata from page contents
  • 💻 Extensive panel UI including social media previews
  • 🦊 Easy-to-understand language in the panel, providing a good middle ground between simplicity and extensive control options.
  • 🧙‍♂️ Most features can be enabled/disabled in config, panel UI only shows enabled features (thanks to dynamic blueprints)
  • 🪝 Hooks for altering the plugin's behavior
  • 🌍 All blueprints are fully translatable (English, German, French and Swedish translations are included)

Future plans:

  • ✅ Live-check of metadata with hints in the panel

This plugin is completely free and published under the MIT license. However, if you are using it in a commercial project and want to help me keep up with maintenance, please consider to ❤️ sponsor me for securing the continued development of the plugin.

Requirements

  • PHP 8.0+
  • Kirby 3.6.0+

How it works

The plugin looks for metadata from a page's content file (e.g. article.txt) by the corresponding key. If the page does not contain the specific field, it looks for a metadata method on the current page model, which can return an array of metadata for the current page. If that also fails, it will fall back to default metadata, as stored in the site.txt file at the top-level of the content directory.

That way, every page will always be able to serve default values, even if the specific page or its model does not contain information like e.g. a thumbnail or a dedicated description.

Installation & Setup

Install using composer (recommended):

composer require fabianmichael/kirby-meta

Alternative download methods:

You can also download this repository as ZIP or add the whole repo as a submodule. To run from source, you need to install the dependencies : composer install.

Available configuration options

The options below have to be set in your config.php. Please note that every option has to be prefixed with the plugin namespace, e.g. sitemap => fabianmichael.meta.sitemap.

Blueprint setup

Your site and page blueprints need to use tabs, as the plugin's input fields all come in a tab. Meta comes with tab blueprints that need to be added to your site and page blueprints accordingly:

# site/blueprints/site.yml
[…]
tabs:
  structure:
    label: Structure
    columns:
      […]
  meta: tabs/meta/site

# site/blueprints/pages/default.yml
[…]
tabs:
  content:
    label: Content
    columns:
      […]
  meta: tabs/meta/page

Template setup

Include the meta snippet within your <head> element, preferably before loading any scripts or stylesheets:

<!doctype html>
<html>
<head>
  <?php snippet('meta') ?>
  […]
</head>
[…]

Now you are ready to add/edit metadata from the panel.

Advanced usage

Providing metadata from page models

Sometimes, you want special behavior for certain templates. The easiest way to achieve this is by creating a page model and implementing a $page->metadata() method, that returns an array some or even all of the following keys:

Using hooks

the meta plugin provides a set of handy hooks, allowing you to further add/remove/modify metadata without overriding the built-in snippets or having to set up a page model for every template.

⚠️ Hooks are a powerful tool that can break the plugin's expected behavior for editors working on the panel. Use with care!

meta.load:after

After metadata has been loaded by calling the $page->metadata() method on a model. This allows you to inject additional data.

return [
  'meta.load:after' => function (
    array $metadata,
    Kirby\Cms\Page $page,
    ?string $languageCode
  ) {
    // set `thumbnail.png` as default share image for all pages,
    // if not other image was already set by a page model
    if (empty($metadata['og_image']) === true) {
      $metadata['og_image'] = $page->image('thumbnail.png');
    }
    return $metadata;
  },
];

meta.jsonld:after hook

After the Schema.org graph has been generated. This allows you to pass additional data to the array.

return [
  'meta.jsonld:after' => function (
    array $json,
    FabianMichael\Meta\PageMeta $meta,
    Kirby\Cms\Page $page
  ) {
    // add breadcrumb to JSON-LD graph
    $items = [];

    $parents = $page->parents();

    if ($parents->count() === 0) {
      return $json;
    }

    $i = 0;

    foreach ($parents->flip() as $parent) {
      $items[] = [
        '@type' => 'ListItem',
        'position' => ++$i,
        'item' => [
          '@id' => $parent->url(),
          'name' => $parent->title()->toString(),
        ],
      ];
    }

    $json['@graph'][] = [
      '@type' => 'BreadcrumbList',
      'itemListElement' => $items,
    ];

    return $json;
  },
];

meta.social:after

Allows you to alter the OpenGraph/Twitter card data.

return [
  'meta.social:after' => function (
    array $social,
    FabianMichael\Meta\PageMeta $meta,
    Kirby\Cms\Page $page
  ) {
    // add first video file of page to OpenGraph markup
    if ($page->hasVideos()) {
      $social[] = [
        'property' => 'og:video',
        'content'  => $page->videos()->first()->url(),
      ];
    }
    return $social;
  },
];

'meta.sitemap… hooks

These hooks allow you to completely alter the way how the sitemap is being generated. These functions are meant to manipulate the provided DOM document and elements directly and should not return anything.

return [
  'hooks' => [
    'meta.sitemap:before' => function (
      Kirby $kirby,
      DOMDocument $doc,
      DOMElement $root
    ) {
      // add namespace for image sitemap
      $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');
    },

    'meta.sitemap.url' => function (
      Kirby $kirby,
      Page $page,
      PageMeta $meta,
      DOMDocument $doc,
      DOMElement $url) {

      if ($page->images()->count() === 0) {
        // dynamically exclude page from the sitemap
        return false;
      }

      foreach ($page->images() as $image) {
        // add all images from page to image sitemap.
        $imageEl = $doc->createElement('image:image');
        $imageEl->appendChild($doc->createElement('image:loc', $image->url()));

        if ($image->alt()->isNotEmpty()) {
          $imageEl->appendChild($doc->createElement('image:caption', $image->alt()));
        }

        $url->appendChild($imageEl);
      }
    },

    'meta.sitemap:after' => function (
      Kirby $kirby,
      DOMDocument $doc,
      DOMElement $root
    ) {
      foreach ($root->getElementsByTagName('url') as $url) {
        if ($lastmod = $url->getElementsByTagName('lastmod')[0] ?? null) {
          // remove lastmod date from sitemap entries for some reason …
          $url->removeChild($lastmod);
        }
      }
    },

    'meta.theme.color' => function (
      ?string $color
    ) {
      return '#ff0000';
    }
  ],
];

Manipulating indexed pages

A few helpers are available for manipulating pages:

Page Method

If you'd like to know if a page is indexed in the sitemap, you can use $page->isIndexible() (returns a bool).

Site Method

To get all indexed pages according to your settings, you can use : $site->indexedPages() (returns a Kirby\Cms\Collection of pages).

Credits

This is partly based on an older version of the meta plugin, that I had initially developed for getkirby.com. I liked the idea so much, that I wanted to adapt it for general use on other websites.

It took a lot of inspiration (and some code) from other existing Kirby plugins, like MetaKnight by diesdas ⚡️ digital and Meta Tags by Pedro Borges.