Giter Club home page Giter Club logo

seomaestro's Introduction

Seo Maestro

StyleCI License: MIT ProcessWire 3

A ProcessWire module helping you to manage SEO related tasks like a boss! 😎✌️

  • Automatically generates and maintains a XML sitemap from your pages.
  • Includes a Fieldtype and Inputfield to manage sitemap settings and meta data for pages (title, description, canonical url, Opengraph, Twitter, structured data, robots etc.).
  • Multi language support for the sitemap and meta data.
  • Configure default values for meta data on template level and let pages inherit or overwrite them individually.
  • Map existing fields to meta data, reducing the need to duplicate content for content editors.
  • Live preview for content editors how the entered meta data appears on Google and Facebook.

Here is an example of all rendered meta data you will get from a SeoMaestro field:

<title>Sed dictum eros quis massa semper rutrum. | acme.com</title>
<meta name="description" content="Si lobortis singularis genitus ibidem saluto. Dolore ad nunc, mos accumsan paratus duis suscipit luptatum facilisis macto uxor iaceo quadrum. Demoveo, appellatio elit neque ad commodo ea. Wisi, iaceo, tincidunt at commoveo rusticus et, ludus.">
<meta name="keywords" content="Foo,Bar">
<link rel="canonical" href="https://acme.com/en/about/">
<meta property="og:title" content="Sed dictum eros quis massa semper rutrum.">
<meta property="og:description" content="Si lobortis singularis genitus ibidem saluto. Dolore ad nunc, mos accumsan paratus duis suscipit luptatum facilisis macto uxor iaceo quadrum. Demoveo, appellatio elit neque ad commodo ea. Wisi, iaceo, tincidunt at commoveo rusticus et, ludus.">
<meta property="og:image" content="https://acme.com/site/assets/files/1001/og-image.jpg">
<meta property="og:image:type" content="image/jpg">
<meta property="og:image:width" content="1600">
<meta property="og:image:height" content="1200">
<meta property="og:image:alt" content="Lorem Ipsum">
<meta property="og:type" content="website">
<meta property="og:url" content="https://acme.com/en/about/">
<meta property="og:locale" content="en_EN">
<meta name="twitter:card" content="summary">
<meta name="twitter:creator" content="@schtifu">
<meta name="twitter:site" content="@schtifu">
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
  {
    "@type": "ListItem",
    "position": 1,
    "name": "About",
    "item": "https://acme.com/en/about/"
  }
  ]
}
</script>
<meta name="generator" content="ProcessWire">
<link rel="alternate" href="https://acme.com/en/about/" hreflang="en">
<link rel="alternate" href="https://acme.com/en/about/" hreflang="x-default">
<link rel="alternate" href="https://acme.com/de/ueber/" hreflang="de">
<link rel="alternate" href="https://acme.com/fi/tietoja/" hreflang="fi">
<meta name="google-site-verification" content="google-1234">
<meta name="msvalidate.01" content="bing-1234">

Requirements

  • ProcessWire 3.0 or newer
  • PHP 7.0 or newer

Installation

Install the module from the modules directory or with Composer:

composer require wanze/seo-maestro

Configuration

The Seo Maestro module offers the following configuration:

  • Enable sitemap generation Automatically generates and maintains a XML sitemap file.
  • Sitemap path Path and filename of the sitemap relative from the ProcessWire root directory. Make sure that you have write permission to the given folder, as the module needs to write an XML file.
  • Cache time A time in minutes how long the sitemap should be cached.
  • Base url The base url used for all page links in the sitemap and URL metatags, e.g. https://yourdomain.com. If empty, the current domain is used.
  • Default language 2-letter language code of the default language, needed to ensure a correct sitemap for multilanguage sites.

Change sitemap.seomaestro.xml to sitemap.xml once you checked that the sitemap file is correct. The default name reduces the risk to accidentally overwrite an already existing file.

Configure Meta Data and Sitemap Settings for Pages

The meta data and the sitemap configuration of each page is managed with the included Fieldtype. Go ahead and create a new field of type Seo Maestro, a good name for the field is seo 😄.

  • Configure default meta data under Details > Default Values. For text based metatags, you may enter strings or placeholders to map existing fields. For example, if your template contains a lead_text field which should be used for the description meta tag by default, use the placeholder {lead_text}. It is also possible to combine strings and placeholders. The following example appends the company name after a page's title: {title} | acme.com.
  • The opengraph image tag supports placeholders as well: Simply reference an image field. If the field is holding multiple images, the first one is used. For example, {images} would pick the first image from the images field.
  • Each page inherits meta tag values and sitemap configuration by default, but may override them individually when editing a page.
  • Under the Input tab, configure which meta data is displayed to the content editor when editing pages. Exclude any meta data you do not need or which should not be changed by content editors. You can also exclude meta groups, e.g. exclude the Opengraph section entirely.
  • The Webmaster Tools section allows you to enter Google and Bing verification codes, which are rendered as meta tags.

