diff --git a/CHANGES.md b/CHANGES.md
index 17d4dc77..c86c29cf 100755
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,13 +12,23 @@
## version 10.5.00
- Modifications :
- - Gestion des module dans l'interface d'administration.
- - à compléter..
+ - Gestion des modules dans l'interface d'administration.
+
+## version 10.4.05
+- Mise à jour :
+ - SiteMapGenerator 4.3.1
+- Modifications :
+ - Bouton de remontée, position plus haute et zindex augmenté.
+ - Éviter le chevauchement du pied de age fixe au-dessus du corps de page.
+- Corrections :
+ - Marges du pied de page fixe placé en dehors du site.
+ - TinyMCE couleurs du sélecteur de paragraphe et de headers lorsque le fond est transparent.
+ - Thème administration, couleur du lien dans un bloc H4.
## version 10.4.04
- Correction :
- Module Blog : balise non fermée dans les commentaires.
-- Modifications :
+- Modifications :
-Constantes de modules.
## version 10.4.03
diff --git a/README.md b/README.md
index 7a84065d..54fc703b 100755
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-# ZwiiCMS 10.4.04
+# ZwiiCMS 10.4.05
Zwii est un CMS sans base de données (flat-file) qui permet de créer et gérer facilement un site web sans aucune connaissance en programmation.
diff --git a/core/class/SitemapGenerator.class.php b/core/class/SitemapGenerator.class.php
deleted file mode 100755
index f15350d0..00000000
--- a/core/class/SitemapGenerator.class.php
+++ /dev/null
@@ -1,540 +0,0 @@
-urls = new \SplFixedArray();
- $this->baseURL = $baseURL;
- $this->basePath = $basePath;
- $this->document = new \DOMDocument("1.0");
- $this->document->preserveWhiteSpace = false;
- $this->document->formatOutput = true;
- }
-
- /**
- * Use this to add many URL at one time.
- * Each inside array can have 1 to 4 fields.
- * @param $urlsArray
- * @throws \InvalidArgumentException
- */
- public function addUrls($urlsArray)
- {
- if (!is_array($urlsArray)) {
- throw new \InvalidArgumentException("Array as argument should be given.");
- }
- foreach ($urlsArray as $url) {
- $this->addUrl(
- isset($url[0]) ? $url[0] : null,
- isset($url[1]) ? $url[1] : null,
- isset($url[2]) ? $url[2] : null,
- isset($url[3]) ? $url[3] : null
- );
- }
- }
-
- /**
- * Use this to add single URL to sitemap.
- * @param string $url URL
- * @param \DateTime $lastModified When it was modified, use ISO 8601
- * @param string $changeFrequency How often search engines should revisit this URL
- * @param string $priority Priority of URL on You site
- * @see http://en.wikipedia.org/wiki/ISO_8601
- * @see http://php.net/manual/en/function.date.php
- * @throws \InvalidArgumentException
- */
- public function addUrl($url, \DateTime $lastModified = null, $changeFrequency = null, $priority = null)
- {
- if ($url == null) {
- throw new \InvalidArgumentException("URL is mandatory. At least one argument should be given.");
- }
- $urlLength = extension_loaded('mbstring') ? mb_strlen($url) : strlen($url);
- if ($urlLength > 2048) {
- throw new \InvalidArgumentException(
- "URL length can't be bigger than 2048 characters.
- Note, that precise url length check is guaranteed only using mb_string extension.
- Make sure Your server allow to use mbstring extension."
- );
- }
- $tmp = new \SplFixedArray(1);
-
- $tmp[self::URL_PARAM_LOC] = $url;
-
- if (isset($lastModified)) {
- $tmp->setSize(2);
- $tmp[self::URL_PARAM_LASTMOD] = $lastModified->format(\DateTime::ATOM);
- }
-
- if (isset($changeFrequency)) {
- $tmp->setSize(3);
- $tmp[self::URL_PARAM_CHANGEFREQ] = $changeFrequency;
- }
-
- if (isset($priority)) {
- $tmp->setSize(4);
- $tmp[self::URL_PARAM_PRIORITY] = $priority;
- }
-
- if ($this->urls->getSize() === 0) {
- $this->urls->setSize(1);
- } else {
- if ($this->urls->getSize() === $this->urls->key()) {
- $this->urls->setSize($this->urls->getSize() * 2);
- }
- }
-
- $this->urls[$this->urls->key()] = $tmp;
- $this->urls->next();
- }
-
-
- /**
- * @throws \BadMethodCallException
- * @throws \InvalidArgumentException
- * @throws \LengthException
- */
- public function createSitemap()
- {
- if (!isset($this->urls)) {
- throw new \BadMethodCallException("To create sitemap, call addUrl or addUrls function first.");
- }
- if ($this->maxURLsPerSitemap > self::MAX_URLS_PER_SITEMAP) {
- throw new \InvalidArgumentException(
- "More than " . self::MAX_URLS_PER_SITEMAP . " URLs per single sitemap is not allowed."
- );
- }
- $generatorInfo = '';
-
-
- $sitemapHeader = '' . $generatorInfo . '
-
- ';
-
- $sitemapIndexHeader = '' . $generatorInfo . '
-
- ';
-
-
- $nullUrls = 0;
- foreach ($this->urls as $url) {
- if (is_null($url)) {
- $nullUrls++;
- }
- }
-
- $nonEmptyUrls = $this->urls->getSize() - $nullUrls;
-
- $chunks = ceil($nonEmptyUrls / $this->maxURLsPerSitemap);
-
- for ($chunkCounter = 0; $chunkCounter < $chunks; $chunkCounter++) {
- $xml = new \SimpleXMLElement($sitemapHeader);
- for ($urlCounter = $chunkCounter * $this->maxURLsPerSitemap;
- $urlCounter < ($chunkCounter + 1) * $this->maxURLsPerSitemap && $urlCounter < $nonEmptyUrls;
- $urlCounter++
- ) {
- $row = $xml->addChild('url');
-
- $row->addChild(
- 'loc',
- htmlspecialchars($this->baseURL . $this->urls[$urlCounter][self::URL_PARAM_LOC], ENT_QUOTES, 'UTF-8')
- );
-
- if ($this->urls[$urlCounter]->getSize() > 1) {
- $row->addChild('lastmod', $this->urls[$urlCounter][self::URL_PARAM_LASTMOD]);
- }
- if ($this->urls[$urlCounter]->getSize() > 2) {
- $row->addChild('changefreq', $this->urls[$urlCounter][self::URL_PARAM_CHANGEFREQ]);
- }
- if ($this->urls[$urlCounter]->getSize() > 3) {
- $row->addChild('priority', $this->urls[$urlCounter][self::URL_PARAM_PRIORITY]);
- }
- }
- if (strlen($xml->asXML()) > self::MAX_FILE_SIZE) {
- throw new \LengthException(
- "Sitemap size equals to " . strlen($xml->asXML())
- . " bytes is more than 10MB (" . self::MAX_FILE_SIZE . " bytes),
- please decrease maxURLsPerSitemap variable."
- );
- }
- $this->sitemaps[] = $xml->asXML();
- }
- if (count($this->sitemaps) > $this->maxSitemaps) {
- throw new \LengthException(
- "Sitemap index can contain {$this->maxSitemaps} sitemaps.
- Perhaps You trying to submit too many maps."
- );
- }
- if (count($this->sitemaps) > 1) {
- for ($i = 0; $i < count($this->sitemaps); $i++) {
- $this->sitemaps[$i] = array(
- str_replace(".xml", ($i + 1) . ".xml", $this->sitemapFileName),
- $this->sitemaps[$i]
- );
- }
- $xml = new \SimpleXMLElement($sitemapIndexHeader);
- foreach ($this->sitemaps as $sitemap) {
- $row = $xml->addChild('sitemap');
- $row->addChild('loc', $this->baseURL . "/" . $this->getSitemapFileName(htmlentities($sitemap[0])));
- $row->addChild('lastmod', date('c'));
- }
- $this->sitemapFullURL = $this->baseURL . "/" . $this->sitemapIndexFileName;
- $this->sitemapIndex = array(
- $this->sitemapIndexFileName,
- $xml->asXML()
- );
- } else {
- $this->sitemapFullURL = $this->baseURL . "/" . $this->getSitemapFileName();
-
- $this->sitemaps[0] = array(
- $this->sitemapFileName,
- $this->sitemaps[0]
- );
- }
- }
-
-
- /**
- * Returns created sitemaps as array of strings.
- * Use it You want to work with sitemap without saving it as files.
- * @return array of strings
- * @access public
- */
- public function toArray()
- {
- if (isset($this->sitemapIndex)) {
- return array_merge(array($this->sitemapIndex), $this->sitemaps);
- } else {
- return $this->sitemaps;
- }
- }
-
- /**
- * Will write sitemaps as files.
- * @access public
- * @throws \BadMethodCallException
- */
- public function writeSitemap()
- {
- if (!isset($this->sitemaps)) {
- throw new \BadMethodCallException("To write sitemap, call createSitemap function first.");
- }
- if (isset($this->sitemapIndex)) {
- $this->document->loadXML($this->sitemapIndex[1]);
- $this->writeFile($this->document->saveXML(), $this->basePath, $this->sitemapIndex[0], true);
- foreach ($this->sitemaps as $sitemap) {
- $this->writeFile($sitemap[1], $this->basePath, $sitemap[0]);
- }
- } else {
- $this->document->loadXML($this->sitemaps[0][1]);
- $this->writeFile($this->document->saveXML(), $this->basePath, $this->sitemaps[0][0], true);
- $this->writeFile($this->sitemaps[0][1], $this->basePath, $this->sitemaps[0][0]);
- }
- }
-
-
- private function getSitemapFileName($name = null)
- {
- if (!$name) {
- $name = $this->sitemapFileName;
- }
- if ($this->createGZipFile) {
- $name .= ".gz";
- }
- return $name;
- }
-
- /**
- * Save file.
- * @param string $content
- * @param string $filePath
- * @param string $fileName
- * @param bool $noGzip
- * @return bool
- * @access private
- */
- private function writeFile($content, $filePath, $fileName, $noGzip = false)
- {
- if (!$noGzip && $this->createGZipFile) {
- return $this->writeGZipFile($content, $filePath, $fileName);
- }
- $file = fopen($filePath . $fileName, 'w');
- fwrite($file, $content);
- return fclose($file);
- }
-
- /**
- * Save GZipped file.
- * @param string $content
- * @param string $filePath
- * @param string $fileName
- * @return bool
- * @access private
- */
- private function writeGZipFile($content, $filePath, $fileName)
- {
- $fileName .= '.gz';
- $file = gzopen($filePath . $fileName, 'w');
- gzwrite($file, $content);
- return gzclose($file);
- }
-
- /**
- * If robots.txt file exist, will update information about newly created sitemaps.
- * If there is no robots.txt will, create one and put into it information about sitemaps.
- * @access public
- * @throws \BadMethodCallException
- */
- public function updateRobots()
- {
- if (!isset($this->sitemaps)) {
- throw new \BadMethodCallException("To update robots.txt, call createSitemap function first.");
- }
- $sampleRobotsFile = "User-agent: *\nAllow: /";
- if (file_exists($this->basePath . $this->robotsFileName)) {
- $robotsFile = explode("\n", file_get_contents($this->basePath . $this->robotsFileName));
- $robotsFileContent = "";
- foreach ($robotsFile as $key => $value) {
- if (substr($value, 0, 8) == 'Sitemap:') {
- unset($robotsFile[$key]);
- } else {
- $robotsFileContent .= $value . "\n";
- }
- }
- $robotsFileContent .= "Sitemap: $this->sitemapFullURL";
- if (!isset($this->sitemapIndex)) {
- $robotsFileContent .= "\nSitemap: " . $this->getSitemapFileName($this->sitemapFullURL);
- }
- file_put_contents($this->basePath . $this->robotsFileName, $robotsFileContent);
- } else {
- $sampleRobotsFile = $sampleRobotsFile . "\n\nSitemap: " . $this->sitemapFullURL;
- if (!isset($this->sitemapIndex)) {
- $sampleRobotsFile .= "\nSitemap: " . $this->getSitemapFileName($this->sitemapFullURL);
- }
- file_put_contents($this->basePath . $this->robotsFileName, $sampleRobotsFile);
- }
- }
-
- /**
- * 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.
- * @return array of messages and http codes from each search engine
- * @access public
- * @throws \BadMethodCallException
- */
- public function submitSitemap($yahooAppId = null)
- {
- if (!isset($this->sitemaps)) {
- throw new \BadMethodCallException("To submit sitemap, call createSitemap function first.");
- }
- if (!extension_loaded('curl')) {
- throw new \BadMethodCallException("cURL library is needed to do submission.");
- }
- $searchEngines = $this->searchEngines;
- $searchEngines[0] = isset($yahooAppId) ?
- str_replace("USERID", $yahooAppId, $searchEngines[0][0]) :
- $searchEngines[0][1];
- $result = array();
- for ($i = 0; $i < count($searchEngines); $i++) {
- $submitSite = curl_init($searchEngines[$i] . htmlspecialchars($this->sitemapFullURL, ENT_QUOTES, 'UTF-8'));
- curl_setopt($submitSite, CURLOPT_RETURNTRANSFER, true);
- $responseContent = curl_exec($submitSite);
- $response = curl_getinfo($submitSite);
- $submitSiteShort = array_reverse(explode(".", parse_url($searchEngines[$i], PHP_URL_HOST)));
- $result[] = array(
- "site" => $submitSiteShort[1] . "." . $submitSiteShort[0],
- "fullsite" => $searchEngines[$i] . htmlspecialchars($this->sitemapFullURL, ENT_QUOTES, 'UTF-8'),
- "http_code" => $response['http_code'],
- "message" => str_replace("\n", " ", strip_tags($responseContent))
- );
- }
- return $result;
- }
-
-
- /**
- * Returns array of URLs
- *
- * Converts internal SplFixedArray to array
- * @return array
- */
- public function getUrls()
- {
- $urls = $this->urls->toArray();
-
- /**
- * @var int $key
- * @var \SplFixedArray $urlSplArr
- */
- foreach ($urls as $key => $urlSplArr) {
- if (!is_null($urlSplArr)) {
- $urlArr = $urlSplArr->toArray();
- $url = [];
- foreach ($urlArr as $paramIndex => $paramValue) {
- switch ($paramIndex) {
- case static::URL_PARAM_LOC:
- $url['loc'] = $paramValue;
- break;
- case static::URL_PARAM_CHANGEFREQ:
- $url['changefreq'] = $paramValue;
- break;
- case static::URL_PARAM_LASTMOD:
- $url['lastmod'] = $paramValue;
- break;
- case static::URL_PARAM_PRIORITY:
- $url['priority'] = $paramValue;
- break;
- default:
- break;
- }
- }
- $urls[$key] = $url;
- }
- }
-
- return $urls;
- }
-
- public function countUrls()
- {
- return $this->urls->getSize();
- }
-}
diff --git a/core/class/autoload.php b/core/class/autoload.php
index a9d16ed4..ac507c05 100755
--- a/core/class/autoload.php
+++ b/core/class/autoload.php
@@ -4,7 +4,9 @@ class autoload {
public static function autoloader () {
require_once 'core/class/helper.class.php';
require_once 'core/class/template.class.php';
- require_once 'core/class/SitemapGenerator.class.php';
+ require_once 'core/class/sitemap/Runtime.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';
require_once 'core/class/phpmailer/Exception.class.php';
require_once 'core/class/phpmailer/SMTP.class.php';
diff --git a/core/class/helper.class.php b/core/class/helper.class.php
index 34332ccd..503ea941 100755
--- a/core/class/helper.class.php
+++ b/core/class/helper.class.php
@@ -161,7 +161,7 @@ class helper {
if (array_key_exists('UPDATE', $class_constants)) {
$update = $value::UPDATE;
} else {
- $update = true;
+ $update = '0.0';
}
// Constante DELETE
if (array_key_exists('DELETE', $class_constants)) {
@@ -170,13 +170,13 @@ class helper {
$delete = true;
}
// Constante DATADIRECTORY
- if ( array_key_exists('DATADIRECTORY', $class_constants)
+ if ( array_key_exists('DATADIRECTORY', $class_constants)
&& $class_constants['DATADIRECTORY'] !== []
&& is_array($class_constants['DATADIRECTORY'])
) {
$dataDirectory = $value::DATADIRECTORY;
} else {
- $dataDirectory = ['fr/module.json'];
+ $dataDirectory = [];
}
// Affection
$modules [$value] = [
diff --git a/core/class/sitemap/FileSystem.class.php b/core/class/sitemap/FileSystem.class.php
new file mode 100755
index 00000000..261102c6
--- /dev/null
+++ b/core/class/sitemap/FileSystem.class.php
@@ -0,0 +1,36 @@
+\n")
+ private $sitemapUrlCount = 0;
+ private $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
+ */
+ public function __construct(string $baseURL, string $basePath = "", FileSystem $fs = null, Runtime $runtime = null)
+ {
+ $this->urls = [];
+ $this->baseURL = rtrim($baseURL, '/');
+
+ if ($fs === null) {
+ $this->fs = new FileSystem();
+ } else {
+ $this->fs = $fs;
+ }
+
+ if ($runtime === null) {
+ $this->runtime = new Runtime();
+ } else {
+ $this->runtime = $runtime;
+ }
+
+ if ($this->runtime->is_writable($basePath) === false) {
+ throw new InvalidArgumentException(
+ sprintf('the provided basePath (%s) should be a writable directory,', $basePath) .
+ ' please check its existence and permissions'
+ );
+ }
+ if (strlen($basePath) > 0 && substr($basePath, -1) != DIRECTORY_SEPARATOR) {
+ $basePath = $basePath . DIRECTORY_SEPARATOR;
+ }
+ $this->basePath = $basePath;
+
+ $this->xmlWriter = $this->createXmlWriter();
+ $this->flushedSitemapFilenameFormat = sprintf("sm-%%d-%d.xml", time());
+ }
+
+ private function createXmlWriter(): XMLWriter
+ {
+ $w = new XMLWriter();
+ $w->openMemory();
+ $w->setIndent(true);
+ return $w;
+ }
+
+ /**
+ * @param string $filename
+ * @return SitemapGenerator
+ */
+ public function setSitemapFilename(string $filename = ''): SitemapGenerator
+ {
+ if (strlen($filename) === 0) {
+ throw new InvalidArgumentException('sitemap filename should not be empty');
+ }
+ if (pathinfo($filename, PATHINFO_EXTENSION) !== 'xml') {
+ throw new InvalidArgumentException('sitemap filename should have *.xml extension');
+ }
+ $this->sitemapFileName = $filename;
+ return $this;
+ }
+
+ /**
+ * @param string $filename
+ * @return $this
+ */
+ public function setSitemapIndexFilename(string $filename = ''): SitemapGenerator
+ {
+ if (strlen($filename) === 0) {
+ throw new InvalidArgumentException('filename should not be empty');
+ }
+ $this->sitemapIndexFileName = $filename;
+ return $this;
+ }
+
+ /**
+ * @param string $filename
+ * @return $this
+ */
+ public function setRobotsFileName(string $filename): SitemapGenerator
+ {
+ if (strlen($filename) === 0) {
+ throw new InvalidArgumentException('filename should not be empty');
+ }
+ $this->robotsFileName = $filename;
+ return $this;
+ }
+
+ /**
+ * @param int $value
+ * @return $this
+ */
+ 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;
+ return $this;
+ }
+
+ public function enableCompression(): SitemapGenerator
+ {
+ $this->isCompressionEnabled = true;
+ return $this;
+ }
+
+ public function disableCompression(): SitemapGenerator
+ {
+ $this->isCompressionEnabled = false;
+ return $this;
+ }
+
+ public function isCompressionEnabled(): bool
+ {
+ return $this->isCompressionEnabled;
+ }
+
+ public function validate(
+ string $path,
+ DateTime $lastModified = null,
+ string $changeFrequency = null,
+ float $priority = null,
+ array $alternates = null,
+ array $extensions = [])
+ {
+ if (!(1 <= mb_strlen($path) && mb_strlen($path) <= self::MAX_URL_LEN)) {
+ throw new InvalidArgumentException(
+ sprintf("The urlPath argument length must be between 1 and %d.", self::MAX_URL_LEN)
+ );
+ }
+ if ($changeFrequency !== null && !in_array($changeFrequency, $this->validChangefreqValues)) {
+ throw new InvalidArgumentException(
+ 'The change frequency argument should be one of: %s' . implode(',', $this->validChangefreqValues)
+ );
+ }
+ 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']);
+ }
+ }
+
+ /**
+ * Add url components.
+ * Instead of storing all urls in the memory, the generator will flush sets of added urls
+ * to the temporary files created on your disk.
+ * The file format is 'sm-{index}-{timestamp}.xml'
+ * @param string $path
+ * @param DateTime|null $lastModified
+ * @param string|null $changeFrequency
+ * @param float|null $priority
+ * @param array|null $alternates
+ * @param array $extensions
+ * @return $this
+ */
+ public function addURL(
+ 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);
+
+ if ($this->totalUrlCount >= self::TOTAL_MAX_URLS) {
+ throw new OutOfRangeException(
+ sprintf("Max url limit reached (%d)", self::TOTAL_MAX_URLS)
+ );
+ }
+ if ($this->isSitemapStarted === false) {
+ $this->writeSitemapStart();
+ }
+
+ $this->writeSitemapUrl($this->baseURL . $path, $lastModified, $changeFrequency, $priority, $alternates, $extensions);
+
+ if ($this->totalUrlCount % 1000 === 0 || $this->sitemapUrlCount >= $this->maxUrlsPerSitemap) {
+ $this->flushWriter();
+ }
+
+ if ($this->sitemapUrlCount === $this->maxUrlsPerSitemap) {
+ $this->writeSitemapEnd();
+ }
+
+ return $this;
+ }
+
+ private function writeSitemapStart()
+ {
+ $this->xmlWriter->startDocument("1.0", "UTF-8");
+ $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')));
+ $this->xmlWriter->startElement('urlset');
+ $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: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)
+ {
+ $this->xmlWriter->startElement('url');
+ $this->xmlWriter->writeElement('loc', htmlspecialchars($loc, ENT_QUOTES));
+
+ if ($lastModified !== null) {
+ $this->xmlWriter->writeElement('lastmod', $lastModified->format(DateTime::ATOM));
+ }
+
+ if ($changeFrequency !== null) {
+ $this->xmlWriter->writeElement('changefreq', $changeFrequency);
+ }
+
+ if ($priority !== null) {
+ $this->xmlWriter->writeElement('priority', number_format($priority, 1, ".", ""));
+ }
+
+ if (is_array($alternates) && count($alternates) > 0) {
+ foreach ($alternates as $alternate) {
+ if (is_array($alternate) && isset($alternate['hreflang']) && isset($alternate['href'])) {
+ $this->xmlWriter->startElement('xhtml:link');
+ $this->xmlWriter->writeAttribute('rel', 'alternate');
+ $this->xmlWriter->writeAttribute('hreflang', $alternate['hreflang']);
+ $this->xmlWriter->writeAttribute('href', $alternate['href']);
+ $this->xmlWriter->endElement();
+ }
+ }
+ }
+
+ foreach ($extensions as $extName => $extFields) {
+ if ($extName === 'google_video') {
+ GoogleVideoExtension::writeVideoTag($this->xmlWriter, $loc, $extFields);
+ }
+ }
+
+ $this->xmlWriter->endElement(); // url
+ $this->sitemapUrlCount++;
+ $this->totalUrlCount++;
+ }
+
+ private function flushWriter()
+ {
+ $targetSitemapFilepath = $this->basePath . sprintf($this->flushedSitemapFilenameFormat, $this->flushedSitemapCounter);
+ $flushedString = $this->xmlWriter->outputMemory(true);
+ $flushedStringLen = mb_strlen($flushedString);
+
+ if ($flushedStringLen === 0) {
+ return;
+ }
+
+ $this->flushedSitemapSize += $flushedStringLen;
+
+ if ($this->flushedSitemapSize > self::MAX_FILE_SIZE - $this->urlsetClosingTagLen) {
+ $this->writeSitemapEnd();
+ $this->writeSitemapStart();
+ }
+ $this->fs->file_put_contents($targetSitemapFilepath, $flushedString, FILE_APPEND);
+ }
+
+ private function writeSitemapEnd()
+ {
+ $targetSitemapFilepath = $this->basePath . 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->isSitemapStarted = false;
+ $this->flushedSitemaps[] = $targetSitemapFilepath;
+ $this->flushedSitemapCounter++;
+ $this->sitemapUrlCount = 0;
+ }
+
+ /**
+ * Flush all stored urls from memory to the disk and close all necessary tags.
+ */
+ public function flush()
+ {
+ $this->flushWriter();
+ if ($this->isSitemapStarted) {
+ $this->writeSitemapEnd();
+ }
+ }
+
+ /**
+ * Move flushed files to their final location. Compress if necessary.
+ */
+ public function finalize()
+ {
+ $this->generatedFiles = [];
+
+ if (count($this->flushedSitemaps) === 1) {
+ $targetSitemapFilename = $this->sitemapFileName;
+ if ($this->isCompressionEnabled) {
+ $targetSitemapFilename .= '.gz';
+ }
+
+ $targetSitemapFilepath = $this->basePath . $targetSitemapFilename;
+
+ if ($this->isCompressionEnabled) {
+ $this->fs->copy($this->flushedSitemaps[0], 'compress.zlib://' . $targetSitemapFilepath);
+ $this->fs->unlink($this->flushedSitemaps[0]);
+ } else {
+ $this->fs->rename($this->flushedSitemaps[0], $targetSitemapFilepath);
+ }
+ $this->generatedFiles['sitemaps_location'] = [$targetSitemapFilepath];
+ $this->generatedFiles['sitemaps_index_url'] = $this->baseURL . '/' . $targetSitemapFilename;
+ } else if (count($this->flushedSitemaps) > 1) {
+ $ext = '.' . pathinfo($this->sitemapFileName, PATHINFO_EXTENSION);
+ $targetExt = $ext;
+ if ($this->isCompressionEnabled) {
+ $targetExt .= '.gz';
+ }
+
+ $sitemapsUrls = [];
+ $targetSitemapFilepaths = [];
+ foreach ($this->flushedSitemaps as $i => $flushedSitemap) {
+ $targetSitemapFilename = str_replace($ext, ($i + 1) . $targetExt, $this->sitemapFileName);
+ $targetSitemapFilepath = $this->basePath . $targetSitemapFilename;
+
+ if ($this->isCompressionEnabled) {
+ $this->fs->copy($flushedSitemap, 'compress.zlib://' . $targetSitemapFilepath);
+ $this->fs->unlink($flushedSitemap);
+ } else {
+ $this->fs->rename($flushedSitemap, $targetSitemapFilepath);
+ }
+ $sitemapsUrls[] = htmlspecialchars($this->baseURL . '/' . $targetSitemapFilename, ENT_QUOTES);
+ $targetSitemapFilepaths[] = $targetSitemapFilepath;
+ }
+
+ $targetSitemapIndexFilepath = $this->basePath . $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;
+ } else {
+ throw new RuntimeException('failed to finalize, please add urls and flush first');
+ }
+ }
+
+ private function createSitemapIndex($sitemapsUrls, $sitemapIndexFileName)
+ {
+ $this->xmlWriter->flush(true);
+ $this->writeSitemapIndexStart();
+ foreach ($sitemapsUrls as $sitemapsUrl) {
+ $this->writeSitemapIndexUrl($sitemapsUrl);
+ }
+ $this->writeSitemapIndexEnd();
+ $this->fs->file_put_contents(
+ $sitemapIndexFileName,
+ $this->xmlWriter->flush(true),
+ FILE_APPEND
+ );
+ }
+
+ private function writeSitemapIndexStart()
+ {
+ $this->xmlWriter->startDocument("1.0", "UTF-8");
+ $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')));
+ $this->xmlWriter->startElement('sitemapindex');
+ $this->xmlWriter->writeAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
+ $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');
+ }
+
+ private function writeSitemapIndexUrl($url)
+ {
+ $this->xmlWriter->startElement('sitemap');
+ $this->xmlWriter->writeElement('loc', htmlspecialchars($url, ENT_QUOTES));
+ $this->xmlWriter->writeElement('lastmod', date('c'));
+ $this->xmlWriter->endElement(); // sitemap
+ }
+
+ private function writeSitemapIndexEnd()
+ {
+ $this->xmlWriter->endElement(); // sitemapindex
+ $this->xmlWriter->endDocument();
+ }
+
+ /**
+ * @return array Array of previously generated files
+ */
+ public function getGeneratedFiles(): array
+ {
+ return $this->generatedFiles;
+ }
+
+ /**
+ * 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.
+ * @return array of messages and http codes from each search engine
+ * @access public
+ * @throws BadMethodCallException
+ */
+ public function submitSitemap($yahooAppId = null): 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.");
+ }
+ $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);
+ $submitSiteShort = array_reverse(explode(".", parse_url($searchEngines[$i], PHP_URL_HOST)));
+ $result[] = [
+ "site" => $submitSiteShort[1] . "." . $submitSiteShort[0],
+ "fullsite" => $submitUrl,
+ "http_code" => $response['http_code'],
+ "message" => str_replace("\n", " ", strip_tags($responseContent)),
+ ];
+ }
+ return $result;
+ }
+
+ /**
+ * Adds sitemap url to robots.txt file located in basePath.
+ * If robots.txt file exists,
+ * the function will append sitemap url to file.
+ * If robots.txt does not exist,
+ * the function will create new robots.txt file with sample content and sitemap url.
+ * @access public
+ * @throws BadMethodCallException
+ * @throws RuntimeException
+ */
+ public function updateRobots(): SitemapGenerator
+ {
+ if (count($this->generatedFiles) === 0) {
+ throw new BadMethodCallException("To update robots.txt, call finalize() first.");
+ }
+
+ $robotsFilePath = $this->basePath . $this->robotsFileName;
+
+ $robotsFileContent = $this->createNewRobotsContentFromFile($robotsFilePath);
+
+ $this->fs->file_put_contents($robotsFilePath, $robotsFileContent);
+
+ return $this;
+ }
+
+ /**
+ * @param $filepath
+ * @return string
+ */
+ private function createNewRobotsContentFromFile($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]);
+ } else {
+ $robotsFileContent .= $value . PHP_EOL;
+ }
+ }
+ } else {
+ $robotsFileContent = $this->getSampleRobotsContent();
+ }
+
+ $robotsFileContent .= "Sitemap: {$this->generatedFiles['sitemaps_index_url']}";
+
+ return $robotsFileContent;
+ }
+
+ /**
+ * @return string
+ * @access private
+ */
+ private function getSampleRobotsContent(): string
+ {
+ return implode(PHP_EOL, $this->sampleRobotsLines) . PHP_EOL;
+ }
+}
diff --git a/core/core.php b/core/core.php
index 9f0377bb..ab0d32a5 100755
--- a/core/core.php
+++ b/core/core.php
@@ -774,22 +774,26 @@ class common {
$timezone = $this->getData(['config','timezone']);
- $sitemap = new \Icamys\SitemapGenerator\SitemapGenerator(helper::baseurl());
+ $outputDir = getcwd();
+
+ $sitemap = new \Icamys\SitemapGenerator\SitemapGenerator(helper::baseurl(false),$outputDir);
// will create also compressed (gzipped) sitemap
- $sitemap->createGZipFile = true;
+ $sitemap->enableCompression();
// determine how many urls should be put into one file
// according to standard protocol 50000 is maximum value (see http://www.sitemaps.org/protocol.html)
- $sitemap->maxURLsPerSitemap = 50000;
+ $sitemap->setMaxUrlsPerSitemap(50000);
// sitemap file name
- $sitemap->sitemapFileName = "sitemap.xml";
+ $sitemap->setSitemapFileName("sitemap.xml");
+
+ // Set the sitemap index file name
+ $sitemap->setSitemapIndexFileName("sitemap-index.xml");
$datetime = new DateTime(date('c'));
$datetime->format(DateTime::ATOM); // Updated ISO8601
- // sitemap index file name
- $sitemap->sitemapIndexFileName = "sitemap-index.xml";
+
foreach($this->getHierarchy(null, null, null) as $parentPageId => $childrenPageIds) {
// Exclure les barres et les pages non publiques et les pages masquées
if ($this->getData(['page',$parentPageId,'group']) !== 0 ||
@@ -831,11 +835,17 @@ class common {
}
- // generating internally a sitemap
- $sitemap->createSitemap();
+ // Flush all stored urls from memory to the disk and close all necessary tags.
+ $sitemap->flush();
- // writing early generated sitemap to file
- $sitemap->writeSitemap();
+ // Move flushed files to their final location. Compress if the option is enabled.
+ $sitemap->finalize();
+
+ // Update robots.txt file in output directory or create a new one
+ $sitemap->updateRobots();
+
+ // Submit your sitemaps to Google, Yahoo, Bing and Ask.com
+ $sitemap->submitSitemap();
return(file_exists('sitemap.xml'));
@@ -1631,6 +1641,16 @@ class common {
}
+ // Version 10.4.05
+ if ($this->getData(['core', 'dataVersion']) < 10405) {
+
+ // Mise à jour forcée des thèmes
+ unlink (self::DATA_DIR . 'admin.css');
+ unlink (self::DATA_DIR . 'theme.css');
+
+ $this->setData(['core', 'dataVersion', 10405]);
+ }
+
// Version 11.0.00
if ($this->getData(['core', 'dataVersion']) < 11000) {
@@ -1651,11 +1671,9 @@ class common {
$this->setData(['config','translate','pt', false ]);
$this->setData(['core', 'dataVersion', 11000]);
- }
- /**
- * mettre à jour defaultdata
- */
+
+ }
}
}
@@ -1741,14 +1759,14 @@ class core extends common {
$css .= 'body{font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'text', 'font'])) . '",sans-serif}';
if($themeBodyImage = $this->getData(['theme', 'body', 'image'])) {
// Image dans html pour éviter les déformations.
- $css .= 'html{background-image:url("../file/source/' . $themeBodyImage . '");background-position:' . $this->getData(['theme', 'body', 'imagePosition']) . ';background-attachment:' . $this->getData(['theme', 'body', 'imageAttachment']) . ';background-size:' . $this->getData(['theme', 'body', 'imageSize']) . ';background-repeat:' . $this->getData(['theme', 'body', 'imageRepeat']) . '}';
+ $css .= 'html, .mce-menu.mce-in.mce-animate {background-image:url("../file/source/' . $themeBodyImage . '");background-position:' . $this->getData(['theme', 'body', 'imagePosition']) . ';background-attachment:' . $this->getData(['theme', 'body', 'imageAttachment']) . ';background-size:' . $this->getData(['theme', 'body', 'imageSize']) . ';background-repeat:' . $this->getData(['theme', 'body', 'imageRepeat']) . '}';
// Couleur du body transparente
- $css .= 'body{background-color: rgba(0,0,0,0)}';
+ $css .= 'body, .mce-menu.mce-in.mce-animate{background-color: rgba(0,0,0,0)}';
} else {
// Pas d'image couleur du body
- $css .= 'html{background-color:' . $colors['normal'] . ';}';
+ $css .= 'html, .mce-menu.mce-in.mce-animate{background-color:' . $colors['normal'] . ';}';
// Même couleur dans le fond de l'éditeur
- $css .= 'div.mce-edit-area{background-color:' . $colors['normal'] . ' !important}';
+ $css .= 'div.mce-edit-area {background-color:' . $colors['normal'] . ' !important}';
}
// Icône BacktoTop
$css .= '#backToTop {background-color:' .$this->getData(['theme', 'body', 'toTopbackgroundColor']). ';color:'.$this->getData(['theme', 'body', 'toTopColor']).';}';
@@ -1756,7 +1774,7 @@ class core extends common {
$colors = helper::colorVariants($this->getData(['theme', 'text', 'linkColor']));
$css .= 'a{color:' . $colors['normal'] . '}';
// Couleurs de site dans TinyMCe
- $css .= 'div.mce-edit-area{font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'text', 'font'])) . '",sans-serif}';
+ $css .= 'div.mce-edit-area {font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'text', 'font'])) . '",sans-serif}';
// Site dans TinyMCE
$css .= '.editorWysiwyg {background-color:' . $this->getData(['theme', 'site', 'backgroundColor']) . ';}';
//$css .= 'a:hover:not(.inputFile, button){color:' . $colors['darken'] . '}';
@@ -1769,8 +1787,23 @@ class core extends common {
//$css .= '.button.buttonGrey,.button.buttonGrey:hover{color:' . $this->getData(['theme', 'text', 'textColor']) . '}';
$css .= '.container{max-width:' . $this->getData(['theme', 'site', 'width']) . '}';
$margin = $this->getData(['theme', 'site', 'margin']) ? '0' : '20px';
- $css .= $this->getData(['theme', 'site', 'width']) === '100%' ? '#site.light{margin:5% auto !important;}#site{margin:0 auto !important;} body{margin:0 auto !important;} #bar{margin:0 auto !important;} body > header{margin:0 auto !important;} body > nav {margin: 0 auto !important;} body > footer {margin:0 auto !important;}': "#site.light{margin: 5% auto !important;}#site{margin: " . $margin . " auto !important;} body{margin:0px 10px;} #bar{margin: 0 -10px;} body > header{margin: 0 -10px;} body > nav {margin: 0 -10px;} body > footer {margin: 0 -10px;} ";
- $css .= $this->getData(['theme', 'site', 'width']) === '750px' ? '.button, button{font-size:0.8em;}' : '';
+ // Marge supplémentaire lorsque le pied de page est fixe
+ if ( $this->getData(['theme', 'footer', 'fixed']) === true &&
+ $this->getData(['theme', 'footer', 'position']) === 'body') {
+ //$css .= '@media (min-width: 769px) { #site {margin-bottom: ' . ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 31 ) . 'px}}';
+ //$css .= '@media (max-width: 768px) { #site {margin-bottom: ' . ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 93 ) . 'px}}';
+ $marginBottomLarge = ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 31 ) . 'px';
+ $marginBottomSmall = ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 93 ) . 'px';
+ } else {
+ $marginBottomSmall = $margin;
+ $marginBottomLarge = $margin;
+ }
+ $css .= $this->getData(['theme', 'site', 'width']) === '100%'
+ ? '@media (min-width: 769px) {#site{margin:0 auto 0 ' . $marginBottomLarge . ' !important;}}@media (max-width: 768px) {#site{margin:0 auto 0 ' . $marginBottomSmall . ' !important;}}#site.light{margin:5% auto !important;} body{margin:0 auto !important;} #bar{margin:0 auto !important;} body > header{margin:0 auto !important;} body > nav {margin: 0 auto !important;} body > footer {margin:0 auto !important;}'
+ : '@media (min-width: 769px) {#site{margin: ' . $margin . ' auto ' . $marginBottomLarge . ' auto !important;}}@media (max-width: 768px) {#site{margin: ' . $margin . ' auto ' . $marginBottomSmall . ' auto !important;}}#site.light{margin: 5% auto !important;} body{margin:0px 10px;} #bar{margin: 0 -10px;} body > header{margin: 0 -10px;} body > nav {margin: 0 -10px;} body > footer {margin: 0 -10px;} ';
+ $css .= $this->getData(['theme', 'site', 'width']) === '750px'
+ ? '.button, button{font-size:0.8em;}'
+ : '';
$css .= '#site{background-color:' . $this->getData(['theme', 'site', 'backgroundColor']) . ';border-radius:' . $this->getData(['theme', 'site', 'radius']) . ';box-shadow:' . $this->getData(['theme', 'site', 'shadow']) . ' #212223;}';
$colors = helper::colorVariants($this->getData(['theme', 'button', 'backgroundColor']));
$css .= '.speechBubble,.button,.button:hover,button[type=\'submit\'],.pagination a,.pagination a:hover,input[type=\'checkbox\']:checked + label:before,input[type=\'radio\']:checked + label:before,.helpContent{background-color:' . $colors['normal'] . ';color:' . $colors['text'] . '}';
@@ -1858,22 +1891,16 @@ class core extends common {
}
$css .= 'footer span, #footerText > p {color:' . $this->getData(['theme', 'footer', 'textColor']) . ';font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'footer', 'font'])) . '",sans-serif;font-weight:' . $this->getData(['theme', 'footer', 'fontWeight']) . ';font-size:' . $this->getData(['theme', 'footer', 'fontSize']) . ';text-transform:' . $this->getData(['theme', 'footer', 'textTransform']) . '}';
- $css .= 'footer{background-color:' . $colors['normal'] . ';color:' . $this->getData(['theme', 'footer', 'textColor']) . '}';
+ $css .= 'footer {background-color:' . $colors['normal'] . ';color:' . $this->getData(['theme', 'footer', 'textColor']) . '}';
$css .= 'footer a{color:' . $this->getData(['theme', 'footer', 'textColor']) . '}';
$css .= 'footer #footersite > div {margin:' . $this->getData(['theme', 'footer', 'height']) . ' 0}';
- $css .= 'footer #footerbody > div {margin:' . $this->getData(['theme', 'footer', 'height']) . ' 0}';
+ $css .= 'footer #footerbody > div {margin:' . $this->getData(['theme', 'footer', 'height']) . ' 0}';
+ $css .= '@media (max-width: 768px) {footer #footerbody > div { padding: 2px }}';
$css .= '#footerSocials{text-align:' . $this->getData(['theme', 'footer', 'socialsAlign']) . '}';
$css .= '#footerText > p {text-align:' . $this->getData(['theme', 'footer', 'textAlign']) . '}';
$css .= '#footerCopyright{text-align:' . $this->getData(['theme', 'footer', 'copyrightAlign']) . '}';
- // Marge supplémentaire lorsque le pied de page est fixe
- if ( $this->getData(['theme', 'footer', 'fixed']) === true &&
- $this->getData(['theme', 'footer', 'position']) === 'body') {
- $css .= "@media (min-width: 769px) { #site {margin-bottom: 100px;} }";
- $css .= "@media (max-width: 768px) { #site {margin-bottom: 150px;} }";
- }
-
// Enregistre la personnalisation
file_put_contents(self::DATA_DIR.'theme.css', $css);
// Effacer le cache pour tenir compte de la couleur de fond TinyMCE
@@ -1891,7 +1918,7 @@ class core extends common {
$colors = helper::colorVariants($this->getData(['admin','backgroundColor']));
$css .= '#site{background-color:' . $colors['normal']. ';}';
$css .= '.row > div {font:' . $this->getData(['admin','fontSize']) . ' "' . $this->getData(['admin','fontText']) . '", sans-serif;}';
- $css .= 'body h1, h2, h3, h4, h5, h6 {font-family:' . $this->getData(['admin','fontTitle' ]) . ', sans-serif;color:' . $this->getData(['admin','colorTitle' ]) . ';}';
+ $css .= 'body h1, h2, h3, h4 a, h5, h6 {font-family:' . $this->getData(['admin','fontTitle' ]) . ', sans-serif;color:' . $this->getData(['admin','colorTitle' ]) . ';}';
$css .= 'body:not(.editorWysiwyg),span .zwiico-help {color:' . $this->getData(['admin','colorText']) . ';}';
$colors = helper::colorVariants($this->getData(['admin','backgroundColorButton']));
$css .= 'input[type="checkbox"]:checked + label::before,.speechBubble{background-color:' . $colors['normal'] . ';color:' . $colors['text'] . ';}';
diff --git a/core/layout/common.css b/core/layout/common.css
index 39df31ef..723f3995 100755
--- a/core/layout/common.css
+++ b/core/layout/common.css
@@ -45,11 +45,11 @@ body {
body {
margin: 0px;
}
-
+/*
#site {
margin: 0px auto;
}
-
+*/
body>header {
margin: 0px 0px;
}
@@ -477,12 +477,13 @@ td>.col12 {
#site {
overflow: hidden;
}
-
+/* Dans theme.css
@media (min-width:768px) {
#site {
margin: 20px auto;
}
}
+*/
/* Bannière */
@@ -952,9 +953,9 @@ footer #footerSocials .zwiico-github:hover {
/* Remonter en haut */
#backToTop {
position: fixed;
- z-index: 30;
+ z-index: 50;
right: 30px;
- bottom: 50px;
+ bottom: 100px;
padding: 13px 16px 16px;
/*
Paramétré dans le thème (9.2.21)
diff --git a/core/module/addon/addon.php b/core/module/addon/addon.php
index b8d5f379..a1521575 100644
--- a/core/module/addon/addon.php
+++ b/core/module/addon/addon.php
@@ -117,10 +117,10 @@ class addon extends common {
'value' => template::ico('download')
])
: '',
- is_array($infoModules[$key]['dataDirectory']) && implode(', ',array_keys($inPages,$key)) !== ''
+ is_array($infoModules[$key]['dataDirectory']) && implode(', ',array_keys($inPages,$key)) === ''
? template::button('moduleExport' . $key, [
'class' => 'buttonBlue',
- 'href' => helper::baseUrl(). $this->getUrl(0) . '/import/' . $key,// appel de fonction vaut exécution, utiliser un paramètre
+ 'href' => helper::baseUrl(). $this->getUrl(0) . '/import/' . $key.'/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre
'value' => template::ico('upload')
])
: ''
@@ -159,7 +159,8 @@ class addon extends common {
// Lecture de la version et de la validation d'update du module pour validation de la mise à jour
// Pour une version <= version installée l'utilisateur doit cocher 'Mise à jour forcée'
$version = '0.0';
- $update = false;
+ $update = '0.0';
+ $valUpdate = false;
$file = file_get_contents( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php');
$file = str_replace(' ','',$file);
$file = str_replace("\t",'',$file);
@@ -171,13 +172,12 @@ class addon extends common {
}
$pos1 = strpos($file, 'constUPDATE');
if( $pos1 !== false){
- $posdeb = strpos($file, "=", $pos1);
- $posend = strpos($file, ";", $posdeb + 1);
- $strUpdate = substr($file, $posdeb + 1, $posend - $posdeb - 1);
- if( strpos( $strUpdate,"true",0) !== false){
- $update = true;
- }
+ $posdeb = strpos($file, "'", $pos1);
+ $posend = strpos($file, "'", $posdeb + 1);
+ $update = substr($file, $posdeb + 1, $posend - $posdeb - 1);
}
+ // Si version actuelle >= version indiquée dans UPDATE la mise à jour est validée
+ if( $infoModules[$moduleName]['update'] >= $update ) $valUpdate = true;
// Module déjà installé ?
$moduleInstal = false;
@@ -193,7 +193,7 @@ class addon extends common {
$valInstalVersion = floatval( $infoModules[$moduleName]['version'] );
$newVersion = false;
if( $valNewVersion > $valInstalVersion ) $newVersion = true;
- $validMaj = $update && ( $newVersion || $checkValidMaj);
+ $validMaj = $valUpdate && ( $newVersion || $checkValidMaj);
// Nouvelle installation ou mise à jour du module
if( ! $moduleInstal || $validMaj ){
@@ -215,8 +215,13 @@ class addon extends common {
else{
$notification = ' Version détectée '.$version.' < à celle installée '.$infoModules[$moduleName]['version'];
}
- if( $update === false){
- $notification = ' Mise à jour par ce procédé interdite par le concepteur du module';
+ if( $valUpdate === false){
+ if( $infoModules[$moduleName]['update'] === $update ){
+ $notification = ' Mise à jour par ce procédé interdite par le concepteur du module';
+ }
+ else{
+ $notification = ' Mise à jour par ce procédé interdite, votre version est trop ancienne';
+ }
}
}
}
@@ -289,40 +294,33 @@ class addon extends common {
$inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC');
// Parcourir les pages utilisant le module
foreach (array_keys($inPages,$this->getUrl(2)) as $pageId) {
- foreach ($infoModules[$this->getUrl(2)]['dataDirectory'] as $moduleId) {
- // Export des pages hébergeant le module
- $pageContent[$pageId] = $this->getData(['page',$pageId]);
- /**
- * Données module.json ?
- */
- if (strpos($moduleId,'module.json')) {
- // Création de l'arborescence des langues
- // Pas de nom dossier de langue - dossier par défaut
- $t = explode ('/',$moduleId);
- if ( is_array($t)) {
- $lang = 'fr';
- } else {
- $lang = $t[0];
+ // Export des pages hébergeant le module
+ $pageContent[$pageId] = $this->getData(['page',$pageId]);
+ // Export de fr/module.json
+ $moduleId = 'fr/module.json';
+ // Création de l'arborescence des langues
+ // Pas de nom dossier de langue - dossier par défaut
+ $t = explode ('/',$moduleId);
+ if ( is_array($t)) {
+ $lang = 'fr';
+ } else {
+ $lang = $t[0];
+ }
+ // Créer le dossier si inexistant
+ if (!is_dir($tmpFolder . '/' . $lang)) {
+ mkdir ($tmpFolder . '/' . $lang);
+ }
+ // Sauvegarde si données non vides
+ $tmpData [$pageId] = $this->getData(['module',$pageId ]);
+ if ($tmpData [$pageId] !== null) {
+ file_put_contents($tmpFolder . '/' . $moduleId, json_encode($tmpData));
+ }
+ // Export des données localisées dans des dossiers
+ foreach ($infoModules[$this->getUrl(2)]['dataDirectory'] as $dirId) {
+ if ( file_exists(self::DATA_DIR . '/' . $dirId)
+ && !file_exists($tmpFolder . '/' . $dirId ) ) {
+ $this->custom_copy ( self::DATA_DIR . '/' . $dirId, $tmpFolder . '/' . $dirId );
}
- // Créer le dossier si inexistant
- if (!is_dir($tmpFolder . '/' . $lang)) {
- mkdir ($tmpFolder . '/' . $lang);
- }
- // Sauvegarde si données non vides
- $tmpData [$pageId] = $this->getData(['module',$pageId ]);
- if ($tmpData [$pageId] !== null) {
- file_put_contents($tmpFolder . '/' . $moduleId, json_encode($tmpData));
- }
- } else {
- /**
- * Données dans un json personnalisé, le sauvegarder
- * Dossier non localisé
- */
- if ( file_exists(self::DATA_DIR . '/' . $moduleId)
- && !file_exists($tmpFolder . '/' . $moduleId ) ) {
- $this->custom_copy ( self::DATA_DIR . '/' . $moduleId, $tmpFolder . '/' . $moduleId );
- }
- }
}
}
// Enregistrement des pages dans le dossier de langue identique à module
@@ -360,26 +358,86 @@ class addon extends common {
* Importer des données d'un module externes ou interne à module.json
*/
public function import(){
- // Soumission du formulaire
- if($this->isPost()) {
- // Récupérer le fichier et le décompacter
- $zipFilename = $this->getInput('addonImportFile', helper::FILTER_STRING_SHORT, true);
- $tempFolder = uniqid();
- mkdir (self::TEMP_DIR . $tempFolder);
- echo $zipFilename;
- $zip = new ZipArchive();
- if ($zip->open(self::FILE_DIR . 'source/' . $zipFilename) === TRUE) {
- $zip->extractTo(self::TEMP_DIR . $tempFolder );
- }
-
- // Supprimer le dossier temporaire même si le thème est invalide
- //$this->removeDir(self::TEMP_DIR . $tempFolder);
- $zip->close();
+ // Jeton incorrect
+ if ($this->getUrl(3) !== $_SESSION['csrf']) {
+ // Valeurs en sortie
+ $this->addOutput([
+ 'redirect' => helper::baseUrl() . 'addon',
+ 'state' => false,
+ 'notification' => 'Action non autorisée'
+ ]);
+ }
+ else{
+ // Soumission du formulaire
+ if($this->isPost()) {
+ // Récupérer le fichier et le décompacter
+ $zipFilename = $this->getInput('addonImportFile', helper::FILTER_STRING_SHORT, true);
+ $tempFolder = uniqid();
+ mkdir (self::TEMP_DIR . $tempFolder);
+ $zip = new ZipArchive();
+ if ($zip->open(self::FILE_DIR . 'source/' . $zipFilename) === TRUE) {
+ $zip->extractTo(self::TEMP_DIR . $tempFolder );
+ }
+ // Import des données localisées page.json et module.json
+ // Pour chaque dossier localisé
+ $dataTarget = array();
+ $dataSource = array();
+ // Liste des pages de même nom dans l'archive et le site
+ $list = '';
+ foreach (self::$i18nList as $key=>$value) {
+ // Les Pages et les modules
+ foreach (['page','module'] as $fileTarget){
+ if (file_exists(self::TEMP_DIR . $tempFolder . '/' .$key . '/' . $fileTarget . '.json')) {
+ // Le dossier de langue existe
+ // faire la fusion
+ $dataSource = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . '/' .$key . '/' . $fileTarget . '.json'), true);
+ // Des pages de même nom que celles de l'archive existent
+ if( $fileTarget === 'page' ){
+ foreach( $dataSource as $keydataSource=>$valuedataSource ){
+ foreach( $this->getData(['page']) as $keypage=>$valuepage ){
+ if( $keydataSource === $keypage){
+ $list === '' ? $list .= ' '.$this->getData(['page', $keypage, 'title']) : $list .= ', '.$this->getData(['page', $keypage, 'title']);
+ }
+ }
+ }
+ }
+ $dataTarget = json_decode(file_get_contents(self::DATA_DIR . $key . '/' . $fileTarget . '.json'), true);
+ $data [$fileTarget] = array_merge($dataTarget[$fileTarget], $dataSource);
+ if( $list === ''){
+ file_put_contents(self::DATA_DIR . '/' .$key . '/' . $fileTarget . '.json', json_encode( $data ,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT|LOCK_EX) );
+ }
+ // Supprimer les fichiers importés
+ unlink (self::TEMP_DIR . $tempFolder . '/' .$key . '/' . $fileTarget . '.json');
+ }
+ }
+ }
+
+ // Import des fichiers placés ailleurs que dans les dossiers localisés.
+ $this->custom_copy (self::TEMP_DIR . $tempFolder,self::DATA_DIR );
+
+ // Supprimer le dossier temporaire
+ $this->removeDir(self::TEMP_DIR . $tempFolder);
+ $zip->close();
+ if( $list !== '' ){
+ $success = false;
+ strpos( $list, ',') === false ? $notification = 'Import impossible la page suivante doit être renommée :'.$list : $notification = 'Import impossible les pages suivantes doivent être renommées :'.$list;
+ }
+ else{
+ $success = true;
+ $notification = 'Import réussi';
+ }
+ // Valeurs en sortie
+ $this->addOutput([
+ 'redirect' => helper::baseUrl() . 'addon',
+ 'state' => $success,
+ 'notification' => $notification
+ ]);
+ }
+ // Valeurs en sortie
+ $this->addOutput([
+ 'title' => 'Importer des données de module',
+ 'view' => 'import'
+ ]);
}
- // Valeurs en sortie
- $this->addOutput([
- 'title' => 'Importer des données de module',
- 'view' => 'import'
- ]);
}
}
diff --git a/core/module/config/view/index/index.php b/core/module/config/view/index/index.php
index 42b8b8ae..f7c50a18 100755
--- a/core/module/config/view/index/index.php
+++ b/core/module/config/view/index/index.php
@@ -90,7 +90,7 @@
'Aucune'] , helper::arrayCollumn($pages, 'title', 'SORT_ASC') ) , [
'label' => 'Recherche dans le site',
'selected' => $this->getData(['locale', 'searchPageId']),
- 'help' => 'Sélectionner la page "Recherche" ou une page contenant le module "Recherche" permet d\'activer un lien dans le pied de page. '
+ 'help' => 'Sélectionner la page "Recherche" ou une page contenant le module "Recherche". Une option du pied de page ajoute un lien discret vers cette page.'
]); ?>
diff --git a/module/blog/blog.php b/module/blog/blog.php
index a7cbe478..560086b3 100755
--- a/module/blog/blog.php
+++ b/module/blog/blog.php
@@ -18,7 +18,7 @@ class blog extends common {
const VERSION = '4.4';
const REALNAME = 'Blog';
const DELETE = true;
- const UPDATE = true;
+ const UPDATE = '0.0';
const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json)
const EDIT_OWNER = 'owner';
@@ -763,4 +763,3 @@ class blog extends common {
}
}
}
-
diff --git a/module/form/form.php b/module/form/form.php
index 21daf0bc..77d416d8 100755
--- a/module/form/form.php
+++ b/module/form/form.php
@@ -19,7 +19,7 @@ class form extends common {
const VERSION = '2.8';
const REALNAME = 'Formulaire';
const DELETE = true;
- const UPDATE = true;
+ const UPDATE = '0.0';
const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json)
public static $actions = [
@@ -290,7 +290,7 @@ class form extends common {
if(
$this->getData(['module', $this->getUrl(0), 'config', 'captcha'])
// AND $this->getInput('formcaptcha', helper::FILTER_INT) !== $this->getInput('formcaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('formcaptchaSecondNumber', helper::FILTER_INT))
- AND password_verify($this->getInput('formCaptcha', helper::FILTER_INT), $this->getInput('formCaptchaResult') ) === false )
+ AND password_verify($this->getInput('formCaptcha', helper::FILTER_INT), $this->getInput('formCaptchaResult') ) === false )
{
self::$inputNotices['formCaptcha'] = 'Incorrect';
diff --git a/module/gallery/gallery.php b/module/gallery/gallery.php
index 53c2b091..9434387f 100755
--- a/module/gallery/gallery.php
+++ b/module/gallery/gallery.php
@@ -20,7 +20,7 @@ class gallery extends common {
const VERSION = '2.6';
const REALNAME = 'Galerie';
const DELETE = true;
- const UPDATE = true;
+ const UPDATE = '0.0';
const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json)
const SORT_ASC = 'SORT_ASC';
@@ -698,4 +698,4 @@ class galleriesHelper extends helper {
}
return $dirContent;
}
-}
\ No newline at end of file
+}
diff --git a/module/news/news.php b/module/news/news.php
index f0685aab..6675ec37 100755
--- a/module/news/news.php
+++ b/module/news/news.php
@@ -18,7 +18,7 @@ class news extends common {
const VERSION = '2.1';
const REALNAME = 'Actualités';
const DELETE = true;
- const UPDATE = true;
+ const UPDATE = '0.0';
const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json)
public static $actions = [
@@ -334,4 +334,4 @@ class news extends common {
return $this->getData(['user', $userId, 'firstname']);
}
}
-}
\ No newline at end of file
+}
diff --git a/module/redirection/redirection.php b/module/redirection/redirection.php
index 58c4e1b7..0d1a704d 100755
--- a/module/redirection/redirection.php
+++ b/module/redirection/redirection.php
@@ -18,7 +18,7 @@ class redirection extends common {
const VERSION = '1.5';
const REALNAME = 'Redirection';
const DELETE = true;
- const UPDATE = true;
+ const UPDATE = '0.0';
const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json)
public static $actions = [
@@ -76,4 +76,4 @@ class redirection extends common {
]);
}
}
-}
\ No newline at end of file
+}
diff --git a/module/search/search.php b/module/search/search.php
index a8b0cd67..c258ddd5 100755
--- a/module/search/search.php
+++ b/module/search/search.php
@@ -21,7 +21,7 @@ class search extends common {
const VERSION = '1.3';
const REALNAME = 'Recherche';
const DELETE = true;
- const UPDATE = true;
+ const UPDATE = '0.0';
const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json)
public static $actions = [