Skip to content
Closed

V2 #16

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to this project will be documented in this file.

## [Unreleased]
- Removed: json-ld backport
- Removed: @WebPage schema integration, because it is provided by Contao core
- Removed: @BreadcrumbList schema integration, because it is provided by Contao core

## [1.14.2] - 2025-12-17
- Fixed: Relative image paths had to be absolute

Expand Down Expand Up @@ -312,4 +317,4 @@ this is possibly breaking!
[#7]: https://github.com/heimrichhannot/contao-head-bundle/pull/7
[#5]: https://github.com/heimrichhannot/contao-head-bundle/pull/5
[#4]: https://github.com/heimrichhannot/contao-head-bundle/pull/4
[#3]: https://github.com/heimrichhannot/contao-head-bundle/pull/3
[#3]: https://github.com/heimrichhannot/contao-head-bundle/pull/3
54 changes: 2 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ This bundle enhances the handling of html `<head>` section tags. It provides ser

## Features
- Provide a nice api to set head tags like meta, title, base, link
- Provide additional json-ld schema data
- Provide additional schema.org json-ld data
- Sets important meta tags like og:title, og:description, og:url and twitter:card out of the box
- Allow setting open graph and twitter fallback image on root page
- Allow setting twitter author per root page
- Backport canonical url option from contao 4.13 for contao 4.9+
- Backport json-ld support for contao 4.9+

## Usage

Expand All @@ -37,41 +36,13 @@ In your root page, you can activate to add fallback image (og:image and twitter:

### Add additional schema.org data

In your root page, you can activate to add additional structured data to you web page.
In your root page, you can activate additional structured data for your web page.
Following schema.org types are available:
* @Organization
* @WebSite
* @WebPage
* @BreadcrumbList


![Screenshot Structured Data Settings](docs%2Fimg%2Fscreenshot_backend_structured_data.png)

### Set json-ld in your templates

This bundle backports the methods of contao 4.12+ to contao 4.9+. So usage is the same as in the contao core.

#### Twig templates

```twig
{% do add_schema_org({
'@type': 'NewsArticle',
'headline': newsHeadline|striptags,
'datePublished': datetime|date('Y-m-d\TH:i:sP'),
}) %}
```

#### PHP templates

```php
<?php $this->addSchemaOrg([
'@type' => 'NewsArticle',
'headline' => $newsHeadline,
'datePublished' => $datetime->date('Y-m-d\TH:i:sP'),
]); ?>
```


## Integration
Use head bundle api set in your code.

Expand Down Expand Up @@ -159,27 +130,6 @@ class SomeEventListener
}
```

### Set json-ld schema data

> From contao 4.12 you can use the JsonLdManager service [from the core](https://docs.contao.org/dev/framework/response-context/#the-jsonldmanager).

To set json-ld schema data, use the `JsonLdManager` service:

```php
<?php
use HeimrichHannot\HeadBundle\Manager\JsonLdManager;

class ExampleController
{
private JsonLdManager $jsonLdManager;

public function __invoke() {
$organisation = $this->jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->organization();
$organisation->name('Example and Sons Ltd.');
$organisation->url('https://example.org');
}
}
```
### Reader Config Contao sample
![image](https://github.com/heimrichhannot/contao-head-bundle/assets/51906753/a5e30fdc-66f9-419a-a5a4-1805bb66b227)

Expand Down
4 changes: 2 additions & 2 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
Psr\Container\ContainerInterface: '@service_container'

HeimrichHannot\HeadBundle\:
resource: '../src/{EventListener,Helper,Twig}/*'
resource: '../src/{EventListener,Helper}/*'
exclude: '../src/Helper/LegacyHelper.php'
bind:
$bundleConfig: '%huh_head%'
Expand All @@ -23,4 +23,4 @@ services:
class: HeimrichHannot\HeadBundle\Manager\TagManager
public: true
deprecated: ~
HeimrichHannot\HeadBundle\Manager\TagManager: '@huh.head.tag_manager'
HeimrichHannot\HeadBundle\Manager\TagManager: '@huh.head.tag_manager'
18 changes: 0 additions & 18 deletions contao/dca/tl_page.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
->addLegend('schema_legend', 'meta_legend', PaletteManipulator::POSITION_AFTER)
->addField('headAddOrganisationSchema', 'schema_legend', PaletteManipulator::POSITION_APPEND)
->addField('headAddWebSiteSchema', 'schema_legend', PaletteManipulator::POSITION_APPEND)
->addField('headAddWebPageSchema', 'schema_legend', PaletteManipulator::POSITION_APPEND)
->addField('headAddBreadcrumbSchema', 'schema_legend', PaletteManipulator::POSITION_APPEND)
->applyToPalette('root', 'tl_page')
->applyToPalette('rootfallback', 'tl_page');

Expand Down Expand Up @@ -107,22 +105,6 @@
],
'sql' => "char(1) NOT NULL default ''",
],
'headAddWebPageSchema' => [
'exclude' => true,
'inputType' => 'checkbox',
'eval' => [
'tl_class' => 'w50 clr',
],
'sql' => "char(1) NOT NULL default ''",
],
'headAddBreadcrumbSchema' => [
'exclude' => true,
'inputType' => 'checkbox',
'eval' => [
'tl_class' => 'w50 clr',
],
'sql' => "char(1) NOT NULL default ''",
],
];

$dca['fields'] = array_merge($dca['fields'], $fields);
8 changes: 2 additions & 6 deletions contao/languages/de/tl_page.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@
$lang['twitterSite'] = ['Twitter @username', 'Der Twitter @username der einer Twitter-Karte zugewiesen werden soll (twitter:site Attribut).'];
$lang['headAddOrganisationSchema'][0] = '@Organization ausgeben';
$lang['headAddOrganisationSchema'][1] = 'Das @Organization Schema ausgeben.';
$lang['headOrganisationSchemaName'][0] = 'Name';
$lang['headOrganisationSchemaName'][1] = 'Geben Sie den Namen der Organisation ein.';
$lang['headOrganisationName'][0] = 'Name';
$lang['headOrganisationName'][1] = 'Geben Sie den Namen der Organisation ein.';
$lang['headOrganisationLogo'][0] = 'Logo';
$lang['headOrganisationLogo'][1] = 'Wählen Sie ein Logo aus.';
$lang['headOrganisationWebsite'][0] = 'URL';
$lang['headOrganisationWebsite'][1] = 'Geben Sie die URL der Organisation ein.';
$lang['headAddWebSiteSchema'][0] = '@WebSite ausgeben';
$lang['headAddWebSiteSchema'][1] = 'Das @WebSite Schema ausgeben.';
$lang['headAddWebPageSchema'][0] = '@WebPage ausgeben';
$lang['headAddWebPageSchema'][1] = 'Das @WebPage Schema ausgeben.';
$lang['headAddBreadcrumbSchema'][0] = '@BreadcrumbList ausgeben';
$lang['headAddBreadcrumbSchema'][1] = 'Das @BreadcrumbList Schema ausgeben.';

/*
* Legends
Expand Down
4 changes: 0 additions & 4 deletions contao/languages/en/tl_page.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
$lang['headOrganisationWebsite'][1] = 'Enter the URL of the organisation.';
$lang['headAddWebSiteSchema'][0] = 'Output @WebSite';
$lang['headAddWebSiteSchema'][1] = 'Output the @WebSite schema.';
$lang['headAddWebPageSchema'][0] = 'Output @WebPage';
$lang['headAddWebPageSchema'][1] = 'Output the @WebPage schema.';
$lang['headAddBreadcrumbSchema'][0] = 'Output @BreadcrumbList';
$lang['headAddBreadcrumbSchema'][1] = 'Output the @BreadcrumbList schema.';

/*
* Legends
Expand Down
38 changes: 24 additions & 14 deletions src/EventListener/Contao/GeneratePageListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use Contao\CoreBundle\InsertTag\InsertTagParser;
use Contao\CoreBundle\Routing\ResponseContext\HtmlHeadBag\HtmlHeadBag;
use Contao\CoreBundle\Routing\ResponseContext\JsonLd\JsonLdManager;
use Contao\CoreBundle\Routing\ResponseContext\ResponseContextAccessor;
use Contao\CoreBundle\ServiceAnnotation\Hook;
use Contao\LayoutModel;
Expand All @@ -21,7 +22,6 @@
use HeimrichHannot\HeadBundle\HeadTag\MetaTag;
use HeimrichHannot\HeadBundle\Helper\TagHelper;
use HeimrichHannot\HeadBundle\Manager\HtmlHeadTagManager;
use HeimrichHannot\HeadBundle\Manager\JsonLdManager;
use HeimrichHannot\UtilsBundle\Util\Utils;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
Expand All @@ -42,7 +42,6 @@ class GeneratePageListener implements ServiceSubscriberInterface
private RequestStack $requestStack;
private Utils $utils;
private TagHelper $tagHelper;
private JsonLdManager $jsonLdManager;
private InsertTagParser $insertTagParser;

public function __construct(
Expand All @@ -52,7 +51,6 @@ public function __construct(
RequestStack $requestStack,
Utils $utils,
TagHelper $tagHelper,
JsonLdManager $jsonLdManager,
InsertTagParser $insertTagParser,
) {
$this->config = $bundleConfig;
Expand All @@ -61,7 +59,6 @@ public function __construct(
$this->requestStack = $requestStack;
$this->utils = $utils;
$this->tagHelper = $tagHelper;
$this->jsonLdManager = $jsonLdManager;
$this->insertTagParser = $insertTagParser;
}

Expand All @@ -81,7 +78,7 @@ public function __invoke(PageModel $pageModel, LayoutModel $layout, PageRegular
$title = $this->insertTagParser->replace('{{page::pageTitle}}');
}

$this->prepareJsonLdContent($pageModel, $title);
$this->prepareJsonLdContent($pageModel);
$this->setOpenGraphTags($title, $description ?? '');
$this->setTwitterTags();
}
Expand Down Expand Up @@ -255,8 +252,14 @@ private function getHtmlHeadBag(): ?object
return null;
}

private function prepareJsonLdContent(PageModel $pageModel, string $title): void
private function prepareJsonLdContent(PageModel $pageModel): void
{
$jsonLdManager = $this->getJsonLdManager();

if (!$jsonLdManager) {
return;
}

/** @var \HeimrichHannot\HeadBundle\Model\PageModel $rootPageModel */
$rootPageModel = $this->utils->request()->getCurrentRootPageModel($pageModel);

Expand All @@ -265,7 +268,7 @@ private function prepareJsonLdContent(PageModel $pageModel, string $title): void
}

if ($rootPageModel->headAddOrganisationSchema) {
$organisation = $this->jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->organization();
$organisation = $jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->organization();

if ($rootPageModel->headOrganisationName) {
$organisation->name($rootPageModel->headOrganisationName);
Expand All @@ -285,21 +288,28 @@ private function prepareJsonLdContent(PageModel $pageModel, string $title): void
}

if ($rootPageModel->headAddWebSiteSchema) {
$website = $this->jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->webSite();
$website = $jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->webSite();
$this->setPropertyIfNotSet($website, 'name', $this->insertTagParser->replace('{{page::mainPageTitle}}'));
$this->setPropertyIfNotSet($website, 'url', $this->utils->request()->getBaseUrl([
'pageModel' => $pageModel,
]));
}

if ($rootPageModel->headAddWebPageSchema && !$this->utils->request()->isIndexPage($pageModel)) {
$webpage = $this->jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->webPage();
$this->setPropertyIfNotSet($webpage, 'name', $title);
}

if ($pageModel->description) {
$this->setPropertyIfNotSet($webpage, 'description', $pageModel->description);
}
private function getJsonLdManager(): ?JsonLdManager
{
if (!$this->container->has(ResponseContextAccessor::class)) {
return null;
}

$responseContext = $this->container->get(ResponseContextAccessor::class)->getResponseContext();

if (!$responseContext->has(JsonLdManager::class)) {
return null;
}

return $responseContext->get(JsonLdManager::class);
}

private function setPropertyIfNotSet(BaseType $type, string $property, string $value): void
Expand Down
71 changes: 1 addition & 70 deletions src/EventListener/Contao/ParseTemplateListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
use Contao\Template;
use HeimrichHannot\HeadBundle\Helper\LegacyHelper;
use HeimrichHannot\HeadBundle\Manager\HtmlHeadTagManager;
use HeimrichHannot\HeadBundle\Manager\JsonLdManager;
use HeimrichHannot\HeadBundle\Model\PageModel;
use HeimrichHannot\UtilsBundle\Util\Utils;
use Spatie\SchemaOrg\Graph;
use Spatie\SchemaOrg\Schema;

/**
* @Hook("parseTemplate")
Expand All @@ -25,22 +20,16 @@ class ParseTemplateListener
{
private array $bundleConfig;
private HtmlHeadTagManager $headTagManager;
private JsonLdManager $jsonLdManager;
private Utils $utils;

public function __construct(array $bundleConfig, HtmlHeadTagManager $headTagManager, JsonLdManager $jsonLdManager, Utils $utils)
public function __construct(array $bundleConfig, HtmlHeadTagManager $headTagManager)
{
$this->bundleConfig = $bundleConfig;
$this->headTagManager = $headTagManager;
$this->jsonLdManager = $jsonLdManager;
$this->utils = $utils;
}

public function __invoke(Template $template): void
{
$this->addLegacyMetaMethod($template);
$this->createBreadcrumbSchema($template);
$this->addSchemaFromArrayMethodPolyfill($template);
}

protected function addLegacyMetaMethod(Template $template): void
Expand All @@ -61,62 +50,4 @@ protected function addLegacyMetaMethod(Template $template): void
};
}
}

private function createBreadcrumbSchema(Template $template): void
{
if (!str_starts_with($template->getName(), 'mod_breadcrumb')) {
return;
}

/** @var PageModel|null $rootPageModel */
$rootPageModel = $this->utils->request()->getCurrentRootPageModel();
if (!$rootPageModel || !$rootPageModel->headAddBreadcrumbSchema) {
return;
}

if ($this->utils->request()->isIndexPage()) {
return;
}

if (!$template->items || !\is_array($items = $template->items)) {
return;
}

$breadcrumb = $this->jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG)->breadcrumbList();

if (!$breadcrumb->getProperty('itemListElement')) {
$listItems = [];
$position = 0;

foreach ($items as $item) {
$listItem = Schema::listItem();
$listItem->position(++$position);
$listItem->name($item['title']);
$listItem->item($item['href']);
$listItems[] = $listItem;
}
$breadcrumb->itemListElement($listItems);
}
}

/**
* @todo Remove this method when contao 4.12+ is required
*/
private function addSchemaFromArrayMethodPolyfill(Template $template): void
{
if (method_exists($template, 'addSchemaOrg')) {
return;
}

$jsonLdManager = $this->jsonLdManager;

$template->addSchemaOrg = function (array $jsonLd) use ($jsonLdManager): void {
$type = $jsonLdManager->createSchemaOrgTypeFromArray($jsonLd);

$jsonLdManager
->getGraphForSchema(JsonLdManager::SCHEMA_ORG)
->set($type, $jsonLd['identifier'] ?? Graph::IDENTIFIER_DEFAULT)
;
};
}
}
Loading
Loading