ℹ️ Edit the field in the context of a template to override any of the default data per template.

XML Sitemap

If enabled, the module hooks after ProcessPageView::finished to generate the XML sitemap after the request has been handled by ProcessWire.

  • The sitemap is only generated if the current user is logged in and the current page is an admin page.
  • It only includes pages of templates having a Seo Maestro field, in order to read the sitemap settings.
  • It includes hidden pages.
  • It excludes pages not viewable for the guest user.

Do not forget to submit the sitemap to Google, either in the Search Console or by specifying the path in a robots.txt file.

⚠ If your installation has lot of pages and the request takes too long to generate the sitemap, or if you run into memory problems, it is better disable the automatic generation. Use the \SeoMaestro\SitemapManager class to create the sitemap on your own, e.g. via CLI script.

Meta Data

Common

Common meta tags that are not managed with the fieldtype, but rendered by default.

Tag Description
<link rel="alternate"> Contains the local url of each active page on multi language sites.
<meta name="generator"> Let anyone know that your site is powered by ProcessWire ❤️

Fieldtype

The following meta data is managed for each page via Seo Maestro field. Meta tags are organized in so called groups.

Group Tags Description
meta title
description
keywords
canonicalUrl
Holds the famous title and description tags that should be optimized for search engines. It is also possible to set a custom canonical URL which by default equals the page's url.
opengraph title
description
image
imageAlt
type
image
locale
siteName
Opengraph meta data is read by facebook and other social networks. By default, title and description inherit the values from the meta group. If an image is specified, the og:image:width, og:image:height and og:image:type tags are included automatically at render time.
twitter card
site
creator
Twitter reads the Opengraph meta data as well, except for a few specific tags managed by this group.
robots noIndex
noFollow
Should robots index a page and follow its links?
structuredData breadcrumb Whether to render structured data (JSON-LD) for breadcrumbs.

Webmaster Tools

If you add the Google and Bing verification codes in the Webmaster Tools section when editing a field, the following meta tags are rendered additionally:

Tag Description
<meta name="google-site-verification"> Contains the Google verification code.
<meta name="msvalidate.01"> Contains the Bing verification code.

Output Meta Tags

Meta tags must be rendered in the <head> region of your templates:

// Render all meta tags, including the common ones.
echo $page->seo;
// or...
echo $page->seo->render();

// Render groups individually, e.g. the opengraph meta data.
echo $page->seo->opengraph->render();

API

The module offers an easy-to-use API to retrieve and modify meta data and sitemap configuration for pages:

// Get a single value.
echo $page->seo->meta->description;

$page->of(false);

// Set values as strings or placeholders to reference the value of another field.
$page->seo->opengraph->description = 'A description for opengraph';
$page->seo->meta->title = '{title}';

// Inherit the Twitter card value from the field configuration.
$page->seo->twitter->card = 'inherit';

// Include the page in the sitemap and bump its priority.
$page->seo->sitemap->include = 1;
$page->seo->sitemap->priority = 0.9;

$page->save();

Values are always set for the current language. Switch the user's language to set values in a different language:

$current = $user->language;

$user->language = $languages->get('de');

$page->of(false);
$page->seo->opengraph->title = 'Hallo Welt';
$page->save();

$user->language = $current;

Available Selectors

The Seo Maestro fieldtype does not support to query meta data with selectors, e.g. seo.meta.title%=foo won't work. All meta data is stored as JSON, allowing to add new data anytime without the need to change the database schema. However, the module stores some useful flags whenever a page is saved, and these flags can be used in selectors:

  • sitemap_include to quickly query if a page is included or excluded in the sitemap.
  • <group>_inherit is set to 1, if a page inherits all meta data of a given group.

Examples

Find all pages included in the sitemap:

$pages->find('seo.sitemap_include=1');

Find all pages excluded from the sitemap inheriting all meta and opengraph data:

$pages->find('seo.sitemap_include=0,seo.meta_inherit=1,seo.opengraph_inherit=1');

Hooks

Several hooks are available for developers to customize the behaviour of the module.

___renderMetatags

Add, remove or modify the rendered metatags of a group.

// Remove the description and canonical URL.
$wire->addHookAfter('SeoMaestro::renderMetatags', function (HookEvent $event) {
    $tags = $event->arguments(0);
    $group = $event->arguments(1);

    if ($group === 'meta') {
        unset($tags['description']);
        unset($tags['canonicalUrl']);
        $event->return = $tags;
    }
});

___renderSeoDataValue

Modify the value of meta data after being rendered.

