diff --git a/.gitignore b/.gitignore deleted file mode 100755 index 622bdb64..00000000 --- a/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -# Fichiers du site -site/* - -# Dossiers vides dans GitHub -!.gitkeep -/.git - -# PHPStorm -.idea/ - -# Trucs -core/vendor/tinymce/link_list.json -.vscode/* -sitemap.xml.gz -sitemap.xml -robots.txt -.DS_Store -# Service de mise à jour -gitupdate.sh diff --git a/.htaccess b/.htaccess index 5b415e20..715df752 100644 --- a/.htaccess +++ b/.htaccess @@ -32,5 +32,5 @@ Options -Indexes Options -MultiViews -# Attention, surtout ne rien modifier ci-dessous ! +# ne pas supprimer la ligne URL rewriting ! # URL rewriting \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index 5c27f2ea..c7760082 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,33 @@ # Changelog +## Version 11.4.00 +### Nouveautés : +- Compatibilité avec PHP 8.1 +- Prise en charge des fontes Web Safe. Les fontes initiales sont transférées dans les fontes optionnelles, donc effaçables. +- Toutes les fontes en ligne sont désormais acceptées quel que soit le CDN, Google Fonte (avec preconnect), CDN Fontes ou autres. +- Désormais, les URL internes sont relatives, cela signifie qu'elles ne contiendront plus le domaine et le chemin d'accès au site. Cela permettra le déplacement d'un site d'un hébergement à un autre, d'un dossier d'hébergement à un autre, sans avoir à convertir les adresses internes. Les données d'un site mis à jour et importées d'une version antérieures sont automatiquement converties. En conséquence, le bloc de conversion de la fenêtre d'import est supprimé. +- Suppression temporaire de l'option d'installation d'un module, il faudra passer par une connexion FTP pour cela. Cette fonctionnalité a été réécrite pour la version 11.2. +### Améliorations : +- Configuration de la bannière, modalité d'affichage de la taille d'image recommandée et affichage des dimensions de l'image. +- Edition d'une page, le nom court se complète automatiquement. +- Configuration de la connexion, une option autorise l'affichage de la page de connexion lorsqu'une page de gestion du site est demandée: 'user', 'theme', 'config', 'edit', 'translate', 'addon'. +- L'option de réécriture d'URL n'est pas plus active avec le serveur Nginx. +- Galerie, version 3.5 : + - Nouvelle structure anticipée sur la version 12, le formulaire d'ajout de la galerie est séparé de la liste des galeries du module. + - Lorsque la galerie n'en contient qu'**une seule galerie**, elle peut être affichée directement, la liste des galeries étant ignorée. Pour cela, activer cette option dans les options de la galerie. + - Le contenu de la page peut désormais être affiché avec le contenu de la galerie sélectionnée. Ce paramètre ce gère au niveau de chaque galerie. + - Déplacement du bouton de retour à la liste des galeries en bas de l'écran. +### Corrections : +- URL Rewrite Apache, bug d'interprétation d'activation de la réécriture d'URL lorsque des données ont été inscrites après la ligne servant de délimiteur *# URL rewriting* dans le fichier htaccess. +- Module Galerie : correction de bugs, tri des images, erreurs d'affectation. +- Module Blog : taille recommandée de l'image erronée lorsque la largeur de l'écran est réglée sur fluide (100%). +- Gestion des pages : positionnement dans le menu accessoire ou dans le menu standard. +- Safari sur Mac, bug avec les cookies qui ne sont pas stockés. +- Nettoyage du code. +### Mise à jour : +- TableDND, script JQUERY de tri de tables utilisé par la galerie passe en version 1.0.5 +- PHPMailer 6.6.0 + ## Version 11.3.07 ### Correction : - Module galerie, option plein écran inopérante. @@ -16,7 +44,6 @@ - Sauvegarde des fontes avec le thème. - Une fonte Websafe remplace une fonte locale dont le fichier n'est pas disponible. - ## Version 11.3.05 ### Correction : - Dossier du fichier de fontes non créé empêchant la création du fichier des appels de fontes. diff --git a/README.md b/README.md index da5cf636..eb9e8330 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ZwiiCMS 11.3.07 +# ZwiiCMS 11.4.00 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/helper.class.php b/core/class/helper.class.php index accf7132..10348db1 100644 --- a/core/class/helper.class.php +++ b/core/class/helper.class.php @@ -86,7 +86,7 @@ class helper { * @param string $sort Type de tri à appliquer au tableau (SORT_ASC, SORT_DESC, ou null) * @return array */ - public static function arrayCollumn($array, $column, $sort = null) { + public static function arrayColumn($array, $column, $sort = null) { $newArray = []; if(empty($array) === false) { $newArray = array_map(function($element) use($column) { @@ -104,6 +104,14 @@ class helper { return $newArray; } + /** + * Compatibilité avec les anciens modules + */ + public static function arrayCollumn($array, $column, $sort = null) { + return (helper::arrayColumn($array, $column, $sort)); + } + + /** * Génère un backup des données de site @@ -255,11 +263,15 @@ class helper { * @return bool */ public static function checkRewrite() { - if(self::$rewriteStatus === null) { + // N'interroge que le serveur Apache + if (strpos($_SERVER["SERVER_SOFTWARE"], 'Apache') > 0) { + self::$rewriteStatus === false; + } elseif(self::$rewriteStatus === null) { // Ouvre et scinde le fichier .htaccess $htaccess = explode('# URL rewriting', file_get_contents('.htaccess')); // Retourne un boolean en fonction du contenu de la partie réservée à l'URL rewriting - self::$rewriteStatus = (empty($htaccess[1]) === false); + //self::$rewriteStatus = (empty($htaccess[1]) === false); + self::$rewriteStatus = (strpos($htaccess[1], 'RewriteEngine on') > 0) ? true : false; } return self::$rewriteStatus; } diff --git a/core/class/jsondb/JsonDb.class.php b/core/class/jsondb/JsonDb.class.php index 978a813a..808b766a 100644 --- a/core/class/jsondb/JsonDb.class.php +++ b/core/class/jsondb/JsonDb.class.php @@ -31,6 +31,16 @@ class JsonDb extends \Prowebcraft\Dot parent::__construct(); } + /** + * Reload data from file + * @return $this + */ + public function reload() + { + $this->loadData(true); + return $this; + } + /** * Set value or array of values to path * diff --git a/core/class/phpmailer/Exception.class.php b/core/class/phpmailer/Exception.class.php index b1e552f5..52eaf951 100644 --- a/core/class/phpmailer/Exception.class.php +++ b/core/class/phpmailer/Exception.class.php @@ -1,4 +1,5 @@ * @author Andy Prevost (codeworxtech) * @author Brent R. Matzelle (original founder) - * @copyright 2012 - 2017 Marcus Bointon + * @copyright 2012 - 2020 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License @@ -34,6 +35,6 @@ class Exception extends \Exception */ public function errorMessage() { - return '' . htmlspecialchars($this->getMessage()) . "
\n"; + return '' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
\n"; } } diff --git a/core/class/phpmailer/PHPMailer.class.php b/core/class/phpmailer/PHPMailer.class.php index 51dff921..718216b5 100644 --- a/core/class/phpmailer/PHPMailer.class.php +++ b/core/class/phpmailer/PHPMailer.class.php @@ -1,4 +1,5 @@ * @author Andy Prevost (codeworxtech) * @author Brent R. Matzelle (original founder) - * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2012 - 2020 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License @@ -102,14 +103,14 @@ class PHPMailer * * @var string */ - public $From = 'root@localhost'; + public $From = ''; /** * The From name of the message. * * @var string */ - public $FromName = 'Root User'; + public $FromName = ''; /** * The envelope sender of the message. @@ -357,9 +358,9 @@ class PHPMailer public $AuthType = ''; /** - * An instance of the PHPMailer OAuth class. + * An implementation of the PHPMailer OAuthTokenProvider interface. * - * @var OAuth + * @var OAuthTokenProvider */ protected $oauth; @@ -388,11 +389,11 @@ class PHPMailer * SMTP class debug output mode. * Debug output level. * Options: - * * SMTP::DEBUG_OFF: No output - * * SMTP::DEBUG_CLIENT: Client messages - * * SMTP::DEBUG_SERVER: Client and server messages - * * SMTP::DEBUG_CONNECTION: As SERVER plus connection status - * * SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed + * @see SMTP::DEBUG_OFF: No output + * @see SMTP::DEBUG_CLIENT: Client messages + * @see SMTP::DEBUG_SERVER: Client and server messages + * @see SMTP::DEBUG_CONNECTION: As SERVER plus connection status + * @see SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed * * @see SMTP::$do_debug * @@ -427,9 +428,11 @@ class PHPMailer public $Debugoutput = 'echo'; /** - * Whether to keep SMTP connection open after each message. - * If this is set to true then to close the connection - * requires an explicit call to smtpClose(). + * Whether to keep the SMTP connection open after each message. + * If this is set to true then the connection will remain open after a send, + * and closing the connection will require an explicit call to smtpClose(). + * It's a good idea to use this if you are sending multiple messages as it reduces overhead. + * See the mailing list example for how to use it. * * @var bool */ @@ -441,6 +444,8 @@ class PHPMailer * Only supported in `mail` and `sendmail` transports, not in SMTP. * * @var bool + * + * @deprecated 6.0.0 PHPMailer isn't a mailing list manager! */ public $SingleTo = false; @@ -684,7 +689,7 @@ class PHPMailer protected $boundary = []; /** - * The array of available languages. + * The array of available text strings for the current language. * * @var array */ @@ -745,7 +750,7 @@ class PHPMailer * * @var string */ - const VERSION = '6.1.5'; + const VERSION = '6.6.0'; /** * Error severity: message only, continue processing. @@ -859,18 +864,25 @@ class PHPMailer $subject = $this->encodeHeader($this->secureHeader($subject)); } //Calling mail() with null params breaks + $this->edebug('Sending with mail()'); + $this->edebug('Sendmail path: ' . ini_get('sendmail_path')); + $this->edebug("Envelope sender: {$this->Sender}"); + $this->edebug("To: {$to}"); + $this->edebug("Subject: {$subject}"); + $this->edebug("Headers: {$header}"); if (!$this->UseSendmailOptions || null === $params) { $result = @mail($to, $subject, $body, $header); } else { + $this->edebug("Additional params: {$params}"); $result = @mail($to, $subject, $body, $header, $params); } - + $this->edebug('Result: ' . ($result ? 'true' : 'false')); return $result; } /** - * Output debugging info via user-defined method. - * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). + * Output debugging info via a user-defined method. + * Only generates output if debug output is enabled. * * @see PHPMailer::$Debugoutput * @see PHPMailer::$SMTPDebug @@ -897,6 +909,7 @@ class PHPMailer switch ($this->Debugoutput) { case 'error_log': //Don't output, just log + /** @noinspection ForgottenDebugOutputInspection */ error_log($str); break; case 'html': @@ -1066,7 +1079,7 @@ class PHPMailer $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim $pos = strrpos($address, '@'); if (false === $pos) { - // At-sign is missing. + //At-sign is missing. $error_message = sprintf( '%s (%s): %s', $this->lang('invalid_address'), @@ -1082,7 +1095,7 @@ class PHPMailer return false; } $params = [$kind, $address, $name]; - // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + //Enqueue addresses with IDN until we know the PHPMailer::$CharSet. if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) { if ('Reply-To' !== $kind) { if (!array_key_exists($address, $this->RecipientsQueue)) { @@ -1099,7 +1112,7 @@ class PHPMailer return false; } - // Immediately add standard addresses without IDN. + //Immediately add standard addresses without IDN. return call_user_func_array([$this, 'addAnAddress'], $params); } @@ -1172,19 +1185,39 @@ class PHPMailer * * @param string $addrstr The address list string * @param bool $useimap Whether to use the IMAP extension to parse the list + * @param string $charset The charset to use when decoding the address list string. * * @return array */ - public static function parseAddresses($addrstr, $useimap = true) + public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) { $addresses = []; if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { //Use this built-in parser if it's available $list = imap_rfc822_parse_adrlist($addrstr, ''); + // Clear any potential IMAP errors to get rid of notices being thrown at end of script. + imap_errors(); foreach ($list as $address) { - if (('.SYNTAX-ERROR.' !== $address->host) && static::validateAddress( - $address->mailbox . '@' . $address->host - )) { + if ( + '.SYNTAX-ERROR.' !== $address->host && + static::validateAddress($address->mailbox . '@' . $address->host) + ) { + //Decode the name part if it's present and encoded + if ( + property_exists($address, 'personal') && + //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled + defined('MB_CASE_UPPER') && + preg_match('/^=\?.*\?=$/s', $address->personal) + ) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); + //Undo any RFC2047-encoded spaces-as-underscores + $address->personal = str_replace('_', '=20', $address->personal); + //Decode the name + $address->personal = mb_decode_mimeheader($address->personal); + mb_internal_encoding($origCharset); + } + $addresses[] = [ 'name' => (property_exists($address, 'personal') ? $address->personal : ''), 'address' => $address->mailbox . '@' . $address->host, @@ -1208,9 +1241,22 @@ class PHPMailer } else { list($name, $email) = explode('<', $address); $email = trim(str_replace('>', '', $email)); + $name = trim($name); if (static::validateAddress($email)) { + //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled + //If this name is encoded, decode it + if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); + //Undo any RFC2047-encoded spaces-as-underscores + $name = str_replace('_', '=20', $name); + //Decode the name + $name = mb_decode_mimeheader($name); + mb_internal_encoding($origCharset); + } $addresses[] = [ - 'name' => trim(str_replace(['"', "'"], '', $name)), + //Remove any surrounding quotes and spaces from the name + 'name' => trim($name, '\'" '), 'address' => $email, ]; } @@ -1236,9 +1282,10 @@ class PHPMailer { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - // Don't validate now addresses with IDN. Will be done in send(). + //Don't validate now addresses with IDN. Will be done in send(). $pos = strrpos($address, '@'); - if ((false === $pos) + if ( + (false === $pos) || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported()) && !static::validateAddress($address)) ) { @@ -1306,8 +1353,9 @@ class PHPMailer if (null === $patternselect) { $patternselect = static::$validator; } - if (is_callable($patternselect)) { - return $patternselect($address); + //Don't allow strings as callables, see SECURITY.md and CVE-2021-3603 + if (is_callable($patternselect) && !is_string($patternselect)) { + return call_user_func($patternselect, $address); } //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) { @@ -1348,7 +1396,7 @@ class PHPMailer /* * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. * - * @see http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) + * @see https://html.spec.whatwg.org/#e-mail-state-(type=email) */ return (bool) preg_match( '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . @@ -1388,23 +1436,33 @@ class PHPMailer */ public function punyencodeAddress($address) { - // Verify we have required functions, CharSet, and at-sign. + //Verify we have required functions, CharSet, and at-sign. $pos = strrpos($address, '@'); - if (!empty($this->CharSet) && + if ( + !empty($this->CharSet) && false !== $pos && static::idnSupported() ) { $domain = substr($address, ++$pos); - // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + //Verify CharSet string is a valid one, and domain properly encoded in this CharSet. if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) { - $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); + //Convert the domain from whatever charset it's in to UTF-8 + $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet); //Ignore IDE complaints about this line - method signature changed in PHP 5.4 $errorcode = 0; if (defined('INTL_IDNA_VARIANT_UTS46')) { - $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46); + //Use the current punycode standard (appeared in PHP 7.2) + $punycode = idn_to_ascii( + $domain, + \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | + \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, + \INTL_IDNA_VARIANT_UTS46 + ); } elseif (defined('INTL_IDNA_VARIANT_2003')) { - $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_2003); + //Fall back to this old, deprecated/removed encoding + $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003); } else { + //Fall back to a default we don't know about $punycode = idn_to_ascii($domain, $errorcode); } if (false !== $punycode) { @@ -1452,8 +1510,9 @@ class PHPMailer */ public function preSend() { - if ('smtp' === $this->Mailer - || ('mail' === $this->Mailer && stripos(PHP_OS, 'WIN') === 0) + if ( + 'smtp' === $this->Mailer + || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0)) ) { //SMTP mandates RFC-compliant line endings //and it's also used with mail() on Windows @@ -1463,25 +1522,21 @@ class PHPMailer static::setLE(PHP_EOL); } //Check for buggy PHP versions that add a header with an incorrect line break - if ('mail' === $this->Mailer - && ((PHP_VERSION_ID >= 70000 && PHP_VERSION_ID < 70017) - || (PHP_VERSION_ID >= 70100 && PHP_VERSION_ID < 70103)) + if ( + 'mail' === $this->Mailer + && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) + || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) && ini_get('mail.add_x_header') === '1' && stripos(PHP_OS, 'WIN') === 0 ) { - trigger_error( - 'Your version of PHP is affected by a bug that may result in corrupted messages.' . - ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . - ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', - E_USER_WARNING - ); + trigger_error($this->lang('buggy_php'), E_USER_WARNING); } try { - $this->error_count = 0; // Reset errors + $this->error_count = 0; //Reset errors $this->mailHeader = ''; - // Dequeue recipient and Reply-To addresses with IDN + //Dequeue recipient and Reply-To addresses with IDN foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { $params[1] = $this->punyencodeAddress($params[1]); call_user_func_array([$this, 'addAnAddress'], $params); @@ -1490,7 +1545,7 @@ class PHPMailer throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); } - // Validate From, Sender, and ConfirmReadingTo addresses + //Validate From, Sender, and ConfirmReadingTo addresses foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { $this->$address_kind = trim($this->$address_kind); if (empty($this->$address_kind)) { @@ -1514,29 +1569,29 @@ class PHPMailer } } - // Set whether the message is multipart/alternative + //Set whether the message is multipart/alternative if ($this->alternativeExists()) { $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; } $this->setMessageType(); - // Refuse to send an empty message unless we are specifically allowing it + //Refuse to send an empty message unless we are specifically allowing it if (!$this->AllowEmpty && empty($this->Body)) { throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); } //Trim subject consistently $this->Subject = trim($this->Subject); - // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + //Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) $this->MIMEHeader = ''; $this->MIMEBody = $this->createBody(); - // createBody may have added some headers, so retain them + //createBody may have added some headers, so retain them $tempheaders = $this->MIMEHeader; $this->MIMEHeader = $this->createHeader(); $this->MIMEHeader .= $tempheaders; - // To capture the complete message when using mail(), create - // an extra header list which createHeader() doesn't fold in + //To capture the complete message when using mail(), create + //an extra header list which createHeader() doesn't fold in if ('mail' === $this->Mailer) { if (count($this->to) > 0) { $this->mailHeader .= $this->addrAppend('To', $this->to); @@ -1549,8 +1604,9 @@ class PHPMailer ); } - // Sign with DKIM if enabled - if (!empty($this->DKIM_domain) + //Sign with DKIM if enabled + if ( + !empty($this->DKIM_domain) && !empty($this->DKIM_selector) && (!empty($this->DKIM_private_string) || (!empty($this->DKIM_private) @@ -1589,7 +1645,7 @@ class PHPMailer public function postSend() { try { - // Choose the mailer and send through it + //Choose the mailer and send through it switch ($this->Mailer) { case 'sendmail': case 'qmail': @@ -1607,6 +1663,9 @@ class PHPMailer return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } } catch (Exception $exc) { + if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true) { + $this->smtp->reset(); + } $this->setError($exc->getMessage()); $this->edebug($exc->getMessage()); if ($this->exceptions) { @@ -1631,22 +1690,47 @@ class PHPMailer */ protected function sendmailSend($header, $body) { + if ($this->Mailer === 'qmail') { + $this->edebug('Sending with qmail'); + } else { + $this->edebug('Sending with sendmail'); + } $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + //A space after `-f` is optional, but there is a long history of its presence + //causing problems, so we don't use one + //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html + //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html + //Example problem: https://www.drupal.org/node/1057954 - // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. - if (!empty($this->Sender) && self::isShellSafe($this->Sender)) { - if ('qmail' === $this->Mailer) { + //PHP 5.6 workaround + $sendmail_from_value = ini_get('sendmail_from'); + if (empty($this->Sender) && !empty($sendmail_from_value)) { + //PHP config has a sender address we can use + $this->Sender = ini_get('sendmail_from'); + } + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) { + if ($this->Mailer === 'qmail') { $sendmailFmt = '%s -f%s'; } else { $sendmailFmt = '%s -oi -f%s -t'; } - } elseif ('qmail' === $this->Mailer) { - $sendmailFmt = '%s'; } else { + //allow sendmail to choose a default envelope sender. It may + //seem preferable to force it to use the From header as with + //SMTP, but that introduces new problems (see + //), and + //it has historically worked this way. $sendmailFmt = '%s -oi -t'; } $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + $this->edebug('Sendmail path: ' . $this->Sendmail); + $this->edebug('Sendmail command: ' . $sendmail); + $this->edebug('Envelope sender: ' . $this->Sender); + $this->edebug("Headers: {$header}"); if ($this->SingleTo) { foreach ($this->SingleToArray as $toAddr) { @@ -1654,13 +1738,15 @@ class PHPMailer if (!$mail) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } + $this->edebug("To: {$toAddr}"); fwrite($mail, 'To: ' . $toAddr . "\n"); fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); + $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); $this->doCallback( ($result === 0), - [$toAddr], + [[$addrinfo['address'], $addrinfo['name']]], $this->cc, $this->bcc, $this->Subject, @@ -1668,6 +1754,7 @@ class PHPMailer $this->From, [] ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -1690,6 +1777,7 @@ class PHPMailer $this->From, [] ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -1710,8 +1798,15 @@ class PHPMailer */ protected static function isShellSafe($string) { - // Future-proof - if (escapeshellcmd($string) !== $string + //It's not possible to use shell commands safely (which includes the mail() function) without escapeshellarg, + //but some hosting providers disable it, creating a security problem that we don't want to have to deal with, + //so we don't. + if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) { + return false; + } + + if ( + escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""]) ) { return false; @@ -1722,9 +1817,9 @@ class PHPMailer for ($i = 0; $i < $length; ++$i) { $c = $string[$i]; - // All other characters have a special meaning in at least one common shell, including = and +. - // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. - // Note that this does permit non-Latin alphanumeric characters based on the current locale. + //All other characters have a special meaning in at least one common shell, including = and +. + //Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + //Note that this does permit non-Latin alphanumeric characters based on the current locale. if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { return false; } @@ -1744,7 +1839,28 @@ class PHPMailer */ protected static function isPermittedPath($path) { - return !preg_match('#^[a-z]+://#i', $path); + //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1 + return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path); + } + + /** + * Check whether a file path is safe, accessible, and readable. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function fileIsAccessible($path) + { + if (!static::isPermittedPath($path)) { + return false; + } + $readable = file_exists($path); + //If not a UNC path (expected to start with \\), check read permission, see #2069 + if (strpos($path, '\\\\') !== 0) { + $readable = $readable && is_readable($path); + } + return $readable; } /** @@ -1777,11 +1893,18 @@ class PHPMailer //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html //Example problem: https://www.drupal.org/node/1057954 - // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. - if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) { - $params = sprintf('-f%s', $this->Sender); + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + + //PHP 5.6 workaround + $sendmail_from_value = ini_get('sendmail_from'); + if (empty($this->Sender) && !empty($sendmail_from_value)) { + //PHP config has a sender address we can use + $this->Sender = ini_get('sendmail_from'); } if (!empty($this->Sender) && static::validateAddress($this->Sender)) { + if (self::isShellSafe($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); } @@ -1789,7 +1912,17 @@ class PHPMailer if ($this->SingleTo && count($toArr) > 1) { foreach ($toArr as $toAddr) { $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); - $this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); + $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); + $this->doCallback( + $result, + [[$addrinfo['address'], $addrinfo['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); } } else { $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); @@ -1867,7 +2000,7 @@ class PHPMailer } $callbacks = []; - // Attempt to send to all recipients + //Attempt to send to all recipients foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { foreach ($togroup as $to) { if (!$this->smtp->recipient($to[0], $this->dsn)) { @@ -1878,11 +2011,11 @@ class PHPMailer $isSent = true; } - $callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]]; + $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]]; } } - // Only send the DATA command if we have viable recipients + //Only send the DATA command if we have viable recipients if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); } @@ -1899,7 +2032,7 @@ class PHPMailer foreach ($callbacks as $cb) { $this->doCallback( $cb['issent'], - [$cb['to']], + [[$cb['to'], $cb['name']]], [], [], $this->Subject, @@ -1944,7 +2077,7 @@ class PHPMailer $options = $this->SMTPOptions; } - // Already connected? + //Already connected? if ($this->smtp->connected()) { return true; } @@ -1958,20 +2091,22 @@ class PHPMailer foreach ($hosts as $hostentry) { $hostinfo = []; - if (!preg_match( - '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', - trim($hostentry), - $hostinfo - )) { + if ( + !preg_match( + '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', + trim($hostentry), + $hostinfo + ) + ) { $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry)); - // Not a valid host entry + //Not a valid host entry continue; } - // $hostinfo[1]: optional ssl or tls prefix - // $hostinfo[2]: the hostname - // $hostinfo[3]: optional port number - // The host string prefix can temporarily override the current setting for SMTPSecure - // If it's not specified, the default value is used + //$hostinfo[1]: optional ssl or tls prefix + //$hostinfo[2]: the hostname + //$hostinfo[3]: optional port number + //The host string prefix can temporarily override the current setting for SMTPSecure + //If it's not specified, the default value is used //Check the host name is a valid name or IP address before trying to use it if (!static::isValidHost($hostinfo[2])) { @@ -1983,11 +2118,11 @@ class PHPMailer $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure); if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) { $prefix = 'ssl://'; - $tls = false; // Can't have SSL and TLS at the same time + $tls = false; //Can't have SSL and TLS at the same time $secure = static::ENCRYPTION_SMTPS; } elseif ('tls' === $hostinfo[1]) { $tls = true; - // tls doesn't use a prefix + //TLS doesn't use a prefix $secure = static::ENCRYPTION_STARTTLS; } //Do we need the OpenSSL extension? @@ -2000,7 +2135,12 @@ class PHPMailer } $host = $hostinfo[2]; $port = $this->Port; - if (array_key_exists(3, $hostinfo) && is_numeric($hostinfo[3]) && $hostinfo[3] > 0 && $hostinfo[3] < 65536) { + if ( + array_key_exists(3, $hostinfo) && + is_numeric($hostinfo[3]) && + $hostinfo[3] > 0 && + $hostinfo[3] < 65536 + ) { $port = (int) $hostinfo[3]; } if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { @@ -2012,26 +2152,29 @@ class PHPMailer } $this->smtp->hello($hello); //Automatically enable TLS encryption if: - // * it's not disabled - // * we have openssl extension - // * we are not already using SSL - // * the server offers STARTTLS + //* it's not disabled + //* we have openssl extension + //* we are not already using SSL + //* the server offers STARTTLS if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) { $tls = true; } if ($tls) { if (!$this->smtp->startTLS()) { - throw new Exception($this->lang('connect_host')); + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); } - // We must resend EHLO after TLS negotiation + //We must resend EHLO after TLS negotiation $this->smtp->hello($hello); } - if ($this->SMTPAuth && !$this->smtp->authenticate( - $this->Username, - $this->Password, - $this->AuthType, - $this->oauth - )) { + if ( + $this->SMTPAuth && !$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->oauth + ) + ) { throw new Exception($this->lang('authenticate')); } @@ -2039,16 +2182,20 @@ class PHPMailer } catch (Exception $exc) { $lastexception = $exc; $this->edebug($exc->getMessage()); - // We must have connected, but then failed TLS or Auth, so close connection nicely + //We must have connected, but then failed TLS or Auth, so close connection nicely $this->smtp->quit(); } } } - // If we get here, all connection attempts have failed, so close connection hard + //If we get here, all connection attempts have failed, so close connection hard $this->smtp->close(); - // As we've caught all exceptions, just report whatever the last one was + //As we've caught all exceptions, just report whatever the last one was if ($this->exceptions && null !== $lastexception) { throw $lastexception; + } elseif ($this->exceptions) { + // no exception was thrown, likely $this->smtp->connect() failed + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); } return false; @@ -2067,17 +2214,19 @@ class PHPMailer /** * Set the language for error messages. - * Returns false if it cannot load the language file. * The default language is English. * * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * Optionally, the language code can be enhanced with a 4-character + * script annotation and/or a 2-character country annotation. * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * Do not set this from user input! * - * @return bool + * @return bool Returns true if the requested language was loaded, false otherwise. */ public function setLanguage($langcode = 'en', $lang_path = '') { - // Backwards compatibility for renamed language codes + //Backwards compatibility for renamed language codes $renamed_langcodes = [ 'br' => 'pt_br', 'cz' => 'cs', @@ -2089,58 +2238,109 @@ class PHPMailer 'am' => 'hy', ]; - if (isset($renamed_langcodes[$langcode])) { + if (array_key_exists($langcode, $renamed_langcodes)) { $langcode = $renamed_langcodes[$langcode]; } - // Define full set of translatable strings in English + //Define full set of translatable strings in English $PHPMAILER_LANG = [ 'authenticate' => 'SMTP Error: Could not authenticate.', + 'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' . + ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . + ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 'data_not_accepted' => 'SMTP Error: data not accepted.', 'empty_message' => 'Message body empty', 'encoding' => 'Unknown encoding: ', 'execute' => 'Could not execute: ', + 'extension_missing' => 'Extension missing: ', 'file_access' => 'Could not access file: ', 'file_open' => 'File Error: Could not open file: ', 'from_failed' => 'The following From address failed: ', 'instantiate' => 'Could not instantiate mail function.', 'invalid_address' => 'Invalid address: ', + 'invalid_header' => 'Invalid header name or value', 'invalid_hostentry' => 'Invalid hostentry: ', 'invalid_host' => 'Invalid host: ', 'mailer_not_supported' => ' mailer is not supported.', 'provide_address' => 'You must provide at least one recipient email address.', 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 'signing' => 'Signing Error: ', + 'smtp_code' => 'SMTP code: ', + 'smtp_code_ex' => 'Additional SMTP info: ', 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_detail' => 'Detail: ', 'smtp_error' => 'SMTP server error: ', 'variable_set' => 'Cannot set or reset variable: ', - 'extension_missing' => 'Extension missing: ', ]; if (empty($lang_path)) { - // Calculate an absolute path so it can work if CWD is not here + //Calculate an absolute path so it can work if CWD is not here $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; } + //Validate $langcode - if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { + $foundlang = true; + $langcode = strtolower($langcode); + if ( + !preg_match('/^(?P[a-z]{2})(?P'; - echo ''; + echo ''; } } @@ -2121,14 +2157,18 @@ class common { // Import des styles liés à la page if($this->output['style']) { echo ''; + // Import de la feuille de style des pages admin if (strpos($this->output['style'], 'admin.css') >= 1 ) { echo ''; } echo ''; } - // Import des fontes liées au thème - if (file_exists(self::DATA_DIR.'fonts/fonts.html')) { - include_once(self::DATA_DIR.'fonts/fonts.html'); + // Import des fontes + if ( file_exists(self::DATA_DIR . 'fonts/fonts.html') ){ + include_once(self::DATA_DIR . 'fonts/fonts.html'); + } + if ( file_exists(self::DATA_DIR . 'fonts/fonts.css') ){ + echo ''; } } @@ -2214,7 +2254,7 @@ class common { } echo '
  • '; - echo '' .  $value . ''; + echo '' .  $value . ''; echo '
  • '; } } @@ -2277,17 +2317,6 @@ class core extends common { } } - // Importe les polices personnalisées - $fontsImported = $this->getData(['fonts', 'imported']); - if (is_array($fontsImported) && - !empty ($fontsImported) - ) { - // Fusionner les fonts avec les fontes installées - self::$fonts = array_merge(self::$fonts, $fontsImported); - // Tri Alphabétique - asort(self::$fonts); - } - // Crée le fichier de personnalisation avancée if(file_exists(self::DATA_DIR.'custom.css') === false) { file_put_contents(self::DATA_DIR.'custom.css', file_get_contents('core/module/theme/resource/custom.css')); @@ -2303,6 +2332,7 @@ class core extends common { file_put_contents(self::DATA_DIR.'admin.css', ''); chmod(self::DATA_DIR.'admin.css', 0755); } + // Check la version rafraichissement du theme $cssVersion = preg_split('/\*+/', file_get_contents(self::DATA_DIR.'theme.css')); if(empty($cssVersion[1]) OR $cssVersion[1] !== md5(json_encode($this->getData(['theme'])))) { @@ -2311,55 +2341,24 @@ class core extends common { /** * Import des polices de caractères - * A partir du CDN ou dans le dossier site/file/source/fonts */ - $fonts = [ $this->getData(['theme', 'text', 'font']), - $this->getData(['theme', 'title', 'font']), - $this->getData(['theme', 'header', 'font']), - $this->getData(['theme', 'menu', 'font']), - $this->getData(['theme', 'footer', 'font']) - ]; - // Suppression des polices identiques - $fonts = array_unique($fonts); - // Lire le fichier des fontes locales - $localFonts = $this->getData(['fonts', 'files']); - /** - * Chargement des polices en ligne dans un fichier séparé - */ - $fontFile = ''; - foreach ($fonts as $fontId) { - if (!array_key_exists($fontId, $localFonts) ) { - $fontFile .= ''; - // Supprimer l'élément des fontes chargées en ligne - unset($fonts[$fontId]); - } - } - - /** - * Fontes installées localement - */ - if ( !empty($localFonts) - ) { - foreach ($localFonts as $fontId => $fontName) { - // Validité du tableau : - if ( array_key_exists($fontId, self::$fonts) || - file_exists(self::DATA_DIR . 'fonts/' . $fontName) ) { - // Chargement de la police - $css .= '@font-face {font-family:"' . self::$fonts[$fontId] . '";'; - $css .= 'src: url("' . helper::baseUrl(false) . self::DATA_DIR . 'fonts/' . $fontName . '");}'; - } else { - // Le fichier de font n'est pas disponible, fonte par défaut - $fonts [$fontId] = 'verdana'; + $f ['files'] = $this->getData(['fonts', 'files']); + $f ['imported'] = $this->getData(['fonts', 'imported']); + $f ['websafe'] = self::$fontsWebSafe; + // Construit un tableau avec leur ID et leur famille + foreach(['websafe', 'imported', 'files'] as $type) { + if (is_array($f[$type])) { + foreach ($f[$type] as $fontId => $fontValue ) { + $fonts[$fontId] = $fontValue['font-family']; } - } } // Fond du body $colors = helper::colorVariants($this->getData(['theme', 'body', 'backgroundColor'])); // Body - $css .= 'body{font-family:"' . self::$fonts[$this->getData(['theme', 'text', 'font'])] . '",sans-serif}'; + $css .= 'body{font-family:' . $fonts[$this->getData(['theme', 'text', 'font'])] . ';}'; 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']) . '}'; @@ -2377,7 +2376,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:"' . self::$fonts[$this->getData(['theme', 'text', 'font'])] . '",sans-serif}'; + $css .= 'div.mce-edit-area {font-family:' . $fonts[$this->getData(['theme', 'text', 'font'])] . ';}'; // Site dans TinyMCE $css .= '.editorWysiwyg {background-color:' . $this->getData(['theme', 'site', 'backgroundColor']) . ';}'; $css .= 'span.mce-text{background-color: unset !important;}'; @@ -2418,7 +2417,7 @@ class core extends common { $css .= '.helpButton span:hover{color:' . $colors['darken'] . '}'; $css .= '.button:active,button[type=\'submit\']:active,.pagination a:active{background-color:' . $colors['veryDarken'] . '}'; $colors = helper::colorVariants($this->getData(['theme', 'title', 'textColor'])); - $css .= 'h1,h2,h3,h4,h5,h6,h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:' . $colors['normal'] . ';font-family:"' . self::$fonts[$this->getData(['theme', 'title', 'font'])] . '",sans-serif;font-weight:' . $this->getData(['theme', 'title', 'fontWeight']) . ';text-transform:' . $this->getData(['theme', 'title', 'textTransform']) . '}'; + $css .= 'h1,h2,h3,h4,h5,h6,h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:' . $colors['normal'] . ';font-family:' . $fonts[$this->getData(['theme', 'title', 'font'])] . ';font-weight:' . $this->getData(['theme', 'title', 'fontWeight']) . ';text-transform:' . $this->getData(['theme', 'title', 'textTransform']) . '}'; $css .= 'h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color:' . $colors['darken'] . '}'; // Les blocs $colors = helper::colorVariants($this->getData(['theme', 'block', 'backgroundColor'])); @@ -2452,7 +2451,7 @@ class core extends common { $css .= 'header{background-image:url("../file/source/' . $themeHeaderImage . '");background-position:' . $this->getData(['theme', 'header', 'imagePosition']) . ';background-repeat:' . $this->getData(['theme', 'header', 'imageRepeat']) . '}'; } $colors = helper::colorVariants($this->getData(['theme', 'header', 'textColor'])); - $css .= 'header span{color:' . $colors['normal'] . ';font-family:"' . self::$fonts[$this->getData(['theme', 'header', 'font'])] . '",sans-serif;font-weight:' . $this->getData(['theme', 'header', 'fontWeight']) . ';font-size:' . $this->getData(['theme', 'header', 'fontSize']) . ';text-transform:' . $this->getData(['theme', 'header', 'textTransform']) . '}'; + $css .= 'header span{color:' . $colors['normal'] . ';font-family:' . $fonts[$this->getData(['theme', 'header', 'font'])] . ';font-weight:' . $this->getData(['theme', 'header', 'fontWeight']) . ';font-size:' . $this->getData(['theme', 'header', 'fontSize']) . ';text-transform:' . $this->getData(['theme', 'header', 'textTransform']) . '}'; } // Bannière au contenu personnalisé @@ -2501,7 +2500,7 @@ class core extends common { $css .= 'nav{padding:0 10px;}'; } - $css .= '#toggle span,#menu a{padding:' . $this->getData(['theme', 'menu', 'height']) .';font-family:"' . self::$fonts[$this->getData(['theme', 'menu', 'font'])] . '",sans-serif;font-weight:' . $this->getData(['theme', 'menu', 'fontWeight']) . ';font-size:' . $this->getData(['theme', 'menu', 'fontSize']) . ';text-transform:' . $this->getData(['theme', 'menu', 'textTransform']) . '}'; + $css .= '#toggle span,#menu a{padding:' . $this->getData(['theme', 'menu', 'height']) .';font-family:' . $fonts[$this->getData(['theme', 'menu', 'font'])] . ';font-weight:' . $this->getData(['theme', 'menu', 'fontWeight']) . ';font-size:' . $this->getData(['theme', 'menu', 'fontSize']) . ';text-transform:' . $this->getData(['theme', 'menu', 'textTransform']) . '}'; // Pied de page $colors = helper::colorVariants($this->getData(['theme', 'footer', 'backgroundColor'])); @@ -2511,7 +2510,7 @@ class core extends common { $css .= 'footer{padding:0}'; } - $css .= 'footer span, #footerText > p {color:' . $this->getData(['theme', 'footer', 'textColor']) . ';font-family:"' . self::$fonts[$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 span, #footerText > p {color:' . $this->getData(['theme', 'footer', 'textColor']) . ';font-family:' . $fonts[$this->getData(['theme', 'footer', 'font'])] . ';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 a{color:' . $this->getData(['theme', 'footer', 'textColor']) . '}'; $css .= 'footer #footersite > div {margin:' . $this->getData(['theme', 'footer', 'height']) . ' 0}'; @@ -2522,12 +2521,6 @@ class core extends common { $css .= '#footerText > p {text-align:' . $this->getData(['theme', 'footer', 'textAlign']) . '}'; $css .= '#footerCopyright{text-align:' . $this->getData(['theme', 'footer', 'copyrightAlign']) . '}'; - // Enregistre les fontes - if (!is_dir(self::DATA_DIR . 'fonts')) { - mkdir(self::DATA_DIR . 'fonts'); - } - file_put_contents(self::DATA_DIR . 'fonts/fonts.html', $fontFile); - // Enregistre la personnalisation file_put_contents(self::DATA_DIR.'theme.css', $css); @@ -2548,52 +2541,25 @@ class core extends common { /** * Import des polices de caractères - * A partir du CDN ou dans le dossier site/file/source/fonts */ - $fonts = [ $this->getData(['admin', 'fontText']), - $this->getData(['admin', 'fontTitle']), - ]; - // Suppression des polices identiques - $fonts = array_unique($fonts); - // Lire le fichier des fontes locales - $localFonts = $this->getData(['fonts', 'files']); - /** - * Chargement des polices en ligne - */ - foreach ($fonts as $fontId) { - if (!array_key_exists($fontId, $localFonts) ) { - $css .= '@import url("https://fonts.cdnfonts.com/css/' . $fontId . '");'; - // Supprimer l'élément des fontes chargées en ligne - unset($fonts[$fontId]); - } - } - - /** - * Fontes installées localement - */ - if ( !empty($localFonts) - ) { - foreach ($localFonts as $fontId => $fontName) { - // Validité du tableau : - if ( array_key_exists($fontId, self::$fonts) || - file_exists(self::DATA_DIR . 'fonts/' . $fontName) ) { - // Chargement de la police - $css .= '@font-face {font-family:"' . self::$fonts[$fontId] . '";'; - $css .= 'src: url("' . helper::baseUrl(false) . self::DATA_DIR . 'fonts/' . $fontName . '");}'; - } else { - // Le fichier de font n'est pas disponible, fonte par défaut - $fonts [$fontId] = 'verdana'; + $f ['files'] = $this->getData(['fonts', 'files']); + $f ['imported'] = $this->getData(['fonts', 'imported']); + $f ['websafe'] = self::$fontsWebSafe; + // Construit un tableau avec leur ID et leur famille + foreach(['websafe', 'imported', 'files'] as $type) { + if (is_array($f[$type])) { + foreach ($f[$type] as $fontId => $fontValue ) { + $fonts[$fontId] = $fontValue['font-family']; } - } } // Thème Administration $colors = helper::colorVariants($this->getData(['admin','backgroundColor'])); $css .= '#site{background-color:' . $colors['normal']. ';}'; - $css .= '.row > div {font:' . $this->getData(['admin','fontSize']) . ' "' . self::$fonts[$this->getData(['admin','fontText'])] . '", sans-serif;}'; - $css .= 'body h1, h2, h3, h4 a, h5, h6 {font-family:"' . self::$fonts[$this->getData(['admin','fontTitle'])] . '", sans-serif;color:' . $this->getData(['admin','colorTitle' ]) . ';}'; + $css .= '.row > div {font:' . $fonts[$this->getData(['admin','fontText'])] . ';font-size:' . $this->getData(['admin','fontSize']) .'}'; + $css .= 'body h1, h2, h3, h4 a, h5, h6 {font-family:' . $fonts[$this->getData(['admin','fontTitle'])] . ';color:' . $this->getData(['admin','colorTitle' ]) . ';}'; // TinyMCE $css .= 'body:not(.editorWysiwyg),span .zwiico-help {color:' . $this->getData(['admin','colorText']) . ';}'; @@ -2921,9 +2887,9 @@ class core extends common { ]); } if ($output['style']) { - $this->addOutput([ - 'style' => $this->output['style'] . file_get_contents($output['style']) - ]); + $this->addOutput([ + 'style' => $this->output['style'] . file_get_contents($output['style']) + ]); } // JS $scriptPath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.js.php'; @@ -3033,15 +2999,23 @@ class core extends common { 'content' => template::speech('La page ' . $accessInfo['pageId'] . ' est ouverte par l\'utilisateur ' . $accessInfo['userName'] . '') ]); } else { - if ( $this->getData(['locale','page403']) !== 'none' - AND $this->getData(['page',$this->getData(['locale','page403'])])) - { - header('Location:' . helper::baseUrl() . $this->getData(['locale','page403'])); + // Redirige vers la page de connexion si page de gestion demandée + if ( $this->getData(['config', 'connect', 'redirectLogin']) === true + && in_array($this->geturl(0), self::$accessList) ) { + http_response_code(302); + header('Location:' . helper::baseUrl() . 'user/login/'); + exit(); } else { - $this->addOutput([ - 'title' => 'Accès interdit', - 'content' => template::speech('Vous n\'êtes pas autorisé à consulter cette page (erreur 403)') - ]); + if ( $this->getData(['locale','page403']) !== 'none' + AND $this->getData(['page',$this->getData(['locale','page403'])])) + { + header('Location:' . helper::baseUrl() . $this->getData(['locale','page403'])); + } else { + $this->addOutput([ + 'title' => 'Accès interdit', + 'content' => template::speech('Vous n\'êtes pas autorisé à consulter cette page (erreur 403)') + ]); + } } } } elseif ($this->output['content'] === '') { diff --git a/core/include/update.inc.php b/core/include/update.inc.php index 3f6d3a09..1766a28e 100644 --- a/core/include/update.inc.php +++ b/core/include/update.inc.php @@ -481,7 +481,7 @@ if ($this->getData(['core', 'dataVersion']) < 10400) { foreach ($pageList as $parentKey => $parent) { //La page est un blog if ($this->getData(['page',$parent,'moduleId']) === 'blog' ) { - $articleIds = array_keys(helper::arrayCollumn($this->getData(['module', $parent, 'posts']), 'publishedOn', 'SORT_DESC')); + $articleIds = array_keys(helper::arrayColumn($this->getData(['module', $parent, 'posts']), 'publishedOn', 'SORT_DESC')); foreach ($articleIds as $key => $article) { // Droits les deux groupes $this->setData(['module', $parent, 'posts', $article,'editConsent', 3]); @@ -809,7 +809,8 @@ if ($this->getData(['core', 'dataVersion']) < 11303) { $this->setData(['core', 'dataVersion', 11303]); } -// Version 11.3.06 + + // Version 11.3.06 if ($this->getData(['core', 'dataVersion']) < 11306) { // Supprime les fontes déclarées en double par la version précédentes @@ -822,3 +823,162 @@ if ($this->getData(['core', 'dataVersion']) < 11306) { // Mise à jour $this->setData(['core', 'dataVersion', 11306]); } + +// Version 11.4.00 +if ($this->getData(['core', 'dataVersion']) < 11400) { + + $fonts = [ + 'arimo'=> [ + 'name' => 'Arimo', + 'font-family' => 'Arimo, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/arimo' + ], + 'dancing-script' => [ + 'name' => 'Dancing Script', + 'font-family' => '\'Dancing Script\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/dancing-script' + ], + 'droid-sans-2'=> [ + 'name' => 'Droid Sans', + 'font-family' => '\'Droid Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/droid-sans-2' + ], + 'droid-serif-2'=> [ + 'name' => 'Droid Serif', + 'font-family' => '\'Droid Serif\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/droid-serif-2' + ], + 'indie-flower'=> [ + 'name' => 'Indie Flower', + 'font-family' => '\'Indie Flower\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/indie-flower' + ], + 'fira-sans' => [ + 'name' => 'Fira Sans', + 'font-family' => '\'Fira Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/fira-sans' + ], + 'liberation-sans'=> [ + 'name' => 'Liberation Sans', + 'font-family' => '\'Liberation Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/liberation-sans' + ], + 'liberation-serif'=> [ + 'name' => 'Liberation Serif', + 'font-family' => '\'Liberation Serif\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/liberation-serif' + ], + 'lobster-2'=> [ + 'name' => 'Lobster', + 'font-family' => 'Lobster, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/lobster-2' + ], + 'lato'=> [ + 'name' => 'lato', + 'font-family' => 'Lato, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/lato' + ], + 'old-standard-tt-3'=> [ + 'name' => 'Old Standard TT', + 'font-family' => '\'Old Standard TT\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/old-standard-tt-3' + ], + 'open-sans' => [ + 'name' => 'Open Sans', + 'font-family' => '\'Open Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/open-sans' + ], + 'oswald-4'=> [ + 'name' => 'Oswald', + 'font-family' => 'Oswald, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/oswald-4' + ], + 'pt-mono'=> [ + 'name' => 'PT Mono', + 'font-family' => '\'PT Mono\', monospace', + 'resource' => 'https://fonts.cdnfonts.com/css/pt-mono' + ], + 'pt-serif'=> [ + 'name' => 'PR Serif', + 'font-family' => '\'PT Serif\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/pt-serif' + ], + 'rancho'=> [ + 'name' => 'Rancho', + 'font-family' => 'Rancho, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/rancho' + ], + 'ubuntu'=> [ + 'name' => 'Ubuntu', + 'font-family' => 'Ubuntu, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/ubuntu' + ], + 'vollkorn'=> [ + 'name' => 'Vollkorn', + 'font-family' => 'Vollkorn, serif', + 'resource' => 'https://fonts.cdnfonts.com/css/vollkorn' + ] + ]; + + // Conversion des fontes locales + $files = $this->getData(['fonts', 'files']); + if (is_array($files)) { + foreach ($files as $fontId => $fontName) { + if ( gettype($fontName) === 'string' + && file_exists(self::DATA_DIR . 'fonts/' . $fontName)) { + $this->setData(['fonts', 'files', $fontId, [ + 'name' => ucfirst($fontId), + 'font-family'=> '\'' . ucfirst($fontId) . '\', sans-serif', + 'resource' => $fontName + ]]); + } + } + } + + // Consersion des fontes importées + $imported = $this->getData(['fonts', 'imported']); + if (is_array($imported)) { + foreach ($imported as $fontId => $fontUrl) { + if ( gettype($fontUrl) === 'string' ) { + $this->setData(['fonts', 'imported', $fontId, [ + 'name' => ucfirst($fontId), + 'font-family'=> '\'' . ucfirst($fontId) . '\', sans-serif', + 'resource' => 'https:\\fonts.cdnfonts.com\css' . $fontUrl + ]]); + } + } + } + // Importation des fontes exemples + $template = $fonts; + foreach ($template as $fontId => $fontValue) { + $this->setData(['fonts', 'imported', $fontId, $fontValue]); + } + + // Redirection des pages d'administration vers la bannière de connexion + $this->setData(['config', 'connect', 'redirectLogin', true]); + + // Transforme les URL en références relatives + /* + $baseUrl = $this->getData(['core', 'baseUrl']); + $baseUrl2 = str_replace('?', '', $baseUrl); + foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) { + $pageList [] = $parentKey; + foreach ($parentValue as $childKey) { + $pageList [] = $childKey; + } + } + foreach ($pageList as $parentKey => $parent) { + $s = $this->getPage( $parent, self::$i18n); + // Suppression des sous-dossiers + $s = str_replace ($baseUrl, './', $s); + $s = str_replace ($baseUrl2, './', $s); + $this->setPage( $parent, $s, self::$i18n); + } + */ + + // Suppression de la variable URL dans core + $this->deleteData(['core', 'baseUrl']); + + // Mise à jour + $this->setData(['core', 'dataVersion', 11400]); +} \ No newline at end of file diff --git a/core/layout/blank.php b/core/layout/blank.php index 789b1af6..6e2d1852 100644 --- a/core/layout/blank.php +++ b/core/layout/blank.php @@ -13,7 +13,8 @@ - + + showContent(); ?> showScript(); ?> diff --git a/core/layout/main.php b/core/layout/main.php index f1771b60..f1c2fbbc 100644 --- a/core/layout/main.php +++ b/core/layout/main.php @@ -13,6 +13,7 @@ + getData(['page', $this->getUrl(0), 'moduleId']) === 'blog' OR $this->getData(['page', $this->getUrl(0), 'moduleId']) === 'news' ) @@ -20,6 +21,7 @@ showStyle(); ?> + @@ -85,11 +87,10 @@
    getData(['theme','header','featureContent']);?>
    - + + getData(['theme','header','linkHomePage']) && $this->getData(['theme','header','feature']) === 'wallpaper' ) ? '' : '';?> - getData(['theme','header','linkHomePage']) && $this->getData(['theme','header','feature']) === 'wallpaper' ) ? '' : ''; ?> - getData(['theme', 'menu', 'position']) === 'body-second'): ?> @@ -187,6 +188,10 @@ showCookies(); ?> showScript();?> - + + diff --git a/core/module/addon/addon.php b/core/module/addon/addon.php index 69836f68..772f5648 100644 --- a/core/module/addon/addon.php +++ b/core/module/addon/addon.php @@ -293,7 +293,7 @@ class addon extends common { // Modules installés $infoModules = helper::getModules(); // Clés moduleIds dans les pages - $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); + $inPages = helper::arrayColumn($this->getData(['page']),'moduleId', 'SORT_DESC'); foreach( $inPages as $key=>$value){ $inPagesTitle[ $this->getData(['page', $key, 'title' ]) ] = $value; } @@ -364,7 +364,7 @@ class addon extends common { $infoModules = helper::getModules(); // Clés moduleIds dans les pages - $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); + $inPages = helper::arrayColumn($this->getData(['page']),'moduleId', 'SORT_DESC'); foreach( $inPages as $key=>$value){ $inPagesTitle[ $this->getData(['page', $key, 'title' ]) ] = $value; } @@ -430,7 +430,7 @@ class addon extends common { mkdir($tmpFolder, 0755); } // Clés moduleIds dans les pages - $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); + $inPages = helper::arrayColumn($this->getData(['page']),'moduleId', 'SORT_DESC'); // Parcourir les pages utilisant le module foreach (array_keys($inPages,$this->getUrl(2)) as $pageId) { // Export des pages hébergeant le module diff --git a/core/module/addon/view/index/index.help.html b/core/module/addon/view/index/index.help.html deleted file mode 100644 index 8af49c96..00000000 --- a/core/module/addon/view/index/index.help.html +++ /dev/null @@ -1,8 +0,0 @@ -

    MODULES INSTALLES

    -Les modules installés sont listés dans le tableau avec leur nom usuel (alias) et leur numéro de version. -Si le module est utilisé le nom de la page ou des pages apparaît, dans le cas contraire une icône permet de le supprimer. -

    EXPORTER IMPORTER

    -

    Exporter produit une archive au nom du module contenant les pages concernées ainsi que les données et ressources utilisées par le module dans ces pages.

    -

    Vous pouvez vous en servir comme d'une sauvegarde partielle ou pour transférer les pages et les données du module vers un autre site.

    -

    Une fois le module installé l'import permet de restaurer les pages et les données sauvegardées. Vous devrez avoir au préalable transféré le fichier zip d'un export sur votre serveur par 'Gérer les fichiers'. -Si une page de même nom existe sur votre site vous serez invité à modifier son nom.

    diff --git a/core/module/addon/view/index/index.php b/core/module/addon/view/index/index.php index e1e9c61f..1c4475c1 100644 --- a/core/module/addon/view/index/index.php +++ b/core/module/addon/view/index/index.php @@ -16,18 +16,12 @@ 'class' => 'buttonHelp' ]); ?> -
    +
    helper::baseUrl() . 'addon/store', 'value' => 'Catalogue en ligne' ]); ?>
    -
    - helper::baseUrl() . 'addon/upload', - 'value' => 'Installer' - ]); ?> -
    diff --git a/core/module/addon/view/store/store.php b/core/module/addon/view/store/store.php index cf2204af..6d8e525d 100644 --- a/core/module/addon/view/store/store.php +++ b/core/module/addon/view/store/store.php @@ -2,7 +2,7 @@
    'buttonGrey', - 'href' => helper::baseUrl() . 'addon/upload', + 'href' => helper::baseUrl() . 'addon', 'ico' => 'left', 'value' => 'Retour' ]); ?> diff --git a/core/module/config/config.php b/core/module/config/config.php index f274265d..d38ec9ce 100644 --- a/core/module/config/config.php +++ b/core/module/config/config.php @@ -20,7 +20,7 @@ class config extends common { 'backup' => self::GROUP_ADMIN, 'copyBackups'=> self::GROUP_ADMIN, 'configMetaImage' => self::GROUP_ADMIN, - 'generateFiles' => self::GROUP_ADMIN, + 'siteMap' => self::GROUP_ADMIN, 'index' => self::GROUP_ADMIN, 'restore' => self::GROUP_ADMIN, 'updateBaseUrl' => self::GROUP_ADMIN, @@ -28,8 +28,7 @@ class config extends common { 'logReset' => self::GROUP_ADMIN, 'logDownload'=> self::GROUP_ADMIN, 'blacklistReset' => self::GROUP_ADMIN, - 'blacklistDownload' => self::GROUP_ADMIN, - + 'blacklistDownload' => self::GROUP_ADMIN ]; public static $timezones = [ @@ -203,17 +202,15 @@ class config extends common { * Sitemap compressé et non compressé * Robots.txt */ - public function generateFiles() { + public function siteMap() { // Mettre à jour le site map $successSitemap=$this->createSitemap(); // Valeurs en sortie $this->addOutput([ - /*'title' => 'Configuration', - 'view' => 'index',*/ 'redirect' => helper::baseUrl() . 'config', - 'notification' => $successSitemap ? 'Mises à jour des fichiers sitemap et robots.txt' : 'Echec d\'écriture, le site map n\'a pas été mis à jour', + 'notification' => $successSitemap ? 'La carte du site a été mise à jour' : 'Echec d\'écriture, la carte du site n\'a pas été mise à jour', 'state' => $successSitemap ]); } @@ -279,8 +276,6 @@ class config extends common { } // Valeurs en sortie $this->addOutput([ - /*'title' => 'Configuration', - 'view' => 'index',*/ 'redirect' => helper::baseUrl() . 'config', 'notification' => $success === false ? 'Service inaccessible ou erreur d\'écriture de l\'image' : 'Image générée avec succès', 'state' => $success === false ? false : true @@ -510,7 +505,8 @@ class config extends common { 'captchaStrong' => $this->getInput('connectCaptchaStrong',helper::FILTER_BOOLEAN), 'autoDisconnect' => $this->getInput('connectAutoDisconnect',helper::FILTER_BOOLEAN), 'captchaType' => $this->getInput('connectCaptchaType'), - 'showPassword' => $this->getInput('connectShowPassword',helper::FILTER_BOOLEAN) + 'showPassword' => $this->getInput('connectShowPassword',helper::FILTER_BOOLEAN), + 'redirectLogin' => $this->getInput('connectRedirectLogin',helper::FILTER_BOOLEAN) ], 'i18n' => [ 'enable' => $this->getInput('localei18n',helper::FILTER_BOOLEAN), @@ -551,17 +547,21 @@ class config extends common { AND helper::checkRewrite() === false ) { // Ajout des lignes dans le .htaccess + $fileContent = file_get_contents('.htaccess'); + $rewriteData = PHP_EOL . + '# URL rewriting' . PHP_EOL . + '' . PHP_EOL . + "\tRewriteEngine on" . PHP_EOL . + "\tRewriteBase " . helper::baseUrl(false, false) . PHP_EOL . + "\tRewriteCond %{REQUEST_FILENAME} !-f" . PHP_EOL . + "\tRewriteCond %{REQUEST_FILENAME} !-d" . PHP_EOL . + "\tRewriteRule ^(.*)$ index.php?$1 [L]" . PHP_EOL . + ''. PHP_EOL . + '# URL rewriting' . PHP_EOL ; + $fileContent = str_replace('# URL rewriting', $rewriteData, $fileContent); file_put_contents( '.htaccess', - PHP_EOL . - '' . PHP_EOL . - "\tRewriteEngine on" . PHP_EOL . - "\tRewriteBase " . helper::baseUrl(false, false) . PHP_EOL . - "\tRewriteCond %{REQUEST_FILENAME} !-f" . PHP_EOL . - "\tRewriteCond %{REQUEST_FILENAME} !-d" . PHP_EOL . - "\tRewriteRule ^(.*)$ index.php?$1 [L]" . PHP_EOL . - '', - FILE_APPEND + $fileContent ); // Change le statut de la réécriture d'URL (pour le helper::baseUrl() de la redirection) helper::$rewriteStatus = true; @@ -572,16 +572,19 @@ class config extends common { AND helper::checkRewrite() ) { // Suppression des lignes dans le .htaccess - $htaccess = explode('# URL rewriting', file_get_contents('.htaccess')); - file_put_contents('.htaccess', $htaccess[0] . '# URL rewriting'); + $fileContent = file_get_contents('.htaccess'); + $fileContent = explode('# URL rewriting', $fileContent); + $fileContent = $fileContent[0] . '# URL rewriting' . $fileContent[2]; + file_put_contents( + '.htaccess', + $fileContent + ); // Change le statut de la réécriture d'URL (pour le helper::baseUrl() de la redirection) helper::$rewriteStatus = false; } - // Met à jour la baseUrl - $this->setData(['core', 'baseUrl', helper::baseUrl(true,false) ]); } // Générer robots.txt et sitemap - $this->generateFiles(); + $this->siteMap(); // Valeurs en sortie $this->addOutput([ 'title' => 'Configuration', @@ -652,59 +655,6 @@ class config extends common { ]); } - /** - * Met à jour les données de site avec l'adresse transmise - */ - public function updateBaseUrl () { - // Supprimer l'information de redirection - $old = str_replace('?','',$this->getData(['core', 'baseUrl'])); - $new = helper::baseUrl(false,false); - $c3 = 0; - $success = false ; - // Boucler sur les pages - foreach($this->getHierarchy(null,null,null) as $parentId => $childIds) { - $content = $this->getPage($parentId, self::$i18n); - $titre = $this->getData(['page', $parentId, 'title']); - $content = $titre . ' ' . $content ; - $replace = str_replace( 'href="' . $old , 'href="'. $new , stripslashes($content),$c1) ; - $replace = str_replace( 'src="' . $old , 'src="'. $new , stripslashes($replace),$c2) ; - - if ($c1 > 0 || $c2 > 0) { - $success = true; - $this->setPage($parentId, $replace, self::$i18n); - $c3 += $c1 + $c2; - } - foreach($childIds as $childId) { - $content = $this->getPage($childId, self::$i18n); - $content = $titre . ' ' . $content ; - $replace = str_replace( 'href="' . $old , 'href="'. $new , stripslashes($content),$c1) ; - $replace = str_replace( 'src="' . $old , 'src="'. $new , stripslashes($replace),$c2) ; - if ($c1 > 0 || $c2 > 0) { - $success = true; - $this->setPage($childId, $replace, self::$i18n); - $c3 += $c1 + $c2; - } - } - } - // Traiter les modules dont la redirection - $content = $this->getdata(['module']); - $replace = $this->recursive_array_replace('href="' . $old , 'href="'. $new, $content, $c1); - $replace = $this->recursive_array_replace('src="' . $old , 'src="'. $new, $replace, $c2); - if ($content !== $replace) { - $this->setdata(['module',$replace]); - $c3 += $c1 + $c2; - $success = true; - } - // Mettre à jour la base URl - $this->setData(['core','baseUrl',helper::baseUrl(true,false)]); - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Restaurer', - 'view' => 'restore', - 'notification' => $success ? $c3. ' conversion' . ($c3 > 1 ? 's' : '') . ' effectuée' . ($c3 > 1 ? 's' : '') : 'Aucune conversion', - 'state' => $success ? true : false - ]); - } /** * Vider le fichier de log diff --git a/core/module/config/view/connect/connect.php b/core/module/config/view/connect/connect.php index d1980853..343090d4 100644 --- a/core/module/config/view/connect/connect.php +++ b/core/module/config/view/connect/connect.php @@ -9,39 +9,31 @@ +
    -
    - $this->getData(['config', 'connect','captcha']) - ]); ?> - $this->getData(['config', 'connect', 'captchaStrong']), - 'help' => 'Option recommandée pour sécuriser la connexion. S\'applique à tous les captchas du site. Le captcha simple se limite à une addition de nombres de 0 à 10. Le captcha complexe utilise quatre opérations de nombres de 0 à 20. Activation recommandée.' +
    + $this->getData(['config', 'connect', 'showPassword']), + 'help' => 'Le survom d\'une icône de l\'écran de connexion affiche temporairement le mot de passe.' ]); ?>
    -
    - 'Type de captcha', - 'selected' => $this->getData(['config', 'connect', 'captchaType']) - ]); ?> -
    -
    +
    $this->getData(['config', 'connect', 'autoDisconnect']), 'help' => 'Déconnecte les sessions ouvertes précédemment sur d\'autres navigateurs ou terminaux. Activation recommandée.' ]); ?>
    -
    - $this->getData(['config', 'connect', 'showPassword']), - 'help' => 'Dans l\'écran de connexion, active une icône dont le survol affiche temporairement le mot de passe.' +
    + $this->getData(['config', 'connect', 'redirectLogin']), + 'help' => 'Cette redirection ne concerne que les pages d\'administration du site.' ]); ?>
    'Connexions successives', + 'label' => 'Limitation des tentatives', 'selected' => $this->getData(['config', 'connect', 'attempt']) ]); ?>
    @@ -73,6 +65,25 @@ ]); ?>
    +
    +
    + $this->getData(['config', 'connect','captcha']) + ]); ?> +
    +
    + $this->getData(['config', 'connect', 'captchaStrong']), + 'help' => 'Option recommandée pour sécuriser la connexion. S\'applique à tous les captchas du site. Le captcha simple se limite à une addition de nombres de 0 à 10. Le captcha complexe utilise quatre opérations de nombres de 0 à 20. Activation recommandée.' + ]); ?> +
    +
    + 'Type de captcha', + 'selected' => $this->getData(['config', 'connect', 'captchaType']) + ]); ?> +
    +
    diff --git a/core/module/config/view/locale/locale.php b/core/module/config/view/locale/locale.php index c71451c9..6c9c8fed 100644 --- a/core/module/config/view/locale/locale.php +++ b/core/module/config/view/locale/locale.php @@ -57,21 +57,21 @@
    - 'Accueil du site', 'selected' =>$this->getData(['locale', 'homePageId']), 'help' => 'La première page que vos visiteurs verront.' ]); ?>
    - 'Page par défaut'],helper::arrayCollumn($module::$orphansList, 'title', 'SORT_ASC')), [ + 'Page par défaut'],helper::arrayColumn($module::$orphansList, 'title', 'SORT_ASC')), [ 'label' => 'Accès interdit, erreur 403', 'selected' =>$this->getData(['locale', 'page403']), 'help' => 'Cette page ne doit pas apparaître dans l\'arborescence du menu. Créez une page orpheline.' ]); ?>
    - 'Page par défaut'],helper::arrayCollumn($module::$orphansList, 'title', 'SORT_ASC')), [ + 'Page par défaut'],helper::arrayColumn($module::$orphansList, 'title', 'SORT_ASC')), [ 'label' => 'Page inexistante, erreur 404', 'selected' =>$this->getData(['locale', 'page404']), 'help' => 'Cette page ne doit pas apparaître dans l\'arborescence du menu. Créez une page orpheline.' @@ -80,14 +80,14 @@
    - 'Aucune'] , helper::arrayCollumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ + 'Aucune'] , helper::arrayColumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ 'label' => 'Mentions légales', 'selected' => $this->getData(['locale', 'legalPageId']), 'help' => 'Les mentions légales sont obligatoires en France. Une option du pied de page ajoute un lien discret vers cette page.' ]); ?>
    - 'Aucune'] , helper::arrayCollumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ + 'Aucune'] , helper::arrayColumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ 'label' => 'Recherche dans le site', 'selected' => $this->getData(['locale', 'searchPageId']), 'help' => 'Sélectionnez une page contenant le module \'Recherche\'. Une option du pied de page ajoute un lien discret vers cette page.' @@ -95,7 +95,7 @@
    'Page par défaut'],helper::arrayCollumn($module::$orphansList, 'title', 'SORT_ASC')), [ + echo template::select('localePage302', array_merge(['none' => 'Page par défaut'],helper::arrayColumn($module::$orphansList, 'title', 'SORT_ASC')), [ 'label' => 'Site en maintenance', 'selected' =>$this->getData(['locale', 'page302']), 'help' => 'Cette page ne doit pas apparaître dans l\'arborescence du menu. Créez une page orpheline.' diff --git a/core/module/config/view/restore/restore.php b/core/module/config/view/restore/restore.php index 22a63cc1..5e4ff26d 100644 --- a/core/module/config/view/restore/restore.php +++ b/core/module/config/view/restore/restore.php @@ -38,46 +38,4 @@
    -
    -
    -
    -

    Conversion après la restauration

    -
    -
    - getData(['core', 'baseUrl'])) ) { - $baseUrlValue = 'Pas de donnée dans la sauvegarde'; - $buttonClass = 'disabled'; - } elseif ($this->getData(['core', 'baseUrl']) === '') { - $baseUrlValue = '/'; - $buttonClass = helper::baseUrl(false,false) !== $this->getData(['core', 'baseUrl']) ? '' : 'disabled'; - } else { - $baseUrlValue = str_replace('?','',$this->getData(['core', 'baseUrl'])); - $buttonClass = helper::baseUrl(false,false) !== $baseUrlValue ? '' : 'disabled'; - } - echo template::text('configRestoreBaseURLToConvert', [ - 'label' => 'Dossier de l\'archive' , - 'value' => $baseUrlValue, - 'readonly' => true, - 'help' => 'Le dossier de base du site est stockée dans la sauvegarde.' - ]); ?> -
    -
    - 'Dossier du site actuel', - 'value' => helper::baseUrl(false,false), - 'readonly' => true - ]); ?> -
    -
    - helper::baseUrl() . 'config/updateBaseUrl', - 'class' => $buttonClass, - 'value' => 'convertir' - ]); ?> -
    -
    -
    -
    -
    diff --git a/core/module/config/view/setup/setup.php b/core/module/config/view/setup/setup.php index 97c603c0..3b371908 100644 --- a/core/module/config/view/setup/setup.php +++ b/core/module/config/view/setup/setup.php @@ -42,9 +42,10 @@ ]); ?>
    - helper::checkRewrite(), - 'help' => 'Supprime ? dans les URL et redirige sur le protocole HTTPS.' + 'help' => 'Supprime le point d\'interrogation dans les URL, l\'option est indisponible avec les autres serveurs Web', + 'disabled' => strpos($_SERVER["SERVER_SOFTWARE"], 'Apache') > 0 ? true : false ]); ?>
    diff --git a/core/module/config/view/social/social.php b/core/module/config/view/social/social.php index 8ef40e88..a28fffe4 100644 --- a/core/module/config/view/social/social.php +++ b/core/module/config/view/social/social.php @@ -23,7 +23,7 @@
    helper::baseUrl() . 'config/generateFiles', + 'href' => helper::baseUrl() . 'config/siteMap', 'value' => 'Générer sitemap.xml et robots.txt' ]); ?>
    diff --git a/core/module/install/install.php b/core/module/install/install.php index 365defc2..0145b020 100644 --- a/core/module/install/install.php +++ b/core/module/install/install.php @@ -54,11 +54,11 @@ class install extends common { $userLastname = $this->getInput('installLastname', helper::FILTER_STRING_SHORT, true); $userMail = $this->getInput('installMail', helper::FILTER_MAIL, true); $userId = $this->getInput('installId', helper::FILTER_ID, true); - + // Création de l'utilisateur si les données sont complétées. // success retour de l'enregistrement des données - + $success = $this->setData([ 'user', $userId, @@ -73,7 +73,7 @@ class install extends common { 'password' => $this->getInput('installPassword', helper::FILTER_PASSWORD, true) ] ]); - + // Compte créé, envoi du mail et création des données du site if ($success) { // Formulaire complété envoi du mail // Envoie le mail @@ -97,7 +97,7 @@ class install extends common { $this->setData(['module', 'blog', 'posts', 'mon-premier-article', 'userId', $userId]); $this->setData(['module', 'blog', 'posts', 'mon-deuxieme-article', 'userId', $userId]); $this->setData(['module', 'blog', 'posts', 'mon-troisieme-article', 'userId', $userId]); - } + } // Images exemples livrées dans tous les cas try { // Décompression dans le dossier de fichier temporaires @@ -120,11 +120,7 @@ class install extends common { // Créer le dossier des fontes if (!is_dir(self::DATA_DIR . 'fonts')) { mkdir(self::DATA_DIR . 'fonts'); - } - // Stocker le dossier d'installation - $this->setData(['core', 'baseUrl', helper::baseUrl(false,false) ]); - // Créer sitemap - $this->createSitemap(); + } // Installation du thème sélectionné $dataThemes = file_get_contents('core/module/install/ressource/themes/themes.json'); @@ -141,7 +137,7 @@ class install extends common { } $this->copyDir('core/module/install/ressource/themes', self::FILE_DIR . 'source/theme'); unlink(self::FILE_DIR . 'source/theme/themes.json'); - + // Valeurs en sortie $this->addOutput([ 'redirect' => helper::baseUrl(false), @@ -153,8 +149,13 @@ class install extends common { // Récupération de la liste des thèmes $dataThemes = file_get_contents('core/module/install/ressource/themes/themes.json'); $dataThemes = json_decode($dataThemes, true); - self::$themes = helper::arrayCollumn($dataThemes, 'name'); - + self::$themes = helper::arrayColumn($dataThemes, 'name'); + + // Créer sitemap + $this->createSitemap(); + // Mise à jour de la liste des pages pour TinyMCE + $this->listPages(); + // Valeurs en sortie $this->addOutput([ 'display' => self::DISPLAY_LAYOUT_LIGHT, @@ -251,19 +252,23 @@ class install extends common { $success = true; $rewrite = $this->getInput('data'); // Réécriture d'URL - if ($rewrite === "true") { - $success = (file_put_contents( + if ($rewrite === "true") { // Ajout des lignes dans le .htaccess + $fileContent = file_get_contents('.htaccess'); + $rewriteData = PHP_EOL . + '# URL rewriting' . PHP_EOL . + '' . PHP_EOL . + "\tRewriteEngine on" . PHP_EOL . + "\tRewriteBase " . helper::baseUrl(false, false) . PHP_EOL . + "\tRewriteCond %{REQUEST_FILENAME} !-f" . PHP_EOL . + "\tRewriteCond %{REQUEST_FILENAME} !-d" . PHP_EOL . + "\tRewriteRule ^(.*)$ index.php?$1 [L]" . PHP_EOL . + ''. PHP_EOL . + '# URL rewriting' . PHP_EOL ; + $fileContent = str_replace('# URL rewriting', $rewriteData, $fileContent); + file_put_contents( '.htaccess', - PHP_EOL . - '' . PHP_EOL . - "\tRewriteEngine on" . PHP_EOL . - "\tRewriteBase " . helper::baseUrl(false, false) . PHP_EOL . - "\tRewriteCond %{REQUEST_FILENAME} !-f" . PHP_EOL . - "\tRewriteCond %{REQUEST_FILENAME} !-d" . PHP_EOL . - "\tRewriteRule ^(.*)$ index.php?$1 [L]" . PHP_EOL . - '', - FILE_APPEND - ) !== false); + $fileContent + ); } // Recopie htaccess if ($this->getData(['config','autoUpdateHtaccess']) && @@ -271,7 +276,7 @@ class install extends common { ) { // L'écraser avec le backup $success = copy( '.htaccess.bak' ,'.htaccess' ); - // Effacer l ebackup + // Effacer le backup unlink('.htaccess.bak'); } // Valeurs en sortie diff --git a/core/module/install/ressource/defaultdata.php b/core/module/install/ressource/defaultdata.php index 197d69b5..0bc0ecc2 100644 --- a/core/module/install/ressource/defaultdata.php +++ b/core/module/install/ressource/defaultdata.php @@ -38,7 +38,8 @@ class init extends common { 'captchaStrong' => false, "captchaType" => 'num', 'autoDisconnect' => true, - 'showPassword' => true + 'showPassword' => true, + 'redirectLogin' => true ], 'i18n' => [ 'enable'=> true, @@ -56,12 +57,11 @@ class init extends common { ] ], 'core' => [ - 'dataVersion' => 11300, + 'dataVersion' => 11400, 'lastBackup' => 0, 'lastClearTmp' => 0, 'lastAutoUpdate' => 0, - 'updateAvailable' => false, - 'baseUrl' => '' + 'updateAvailable' => false ], 'locale' => [ 'homePageId' => 'accueil', @@ -87,7 +87,108 @@ class init extends common { ], 'fonts' => [ 'files' => [], - 'imported' => [] + 'imported'=> [ + 'arimo'=> [ + 'name' => 'Arimo', + 'font-family' => 'Arimo, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/arimo' + ], + 'arvo'=> [ + 'name' => 'Arvo', + 'font-family' => 'Arvo, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/arvo' + ], + 'dancing-script' => [ + 'name' => 'Dancing Script', + 'font-family' => '\'Dancing Script\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/dancing-script' + ], + 'droid-sans-2'=> [ + 'name' => 'Droid Sans', + 'font-family' => '\'Droid Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/droid-sans-2' + ], + 'droid-serif-2'=> [ + 'name' => 'Droid Serif', + 'font-family' => '\'Droid Serif\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/droid-serif-2' + ], + 'indie-flower'=> [ + 'name' => 'Indie Flower', + 'font-family' => '\'Indie Flower\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/indie-flower' + ], + 'liberation-sans'=> [ + 'name' => 'Liberation Sans', + 'font-family' => '\'Liberation Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/liberation-sans' + ], + 'liberation-serif'=> [ + 'name' => 'Liberation Serif', + 'font-family' => '\'Liberation Serif\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/liberation-serif' + ], + 'lobster-2'=> [ + 'name' => 'Lobster', + 'font-family' => 'Lobster, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/lobster-2' + ], + 'lato'=> [ + 'name' => 'lato', + 'font-family' => 'Lato, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/lato' + ], + 'lora'=> [ + 'name' => 'Lora', + 'font-family' => 'Lora, serif', + 'resource' => 'https://fonts.cdnfonts.com/css/lora' + ], + 'old-standard-tt-3'=> [ + 'name' => 'Old Standard TT', + 'font-family' => '\'Old Standard TT\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/old-standard-tt-3' + ], + 'open-sans' => [ + 'name' => 'Open Sans', + 'font-family' => '\'Open Sans\', sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/open-sans' + ], + 'oswald-4'=> [ + 'name' => 'Oswald', + 'font-family' => 'Oswald, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/oswald-4' + ], + 'pt-mono'=> [ + 'name' => 'PT Mono', + 'font-family' => '\'PT Mono\', monospace', + 'resource' => 'https://fonts.cdnfonts.com/css/pt-mono' + ], + 'pt-serif'=> [ + 'name' => 'PR Serif', + 'font-family' => '\'PT Serif\', serif', + 'resource' => 'https://fonts.cdnfonts.com/css/pt-serif' + ], + 'rancho'=> [ + 'name' => 'Rancho', + 'font-family' => 'Rancho, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/rancho' + ], + 'roboto'=> [ + 'name' => 'Roboto', + 'font-family' => 'Roboto, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/roboto' + ], + 'ubuntu'=> [ + 'name' => 'Ubuntu', + 'font-family' => 'Ubuntu, sans-serif', + 'resource' => 'https://fonts.cdnfonts.com/css/ubuntu' + ], + 'vollkorn'=> [ + 'name' => 'Vollkorn', + 'font-family' => 'Vollkorn, serif', + 'resource' => 'https://fonts.cdnfonts.com/css/vollkorn' + ] + ] ], 'page' => [ 'accueil' => [ @@ -132,7 +233,7 @@ class init extends common { ], 'footer' => [ 'backgroundColor' => 'rgba(255, 255, 255, 1)', - 'font' => 'open-sans', + 'font' => 'georgia', 'fontSize' => '.8em', 'fontWeight' => 'normal', 'height' => '5px', @@ -159,7 +260,7 @@ class init extends common { ], 'header' => [ 'backgroundColor' => 'rgba(32, 59, 82, 1)', - 'font' => 'oswald-4', + 'font' => 'arial', 'fontSize' => '2em', 'fontWeight' => 'normal', 'height' => '150px', @@ -182,7 +283,7 @@ class init extends common { 'menu' => [ 'backgroundColor' => 'rgba(32, 59, 82, 1)', 'backgroundColorSub' => 'rgba(32, 59, 82, 1)', - 'font' => 'open-sans', + 'font' => 'arial', 'fontSize' => '1em', 'fontWeight' => 'normal', 'height' => '15px 10px', @@ -213,13 +314,13 @@ class init extends common { 'borderColor' => 'rgba(236, 239, 241, 1)' ], 'text' => [ - 'font' => 'open-sans', + 'font' => 'georgia', 'fontSize' => '13px', 'textColor' => 'rgba(33, 34, 35, 1)', 'linkColor' => 'rgba(74, 105, 189, 1)' ], 'title' => [ - 'font' => 'oswald-4', + 'font' => 'arial', 'fontWeight' => 'normal', 'textColor' => 'rgba(74, 105, 189, 1)', 'textTransform' => 'none' @@ -231,9 +332,9 @@ class init extends common { ], 'admin' => [ 'backgroundColor' => 'rgba(255, 255, 255, 1)', - 'fontText' => 'open-sans', + 'fontText' => 'georgia', 'fontSize' => '13px', - 'fontTitle' => 'oswald-4', + 'fontTitle' => 'arial', 'colorText' => 'rgba(33, 34, 35, 1)', 'colorTitle' => 'rgba(74, 105, 189, 1)', 'backgroundColorButton' => 'rgba(74, 105, 189, 1)', diff --git a/core/module/page/page.php b/core/module/page/page.php index 17cbb7d3..f91fbdd7 100644 --- a/core/module/page/page.php +++ b/core/module/page/page.php @@ -146,7 +146,7 @@ class page extends common { 'group' => self::GROUP_VISITOR, 'targetBlank' => false, 'title' => $pageTitle, - 'shortTitle' => $pageTitle, + 'shortTitle' => '', 'block' => '12', 'barLeft' => '', 'barRight' => '', @@ -164,6 +164,8 @@ class page extends common { $this->setPage($pageId, '

    Contenu de votre nouvelle page.

    ', self::$i18n); // Met à jour le site map $this->createSitemap('all'); + // Mise à jour de la liste des pages pour TinyMCE + $this->listPages(); // Valeurs en sortie $this->addOutput([ 'redirect' => helper::baseUrl() . $pageId, @@ -289,6 +291,8 @@ class page extends common { $this->deleteData(['module', $url[0]]); // Met à jour le site map $this->createSitemap('all'); + // Mise à jour de la liste des pages pour TinyMCE + $this->listPages(); // Valeurs en sortie $this->addOutput([ 'redirect' => helper::baseUrl(false), @@ -388,19 +392,25 @@ class page extends common { $lastPosition = 1; $hierarchy = $this->getInput('pageEditParentPageId') ? $this->getHierarchy($this->getInput('pageEditParentPageId')) : array_keys($this->getHierarchy()); $position = $this->getInput('pageEditPosition', helper::FILTER_INT); + $extraPosition = $this->getinput('pageEditExtraPosition', helper::FILTER_BOOLEAN); foreach($hierarchy as $hierarchyPageId) { - // Ignore la page en cours de modification - if($hierarchyPageId === $this->getUrl(2)) { - continue; - } - // Incrémente de +1 pour laisser la place à la position de la page en cours de modification - if($lastPosition === $position) { + + // Ne traite que les pages du menu sélectionné + if ($this->getData(['page', $hierarchyPageId, 'extraPosition']) === $extraPosition ) { + // Ignore la page en cours de modification + if($hierarchyPageId === $this->getUrl(2) ) { + continue; + } + // Incrémente de +1 pour laisser la place à la position de la page en cours de modification + if($lastPosition === $position) { + $lastPosition++; + } + // Change la position + $this->setData(['page', $hierarchyPageId, 'position', $lastPosition]); + // Incrémente pour la prochaine position $lastPosition++; } - // Change la position - $this->setData(['page', $hierarchyPageId, 'position', $lastPosition]); - // Incrémente pour la prochaine position - $lastPosition++; + } if ($this->getinput('pageEditBlock') !== 'bar') { $barLeft = $this->getinput('pageEditBarLeft'); @@ -476,7 +486,7 @@ class page extends common { 'hideMenuSide' => $this->getinput('pageEditHideMenuSide', helper::FILTER_BOOLEAN), 'hideMenuHead' => $this->getinput('pageEditHideMenuHead', helper::FILTER_BOOLEAN), 'hideMenuChildren' => $this->getinput('pageEditHideMenuChildren', helper::FILTER_BOOLEAN), - 'extraPosition' => $this->getinput('pageEditExtraPosition', helper::FILTER_BOOLEAN) + 'extraPosition' => $extraPosition ] ]); @@ -487,9 +497,11 @@ class page extends common { $content = empty($this->getInput('pageEditContent', null)) ? '

    ' : str_replace('

    ', '

     

    ', $this->getInput('pageEditContent', null)); $this->setPage($pageId , $content, self::$i18n); - // Met à jour le site map $this->createSitemap('all'); + // Mise à jour de la liste des pages pour TinyMCE + $this->listPages(); + // Redirection vers la configuration if( $this->getInput('pageEditModuleRedirect', helper::FILTER_BOOLEAN) @@ -510,7 +522,7 @@ class page extends common { } } } - self::$moduleIds = array_merge( ['' => 'Aucun'] , helper::arrayCollumn(helper::getModules(),'realName','SORT_ASC')); // Pages sans parent + self::$moduleIds = array_merge( ['' => 'Aucun'] , helper::arrayColumn(helper::getModules(),'realName','SORT_ASC')); // Pages sans parent foreach($this->getHierarchy() as $parentPageId => $childrenPageIds) { if($parentPageId !== $this->getUrl(2)) { self::$pagesNoParentId[$parentPageId] = $this->getData(['page', $parentPageId, 'title']); @@ -523,8 +535,6 @@ class page extends common { self::$pagesBarId[$parentPageId] = $this->getData(['page', $parentPageId, 'title']); } } - // Mise à jour de la liste des pages pour TinyMCE - $this->pages2Json(); // Valeurs en sortie $this->addOutput([ 'title' => $this->getData(['page', $this->getUrl(2), 'title']), diff --git a/core/module/page/view/edit/edit.js.php b/core/module/page/view/edit/edit.js.php index e344bd80..61dd9794 100644 --- a/core/module/page/view/edit/edit.js.php +++ b/core/module/page/view/edit/edit.js.php @@ -514,6 +514,12 @@ pageTypeMenuDOM.on("change", function() { } }); +/** + * Duplication du champ Title dans Short title + */ +$("#pageEditTitle").on("input", function() { + $("#pageEditShortTitle").val($(this).val()); +}); /** * Actualise la liste de pages lorsque le menu accessoire est sélectionné diff --git a/core/module/sitemap/sitemap.php b/core/module/sitemap/sitemap.php index 3807a403..0537f648 100644 --- a/core/module/sitemap/sitemap.php +++ b/core/module/sitemap/sitemap.php @@ -42,8 +42,8 @@ class sitemap extends common !empty($this->getData(['module',$parentId, 'posts' ]))) { $items .= '
      '; // Ids des articles par ordre de publication - $articleIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $parentId,'posts']), 'publishedOn', 'SORT_DESC'); - $articleIdsStates = helper::arrayCollumn($this->getData(['module', $parentId, 'posts']), 'state', 'SORT_DESC'); + $articleIdsPublishedOns = helper::arrayColumn($this->getData(['module', $parentId,'posts']), 'publishedOn', 'SORT_DESC'); + $articleIdsStates = helper::arrayColumn($this->getData(['module', $parentId, 'posts']), 'state', 'SORT_DESC'); $articleIds = []; foreach ($articleIdsPublishedOns as $articleId => $articlePublishedOn) { if ($articlePublishedOn <= time() and $articleIdsStates[$articleId]) { @@ -78,8 +78,8 @@ class sitemap extends common !empty($this->getData(['module', $childId, 'posts' ]))) { $items .= '
        '; // Ids des articles par ordre de publication - $articleIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $childId,'posts']), 'publishedOn', 'SORT_DESC'); - $articleIdsStates = helper::arrayCollumn($this->getData(['module', $childId, 'posts']), 'state', 'SORT_DESC'); + $articleIdsPublishedOns = helper::arrayColumn($this->getData(['module', $childId,'posts']), 'publishedOn', 'SORT_DESC'); + $articleIdsStates = helper::arrayColumn($this->getData(['module', $childId, 'posts']), 'state', 'SORT_DESC'); $articleIds = []; foreach ($articleIdsPublishedOns as $articleId => $articlePublishedOn) { if ($articlePublishedOn <= time() and $articleIdsStates[$articleId]) { diff --git a/core/module/theme/theme.php b/core/module/theme/theme.php index 9b624d66..11040563 100644 --- a/core/module/theme/theme.php +++ b/core/module/theme/theme.php @@ -1,1066 +1,1242 @@ - - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - * @copyright : Frédéric Tempez - * @copyright Copyright (C) 2018-2022, Frédéric Tempez - */ - -class theme extends common { - - public static $actions = [ - 'advanced' => self::GROUP_ADMIN, - 'body' => self::GROUP_ADMIN, - 'footer' => self::GROUP_ADMIN, - 'header' => self::GROUP_ADMIN, - 'index' => self::GROUP_ADMIN, - 'menu' => self::GROUP_ADMIN, - 'reset' => self::GROUP_ADMIN, - 'site' => self::GROUP_ADMIN, - 'admin' => self::GROUP_ADMIN, - 'manage' => self::GROUP_ADMIN, - 'export' => self::GROUP_ADMIN, - 'import' => self::GROUP_ADMIN, - 'save' => self::GROUP_ADMIN, - 'fonts' => self::GROUP_ADMIN, - 'fontAdd' => self::GROUP_ADMIN, - 'fontDelete' => self::GROUP_ADMIN - ]; - public static $aligns = [ - 'left' => 'À gauche', - 'center' => 'Au centre', - 'right' => 'À droite' - ]; - public static $attachments = [ - 'scroll' => 'Standard', - 'fixed' => 'Fixe' - ]; - public static $containerWides = [ - 'container' => 'Limitée au site', - 'none' => 'Etendue sur la page' - ]; - public static $footerblocks = [ - 1 => [ - 'hide' => 'Masqué', - 'center' => 'Affiché' ], - 2 => [ - 'hide' => 'Masqué', - 'left' => 'À gauche', - 'right' => 'À droite' ], - 3 => [ - 'hide' => 'Masqué', - 'left' => 'À gauche', - 'center' => 'Au centre', - 'right' => 'À droite' ], - 4 => [ - 'hide' => 'Masqué', - 'left' => 'En haut', - 'center' => 'Au milieu', - 'right' => 'En bas' ] - ]; - - public static $fontWeights = [ - 'normal' => 'Maigre', - 'bold' => 'Gras' - ]; - public static $footerHeights = [ - '0px' => 'Nulles (0px)', - '5px' => 'Très petites (5px)', - '10px' => 'Petites (10px)', - '15px' => 'Moyennes (15px)', - '20px' => 'Grandes (20px)' - ]; - public static $footerPositions = [ - 'hide' => 'Caché', - 'site' => 'Dans le site', - 'body' => 'En dessous du site' - ]; - public static $footerFontSizes = [ - '.8em' => 'Très petite (80%)', - '.9em' => 'Petite (90%)', - '1em' => 'Standard (100%)', - '1.1em' => 'Moyenne (110%)', - '1.2em' => 'Grande (120%)', - '1.3em' => 'Très grande (130%)' - ]; - public static $headerFontSizes = [ - '1.6em' => 'Très petite (160%)', - '1.8em' => 'Petite (180%)', - '2em' => 'Moyenne (200%)', - '2.2em' => 'Grande (220%)', - '2.4vmax' => 'Très grande (240%)' - ]; - public static $headerHeights = [ - 'unset' => 'Libre', // texte dynamique cf header.js.php - '100px' => 'Très petite (100px) ', - '150px' => 'Petite (150px)', - '200px' => 'Moyenne (200px)', - '300px' => 'Grande (300px)', - '400px' => 'Très grande (400px)', - ]; - public static $headerPositions = [ - 'body' => 'Au dessus du site', - 'site' => 'Dans le site', - 'hide' => 'Cachée' - ]; - public static $headerFeatures = [ - 'wallpaper' => 'Couleur unie ou papier-peint', - 'feature' => 'Contenu personnalisé' - ]; - public static $imagePositions = [ - 'top left' => 'En haut à gauche', - 'top center' => 'En haut au centre', - 'top right' => 'En haut à droite', - 'center left' => 'Au milieu à gauche', - 'center center' => 'Au milieu au centre', - 'center right' => 'Au milieu à droite', - 'bottom left' => 'En bas à gauche', - 'bottom center' => 'En bas au centre', - 'bottom right' => 'En bas à droite' - ]; - public static $menuFontSizes = [ - '.8em' => 'Très petite (80%)', - '.9em' => 'Petite (90%)', - '1em' => 'Standard (100%)', - '1.1em' => 'Moyenne (110%)', - '1.2em' => 'Grande (120%)', - '1.3em' => 'Très grande (130%)' - ]; - public static $menuHeights = [ - '5px 10px' => 'Très petite', - '10px' => 'Petite', - '15px 10px' => 'Moyenne', - '20px 15px' => 'Grande', - '25px 15px' => 'Très grande' - ]; - public static $menuPositionsSite = [ - 'top' => 'En-dehors du site', - 'site-first' => 'Avant la bannière', - 'site-second' => 'Après la bannière', - 'hide' => 'Caché' - ]; - public static $menuPositionsBody = [ - 'top' => 'En-dehors du site', - 'body-first' => 'Avant la bannière', - 'body-second' => 'Après la bannière', - 'site' => 'Dans le site', - 'hide' => 'Caché' - ]; - public static $menuRadius = [ - '0px' => 'Aucun', - '3px 3px 0px 0px' => 'Très léger', - '6px 6px 0px 0px' => 'Léger', - '9px 9px 0px 0px' => 'Moyen', - '12px 12px 0px 0px' => 'Important', - '15px 15px 0px 0px' => 'Très important' - ]; - public static $radius = [ - '0px' => 'Aucun', - '5px' => 'Très léger', - '10px' => 'Léger', - '15px' => 'Moyen', - '25px' => 'Important', - '50px' => 'Très important' - ]; - public static $repeats = [ - 'no-repeat' => 'Ne pas répéter', - 'repeat-x' => 'Sur l\'axe horizontal', - 'repeat-y' => 'Sur l\'axe vertical', - 'repeat' => 'Sur les deux axes' - ]; - public static $shadows = [ - '0px' => 'Aucune', - '1px 1px 5px' => 'Très légère', - '1px 1px 10px' => 'Légère', - '1px 1px 15px' => 'Moyenne', - '1px 1px 25px' => 'Importante', - '1px 1px 50px' => 'Très importante' - ]; - public static $siteFontSizes = [ - '12px' => '12 pixels', - '13px' => '13 pixels', - '14px' => '14 pixels', - '15px' => '15 pixels', - '16px' => '16 pixels' - ]; - public static $bodySizes = [ - 'auto' => 'Automatique', - '100% 100%' => 'Image étirée (100% 100%)', - 'cover' => 'Responsive (cover)', - 'contain' => 'Responsive (contain)' - ]; - public static $textTransforms = [ - 'none' => 'Standard', - 'lowercase' => 'Minuscules', - 'uppercase' => 'Majuscules', - 'capitalize' => 'Majuscule à chaque mot' - ]; - public static $siteWidths = [ - '750px' => 'Petite (750 pixels)', - '960px' => 'Moyenne (960 pixels)', - '1170px' => 'Grande (1170 pixels)', - '100%' => 'Fluide (100%)' - ]; - public static $headerWide = [ - 'auto auto' => 'Automatique', - '100% 100%' => 'Image étirée (100% 100%)', - 'cover' => 'Responsive (cover)', - 'contain' => 'Responsive (contain)' - ]; - public static $footerTemplate = [ - '1' => 'Une seule colonne', - '2' => 'Deux colonnes : 1/2 - 1/2', - '3' => 'Trois colonnes : 1/3 - 1/3 - 1/3', - '4' => 'Trois lignes superposées' - ]; - public static $burgerContent = [ - 'none' => 'Aucun', - 'title' => 'Titre du site', - 'logo' => 'Logo du site' - ]; - - - // Variable pour construire la liste des pages du site - public static $pagesList = []; - // Variable pour construire la liste des fontes installées - public static $fontsList = []; - - /** - * Thème des écrans d'administration - */ - public function admin() { - // Soumission du formulaire - if($this->isPost()) { - $this->setData(['admin', [ - 'backgroundColor' => $this->getInput('adminBackgroundColor'), - 'colorTitle' => $this->getInput('adminColorTitle'), - 'colorText' => $this->getInput('adminColorText'), - 'colorButtonText' => $this->getInput('adminColorButtonText'), - 'backgroundColorButton' => $this->getInput('adminColorButton'), - 'backgroundColorButtonGrey' => $this->getInput('adminColorGrey'), - 'backgroundColorButtonRed' => $this->getInput('adminColorRed'), - 'backgroundColorButtonGreen'=> $this->getInput('adminColorGreen'), - 'backgroundColorButtonHelp'=> $this->getInput('adminColorHelp'), - 'fontText' => $this->getInput('adminFontText'), - 'fontSize' => $this->getInput('adminFontTextSize'), - 'fontTitle' => $this->getInput('adminFontTitle'), - 'backgroundBlockColor' => $this->getInput('adminBackGroundBlockColor'), - 'borderBlockColor' => $this->getInput('adminBorderBlockColor'), - ]]); - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme/admin', - 'state' => true - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Administration', - 'view' => 'admin', - 'vendor' => [ - 'tinycolorpicker' - ], - ]); - } - - /** - * Mode avancé - */ - public function advanced() { - // Soumission du formulaire - if($this->isPost()) { - // Enregistre le CSS - file_put_contents(self::DATA_DIR.'custom.css', $this->getInput('themeAdvancedCss', null)); - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme/advanced', - 'state' => true - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Éditeur CSS', - 'vendor' => [ - 'codemirror' - ], - 'view' => 'advanced' - ]); - } - - /** - * Options de l'arrière plan - */ - public function body() { - // Soumission du formulaire - if($this->isPost()) { - $this->setData(['theme', 'body', [ - 'backgroundColor' => $this->getInput('themeBodyBackgroundColor'), - 'image' => $this->getInput('themeBodyImage'), - 'imageAttachment' => $this->getInput('themeBodyImageAttachment'), - 'imagePosition' => $this->getInput('themeBodyImagePosition'), - 'imageRepeat' => $this->getInput('themeBodyImageRepeat'), - 'imageSize' => $this->getInput('themeBodyImageSize'), - 'toTopbackgroundColor' => $this->getInput('themeBodyToTopBackground'), - 'toTopColor' => $this->getInput('themeBodyToTopColor') - ]]); - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme', - 'state' => true - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Personnalisation de l\'arrière plan', - 'vendor' => [ - 'tinycolorpicker' - ], - 'view' => 'body' - ]); - } - - /** - * Options du pied de page - */ - public function footer() { - // Soumission du formulaire - if($this->isPost()) { - if ( $this->getInput('themeFooterCopyrightPosition') === 'hide' && - $this->getInput('themeFooterSocialsPosition') === 'hide' && - $this->getInput('themeFooterTextPosition') === 'hide' ) { - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Sélectionnez au moins un contenu à afficher', - 'redirect' => helper::baseUrl() . 'theme/footer', - 'state' => false - ]); - } else { - $this->setData(['theme', 'footer', [ - 'backgroundColor' => $this->getInput('themeFooterBackgroundColor'), - 'copyrightAlign' => $this->getInput('themeFooterCopyrightAlign'), - 'height' => $this->getInput('themeFooterHeight'), - 'loginLink' => $this->getInput('themeFooterLoginLink'), - 'margin' => $this->getInput('themeFooterMargin', helper::FILTER_BOOLEAN), - 'position' => $this->getInput('themeFooterPosition'), - 'fixed' => $this->getInput('themeFooterFixed', helper::FILTER_BOOLEAN), - 'socialsAlign' => $this->getInput('themeFooterSocialsAlign'), - 'text' => $this->getInput('themeFooterText', null), - 'textAlign' => $this->getInput('themeFooterTextAlign'), - 'textColor' => $this->getInput('themeFooterTextColor'), - 'copyrightPosition' => $this->getInput('themeFooterCopyrightPosition'), - 'textPosition' => $this->getInput('themeFooterTextPosition'), - 'socialsPosition' => $this->getInput('themeFooterSocialsPosition'), - 'textTransform' => $this->getInput('themeFooterTextTransform'), - 'font' => $this->getInput('themeFooterFont'), - 'fontSize' => $this->getInput('themeFooterFontSize'), - 'fontWeight' => $this->getInput('themeFooterFontWeight'), - 'displayVersion' => $this->getInput('themefooterDisplayVersion', helper::FILTER_BOOLEAN), - 'displaySiteMap' => $this->getInput('themefooterDisplaySiteMap', helper::FILTER_BOOLEAN), - 'displayCopyright' => $this->getInput('themefooterDisplayCopyright', helper::FILTER_BOOLEAN), - 'displayCookie' => $this->getInput('themefooterDisplayCookie', helper::FILTER_BOOLEAN), - 'displayLegal' => $this->getInput('themeFooterDisplayLegal', helper::FILTER_BOOLEAN), - 'displaySearch' => $this->getInput('themeFooterDisplaySearch', helper::FILTER_BOOLEAN), - 'displayMemberBar'=> $this->getInput('themeFooterDisplayMemberBar', helper::FILTER_BOOLEAN), - 'template' => $this->getInput('themeFooterTemplate') - ]]); - - // Sauvegarder la configuration localisée - $this->setData(['locale','legalPageId', $this->getInput('configLegalPageId')]); - $this->setData(['locale','searchPageId', $this->getInput('configSearchPageId')]); - - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme', - 'state' => true - ]); - } - } - - // Liste des pages - self::$pagesList = $this->getData(['page']); - foreach(self::$pagesList as $page => $pageId) { - if ($this->getData(['page',$page,'block']) === 'bar' || - $this->getData(['page',$page,'disable']) === true) { - unset(self::$pagesList[$page]); - } - } - - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Personnalisation du pied de page', - 'vendor' => [ - 'tinycolorpicker', - 'tinymce' - ], - 'view' => 'footer' - ]); - } - - /** - * Options de la bannière - */ - public function header() { - // Soumission du formulaire - if($this->isPost()) { - // Modification des URL des images dans la bannière perso - $featureContent = $this->getInput('themeHeaderText', null); - $featureContent = str_replace(helper::baseUrl(false,false), './', $featureContent); - - /** - * Stocker les images incluses dans la bannière perso dans un tableau - */ - preg_match_all('/]+>/i',$featureContent, $results); - foreach($results[0] as $value) { - // Lire le contenu XML - $sx = simplexml_load_string($value); - // Élément à remplacer - $files [] = str_replace('./site/file/source/','',(string) $sx[0]['src']); - } - - // Sauvegarder - $this->setData(['theme', 'header', [ - 'backgroundColor' => $this->getInput('themeHeaderBackgroundColor'), - 'font' => $this->getInput('themeHeaderFont'), - 'fontSize' => $this->getInput('themeHeaderFontSize'), - 'fontWeight' => $this->getInput('themeHeaderFontWeight'), - 'height' => $this->getInput('themeHeaderHeight'), - 'wide' => $this->getInput('themeHeaderWide'), - 'image' => $this->getInput('themeHeaderImage'), - 'imagePosition' => $this->getInput('themeHeaderImagePosition'), - 'imageRepeat' => $this->getInput('themeHeaderImageRepeat'), - 'margin' => $this->getInput('themeHeaderMargin', helper::FILTER_BOOLEAN), - 'position' => $this->getInput('themeHeaderPosition'), - 'textAlign' => $this->getInput('themeHeaderTextAlign'), - 'textColor' => $this->getInput('themeHeaderTextColor'), - 'textHide' => $this->getInput('themeHeaderTextHide', helper::FILTER_BOOLEAN), - 'textTransform' => $this->getInput('themeHeaderTextTransform'), - 'linkHomePage' => $this->getInput('themeHeaderlinkHomePage',helper::FILTER_BOOLEAN), - 'imageContainer' => $this->getInput('themeHeaderImageContainer'), - 'tinyHidden' => $this->getInput('themeHeaderTinyHidden', helper::FILTER_BOOLEAN), - 'feature' => $this->getInput('themeHeaderFeature'), - 'featureContent' => $featureContent, - 'featureFiles' => $files - ]]); - // Modification de la position du menu selon la position de la bannière - if ( $this->getData(['theme','header','position']) == 'site' ) - { - $this->setData(['theme', 'menu', 'position',str_replace ('body-','site-',$this->getData(['theme','menu','position']))]); - } - if ( $this->getData(['theme','header','position']) == 'body') - { - $this->setData(['theme', 'menu', 'position',str_replace ('site-','body-',$this->getData(['theme','menu','position']))]); - } - // Menu accroché à la bannière qui devient cachée - if ( $this->getData(['theme','header','position']) == 'hide' && - in_array( $this->getData(['theme','menu','position']) , ['body-first', 'site-first', 'body-first' , 'site-second']) - ) { - $this->setData(['theme', 'menu', 'position','site']); - } - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme', - 'state' => true - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Personnalisation de la bannière', - 'vendor' => [ - 'tinycolorpicker', - 'tinymce' - ], - 'view' => 'header' - ]); - } - - /** - * Accueil de la personnalisation - */ - public function index() { - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Personnalisation des thèmes', - 'view' => 'index' - ]); - } - - /** - * Options du menu - */ - public function menu() { - // Soumission du formulaire - if($this->isPost()) { - $this->setData(['theme', 'menu', [ - 'backgroundColor' => $this->getInput('themeMenuBackgroundColor'), - 'backgroundColorSub' => $this->getInput('themeMenuBackgroundColorSub'), - 'font' => $this->getInput('themeMenuFont'), - 'fontSize' => $this->getInput('themeMenuFontSize'), - 'fontWeight' => $this->getInput('themeMenuFontWeight'), - 'height' => $this->getInput('themeMenuHeight'), - 'wide' => $this->getInput('themeMenuWide'), - 'loginLink' => $this->getInput('themeMenuLoginLink', helper::FILTER_BOOLEAN), - 'margin' => $this->getInput('themeMenuMargin', helper::FILTER_BOOLEAN), - 'position' => $this->getInput('themeMenuPosition'), - 'textAlign' => $this->getInput('themeMenuTextAlign'), - 'textColor' => $this->getInput('themeMenuTextColor'), - 'textTransform' => $this->getInput('themeMenuTextTransform'), - 'fixed' => $this->getInput('themeMenuFixed', helper::FILTER_BOOLEAN), - 'activeColorAuto' => $this->getInput('themeMenuActiveColorAuto', helper::FILTER_BOOLEAN), - 'activeColor' => $this->getInput('themeMenuActiveColor'), - 'activeTextColor' => $this->getInput('themeMenuActiveTextColor'), - 'radius' => $this->getInput('themeMenuRadius'), - 'burgerTitle' => $this->getInput('themeMenuBurgerTitle', helper::FILTER_BOOLEAN), - 'memberBar' => $this->getInput('themeMenuMemberBar', helper::FILTER_BOOLEAN), - 'burgerLogo' => $this->getInput('themeMenuBurgerLogo'), - 'burgerContent' => $this->getInput('themeMenuBurgerContent') - ]]); - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme', - 'state' => true - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Personnalisation du menu', - 'vendor' => [ - 'tinycolorpicker' - ], - 'view' => 'menu' - ]); - } - - /** - * Options des fontes - */ - public function fonts() { - - // Polices trouvées dans la configuration - $fonts = $this->getData(['fonts']); - - // Polices liées au thème - $used = [ - 'Bannière' => $this->getData (['theme', 'header', 'font']), - 'Menu' => $this->getData (['theme', 'menu', 'font']), - 'Titre ' => $this->getData (['theme', 'title', 'font']), - 'Texte' => $this->getData (['theme', 'text', 'font']), - 'Pied de page' => $this->getData (['theme', 'footer', 'font']), - 'Titre (admin)' => $this->getData (['admin', 'fontTitle' ]), - 'Admin (texte)' => $this->getData (['admin', 'fontText' ]) - ]; - - // Parcourir les fontes installées et construire le tableau pour le formulaire - foreach (self::$fonts as $fontId => $fontName) { - - // Fontes utilisées par le thème - $fontUsed[$fontId] = ''; - foreach ($used as $key => $value) { - if ( $value === $fontId) { - $fontUsed[$fontId] .= $key . '
        '; - } - } - self::$fontsList [] = [ - $fontName, - $fontId, - $fontUsed[$fontId], - //array_key_exists($fontId, $fonts['imported']) ? 'Importée' : '', - array_key_exists($fontId, $fonts['files']) ? $fonts['files'][$fontId] : 'CDN Fonts', - array_key_exists($fontId, $fonts['imported']) || array_key_exists($fontId, $fonts['files']) - ? template::button('themeFontDelete' . $fontId, [ - 'class' => 'themeFontDelete buttonRed', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/fontDelete/' . $fontId . '/' . $_SESSION['csrf'], - 'value' => template::ico('cancel'), - 'disabled' => !empty($fontUsed[$fontId]) - ]) - : '' - ]; - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Gestion des fontes', - 'view' => 'fonts' - ]); - } - - /** - * Ajouter une fonte - */ - public function fontAdd() { - // Soumission du formulaire - if ($this->isPost()) { - - $fontId = $this->getInput('fontAddFontId', helper::FILTER_STRING_SHORT, true); - $fontName = $this->getInput('fontAddFontName',helper::FILTER_STRING_SHORT, true); - $filePath = $this->getInput('fontAddFile', helper::FILTER_STRING_SHORT); - $type = $this->getInput('fontAddFontImported', helper::FILTER_BOOLEAN) ? 'imported' : 'files'; - $e = explode ('/', $filePath); - $file = $e[count($e) - 1 ]; - - // Vérifier l'existence de fontId et validité de family name si usage en ligne de cdnFonts - $data = helper::getUrlContents('https://www.cdnfonts.com/' . $fontId . '.font'); - - if ( $filePath === '' - && $fontName !== '' - && strpos($data, $fontName) === false - ) { - - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Erreur de nom de fonte ou d\'identifiant', - 'redirect' => helper::baseUrl() . 'theme/fontAdd', - 'state' => false - ]); - - } else { - - // Concaténation dans les tableaux existants - switch ($type) { - case 'imported': - $imported = $this->getData(['fonts', 'imported']); - $imported = array_merge([$fontId => $fontName], $imported); - $this->setData(['fonts', 'imported', $imported ]); - break; - case 'files': - $files = $this->getData(['fonts', 'files']); - $files = array_merge([$fontId => $file], $files); - $this->setData(['fonts', 'files', $files ]); - // Copier la fonte si le nom du fichier est fourni - copy ( self::FILE_DIR . 'source/' . $filePath, self::DATA_DIR . 'fonts/' . $file ); - break; - } - - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'La fonte a été importée', - 'redirect' => helper::baseUrl() . 'theme/fonts', - 'state' => true - ]); - } - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Ajouter une fonte', - 'view' => 'fontAdd' - ]); - } - - /** - * Effacer une fonte - */ - public function fontDelete() { - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'theme/fonts', - 'notification' => 'Action non autorisée' - ]); - } - // Suppression - else { - - // Charger les données des fontes - $files = $this->getData(['fonts', 'files']); - $imported = $this->getData(['fonts', 'imported']); - - // Effacer le fichier existant - if ( file_exists(self::DATA_DIR . $files[$this->getUrl(2)]) ) { - unlink(self::DATA_DIR . $files[$this->getUrl(2)]); - } - - // Supprimer les entrées - unset($files[$this->getUrl(2)]); - unset($imported[$this->getUrl(2)]); - - // Mettre à jour le fichier des fontes - $this->setData(['fonts', 'files', $files ]); - $this->setData(['fonts', 'imported', $imported ]); - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'theme/fonts', - 'notification' => 'Fonte supprimée', - 'state' => true - ]); - } - } - - - /** - * Réinitialisation de la personnalisation avancée - */ - public function reset() { - // $url prend l'adresse sans le token - $url = explode('&',$this->getUrl(2)); - - if ( isset($_GET['csrf']) - AND $_GET['csrf'] === $_SESSION['csrf'] - ) { - // Réinitialisation - $redirect =''; - switch ($url[0]) { - case 'admin': - $this->initData('admin'); - $redirect = helper::baseUrl() . 'theme/admin'; - break; - case 'manage': - $this->initData('theme'); - $redirect = helper::baseUrl() . 'theme/manage'; - break; - case 'custom': - unlink(self::DATA_DIR.'custom.css'); - $redirect = helper::baseUrl() . 'theme/advanced'; - break; - default : - $redirect = helper::baseUrl() . 'theme'; - } - - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Réinitialisation effectuée', - 'redirect' => $redirect, - 'state' => true - ]); - } else { - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Jeton incorrect' - ]); - } - } - - - /** - * Options du site - */ - public function site() { - // Soumission du formulaire - if($this->isPost()) { - $this->setData(['theme', 'title', [ - 'font' => $this->getInput('themeTitleFont'), - 'textColor' => $this->getInput('themeTitleTextColor'), - 'fontWeight' => $this->getInput('themeTitleFontWeight'), - 'textTransform' => $this->getInput('themeTitleTextTransform') - ]]); - $this->setData(['theme', 'text', [ - 'font' => $this->getInput('themeTextFont'), - 'fontSize' => $this->getInput('themeTextFontSize'), - 'textColor' => $this->getInput('themeTextTextColor'), - 'linkColor'=> $this->getInput('themeTextLinkColor') - ]]); - $this->setData(['theme', 'site', [ - 'backgroundColor' => $this->getInput('themeSiteBackgroundColor'), - 'radius' => $this->getInput('themeSiteRadius'), - 'shadow' => $this->getInput('themeSiteShadow'), - 'width' => $this->getInput('themeSiteWidth'), - 'margin' => $this->getInput('themeSiteMargin',helper::FILTER_BOOLEAN) - ]]); - $this->setData(['theme', 'button', [ - 'backgroundColor' => $this->getInput('themeButtonBackgroundColor') - ]]); - $this->setData(['theme', 'block', [ - 'backgroundColor' => $this->getInput('themeBlockBackgroundColor'), - 'borderColor' => $this->getInput('themeBlockBorderColor') - ]]); - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Modifications enregistrées', - 'redirect' => helper::baseUrl() . 'theme', - 'state' => true - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Personnalisation du site', - 'vendor' => [ - 'tinycolorpicker', - 'tinymce' - ], - 'view' => 'site' - ]); - } - - /** - * Import du thème - */ - public function manage() { - if($this->isPost() ) { - - $zipFilename = $this->getInput('themeManageImport', helper::FILTER_STRING_SHORT, true); - $data = $this->import(self::FILE_DIR.'source/' . $zipFilename); - if ($data['success']) { - header("Refresh:0"); - } else { - // Valeurs en sortie - $this->addOutput([ - 'notification' => $data['notification'], - 'state' => $data['success'], - 'title' => 'Gestion des thèmes', - 'view' => 'manage' - ]);; - } - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Gestion des thèmes', - 'view' => 'manage' - ]); - } - - /** - * Importe un thème - * @param string Url du thème à télécharger - * @param @return array contenant $success = true ou false ; $ notification string message à afficher - */ - - public function import($zipName = '') { - - if ($zipName !== '' && - file_exists($zipName)) { - // Init variables de retour - $success = false; - $notification = ''; - // Dossier temporaire - $tempFolder = uniqid(); - // Ouvrir le zip - $zip = new ZipArchive(); - if ($zip->open($zipName) === TRUE) { - mkdir (self::TEMP_DIR . $tempFolder, 0755); - $zip->extractTo(self::TEMP_DIR . $tempFolder ); - $modele = ''; - // Archive de thème ? - if ( - file_exists(self::TEMP_DIR . $tempFolder . '/site/data/custom.css') - AND file_exists(self::TEMP_DIR . $tempFolder . '/site/data/theme.css') - AND file_exists(self::TEMP_DIR . $tempFolder . '/site/data/theme.json') - ) { - $modele = 'theme'; - } - if( - file_exists(self::TEMP_DIR . $tempFolder . '/site/data/admin.json') - AND file_exists(self::TEMP_DIR . $tempFolder . '/site/data/admin.css') - ) { - $modele = 'admin'; - - } - if (!empty($modele) - ) { - // traiter l'archive - $success = $zip->extractTo('.'); - - // Substitution des fontes Google - if ($modele = 'theme') { - $c = $this->subFonts(self::DATA_DIR . 'theme.json'); - // Un remplacement nécessite la régénération de la feuille de style - if ($c > 0 - AND file_exists(self::DATA_DIR . 'theme.css') - ) { - unlink(self::DATA_DIR . 'theme.css'); - } - } - if ($modele = 'admin') { - $c = $this->subFonts(self::DATA_DIR . 'admin.json'); - // Un remplacement nécessite la régénération de la feuille de style - if ($c > 0 - AND file_exists(self::DATA_DIR . 'admin.css') - ) { - unlink(self::DATA_DIR . 'admin.css'); - } - } - - // traitement d'erreur - $notification = $success ? 'Le thème a été importé' : 'Erreur lors de l\'extraction, vérifiez les permissions.'; - - - } else { - // pas une archive de thème - $success = false; - $notification = 'Ce n\'est pas l\'archive d\'un thème !'; - } - // Supprimer le dossier temporaire même si le thème est invalide - $this->removeDir(self::TEMP_DIR . $tempFolder); - $zip->close(); - } else { - // erreur à l'ouverture - $success = false; - $notification = 'Impossible d\'ouvrir l\'archive'; - } - return (['success' => $success, 'notification' => $notification]); - } - - return (['success' => false, 'notification' => 'Archive non spécifiée ou introuvable']); - } - - - - /** - * Export du thème - */ - public function export() { - // Make zip - $zipFilename = $this->zipTheme($this->getUrl(2)); - // Téléchargement du ZIP - header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); - header('Content-Transfer-Encoding: binary'); - header('Content-Disposition: attachment; filename="' . $zipFilename . '"'); - header('Content-Length: ' . filesize(self::TEMP_DIR . $zipFilename)); - readfile(self::TEMP_DIR . $zipFilename); - // Nettoyage du dossier - unlink (self::TEMP_DIR . $zipFilename); - exit(); - } - - /** - * Export du thème - */ - public function save() { - // Make zip - $zipFilename = $this->zipTheme($this->getUrl(2)); - // Téléchargement du ZIP - if (!is_dir(self::FILE_DIR.'source/theme')) { - mkdir(self::FILE_DIR.'source/theme', 0755); - } - copy (self::TEMP_DIR . $zipFilename , self::FILE_DIR.'source/theme/' . $zipFilename); - // Nettoyage du dossier - unlink (self::TEMP_DIR . $zipFilename); - // Valeurs en sortie - $this->addOutput([ - 'notification' => 'Archive '.$zipFilename.' sauvegardée avec succès', - 'redirect' => helper::baseUrl() . 'theme/manage', - 'state' => true - ]); - } - - /** - * construction du zip Fonction appelée par export() et save() - * @param string $modele theme ou admin - */ - private function zipTheme($modele) { - // Creation du dossier - $zipFilename = $modele . date('Y-m-d-H-i-s', time()) . '.zip'; - $zip = new ZipArchive(); - if ($zip->open(self::TEMP_DIR . $zipFilename, ZipArchive::CREATE | ZipArchive::OVERWRITE ) === TRUE) { - switch ($modele) { - case 'admin': - $zip->addFile(self::DATA_DIR.'admin.json',self::DATA_DIR.'admin.json'); - $zip->addFile(self::DATA_DIR.'admin.css',self::DATA_DIR.'admin.css'); - // Ajoute les fontes - $zip->addEmptyDir(self::DATA_DIR .'fonts'); - $fonts = $this->getData(['fonts', 'files']); - foreach ($fonts as $fontId => $fontName) { - $zip->addFile(self::DATA_DIR .'fonts/' . $fontName, self::DATA_DIR.'fonts/' . $fontName); - } - if (file_exists(self::DATA_DIR .'fonts/fonts.html')) { - - $zip->addFile(self::DATA_DIR .'fonts/fonts.html', self::DATA_DIR .'fonts/fonts.html'); - } - break; - case 'theme': - $zip->addFile(self::DATA_DIR.'theme.json',self::DATA_DIR.'theme.json'); - $zip->addFile(self::DATA_DIR.'theme.css',self::DATA_DIR.'theme.css'); - $zip->addFile(self::DATA_DIR.'custom.css',self::DATA_DIR.'custom.css'); - // Traite l'image dans le body - if ($this->getData(['theme','body','image']) !== '' ) { - $zip->addFile(self::FILE_DIR.'source/'.$this->getData(['theme','body','image']), - self::FILE_DIR.'source/'.$this->getData(['theme','body','image']) - ); - } - // Traite l'image dans le header - if ($this->getData(['theme','header','image']) !== '' ) { - $zip->addFile(self::FILE_DIR.'source/'.$this->getData(['theme','header','image']), - self::FILE_DIR.'source/'.$this->getData(['theme','header','image']) - ); - } - // Traite les images du header perso - if (!empty($this->getData(['theme','header','featureFiles'])) ) { - foreach($this->getData(['theme','header','featureFiles']) as $value) { - $zip->addFile(self::FILE_DIR . 'source/' . $value, - self::FILE_DIR . 'source/' . $value ); - } - } - // Ajoute les fontes - $zip->addEmptyDir(self::DATA_DIR .'fonts'); - $fonts = $this->getData(['fonts', 'files']); - foreach ($fonts as $fontId => $fontName) { - $zip->addFile(self::DATA_DIR .'fonts/' . $fontName, self::DATA_DIR.'fonts/' . $fontName); - } - if (file_exists(self::DATA_DIR .'fonts/fonts.html')) { - - $zip->addFile(self::DATA_DIR .'fonts/fonts.html', self::DATA_DIR .'fonts/fonts.html'); - } - - break; - } - $ret = $zip->close(); - } - return ($zipFilename); - } - - /** - * Subsitution des fontes de Google Fonts vers CdnFont grâce à un tableau de conversion - * @param string $file, nom du fichier json à convertir - * @return int nombre de substitution effectuées - */ - private function subFonts($file) { - // Tableau de substitution des fontes - $fonts = [ - 'Abril+Fatface' => 'abril-fatface', - 'Arimo' => 'arimo', - 'Arvo' => 'arvo', - 'Berkshire+Swash' => 'berkshire-swash', - 'Cabin' => 'genera', - 'Dancing+Script' => 'dancing-script', - 'Droid+Sans' => 'droid-sans-2', - 'Droid+Serif' => 'droid-serif-2', - 'Fira+Sans' => 'fira-sans', - 'Inconsolata' => 'inconsolata-2', - 'Indie+Flower' =>'indie-flower', - 'Josefin+Slab' => 'josefin-sans-std', - 'Lobster' => 'lobster-2', - 'Lora' => 'lora', - 'Lato' =>'lato', - 'Marvel' => 'montserrat-ace', - 'Old+Standard+TT' => 'old-standard-tt-3', - 'Open+Sans' =>'open-sans', - // Corriger l'erreur de nom de police installée par défaut, il manquait un O en majuscule - 'open+Sans' =>'open-sans', - 'Oswald' =>'oswald-4', - 'PT+Mono' => 'pt-mono', - 'PT+Serif' =>'pt-serif', - 'Raleway' => 'raleway-5', - 'Rancho' => 'rancho', - 'Roboto' => 'Roboto', - 'Signika' => 'signika', - 'Ubuntu' => 'ubuntu', - 'Vollkorn' => 'vollkorn' - ]; - - $data = file_get_contents($file); - $count = 0; - foreach ($fonts as $oldId => $newId){ - $data = str_replace($oldId, $newId, $data, $c); - $count = $count + (int) $c; - } - // Sauvegarder la chaîne modifiée - if ($count > 0) { - file_put_contents($file, $data); - } - // Retourner le nombre d'occurrences - return ($count); - } - -} + + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + * @copyright : Frédéric Tempez + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + */ + +class theme extends common { + + public static $actions = [ + 'advanced' => self::GROUP_ADMIN, + 'body' => self::GROUP_ADMIN, + 'footer' => self::GROUP_ADMIN, + 'header' => self::GROUP_ADMIN, + 'index' => self::GROUP_ADMIN, + 'menu' => self::GROUP_ADMIN, + 'reset' => self::GROUP_ADMIN, + 'site' => self::GROUP_ADMIN, + 'admin' => self::GROUP_ADMIN, + 'manage' => self::GROUP_ADMIN, + 'export' => self::GROUP_ADMIN, + 'import' => self::GROUP_ADMIN, + 'save' => self::GROUP_ADMIN, + 'fonts' => self::GROUP_ADMIN, + 'fontAdd' => self::GROUP_ADMIN, + 'fontEdit' => self::GROUP_ADMIN, + 'fontDelete' => self::GROUP_ADMIN + ]; + public static $aligns = [ + 'left' => 'À gauche', + 'center' => 'Au centre', + 'right' => 'À droite' + ]; + public static $attachments = [ + 'scroll' => 'Standard', + 'fixed' => 'Fixe' + ]; + public static $containerWides = [ + 'container' => 'Limitée au site', + 'none' => 'Etendue sur la page' + ]; + public static $footerblocks = [ + 1 => [ + 'hide' => 'Masqué', + 'center' => 'Affiché' ], + 2 => [ + 'hide' => 'Masqué', + 'left' => 'À gauche', + 'right' => 'À droite' ], + 3 => [ + 'hide' => 'Masqué', + 'left' => 'À gauche', + 'center' => 'Au centre', + 'right' => 'À droite' ], + 4 => [ + 'hide' => 'Masqué', + 'left' => 'En haut', + 'center' => 'Au milieu', + 'right' => 'En bas' ] + ]; + + public static $fontWeights = [ + 'normal' => 'Maigre', + 'bold' => 'Gras' + ]; + public static $footerHeights = [ + '0px' => 'Nulles (0px)', + '5px' => 'Très petites (5px)', + '10px' => 'Petites (10px)', + '15px' => 'Moyennes (15px)', + '20px' => 'Grandes (20px)' + ]; + public static $footerPositions = [ + 'hide' => 'Caché', + 'site' => 'Dans le site', + 'body' => 'En dessous du site' + ]; + public static $footerFontSizes = [ + '.8em' => 'Très petite (80%)', + '.9em' => 'Petite (90%)', + '1em' => 'Standard (100%)', + '1.1em' => 'Moyenne (110%)', + '1.2em' => 'Grande (120%)', + '1.3em' => 'Très grande (130%)' + ]; + public static $headerFontSizes = [ + '1.6em' => 'Très petite (160%)', + '1.8em' => 'Petite (180%)', + '2em' => 'Moyenne (200%)', + '2.2em' => 'Grande (220%)', + '2.4vmax' => 'Très grande (240%)' + ]; + public static $headerHeights = [ + 'unset' => 'Libre', // texte dynamique cf header.js.php + '100px' => 'Très petite (100px) ', + '150px' => 'Petite (150px)', + '200px' => 'Moyenne (200px)', + '300px' => 'Grande (300px)', + '400px' => 'Très grande (400px)', + ]; + public static $headerPositions = [ + 'body' => 'Au dessus du site', + 'site' => 'Dans le site', + 'hide' => 'Cachée' + ]; + public static $headerFeatures = [ + 'wallpaper' => 'Couleur unie ou papier-peint', + 'feature' => 'Contenu personnalisé' + ]; + public static $imagePositions = [ + 'top left' => 'En haut à gauche', + 'top center' => 'En haut au centre', + 'top right' => 'En haut à droite', + 'center left' => 'Au milieu à gauche', + 'center center' => 'Au milieu au centre', + 'center right' => 'Au milieu à droite', + 'bottom left' => 'En bas à gauche', + 'bottom center' => 'En bas au centre', + 'bottom right' => 'En bas à droite' + ]; + public static $menuFontSizes = [ + '.8em' => 'Très petite (80%)', + '.9em' => 'Petite (90%)', + '1em' => 'Standard (100%)', + '1.1em' => 'Moyenne (110%)', + '1.2em' => 'Grande (120%)', + '1.3em' => 'Très grande (130%)' + ]; + public static $menuHeights = [ + '5px 10px' => 'Très petite', + '10px' => 'Petite', + '15px 10px' => 'Moyenne', + '20px 15px' => 'Grande', + '25px 15px' => 'Très grande' + ]; + public static $menuPositionsSite = [ + 'top' => 'En-dehors du site', + 'site-first' => 'Avant la bannière', + 'site-second' => 'Après la bannière', + 'hide' => 'Caché' + ]; + public static $menuPositionsBody = [ + 'top' => 'En-dehors du site', + 'body-first' => 'Avant la bannière', + 'body-second' => 'Après la bannière', + 'site' => 'Dans le site', + 'hide' => 'Caché' + ]; + public static $menuRadius = [ + '0px' => 'Aucun', + '3px 3px 0px 0px' => 'Très léger', + '6px 6px 0px 0px' => 'Léger', + '9px 9px 0px 0px' => 'Moyen', + '12px 12px 0px 0px' => 'Important', + '15px 15px 0px 0px' => 'Très important' + ]; + public static $radius = [ + '0px' => 'Aucun', + '5px' => 'Très léger', + '10px' => 'Léger', + '15px' => 'Moyen', + '25px' => 'Important', + '50px' => 'Très important' + ]; + public static $repeats = [ + 'no-repeat' => 'Ne pas répéter', + 'repeat-x' => 'Sur l\'axe horizontal', + 'repeat-y' => 'Sur l\'axe vertical', + 'repeat' => 'Sur les deux axes' + ]; + public static $shadows = [ + '0px' => 'Aucune', + '1px 1px 5px' => 'Très légère', + '1px 1px 10px' => 'Légère', + '1px 1px 15px' => 'Moyenne', + '1px 1px 25px' => 'Importante', + '1px 1px 50px' => 'Très importante' + ]; + public static $siteFontSizes = [ + '12px' => '12 pixels', + '13px' => '13 pixels', + '14px' => '14 pixels', + '15px' => '15 pixels', + '16px' => '16 pixels' + ]; + public static $bodySizes = [ + 'auto' => 'Automatique', + '100% 100%' => 'Image étirée (100% 100%)', + 'cover' => 'Responsive (cover)', + 'contain' => 'Responsive (contain)' + ]; + public static $textTransforms = [ + 'none' => 'Standard', + 'lowercase' => 'Minuscules', + 'uppercase' => 'Majuscules', + 'capitalize' => 'Majuscule à chaque mot' + ]; + public static $siteWidths = [ + '750px' => 'Petite (750 pixels)', + '960px' => 'Moyenne (960 pixels)', + '1170px' => 'Grande (1170 pixels)', + '100%' => 'Fluide (100%)' + ]; + public static $headerWide = [ + 'auto auto' => 'Automatique', + '100% 100%' => 'Image étirée (100% 100%)', + 'cover' => 'Responsive (cover)', + 'contain' => 'Responsive (contain)' + ]; + public static $footerTemplate = [ + '1' => 'Une seule colonne', + '2' => 'Deux colonnes : 1/2 - 1/2', + '3' => 'Trois colonnes : 1/3 - 1/3 - 1/3', + '4' => 'Trois lignes superposées' + ]; + public static $burgerContent = [ + 'none' => 'Aucun', + 'title' => 'Titre du site', + 'logo' => 'Logo du site' + ]; + + + // Variable pour construire la liste des pages du site + public static $pagesList = []; + // Variable pour construire la liste des fontes installées + public static $fontsNames= []; + public static $fonts = []; + // Variable pour détailler les fontes installées + public static $fontsDetail = []; + + /** + * Thème des écrans d'administration + */ + public function admin() { + // Soumission du formulaire + if($this->isPost()) { + $this->setData(['admin', [ + 'backgroundColor' => $this->getInput('adminBackgroundColor'), + 'colorTitle' => $this->getInput('adminColorTitle'), + 'colorText' => $this->getInput('adminColorText'), + 'colorButtonText' => $this->getInput('adminColorButtonText'), + 'backgroundColorButton' => $this->getInput('adminColorButton'), + 'backgroundColorButtonGrey' => $this->getInput('adminColorGrey'), + 'backgroundColorButtonRed' => $this->getInput('adminColorRed'), + 'backgroundColorButtonGreen'=> $this->getInput('adminColorGreen'), + 'backgroundColorButtonHelp'=> $this->getInput('adminColorHelp'), + 'fontText' => $this->getInput('adminFontText'), + 'fontSize' => $this->getInput('adminFontTextSize'), + 'fontTitle' => $this->getInput('adminFontTitle'), + 'backgroundBlockColor' => $this->getInput('adminBackGroundBlockColor'), + 'borderBlockColor' => $this->getInput('adminBorderBlockColor'), + ]]); + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme/admin', + 'state' => true + ]); + } + // Lire les fontes installées + $this->enumFonts(); + // Toutes les fontes installées sont chargées + $this->setFonts('all'); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Administration', + 'view' => 'admin', + 'vendor' => [ + 'tinycolorpicker' + ], + ]); + } + + /** + * Mode avancé + */ + public function advanced() { + // Soumission du formulaire + if($this->isPost()) { + // Enregistre le CSS + file_put_contents(self::DATA_DIR.'custom.css', $this->getInput('themeAdvancedCss', null)); + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme/advanced', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Éditeur CSS', + 'vendor' => [ + 'codemirror' + ], + 'view' => 'advanced' + ]); + } + + /** + * Options de l'arrière plan + */ + public function body() { + // Soumission du formulaire + if($this->isPost()) { + $this->setData(['theme', 'body', [ + 'backgroundColor' => $this->getInput('themeBodyBackgroundColor'), + 'image' => $this->getInput('themeBodyImage'), + 'imageAttachment' => $this->getInput('themeBodyImageAttachment'), + 'imagePosition' => $this->getInput('themeBodyImagePosition'), + 'imageRepeat' => $this->getInput('themeBodyImageRepeat'), + 'imageSize' => $this->getInput('themeBodyImageSize'), + 'toTopbackgroundColor' => $this->getInput('themeBodyToTopBackground'), + 'toTopColor' => $this->getInput('themeBodyToTopColor') + ]]); + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Personnalisation de l\'arrière plan', + 'vendor' => [ + 'tinycolorpicker' + ], + 'view' => 'body' + ]); + } + + /** + * Options du pied de page + */ + public function footer() { + // Soumission du formulaire + if($this->isPost()) { + if ( $this->getInput('themeFooterCopyrightPosition') === 'hide' && + $this->getInput('themeFooterSocialsPosition') === 'hide' && + $this->getInput('themeFooterTextPosition') === 'hide' ) { + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Sélectionnez au moins un contenu à afficher', + 'redirect' => helper::baseUrl() . 'theme/footer', + 'state' => false + ]); + } else { + $this->setData(['theme', 'footer', [ + 'backgroundColor' => $this->getInput('themeFooterBackgroundColor'), + 'copyrightAlign' => $this->getInput('themeFooterCopyrightAlign'), + 'height' => $this->getInput('themeFooterHeight'), + 'loginLink' => $this->getInput('themeFooterLoginLink'), + 'margin' => $this->getInput('themeFooterMargin', helper::FILTER_BOOLEAN), + 'position' => $this->getInput('themeFooterPosition'), + 'fixed' => $this->getInput('themeFooterFixed', helper::FILTER_BOOLEAN), + 'socialsAlign' => $this->getInput('themeFooterSocialsAlign'), + 'text' => $this->getInput('themeFooterText', null), + 'textAlign' => $this->getInput('themeFooterTextAlign'), + 'textColor' => $this->getInput('themeFooterTextColor'), + 'copyrightPosition' => $this->getInput('themeFooterCopyrightPosition'), + 'textPosition' => $this->getInput('themeFooterTextPosition'), + 'socialsPosition' => $this->getInput('themeFooterSocialsPosition'), + 'textTransform' => $this->getInput('themeFooterTextTransform'), + 'font' => $this->getInput('themeFooterFont'), + 'fontSize' => $this->getInput('themeFooterFontSize'), + 'fontWeight' => $this->getInput('themeFooterFontWeight'), + 'displayVersion' => $this->getInput('themefooterDisplayVersion', helper::FILTER_BOOLEAN), + 'displaySiteMap' => $this->getInput('themefooterDisplaySiteMap', helper::FILTER_BOOLEAN), + 'displayCopyright' => $this->getInput('themefooterDisplayCopyright', helper::FILTER_BOOLEAN), + 'displayCookie' => $this->getInput('themefooterDisplayCookie', helper::FILTER_BOOLEAN), + 'displayLegal' => $this->getInput('themeFooterDisplayLegal', helper::FILTER_BOOLEAN), + 'displaySearch' => $this->getInput('themeFooterDisplaySearch', helper::FILTER_BOOLEAN), + 'displayMemberBar'=> $this->getInput('themeFooterDisplayMemberBar', helper::FILTER_BOOLEAN), + 'template' => $this->getInput('themeFooterTemplate') + ]]); + + // Sauvegarder la configuration localisée + $this->setData(['locale','legalPageId', $this->getInput('configLegalPageId')]); + $this->setData(['locale','searchPageId', $this->getInput('configSearchPageId')]); + + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme', + 'state' => true + ]); + } + } + + // Liste des pages + self::$pagesList = $this->getData(['page']); + foreach(self::$pagesList as $page => $pageId) { + if ($this->getData(['page',$page,'block']) === 'bar' || + $this->getData(['page',$page,'disable']) === true) { + unset(self::$pagesList[$page]); + } + } + // Lire les fontes installées + $this->enumFonts(); + // Toutes les fontes installées sont chargées + $this->setFonts('all'); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Personnalisation du pied de page', + 'vendor' => [ + 'tinycolorpicker', + 'tinymce' + ], + 'view' => 'footer' + ]); + } + + /** + * Options de la bannière + */ + public function header() { + // Soumission du formulaire + if($this->isPost()) { + // Modification des URL des images dans la bannière perso + $featureContent = $this->getInput('themeHeaderText', null); + //$featureContent = str_replace(helper::baseUrl(false,false), './', $featureContent); + + /** + * Stocker les images incluses dans la bannière perso dans un tableau + */ + preg_match_all('/]+>/i',$featureContent, $results); + foreach($results[0] as $value) { + // Lire le contenu XML + $sx = simplexml_load_string($value); + // Élément à remplacer + $files [] = str_replace('./site/file/source/','',(string) $sx[0]['src']); + } + + // Sauvegarder + $this->setData(['theme', 'header', [ + 'backgroundColor' => $this->getInput('themeHeaderBackgroundColor'), + 'font' => $this->getInput('themeHeaderFont'), + 'fontSize' => $this->getInput('themeHeaderFontSize'), + 'fontWeight' => $this->getInput('themeHeaderFontWeight'), + 'height' => $this->getInput('themeHeaderHeight'), + 'wide' => $this->getInput('themeHeaderWide'), + 'image' => $this->getInput('themeHeaderImage'), + 'imagePosition' => $this->getInput('themeHeaderImagePosition'), + 'imageRepeat' => $this->getInput('themeHeaderImageRepeat'), + 'margin' => $this->getInput('themeHeaderMargin', helper::FILTER_BOOLEAN), + 'position' => $this->getInput('themeHeaderPosition'), + 'textAlign' => $this->getInput('themeHeaderTextAlign'), + 'textColor' => $this->getInput('themeHeaderTextColor'), + 'textHide' => $this->getInput('themeHeaderTextHide', helper::FILTER_BOOLEAN), + 'textTransform' => $this->getInput('themeHeaderTextTransform'), + 'linkHomePage' => $this->getInput('themeHeaderlinkHomePage',helper::FILTER_BOOLEAN), + 'imageContainer' => $this->getInput('themeHeaderImageContainer'), + 'tinyHidden' => $this->getInput('themeHeaderTinyHidden', helper::FILTER_BOOLEAN), + 'feature' => $this->getInput('themeHeaderFeature'), + 'featureContent' => $featureContent, + 'featureFiles' => $files + ]]); + // Modification de la position du menu selon la position de la bannière + if ( $this->getData(['theme','header','position']) == 'site' ) + { + $this->setData(['theme', 'menu', 'position',str_replace ('body-','site-',$this->getData(['theme','menu','position']))]); + } + if ( $this->getData(['theme','header','position']) == 'body') + { + $this->setData(['theme', 'menu', 'position',str_replace ('site-','body-',$this->getData(['theme','menu','position']))]); + } + // Menu accroché à la bannière qui devient cachée + if ( $this->getData(['theme','header','position']) == 'hide' && + in_array( $this->getData(['theme','menu','position']) , ['body-first', 'site-first', 'body-first' , 'site-second']) + ) { + $this->setData(['theme', 'menu', 'position','site']); + } + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme', + 'state' => true + ]); + } + // Lire les fontes installées + $this->enumFonts(); + // Toutes les fontes installées sont chargées + $this->setFonts('all'); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Personnalisation de la bannière', + 'vendor' => [ + 'tinycolorpicker', + 'tinymce' + ], + 'view' => 'header' + ]); + } + + /** + * Accueil de la personnalisation + */ + public function index() { + + // Restaurer les fontes utilisateurs + $this->setFonts('user'); + + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Personnalisation des thèmes', + 'view' => 'index' + ]); + } + + /** + * Options du menu + */ + public function menu() { + // Soumission du formulaire + if($this->isPost()) { + $this->setData(['theme', 'menu', [ + 'backgroundColor' => $this->getInput('themeMenuBackgroundColor'), + 'backgroundColorSub' => $this->getInput('themeMenuBackgroundColorSub'), + 'font' => $this->getInput('themeMenuFont'), + 'fontSize' => $this->getInput('themeMenuFontSize'), + 'fontWeight' => $this->getInput('themeMenuFontWeight'), + 'height' => $this->getInput('themeMenuHeight'), + 'wide' => $this->getInput('themeMenuWide'), + 'loginLink' => $this->getInput('themeMenuLoginLink', helper::FILTER_BOOLEAN), + 'margin' => $this->getInput('themeMenuMargin', helper::FILTER_BOOLEAN), + 'position' => $this->getInput('themeMenuPosition'), + 'textAlign' => $this->getInput('themeMenuTextAlign'), + 'textColor' => $this->getInput('themeMenuTextColor'), + 'textTransform' => $this->getInput('themeMenuTextTransform'), + 'fixed' => $this->getInput('themeMenuFixed', helper::FILTER_BOOLEAN), + 'activeColorAuto' => $this->getInput('themeMenuActiveColorAuto', helper::FILTER_BOOLEAN), + 'activeColor' => $this->getInput('themeMenuActiveColor'), + 'activeTextColor' => $this->getInput('themeMenuActiveTextColor'), + 'radius' => $this->getInput('themeMenuRadius'), + 'burgerTitle' => $this->getInput('themeMenuBurgerTitle', helper::FILTER_BOOLEAN), + 'memberBar' => $this->getInput('themeMenuMemberBar', helper::FILTER_BOOLEAN), + 'burgerLogo' => $this->getInput('themeMenuBurgerLogo'), + 'burgerContent' => $this->getInput('themeMenuBurgerContent') + ]]); + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme', + 'state' => true + ]); + } + // Lire les fontes installées + $this->enumFonts(); + // Toutes les fontes installées sont chargées + $this->setFonts('all'); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Personnalisation du menu', + 'vendor' => [ + 'tinycolorpicker' + ], + 'view' => 'menu' + ]); + } + + /** + * Options des fontes + */ + public function fonts() { + + // Toutes les fontes installées sont chargées + $this->setFonts('all'); + + // Polices liées au thème + $used = [ + 'Bannière' => $this->getData (['theme', 'header', 'font']), + 'Menu' => $this->getData (['theme', 'menu', 'font']), + 'Titre ' => $this->getData (['theme', 'title', 'font']), + 'Texte' => $this->getData (['theme', 'text', 'font']), + 'Pied de page' => $this->getData (['theme', 'footer', 'font']), + 'Titre (admin)' => $this->getData (['admin', 'fontTitle' ]), + 'Admin (texte)' => $this->getData (['admin', 'fontText' ]) + ]; + + // Récupérer le détail des fontes installées + //$f = $this->getFonts(); + $f ['files'] = $this->getData(['fonts', 'files']); + $f ['imported'] = $this->getData(['fonts', 'imported']); + $f ['websafe'] = self::$fontsWebSafe; + + // Parcourir les fontes disponibles et construire le tableau pour le formulaire + foreach ($f as $type => $typeValue) { + if (is_array($typeValue)) { + foreach ($typeValue as $fontId => $fontValue) { + // Fontes utilisées par les thèmes + $fontUsed[$fontId] = ''; + foreach ($used as $key => $value) { + if ( $value === $fontId) { + $fontUsed[$fontId] .= $key . '
        '; + } + } + self::$fontsDetail [] = [ + $fontId, + '' . $f[$type][$fontId]['name'] . '' , + $f[$type][$fontId]['font-family'], + $fontUsed[$fontId], + $type, + $type !== 'websafe' ? template::button('themeFontEdit' . $fontId, [ + 'class' => 'themeFontEdit', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/fontEdit/' . $type . '/' . $fontId . '/' . $_SESSION['csrf'], + 'value' => template::ico('pencil'), + 'disabled' => !empty($fontUsed[$fontId]) + ]) + : '', + $type !== 'websafe' ? template::button('themeFontDelete' . $fontId, [ + 'class' => 'themeFontDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/fontDelete/' . $type . '/' . $fontId . '/' . $_SESSION['csrf'], + 'value' => template::ico('cancel'), + 'disabled' => !empty($fontUsed[$fontId]) + ]) + : '' + ]; + } + } + } + sort(self::$fontsDetail); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Gestion des fontes', + 'view' => 'fonts' + ]); + } + + /** + * Ajouter une fonte + */ + public function fontAdd() { + // Soumission du formulaire + if ($this->isPost()) { + // Type d'import en ligne ou local + $type = $this->getInput('fontAddFontImported', helper::FILTER_BOOLEAN) ? 'imported' : 'files'; + $typeFlip = $type === 'files' ? 'imported' : 'files'; + $ressource = $type === 'imported' ? $this->getInput('fontAddUrl', null) : $this->getInput('fontAddFile', null); + $fontId = $this->getInput('fontAddFontId', null, true); + $fontName = $this->getInput('fontAddFontName', null, true); + $fontFamilyName = $this->getInput('fontAddFontFamilyName', null, true); + + // Remplace les doubles quotes par des simples quotes + $fontFamilyName = str_replace('"', '\'', $fontFamilyName); + + // Supprime la fonte si elle existe dans le type inverse + if (is_array($this->getData(['fonts', $typeFlip, $fontId])) ) { + $this->deleteData(['fonts', $typeFlip, $fontId ]); + } + // Stocker la fonte + $this->setData(['fonts', + $type, + $fontId, [ + 'name' => $fontName, + 'font-family' => $fontFamilyName, + 'resource' => $ressource + ]]); + + + // Copier la fonte si le nom du fichier est fourni + if ( $type === 'files' && + file_exists(self::FILE_DIR . 'source/' . $ressource) + ) { + copy ( self::FILE_DIR . 'source/' . $ressource, self::DATA_DIR . 'fonts/' . $ressource ); + } + + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'La fonte a été créée', + 'redirect' => helper::baseUrl() . 'theme/fonts', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Ajouter une fonte', + 'view' => 'fontAdd' + ]); + } + + + /** + * Ajouter une fonte + */ + public function fontEdit() { + // Soumission du formulaire + if ($this->isPost()) { + // Type d'import en ligne ou local + $type = $this->getInput('fontEditFontImported', helper::FILTER_BOOLEAN) ? 'imported' : 'files'; + $typeFlip = $type === 'files' ? 'imported' : 'files'; + $ressource = $type === 'imported' ? $this->getInput('fontEditUrl', null) : $this->getInput('fontEditFile', null); + $fontId = $this->getInput('fontEditFontId', null, true); + $fontName = $this->getInput('fontEditFontName', null , true); + $fontFamilyName = $this->getInput('fontEditFontFamilyName', null, true); + + // Remplace les doubles quotes par des simples quotes + $fontFamilyName = str_replace('"', '\'', $fontFamilyName); + + // Supprime la fonte si elle existe dans le type inverse + if (is_array($this->getData(['fonts', $typeFlip, $fontId])) ) { + $this->deleteData(['fonts', $typeFlip, $fontId ]); + } + // Stocker les fontes + $this->setData(['fonts', + $type, + $fontId, [ + 'name' => $fontName, + 'font-family' => $fontFamilyName, + 'resource' => $ressource + ]]); + // Copier la fonte si le nom du fichier est fourni + if ( $type === 'files' && + file_exists(self::FILE_DIR . 'source/' . $ressource) + ) { + copy ( self::FILE_DIR . 'source/' . $ressource, self::DATA_DIR . 'fonts/' . $ressource ); + } + + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'La fonte a été actualisée', + 'redirect' => helper::baseUrl() . 'theme/fonts', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Editer une fonte', + 'view' => 'fontEdit' + ]); + } + + /** + * Effacer une fonte + */ + public function fontDelete() { + // Jeton incorrect + if ($this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'theme/fonts', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + + // Effacer la fonte de la base + $this->deleteData(['fonts', $this->getUrl(2), $this->getUrl(3)]); + + // Effacer le fichier existant + if ( $this->getUrl(2) === 'file' && + file_exists(self::DATA_DIR . $this->getUrl(2)) ) { + unlink(self::DATA_DIR . $this->getUrl(2)); + } + + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'theme/fonts', + 'notification' => 'La fonte a été supprimée', + 'state' => true + ]); + } + } + + + /** + * Réinitialisation de la personnalisation avancée + */ + public function reset() { + // $url prend l'adresse sans le token + $url = explode('&',$this->getUrl(2)); + + if ( isset($_GET['csrf']) + AND $_GET['csrf'] === $_SESSION['csrf'] + ) { + // Réinitialisation + $redirect =''; + switch ($url[0]) { + case 'admin': + $this->initData('admin'); + $redirect = helper::baseUrl() . 'theme/admin'; + break; + case 'manage': + $this->initData('theme'); + $redirect = helper::baseUrl() . 'theme/manage'; + break; + case 'custom': + unlink(self::DATA_DIR.'custom.css'); + $redirect = helper::baseUrl() . 'theme/advanced'; + break; + default : + $redirect = helper::baseUrl() . 'theme'; + } + + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Réinitialisation effectuée', + 'redirect' => $redirect, + 'state' => true + ]); + } else { + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Jeton incorrect' + ]); + } + } + + + /** + * Options du site + */ + public function site() { + // Soumission du formulaire + if($this->isPost()) { + $this->setData(['theme', 'title', [ + 'font' => $this->getInput('themeTitleFont'), + 'textColor' => $this->getInput('themeTitleTextColor'), + 'fontWeight' => $this->getInput('themeTitleFontWeight'), + 'textTransform' => $this->getInput('themeTitleTextTransform') + ]]); + $this->setData(['theme', 'text', [ + 'font' => $this->getInput('themeTextFont'), + 'fontSize' => $this->getInput('themeTextFontSize'), + 'textColor' => $this->getInput('themeTextTextColor'), + 'linkColor'=> $this->getInput('themeTextLinkColor') + ]]); + $this->setData(['theme', 'site', [ + 'backgroundColor' => $this->getInput('themeSiteBackgroundColor'), + 'radius' => $this->getInput('themeSiteRadius'), + 'shadow' => $this->getInput('themeSiteShadow'), + 'width' => $this->getInput('themeSiteWidth'), + 'margin' => $this->getInput('themeSiteMargin',helper::FILTER_BOOLEAN) + ]]); + $this->setData(['theme', 'button', [ + 'backgroundColor' => $this->getInput('themeButtonBackgroundColor') + ]]); + $this->setData(['theme', 'block', [ + 'backgroundColor' => $this->getInput('themeBlockBackgroundColor'), + 'borderColor' => $this->getInput('themeBlockBorderColor') + ]]); + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . 'theme', + 'state' => true + ]); + } + // Lire les fontes installées + $this->enumFonts(); + // Toutes les fontes installées sont chargées + $this->setFonts('all'); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Personnalisation du site', + 'vendor' => [ + 'tinycolorpicker', + 'tinymce' + ], + 'view' => 'site' + ]); + } + + /** + * Import du thème + */ + public function manage() { + if($this->isPost() ) { + + $zipFilename = $this->getInput('themeManageImport', helper::FILTER_STRING_SHORT, true); + $data = $this->import(self::FILE_DIR.'source/' . $zipFilename); + if ($data['success']) { + header("Refresh:0"); + } else { + // Valeurs en sortie + $this->addOutput([ + 'notification' => $data['notification'], + 'state' => $data['success'], + 'title' => 'Gestion des thèmes', + 'view' => 'manage' + ]);; + } + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Gestion des thèmes', + 'view' => 'manage' + ]); + } + + /** + * Importe un thème + * @param string Url du thème à télécharger + * @param @return array contenant $success = true ou false ; $ notification string message à afficher + */ + + public function import($zipName = '') { + + if ($zipName !== '' && + file_exists($zipName)) { + // Init variables de retour + $success = false; + $notification = ''; + // Dossier temporaire + $tempFolder = uniqid(); + // Ouvrir le zip + $zip = new ZipArchive(); + if ($zip->open($zipName) === TRUE) { + mkdir (self::TEMP_DIR . $tempFolder, 0755); + $zip->extractTo(self::TEMP_DIR . $tempFolder ); + $modele = ''; + // Archive de thème ? + if ( + file_exists(self::TEMP_DIR . $tempFolder . '/site/data/custom.css') + AND file_exists(self::TEMP_DIR . $tempFolder . '/site/data/theme.css') + AND file_exists(self::TEMP_DIR . $tempFolder . '/site/data/theme.json') + ) { + $modele = 'theme'; + } + if( + file_exists(self::TEMP_DIR . $tempFolder . '/site/data/admin.json') + AND file_exists(self::TEMP_DIR . $tempFolder . '/site/data/admin.css') + ) { + $modele = 'admin'; + + } + if (!empty($modele) + ) { + // traiter l'archive + $success = $zip->extractTo('.'); + + // Substitution des fontes Google + if ($modele = 'theme') { + $c = $this->subFonts(self::DATA_DIR . 'theme.json'); + // Un remplacement nécessite la régénération de la feuille de style + if ($c > 0 + AND file_exists(self::DATA_DIR . 'theme.css') + ) { + unlink(self::DATA_DIR . 'theme.css'); + } + } + if ($modele = 'admin') { + $c = $this->subFonts(self::DATA_DIR . 'admin.json'); + // Un remplacement nécessite la régénération de la feuille de style + if ($c > 0 + AND file_exists(self::DATA_DIR . 'admin.css') + ) { + unlink(self::DATA_DIR . 'admin.css'); + } + } + + // traitement d'erreur + $notification = $success ? 'Le thème a été importé' : 'Erreur lors de l\'extraction, vérifiez les permissions.'; + + + } else { + // pas une archive de thème + $success = false; + $notification = 'Ce n\'est pas l\'archive d\'un thème !'; + } + // Supprimer le dossier temporaire même si le thème est invalide + $this->removeDir(self::TEMP_DIR . $tempFolder); + $zip->close(); + } else { + // erreur à l'ouverture + $success = false; + $notification = 'Impossible d\'ouvrir l\'archive'; + } + return (['success' => $success, 'notification' => $notification]); + } + + return (['success' => false, 'notification' => 'Archive non spécifiée ou introuvable']); + } + + + + /** + * Export du thème + */ + public function export() { + // Make zip + $zipFilename = $this->zipTheme($this->getUrl(2)); + // Téléchargement du ZIP + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Transfer-Encoding: binary'); + header('Content-Disposition: attachment; filename="' . $zipFilename . '"'); + header('Content-Length: ' . filesize(self::TEMP_DIR . $zipFilename)); + readfile(self::TEMP_DIR . $zipFilename); + // Nettoyage du dossier + unlink (self::TEMP_DIR . $zipFilename); + exit(); + } + + /** + * Export du thème + */ + public function save() { + // Make zip + $zipFilename = $this->zipTheme($this->getUrl(2)); + // Téléchargement du ZIP + if (!is_dir(self::FILE_DIR.'source/theme')) { + mkdir(self::FILE_DIR.'source/theme', 0755); + } + copy (self::TEMP_DIR . $zipFilename , self::FILE_DIR.'source/theme/' . $zipFilename); + // Nettoyage du dossier + unlink (self::TEMP_DIR . $zipFilename); + // Valeurs en sortie + $this->addOutput([ + 'notification' => 'Archive '.$zipFilename.' sauvegardée avec succès', + 'redirect' => helper::baseUrl() . 'theme/manage', + 'state' => true + ]); + } + + /** + * construction du zip Fonction appelée par export() et save() + * @param string $modele theme ou admin + */ + private function zipTheme($modele) { + // Creation du dossier + $zipFilename = $modele . date('Y-m-d-H-i-s', time()) . '.zip'; + $zip = new ZipArchive(); + if ($zip->open(self::TEMP_DIR . $zipFilename, ZipArchive::CREATE | ZipArchive::OVERWRITE ) === TRUE) { + switch ($modele) { + case 'admin': + $zip->addFile(self::DATA_DIR.'admin.json',self::DATA_DIR.'admin.json'); + $zip->addFile(self::DATA_DIR.'admin.css',self::DATA_DIR.'admin.css'); + // Ajoute les fontes + $zip->addEmptyDir(self::DATA_DIR .'fonts'); + $fonts = $this->getData(['fonts', 'files']); + foreach ($fonts as $fontId => $fontName) { + $zip->addFile(self::DATA_DIR .'fonts/' . $fontName, self::DATA_DIR.'fonts/' . $fontName); + } + if (file_exists(self::DATA_DIR .'fonts/fonts.html')) { + + $zip->addFile(self::DATA_DIR .'fonts/fonts.html', self::DATA_DIR .'fonts/fonts.html'); + } + break; + case 'theme': + $zip->addFile(self::DATA_DIR.'theme.json',self::DATA_DIR.'theme.json'); + $zip->addFile(self::DATA_DIR.'theme.css',self::DATA_DIR.'theme.css'); + $zip->addFile(self::DATA_DIR.'custom.css',self::DATA_DIR.'custom.css'); + // Traite l'image dans le body + if ($this->getData(['theme','body','image']) !== '' ) { + $zip->addFile(self::FILE_DIR.'source/'.$this->getData(['theme','body','image']), + self::FILE_DIR.'source/'.$this->getData(['theme','body','image']) + ); + } + // Traite l'image dans le header + if ($this->getData(['theme','header','image']) !== '' ) { + $zip->addFile(self::FILE_DIR.'source/'.$this->getData(['theme','header','image']), + self::FILE_DIR.'source/'.$this->getData(['theme','header','image']) + ); + } + // Traite les images du header perso + if (!empty($this->getData(['theme','header','featureFiles'])) ) { + foreach($this->getData(['theme','header','featureFiles']) as $value) { + $zip->addFile(self::FILE_DIR . 'source/' . $value, + self::FILE_DIR . 'source/' . $value ); + } + } + // Ajoute les fontes + $zip->addEmptyDir(self::DATA_DIR .'fonts'); + $fonts = $this->getData(['fonts', 'files']); + foreach ($fonts as $fontId => $fontName) { + $zip->addFile(self::DATA_DIR .'fonts/' . $fontName, self::DATA_DIR.'fonts/' . $fontName); + } + if (file_exists(self::DATA_DIR .'fonts/fonts.html')) { + + $zip->addFile(self::DATA_DIR .'fonts/fonts.html', self::DATA_DIR .'fonts/fonts.html'); + } + break; + } + $ret = $zip->close(); + } + return ($zipFilename); + } + + /** + * Substitution des fontes de Google Fonts vers CdnFont grâce à un tableau de conversion + * Cette fonction est utilisée par l'import. + * @param string $file, nom du fichier json à convertir + * @return int nombre de substitution effectuées + */ + private function subFonts($file) { + // Tableau de substitution des fontes + $fonts = [ + 'Abril+Fatface' => 'abril-fatface', + 'Arimo' => 'arimo', + 'Arvo' => 'arvo', + 'Berkshire+Swash' => 'berkshire-swash', + 'Cabin' => 'genera', + 'Dancing+Script' => 'dancing-script', + 'Droid+Sans' => 'droid-sans-2', + 'Droid+Serif' => 'droid-serif-2', + 'Fira+Sans' => 'fira-sans', + 'Inconsolata' => 'inconsolata-2', + 'Indie+Flower' =>'indie-flower', + 'Josefin+Slab' => 'josefin-sans-std', + 'Lobster' => 'lobster-2', + 'Lora' => 'lora', + 'Lato' =>'lato', + 'Marvel' => 'montserrat-ace', + 'Old+Standard+TT' => 'old-standard-tt-3', + 'Open+Sans' =>'open-sans', + // Corriger l'erreur de nom de police installée par défaut, il manquait un O en majuscule + 'open+Sans' =>'open-sans', + 'Oswald' =>'oswald-4', + 'PT+Mono' => 'pt-mono', + 'PT+Serif' =>'pt-serif', + 'Raleway' => 'raleway-5', + 'Rancho' => 'rancho', + 'Roboto' => 'Roboto', + 'Signika' => 'signika', + 'Ubuntu' => 'ubuntu', + 'Vollkorn' => 'vollkorn' + ]; + + $data = file_get_contents($file); + $count = 0; + foreach ($fonts as $oldId => $newId){ + $data = str_replace($oldId, $newId, $data, $c); + $count = $count + (int) $c; + } + // Sauvegarder la chaîne modifiée + if ($count > 0) { + file_put_contents($file, $data); + } + // Retourner le nombre d'occurrences + return ($count); + } + + + // Retourne un tableau simple des fonts installées idfont avec le nom + // Cette fonction est utile aux sélecteurs de fonts dans les formulaires. + public function enumFonts() { + /** + * Récupère la liste des fontes installées et construit deux tableaux + * id - nom + * id - font-family - resource + */ + $f ['files'] = $this->getData(['fonts', 'files']); + $f ['imported'] = $this->getData(['fonts', 'imported']); + $f ['websafe'] = self::$fontsWebSafe; + // Construit un tableau avec leur ID et leur famille + foreach(['websafe', 'imported', 'files'] as $type) { + if (is_array($f[$type])) { + foreach ($f[$type] as $fontId => $fontValue ) { + self::$fonts['name'][$fontId] = $fontValue['name']; + self::$fonts['family'][$fontId] = $fontValue['font-family']; + } + } + } + // Liste des fontes pour les sélecteurs + ksort(self::$fonts['name']); + ksort(self::$fonts['family']); + } + + /** + * Création d'un fichier de liens d'appel des fontes + * @param string $scope vaut all pour toutes les fontes ; 'user' pour les fontes utilisateurs + */ + private function setFonts($scope = 'all') { + + // Filtrage par fontes installées + $fontsInstalled = [ $this->getData(['theme', 'text', 'font']), + $this->getData(['theme', 'title', 'font']), + $this->getData(['theme', 'header', 'font']), + $this->getData(['theme', 'menu', 'font']), + $this->getData(['theme', 'footer', 'font']), + $this->getData(['admin', 'fontText']), + $this->getData(['admin', 'fontTitle']), + ]; + + // Compression + $fontsInstalled = array_unique($fontsInstalled); + + /** + * Chargement des polices en ligne dans un fichier fonts.html inclus dans main.php + */ + $gf = false; + $fileContent = ''; + foreach ($this->getData(['fonts', 'imported']) as $fontId => $fontValue) { + if ( + ( $scope === 'user' && in_array($fontId, $fontsInstalled) ) + || $scope === 'all' + ) { + //Pré chargement à revoir + //$fileContent .= ''; + $fileContent .= ''; + // Pré connect pour api.google + $gf = strpos($fontValue['resource'], 'fonts.googleapis.com') === false ? $gf || false : $gf || true; + } + } + + // Ajoute le préconnect des fontes Googles. + $fileContent = $gf ? '' . $fileContent + : $fileContent; + + + + /** + * Fontes installées localement + */ + $fileContentCss = ''; + foreach ($this->getData(['fonts', 'files']) as $fontId => $fontValue) { + if ( + ( $scope === 'user' && in_array($fontId, $fontsInstalled) ) + || $scope === 'all' + ) { + if (file_exists(self::DATA_DIR . 'fonts/' . $fontValue['resource']) ) { + // Extension + $path_parts = pathinfo(helper::baseUrl(false) . self::DATA_DIR . 'fonts/' . $fontValue['resource']); + // Chargement de la police + $fileContentCss .= '@font-face {' ; + $fileContentCss .= 'font-family:"' . $fontId . '";'; + $fileContentCss .= 'src: url("' . $fontValue['resource'] . '") format("' . $path_parts['extension'] . '");'; + $fileContentCss .= '}' ; + // Préchargement + //$fileContent = '' . $fileContent; + } + } + } + + // Enregistre la personnalisation + file_put_contents(self::DATA_DIR.'fonts/fonts.html', $fileContent); + // Enregistre la personnalisation + file_put_contents(self::DATA_DIR.'fonts/fonts.css', $fileContentCss); + + } + +} diff --git a/core/module/theme/view/admin/admin.php b/core/module/theme/view/admin/admin.php index ef3e4f9a..38b37dd4 100644 --- a/core/module/theme/view/admin/admin.php +++ b/core/module/theme/view/admin/admin.php @@ -128,10 +128,10 @@

        Mise en forme du texte

        - 'Police du texte', 'selected' => $this->getData(['admin', 'fontText']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        @@ -141,10 +141,10 @@ ]); ?>
        - 'Police des titres', 'selected' => $this->getData(['admin', 'fontTitle']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        diff --git a/core/module/theme/view/fontAdd/fontAdd.js.php b/core/module/theme/view/fontAdd/fontAdd.js.php index dd53c7cf..e9105513 100644 --- a/core/module/theme/view/fontAdd/fontAdd.js.php +++ b/core/module/theme/view/fontAdd/fontAdd.js.php @@ -15,7 +15,8 @@ */ $(document).ready(function(){ $('input[name=fontAddFontImported]').prop('checked', true); - $('#fontAddFileWrapper').hide(); + $('input[name=fontAddFontUrl]').prop('checked', false); + $('#containerFontAddFile').hide(); }); @@ -28,7 +29,8 @@ $("input[name=fontAddFontImported]").on("click", function() { } else { $('input[name=fontAddFontFile]').prop('checked', true); } - $('#fontAddFileWrapper').hide(); + $('#containerFontAddFile').hide(); + $('#containerFontAddUrl').show(); }); $("input[name=fontAddFontFile]").on("click", function() { @@ -37,5 +39,6 @@ $("input[name=fontAddFontFile]").on("click", function() { } else { $('input[name=fontAddFontImported]').prop('checked', true); } - $('#fontAddFileWrapper').show(); + $('#containerFontAddFile').show(); + $('#containerFontAddUrl').hide(); }); diff --git a/core/module/theme/view/fontAdd/fontAdd.php b/core/module/theme/view/fontAdd/fontAdd.php index 9085f503..e9bfe8a5 100644 --- a/core/module/theme/view/fontAdd/fontAdd.php +++ b/core/module/theme/view/fontAdd/fontAdd.php @@ -1,23 +1,21 @@
        -
        +
        'buttonGrey', 'href' => helper::baseUrl() . 'theme/fonts', - 'ico' => 'left', - 'value' => 'Retour' + 'value' => template::ico('left') ]); ?>
        -
        - + 'https://doc.zwiicms.fr/fontes#add', 'target' => '_blank', - 'ico' => 'help', - 'value' => 'Aide', + 'value' => template::ico('help'), 'class' => 'buttonHelp' ]); ?>
        -
        +
        'Valider', 'uniqueSubmission' => true @@ -30,14 +28,10 @@

        Identité de la fonte

        - cdnFonts', [ - 'help' => 'Police utilisée en ligne, se connecter sur cdnFonts pour récupérer les informations nécessaires.' - ]); ?> +
        - 'Sélectionnez un fichier de fonte au format WOFF.' - ]); ?> +
        @@ -45,7 +39,7 @@ 'off', 'label' => 'Identifiant (sans espace ni majuscule)', - 'placeholder' => 'perry-gothic' + 'placeholder' => 'big-marker-extrude' ]); ?>
        @@ -58,10 +52,26 @@
        +
        + 'off', + 'label' => 'Famille', + 'placeholder' => "'Big Marker Extrude', sans-serif" + ]); ?> +
        +
        +
        'Fichier de police (Format WOFF)', - 'placeholder' => 'https://fonts.cdnfonts.com/s/7896/PERRYGOT.woff' + 'label' => 'Fichier de fonte (Format WOFF)' + ]); ?> +
        +
        +
        +
        + 'Url du fichier de fonte', + 'placeholder' => 'https://fonts.cdnfonts.com/css/big-marker-extrude' ]); ?>
        diff --git a/core/module/theme/view/fontEdit/fontEdit.css b/core/module/theme/view/fontEdit/fontEdit.css new file mode 100644 index 00000000..52709ea6 --- /dev/null +++ b/core/module/theme/view/fontEdit/fontEdit.css @@ -0,0 +1,18 @@ +/** + * This file is part of Zwii. + * + * For full copyright and license information, please see the LICENSE + * file that was distributed with this source code. + * + * @author Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/core/module/theme/view/fontEdit/fontEdit.js.php b/core/module/theme/view/fontEdit/fontEdit.js.php new file mode 100644 index 00000000..58bc405c --- /dev/null +++ b/core/module/theme/view/fontEdit/fontEdit.js.php @@ -0,0 +1,59 @@ +/** + * This file is part of Zwii. + * For full copyright and license information, please see the LICENSE + * file that was distributed with this source code. + * + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** + * Option par défaut du sélecteur de mode + */ + $(document).ready(function(){ + if( $('input[name=fontEditFontImported]').is(':checked') ){ + $('#containerfontEditFile').hide(); + $('#containerfontEditUrl').show(); + $('#fontEditFontFileWrapper').hide(); + $('input[name=fontEditFontImported]').attr('disabled', 'disabled'); + + } + + + if( $('input[name=fontEditFontFile]').is(':checked') ){ + $('#containerfontEditFile').show(); + $('#containerfontEditUrl').hide(); + $('#fontEditFontImportedWrapper').hide(); + $('input[name=fontEditFontFile]').attr('disabled', 'disabled'); + } + +}); + + +/** + * Mode téléchargement en ligne de la fonte ou installation locale + + $("input, select").on("change", function() { + + if( $('input[name=fontEditFontImported]').is(':checked') ){ + $('input[name=fontEditFontFile]').prop('checked', false); + $('#containerfontEditFile').hide(); + $('#containerfontEditUrl').show(); + } else { + + } + + + if( $('input[name=fontEditFontFile]').is(':checked') ){ + $('input[name=fontEditFontImported]').prop('checked', false); + $('#containerfontEditFile').show(); + $('#containerfontEditUrl').hide(); + } else { + $('input[name=fontEditFontImported]').prop('checked', true); + } + +}); + */ \ No newline at end of file diff --git a/core/module/theme/view/fontEdit/fontEdit.php b/core/module/theme/view/fontEdit/fontEdit.php new file mode 100644 index 00000000..30cb46c8 --- /dev/null +++ b/core/module/theme/view/fontEdit/fontEdit.php @@ -0,0 +1,85 @@ + +
        +
        + 'buttonGrey', + 'href' => helper::baseUrl() . 'theme/fonts', + 'value' => template::ico('left') + ]); ?> +
        +
        + 'https://doc.zwiicms.fr/fontes#add', + 'target' => '_blank', + 'value' => template::ico('help'), + 'class' => 'buttonHelp' + ]); ?> +
        +
        + 'Valider', + 'uniqueSubmission' => true + ]); ?> +
        +
        +
        +
        +
        +

        Identité de la fonte

        +
        +
        + $this->getUrl(2) === 'imported' ? true : false + ]); ?> +
        +
        + $this->getUrl(2) === 'files' ? true : false + ]); ?> +
        +
        +
        +
        + 'off', + 'label' => 'Identifiant (sans espace ni majuscule)', + 'value' => $this->getUrl(3) + ]); ?> +
        +
        + 'off', + 'label' => 'Nom', + 'value' => $this->getData(['fonts', $this->getUrl(2), $this->getUrl(3), 'name']) + ]); ?> +
        +
        +
        +
        + 'off', + 'label' => 'Famille', + 'value' => stripslashes($this->getData(['fonts', $this->getUrl(2), $this->getUrl(3), 'font-family'])) + ]); ?> +
        +
        +
        +
        + 'Fichier de fonte (Format WOFF)', + 'value' => $this->getUrl(2) === 'files' ? $this->getData(['fonts', $this->getUrl(2), $this->getUrl(3), 'resource']) : '' + ]); ?> +
        +
        +
        +
        + 'Url du fichier de fonte', + 'value' => $this->getUrl(2) === 'imported' ? $this->getData(['fonts', $this->getUrl(2), $this->getUrl(3), 'resource']) : '' + ]); ?> +
        +
        +
        +
        +
        + \ No newline at end of file diff --git a/core/module/theme/view/fonts/fonts.php b/core/module/theme/view/fonts/fonts.php index 2458ba1c..27ebd056 100644 --- a/core/module/theme/view/fonts/fonts.php +++ b/core/module/theme/view/fonts/fonts.php @@ -1,22 +1,20 @@
        -
        +
        'buttonGrey', 'href' => helper::baseUrl() . 'theme', - 'ico' => 'left', - 'value' => 'Retour' + 'value' => template::ico('left') ]); ?>
        -
        +
        'https://doc.zwiicms.fr/fontes', 'target' => '_blank', - 'ico' => 'help', - 'value' => 'Aide', + 'value' => template::ico('help'), 'class' => 'buttonHelp' ]); ?>
        -
        +
        helper::baseUrl() . $this->getUrl(0) . '/fontAdd', 'ico' => 'plus', @@ -24,8 +22,8 @@ ]); ?>
        - - + + \ No newline at end of file diff --git a/core/module/theme/view/footer/footer.php b/core/module/theme/view/footer/footer.php index 9c47aa82..fad814cc 100644 --- a/core/module/theme/view/footer/footer.php +++ b/core/module/theme/view/footer/footer.php @@ -133,7 +133,7 @@ ]); ?>
        - 'Aucune'] , helper::arrayCollumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ + 'Aucune'] , helper::arrayColumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ 'label' => 'Page "Mentions légales" ' . template::flag('site', '20px'), 'selected' => $this->getData(['locale', 'legalPageId']) ]); ?> @@ -147,7 +147,7 @@ ]); ?>
        - 'Aucune'] , helper::arrayCollumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ + 'Aucune'] , helper::arrayColumn($module::$pagesList, 'title', 'SORT_ASC') ) , [ 'label' => 'Page "Rechercher" ' . template::flag('site', '20px'), 'selected' => $this->getData(['locale', 'searchPageId']) ]); ?> @@ -171,10 +171,10 @@

        Mise en forme du texte

        - 'Police', + 'Fonte', 'selected' => $this->getData(['theme', 'footer', 'font']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        diff --git a/core/module/theme/view/header/header.js.php b/core/module/theme/view/header/header.js.php index 7cc85e53..85f736b6 100644 --- a/core/module/theme/view/header/header.js.php +++ b/core/module/theme/view/header/header.js.php @@ -47,9 +47,9 @@ $("input, select").on("change", function() { var tmpImg = new Image(); tmpImg.onload = function() { // Informations affichées - $("#themeHeaderImageHeight").val(tmpImg.height + " px"); - $("#themeHeaderImageWidth").val(tmpImg.width + " px"); - $("#themeHeaderImageRatio").val(tmpImg.width / tmpImg.height); + $("#themeHeaderImageHeight").html(tmpImg.height + "px"); + $("#themeHeaderImageWidth").html(tmpImg.width + "px"); + $("#themeHeaderImageRatio").html(tmpImg.width / tmpImg.height); // Limiter la hauteur à 600 px if (tmpImg.height > 600) { @@ -69,6 +69,9 @@ $("input, select").on("change", function() { $("#themeHeaderHeight option:eq(0)").val(tmpImgHeight + "px"); // Modifier l'option $("#themeHeaderHeight option:eq(0)").html("Hauteur de l\'image sélectionnée (" + tmpImgHeight + "px)"); + $("#themeHeaderImageInfo").show(); + } else { + $("#themeHeaderImageInfo").hide(); } }; @@ -207,10 +210,10 @@ $("input, select").on("change", function() { // Affiche / Cache les options de l'image du fond $("#themeHeaderImage").on("change", function() { if($(this).val()) { - $("#themeHeaderImageOptions").slideDown(); + $(".themeHeaderImageOptions").slideDown(); } else { - $("#themeHeaderImageOptions").slideUp(function() { + $(".themeHeaderImageOptions").slideUp(function() { $("#themeHeaderTextHide").prop("checked", false).trigger("change"); }); } diff --git a/core/module/theme/view/header/header.php b/core/module/theme/view/header/header.php index a9956412..04de81ed 100644 --- a/core/module/theme/view/header/header.php +++ b/core/module/theme/view/header/header.php @@ -106,10 +106,10 @@ ]); ?>
        - 'Police', + 'Fonte', 'selected' => $this->getData(['theme', 'header', 'font']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        @@ -154,40 +154,20 @@ $imageFile = file_exists(self::FILE_DIR.'source/'.$this->getData(['theme', 'header', 'image'])) ? $this->getData(['theme', 'header', 'image']) : ""; echo template::file('themeHeaderImage', [ - 'help' => 'Sélectionner une image aux dimensions recommandées ci-dessous :', 'label' => 'Image', 'type' => 1, 'value' => $imageFile ]); ?> + + Largeur de l'image : (largeur de site : getData(['theme', 'site', 'width']); ?>) + - + Hauteur de l'image : + - + Ratio : +
        -
        -
        - Informations sur l'image : -
        -
        - 'Largeur', - 'class' => 'textAlignCenter', - 'disable' => true - ]); ?> -
        -
        - 'Hauteur', - 'class' => 'textAlignCenter', - 'disable' => true - ]); ?> -
        -
        - 'Ratio (L/H)', - 'class' => 'textAlignCenter', - 'disable' => true - ]); ?> -
        -
        -
        +
        'https://doc.zwiicms.fr/theme-2', + 'href' => 'https://doc.zwiicms.fr/gestion-du-theme', 'target' => '_blank', 'ico' => 'help', 'value' => 'Aide', @@ -73,7 +73,7 @@
        'https://doc.zwiicms.fr/theme-2', + 'href' => 'https://doc.zwiicms.fr/gestion-du-theme', 'target' => '_blank', 'ico' => 'help', 'value' => 'Aide', diff --git a/core/module/theme/view/menu/menu.php b/core/module/theme/view/menu/menu.php index 9bcbb88a..ba81757f 100644 --- a/core/module/theme/view/menu/menu.php +++ b/core/module/theme/view/menu/menu.php @@ -183,10 +183,10 @@

        Mise en forme du texte

        - 'Police', + 'Fonte', 'selected' => $this->getData(['theme', 'menu', 'font']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        diff --git a/core/module/theme/view/site/site.js.php b/core/module/theme/view/site/site.js.php index 3ff25012..02a9ab7f 100644 --- a/core/module/theme/view/site/site.js.php +++ b/core/module/theme/view/site/site.js.php @@ -11,8 +11,6 @@ * @link http://zwiicms.fr/ */ - - /** * Aperçu en direct */ @@ -28,21 +26,19 @@ $("input, select").on("change",function() { $("#themeSiteMarginWrapper").show(); } + /** * Aperçu dans la boîte */ - // Import des polices de caractères + // Import des polices de caractères pour l'aperçu en temps réel var titleFont = $("#themeTitleFont :selected").val(); var titleFontText = $("#themeTitleFont :selected").text(); var textFont = $("#themeTextFont :selected").val(); var textFontText = $("#themeTextFont :selected").text(); - console.log(textFontText); - var css = "@import url('https://fonts.cdnfonts.com/css/" + titleFont + "');"; - var css = "@import url('https://fonts.cdnfonts.com/css/" + textFont + "');"; // Couleurs des boutons var colors = core.colorVariants($("#themeButtonBackgroundColor").val()); - css += ".button.buttonSubmitPreview{background-color:" + colors.normal + ";}"; + var css = ".button.buttonSubmitPreview{background-color:" + colors.normal + ";}"; css += ".button.buttonSubmitPreview:hover{background-color:" + colors.darken + "}"; css += ".button.buttonSubmitPreview{color:" + colors.text + ";}"; @@ -114,5 +110,4 @@ $("input, select").on("change",function() { .attr("id", "themePreview") .text(css) .appendTo("head"); - }).trigger("change"); diff --git a/core/module/theme/view/site/site.php b/core/module/theme/view/site/site.php index 7f4c4e67..a6e42200 100644 --- a/core/module/theme/view/site/site.php +++ b/core/module/theme/view/site/site.php @@ -141,7 +141,8 @@
        -

        Bloc

        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

        +

        Bloc

        +

        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

        Lorem ipsum dolor sit amet.

        @@ -158,10 +159,11 @@

        Mise en forme du texte

        - 'Police', + 'Fonte', 'selected' => $this->getData(['theme', 'text', 'font']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        @@ -179,10 +181,10 @@

        Mise en forme des titres

        - 'Police', + 'Fonte', 'selected' => $this->getData(['theme', 'title', 'font']), - 'fonts' => true + 'fonts' => $module::$fonts['family'] ]); ?>
        diff --git a/core/module/user/user.php b/core/module/user/user.php index 82a22882..c4e4879f 100644 --- a/core/module/user/user.php +++ b/core/module/user/user.php @@ -336,7 +336,7 @@ class user extends common { * Liste des utilisateurs */ public function index() { - $userIdsFirstnames = helper::arrayCollumn($this->getData(['user']), 'firstname'); + $userIdsFirstnames = helper::arrayColumn($this->getData(['user']), 'firstname'); ksort($userIdsFirstnames); foreach($userIdsFirstnames as $userId => $userFirstname) { if ($this->getData(['user', $userId, 'group'])) { @@ -397,7 +397,7 @@ class user extends common { ] ]); // Verrouillage des IP - $ipBlackList = helper::arrayCollumn($this->getData(['blacklist']), 'ip'); + $ipBlackList = helper::arrayColumn($this->getData(['blacklist']), 'ip'); if ( $this->getData(['blacklist',$userId,'connectFail']) >= $this->getData(['config', 'connect', 'attempt']) AND in_array($this->getData(['blacklist',$userId,'ip']),$ipBlackList) ) { $logStatus = 'Compte inconnu verrouillé'; diff --git a/core/vendor/filemanager/config/config.php b/core/vendor/filemanager/config/config.php index 786a9b0b..8608494e 100644 --- a/core/vendor/filemanager/config/config.php +++ b/core/vendor/filemanager/config/config.php @@ -3,8 +3,8 @@ $version = "9.14.0"; if (session_id() == '') session_start(); mb_internal_encoding('UTF-8'); -mb_http_output('UTF-8'); -mb_http_input('UTF-8'); +mb_http_output(); +mb_http_input(); mb_language('uni'); if (function_exists('mb_regex_encoding')) { mb_regex_encoding('UTF-8'); diff --git a/core/vendor/tinymce/init.js b/core/vendor/tinymce/init.js index 6daa4b89..b4d3bad3 100755 --- a/core/vendor/tinymce/init.js +++ b/core/vendor/tinymce/init.js @@ -114,7 +114,7 @@ tinymce.init({ // Active l'onglet avancé lors de l'ajout d'une image image_advtab: true, // Urls absolues - relative_urls: false, + relative_urls: true, // Url de base document_base_url: baseUrl, // Gestionnaire de fichiers @@ -294,7 +294,7 @@ tinymce.init({ // Active l'onglet avancé lors de l'ajout d'une image image_advtab: true, // Urls absolues - relative_urls: false, + relative_urls: true, // Url de base document_base_url: baseUrl, // Contenu du bouton formats diff --git a/module/blog/blog.php b/module/blog/blog.php index 18964165..69887203 100644 --- a/module/blog/blog.php +++ b/module/blog/blog.php @@ -140,8 +140,8 @@ class blog extends common { $feeds->setDate(date('r',time())); $feeds->addGenerator(); // Corps des articles - $articleIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC'); - $articleIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'posts']), 'state', 'SORT_DESC'); + $articleIdsPublishedOns = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC'); + $articleIdsStates = helper::arrayColumn($this->getData(['module', $this->getUrl(0),'posts']), 'state', 'SORT_DESC'); foreach( $articleIdsPublishedOns as $articleId => $articlePublishedOn ) { if( $articlePublishedOn <= time() AND $articleIdsStates[$articleId] ) { // Miniature @@ -230,7 +230,7 @@ class blog extends common { ]); } // Liste des utilisateurs - self::$users = helper::arrayCollumn($this->getData(['user']), 'firstname'); + self::$users = helper::arrayColumn($this->getData(['user']), 'firstname'); ksort(self::$users); foreach(self::$users as $userId => &$userFirstname) { $userFirstname = $userFirstname . ' ' . $this->getData(['user', $userId, 'lastname']); @@ -259,7 +259,7 @@ class blog extends common { 'value' => 'Tout effacer' ]); // Ids des commentaires par ordre de création - $commentIds = array_keys(helper::arrayCollumn($comments, 'createdOn', 'SORT_DESC')); + $commentIds = array_keys(helper::arrayColumn($comments, 'createdOn', 'SORT_DESC')); // Pagination $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['module', $this->getUrl(0), 'config', 'itemsperPage']) ); // Liste des pages @@ -416,7 +416,7 @@ class blog extends common { ]); } else { // Ids des articles par ordre de publication - $articleIds = array_keys(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC')); + $articleIds = array_keys(helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC')); // Gestion des droits d'accès $filterData=[]; foreach ($articleIds as $key => $value) { @@ -448,7 +448,7 @@ class blog extends common { // Articles en fonction de la pagination for($i = $pagination['first']; $i < $pagination['last']; $i++) { // Nombre de commentaires à approuver et approuvés - $approvals = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'comment' ]),'approval', 'SORT_DESC'); + $approvals = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'comment' ]),'approval', 'SORT_DESC'); if ( is_array($approvals) ) { $a = array_values($approvals); $toApprove = count(array_keys($a,false)); @@ -595,7 +595,7 @@ class blog extends common { ]); } // Liste des utilisateurs - self::$users = helper::arrayCollumn($this->getData(['user']), 'firstname'); + self::$users = helper::arrayColumn($this->getData(['user']), 'firstname'); ksort(self::$users); foreach(self::$users as $userId => &$userFirstname) { // Les membres ne sont pas éditeurs, les exclure de la liste @@ -709,7 +709,7 @@ class blog extends common { // Ligne suivante si affichage du nombre total de commentaires approuvés sous l'article self::$nbCommentsApproved = count($commentsApproved); } - $commentIds = array_keys(helper::arrayCollumn($commentsApproved, 'createdOn', 'SORT_DESC')); + $commentIds = array_keys(helper::arrayColumn($commentsApproved, 'createdOn', 'SORT_DESC')); // Pagination $pagination = helper::pagination($commentIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0),'config', 'itemsperPage']),'#comment'); // Liste des pages @@ -749,8 +749,8 @@ class blog extends common { // Liste des articles else { // Ids des articles par ordre de publication - $articleIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'posts']), 'publishedOn', 'SORT_DESC'); - $articleIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'posts']), 'state', 'SORT_DESC'); + $articleIdsPublishedOns = helper::arrayColumn($this->getData(['module', $this->getUrl(0),'posts']), 'publishedOn', 'SORT_DESC'); + $articleIdsStates = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'state', 'SORT_DESC'); $articleIds = []; foreach($articleIdsPublishedOns as $articleId => $articlePublishedOn) { if($articlePublishedOn <= time() AND $articleIdsStates[$articleId]) { diff --git a/module/blog/view/edit/edit.php b/module/blog/view/edit/edit.php index 404467b8..14a3f653 100644 --- a/module/blog/view/edit/edit.php +++ b/module/blog/view/edit/edit.php @@ -20,7 +20,7 @@
        'Publier', - 'uniqueSubmission' => true, + 'uniqueSubmission' => true ]); ?>
        @@ -39,7 +39,7 @@
        'Taille optimale de l\'image de couverture : ' . ((int) substr($this->getData(['theme', 'site', 'width']), 0, -2) - (20 * 2)) . ' x 350 pixels.', + 'help' => $this->getData(['theme', 'site', 'width']) !== '100%' ? 'Taille optimale de l\'image de couverture : ' . ((int) substr($this->getData(['theme', 'site', 'width']), 0, -2) - (20 * 2)) . ' x 350 pixels.' : '', 'label' => 'Image de couverture', 'type' => 1, 'value' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'picture']) diff --git a/module/form/form.php b/module/form/form.php index 627ad4da..53733989 100644 --- a/module/form/form.php +++ b/module/form/form.php @@ -80,7 +80,7 @@ class form extends common { */ public function config() { // Liste des utilisateurs - $userIdsFirstnames = helper::arrayCollumn($this->getData(['user']), 'firstname'); + $userIdsFirstnames = helper::arrayColumn($this->getData(['user']), 'firstname'); ksort($userIdsFirstnames); self::$listUsers [] = ''; foreach($userIdsFirstnames as $userId => $userFirstname) { diff --git a/module/form/view/config/config.js.php b/module/form/view/config/config.js.php index 1522ee3d..f6e56700 100644 --- a/module/form/view/config/config.js.php +++ b/module/form/view/config/config.js.php @@ -69,7 +69,7 @@ function position() { var inputUid = 0; var inputs = getData(['module', $this->getUrl(0), 'input'])); ?>; if(inputs) { - var inputsPerPosition = getData(['module', $this->getUrl(0), 'input']), 'position', 'SORT_ASC')); ?>; + var inputsPerPosition = getData(['module', $this->getUrl(0), 'input']), 'position', 'SORT_ASC')); ?>; $.each(inputsPerPosition, function(id) { add(inputUid, inputs[id]); inputUid++; diff --git a/module/gallery/gallery.php b/module/gallery/gallery.php index 637facb5..4e783539 100644 --- a/module/gallery/gallery.php +++ b/module/gallery/gallery.php @@ -17,7 +17,7 @@ class gallery extends common { - const VERSION = '3.4'; + const VERSION = '3.5'; const REALNAME = 'Galerie'; const DELETE = true; const UPDATE = '0.0'; @@ -51,7 +51,9 @@ class gallery extends common { 'sortGalleries' => self::GROUP_MODERATOR, 'sortPictures' => self::GROUP_MODERATOR, 'edit' => self::GROUP_MODERATOR, + 'add' => self::GROUP_MODERATOR, 'theme' => self::GROUP_MODERATOR, + 'option' => self::GROUP_MODERATOR, 'index' => self::GROUP_VISITOR ]; @@ -151,7 +153,6 @@ class gallery extends common { */ private function update() { - // Initialisation du module, créer les données si elles sont manquantes. $this->init(); @@ -264,7 +265,7 @@ class gallery extends common { * */ public function sortGalleries() { - if($_POST['response']) { + if( isset($_POST['response']) ){ $data = explode('&',$_POST['response']); $data = str_replace('galleryTable%5B%5D=','',$data); for($i=0;$i $this->getData(['module',$this->getUrl(0), 'content', $data[$i],'config','homePicture']), 'sort' => $this->getData(['module',$this->getUrl(0), 'content', $data[$i],'config','sort']), 'position'=> $i, - 'fullScreen' => $this->getData(['module',$this->getUrl(0), 'content',$data[$i],'config','fullScreen']) + 'fullScreen' => $this->getData(['module',$this->getUrl(0), 'content',$data[$i],'config','fullScreen']), + 'showPageContent' => $this->getData(['module',$this->getUrl(0), 'content',$data[$i],'config','showPageContent']) ], 'legend' => $this->getData(['module',$this->getUrl(0), 'content', $data[$i],'legend']), @@ -290,22 +292,22 @@ class gallery extends common { * */ public function sortPictures() { - if($_POST['response']) { + if( isset($_POST['response']) ) { $galleryName = $_POST['gallery']; $data = explode('&',$_POST['response']); $data = str_replace('galleryTable%5B%5D=','',$data); // Sauvegarder $this->setData(['module', $this->getUrl(0), 'content', $galleryName, [ 'config' => [ - 'name' => $this->getData(['module',$this->getUrl(0),$galleryName,'config','name']), - 'directory' => $this->getData(['module',$this->getUrl(0),$galleryName,'config','directory']), - 'homePicture' => $this->getData(['module',$this->getUrl(0),$galleryName,'config','homePicture']), - 'sort' => $this->getData(['module',$this->getUrl(0),$galleryName,'config','sort']), - 'position' => $this->getData(['module',$this->getUrl(0),$galleryName,'config','position']), - 'fullScreen' => $this->getData(['module',$this->getUrl(0),$galleryName,'config','fullScreen']) + 'name' => $this->getData(['module',$this->getUrl(0), 'content', $galleryName,'config','name']), + 'directory' => $this->getData(['module',$this->getUrl(0),'content',$galleryName,'config','directory']), + 'homePicture' => $this->getData(['module',$this->getUrl(0),'content',$galleryName,'config','homePicture']), + 'sort' => $this->getData(['module',$this->getUrl(0),'content',$galleryName,'config','sort']), + 'position' => $this->getData(['module',$this->getUrl(0),'content',$galleryName,'config','position']), + 'fullScreen' => $this->getData(['module',$this->getUrl(0),'content',$galleryName,'config','fullScreen']) ], - 'legend' => $this->getData(['module',$this->getUrl(0),$galleryName,'legend']), + 'legend' => $this->getData(['module',$this->getUrl(0), 'content', $galleryName,'legend']), 'positions' => array_flip($data) ]]); } @@ -322,7 +324,7 @@ class gallery extends common { //Affichage de la galerie triée $g = $this->getData(['module', $this->getUrl(0), 'content']); - $p = helper::arrayCollumn(helper::arrayCollumn($g,'config'),'position'); + $p = helper::arrayColumn(helper::arrayColumn($g,'config'),'position'); asort($p,SORT_NUMERIC); $galleries = []; foreach ($p as $positionId => $item) { @@ -343,7 +345,7 @@ class gallery extends common { } // Met en forme le tableau self::$galleries[] = [ - template::ico('sort'), + $gallery['config']['position'] + 1, $gallery['config']['name'], $gallery['config']['directory'], template::button('galleryConfigEdit' . $galleryId , [ @@ -385,9 +387,10 @@ class gallery extends common { 'name' => $this->getInput('galleryConfigName'), 'directory' => $this->getInput('galleryConfigDirectory', helper::FILTER_STRING_SHORT, true), 'homePicture' => $homePicture, - 'sort' => self::SORT_ASC, - 'position' => $this->getData(['module', $this->getUrl(0), 'content', $galleryId,'config','position']), - 'fullScreen' => false + 'sort' => $this->getInput('galleryConfigSort'), + 'position' => count($this->getData(['module', $this->getUrl(0), 'content'])) + 1, + 'fullScreen' => $this->getInput('galleryConfigFullscreen', helper::FILTER_BOOLEAN), + 'showPageContent' => $this->getInput('galleryConfigShowPageContent', helper::FILTER_BOOLEAN) ], 'legend' => [], 'positions' => [] @@ -410,6 +413,58 @@ class gallery extends common { ]); } + /** + * Ajout d'une galerie + */ + public function add() { + // Soumission du formulaire d'ajout d'une galerie + if($this->isPost()) { + if (!$this->getInput('galleryAddFilterResponse')) { + $galleryId = helper::increment($this->getInput('galleryAddName', helper::FILTER_ID, true), (array) $this->getData(['module', $this->getUrl(0), 'content'])); + // définir une vignette par défaut + $directory = $this->getInput('galleryAddDirectory', helper::FILTER_STRING_SHORT, true); + $iterator = new DirectoryIterator($directory); + foreach($iterator as $fileInfos) { + if($fileInfos->isDot() === false AND $fileInfos->isFile() AND @getimagesize($fileInfos->getPathname())) { + // Créer la miniature si manquante + if (!file_exists( str_replace('source','thumb',$fileInfos->getPath()) . '/' . self::THUMBS_SEPARATOR . strtolower($fileInfos->getFilename()))) { + $this->makeThumb($fileInfos->getPathname(), + str_replace('source','thumb',$fileInfos->getPath()) . '/' . self::THUMBS_SEPARATOR . strtolower($fileInfos->getFilename()), + self::THUMBS_WIDTH); + } + // Miniatures + $homePicture = strtolower($fileInfos->getFilename()); + break; + } + } + $this->setData(['module', $this->getUrl(0), 'content', $galleryId, [ + 'config' => [ + 'name' => $this->getInput('galleryAddName'), + 'directory' => $this->getInput('galleryAddDirectory', helper::FILTER_STRING_SHORT, true), + 'homePicture' => $homePicture, + 'sort' => $this->getInput('galleryAddSort'), + 'position' => count($this->getData(['module', $this->getUrl(0), 'content'])) + 1, + 'fullScreen' => $this->getInput('galleryAddFullscreen', helper::FILTER_BOOLEAN), + 'showPageContent' => $this->getInput('galleryAddShowPageContent', helper::FILTER_BOOLEAN) + ], + 'legend' => [], + 'positions' => [] + ]]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', // '#galleryAddForm'*/, + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } + } else { + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Ajout d\'une galerie', + 'view' => 'add' + ]); + } + } /** * Suppression */ @@ -510,11 +565,12 @@ class gallery extends common { // pas de positions, on active le tri alpha 'sort' => $this->getInput('galleryEditSort'), 'position' => $this->getData(['module', $this->getUrl(0), 'content', $galleryId,'config','position']), - 'fullScreen' => $this->getInput('galleryEditFullscreen', helper::FILTER_BOOLEAN) + 'fullScreen' => $this->getInput('galleryEditFullscreen', helper::FILTER_BOOLEAN), + 'showPageContent' => $this->getInput('galleryEditShowPageContent', helper::FILTER_BOOLEAN) ], 'legend' => $legends, - 'positions' => empty($oldPositions) ? $this->getdata(['module', $this->getUrl(0), 'content', $galleryId, 'positions']) : $oldPositions + 'positions' => empty($oldPositions) ? $this->getData(['module', $this->getUrl(0), 'content', $galleryId, 'positions']) : $oldPositions ]]); } // Valeurs en sortie @@ -528,7 +584,6 @@ class gallery extends common { $directory = $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'directory']); if(is_dir($directory)) { $iterator = new DirectoryIterator($directory); - foreach($iterator as $fileInfos) { if($fileInfos->isDot() === false AND $fileInfos->isFile() AND @getimagesize($fileInfos->getPathname())) { // Créer la miniature RFM si manquante @@ -538,7 +593,7 @@ class gallery extends common { 122); } self::$pictures[str_replace('.','',$fileInfos->getFilename())] = [ - template::ico('sort'), + $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'positions', str_replace('.','',$fileInfos->getFilename())]) + 1, $fileInfos->getFilename(), template::checkbox( 'homePicture[' . $fileInfos->getFilename() . ']', true, '', [ 'checked' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2),'config', 'homePicture']) === $fileInfos->getFilename() ? true : false, @@ -555,7 +610,7 @@ class gallery extends common { // Tri des images switch ($this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'sort'])) { case self::SORT_HAND: - $positions = $this->getdata(['module',$this->getUrl(0), $this->getUrl(2),'positions']); + $positions = $this->getData(['module',$this->getUrl(0),'content', $this->getUrl(2),'positions']); if ($positions) { foreach ($positions as $key => $value) { if (array_key_exists($key,self::$pictures)) { @@ -603,9 +658,10 @@ class gallery extends common { // Mise à jour des données de module $this->update(); // Une seule galerie, bifurquer sur celle-ci - $gallery = count($this->getData(['module', $this->getUrl(0), 'content'])) === 1 - ? array_key_first($this->getData(['module', $this->getUrl(0), 'content'])) - : $this->getUrl(1); + $gallery = $this->getData(['module', $this->getUrl(0), 'theme', 'showUniqueGallery']) === true && + count($this->getData(['module', $this->getUrl(0), 'content'])) === 1 + ? array_key_first($this->getData(['module', $this->getUrl(0), 'content'])) + : $this->getUrl(1); // Images d'une galerie if($gallery) { // La galerie n'existe pas @@ -669,7 +725,8 @@ class gallery extends common { 'showBarEditButton' => true, 'title' => $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'name']), 'view' => 'gallery', - 'style' => $this->getData(['module', $this->getUrl(0), 'theme', 'style']) + 'style' => $this->getData(['module', $this->getUrl(0), 'theme', 'style']), + 'showPageContent' => $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'showPageContent']), ]); } // Pas d'image dans la galerie @@ -686,7 +743,7 @@ class gallery extends common { else { // Tri des galeries suivant l'ordre défini $g = $this->getData(['module', $this->getUrl(0), 'content']); - $p = helper::arrayCollumn(helper::arrayCollumn($g,'config'),'position'); + $p = helper::arrayColumn(helper::arrayColumn($g,'config'),'position'); asort($p,SORT_NUMERIC); $galleries = []; foreach ($p as $positionId => $item) { @@ -732,7 +789,9 @@ class gallery extends common { 'showBarEditButton' => true, 'showPageContent' => true, 'view' => 'index', - 'style' => $this->getData(['module', $this->getUrl(0), 'theme', 'style']) + 'style' => file_exists($this->getData(['module', $this->getUrl(0), 'theme', 'style'])) + ? $this->getData(['module', $this->getUrl(0), 'theme', 'style']) + : '' ]); } } @@ -770,7 +829,8 @@ class gallery extends common { 'legendAlign' => $this->getinput('galleryThemeLegendAlign', helper::FILTER_STRING_SHORT), 'legendTextColor' => $this->getinput('galleryThemeLegendTextColor', helper::FILTER_STRING_SHORT), 'legendBgColor' => $this->getinput('galleryThemeLegendBgColor', helper::FILTER_STRING_SHORT), - 'style' => self::DATADIRECTORY . $this->getUrl(0) . '/theme.css' + 'showUniqueGallery' => $this->getinput('galleryThemeShowUniqueGallery', helper::FILTER_BOOLEAN), + 'style' => self::DATADIRECTORY . $this->getUrl(0) . '/theme.css', ]]); // Création des fichiers CSS $content = file_get_contents('module/gallery/ressource/vartheme.css'); @@ -808,6 +868,41 @@ class gallery extends common { ]); } + /** + * Option de configuration de la galerie + */ + public function option() { + // Jeton incorrect + if ($this->getUrl(2) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Soumission du formulaire + if($this->isPost()) { + // Dossier de l'instance + if (!is_dir(self::DATADIRECTORY . $this->getUrl(0) )) { + mkdir (self::DATADIRECTORY . $this->getUrl(0), 0755, true); + } + $this->setData(['module', $this->getUrl(0), 'config', [ + 'showUniqueGallery' => $this->getinput('galleriesOptionShowUniqueGallery', helper::FILTER_BOOLEAN) + ]]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl() . '/option', + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => "Options", + 'view' => 'option' + ]); + } + } class galleriesHelper extends helper { diff --git a/module/gallery/vendor/tablednd/MIT-LICENSE.txt b/module/gallery/vendor/tablednd/MIT-LICENSE.txt new file mode 100644 index 00000000..fb40abd8 --- /dev/null +++ b/module/gallery/vendor/tablednd/MIT-LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) Denis Howlett +Copyright 2012 Nick Lombard - nickl- and other contributors +https://github.com/isocra/TableDnD + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/gallery/vendor/tablednd/lisez-moi.txt b/module/gallery/vendor/tablednd/lisez-moi.txt index dfa8616a..695d1a4f 100755 --- a/module/gallery/vendor/tablednd/lisez-moi.txt +++ b/module/gallery/vendor/tablednd/lisez-moi.txt @@ -1 +1,2 @@ -https://github.com/isocra/TableDnD \ No newline at end of file +https://github.com/isocra/TableDnD +Version 1.0.5 \ No newline at end of file diff --git a/module/gallery/vendor/tablednd/tablednd.min.js b/module/gallery/vendor/tablednd/tablednd.min.js index 225739f4..bb516a6a 100755 --- a/module/gallery/vendor/tablednd/tablednd.min.js +++ b/module/gallery/vendor/tablednd/tablednd.min.js @@ -1,2 +1,2 @@ -/*! jquery.tablednd.js 30-12-2017 */ -!function(a,b,c,d){var e="touchstart mousedown",f="touchmove mousemove",g="touchend mouseup";a(c).ready(function(){function b(a){for(var b={},c=a.match(/([^;:]+)/g)||[];c.length;)b[c.shift()]=c.shift().trim();return b}a("table").each(function(){"dnd"===a(this).data("table")&&a(this).tableDnD({onDragStyle:a(this).data("ondragstyle")&&b(a(this).data("ondragstyle"))||null,onDropStyle:a(this).data("ondropstyle")&&b(a(this).data("ondropstyle"))||null,onDragClass:a(this).data("ondragclass")===d&&"tDnD_whileDrag"||a(this).data("ondragclass"),onDrop:a(this).data("ondrop")&&new Function("table","row",a(this).data("ondrop")),onDragStart:a(this).data("ondragstart")&&new Function("table","row",a(this).data("ondragstart")),onDragStop:a(this).data("ondragstop")&&new Function("table","row",a(this).data("ondragstop")),scrollAmount:a(this).data("scrollamount")||5,sensitivity:a(this).data("sensitivity")||10,hierarchyLevel:a(this).data("hierarchylevel")||0,indentArtifact:a(this).data("indentartifact")||'
         
        ',autoWidthAdjust:a(this).data("autowidthadjust")||!0,autoCleanRelations:a(this).data("autocleanrelations")||!0,jsonPretifySeparator:a(this).data("jsonpretifyseparator")||"\t",serializeRegexp:a(this).data("serializeregexp")&&new RegExp(a(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:a(this).data("serializeparamname")||!1,dragHandle:a(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(b){return this.each(function(){this.tableDnDConfig=a.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,indentArtifact:'
         
        ',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:"\t",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},b||{}),a.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&a.tableDnD.makeIndented(this)}),this},makeIndented:function(b){var c,d,e=b.tableDnDConfig,f=b.rows,g=a(f).first().find("td:first")[0],h=0,i=0;if(a(b).hasClass("indtd"))return null;d=a(b).addClass("indtd").attr("style"),a(b).css({whiteSpace:"nowrap"});for(var j=0;ja.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,b.nextSibling)||00&&a(c).find("td:first").children(":first").remove()&&a(c).data("level",--d),0>b.horizontal&&d=d&&a(c).children(":first").prepend(e.indentArtifact)&&a(c).data("level",++d)},mousemove:function(b){var c,d,e,f,g,h=a(a.tableDnD.dragObject),i=a.tableDnD.currentTable.tableDnDConfig;return b&&b.preventDefault(),!!a.tableDnD.dragObject&&("touchmove"===b.type&&event.preventDefault(),i.onDragClass&&h.addClass(i.onDragClass)||h.css(i.onDragStyle),d=a.tableDnD.mouseCoords(b),f=d.x-a.tableDnD.mouseOffset.x,g=d.y-a.tableDnD.mouseOffset.y,a.tableDnD.autoScroll(d),c=a.tableDnD.findDropTargetRow(h,g),e=a.tableDnD.findDragDirection(f,g),a.tableDnD.moveVerticle(e,c),a.tableDnD.moveHorizontal(e,c),!1)},findDragDirection:function(a,b){var c=this.currentTable.tableDnDConfig.sensitivity,d=this.oldX,e=this.oldY,f=d-c,g=d+c,h=e-c,i=e+c,j={horizontal:a>=f&&a<=g?0:a>d?-1:1,vertical:b>=h&&b<=i?0:b>e?-1:1};return 0!==j.horizontal&&(this.oldX=a),0!==j.vertical&&(this.oldY=b),j},findDropTargetRow:function(b,c){for(var d=0,e=this.currentTable.rows,f=this.currentTable.tableDnDConfig,g=0,h=null,i=0;ig-d&&c1&&a(this.currentTable.rows).each(function(){if((h=a(this).data("level"))>1)for(e=a(this).prev().data("level");h>e+1;)a(this).find("td:first").children(":first").remove(),a(this).data("level",--h)}),b.onDragClass&&a(d).removeClass(b.onDragClass)||a(d).css(b.onDropStyle),this.dragObject=null,b.onDrop&&this.originalOrder!==this.currentOrder()&&a(d).hide().fadeIn("fast")&&b.onDrop(this.currentTable,d),b.onDragStop&&b.onDragStop(this.currentTable,d),this.currentTable=null},mouseup:function(b){return b&&b.preventDefault(),a.tableDnD.processMouseup(),!1},jsonize:function(a){var b=this.currentTable;return a?JSON.stringify(this.tableData(b),null,b.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(b))},serialize:function(){return a.param(this.tableData(this.currentTable))},serializeTable:function(a){for(var b="",c=a.tableDnDConfig.serializeParamName||a.id,d=a.rows,e=0;e0&&(b+="&");var f=d[e].id;f&&a.tableDnDConfig&&a.tableDnDConfig.serializeRegexp&&(f=f.match(a.tableDnDConfig.serializeRegexp)[0],b+=c+"[]="+f)}return b},serializeTables:function(){var b=[];return a("table").each(function(){this.id&&b.push(a.param(a.tableDnD.tableData(this)))}),b.join("&")},tableData:function(b){var c,d,e,f,g=b.tableDnDConfig,h=[],i=0,j=0,k=null,l={};if(b||(b=this.currentTable),!b||!b.rows||!b.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!b.id&&!g.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};f=g.autoCleanRelations&&b.rows||a.makeArray(b.rows),d=g.serializeParamName||b.id,e=d,c=function(a){return a&&g&&g.serializeRegexp?a.match(g.serializeRegexp)[0]:a},l[e]=[],!g.autoCleanRelations&&a(f[0]).data("level")&&f.unshift({id:"undefined"});for(var m=0;mi)h.push([e,i]),e=c(f[m-1].id);else if(j=i&&(h[n][1]=0);i=j,a.isArray(l[e])||(l[e]=[]),k=c(f[m].id),k&&l[e].push(k)}else(k=c(f[m].id))&&l[e].push(k);return l}},jQuery.fn.extend({tableDnD:a.tableDnD.build,tableDnDUpdate:a.tableDnD.updateTables,tableDnDSerialize:a.proxy(a.tableDnD.serialize,a.tableDnD),tableDnDSerializeAll:a.tableDnD.serializeTables,tableDnDData:a.proxy(a.tableDnD.tableData,a.tableDnD)})}(jQuery,window,window.document); \ No newline at end of file +/*! jquery.tablednd.js 20-11-2020 */ +!function(a,b,c,d){var e="touchstart mousedown",f="touchmove mousemove",g="touchend mouseup";a(c).ready(function(){function b(a){for(var b={},c=a.match(/([^;:]+)/g)||[];c.length;)b[c.shift()]=c.shift().trim();return b}a("table").each(function(){"dnd"===a(this).data("table")&&a(this).tableDnD({onDragStyle:a(this).data("ondragstyle")&&b(a(this).data("ondragstyle"))||null,onDropStyle:a(this).data("ondropstyle")&&b(a(this).data("ondropstyle"))||null,onDragClass:a(this).data("ondragclass")===d&&"tDnD_whileDrag"||a(this).data("ondragclass"),onDrop:a(this).data("ondrop")&&new Function("table","row",a(this).data("ondrop")),onDragStart:a(this).data("ondragstart")&&new Function("table","row",a(this).data("ondragstart")),onDragStop:a(this).data("ondragstop")&&new Function("table","row",a(this).data("ondragstop")),scrollAmount:a(this).data("scrollamount")||5,sensitivity:a(this).data("sensitivity")||10,hierarchyLevel:a(this).data("hierarchylevel")||0,indentArtifact:a(this).data("indentartifact")||'
         
        ',autoWidthAdjust:a(this).data("autowidthadjust")||!0,autoCleanRelations:a(this).data("autocleanrelations")||!0,jsonPretifySeparator:a(this).data("jsonpretifyseparator")||"\t",serializeRegexp:a(this).data("serializeregexp")&&new RegExp(a(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:a(this).data("serializeparamname")||!1,dragHandle:a(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(b){return this.each(function(){this.tableDnDConfig=a.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,indentArtifact:'
         
        ',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:"\t",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},b||{}),a.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&a.tableDnD.makeIndented(this)}),this},makeIndented:function(b){var c,d,e=b.tableDnDConfig,f=b.rows,g=a(f).first().find("td:first")[0],h=0,i=0;if(a(b).hasClass("indtd"))return null;d=a(b).addClass("indtd").attr("style"),a(b).css({whiteSpace:"nowrap"});for(var j=0;ja.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,b.nextSibling)||00&&a(c).find("td:first").children(":first").remove()&&a(c).data("level",--d),0>b.horizontal&&d=d&&a(c).children(":first").prepend(e.indentArtifact)&&a(c).data("level",++d)},mousemove:function(b){var c,d,e,f,g,h=a(a.tableDnD.dragObject),i=a.tableDnD.currentTable.tableDnDConfig;return b&&b.preventDefault(),!!a.tableDnD.dragObject&&("touchmove"===b.type&&event.preventDefault(),i.onDragClass&&h.addClass(i.onDragClass)||h.css(i.onDragStyle),d=a.tableDnD.mouseCoords(b),f=d.x-a.tableDnD.mouseOffset.x,g=d.y-a.tableDnD.mouseOffset.y,a.tableDnD.autoScroll(d),c=a.tableDnD.findDropTargetRow(h,g),e=a.tableDnD.findDragDirection(f,g),a.tableDnD.moveVerticle(e,c),a.tableDnD.moveHorizontal(e,c),!1)},findDragDirection:function(a,b){var c=this.currentTable.tableDnDConfig.sensitivity,d=this.oldX,e=this.oldY,f=d-c,g=d+c,h=e-c,i=e+c,j={horizontal:a>=f&&a<=g?0:a>d?-1:1,vertical:b>=h&&b<=i?0:b>e?-1:1};return 0!==j.horizontal&&(this.oldX=a),0!==j.vertical&&(this.oldY=b),j},findDropTargetRow:function(b,c){for(var d=0,e=this.currentTable.rows,f=this.currentTable.tableDnDConfig,g=0,h=null,i=0;ig-d&&c1&&a(this.currentTable.rows).each(function(){if((h=a(this).data("level"))>1)for(e=a(this).prev().data("level");h>e+1;)a(this).find("td:first").children(":first").remove(),a(this).data("level",--h)}),b.onDragClass&&a(d).removeClass(b.onDragClass)||a(d).css(b.onDropStyle),this.dragObject=null,b.onDrop&&this.originalOrder!==this.currentOrder()&&a(d).hide().fadeIn("fast")&&b.onDrop(this.currentTable,d),b.onDragStop&&b.onDragStop(this.currentTable,d),this.currentTable=null},mouseup:function(b){return b&&b.preventDefault(),a.tableDnD.processMouseup(),!1},jsonize:function(a){var b=this.currentTable;return a?JSON.stringify(this.tableData(b),null,b.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(b))},serialize:function(){return a.param(this.tableData(this.currentTable))},serializeTable:function(a){for(var b="",c=a.tableDnDConfig.serializeParamName||a.id,d=a.rows,e=0;e0&&(b+="&");var f=d[e].id;f&&a.tableDnDConfig&&a.tableDnDConfig.serializeRegexp&&(f=f.match(a.tableDnDConfig.serializeRegexp)[0],b+=c+"[]="+f)}return b},serializeTables:function(){var b=[];return a("table").each(function(){this.id&&b.push(a.param(a.tableDnD.tableData(this)))}),b.join("&")},tableData:function(b){var c,d,e,f,g=b.tableDnDConfig,h=[],i=0,j=0,k=null,l={};if(b||(b=this.currentTable),!b||!b.rows||!b.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!b.id&&!g.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};f=g.autoCleanRelations&&b.rows||a.makeArray(b.rows),d=g.serializeParamName||b.id,e=d,c=function(a){return a&&g&&g.serializeRegexp?a.match(g.serializeRegexp)[0]:a},l[e]=[],!g.autoCleanRelations&&a(f[0]).data("level")&&f.unshift({id:"undefined"});for(var m=0;mi)h.push([e,i]),e=c(f[m-1].id);else if(j=i&&(h[n][1]=0);i=j,a.isArray(l[e])||(l[e]=[]),k=c(f[m].id),k&&l[e].push(k)}else(k=c(f[m].id))&&l[e].push(k);return l}},jQuery.fn.extend({tableDnD:a.tableDnD.build,tableDnDUpdate:a.tableDnD.updateTables,tableDnDSerialize:a.proxy(a.tableDnD.serialize,a.tableDnD),tableDnDSerializeAll:a.tableDnD.serializeTables,tableDnDData:a.proxy(a.tableDnD.tableData,a.tableDnD)})}(jQuery,window,window.document); \ No newline at end of file diff --git a/module/gallery/view/add/add.css b/module/gallery/view/add/add.css new file mode 100644 index 00000000..56181936 --- /dev/null +++ b/module/gallery/view/add/add.css @@ -0,0 +1,19 @@ +/** + * This file is part of Zwii. + * + * For full copyright and license information, please see the LICENSE + * file that was distributed with this source code. + * + * @author Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/gallery/view/add/add.js.php b/module/gallery/view/add/add.js.php new file mode 100644 index 00000000..46d48000 --- /dev/null +++ b/module/gallery/view/add/add.js.php @@ -0,0 +1,87 @@ +/** + * This file is part of Zwii. + * + * For full copyright and license information, please see the LICENSE + * file that was distributed with this source code. + * + * @author Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + $( document ).ready(function() { + + + /** + * Tri de la galerie avec drag and drop + */ + $("#galleryTable").tableDnD({ + onDrop: function(table, row) { + $("#galleryAddFilterResponse").val($.tableDnD.serialize()); + }, + onDragStop : function(table, row) { + // Affiche le bouton de tri après un déplacement + //$(":input[type='submit']").prop('disabled', false); + // Sauvegarde le tri + sortGalleries(); + }, + // Supprime le tiret des séparateurs + serializeRegexp: "" + }); + + + + /** + * Confirmation de suppression + */ + $(".galleryAddDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer cette galerie ?", function() { + $(location).attr("href", _this.attr("href")); + }); + }); + +}); + +/** + * Liste des dossiers + */ +var oldResult = []; +var directoryDOM = $("#galleryAddDirectory"); +var directoryOldDOM = $("#galleryAddDirectoryOld"); +function dirs() { + $.ajax({ + type: "POST", + url: "getUrl(0); ?>/dirs", + success: function(result) { + if($(result).not(oldResult).length !== 0 || $(oldResult).not(result).length !== 0) { + directoryDOM.empty(); + for(var i = 0; i < result.length; i++) { + directoryDOM.append(function(i) { + var option = $("