diff --git a/cms/contents/page/config.yaml b/cms/contents/page/config.yaml index e78666e3..4c073c21 100644 --- a/cms/contents/page/config.yaml +++ b/cms/contents/page/config.yaml @@ -1,6 +1,6 @@ # this is the page base configuration content: - revision: 2 + revision: 3 entity_class: 'Softspring\CmsBundle\Entity\Page' default_layout: 'default' version_seo: @@ -10,6 +10,10 @@ content: type: translation metaKeywords: type: translation + canonicalContent: + type: translatable + type_options: + type: content indexing: noIndex: type: checkbox @@ -41,7 +45,7 @@ content: admin_page.form.indexing.sitemapChangefreq.values.weekly: weekly admin_page.form.indexing.sitemapChangefreq.values.monthly: monthly admin_page.form.indexing.sitemapChangefreq.values.yearly: yearly - admin_page.form.indexing.sitemapChangefreq.values.never: never + admin_page.form.indexing.sitemapChangefreq.values.never: never sitemapPriority: type: number type_options: diff --git a/cms/contents/page/seo-migrate.php b/cms/contents/page/seo-migrate.php index 58fc8a1c..2d5091a2 100644 --- a/cms/contents/page/seo-migrate.php +++ b/cms/contents/page/seo-migrate.php @@ -13,5 +13,14 @@ } } + // canonicalContent became translatable in revision 3. + // Migrate previous scalar/entity value to a translatable structure. + if ($originVersion < 3 && $targetVersion >= 3 && isset($seo['canonicalContent']) && !is_array($seo['canonicalContent'])) { + $seo['canonicalContent'] = \Softspring\TranslatableBundle\Model\Translation::createFromArray([ + 'en' => $seo['canonicalContent'], + '_default' => 'en', + ]); + } + return $seo; }; diff --git a/cms/contents/page/translations/sfs_cms_contents.en.yaml b/cms/contents/page/translations/sfs_cms_contents.en.yaml index 470776b5..2e02beb0 100644 --- a/cms/contents/page/translations/sfs_cms_contents.en.yaml +++ b/cms/contents/page/translations/sfs_cms_contents.en.yaml @@ -311,6 +311,9 @@ admin_page: metaTitle.label: "Title (browser title and meta tags)" metaDescription.label: "Description (meta tag)" metaKeywords.label: "Keywords (meta tag)" + canonicalContent.label: "Canonical URL source" + canonicalContent.placeholder: "Use this page as canonical (default)" + canonicalContent.help: "If selected, this page will output a canonical URL pointing to the selected page." version_form: note.label: "Note" diff --git a/cms/contents/page/translations/sfs_cms_contents.es.yaml b/cms/contents/page/translations/sfs_cms_contents.es.yaml index f1d10709..b5945f39 100644 --- a/cms/contents/page/translations/sfs_cms_contents.es.yaml +++ b/cms/contents/page/translations/sfs_cms_contents.es.yaml @@ -311,6 +311,9 @@ admin_page: metaTitle.label: "Título (título del navegador y etiquetas meta)" metaDescription.label: "Descripción (etiqueta meta)" metaKeywords.label: "Palabras clave (etiqueta meta)" + canonicalContent.label: "Fuente de URL canónica" + canonicalContent.placeholder: "Usar esta página como canónica (por defecto)" + canonicalContent.help: "Si se selecciona, esta página generará la URL canónica apuntando a la página seleccionada." version_form: note.label: "Nota" diff --git a/cms/layouts/default/render.html.twig b/cms/layouts/default/render.html.twig index 40db06b5..1a6dec5b 100644 --- a/cms/layouts/default/render.html.twig +++ b/cms/layouts/default/render.html.twig @@ -21,9 +21,18 @@ - {% for locale, url in sfs_cms_alternate_urls() %} - - {% endfor %} + {% set canonical_content = version.seo.canonicalContent|default(null) %} + {% if canonical_content is iterable %} + {% set canonical_content = canonical_content[app.request.locale]|default(null) %} + {% endif %} + {% if canonical_content and canonical_content != version.content %} + + {% else %} + + {% for locale, url in sfs_cms_alternate_urls() %} + + {% endfor %} + {% endif %} {% endblock seo %} {% block body %} @@ -93,4 +102,4 @@ }); {% endif %} -{% endblock javascripts %} \ No newline at end of file +{% endblock javascripts %} diff --git a/src/EntityTransformer/ContentVersionTransformer.php b/src/EntityTransformer/ContentVersionTransformer.php index 53ff8d10..506d3294 100644 --- a/src/EntityTransformer/ContentVersionTransformer.php +++ b/src/EntityTransformer/ContentVersionTransformer.php @@ -77,14 +77,16 @@ public function untransform(object $entity, ObjectManager $em): void } if ($contentVersion->getSeo()) { - $seo = $contentVersion->getSeo(); - $config = $this->cmsConfig->getContent($contentVersion->getContent()); - $seo = DataMigrator::migrate($config['seo_revision_migration_scripts'], $seo, $config['revision'], $this->cmsConfig); - foreach ($seo as $field => $value) { - $seo[$field] = $this->untransformEntityValues($value, $em); - } - $contentVersion->setSeo($seo); + + $contentVersion->_setSeoCallback(function (array $seo) use ($em, $config): array { + $seo = DataMigrator::migrate($config['seo_revision_migration_scripts'], $seo, $config['revision'], $this->cmsConfig); + foreach ($seo as $field => $value) { + $seo[$field] = $this->untransformEntityValues($value, $em); + } + + return $seo; + }); } } diff --git a/src/Form/Type/ContentType.php b/src/Form/Type/ContentType.php new file mode 100644 index 00000000..52c2de6b --- /dev/null +++ b/src/Form/Type/ContentType.php @@ -0,0 +1,38 @@ +setDefaults([ + 'class' => ContentInterface::class, + 'em' => $this->sfsContentEm, + 'required' => false, + 'choice_label' => function (ContentInterface $content) { + return $content->getName(); + }, + 'group_by' => function (ContentInterface $content) { + return $this->translator->trans("{$this->contentManager->getType($content)}.name", [], 'sfs_cms_contents'); + }, + ]); + } +} diff --git a/src/Helper/RoutingHelper.php b/src/Helper/RoutingHelper.php index 81ecff8b..aa4b95cb 100644 --- a/src/Helper/RoutingHelper.php +++ b/src/Helper/RoutingHelper.php @@ -91,34 +91,43 @@ public function generatePath($route, ?string $locale = null, $site = null): stri return $this->generateUrl($route, $locale, $site, UrlGeneratorInterface::ABSOLUTE_PATH); } + public function generateUrlForContent($content, string $locale, $site = null): string + { + foreach ($content->getRoutes() as $route) { + if ($route->getPathForLocale($locale)) { + return $this->generateUrl($route, $locale, $site); + } + } + + return '#'; + } + public function generateUrl($route, ?string $locale = null, $site = null, int $referenceType = UrlGeneratorInterface::ABSOLUTE_URL): string { if (is_null($route)) { return '#'; } - if (is_array($route)) { - if (is_null($route['route_name'])) { - return '#'; - } + $params = []; + $params['_locale'] = $locale ?: ($this->requestStack->getCurrentRequest()?->getLocale() ?: 'en'); - $params = $route['route_params'] ?? []; + if ($site) { + $params['_site'] = $site; + } - $params['_locale'] = $locale ?: ($this->requestStack->getCurrentRequest()?->getLocale() ?: 'en'); + $routeName = $route; - if ($site) { - $params['_site'] = $site; + if (is_array($route)) { + if (is_null($route['route_name'])) { + return '#'; } - return $this->router->generate($route['route_name'], $params, $referenceType); + $params = array_merge($params, $route['route_params'] ?? []); + $routeName = $route['route_name']; } elseif ($route instanceof RouteInterface) { - return $this->router->generate($route->getId(), [], $referenceType); + $routeName = $route->getId(); } - $params = [ - '_locale' => $locale ?: ($this->requestStack->getCurrentRequest()?->getLocale() ?: 'en'), - ]; - - return $this->router->generate($route, $params, $referenceType); + return $this->router->generate($routeName, $params, $referenceType); } } diff --git a/src/Model/ContentVersion.php b/src/Model/ContentVersion.php index 4eaba454..789a7cc9 100644 --- a/src/Model/ContentVersion.php +++ b/src/Model/ContentVersion.php @@ -16,6 +16,10 @@ abstract class ContentVersion implements ContentVersionInterface protected ?array $seo = null; + protected mixed $_getSeoCallback = null; + + protected mixed $_rawSeo = null; + public function getContent(): ?ContentInterface { return $this->content; @@ -49,8 +53,19 @@ public function setLayout(?string $layout): void $this->layout = $layout; } + public function _setSeoCallback(callable $getSeoCallback): void + { + $this->_getSeoCallback = $getSeoCallback; + } + public function getSeo(): ?array { + if ($this->_getSeoCallback) { + $this->_rawSeo = $this->seo; + $this->seo = call_user_func($this->_getSeoCallback, $this->seo); + $this->_getSeoCallback = null; + } + return $this->seo; } @@ -58,4 +73,9 @@ public function setSeo(?array $seo): void { $this->seo = $seo; } + + public function getRawSeo(): ?array + { + return $this->_rawSeo ?? $this->seo; + } } diff --git a/src/Routing/UrlGenerator.php b/src/Routing/UrlGenerator.php index ec85e5d8..0388de41 100644 --- a/src/Routing/UrlGenerator.php +++ b/src/Routing/UrlGenerator.php @@ -48,7 +48,7 @@ public function getUrl($routeOrName, ?string $locale = null, $site = null, array } if ($route->getContent() && !$route->getContent()->getPublishedVersion()) { - return '#'; + return '#not-published'; } $queryString = !empty($routeParams) ? '?'.http_build_query($routeParams) : ''; diff --git a/src/Twig/Extension/RouterExtension.php b/src/Twig/Extension/RouterExtension.php index 016ff2a3..e5560b0c 100644 --- a/src/Twig/Extension/RouterExtension.php +++ b/src/Twig/Extension/RouterExtension.php @@ -24,6 +24,7 @@ public function getFunctions(): array new TwigFunction('sfs_cms_link_attr', [$this, 'generateLinkAttributes'], ['is_safe' => ['html']]), new TwigFunction('sfs_cms_resolve_request_from_url', [$this->routingHelper, 'resolveRequestFromUrl']), new TwigFunction('sfs_cms_url', [$this->routingHelper, 'generateUrl']), + new TwigFunction('sfs_cms_url_for_content', [$this->routingHelper, 'generateUrlForContent']), new TwigFunction('sfs_cms_path', [$this->routingHelper, 'generatePath']), new TwigFunction('sfs_cms_route_path_url', [$this->urlGenerator, 'getUrlFixed']), // TODO REVIEW THIS, check if it works with symfony native routes new TwigFunction('sfs_cms_route_path_path', [$this->urlGenerator, 'getPathFixed']), // TODO REVIEW THIS, check if it works with symfony native routes diff --git a/src/Twig/Extension/TranslateExtension.php b/src/Twig/Extension/TranslateExtension.php index 0c801a88..0922a3e4 100644 --- a/src/Twig/Extension/TranslateExtension.php +++ b/src/Twig/Extension/TranslateExtension.php @@ -160,4 +160,5 @@ public function getLocalePaths(?string $defaultRoute = null): array return $localePaths; } + } diff --git a/templates/admin/content/_read_indexing.html.twig b/templates/admin/content/_read_indexing.html.twig index f022c222..e2a340b6 100644 --- a/templates/admin/content/_read_indexing.html.twig +++ b/templates/admin/content/_read_indexing.html.twig @@ -40,4 +40,4 @@ {% endblock %} -{% endembed %} \ No newline at end of file +{% endembed %} diff --git a/templates/admin/content/_version_info_seo.html.twig b/templates/admin/content/_version_info_seo.html.twig index 30eab94d..da436059 100644 --- a/templates/admin/content/_version_info_seo.html.twig +++ b/templates/admin/content/_version_info_seo.html.twig @@ -6,7 +6,7 @@ {% block content %}
{{ version_entity.seo|json_encode(constant('JSON_PRETTY_PRINT')) }}
+ style="max-height: 300px">{{ version_entity.rawSeo()|json_encode(constant('JSON_PRETTY_PRINT')) }}
{% endblock content %} {% endembed %} diff --git a/tests/Unit/Entity/PageTest.php b/tests/Unit/Entity/PageTest.php index 4d72b329..12dcfe73 100644 --- a/tests/Unit/Entity/PageTest.php +++ b/tests/Unit/Entity/PageTest.php @@ -115,4 +115,4 @@ public function testLastVersionNumber(): void $page->setLastVersionNumber(1); $this->assertEquals(1, $page->getLastVersionNumber()); } -} \ No newline at end of file +}