Skip to content

Commit

Permalink
[!!!][FEATURE] Support multiple sitemaps to be located
Browse files Browse the repository at this point in the history
Related: #199
  • Loading branch information
eliashaeussler committed Jul 21, 2023
1 parent 5714fe8 commit bb86699
Show file tree
Hide file tree
Showing 21 changed files with 375 additions and 201 deletions.
24 changes: 20 additions & 4 deletions Classes/Backend/ContextMenu/ItemProviders/CacheWarmupProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use EliasHaeussler\Typo3Warming\Configuration;
use EliasHaeussler\Typo3Warming\Sitemap;
use EliasHaeussler\Typo3Warming\Utility;
use Exception;
use TYPO3\CMS\Backend;
use TYPO3\CMS\Core;

Expand Down Expand Up @@ -239,10 +240,25 @@ private function canWarmupCachesOfSite(Core\Site\Entity\SiteLanguage $siteLangua
$site = $this->getCurrentSite();
$languageId = $siteLanguage?->getLanguageId();

return $site !== null
&& $site->getRootPageId() === (int)$this->identifier
&& Utility\AccessUtility::canWarmupCacheOfSite($site, $languageId)
&& $this->sitemapLocator->siteContainsSitemap($site, $siteLanguage);
if ($site === null ||
$site->getRootPageId() !== (int)$this->identifier ||
!Utility\AccessUtility::canWarmupCacheOfSite($site, $languageId)
) {
return false;
}

// Check if any sitemap exists
try {
foreach ($this->sitemapLocator->locateBySite($site, $siteLanguage) as $sitemap) {
if ($this->sitemapLocator->sitemapExists($sitemap)) {
return true;
}
}
} catch (Exception) {
// Unable to locate any sitemaps
}

return false;
}

private function getCurrentSite(): ?Core\Site\Entity\Site
Expand Down
60 changes: 42 additions & 18 deletions Classes/Cache/SitemapsCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,51 +43,75 @@ public function __construct(
}

