diff --git a/CHANGELOG.md b/CHANGELOG.md index 148ed78..5650b88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 \ No newline at end of file +[#3]: https://github.com/heimrichhannot/contao-head-bundle/pull/3 diff --git a/README.md b/README.md index a30df7c..ac90888 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,11 @@ This bundle enhances the handling of html `` 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 @@ -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 -addSchemaOrg([ - '@type' => 'NewsArticle', - 'headline' => $newsHeadline, - 'datePublished' => $datetime->date('Y-m-d\TH:i:sP'), -]); ?> -``` - - ## Integration Use head bundle api set in your code. @@ -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 -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) diff --git a/config/services.yaml b/config/services.yaml index f9b998e..9170a6e 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -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%' @@ -23,4 +23,4 @@ services: class: HeimrichHannot\HeadBundle\Manager\TagManager public: true deprecated: ~ - HeimrichHannot\HeadBundle\Manager\TagManager: '@huh.head.tag_manager' \ No newline at end of file + HeimrichHannot\HeadBundle\Manager\TagManager: '@huh.head.tag_manager' diff --git a/contao/dca/tl_page.php b/contao/dca/tl_page.php index 9eaba95..ec28d74 100644 --- a/contao/dca/tl_page.php +++ b/contao/dca/tl_page.php @@ -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'); @@ -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); diff --git a/contao/languages/de/tl_page.php b/contao/languages/de/tl_page.php index 642b09b..0922cfd 100644 --- a/contao/languages/de/tl_page.php +++ b/contao/languages/de/tl_page.php @@ -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 diff --git a/contao/languages/en/tl_page.php b/contao/languages/en/tl_page.php index 1facdbd..1ee2bb2 100644 --- a/contao/languages/en/tl_page.php +++ b/contao/languages/en/tl_page.php @@ -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 diff --git a/src/EventListener/Contao/GeneratePageListener.php b/src/EventListener/Contao/GeneratePageListener.php index cffaa39..97ca6ce 100644 --- a/src/EventListener/Contao/GeneratePageListener.php +++ b/src/EventListener/Contao/GeneratePageListener.php @@ -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; @@ -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; @@ -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( @@ -52,7 +51,6 @@ public function __construct( RequestStack $requestStack, Utils $utils, TagHelper $tagHelper, - JsonLdManager $jsonLdManager, InsertTagParser $insertTagParser, ) { $this->config = $bundleConfig; @@ -61,7 +59,6 @@ public function __construct( $this->requestStack = $requestStack; $this->utils = $utils; $this->tagHelper = $tagHelper; - $this->jsonLdManager = $jsonLdManager; $this->insertTagParser = $insertTagParser; } @@ -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(); } @@ -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); @@ -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); @@ -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 diff --git a/src/EventListener/Contao/ParseTemplateListener.php b/src/EventListener/Contao/ParseTemplateListener.php index c3ed59d..3bcac85 100644 --- a/src/EventListener/Contao/ParseTemplateListener.php +++ b/src/EventListener/Contao/ParseTemplateListener.php @@ -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") @@ -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 @@ -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) - ; - }; - } } diff --git a/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php b/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php index 334639f..477cb46 100644 --- a/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php +++ b/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php @@ -9,10 +9,8 @@ namespace HeimrichHannot\HeadBundle\EventListener\Contao; use Contao\CoreBundle\Framework\ContaoFramework; -use Contao\CoreBundle\Routing\ResponseContext\JsonLd\JsonLdManager as ContaoJsonLdManager; use Contao\CoreBundle\ServiceAnnotation\Hook; use HeimrichHannot\HeadBundle\Manager\HtmlHeadTagManager; -use HeimrichHannot\HeadBundle\Manager\JsonLdManager; /** * @Hook("replaceDynamicScriptTags") @@ -21,13 +19,11 @@ class ReplaceDynamicScriptTagsListener { private array $bundleConfig; private HtmlHeadTagManager $headTagManager; - private JsonLdManager $jsonLdManager; - public function __construct(array $bundleConfig, HtmlHeadTagManager $headTagManager, JsonLdManager $jsonLdManager) + public function __construct(array $bundleConfig, HtmlHeadTagManager $headTagManager) { $this->bundleConfig = $bundleConfig; $this->headTagManager = $headTagManager; - $this->jsonLdManager = $jsonLdManager; } /** @@ -36,7 +32,6 @@ public function __construct(array $bundleConfig, HtmlHeadTagManager $headTagMana public function __invoke(string $buffer): string { $buffer = $this->addHeadTags($buffer); - $buffer = $this->addJsonLs($buffer); return $buffer; } @@ -49,16 +44,6 @@ private function addHeadTags(string $buffer): string return $buffer; } - - private function addJsonLs(string $buffer): string - { - if (class_exists(ContaoJsonLdManager::class)) { - return $buffer; - } - - return $this->replace($buffer, 'TL_BODY', $this->jsonLdManager->collectFinalScriptFromGraphs()); - } - private function replace(string $buffer, string $tag, string $content): string { $nonce = ''; diff --git a/src/Manager/JsonLdManager.php b/src/Manager/JsonLdManager.php deleted file mode 100644 index 4db45c6..0000000 --- a/src/Manager/JsonLdManager.php +++ /dev/null @@ -1,142 +0,0 @@ -container = $container; - } - - public function getGraphForSchema(string $schema): Graph - { - if ($this->getContaoJsLdManager()) { - return $this->getContaoJsLdManager()->getGraphForSchema($schema); - } - - $schema = rtrim($schema, '/'); - - if (!\array_key_exists($schema, $this->graphs)) { - $this->graphs[$schema] = new Graph($schema); - } - - return $this->graphs[$schema]; - } - - /** - * @return array - */ - public function getGraphs(): array - { - return $this->graphs; - } - - public function collectFinalScriptFromGraphs(): string - { - if ($this->getContaoJsLdManager()) { - return $this->getContaoJsLdManager()->collectFinalScriptFromGraphs(); - } - - $buffer = ''; - - foreach ($this->getGraphs() as $graph) { - foreach ($graph->getNodes() as $node) { - /** @var BaseType $schema */ - foreach ($node as $schema) { - $buffer .= $schema->toScript(); - } - } - } - - return $buffer; - } - - /** - * @throws \InvalidArgumentException - */ - public function createSchemaOrgTypeFromArray(array $jsonLd): Type - { - if ($this->getContaoJsLdManager()) { - return $this->getContaoJsLdManager()->createSchemaOrgTypeFromArray($jsonLd); - } - - if (!isset($jsonLd['@type'])) { - throw new \InvalidArgumentException('Must provide the @type property!'); - } - - $schemaClass = '\Spatie\SchemaOrg\\' . $jsonLd['@type']; - - if (!class_exists($schemaClass)) { - throw new \InvalidArgumentException(sprintf('Unknown schema.org type "%s" provided!', $jsonLd['@type'])); - } - - $schema = new $schemaClass(); - unset($jsonLd['@type']); - - foreach ($jsonLd as $k => $v) { - if (\is_array($v) && isset($v['@type'])) { - $v = $this->createSchemaOrgTypeFromArray($v); - } - - $schema->setProperty($k, $v); - } - - return $schema; - } - - public static function getSubscribedServices(): array - { - $services = []; - - if (class_exists(ResponseContextAccessor::class)) { - $services[] = '?' . ResponseContextAccessor::class; - } - - return $services; - } - - private function recursiveKeySort(array &$array): void - { - foreach ($array as &$value) { - if (\is_array($value)) { - self::recursiveKeySort($value); - } - } - - ksort($array); - } - - private function getContaoJsLdManager() - { - if (!class_exists(ResponseContextAccessor::class) || !class_exists(ContaoJsonLdManager::class)) { - return null; - } - - if (!$this->container->has(ResponseContextAccessor::class) - || !$this->container->get(ResponseContextAccessor::class)->getResponseContext()->has(ContaoJsonLdManager::class)) { - return null; - } - - return $this->container->get(ResponseContextAccessor::class)->getResponseContext()->get(ContaoJsonLdManager::class); - } -} diff --git a/src/Model/PageModel.php b/src/Model/PageModel.php index 2b46ced..19c6553 100644 --- a/src/Model/PageModel.php +++ b/src/Model/PageModel.php @@ -3,7 +3,6 @@ namespace HeimrichHannot\HeadBundle\Model; /** - * @property bool $headAddBreadcrumbSchema * @property bool $addHeadDefaultImage * @property string $headDefaultImage * @property bool $headAddOrganisationSchema @@ -11,7 +10,6 @@ * @property string $headOrganisationLogo * @property string $headOrganisationWebsite * @property bool $headAddWebSiteSchema - * @property bool $headAddWebPageSchema * @property string $twitterSite */ class PageModel extends \Contao\PageModel diff --git a/src/Twig/Extension/HeadBundleTwigExtension.php b/src/Twig/Extension/HeadBundleTwigExtension.php deleted file mode 100644 index 123d958..0000000 --- a/src/Twig/Extension/HeadBundleTwigExtension.php +++ /dev/null @@ -1,31 +0,0 @@ -jsonLdManager = $jsonLdManager; - } - - public function add(array $jsonLd): void - { - $type = $this->jsonLdManager->createSchemaOrgTypeFromArray($jsonLd); - - $this->jsonLdManager - ->getGraphForSchema(JsonLdManager::SCHEMA_ORG) - ->set($type, $jsonLd['identifier'] ?? Graph::IDENTIFIER_DEFAULT) - ; - } -}