// Add the brand name after the title. 
$wire->addHookAfter('SeoMaestro::renderSeoDataValue', function (HookEvent $event) {
    $group = $event->arguments(0);
    $name = $event->arguments(1);
    $value = $event->arguments(2);
    
    if ($group === 'meta' && $name === 'title') {
        $event->return = $value . ' | acme.com';
    }
});

___alterSeoDataForm

Customize the inputfields of the form containing the SEO data, e.g. change collapsed states or descriptions.

___sitemapAlwaysExclude

Specify pages that should never appear in the sitemap, regardless of sitemap settings on page level. The 404 page is excluded by default.

$wire->addHookAfter('SeoMaestro::sitemapAlwaysExclude', function (HookEvent $event) {
    $pageArray = $event->arguments(0);
    $pageArray->add($excludedPage);
});

___sitemapItems

Add or modify items in the sitemap.

$item = (new SitemapItem())
    ->set('loc', '/en/my-custom-url')
    ->set('priority', 'custom-priority')
    ->set('changefreq', 'changefreq-custom')
    ->addAlternate('de', '/de/my-custom-url-de');

$wire->addHookAfter('SeoMaestro::sitemapItems', function (HookEvent $event) use ($item) {
    $event->return = array_merge($event->return, [$item]);
});

Running Tests

The module includes PHPUnit based tests cases, located in the ./tests directory.

  • Make sure that the dev dependencies are installed by running composer install in site/modules/SeoMaestro.
  • The tests will create pages, fields and templates. Everything should get cleaned up properly, but you should never ever run them on a production environment 😉.
  • Some tests expect a multi language setup to exist. To make them pass, use the multi language site profile provided by ProcessWire. Check the .travis.yml file for an automated setup.

To run the tests:

cd site/modules/SeoMaestro && vendor/bin/phpunit --bootstrap tests/bootstrap.php tests/src --colors

seomaestro's People

Contributors

dependabot[bot] avatar sebiworld avatar tiefenb avatar wanze avatar yuters avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

seomaestro's Issues

PHP 8.1 errors

PHP Deprecated: preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated in /site/modules/SeoMaestro/src/SeoDataBase.php:236

Attribute value not allowed on element meta at this point.

Thanks for this module @wanze ... I noticed in the validator Tracy Debugger that the meta value should not be in some places of the code ...

See this screenshot:

fireshot capture 25 - processwire cms will rise to any task - http___starter test_

It seems that it is enough to change in two files

site\modules\SeoMaestro\src\MetaSeoData.php
site\modules\SeoMaestro\src\TwitterSeoData.php

Canonical url default

Hey there Wanze.

I am wondering if the the default canonical url should be set by https://processwire.com/api/ref/wire-input/canonical-url/

I have had issues with this module and paginated pages. Specifically pages linked to from eg /posts/page2 aren't being indexed because /posts/page2 is canonicalised to posts/ by default.

This is a clear and up to guide to best practices around pagination: https://ahrefs.com/blog/rel-prev-next-pagination/

Basically if paginated pages have different content and you don't have a view all page then they should be canonicalised to themselves.

Recommended meta description length

Currently the module suggests a meta description "less than 320 characters long":

Every page should have a unique Meta Description, ideally less than 320 characters long.

Actual recommendation (i.e. the amount of characters that actually get displayed in Google) is between 120 to 158 characters. Mobile results have less space (and show fewer characters), desktop results have more space (and bigger limit). Google did use the 300+ char limit for some months between 2017 and 2018, but that change was relatively short lived :)

Overall I think that suggesting "ideally less than 320 characters" is misleading for authors who may not be aware of existing limits. As such, I think that "ideally less than 160 characters" would be a good compromise.

localHttpUrl does not exist

Hi @wanze thanks for writing this module.

I got this error right after I added seo_maestro field to my template.

User Error