/**
* @return list<Sitemap\SiteAwareSitemap>
* @throws Exception\InvalidUrlException
*/
public function get(
Core\Site\Entity\Site $site,
Core\Site\Entity\SiteLanguage $siteLanguage = null,
): Sitemap\SiteAwareSitemap|null {
/** @var array<string, array<string, string>>|false $cacheData */
): array {
/** @var array<string, array<string, string|list<string>>>|false $cacheData */
$cacheData = $this->cache->require(self::ENTRY_IDENTIFIER);

// Early return if cache is empty
if ($cacheData === false) {
return null;
return [];
}

// Fetch sitemap from cache data
// Fetch sitemaps from cache data
$siteIdentifier = $site->getIdentifier();
$languageIdentifier = $this->buildLanguageIdentifier($site, $siteLanguage);
$sitemap = $cacheData[$siteIdentifier][$languageIdentifier] ?? null;
$sitemaps = $cacheData[$siteIdentifier][$languageIdentifier] ?? null;

// Early return if sitemap is not cached
if (!\is_string($sitemap)) {
return null;
// BC: Convert single sitemap to sitemaps array
if (\is_string($sitemaps)) {
$sitemaps = [$sitemaps];
}

return new Sitemap\SiteAwareSitemap(
new Core\Http\Uri($sitemap),
$site,
$siteLanguage ?? $site->getDefaultLanguage(),
// Early return if sitemaps are not cached
if (!\is_array($sitemaps)) {
return [];
}

return array_values(
array_map(
static fn (string $sitemapUrl) => new Sitemap\SiteAwareSitemap(
new Core\Http\Uri($sitemapUrl),
$site,
$siteLanguage ?? $site->getDefaultLanguage(),
),
array_filter($sitemaps, 'is_string'),
),
);
}

public function set(Sitemap\SiteAwareSitemap $sitemap): void
/**
* @param list<Sitemap\SiteAwareSitemap> $sitemaps
*/
public function set(array $sitemaps): void
{
/** @var array<string, array<string, string>>|false $cacheData */
/** @var array<string, array<string, string|list<string>>>|false $cacheData */
$cacheData = $this->cache->require(self::ENTRY_IDENTIFIER);

// Enforce array for cached data
if ($cacheData === false) {
$cacheData = [];
}

// Append sitemap url to cache data
$siteIdentifier = $sitemap->getSite()->getIdentifier();
$languageIdentifier = $this->buildLanguageIdentifier($sitemap->getSite(), $sitemap->getSiteLanguage());
$cacheData[$siteIdentifier][$languageIdentifier] = (string)$sitemap->getUri();
// Append sitemap urls to cache data
foreach ($sitemaps as $sitemap) {
$siteIdentifier = $sitemap->getSite()->getIdentifier();
$languageIdentifier = $this->buildLanguageIdentifier($sitemap->getSite(), $sitemap->getSiteLanguage());
$cachedUrls = $cacheData[$siteIdentifier][$languageIdentifier] ?? [];

// BC: Convert single sitemap to sitemaps array
if (\is_string($cachedUrls)) {
$cachedUrls = [$cachedUrls];
}

$cachedUrls[] = (string)$sitemap->getUri();
$cacheData[$siteIdentifier][$languageIdentifier] = $cachedUrls;
}

$this->cache->set(
self::ENTRY_IDENTIFIER,
Expand Down
9 changes: 5 additions & 4 deletions Classes/Command/WarmupCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,11 @@ private function resolveSites(array $sites, array $languages): array
}

foreach ($languageIds as $languageId) {
$resolvedSitemaps[] = (string)$this->sitemapLocator->locateBySite(
$site,
$site->getLanguageById($languageId)
)->getUri();
$sitemaps = $this->sitemapLocator->locateBySite($site, $site->getLanguageById($languageId));

foreach ($sitemaps as $sitemap) {
$resolvedSitemaps[] = (string)$sitemap->getUri();
}
}
}
}
Expand Down
28 changes: 15 additions & 13 deletions Classes/Controller/FetchSitesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,22 @@ private function createSiteGroup(Core\Site\Entity\Site $site, array $page): ?Val
$items = [];

// Check all available languages for possible sitemaps
foreach ($this->sitemapLocator->locateAllBySite($site) as $sitemap) {
$siteLanguage = $sitemap->getSiteLanguage();
$url = null;

// Check if sitemap is accessible
if ($this->sitemapLocator->siteContainsSitemap($site, $siteLanguage)) {
$url = (string)$sitemap->getUri();
foreach ($this->sitemapLocator->locateAllBySite($site) as $i => $sitemaps) {
foreach ($sitemaps as $sitemap) {
$siteLanguage = $sitemap->getSiteLanguage();
$url = null;

// Check if sitemap exists
if ($this->sitemapLocator->sitemapExists($sitemap)) {
$url = (string)$sitemap->getUri();
}

$items[] = new ValueObject\Modal\SiteGroupItem(
$siteLanguage,
$siteLanguage === $site->getDefaultLanguage(),
$url,
);
}

$items[] = new ValueObject\Modal\SiteGroupItem(
$siteLanguage,
$siteLanguage === $site->getDefaultLanguage(),
$url,
);
}

// Early return if no languages are available
Expand Down
4 changes: 2 additions & 2 deletions Classes/Service/CacheWarmupService.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ public function warmup(
foreach ($sites as $siteWarmupRequest) {
foreach ($siteWarmupRequest->getLanguageIds() as $languageId) {
$siteLanguage = $siteWarmupRequest->getSite()->getLanguageById($languageId);
$sitemap = $this->sitemapLocator->locateBySite($siteWarmupRequest->getSite(), $siteLanguage);
$cacheWarmer->addSitemaps($sitemap);
$sitemaps = $this->sitemapLocator->locateBySite($siteWarmupRequest->getSite(), $siteLanguage);
$cacheWarmer->addSitemaps($sitemaps);
}
}

Expand Down
6 changes: 4 additions & 2 deletions Classes/Sitemap/Provider/DefaultProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ final class DefaultProvider implements Provider
public function get(
Core\Site\Entity\Site $site,
Core\Site\Entity\SiteLanguage $siteLanguage = null,
): ?Sitemap\SiteAwareSitemap {
return new Sitemap\SiteAwareSitemap(
): array {
$sitemap = new Sitemap\SiteAwareSitemap(
Utility\HttpUtility::getSiteUrlWithPath($site, self::DEFAULT_PATH, $siteLanguage),
$site,
$siteLanguage ?? $site->getDefaultLanguage(),
);

return [$sitemap];
}

/**
Expand Down
9 changes: 5 additions & 4 deletions Classes/Sitemap/Provider/PageTypeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,26 @@ final class PageTypeProvider implements Provider
public function get(
Core\Site\Entity\Site $site,
Core\Site\Entity\SiteLanguage $siteLanguage = null,
): ?Sitemap\SiteAwareSitemap {
): array {
// Early return if EXT:seo is not installed
if (!Core\Utility\ExtensionManagementUtility::isLoaded('seo')) {
return null;
return [];
}

$pageTypeMap = $site->getConfiguration()['routeEnhancers']['PageTypeSuffix']['map'] ?? null;

// Early return if no page type map is configured
if (!\is_array($pageTypeMap) || !\in_array(self::EXPECTED_PAGE_TYPE, $pageTypeMap, true)) {
return null;
return [];
}

$uri = $site->getRouter()->generateUri('/', [
'type' => self::EXPECTED_PAGE_TYPE,
'_language' => $siteLanguage,
]);
$sitemap = new Sitemap\SiteAwareSitemap($uri, $site, $siteLanguage ?? $site->getDefaultLanguage());

return new Sitemap\SiteAwareSitemap($uri, $site, $siteLanguage ?? $site->getDefaultLanguage());
return [$sitemap];
}

/**
Expand Down
5 changes: 4 additions & 1 deletion Classes/Sitemap/Provider/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@
*/
interface Provider
{
/**
* @return list<Sitemap\SiteAwareSitemap>
*/
public function get(
Core\Site\Entity\Site $site,
Core\Site\Entity\SiteLanguage $siteLanguage = null,
): ?Sitemap\SiteAwareSitemap;
): array;

public static function getPriority(): int;
}
21 changes: 13 additions & 8 deletions Classes/Sitemap/Provider/RobotsTxtProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,29 @@ public function __construct(
public function get(
Core\Site\Entity\Site $site,
Core\Site\Entity\SiteLanguage $siteLanguage = null,
): ?Sitemap\SiteAwareSitemap {
): array {
$robotsTxtUri = Utility\HttpUtility::getSiteUrlWithPath($site, 'robots.txt', $siteLanguage);
$robotsTxt = $this->fetchRobotsTxt($robotsTxtUri);

// Early return if no robots.txt exists
if ($robotsTxt === null || trim($robotsTxt) === '') {
return null;
return [];
}

// Early return if no sitemap is specified in robots.txt
if (preg_match(self::SITEMAP_PATTERN, $robotsTxt, $matches) !== 1) {
return null;
if ((int)preg_match_all(self::SITEMAP_PATTERN, $robotsTxt, $matches) === 0) {
return [];
}

return new Sitemap\SiteAwareSitemap(
new Core\Http\Uri($matches['url']),
$site,
$siteLanguage ?? $site->getDefaultLanguage(),
return array_values(
array_map(
static fn (string $url) => new Sitemap\SiteAwareSitemap(
new Core\Http\Uri($url),
$site,
$siteLanguage ?? $site->getDefaultLanguage(),
),
$matches['url'],
),
);
}

Expand Down
8 changes: 5 additions & 3 deletions Classes/Sitemap/Provider/SiteProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ final class SiteProvider implements Provider
public function get(
Core\Site\Entity\Site $site,
Core\Site\Entity\SiteLanguage $siteLanguage = null,
): ?Sitemap\SiteAwareSitemap {
): array {
if ($siteLanguage !== null && $siteLanguage !== $site->getDefaultLanguage()) {
$sitemapPath = $siteLanguage->toArray()['xml_sitemap_path'] ?? null;
} else {
Expand All @@ -47,14 +47,16 @@ public function get(

// Early return if no sitemap path is configured
if (!\is_string($sitemapPath) || trim($sitemapPath) === '') {
return null;
return [];
}

return new Sitemap\SiteAwareSitemap(
$sitemap = new Sitemap\SiteAwareSitemap(
Utility\HttpUtility::getSiteUrlWithPath($site, $sitemapPath, $siteLanguage),
$site,
$siteLanguage ?? $site->getDefaultLanguage(),
);

return [$sitemap];
}

/**
Expand Down
Loading

0 comments on commit bb86699

Please sign in to comment.