Skip to content

Commit

Permalink
generate sitemap.xml per siteAccess && inject the multilingual and mu…
Browse files Browse the repository at this point in the history
…ltinational site annotations tag.
  • Loading branch information
mohamed-larbi-jebari committed May 31, 2024
1 parent ba9e83c commit a4d7791
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 68 deletions.
238 changes: 180 additions & 58 deletions components/SEOBundle/bundle/Controller/SitemapController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult;
use Ibexa\Contracts\Core\Variation\VariationHandler;
use Ibexa\Core\Helper\FieldHelper;
use Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter;
use Novactive\Bundle\eZSEOBundle\Core\Helper\SiteMapHelper;
use Novactive\Bundle\eZSEOBundle\Core\Sitemap\QueryFactory;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Throwable;

Expand Down Expand Up @@ -71,16 +73,41 @@ public function __construct(
*/
public function indexAction(QueryFactory $queryFactory): Response
{
$isMultisiteAccess = $this->getConfigResolver()
->getParameter('multi_siteaccess_sitemap', 'nova_ezseo') ?? false;
// Dom Doc
$sitemap = new DOMDocument('1.0', 'UTF-8');
$sitemap->formatOutput = true;
// Create an index for multi site
$root = $sitemap->createElement('sitemapindex');
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$sitemap->appendChild($root);

$this->fillSitemapIndex($sitemap, $root, $queryFactory);
if ($isMultisiteAccess) {
$root = $sitemap->createElement('sitemapindex');
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$sitemap->appendChild($root);

$this->fillSitemapMultiSiteIndex($sitemap, $root, $queryFactory);
} else {
$searchService = $this->getRepository()->getSearchService();
$query = $queryFactory();
$query->limit = 0;
$resultCount = $searchService->findLocations($query)->totalCount;
// create an index if we are greater than th PACKET_MAX
if ($resultCount > static::PACKET_MAX) {
$root = $sitemap->createElement('sitemapindex');
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$sitemap->appendChild($root);

$this->fillSitemapIndex($sitemap, $resultCount, $root);
} else {
// if we are less or equal than the PACKET_SIZE, redo the search with no limit and list directly the urlmap
$query->limit = $resultCount;
$results = $searchService->findLocations($query);
$root = $sitemap->createElement('urlset');
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$root->setAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');
$this->fillSitemap($sitemap, $root, $results);
$sitemap->appendChild($root);
}
}
$response = new Response($sitemap->saveXML(), 200, ['Content-type' => 'text/xml']);
$response->setSharedMaxAge(86400);

Expand All @@ -94,16 +121,45 @@ public function indexAction(QueryFactory $queryFactory): Response
*/
public function pageAction(QueryFactory $queryFactory, int $page = 1): Response
{
$schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9" .
" http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd".
" http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1".
" http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" .
" http://www.google.com/schemas/sitemap-video/1.1";
$isMultisiteAccess = $this->getConfigResolver()
->getParameter('multi_siteaccess_sitemap', 'nova_ezseo') ?? false;

$sitemap = new DOMDocument('1.0', 'UTF-8');
$root = $sitemap->createElement('urlset');
$sitemap->formatOutput = true;
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$root->setAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');

// Create an index for multi site
if ($isMultisiteAccess) {
$this->multisiteAccessPage($sitemap, $root, $queryFactory, $page);
} else {
$sitemap->appendChild($root);
$query = $queryFactory();
$query->limit = static::PACKET_MAX;
$query->offset = static::PACKET_MAX * ($page - 1);
$searchService = $this->getRepository()->getSearchService();
$results = $searchService->findLocations($query);
$this->fillSitemap($sitemap, $root, $results);
}

$response = new Response($sitemap->saveXML(), 200, ['Content-type' => 'text/xml']);
$response->setSharedMaxAge(86400);

return $response;
}

public function multisiteAccessPage(
DOMDocument $sitemap,
DOMElement $root,
QueryFactory $queryFactory,
int $page = 1
): void {
$schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9" .
" http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd".
" http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1".
" http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" .
" http://www.google.com/schemas/sitemap-video/1.1";
$root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$root->setAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
$root->setAttribute('xmlns:video', 'http://www.google.com/schemas/sitemap-video/1.1');
Expand All @@ -124,75 +180,44 @@ public function pageAction(QueryFactory $queryFactory, int $page = 1): Response
$searchService = $this->getRepository()->getSearchService();

$results = $searchService->findLocations($query);
$this->fillSitemap($sitemap, $root, $results);
$this->fillMultiLanguagesSitemap($sitemap, $root, $results);
}


$response = new Response($sitemap->saveXML(), 200, ['Content-type' => 'text/xml']);
$response->setSharedMaxAge(86400);

return $response;
}

/**
* Fill a sitemap.
*/
protected function fillSitemap(DOMDocument $sitemap, DOMElement $root, SearchResult $results): void
{
$currentSiteAccess = $this->siteMapHelper->getCurrentSiteAccess();
$siteAccesses = $this->getConfigResolver()->getParameter('translation_siteaccesses');
foreach ($results->searchHits as $searchHit) {
/**
* @var Location $location
*/
$location = $searchHit->valueObject;
try {
$url = $this->generateUrl(
UrlAliasRouter::URL_ALIAS_ROUTE_NAME,
['locationId' => $location->id],
UrlGeneratorInterface::ABSOLUTE_URL
);
} catch (\Exception $exception) {
$this->siteMapHelper->logException($exception);
continue;
}

$mainLanguageUrl = $this->siteMapHelper->generateLocationUrl($location->id, $currentSiteAccess);
if (null === $mainLanguageUrl || 0 != strpos($mainLanguageUrl, 'view/content/')) {
if (0 != strpos($url, 'view/content/')) {
continue;
}

$modified = $location->contentInfo->modificationDate->format('c');
$loc = $sitemap->createElement('loc', $mainLanguageUrl);
$loc = $sitemap->createElement('loc', $url);
$lastmod = $sitemap->createElement('lastmod', $modified);
$urlElt = $sitemap->createElement('url');

$urlElt->appendChild($loc);
// Inject the image tags if config is enabled
$this->injectImageTag($location, $sitemap, $urlElt);

try {
$languagesCodes = [];
$contentInfo = $location->contentInfo;
$contentLanguages = $this->repository->getContentService()
->loadVersionInfo($contentInfo)->getLanguages();
foreach ($contentLanguages as $language) {
$languagesCodes[] = $language->languageCode;
}
foreach ($siteAccesses as $siteAccess) {
$siteAccessMainLanguage = $this->siteMapHelper->getSiteAccessMainLanguage($siteAccess);
if (!in_array($this->siteMapHelper->getSiteAccessMainLanguage($siteAccess), $languagesCodes)) {
continue;
}

$url = $this->siteMapHelper->generateLocationUrl($location->id, $siteAccess);
$hreflang = $this->siteMapHelper->getHrefLang($siteAccessMainLanguage);
if (null === $mainLanguageUrl || 0 != strpos($url, 'view/content/')) {
continue;
}

$xhtml = $sitemap->createElement('xhtml:link');
$xhtml->setAttribute('rel', 'alternate');
$xhtml->setAttribute('hreflang', $hreflang);
$xhtml->setAttribute('href', $url);
$urlElt->appendChild($xhtml);
}
} catch (Throwable $e) {
$this->siteMapHelper->logException($e);
continue;
}


$urlElt->appendChild($loc);
$urlElt->appendChild($lastmod);
$root->appendChild($urlElt);
}
Expand All @@ -201,7 +226,33 @@ protected function fillSitemap(DOMDocument $sitemap, DOMElement $root, SearchRes
/**
* Fill the sitemap index.
*/
protected function fillSitemapIndex(
protected function fillSitemapIndex(DOMDocument $sitemap, int $numberOfResults, DOMElement $root): void
{
$numberOfPage = (int) ceil($numberOfResults / static::PACKET_MAX);
for ($sitemapNumber = 1; $sitemapNumber <= $numberOfPage; ++$sitemapNumber) {
$sitemapElt = $sitemap->createElement('sitemap');

try {
$locUrl = $this->generateUrl(
'_novaseo_sitemap_page',
['page' => $sitemapNumber],
UrlGeneratorInterface::ABSOLUTE_URL
);
} catch (\Exception $exception) {
$this->siteMapHelper->logException($exception);
continue;
}

$loc = $sitemap->createElement('loc', $locUrl);
$date = new DateTime();
$modificationDate = $date->format('c');
$mod = $sitemap->createElement('lastmod', $modificationDate);
$sitemapElt->appendChild($loc);
$sitemapElt->appendChild($mod);
$root->appendChild($sitemapElt);
}
}
protected function fillSitemapMultiSiteIndex(
DOMDocument $sitemap,
DOMElement $root,
QueryFactory $queryFactory
Expand Down Expand Up @@ -243,8 +294,79 @@ protected function fillSitemapIndex(
});
}

public function injectImageTag($location, DOMDocument $sitemap, DOMElement $root): void
{
/**
* Fill a sitemap.
*/
protected function fillMultiLanguagesSitemap(
DOMDocument $sitemap,
DOMElement $root,
SearchResult $results
): void {
$currentSiteAccess = $this->siteMapHelper->getCurrentSiteAccess();
foreach ($results->searchHits as $searchHit) {
/**
* @var Location $location
*/
$location = $searchHit->valueObject;

$mainLanguageUrl = $this->siteMapHelper->generateLocationUrl($location->id, $currentSiteAccess);
if (null === $mainLanguageUrl || 0 != strpos($mainLanguageUrl, 'view/content/')) {
continue;
}

$modified = $location->contentInfo->modificationDate->format('c');
$loc = $sitemap->createElement('loc', $mainLanguageUrl);
$lastmod = $sitemap->createElement('lastmod', $modified);
$urlElt = $sitemap->createElement('url');

$urlElt->appendChild($loc);
// Inject the image tags if config is enabled
$this->injectImageTag($location, $sitemap, $urlElt);
// Inject the alternate lang tags if config is enabled
$this->injectAlternateLangTag($location, $sitemap, $urlElt);

$urlElt->appendChild($lastmod);
$root->appendChild($urlElt);
}
}

public function injectAlternateLangTag($location, DOMDocument $sitemap, DOMElement $root): void {
$isMultiLanguages = $this->getConfigResolver()->getParameter('multi_languages_sitemap', 'nova_ezseo');
if ($isMultiLanguages) {
try {
$siteAccesses = $this->getConfigResolver()->getParameter('translation_siteaccesses');
$languagesCodes = [];
$contentInfo = $location->contentInfo;
$contentLanguages = $this->repository->getContentService()
->loadVersionInfo($contentInfo)->getLanguages();
foreach ($contentLanguages as $language) {
$languagesCodes[] = $language->languageCode;
}
foreach ($siteAccesses as $siteAccess) {
$siteAccessMainLanguage = $this->siteMapHelper->getSiteAccessMainLanguage($siteAccess);
if (!in_array($this->siteMapHelper->getSiteAccessMainLanguage($siteAccess), $languagesCodes)) {
continue;
}

$url = $this->siteMapHelper->generateLocationUrl($location->id, $siteAccess);
$hreflang = $this->siteMapHelper->getHrefLang($siteAccessMainLanguage);
if (null === $url || 0 != strpos($url, 'view/content/')) {
continue;
}

$xhtml = $sitemap->createElement('xhtml:link');
$xhtml->setAttribute('rel', 'alternate');
$xhtml->setAttribute('hreflang', $hreflang);
$xhtml->setAttribute('href', $url);
$root->appendChild($xhtml);
}
} catch (Throwable $e) {
$this->siteMapHelper->logException($e);
}
}
}

public function injectImageTag($location, DOMDocument $sitemap, DOMElement $root): void {
$displayImage = $this->getConfigResolver()->getParameter('display_images_in_sitemap', 'nova_ezseo');

if (true === $displayImage) {
Expand Down
17 changes: 7 additions & 10 deletions components/SEOBundle/bundle/Core/Sitemap/QueryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,19 @@ public function __invoke(
// always here, we want visible Contents
$criterions = [new Criterion\Visibility(Criterion\Visibility::VISIBLE)];

// do we want to limit per Root Location, but default we don't
$limitToRootLocation = $this->configResolver->getParameter('limit_to_rootlocation', 'nova_ezseo');
if ((int) $rootLocationId) {
try {
$rootLocation = $this->repository->getLocationService()->loadLocation($rootLocationId);
} catch (NotFoundException|UnauthorizedException $e) {
$rootLocation = null;
}
} else {
$rootLocation = null;
}

if (!$rootLocation) {
$rootLocation = $this->getRootLocation();
}

if ($rootLocation) {
$criterions[] = new Criterion\Subtree($rootLocation->pathString);
if ($rootLocation){
$criterions[] = new Criterion\Subtree($rootLocation->pathString);
}
} elseif (true === $limitToRootLocation) {
$criterions[] = new Criterion\Subtree($this->getRootLocation()->pathString);
}

// Inclusions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('google_gatracker')->defaultValue('~')->end()
->scalarNode('google_anonymizeIp')->defaultValue('~')->end()
->scalarNode('bing_verification')->defaultValue('~')->end()
->booleanNode('limit_to_rootlocation')->defaultFalse()->end()
->booleanNode('multi_siteaccess_sitemap')->defaultFalse()->end()
->booleanNode('multi_languages_sitemap')->defaultFalse()->end()
->booleanNode('display_images_in_sitemap')->defaultFalse()->end()
->scalarNode('fieldtype_metas_identifier')->defaultValue('metas')->end()
->arrayNode('fieldtype_metas')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ public function load(array $configs, ContainerBuilder $container): void
$processor->mapSetting('google_gatracker', $config);
$processor->mapSetting('google_anonymizeIp', $config);
$processor->mapSetting('bing_verification', $config);
$processor->mapSetting('limit_to_rootlocation', $config);
$processor->mapSetting('display_images_in_sitemap', $config);
$processor->mapSetting('multi_siteaccess_sitemap', $config);
$processor->mapSetting('multi_languages_sitemap', $config);
$processor->mapSetting('robots', $config);
$processor->mapConfigArray('fieldtype_metas', $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL);
$processor->mapConfigArray('default_metas', $config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ parameters:
nova_ezseo.default.google_anonymizeIp: ~
nova_ezseo.default.bing_verification: ~
nova_ezseo.default.custom_fallback_service: ~
nova_ezseo.default.limit_to_rootlocation: false
nova_ezseo.default.display_images_in_sitemap: false
nova_ezseo.default.multi_siteaccess_sitemap: false
nova_ezseo.default.multi_languages_sitemap: false
nova_ezseo.default.sitemap_excludes:
locations: []
subtrees: []
Expand Down
Loading

0 comments on commit a4d7791

Please sign in to comment.