Exception: Method Page::localHttpUrl does not exist or is not callable in this context (in /####/public/wire/core/Wire.php line 519)

Note: Currently running a local PW site on Laravel Valet.

[Feature Request] Meta Image as Image Field

It would be good if the image field was rendered as an image field rather than a text field. This would make it easier for clients to change the image presented when sharing pages.

Global Defaults

I'd love to have some site wide global defaults that can be overridden by the template defaults and then page defaults.

Support image fields that are nested, i.e. hero_fieldset.hero_image

Hi Wanze,

Great module, thanks for developing.

I'm trying to use a default image field that is nested hero_fieldset.hero_image, if the image field is not populated with an image it throws the following:
Error
Call to a member function get() on null

Updating line 149 of OpengraphSeoData.php as follows prevents the error, I was wondering if you'd consider incorporating this fix into a future release.

if (!$field || !$field->get('defaultValuePage')) {

Thanks very much, Tim

Add possibility to always include the website name in the meta title

The meta title often includes the website's name at the end, typically separated by a character. For example: This is a very important meta title | acme.com. Currently, this can be achieved in two ways:

  1. The content editor includes the information when writing the title (bad)
  2. We can use a hook to add the information at rendering time (better, but still bad)

Proposal
Introduce a new setting on field level which controls how the meta title gets rendered. The most flexible solution is probably to define a string that contains a placeholder for the meta title, e.g. {meta_title} | acme.com.

How to fallback to a custom Opengraph image

Hi @wanze,

thank you …

  1. I'll open an github issue
  2. I have set up an exclusive images field (og_image). In the details tab @seo maestro field it is set as default for the opengraph image {og_image}.
    The images fieldtype offers an option to fetch a default value (tab details) from a specific page if the field is empty. Since I have a global site settings page, I would like to fetch a default og_image from that settings page (in case the content page og_image-field is emtpy).

When I fetch $page->og_image on a content page, it returns the default image from the settings-page. The seo maestro output in header however contains no image tag.
I guess it's in your function getPageImage(), maybe it's related to the getUnformatted() call… that ignores the images-field options?

images-default-value

Originally posted by @esszett in #5 (comment)

Seo image is duplacated multiple times

I've figured out a problem with your module.

  • PW Ver. 3.0.184
  • Modul Ver. 1.1.0
  • PHP 7.4
  • Settings - see. Screenshot below

Description:
Till now I've only setup one image on the Homepage (page id 1) for the SEO image field.
But it seems, that every time ~~ I save the page ~~ an extra image is created in site/assets/files/1234 the folder corresponding to the page I'm saving.

EDIT OUUUCH -It seems even worse: - The suspicious files are generated every time the page is loaded in frontend - see last three screenshots.

This blows up the assets folder enormously and is absolutely weird.

Settings
Screenshot 2021-12-20 at 10-30-13 Feld bearbeiten seo_meta • stadthalle-kelkheim local

Duplicate files
2021-12-20 10_39_12

SEO hompage - ID 1
2021-12-20 10_44_08

SEO page id 1029
2021-12-20 10_48_32

** Files beeing generated after EVERY page visit**

2021-12-20 11_11_58
2021-12-20 11_13_13
2021-12-20 11_14_14

Ability to set canonical URL

Currently, the canonical URL is rendered automatically and it equals the page's url. While the canonical url can already be changed with hooks, it would be nice to alter this via GUI, on page level.

  • What is the group where the canonical URL belongs to? meta?
  • Should we introduce a new group for this, e.g. common or url. The common group could hold various tags/settings not fitting into any existing group.

One line incompatible with PHP version less than 7.0

Hi. I uploaded my PW site to a client server that was running older PHP version and SeoMaestro threw a 500 server error. Error log showed an issue:

Parse Error: syntax error, unexpected '?' (line 211 of /site/modules/SeoMaestro/src/SeoDataBase.php)

Which is return $defaultConfig[$key] ?? null;

So the ?? is the only thing that stops SeoMaestro from working in older PHP versions.

I suggest changing it to "old-school" return isset($defaultConfig[$key]) ? $defaultConfig[$key] : null;

SeoDataBase encode issue

using v1.0 and ENT_XML1 with entities1 broke Turkish character "ü".

convert "Tüm" to "T&amp;uuml;m" and browser not propery display.

return $sanitizer->entities1($value, ENT_QUOTES | ENT_XML1);

changed this line to return $sanitizer->entities1($value, ENT_QUOTES); problem resolved.

php v7.2.19

Multiple og:images

Hi,

I have been using this module for quite a while, but today I have a problem that I need to define multiple og:images for a page. I was surprised at first, but when I looked up that this is quite possible, I looked to see if this module supports that. With regret I had to find out that this is not the case.

I think that this problem is quite rare and could definitely be solved otherwise, but also an extension of the module would make sense.

The problem is as follows. We link our pages on Facebook and it is quite supposed to use other thumbnails than the default image. Apparently it is not possible from our side when posting a web page, to add an additional image as a thumbnail, so I was asked to add another og:image, because through this Facebook lists all og:images and you are able to choose one of them when posting.

I will now implement a WorkAround as a temporary solution, but it would be great if a feature is implemented in the module that could insert multiple images via og:images.

Finally, I thank you again very much for this module.

How to use with "Media Manager" plugin?

Hi, we use the Media Manager plugin (https://modules.processwire.com/modules/process-media-manager/) for images in our templates and to output e.g. the og:image tag, the syntax {image_media_manager.first.media} does not work in the default values settings page (I assume because the Media Manager plugin does not return a Processwire PageImage object but a MediaManager object as described here: https://mediamanager.kongondo.com/documentation/frontend-output-of-media-manager-fields/media-manager-objects/).

The value {image_media_manager.first.media.httpUrl} or {image_media_manager.first.media.url} also won't work.

Is there a way to do this with the provided syntax only or do we need to write a hook?

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.