From 018b3ebc5e398e31ab95fad793e73f95648649d1 Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Mon, 6 Jan 2025 16:37:32 +0100 Subject: [PATCH] 1.17 siteMap v5 --- core/class/autoload.php | 4 + core/class/jsondb/Dot.class.php | 10 +- core/class/sitemap/Config.php | 115 ++++++ core/class/sitemap/IConfig.class.php | 31 ++ core/class/sitemap/IFileSystem.class.php | 18 + core/class/sitemap/IRuntime.class.php | 22 + core/class/sitemap/SitemapGenerator.class.php | 381 +++++++++++------- core/core.php | 54 +-- 8 files changed, 463 insertions(+), 172 deletions(-) create mode 100644 core/class/sitemap/Config.php create mode 100644 core/class/sitemap/IConfig.class.php create mode 100644 core/class/sitemap/IFileSystem.class.php create mode 100644 core/class/sitemap/IRuntime.class.php diff --git a/core/class/autoload.php b/core/class/autoload.php index bf408d6..9d6ccd8 100644 --- a/core/class/autoload.php +++ b/core/class/autoload.php @@ -7,7 +7,11 @@ class autoload { require_once 'core/class/helper.class.php'; require_once 'core/class/template.class.php'; require_once 'core/class/layout.class.php'; + require_once 'core/class/sitemap/IConfig.class.php'; + require_once 'core/class/sitemap/Config.class.php'; + require_once 'core/class/sitemap/IRuntime.class.php'; require_once 'core/class/sitemap/Runtime.class.php'; + require_once 'core/class/sitemap/IFileSystem.class.php'; require_once 'core/class/sitemap/FileSystem.class.php'; require_once 'core/class/sitemap/SitemapGenerator.class.php'; require_once 'core/class/phpmailer/PHPMailer.class.php'; diff --git a/core/class/jsondb/Dot.class.php b/core/class/jsondb/Dot.class.php index 8ae39fb..7051549 100644 --- a/core/class/jsondb/Dot.class.php +++ b/core/class/jsondb/Dot.class.php @@ -21,7 +21,7 @@ class Dot implements \ArrayAccess, \Iterator, \Countable * * @param array|null $data Data */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { if (is_array($data)) { $this->data = $data; @@ -101,7 +101,7 @@ class Dot implements \ArrayAccess, \Iterator, \Countable } } else { // Iterate path - $keys = explode('.', (string) $key); + $keys = explode('.', (string)$key); if ($pop === true) { array_pop($keys); } @@ -199,7 +199,7 @@ class Dot implements \ArrayAccess, \Iterator, \Countable */ public function has($key) { - $keys = explode('.', (string) $key); + $keys = explode('.', (string)$key); $data = &$this->data; foreach ($keys as $key) { if (!isset($data[$key])) { @@ -371,7 +371,7 @@ class Dot implements \ArrayAccess, \Iterator, \Countable */ public function isEmpty(): bool { - return !(bool) count($this->data); + return !(bool)count($this->data); } /** @@ -391,7 +391,7 @@ class Dot implements \ArrayAccess, \Iterator, \Countable */ public function toJson() { - return json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + return json_encode($this->data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT); } /** diff --git a/core/class/sitemap/Config.php b/core/class/sitemap/Config.php new file mode 100644 index 0000000..ca6ae77 --- /dev/null +++ b/core/class/sitemap/Config.php @@ -0,0 +1,115 @@ +fs = null; + $this->runtime = null; + } + + /** + * @return string + */ + public function getBaseURL(): string + { + return $this->baseURL; + } + + /** + * @param string $baseURL + * @return Config + */ + public function setBaseURL(string $baseURL): Config + { + $this->baseURL = $baseURL; + return $this; + } + + /** + * @return string + */ + public function getSaveDirectory(): string + { + return $this->saveDirectory; + } + + /** + * @param string $saveDirectory + * @return Config + */ + public function setSaveDirectory(string $saveDirectory): Config + { + $this->saveDirectory = $saveDirectory; + return $this; + } + + /** + * @return IFileSystem|null + */ + public function getFS(): IFileSystem|null + { + return $this->fs; + } + + /** + * @param IFileSystem|null $fs + * @return Config + */ + public function setFS(IFileSystem|null $fs): Config + { + $this->fs = $fs; + return $this; + } + + /** + * @return IRuntime|null + */ + public function getRuntime(): IRuntime|null + { + return $this->runtime; + } + + /** + * @param IRuntime|null $runtime + * @return Config + */ + public function setRuntime(IRuntime|null $runtime): Config + { + $this->runtime = $runtime; + return $this; + } + + public function getSitemapIndexURL(): string + { + return $this->sitemapIndexURL; + } + + public function setSitemapIndexURL(string $sitemapIndexURL): Config + { + $this->sitemapIndexURL = $sitemapIndexURL; + return $this; + } +} \ No newline at end of file diff --git a/core/class/sitemap/IConfig.class.php b/core/class/sitemap/IConfig.class.php new file mode 100644 index 0000000..c736a71 --- /dev/null +++ b/core/class/sitemap/IConfig.class.php @@ -0,0 +1,31 @@ +\n") - private $sitemapUrlCount = 0; - private $generatedFiles = []; + private int $urlsetClosingTagLen = 10; // strlen("\n") + private int $sitemapURLCount = 0; + private array $generatedFiles = []; /** - * @param string $baseURL You site URL - * @param string $basePath Relative path where sitemap and robots should be stored. - * @param FileSystem|null $fs - * @param Runtime|null $runtime + * @param IConfig $config Configuration object. + * @throws InvalidArgumentException */ - public function __construct(string $baseURL, string $basePath = "", FileSystem $fs = null, Runtime $runtime = null) + public function __construct(IConfig $config) { - $this->urls = []; - $this->baseURL = rtrim($baseURL, '/'); + if ($config->getBaseURL() === '') { + throw new InvalidArgumentException('baseURL config parameter is required'); + } - if ($fs === null) { + $this->baseURL = rtrim($config->getBaseURL(), '/'); + $this->sitemapIndexURL = rtrim($config->getBaseURL(), '/'); + + if ($config->getSitemapIndexURL()) { + $this->sitemapIndexURL = rtrim($config->getSitemapIndexURL(), '/'); + } + + $configFS = $config->getFS(); + if ($configFS === null) { $this->fs = new FileSystem(); } else { - $this->fs = $fs; + $this->fs = $configFS; } - if ($runtime === null) { + $configRuntime = $config->getRuntime(); + if ($configRuntime === null) { $this->runtime = new Runtime(); } else { - $this->runtime = $runtime; + $this->runtime = $configRuntime; } - if ($this->runtime->is_writable($basePath) === false) { + if ($this->runtime->is_writable($config->getSaveDirectory()) === false) { throw new InvalidArgumentException( - sprintf('the provided basePath (%s) should be a writable directory,', $basePath) . + sprintf('the provided basePath (%s) should be a writable directory,', $config->getSaveDirectory()) . ' please check its existence and permissions' ); } - if (strlen($basePath) > 0 && substr($basePath, -1) != DIRECTORY_SEPARATOR) { - $basePath = $basePath . DIRECTORY_SEPARATOR; + + $this->saveDirectory = $config->getSaveDirectory(); + if (strlen($this->saveDirectory) > 0 && substr($this->saveDirectory, -1) != DIRECTORY_SEPARATOR) { + $this->saveDirectory = $this->saveDirectory . DIRECTORY_SEPARATOR; } - $this->basePath = $basePath; $this->xmlWriter = $this->createXmlWriter(); $this->flushedSitemapFilenameFormat = sprintf("sm-%%d-%d.xml", time()); @@ -267,7 +267,10 @@ class SitemapGenerator /** * @param string $filename + * * @return SitemapGenerator + * + * @throws InvalidArgumentException */ public function setSitemapFilename(string $filename = ''): SitemapGenerator { @@ -281,9 +284,26 @@ class SitemapGenerator return $this; } + /** + * @param string $path + * @return SitemapGenerator + * @throws InvalidArgumentException + */ + public function setSitemapStylesheet(string $path): SitemapGenerator + { + if (strlen($path) === 0) { + throw new InvalidArgumentException('sitemap stylesheet path should not be empty'); + } + $this->sitemapStylesheetLink = $path; + return $this; + } + /** * @param string $filename + * * @return $this + * + * @throws InvalidArgumentException */ public function setSitemapIndexFilename(string $filename = ''): SitemapGenerator { @@ -297,6 +317,7 @@ class SitemapGenerator /** * @param string $filename * @return $this + * @throws InvalidArgumentException */ public function setRobotsFileName(string $filename): SitemapGenerator { @@ -310,15 +331,16 @@ class SitemapGenerator /** * @param int $value * @return $this + * @throws OutOfRangeException */ - public function setMaxUrlsPerSitemap(int $value): SitemapGenerator + public function setMaxURLsPerSitemap(int $value): SitemapGenerator { if ($value < 1 || self::MAX_URLS_PER_SITEMAP < $value) { throw new OutOfRangeException( sprintf('value %d is out of range 1-%d', $value, self::MAX_URLS_PER_SITEMAP) ); } - $this->maxUrlsPerSitemap = $value; + $this->maxURLsPerSitemap = $value; return $this; } @@ -339,13 +361,19 @@ class SitemapGenerator return $this->isCompressionEnabled; } + /** + * @param string $path + * @param string|null $changeFrequency + * @param float|null $priority + * @param array $extensions + * @return void + * @throws InvalidArgumentException + */ public function validate( - string $path, - DateTime $lastModified = null, - string $changeFrequency = null, - float $priority = null, - array $alternates = null, - array $extensions = []) + string $path, + ?string $changeFrequency = null, + ?float $priority = null, + array $extensions = []): void { if (!(1 <= mb_strlen($path) && mb_strlen($path) <= self::MAX_URL_LEN)) { throw new InvalidArgumentException( @@ -360,8 +388,14 @@ class SitemapGenerator if ($priority !== null && !in_array($priority, $this->validPriorities)) { throw new InvalidArgumentException("Priority argument should be a float number in the range [0.0..1.0]"); } - if ($extensions !== null && isset($extensions['google_video'])) { - GoogleVideoExtension::validate($this->baseURL . $path, $extensions['google_video']); + if (count($extensions) > 0) { + if (isset($extensions['google_video'])) { + GoogleVideoExtension::validate($this->baseURL . $path, $extensions['google_video']); + } + + if (isset($extensions['google_image'])) { + GoogleImageExtension::validateEntryFields($extensions['google_image']); + } } } @@ -377,19 +411,22 @@ class SitemapGenerator * @param array|null $alternates * @param array $extensions * @return $this + * @throws OutOfRangeException + * @throws UnexpectedValueException + * @throws InvalidArgumentException */ public function addURL( - string $path, - DateTime $lastModified = null, - string $changeFrequency = null, - float $priority = null, - array $alternates = null, - array $extensions = [] + string $path, + ?DateTime $lastModified = null, + ?string $changeFrequency = null, + ?float $priority = null, + ?array $alternates = null, + array $extensions = [] ): SitemapGenerator { - $this->validate($path, $lastModified, $changeFrequency, $priority, $alternates, $extensions); + $this->validate($path, $changeFrequency, $priority, $extensions); - if ($this->totalUrlCount >= self::TOTAL_MAX_URLS) { + if ($this->totalURLCount >= self::TOTAL_MAX_URLS) { throw new OutOfRangeException( sprintf("Max url limit reached (%d)", self::TOTAL_MAX_URLS) ); @@ -400,20 +437,24 @@ class SitemapGenerator $this->writeSitemapUrl($this->baseURL . $path, $lastModified, $changeFrequency, $priority, $alternates, $extensions); - if ($this->totalUrlCount % 1000 === 0 || $this->sitemapUrlCount >= $this->maxUrlsPerSitemap) { + if ($this->totalURLCount % 1000 === 0 || $this->sitemapURLCount >= $this->maxURLsPerSitemap) { $this->flushWriter(); } - if ($this->sitemapUrlCount === $this->maxUrlsPerSitemap) { + if ($this->sitemapURLCount === $this->maxURLsPerSitemap) { $this->writeSitemapEnd(); } return $this; } - private function writeSitemapStart() + protected function writeSitemapStart(): void { $this->xmlWriter->startDocument("1.0", "UTF-8"); + if ($this->sitemapStylesheetLink != "") { + $this->xmlWriter->writePi('xml-stylesheet', + sprintf('type="text/xsl" href="%s"', $this->sitemapStylesheetLink)); + } $this->xmlWriter->writeComment(sprintf('generator-class="%s"', get_class($this))); $this->xmlWriter->writeComment(sprintf('generator-version="%s"', $this->classVersion)); $this->xmlWriter->writeComment(sprintf('generated-on="%s"', date('c'))); @@ -421,15 +462,48 @@ class SitemapGenerator $this->xmlWriter->writeAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9'); $this->xmlWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml'); $this->xmlWriter->writeAttribute('xmlns:video', 'http://www.google.com/schemas/sitemap-video/1.1'); + $this->xmlWriter->writeAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1'); $this->xmlWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); $this->xmlWriter->writeAttribute('xsi:schemaLocation', 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'); $this->isSitemapStarted = true; } - private function writeSitemapUrl($loc, $lastModified, $changeFrequency, $priority, $alternates, $extensions) + /** + * @param string $url + * @return string + * @throws UnexpectedValueException + */ + private function encodeEscapeURL(string $url): string { + // In-place encoding only on non-ASCII characters, like browsers do. + $encoded = preg_replace_callback('/[^\x20-\x7f]/', function ($match) { + return urlencode($match[0]); + }, $url); + if (!is_string($encoded)) { + throw new UnexpectedValueException('Failed to encode URL'); + } + return htmlspecialchars($encoded, ENT_QUOTES, 'UTF-8'); + } + + /** + * @param string $loc + * @param DateTime|null $lastModified + * @param string|null $changeFrequency + * @param float|null $priority + * @param array|null $alternates + * @param array $extensions + * @throws UnexpectedValueException + */ + private function writeSitemapUrl( + string $loc, + ?DateTime $lastModified = null, + ?string $changeFrequency = null, + ?float $priority = null, + ?array $alternates = null, + array $extensions = [] + ): void { $this->xmlWriter->startElement('url'); - $this->xmlWriter->writeElement('loc', htmlspecialchars($loc, ENT_QUOTES)); + $this->xmlWriter->writeElement('loc', $this->encodeEscapeURL($loc)); if ($lastModified !== null) { $this->xmlWriter->writeElement('lastmod', $lastModified->format(DateTime::ATOM)); @@ -459,17 +533,20 @@ class SitemapGenerator if ($extName === 'google_video') { GoogleVideoExtension::writeVideoTag($this->xmlWriter, $loc, $extFields); } + if ($extName === 'google_image') { + GoogleImageExtension::writeImageTag($this->xmlWriter, $extFields); + } } $this->xmlWriter->endElement(); // url - $this->sitemapUrlCount++; - $this->totalUrlCount++; + $this->sitemapURLCount++; + $this->totalURLCount++; } - private function flushWriter() + private function flushWriter(): void { - $targetSitemapFilepath = $this->basePath . sprintf($this->flushedSitemapFilenameFormat, $this->flushedSitemapCounter); - $flushedString = $this->xmlWriter->outputMemory(true); + $targetSitemapFilepath = $this->saveDirectory . sprintf($this->flushedSitemapFilenameFormat, $this->flushedSitemapCounter); + $flushedString = $this->xmlWriter->outputMemory(); $flushedStringLen = mb_strlen($flushedString); if ($flushedStringLen === 0) { @@ -485,22 +562,23 @@ class SitemapGenerator $this->fs->file_put_contents($targetSitemapFilepath, $flushedString, FILE_APPEND); } - private function writeSitemapEnd() + private function writeSitemapEnd(): void { - $targetSitemapFilepath = $this->basePath . sprintf($this->flushedSitemapFilenameFormat, $this->flushedSitemapCounter); + $targetSitemapFilepath = $this->saveDirectory . sprintf($this->flushedSitemapFilenameFormat, $this->flushedSitemapCounter); $this->xmlWriter->endElement(); // urlset $this->xmlWriter->endDocument(); - $this->fs->file_put_contents($targetSitemapFilepath, $this->xmlWriter->flush(true), FILE_APPEND); + $this->fs->file_put_contents($targetSitemapFilepath, $this->xmlWriter->flush(), FILE_APPEND); $this->isSitemapStarted = false; $this->flushedSitemaps[] = $targetSitemapFilepath; $this->flushedSitemapCounter++; - $this->sitemapUrlCount = 0; + $this->sitemapURLCount = 0; + $this->flushedSitemapSize = 0; } /** * Flush all stored urls from memory to the disk and close all necessary tags. */ - public function flush() + public function flush(): void { $this->flushWriter(); if ($this->isSitemapStarted) { @@ -510,8 +588,9 @@ class SitemapGenerator /** * Move flushed files to their final location. Compress if necessary. + * @throws RuntimeException */ - public function finalize() + public function finalize(): void { $this->generatedFiles = []; @@ -521,7 +600,7 @@ class SitemapGenerator $targetSitemapFilename .= '.gz'; } - $targetSitemapFilepath = $this->basePath . $targetSitemapFilename; + $targetSitemapFilepath = $this->saveDirectory . $targetSitemapFilename; if ($this->isCompressionEnabled) { $this->fs->copy($this->flushedSitemaps[0], 'compress.zlib://' . $targetSitemapFilepath); @@ -530,7 +609,7 @@ class SitemapGenerator $this->fs->rename($this->flushedSitemaps[0], $targetSitemapFilepath); } $this->generatedFiles['sitemaps_location'] = [$targetSitemapFilepath]; - $this->generatedFiles['sitemaps_index_url'] = $this->baseURL . '/' . $targetSitemapFilename; + $this->generatedFiles['sitemaps_index_url'] = $this->sitemapIndexURL . '/' . $targetSitemapFilename; } else if (count($this->flushedSitemaps) > 1) { $ext = '.' . pathinfo($this->sitemapFileName, PATHINFO_EXTENSION); $targetExt = $ext; @@ -541,8 +620,8 @@ class SitemapGenerator $sitemapsUrls = []; $targetSitemapFilepaths = []; foreach ($this->flushedSitemaps as $i => $flushedSitemap) { - $targetSitemapFilename = str_replace($ext, ($i + 1) . $targetExt, $this->sitemapFileName); - $targetSitemapFilepath = $this->basePath . $targetSitemapFilename; + $targetSitemapFilename = str_replace($ext, ((int)$i + 1) . $targetExt, $this->sitemapFileName); + $targetSitemapFilepath = $this->saveDirectory . $targetSitemapFilename; if ($this->isCompressionEnabled) { $this->fs->copy($flushedSitemap, 'compress.zlib://' . $targetSitemapFilepath); @@ -550,23 +629,24 @@ class SitemapGenerator } else { $this->fs->rename($flushedSitemap, $targetSitemapFilepath); } - $sitemapsUrls[] = htmlspecialchars($this->baseURL . '/' . $targetSitemapFilename, ENT_QUOTES); + $sitemapsUrls[] = htmlspecialchars( + $this->sitemapIndexURL . '/' . $targetSitemapFilename, ENT_QUOTES); $targetSitemapFilepaths[] = $targetSitemapFilepath; } - $targetSitemapIndexFilepath = $this->basePath . $this->sitemapIndexFileName; + $targetSitemapIndexFilepath = $this->saveDirectory . $this->sitemapIndexFileName; $this->createSitemapIndex($sitemapsUrls, $targetSitemapIndexFilepath); $this->generatedFiles['sitemaps_location'] = $targetSitemapFilepaths; $this->generatedFiles['sitemaps_index_location'] = $targetSitemapIndexFilepath; - $this->generatedFiles['sitemaps_index_url'] = $this->baseURL . '/' . $this->sitemapIndexFileName; + $this->generatedFiles['sitemaps_index_url'] = $this->sitemapIndexURL . '/' . $this->sitemapIndexFileName; } else { throw new RuntimeException('failed to finalize, please add urls and flush first'); } } - private function createSitemapIndex($sitemapsUrls, $sitemapIndexFileName) + private function createSitemapIndex(array $sitemapsUrls, string $sitemapIndexFileName): void { - $this->xmlWriter->flush(true); + $this->xmlWriter->flush(); $this->writeSitemapIndexStart(); foreach ($sitemapsUrls as $sitemapsUrl) { $this->writeSitemapIndexUrl($sitemapsUrl); @@ -574,12 +654,11 @@ class SitemapGenerator $this->writeSitemapIndexEnd(); $this->fs->file_put_contents( $sitemapIndexFileName, - $this->xmlWriter->flush(true), - FILE_APPEND + $this->xmlWriter->flush(), ); } - private function writeSitemapIndexStart() + protected function writeSitemapIndexStart(): void { $this->xmlWriter->startDocument("1.0", "UTF-8"); $this->xmlWriter->writeComment(sprintf('generator-class="%s"', get_class($this))); @@ -591,15 +670,19 @@ class SitemapGenerator $this->xmlWriter->writeAttribute('xsi:schemaLocation', 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'); } - private function writeSitemapIndexUrl($url) + /** + * @param string $url + * @throws UnexpectedValueException + */ + private function writeSitemapIndexUrl(string $url): void { $this->xmlWriter->startElement('sitemap'); - $this->xmlWriter->writeElement('loc', htmlspecialchars($url, ENT_QUOTES)); + $this->xmlWriter->writeElement('loc', $this->encodeEscapeURL($url)); $this->xmlWriter->writeElement('lastmod', date('c')); $this->xmlWriter->endElement(); // sitemap } - private function writeSitemapIndexEnd() + private function writeSitemapIndexEnd(): void { $this->xmlWriter->endElement(); // sitemapindex $this->xmlWriter->endDocument(); @@ -615,34 +698,40 @@ class SitemapGenerator /** * Will inform search engines about newly created sitemaps. - * Google, Ask, Bing and Yahoo will be noticed. - * If You don't pass yahooAppId, Yahoo still will be informed, - * but this method can be used once per day. If You will do this often, - * message that limit was exceeded will be returned from Yahoo. - * @param string $yahooAppId Your site Yahoo appid. + * Google and Yandex will be notified. * @return array of messages and http codes from each search engine * @access public * @throws BadMethodCallException */ - public function submitSitemap($yahooAppId = null): array + public function submitSitemap(): array { if (count($this->generatedFiles) === 0) { throw new BadMethodCallException("To update robots.txt, call finalize() first."); } if (!$this->runtime->extension_loaded('curl')) { - throw new BadMethodCallException("cURL extension is needed to do submission."); + throw new BadMethodCallException("curl extension is needed to do submission."); } $searchEngines = $this->searchEngines; - $searchEngines[0] = isset($yahooAppId) ? - str_replace("USERID", $yahooAppId, $searchEngines[0][0]) : - $searchEngines[0][1]; $result = []; for ($i = 0; $i < count($searchEngines); $i++) { $submitUrl = $searchEngines[$i] . htmlspecialchars($this->generatedFiles['sitemaps_index_url'], ENT_QUOTES); - $submitSite = $this->runtime->curl_init($submitUrl); - $this->runtime->curl_setopt($submitSite, CURLOPT_RETURNTRANSFER, true); - $responseContent = $this->runtime->curl_exec($submitSite); - $response = $this->runtime->curl_getinfo($submitSite); + $curlResource = $this->runtime->curl_init($submitUrl); + if (is_bool($curlResource) && !$curlResource) { + throw new RuntimeException("failed to execute curl_init for url " . $submitUrl); + } + if (!$this->runtime->curl_setopt($curlResource, CURLOPT_RETURNTRANSFER, true)) { + throw new RuntimeException( + "failed to set curl option CURLOPT_RETURNTRANSFER to true, error: " + . $this->runtime->curl_error($curlResource) + ); + } + $responseContent = $this->runtime->curl_exec($curlResource); + if (is_bool($responseContent) && !$responseContent) { + throw new RuntimeException( + "failed to run curl_exec, error: " . $this->runtime->curl_error($curlResource) + ); + } + $response = $this->runtime->curl_getinfo($curlResource); $submitSiteShort = array_reverse(explode(".", parse_url($searchEngines[$i], PHP_URL_HOST))); $result[] = [ "site" => $submitSiteShort[1] . "." . $submitSiteShort[0], @@ -670,7 +759,7 @@ class SitemapGenerator throw new BadMethodCallException("To update robots.txt, call finalize() first."); } - $robotsFilePath = $this->basePath . $this->robotsFileName; + $robotsFilePath = $this->saveDirectory . $this->robotsFileName; $robotsFileContent = $this->createNewRobotsContentFromFile($robotsFilePath); @@ -680,28 +769,38 @@ class SitemapGenerator } /** - * @param $filepath + * @param string $filepath * @return string + * @throws RuntimeException */ - private function createNewRobotsContentFromFile($filepath): string + private function createNewRobotsContentFromFile(string $filepath): string { if ($this->fs->file_exists($filepath)) { - $robotsFileContent = ""; - $robotsFile = explode(PHP_EOL, $this->fs->file_get_contents($filepath)); - foreach ($robotsFile as $key => $value) { - if (substr($value, 0, 8) == 'Sitemap:') { - unset($robotsFile[$key]); + $existingContent = $this->fs->file_get_contents($filepath); + // if $existingContent is bool and false, it means that file exists but is not readable + if (is_bool($existingContent) && !$existingContent) { + throw new RuntimeException("Failed to read existing robots.txt file: $filepath"); + } + if (is_string($existingContent)) { + $contentLines = explode(PHP_EOL, $existingContent); + } else { + $contentLines = []; + } + $newContent = ""; + foreach ($contentLines as $key => $line) { + if (str_starts_with($line, 'Sitemap:')) { + unset($contentLines[$key]); } else { - $robotsFileContent .= $value . PHP_EOL; + $newContent .= $line . PHP_EOL; } } } else { - $robotsFileContent = $this->getSampleRobotsContent(); + $newContent = $this->getSampleRobotsContent(); } - $robotsFileContent .= "Sitemap: {$this->generatedFiles['sitemaps_index_url']}"; + $newContent .= "Sitemap: {$this->generatedFiles['sitemaps_index_url']}"; - return $robotsFileContent; + return $newContent; } /** diff --git a/core/core.php b/core/core.php index 988d5a1..0610b09 100644 --- a/core/core.php +++ b/core/core.php @@ -51,7 +51,7 @@ class common const ACCESS_TIMER = 1800; // Numéro de version - const ZWII_VERSION = '1.16.02'; + const ZWII_VERSION = '1.17.00'; // URL autoupdate const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/campus-update/raw/branch/master/'; @@ -201,8 +201,7 @@ class common // Descripteur de données Entrées / Sorties // Liste ici tous les fichiers de données - public $dataFiles = [ - ]; + public $dataFiles = []; private $configFiles = [ 'admin' => '', @@ -379,7 +378,6 @@ class common $this->initData($stageId, self::$siteContent); } } - } // Récupère un utilisateur connecté @@ -399,11 +397,11 @@ class common : 'fr_FR'; } else { // Par défaut la langue définie par défaut à l'installation - if ($this->getData(['config','defaultLanguageUI'])) { - self::$i18nUI = $this->getData(['config','defaultLanguageUI']); + if ($this->getData(['config', 'defaultLanguageUI'])) { + self::$i18nUI = $this->getData(['config', 'defaultLanguageUI']); } else { self::$i18nUI = 'fr_FR'; - $this->setData(['config','defaultLanguageUI', 'fr_FR']); + $this->setData(['config', 'defaultLanguageUI', 'fr_FR']); } } @@ -477,7 +475,6 @@ class common // Mise à jour des données core include('core/include/update.inc.php'); - } /** @@ -693,7 +690,6 @@ class common // Instanciation de l'objet et stockage dans dataFiles $this->dataFiles[$module] = new \Prowebcraft\JsonDb($config); - } @@ -741,12 +737,10 @@ class common $content = $path === 'home' ? init::$siteContent : init::$courseContent; foreach ($content as $key => $value) { $this->setPage($key, $value['content'], $path); - } } common::$coreNotices[] = $module; - } /** * Initialisation des données @@ -1110,22 +1104,35 @@ class common * @param string Valeurs possibles */ + public function updateSitemap() { // Le drapeau prend true quand au moins une page est trouvée $flag = false; - // Rafraîchit la liste des pages après une modification de pageId notamment + // Rafraîchit la liste des pages après une modification de pageId notamment $this->buildHierarchy(); // Actualise la liste des pages pour TinyMCE $this->tinyMcePages(); - //require_once 'core/vendor/sitemap/SitemapGenerator.php'; + //require_once 'core/vendor/sitemap/SitemapGenerator.php'; $timezone = $this->getData(['config', 'timezone']); - $outputDir = getcwd(); - $sitemap = new \Icamys\SitemapGenerator\SitemapGenerator(helper::baseurl(false), $outputDir); + + $config = new \Icamys\SitemapGenerator\Config(); + + + // Your site URL. + $config->setBaseURL(helper::baseurl(false)); + // // OPTIONAL. Setting the current working directory to be output directory + $config->setSaveDirectory(sys_get_temp_dir()); + + + $sitemap = new \Icamys\SitemapGenerator\SitemapGenerator($config); + + // Create a compressed sitemap + $sitemap->enableCompression(); // will create also compressed (gzipped) sitemap : option buguée // $sitemap->enableCompression(); @@ -1155,7 +1162,7 @@ class common // Page désactivée, traiter les sous-pages sans prendre en compte la page parente. if ($this->getData(['page', $parentPageId, 'disable']) !== true) { // Cas de la page d'accueil ne pas dupliquer l'URL - $pageId = ($parentPageId !== $this->homePageId()) ? $parentPageId : ''; + $pageId = ($parentPageId !== $this->getData(['locale', 'homePageId'])) ? $parentPageId : ''; $sitemap->addUrl('/' . $pageId, $datetime); $flag = true; } @@ -1169,7 +1176,6 @@ class common if ($this->getData(['module', $parentPageId, 'posts', $articleId, 'state']) === true) { $date = $this->getData(['module', $parentPageId, 'posts', $articleId, 'publishedOn']); $sitemap->addUrl('/' . $parentPageId . '/' . $articleId, DateTime::createFromFormat('U', $date)); - $flag = true; } } } @@ -1179,7 +1185,7 @@ class common continue; } // Cas de la page d'accueil ne pas dupliquer l'URL - $pageId = ($childKey !== $this->homePageId()) ? $childKey : ''; + $pageId = ($childKey !== $this->getData(['locale', 'homePageId'])) ? $childKey : ''; $sitemap->addUrl('/' . $childKey, $datetime); $flag = true; @@ -1191,8 +1197,7 @@ class common foreach ($this->getData(['module', $childKey, 'posts']) as $articleId => $article) { if ($this->getData(['module', $childKey, 'posts', $articleId, 'state']) === true) { $date = $this->getData(['module', $childKey, 'posts', $articleId, 'publishedOn']); - $sitemap->addUrl('/' . $childKey . '/' . $articleId, DateTime::createFromFormat('U', $date)); - $flag = true; + $sitemap->addUrl('/' . $childKey . '/' . $articleId, new DateTime("@{$date}", new DateTimeZone($timezone))); } } } @@ -1217,7 +1222,7 @@ class common } $sitemap->updateRobots(); } else { - file_put_contents('robots.txt', 'User-agent: *' . PHP_EOL . 'Disallow: /'); + $this->secure_file_put_contents('robots.txt', 'User-agent: *' . PHP_EOL . 'Disallow: /'); } // Submit your sitemaps to Google, Yahoo, Bing and Ask.com @@ -1226,9 +1231,7 @@ class common } return (file_exists('sitemap.xml') && file_exists('robots.txt')); - } - /* * Création d'une miniature * Fonction utilisée lors de la mise à jour d'une version 9 à une version 10 @@ -1533,7 +1536,7 @@ class common foreach ($courses as $courseId => $value) { // Affiche les espaces gérés par l'éditeur, les espaces où il participe et les espaces anonymes if ( - // le membre est inscrit + // le membre est inscrit ($this->getData(['enrolment', $courseId]) && array_key_exists($this->getUser('id'), $this->getData(['enrolment', $courseId]))) // Il est l'auteur || $this->getUser('id') === $this->getData(['course', $courseId, 'author']) @@ -1586,5 +1589,4 @@ class common return $this->getData(['user', $userId, 'firstname']); } } - -} \ No newline at end of file +}