ZwiiCMS, le gestionnaire de site Web sans base de données. #zwii #cms #nosql #json https://www.zwiicms.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3070 lines
119 KiB

  1. <?php
  2. /**
  3. * This file is part of Zwii.
  4. * For full copyright and license information, please see the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @author Rémi Jean <remi.jean@outlook.com>
  8. * @copyright Copyright (C) 2008-2018, Rémi Jean
  9. * @author Frédéric Tempez <frederic.tempez@outlook.com>
  10. * @copyright Copyright (C) 2018-2021, Frédéric Tempez
  11. * @license GNU General Public License, version 3
  12. * @link http://zwiicms.fr/
  13. */
  14. class common {
  15. const DISPLAY_RAW = 0;
  16. const DISPLAY_JSON = 1;
  17. const DISPLAY_RSS = 2;
  18. const DISPLAY_LAYOUT_BLANK = 3;
  19. const DISPLAY_LAYOUT_MAIN = 4;
  20. const DISPLAY_LAYOUT_LIGHT = 5;
  21. const GROUP_BANNED = -1;
  22. const GROUP_VISITOR = 0;
  23. const GROUP_MEMBER = 1;
  24. const GROUP_MODERATOR = 2;
  25. const GROUP_ADMIN = 3;
  26. const SIGNATURE_ID = 1;
  27. const SIGNATURE_PSEUDO = 2;
  28. const SIGNATURE_FIRSTLASTNAME = 3;
  29. const SIGNATURE_LASTFIRSTNAME = 4;
  30. // Dossier de travail
  31. const BACKUP_DIR = 'site/backup/';
  32. const DATA_DIR = 'site/data/';
  33. const FILE_DIR = 'site/file/';
  34. const TEMP_DIR = 'site/tmp/';
  35. // Miniatures de la galerie
  36. const THUMBS_SEPARATOR = 'mini_';
  37. const THUMBS_WIDTH = 640;
  38. // Contrôle d'édition temps max en secondes avant déconnexion 30 minutes
  39. const ACCESS_TIMER = 1800;
  40. // Numéro de version
  41. const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/update/raw/branch/master/';
  42. const ZWII_VERSION = '10.6.03';
  43. const ZWII_UPDATE_CHANNEL = "v10";
  44. public static $actions = [];
  45. public static $coreModuleIds = [
  46. 'config',
  47. 'install',
  48. 'maintenance',
  49. 'page',
  50. 'sitemap',
  51. 'theme',
  52. 'user',
  53. 'addon'
  54. ];
  55. public static $accessList = [
  56. 'user',
  57. 'theme',
  58. 'config',
  59. 'edit',
  60. 'config'
  61. ];
  62. public static $accessExclude = [
  63. 'login',
  64. 'logout'
  65. ];
  66. private $data = [];
  67. private $hierarchy = [
  68. 'all' => [],
  69. 'visible' => [],
  70. 'bar' => []
  71. ];
  72. private $input = [
  73. '_COOKIE' => [],
  74. '_POST' => []
  75. ];
  76. public static $inputBefore = [];
  77. public static $inputNotices = [];
  78. public static $importNotices = [];
  79. public static $captchaNotices = [];
  80. public static $coreNotices = [];
  81. public $output = [
  82. 'access' => true,
  83. 'content' => '',
  84. 'contentLeft' => '',
  85. 'contentRight' => '',
  86. 'display' => self::DISPLAY_LAYOUT_MAIN,
  87. 'metaDescription' => '',
  88. 'metaTitle' => '',
  89. 'notification' => '',
  90. 'redirect' => '',
  91. 'script' => '',
  92. 'showBarEditButton' => false,
  93. 'showPageContent' => false,
  94. 'state' => false,
  95. 'style' => '',
  96. 'title' => null, // Null car un titre peut être vide
  97. // Trié par ordre d'exécution
  98. 'vendor' => [
  99. 'jquery',
  100. 'normalize',
  101. 'lity',
  102. 'filemanager',
  103. //'flatpickr', Appelé par les modules désactivé par défaut
  104. // 'tinycolorpicker', Désactivé par défaut
  105. // 'tinymce', Désactivé par défaut
  106. // 'codemirror', // Désactivé par défaut
  107. 'tippy',
  108. 'zwiico',
  109. 'imagemap',
  110. 'simplelightbox'
  111. ],
  112. 'view' => ''
  113. ];
  114. public static $groups = [
  115. self::GROUP_BANNED => 'Banni',
  116. self::GROUP_VISITOR => 'Visiteur',
  117. self::GROUP_MEMBER => 'Membre',
  118. self::GROUP_MODERATOR => 'Éditeur',
  119. self::GROUP_ADMIN => 'Administrateur'
  120. ];
  121. public static $groupEdits = [
  122. self::GROUP_BANNED => 'Banni',
  123. self::GROUP_MEMBER => 'Membre',
  124. self::GROUP_MODERATOR => 'Éditeur',
  125. self::GROUP_ADMIN => 'Administrateur'
  126. ];
  127. public static $groupNews = [
  128. self::GROUP_MEMBER => 'Membre',
  129. self::GROUP_MODERATOR => 'Éditeur',
  130. self::GROUP_ADMIN => 'Administrateur'
  131. ];
  132. public static $groupPublics = [
  133. self::GROUP_VISITOR => 'Visiteur',
  134. self::GROUP_MEMBER => 'Membre',
  135. self::GROUP_MODERATOR => 'Éditeur',
  136. self::GROUP_ADMIN => 'Administrateur'
  137. ];
  138. // Langues proposées
  139. public static $i18nList = [
  140. 'fr' => 'Français (fr)',
  141. 'de' => 'Allemand (de)',
  142. 'en' => 'Anglais (en)',
  143. 'es' => 'Espagnol (es)',
  144. 'it' => 'Italien (it)',
  145. 'nl' => 'Néerlandais (nl)',
  146. 'pt' => 'Portugais (pt)',
  147. ];
  148. // Langue courante
  149. public static $i18nCurrent = 'fr';
  150. public static $timezone;
  151. private $url = '';
  152. // Données de site
  153. private $user = [];
  154. private $core = [];
  155. private $config = [];
  156. // Dossier localisé
  157. private $page = [];
  158. private $module = [];
  159. private $locale = [];
  160. // Descripteur de données Entrées / Sorties
  161. // Liste ici tous les fichiers de données
  162. private $dataFiles = [
  163. 'config' => '',
  164. 'page' => '',
  165. 'module' => '',
  166. 'core' => '',
  167. 'page' => '',
  168. 'user' => '',
  169. 'theme' => '',
  170. 'admin' => '',
  171. 'blacklist' => '',
  172. 'locale' => ''
  173. ];
  174. /**
  175. * Constructeur commun
  176. */
  177. public function __construct() {
  178. // Extraction des données http
  179. if(isset($_POST)) {
  180. $this->input['_POST'] = $_POST;
  181. }
  182. if(isset($_COOKIE)) {
  183. $this->input['_COOKIE'] = $_COOKIE;
  184. }
  185. // Instanciation de la classe des entrées / sorties
  186. // Récupère les descripteurs
  187. foreach ($this->dataFiles as $keys => $value) {
  188. // Constructeur JsonDB
  189. $this->dataFiles[$keys] = new \Prowebcraft\JsonDb([
  190. 'name' => $keys . '.json',
  191. 'dir' => $this->dataPath ($keys,self::$i18nCurrent),
  192. 'backup' => file_exists('site/data/.backup')
  193. ]);;
  194. }
  195. // Import version 9
  196. if (file_exists(self::DATA_DIR . 'core.json') === true &&
  197. $this->getData(['core','dataVersion']) < 10000) {
  198. $keepUsers = isset($_SESSION['KEEP_USERS']) ? $_SESSION['KEEP_USERS'] : false;
  199. $this->importData($keepUsers);
  200. unset ($_SESSION['KEEP_USERS']);
  201. // Réinstaller htaccess
  202. copy('core/module/install/ressource/.htaccess', self::DATA_DIR . '.htaccess');
  203. common::$importNotices [] = "Importation réalisée avec succès" ;
  204. //echo '<script>window.location.replace("' . helper::baseUrl() . $this->getData(['config','homePageId']) . '")</script>';
  205. }
  206. // Installation fraîche, initialisation des modules manquants
  207. // La langue d'installation par défaut est fr
  208. foreach ($this->dataFiles as $stageId => $item) {
  209. $folder = $this->dataPath ($stageId, self::$i18nCurrent);
  210. if (file_exists($folder . $stageId .'.json') === false) {
  211. $this->initData($stageId,self::$i18nCurrent);
  212. common::$coreNotices [] = $stageId ;
  213. }
  214. }
  215. // Utilisateur connecté
  216. if($this->user === []) {
  217. $this->user = $this->getData(['user', $this->getInput('ZWII_USER_ID')]);
  218. }
  219. // Construit la liste des pages parents/enfants
  220. if($this->hierarchy['all'] === []) {
  221. $pages = helper::arrayCollumn($this->getData(['page']), 'position', 'SORT_ASC');
  222. // Parents
  223. foreach($pages as $pageId => $pagePosition) {
  224. if(
  225. // Page parent
  226. $this->getData(['page', $pageId, 'parentPageId']) === ""
  227. // Ignore les pages dont l'utilisateur n'a pas accès
  228. AND (
  229. $this->getData(['page', $pageId, 'group']) === self::GROUP_VISITOR
  230. OR (
  231. $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  232. AND $this->getUser('group') >= $this->getData(['page', $pageId, 'group'])
  233. )
  234. )
  235. ) {
  236. if($pagePosition !== 0) {
  237. $this->hierarchy['visible'][$pageId] = [];
  238. }
  239. if($this->getData(['page', $pageId, 'block']) === 'bar') {
  240. $this->hierarchy['bar'][$pageId] = [];
  241. }
  242. $this->hierarchy['all'][$pageId] = [];
  243. }
  244. }
  245. // Enfants
  246. foreach($pages as $pageId => $pagePosition) {
  247. if(
  248. // Page parent
  249. $parentId = $this->getData(['page', $pageId, 'parentPageId'])
  250. // Ignore les pages dont l'utilisateur n'a pas accès
  251. AND (
  252. (
  253. $this->getData(['page', $pageId, 'group']) === self::GROUP_VISITOR
  254. AND $this->getData(['page', $parentId, 'group']) === self::GROUP_VISITOR
  255. )
  256. OR (
  257. $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  258. AND $this->getUser('group') >= $this->getData(['page', $parentId, 'group'])
  259. AND $this->getUser('group') >= $this->getData(['page', $pageId, 'group'])
  260. )
  261. )
  262. ) {
  263. if($pagePosition !== 0) {
  264. $this->hierarchy['visible'][$parentId][] = $pageId;
  265. }
  266. if($this->getData(['page', $pageId, 'block']) === 'bar') {
  267. $this->hierarchy['bar'][$pageId] = [];
  268. }
  269. $this->hierarchy['all'][$parentId][] = $pageId;
  270. }
  271. }
  272. }
  273. // Construit l'url
  274. if($this->url === '') {
  275. if($url = $_SERVER['QUERY_STRING']) {
  276. $this->url = $url;
  277. }
  278. else {
  279. $this->url = $this->getData(['locale', 'homePageId']);
  280. }
  281. }
  282. // Mise à jour des données core
  283. $this->update();
  284. // Données de proxy
  285. $proxy = $this->getData(['config','proxyType']) . $this->getData(['config','proxyUrl']) . ':' . $this->getData(['config','proxyPort']);
  286. if (!empty($this->getData(['config','proxyUrl'])) &&
  287. !empty($this->getData(['config','proxyPort'])) ) {
  288. $context = array(
  289. 'http' => array(
  290. 'proxy' => $proxy,
  291. 'request_fulluri' => true,
  292. 'verify_peer' => false,
  293. 'verify_peer_name' => false,
  294. ),
  295. "ssl"=>array(
  296. "verify_peer"=>false,
  297. "verify_peer_name"=>false
  298. )
  299. );
  300. stream_context_set_default($context);
  301. }
  302. }
  303. /**
  304. * Ajoute les valeurs en sortie
  305. * @param array $output Valeurs en sortie
  306. */
  307. public function addOutput($output) {
  308. $this->output = array_merge($this->output, $output);
  309. }
  310. /**
  311. * Ajoute une notice de champ obligatoire
  312. * @param string $key Clef du champ
  313. */
  314. public function addRequiredInputNotices($key) {
  315. // La clef est un tableau
  316. if(preg_match('#\[(.*)\]#', $key, $secondKey)) {
  317. $firstKey = explode('[', $key)[0];
  318. $secondKey = $secondKey[1];
  319. if(empty($this->input['_POST'][$firstKey][$secondKey])) {
  320. common::$inputNotices[$firstKey . '_' . $secondKey] = 'Obligatoire';
  321. }
  322. }
  323. // La clef est une chaine
  324. elseif(empty($this->input['_POST'][$key])) {
  325. common::$inputNotices[$key] = 'Obligatoire';
  326. }
  327. }
  328. /**
  329. * Check du token CSRF (true = bo
  330. */
  331. public function checkCSRF() {
  332. return ((empty($_POST['csrf']) OR hash_equals($_SESSION['csrf'], $_POST['csrf']) === false) === false);
  333. }
  334. /**
  335. * Supprime des données
  336. * @param array $keys Clé(s) des données
  337. */
  338. public function deleteData($keys) {
  339. // Descripteur
  340. $db = $this->dataFiles[$keys[0]];
  341. // Aiguillage
  342. switch(count($keys)) {
  343. case 1:
  344. $db->delete($keys[0], true);
  345. break;
  346. case 2:
  347. $db->delete($keys[0].'.'.$keys[1],true);
  348. break;
  349. case 3:
  350. $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2], true);
  351. break;
  352. case 4:
  353. $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3], true);
  354. break;
  355. case 5:
  356. $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4], true);
  357. break;
  358. case 6:
  359. $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5], true);
  360. break;
  361. case 7:
  362. $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6], true);
  363. break;
  364. }
  365. }
  366. /**
  367. * Accède aux données
  368. * @param array $keys Clé(s) des données
  369. * @return mixed
  370. */
  371. public function getData($keys = []) {
  372. if (count($keys) >= 1) {
  373. /**
  374. * Lecture directe
  375. */
  376. $db = $this->dataFiles[$keys[0]];
  377. switch(count($keys)) {
  378. case 1:
  379. $tempData = $db->get($keys[0]);
  380. break;
  381. case 2:
  382. $tempData = $db->get($keys[0].'.'.$keys[1]);
  383. break;
  384. case 3:
  385. $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2]);
  386. break;
  387. case 4:
  388. $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3]);
  389. break;
  390. case 5:
  391. $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4]);
  392. break;
  393. case 6:
  394. $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5]);
  395. break;
  396. case 7:
  397. $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6]);
  398. break;
  399. case 8:
  400. $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6].'.'.$keys[7]);
  401. break;
  402. }
  403. return $tempData;
  404. }
  405. }
  406. /*
  407. * Dummy function
  408. * Compatibilité des modules avec v8 et v9
  409. */
  410. public function saveData() {
  411. return;
  412. }
  413. /**
  414. * Accède à la liste des pages parents et de leurs enfants
  415. * @param int $parentId Id de la page parent
  416. * @param bool $onlyVisible Affiche seulement les pages visibles
  417. * @param bool $onlyBlock Affiche seulement les pages de type barre
  418. * @return array
  419. */
  420. public function getHierarchy($parentId = null, $onlyVisible = true, $onlyBlock = false) {
  421. $hierarchy = $onlyVisible ? $this->hierarchy['visible'] : $this->hierarchy['all'];
  422. $hierarchy = $onlyBlock ? $this->hierarchy['bar'] : $hierarchy;
  423. // Enfants d'un parent
  424. if($parentId) {
  425. if(array_key_exists($parentId, $hierarchy)) {
  426. return $hierarchy[$parentId];
  427. }
  428. else {
  429. return [];
  430. }
  431. }
  432. // Parents et leurs enfants
  433. else {
  434. return $hierarchy;
  435. }
  436. }
  437. /**
  438. * Accède à une valeur des variables http (ordre de recherche en l'absence de type : _COOKIE, _POST)
  439. * @param string $key Clé de la valeur
  440. * @param int $filter Filtre à appliquer à la valeur
  441. * @param bool $required Champ requis
  442. * @return mixed
  443. */
  444. public function getInput($key, $filter = helper::FILTER_STRING_SHORT, $required = false) {
  445. // La clef est un tableau
  446. if(preg_match('#\[(.*)\]#', $key, $secondKey)) {
  447. $firstKey = explode('[', $key)[0];
  448. $secondKey = $secondKey[1];
  449. foreach($this->input as $type => $values) {
  450. // Champ obligatoire
  451. if($required) {
  452. $this->addRequiredInputNotices($key);
  453. }
  454. // Check de l'existence
  455. // Également utile pour les checkbox qui ne retournent rien lorsqu'elles ne sont pas cochées
  456. if(
  457. array_key_exists($firstKey, $values)
  458. AND array_key_exists($secondKey, $values[$firstKey])
  459. ) {
  460. // Retourne la valeur filtrée
  461. if($filter) {
  462. return helper::filter($this->input[$type][$firstKey][$secondKey], $filter);
  463. }
  464. // Retourne la valeur
  465. else {
  466. return $this->input[$type][$firstKey][$secondKey];
  467. }
  468. }
  469. }
  470. }
  471. // La clef est une chaine
  472. else {
  473. foreach($this->input as $type => $values) {
  474. // Champ obligatoire
  475. if($required) {
  476. $this->addRequiredInputNotices($key);
  477. }
  478. // Check de l'existence
  479. // Également utile pour les checkbox qui ne retournent rien lorsqu'elles ne sont pas cochées
  480. if(array_key_exists($key, $values)) {
  481. // Retourne la valeur filtrée
  482. if($filter) {
  483. return helper::filter($this->input[$type][$key], $filter);
  484. }
  485. // Retourne la valeur
  486. else {
  487. return $this->input[$type][$key];
  488. }
  489. }
  490. }
  491. }
  492. // Sinon retourne null
  493. return helper::filter(null, $filter);
  494. }
  495. /**
  496. * Accède à une partie l'url ou à l'url complète
  497. * @param int $key Clé de l'url
  498. * @return string|null
  499. */
  500. public function getUrl($key = null) {
  501. // Url complète
  502. if($key === null) {
  503. return $this->url;
  504. }
  505. // Une partie de l'url
  506. else {
  507. $url = explode('/', $this->url);
  508. return array_key_exists($key, $url) ? $url[$key] : null;
  509. }
  510. }
  511. /**
  512. * Accède à l'utilisateur connecté
  513. * @param int $key Clé de la valeur
  514. * @return string|null
  515. */
  516. public function getUser($key) {
  517. if(is_array($this->user) === false) {
  518. return false;
  519. }
  520. elseif($key === 'id') {
  521. return $this->getInput('ZWII_USER_ID');
  522. }
  523. elseif(array_key_exists($key, $this->user)) {
  524. return $this->user[$key];
  525. }
  526. else {
  527. return false;
  528. }
  529. }
  530. /**
  531. * Check qu'une valeur est transmise par la méthode _POST
  532. * @return bool
  533. */
  534. public function isPost() {
  535. return ($this->checkCSRF() AND $this->input['_POST'] !== []);
  536. }
  537. /**
  538. * Import des données de la version 9
  539. * Convertit un fichier de données data.json puis le renomme
  540. */
  541. public function importData($keepUsers = false) {
  542. // Trois tentatives de lecture
  543. for($i = 0; $i < 3; $i++) {
  544. $tempData=json_decode(file_get_contents(self::DATA_DIR.'core.json'), true);
  545. $tempTheme=json_decode(file_get_contents(self::DATA_DIR.'theme.json'), true);
  546. if($tempData && $tempTheme) {
  547. // Backup
  548. rename (self::DATA_DIR.'core.json',self::DATA_DIR.'imported_core.json');
  549. rename (self::DATA_DIR.'theme.json',self::DATA_DIR.'imported_theme.json');
  550. break;
  551. }
  552. elseif($i === 2) {
  553. throw new \ErrorException('Import des données impossible.');
  554. }
  555. // Pause de 10 millisecondes
  556. usleep(10000);
  557. }
  558. // Dossier de langues
  559. if (!file_exists(self::DATA_DIR . '/fr')) {
  560. mkdir (self::DATA_DIR . '/fr');
  561. }
  562. // Un seul fichier pour éviter les erreurs de sauvegarde des v9
  563. $tempData = array_merge($tempData,$tempTheme);
  564. // Ecriture des données
  565. $this->setData(['config',$tempData['config']]);
  566. $this->setData(['core',$tempData['core']]);
  567. $this->setData(['page',$tempData['page']]);
  568. $this->setData(['module',$tempData['module']]);
  569. $this->setData(['theme',$tempData['theme']]);
  570. // Import des users sauvegardés si option active
  571. if ($keepUsers === false
  572. AND $tempData['user'] !== NULL) {
  573. $this->setData(['user',$tempData['user']]);
  574. }
  575. // Nettoyage du fichier de thème pour forcer une régénération
  576. if (file_exists(self::DATA_DIR . '/theme.css')) { // On ne sait jamais
  577. unlink (self::DATA_DIR . '/theme.css');
  578. }
  579. }
  580. /**
  581. * Génère un fichier json avec la liste des pages
  582. *
  583. */
  584. public function pages2Json() {
  585. // Sauve la liste des pages pour TinyMCE
  586. $parents = [];
  587. $rewrite = (helper::checkRewrite()) ? '' : '?';
  588. // Boucle de recherche des pages actives
  589. foreach($this->getHierarchy(null,false,false) as $parentId => $childIds) {
  590. $children = [];
  591. // Exclure les barres
  592. if ($this->getData(['page', $parentId, 'block']) !== 'bar' ) {
  593. // Boucler sur les enfants et récupérer le tableau children avec la liste des enfants
  594. foreach($childIds as $childId) {
  595. $children [] = [ 'title' => ' » '. html_entity_decode($this->getData(['page', $childId, 'title']), ENT_QUOTES) ,
  596. 'value'=> $rewrite.$childId
  597. ];
  598. }
  599. // Traitement
  600. if (empty($childIds)) {
  601. // Pas d'enfant, uniquement l'entrée du parent
  602. $parents [] = ['title' => html_entity_decode($this->getData(['page', $parentId, 'title']), ENT_QUOTES) ,
  603. 'value'=> $rewrite.$parentId
  604. ];
  605. } else {
  606. // Des enfants, on ajoute la page parent en premier
  607. array_unshift ($children , ['title' => html_entity_decode($this->getData(['page', $parentId, 'title']), ENT_QUOTES) ,
  608. 'value'=> $rewrite.$parentId
  609. ]);
  610. // puis on ajoute les enfants au parent
  611. $parents [] = ['title' => html_entity_decode($this->getData(['page', $parentId, 'title']), ENT_QUOTES) ,
  612. 'value'=> $rewrite.$parentId ,
  613. 'menu' => $children
  614. ];
  615. }
  616. }
  617. }
  618. // Sitemap et Search
  619. $children = [];
  620. $children [] = ['title'=>'Rechercher dans le site',
  621. 'value'=>$rewrite.'search'
  622. ];
  623. $children [] = ['title'=>'Plan du site',
  624. 'value'=>$rewrite.'sitemap'
  625. ];
  626. $parents [] = ['title' => 'Pages spéciales',
  627. 'value' => '#',
  628. 'menu' => $children
  629. ];
  630. // Enregistrement : 3 tentatives
  631. for($i = 0; $i < 3; $i++) {
  632. if (file_put_contents ('core/vendor/tinymce/link_list.json', json_encode($parents), LOCK_EX) !== false) {
  633. break;
  634. }
  635. // Pause de 10 millisecondes
  636. usleep(10000);
  637. }
  638. }
  639. /**
  640. * Retourne une chemin localisé pour l'enregistrement des données
  641. * @param $stageId nom du module
  642. * @param $lang langue des pages
  643. * @return string du dossier à créer
  644. */
  645. public function dataPath($id, $lang) {
  646. // Sauf pour les pages et les modules
  647. if ($id === 'page' ||
  648. $id === 'module' ||
  649. $id === 'locale' ) {
  650. $folder = self::DATA_DIR . $lang . '/' ;
  651. } else {
  652. $folder = self::DATA_DIR;
  653. }
  654. return ($folder);
  655. }
  656. /**
  657. * Génère un fichier un fichier sitemap.xml
  658. * https://github.com/icamys/php-sitemap-generator
  659. * $command valeurs possible
  660. * all : génère un site map complet
  661. * Sinon contient id de la page à créer
  662. */
  663. public function createSitemap($command = "all") {
  664. //require_once "core/vendor/sitemap/SitemapGenerator.php";
  665. $timezone = $this->getData(['config','timezone']);
  666. $outputDir = getcwd();
  667. $sitemap = new \Icamys\SitemapGenerator\SitemapGenerator(helper::baseurl(false),$outputDir);
  668. // will create also compressed (gzipped) sitemap
  669. $sitemap->enableCompression();
  670. // determine how many urls should be put into one file
  671. // according to standard protocol 50000 is maximum value (see http://www.sitemaps.org/protocol.html)
  672. $sitemap->setMaxUrlsPerSitemap(50000);
  673. // sitemap file name
  674. $sitemap->setSitemapFileName( 'sitemap.xml') ;
  675. // Set the sitemap index file name
  676. $sitemap->setSitemapIndexFileName( 'sitemap-index.xml');
  677. $datetime = new DateTime(date('c'));
  678. $datetime->format(DateTime::ATOM); // Updated ISO8601
  679. foreach($this->getHierarchy(null, null, null) as $parentPageId => $childrenPageIds) {
  680. // Exclure les barres et les pages non publiques et les pages masquées
  681. if ($this->getData(['page',$parentPageId,'group']) !== 0 ||
  682. $this->getData(['page', $parentPageId, 'block']) === 'bar' ) {
  683. continue;
  684. }
  685. // Page désactivée, traiter les sous-pages sans prendre en compte la page parente.
  686. if ($this->getData(['page', $parentPageId, 'disable']) !== true ) {
  687. $sitemap->addUrl ($parentPageId,$datetime);
  688. }
  689. // Articles du blog
  690. if ($this->getData(['page', $parentPageId, 'moduleId']) === 'blog' &&
  691. !empty($this->getData(['module',$parentPageId])) ) {
  692. foreach($this->getData(['module',$parentPageId,'posts']) as $articleId => $article) {
  693. if($this->getData(['module',$parentPageId,'posts',$articleId,'state']) === true) {
  694. $date = $this->getData(['module',$parentPageId,'posts',$articleId,'publishedOn']);
  695. $sitemap->addUrl( $parentPageId . '/' . $articleId , new DateTime("@{$date}",new DateTimeZone($timezone)));
  696. }
  697. }
  698. }
  699. // Sous-pages
  700. foreach($childrenPageIds as $childKey) {
  701. if ($this->getData(['page',$childKey,'group']) !== 0 || $this->getData(['page', $childKey, 'disable']) === true) {
  702. continue;
  703. }
  704. $sitemap->addUrl($childKey,$datetime);
  705. // La sous-page est un blog
  706. if ($this->getData(['page', $childKey, 'moduleId']) === 'blog' &&
  707. !empty($this->getData(['module',$childKey])) ) {
  708. foreach($this->getData(['module',$childKey,'posts']) as $articleId => $article) {
  709. if($this->getData(['module',$childKey,'posts',$articleId,'state']) === true) {
  710. $date = $this->getData(['module',$childKey,'posts',$articleId,'publishedOn']);
  711. $sitemap->addUrl( $childKey . '/' . $articleId , new DateTime("@{$date}",new DateTimeZone($timezone)));
  712. }
  713. }
  714. }
  715. }
  716. }
  717. // Flush all stored urls from memory to the disk and close all necessary tags.
  718. $sitemap->flush();
  719. // Move flushed files to their final location. Compress if the option is enabled.
  720. $sitemap->finalize();
  721. // Update robots.txt file in output directory or create a new one
  722. $sitemap->updateRobots();
  723. // Submit your sitemaps to Google, Yahoo, Bing and Ask.com
  724. if (empty ($this->getData(['config','proxyType']) . $this->getData(['config','proxyUrl']) . ':' . $this->getData(['config','proxyPort'])) ) {
  725. $sitemap->submitSitemap();
  726. }
  727. return(file_exists('sitemap.xml') && file_exists('robots.txt'));
  728. }
  729. /*
  730. * Création d'une miniature
  731. * Fonction utilisée lors de la mise à jour d'une version 9 à une version 10
  732. * @param string $src image source
  733. * @param string $dets image destination
  734. * @param integer $desired_width largeur demandée
  735. */
  736. function makeThumb($src, $dest, $desired_width) {
  737. // Vérifier l'existence du dossier de destination.
  738. $fileInfo = pathinfo($dest);
  739. if (!is_dir($fileInfo['dirname'])) {
  740. mkdir($fileInfo['dirname'],0755,true);
  741. }
  742. // Type d'image
  743. switch( $fileInfo['extension']) {
  744. case 'jpeg':
  745. case 'jpg':
  746. $source_image = imagecreatefromjpeg($src);
  747. break;
  748. case 'png':
  749. $source_image = imagecreatefrompng($src);
  750. break;
  751. case 'gif':
  752. $source_image = imagecreatefromgif($src);
  753. break;
  754. }
  755. // Image valide
  756. if ($source_image) {
  757. $width = imagesx($source_image);
  758. $height = imagesy($source_image);
  759. /* find the "desired height" of this thumbnail, relative to the desired width */
  760. $desired_height = floor($height * ($desired_width / $width));
  761. /* create a new, "virtual" image */
  762. $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
  763. /* copy source image at a resized size */
  764. imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
  765. switch(mime_content_type($src) ) {
  766. case 'image/jpeg':
  767. case 'image/jpg':
  768. return (imagejpeg($virtual_image, $dest));
  769. break;
  770. case 'image/png':
  771. return (imagepng($virtual_image, $dest));
  772. break;
  773. case 'image/gif':
  774. return (imagegif($virtual_image, $dest));
  775. break;
  776. }
  777. } else {
  778. return (false);
  779. }
  780. }
  781. /**
  782. * Envoi un mail
  783. * @param string|array $to Destinataire
  784. * @param string $subject Sujet
  785. * @param string $content Contenu
  786. * @return bool
  787. */
  788. public function sendMail($to, $subject, $content, $replyTo = null) {
  789. // Layout
  790. ob_start();
  791. include 'core/layout/mail.php';
  792. $layout = ob_get_clean();
  793. $mail = new PHPMailer\PHPMailer\PHPMailer;
  794. $mail->CharSet = 'UTF-8';
  795. // Mail
  796. try{
  797. // Paramètres SMTP
  798. if ($this->getdata(['config','smtp','enable'])) {
  799. //$mail->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER;
  800. $mail->isSMTP();
  801. $mail->SMTPAutoTLS = false;
  802. $mail->Host = $this->getdata(['config','smtp','host']);
  803. $mail->Port = (int) $this->getdata(['config','smtp','port']);
  804. if ($this->getData(['config','smtp','auth'])) {
  805. $mail->Username = $this->getData(['config','smtp','username']);
  806. $mail->Password = helper::decrypt($this->getData(['config','smtp','username']),$this->getData(['config','smtp','password']));
  807. $mail->SMTPAuth = $this->getData(['config','smtp','auth']);
  808. $mail->SMTPSecure = $this->getData(['config','smtp','secure']);
  809. $mail->setFrom($this->getData(['config','smtp','username']));
  810. if (is_null($replyTo)) {
  811. $mail->addReplyTo($this->getData(['config','smtp','username']));
  812. } else {
  813. $mail->addReplyTo($replyTo);
  814. }
  815. }
  816. // Fin SMTP
  817. } else {
  818. $host = str_replace('www.', '', $_SERVER['HTTP_HOST']);
  819. $mail->setFrom('no-reply@' . $host, $this->getData(['locale', 'title']));
  820. if (is_null($replyTo)) {
  821. $mail->addReplyTo('no-reply@' . $host, $this->getData(['locale', 'title']));
  822. } else {
  823. $mail->addReplyTo($replyTo);
  824. }
  825. }
  826. if(is_array($to)) {
  827. foreach($to as $userMail) {
  828. $mail->addAddress($userMail);
  829. }
  830. }
  831. else {
  832. $mail->addAddress($to);
  833. }
  834. $mail->isHTML(true);
  835. $mail->Subject = $subject;
  836. $mail->Body = $layout;
  837. $mail->AltBody = strip_tags($content);
  838. if($mail->send()) {
  839. return true;
  840. }
  841. else {
  842. return $mail->ErrorInfo;
  843. }
  844. } catch (phpmailerException $e) {
  845. return $e->errorMessage();
  846. } catch (Exception $e) {
  847. return $e->getMessage();
  848. }
  849. }
  850. /**
  851. * Sauvegarde des données
  852. * @param array $keys Clé(s) des données
  853. */
  854. public function setData($keys = []) {
  855. // Pas d'enregistrement lorsqu'une notice est présente ou tableau transmis vide
  856. if (!empty(self::$inputNotices)
  857. OR empty($keys)) {
  858. return false;
  859. }
  860. // Empêcher la sauvegarde d'une donnée nulle.
  861. if (gettype($keys[count($keys) -1]) === NULL) {
  862. return false;
  863. }
  864. // Descripteur
  865. $db = $this->dataFiles[$keys[0]];
  866. // Aiguillage
  867. switch(count($keys)) {
  868. case 2:
  869. $db->set($keys[0],$keys[1], true);
  870. break;
  871. case 3:
  872. $db->set($keys[0].'.'.$keys[1],$keys[2], true);
  873. break;
  874. case 4:
  875. $db->set($keys[0].'.'.$keys[1].'.'.$keys[2],$keys[3], true);
  876. break;
  877. case 5:
  878. $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3],$keys[4], true);
  879. break;
  880. case 6:
  881. $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4],$keys[5], true);
  882. break;
  883. case 7:
  884. $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5],$keys[6], true);
  885. break;
  886. case 8:
  887. $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6],$keys[7], true );
  888. break;
  889. }
  890. return true;
  891. }
  892. /**
  893. * Initialisation des données
  894. * @param array $module : nom du module à générer
  895. * choix valides : core config user theme page module
  896. */
  897. public function initData($module, $lang = 'fr', $sampleSite = false) {
  898. // Tableau avec les données vierges
  899. require_once('core/module/install/ressource/defaultdata.php');
  900. // Stockage dans un sous-dossier localisé
  901. // Le dossier de langue existe t-il ?
  902. if (!file_exists(self::DATA_DIR . '/' . $lang)) {
  903. mkdir (self::DATA_DIR . '/' . $lang);
  904. }
  905. $db = $this->dataFiles[$module];
  906. if ($sampleSite === true) {
  907. $db->set($module,init::$siteData[$module]);
  908. } else {
  909. $db->set($module,init::$defaultData[$module]);
  910. }
  911. $db->save;
  912. }
  913. /**
  914. * Effacer un dossier non vide.
  915. * @param string URL du dossier à supprimer
  916. */
  917. public function removeDir ( $path ) {
  918. foreach ( new DirectoryIterator($path) as $item ) {
  919. if ( $item->isFile() ) @unlink($item->getRealPath());
  920. if ( !$item->isDot() && $item->isDir() ) $this->removeDir($item->getRealPath());
  921. }
  922. return ( rmdir($path) );
  923. }
  924. /**
  925. * Génère une archive d'un dossier et des sous-dossiers
  926. * @param string fileName path et nom de l'archive
  927. * @param string folder path à zipper
  928. * @param array filter dossiers à exclure
  929. */
  930. public function makeZip ($fileName, $folder, $filter ) {
  931. $zip = new ZipArchive();
  932. $zip->open($fileName, ZipArchive::CREATE | ZipArchive::OVERWRITE);
  933. //$directory = 'site/';
  934. $files = new RecursiveIteratorIterator(
  935. new RecursiveCallbackFilterIterator(
  936. new RecursiveDirectoryIterator(
  937. $folder,
  938. RecursiveDirectoryIterator::SKIP_DOTS
  939. ),
  940. function ($fileInfo, $key, $iterator) use ($filter) {
  941. return $fileInfo->isFile() || !in_array($fileInfo->getBaseName(), $filter);
  942. }
  943. )
  944. );
  945. foreach ($files as $name => $file) {
  946. if (!$file->isDir()) {
  947. $filePath = $file->getRealPath();
  948. $relativePath = substr($filePath, strlen(realpath($folder)) + 1);
  949. $zip->addFile($filePath, $relativePath);
  950. }
  951. }
  952. $zip->close();
  953. }
  954. /**
  955. * Mises à jour
  956. */
  957. private function update() {
  958. // Version 9.0.0
  959. if($this->getData(['core', 'dataVersion']) < 9000) {
  960. $this->deleteData(['theme', 'site', 'block']);
  961. if ($this->getData(['theme','menu','position']) === 'body-top') {
  962. $this->setData(['theme','menu','position','top']);
  963. }
  964. $this->setData(['theme', 'menu','fixed',false]);
  965. $this->setData(['core', 'dataVersion', 9000]);
  966. //$this->SaveData();
  967. }
  968. // Version 9.0.01
  969. if($this->getData(['core', 'dataVersion']) < 9001) {
  970. $this->deleteData(['config', 'social', 'googleplusId']);
  971. $this->setData(['core', 'dataVersion', 9001]);
  972. //$this->SaveData();
  973. }
  974. // Version 9.0.08
  975. if($this->getData(['core', 'dataVersion']) < 9008) {
  976. $this->setData(['theme', 'footer', 'textTransform','none']);
  977. $this->setData(['theme', 'footer', 'fontWeight','normal']);
  978. $this->setData(['theme', 'footer', 'fontSize','.8em']);
  979. $this->setData(['theme', 'footer', 'font','Open+Sans']);
  980. $this->setData(['core', 'dataVersion', 9008]);
  981. //$this->SaveData();
  982. }
  983. // Version 9.0.09
  984. if($this->getData(['core', 'dataVersion']) < 9009) {
  985. $this->setData(['core', 'dataVersion', 9009]);
  986. //$this->SaveData();
  987. }
  988. // Version 9.0.10
  989. if($this->getData(['core', 'dataVersion']) < 9010) {
  990. $this->deleteData(['config', 'social', 'googleplusId']);
  991. $this->setData(['core', 'dataVersion', 9010]);
  992. //$this->SaveData();
  993. }
  994. // Version 9.0.11
  995. if($this->getData(['core', 'dataVersion']) < 9011) {
  996. if ($this->getData(['theme','menu','position']) === 'body')
  997. $this->setData(['theme','menu','position','site']);
  998. $this->setData(['core', 'dataVersion', 9011]);
  999. //$this->SaveData();
  1000. }
  1001. // Version 9.0.17
  1002. if($this->getData(['core', 'dataVersion']) < 9017) {
  1003. $this->setData(['theme','footer','displayVersion', true ]);
  1004. $this->setData(['core', 'dataVersion', 9017]);
  1005. //$this->SaveData();
  1006. }
  1007. // Version 9.1.0
  1008. if($this->getData(['core', 'dataVersion']) < 9100) {
  1009. $this->setData(['theme','footer','displayVersion', true ]);
  1010. $this->setData(['theme','footer','displaySiteMap', true ]);
  1011. $this->setData(['theme','footer','displayCopyright', false ]);
  1012. $this->setData(['core', 'dataVersion', 9100]);
  1013. //$this->SaveData();
  1014. }
  1015. // Version 9.2.00
  1016. if($this->getData(['core', 'dataVersion']) < 9200) {
  1017. $this->setData(['theme','footer','template', 3 ]);
  1018. $this->setData(['theme','footer','margin', true ]);
  1019. $this->setData(['theme','footer','displayLegal', !empty($this->getdata(['config','legalPageId'])) ]);
  1020. $this->setData(['theme','footer','displaySearch', false ]);
  1021. $this->setData(['config','social','githubId', '' ]);
  1022. $this->setData(['core', 'dataVersion', 9200]);
  1023. //$this->SaveData();
  1024. }
  1025. // Version 9.2.05
  1026. if($this->getData(['core', 'dataVersion']) < 9205) {
  1027. // Nettoyage Swiper
  1028. if (file_exists('core/vendor/tinymce/templates/swiper.html')) {
  1029. unlink ('core/vendor/tinymce/templates/swiper.html');
  1030. }
  1031. if (is_dir('core/vendor/swiper')) {
  1032. $dir = getcwd();
  1033. chdir('core/vendor/swiper');
  1034. $files = glob('*');
  1035. foreach($files as $file) unlink($file);
  1036. chdir($dir);
  1037. rmdir ('core/vendor/swiper/');
  1038. }
  1039. $this->setData(['core', 'dataVersion', 9205]);
  1040. //$this->SaveData();
  1041. }
  1042. // Version 9.2.10
  1043. if($this->getData(['core', 'dataVersion']) < 9210) {
  1044. // Utile pour l'installation d'un backup sur un autre serveur
  1045. //$this->setData(['core', 'baseUrl', helper::baseUrl(false,false) ]);
  1046. // Suppression d'une option de hauteur de la bannière
  1047. if ($this->getData(['theme', 'header','height']) === 'none') {
  1048. $this->setData(['theme', 'header','height','150px']);
  1049. }
  1050. // Changer le nom de la clé linkHome -> linkHomePage
  1051. $this->setdata(['theme','header','linkHomePage',$this->getData(['theme','header','linkHome'])]);
  1052. $this->deleteData(['theme','header','linkHome']);
  1053. // Préparation des clés de légendes pour la v10
  1054. // Construire une liste plate de parents et d'enfants
  1055. $pageList = array();
  1056. foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) {
  1057. $pageList [] = $parentKey;
  1058. foreach ($parentValue as $childKey) {
  1059. $pageList [] = $childKey;
  1060. }
  1061. }
  1062. // Parcourir toutes les pages
  1063. foreach ($pageList as $parentKey => $parent) {
  1064. //La page a une galerie
  1065. if ($this->getData(['page',$parent,'moduleId']) === 'gallery' ) {
  1066. // Lire les données du module
  1067. // Parcourir les dossiers de la galerie
  1068. $tempData = $this->getData(['module', $parent]);
  1069. foreach ($tempData as $galleryKey => $galleryItem) {
  1070. foreach ($galleryItem as $legendKey => $legendValue) {
  1071. // Recherche la clé des légendes
  1072. if ($legendKey === 'legend') {
  1073. foreach ($legendValue as $itemKey=>$itemValue) {
  1074. // Ancien nom avec un point devant l'extension ?
  1075. if (strpos($itemKey,'.') > 0) {
  1076. // Créer une nouvelle clé
  1077. $this->setData(['module', $parent, $galleryKey, 'legend',str_replace('.','',$itemKey),$itemValue]);
  1078. // Supprimer la valeur
  1079. $this->deleteData(['module', $parent, $galleryKey, 'legend',$itemKey]);
  1080. }
  1081. }
  1082. }
  1083. }
  1084. }
  1085. }
  1086. }
  1087. $this->setData(['core', 'dataVersion', 9210]);
  1088. }
  1089. // Version 9.2.11
  1090. if($this->getData(['core', 'dataVersion']) < 9211) {
  1091. $autoUpdate= mktime(0, 0, 0);
  1092. $this->setData(['core', 'lastAutoUpdate', $autoUpdate]);
  1093. $this->setData(['config','autoUpdate', true]);
  1094. $this->setData(['core', 'dataVersion', 9211]);
  1095. }
  1096. // Version 9.2.12
  1097. if($this->getData(['core', 'dataVersion']) < 9212) {
  1098. $this->setData(['theme','menu', 'activeColorAuto',true]);
  1099. $this->setData(['theme','menu', 'activeColor','rgba(255, 255, 255, 1)']);
  1100. $this->setData(['core', 'dataVersion', 9212]);
  1101. //$this->SaveData();
  1102. }
  1103. // Version 9.2.15
  1104. if($this->getData(['core', 'dataVersion']) < 9215) {
  1105. // Données de la barre de langue dans le menu
  1106. $this->setData(['theme','menu','burgerTitle',true]);
  1107. $this->setData(['core', 'dataVersion', 9215]);
  1108. }
  1109. // Version 9.2.16
  1110. if($this->getData(['core', 'dataVersion']) < 9216) {
  1111. // Utile pour l'installation d'un backup sur un autre serveur
  1112. // mais avec la réécriture d'URM
  1113. $this->setData(['core', 'baseUrl', helper::baseUrl(true,false) ]);
  1114. $this->setData(['core', 'dataVersion', 9216]);
  1115. }
  1116. // Version 9.2.21
  1117. if($this->getData(['core', 'dataVersion']) < 9221) {
  1118. // Utile pour l'installation d'un backup sur un autre serveur
  1119. // mais avec la réécriture d'URM
  1120. $this->setData(['theme', 'body', 'toTopbackgroundColor', 'rgba(33, 34, 35, .8)' ]);
  1121. $this->setData(['theme', 'body', 'toTopColor', 'rgba(255, 255, 255, 1)' ]);
  1122. $this->setData(['core', 'dataVersion', 9221]);
  1123. }
  1124. // Version 9.2.23
  1125. if($this->getData(['core', 'dataVersion']) < 9223) {
  1126. // Utile pour l'installation d'un backup sur un autre serveur
  1127. // mais avec la réécriture d'URM
  1128. $this->setData(['config', 'proxyUrl', '' ]);
  1129. $this->setData(['config', 'proxyPort', '' ]);
  1130. $this->setData(['config', 'proxyType', 'tcp://' ]);
  1131. $this->setData(['core', 'dataVersion', 9223]);
  1132. }
  1133. // Version 9.2.27
  1134. if($this->getData(['core', 'dataVersion']) < 9227) {
  1135. // Forcer la régénération du thème
  1136. if (file_exists(self::DATA_DIR.'theme.css')) {
  1137. unlink (self::DATA_DIR.'theme.css');
  1138. }
  1139. $this->setData(['core', 'dataVersion', 9227]);
  1140. }
  1141. // Version 10.0.00
  1142. if($this->getData(['core', 'dataVersion']) < 10000) {
  1143. $this->setData(['config', 'faviconDark','faviconDark.ico']);
  1144. //----------------------------------------
  1145. // Mettre à jour les données des galeries
  1146. $pageList = array();
  1147. foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) {
  1148. $pageList [] = $parentKey;
  1149. foreach ($parentValue as $childKey) {
  1150. $pageList [] = $childKey;
  1151. }
  1152. }
  1153. // Mise à jour des données pour la galerie v2
  1154. foreach ($pageList as $parentKey => $parent) {
  1155. //La page a une galerie
  1156. if ($this->getData(['page',$parent,'moduleId']) === 'gallery' ) {
  1157. // Parcourir les dossiers de la galerie
  1158. $tempData = $this->getData(['module', $parent]);
  1159. $i = 1;
  1160. foreach ($tempData as $galleryKey => $galleryItem) {
  1161. // Ordre de tri des galeries
  1162. if ( $this->getdata(['module',$parent,$galleryKey,'config','sort']) === NULL) {
  1163. $this->setdata(['module',$parent,$galleryKey,'config','sort','SORT_ASC']);
  1164. }
  1165. // Position de la galerie, tri manuel
  1166. if ( $this->getdata(['module',$parent,$galleryKey,'config','position']) === NULL) {
  1167. $this->setdata(['module',$parent,$galleryKey,'config','position',$i++]);
  1168. }
  1169. // Positions des images, tri manuel
  1170. if ( $this->getdata(['module',$parent,$galleryKey,'positions']) === NULL) {
  1171. $c = count($this->getdata(['module',$parent,$galleryKey,'legend']));
  1172. $this->setdata(['module',$parent,$galleryKey,'positions', range(0,$c-1) ]);
  1173. }
  1174. // Image de couverture
  1175. if ( $this->getdata(['module',$parent,$galleryKey,'config','homePicture']) === NULL) {
  1176. if (is_dir($this->getdata(['module',$parent,$galleryKey,'config','directory']))) {
  1177. $iterator = new DirectoryIterator($this->getdata(['module',$parent,$galleryKey,'config','directory']));
  1178. foreach($iterator as $fileInfos) {
  1179. if($fileInfos->isDot() === false AND $fileInfos->isFile() AND @getimagesize($fileInfos->getPathname())) {
  1180. $this->setdata(['module',$parent,$galleryKey,'config','homePicture',$fileInfos->getFilename()]);
  1181. break;
  1182. }
  1183. }
  1184. }
  1185. }
  1186. }
  1187. }
  1188. }
  1189. // Contrôle des options php.ini pour la mise à jour auto
  1190. if (helper::urlGetContents('http://zwiicms.fr/update/' . common::ZWII_UPDATE_CHANNEL . '/version') === false) {
  1191. $this->setData(['config','autoUpdate',false]);
  1192. }
  1193. $this->setData(['core', 'dataVersion', 10000]);
  1194. }
  1195. // Version 10.0.92
  1196. if ($this->getData(['core', 'dataVersion']) < 10092) {
  1197. // Suppression du dossier fullpage
  1198. if (is_dir('core/vendor/fullpage')) {
  1199. $dir = getcwd();
  1200. chdir('core/vendor/fullpage');
  1201. $files = glob('*');
  1202. foreach($files as $file) unlink($file);
  1203. chdir($dir);
  1204. rmdir ('core/vendor/fullpage/');
  1205. }
  1206. if (file_exists('core/vendor/tinymce/templates/fullPageSections.html')) {
  1207. unlink ('core/vendor/tinymce/templates/fullPageSections.html'); }
  1208. if (file_exists('core/vendor/tinymce/templates/fullPageSlides.html')) {
  1209. unlink ('core/vendor/tinymce/templates/fullPageSlides.html'); }
  1210. $this->setData(['core', 'dataVersion', 10092]);
  1211. }
  1212. // Version 10.0.93
  1213. if ($this->getData(['core', 'dataVersion']) < 10093) {
  1214. // Déplacement du fichier admin.css dans data
  1215. if (file_exists('core/layout/admin.css')) {
  1216. copy('core/layout/admin.css',self::DATA_DIR.'admin.css');
  1217. unlink('core/layout/admin.css');
  1218. }
  1219. //Déplacement d'un fichier de ressources
  1220. if (file_exists('core/module/config/ressource/.htaccess')) {
  1221. unlink('core/module/config/ressource/.htaccess');
  1222. rmdir ('core/module/config/ressource');
  1223. }
  1224. $this->setData(['core', 'dataVersion', 10093]);
  1225. // Réorganisation du thème
  1226. $this->setData(['theme','text','linkTextColor',$this->getData(['theme','link', 'textColor'])]);
  1227. }
  1228. // Version 10.1.04
  1229. if ($this->getData(['core', 'dataVersion']) < 10104) {
  1230. $this->setData(['theme','text','linkColor','rgba(74, 105, 189, 1)']);
  1231. $this->deleteData(['theme','text','linkTextColor']);
  1232. $this->setdata(['theme','block','backgroundColor','rgba(236, 239, 241, 1)']);
  1233. $this->setdata(['theme','block','borderColor','rgba(236, 239, 241, 1)']);
  1234. $this->setdata(['theme','menu','radius','0px']);
  1235. $this->setData(['core', 'dataVersion', 10104]);
  1236. }
  1237. // Version 10.2.00
  1238. if ($this->getData(['core', 'dataVersion']) < 10200) {
  1239. // Paramètres du compte connecté
  1240. if ($this->getUser('id')) {
  1241. $this->setData(['user', $this->getUser('id'), 'connectFail',0]);
  1242. $this->setData(['user', $this->getUser('id'), 'connectTimeout',0]);
  1243. $this->setData(['user', $this->getUser('id'), 'accessTimer',0]);
  1244. $this->setData(['user', $this->getUser('id'), 'accessUrl','']);
  1245. $this->setData(['user', $this->getUser('id'), 'accessCsrf',$_SESSION['csrf']]);
  1246. }
  1247. // Paramètres de sécurité
  1248. $this->setData(['config', 'connect', 'attempt',999]);
  1249. $this->setData(['config', 'connect', 'timeout',0]);
  1250. $this->setData(['config', 'connect', 'log',false]);
  1251. // Thème
  1252. $this->deleteData(['admin','colorButtonText']);
  1253. // Remettre à zéro le thème pour la génération du CSS du blog
  1254. if (file_exists(self::DATA_DIR . 'theme.css')) {
  1255. unlink(self::DATA_DIR . 'theme.css');
  1256. }
  1257. // Créer les en-têtes du journal
  1258. $d = 'Date;Heure;IP;Id;Action' . PHP_EOL;
  1259. file_put_contents(self::DATA_DIR . 'journal.log',$d);
  1260. // Init préservation htaccess
  1261. $this->setData(['config','autoUpdateHtaccess',false]);
  1262. // Options de barre de membre simple
  1263. $this->setData(['theme','menu','memberBar',true]);
  1264. // Thème Menu : couleur de page active non définie
  1265. if (!$this->getData(['theme','menu','activeTextColor']) ) {
  1266. $this->setData(['theme','menu','activeTextColor', $this->getData(['theme','menu','textColor']) ]);
  1267. }
  1268. $this->setData(['core', 'updateAvailable', false]);
  1269. $this->setData(['core', 'dataVersion', 10200]);
  1270. }
  1271. // Version 10.2.01
  1272. if ($this->getData(['core', 'dataVersion']) < 10201) {
  1273. // Options de barre de membre simple
  1274. $this->setData(['theme','footer','displayMemberBar',false]);
  1275. $this->deleteData(['theme','footer','displayMemberAccount']);
  1276. $this->deleteData(['theme','footer','displayMemberLogout']);
  1277. $this->setData(['core', 'dataVersion', 10201]);
  1278. }
  1279. // Version 10.3.00
  1280. if ($this->getData(['core', 'dataVersion']) < 10300) {
  1281. // Options de barre de membre simple
  1282. $this->setData(['config','page404','none']);
  1283. $this->setData(['config','page403','none']);
  1284. $this->setData(['config','page302','none']);
  1285. // Module de recherche
  1286. // Suppression du dossier search
  1287. if (is_dir('core/module/search')) {
  1288. $dir = getcwd();
  1289. chdir('core/module/search');
  1290. $files = glob('*');
  1291. foreach($files as $file) unlink($file);
  1292. chdir($dir);
  1293. rmdir ('core/module/search/');
  1294. }
  1295. // Désactivation de l'option dans le pied de page
  1296. $this->setData(['theme','footer','displaySearch',false]);
  1297. // Inscription des nouvelles variables
  1298. $this->setData(['config','searchPageId','']);
  1299. // Mettre à jour les données des galeries
  1300. $pageList = array();
  1301. foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) {
  1302. $pageList [] = $parentKey;
  1303. foreach ($parentValue as $childKey) {
  1304. $pageList [] = $childKey;
  1305. }
  1306. }
  1307. // Mise à jour des données de thème de la galerie
  1308. // Les données de thème sont communes au site
  1309. foreach ($pageList as $parentKey => $parent) {
  1310. //La page a une galerie
  1311. if ($this->getData(['page',$parent,'moduleId']) === 'gallery' ) {
  1312. foreach ( $this->getData(['module', $parent]) as $galleryKey => $galleryItem) {
  1313. // Transfert du theme dans une structure unique
  1314. if ( is_array($this->getdata(['theme',$parent])) ) {
  1315. $this->setdata(['theme','gallery',$this->getdata(['theme',$parent])]);
  1316. }
  1317. }
  1318. $this->deleteData(['theme',$parent]);
  1319. }
  1320. }
  1321. // Mise à jour du numéro de version
  1322. $this->setData(['core', 'dataVersion', 10300]);
  1323. }
  1324. // Version 10.3.01
  1325. if ($this->getData(['core', 'dataVersion']) < 10301) {
  1326. // Inscription des nouvelles variables
  1327. if ($this->getData(['config','searchPageId']) === '') {
  1328. $this->setData(['config','searchPageId','none']);
  1329. }
  1330. if ($this->getData(['config','legalPageId']) === '') {
  1331. $this->setData(['config','legalPageId','none']);
  1332. }
  1333. $this->setData(['core', 'dataVersion', 10301]);
  1334. }
  1335. // Version 10.3.02
  1336. if ($this->getData(['core', 'dataVersion']) < 10302) {
  1337. // Activation par défaut du captcha à la connexion
  1338. $this->setData(['config', 'connect','captcha', true]);
  1339. $this->setData(['core', 'dataVersion', 10302]);
  1340. }
  1341. // Version 10.3.03
  1342. if ($this->getData(['core', 'dataVersion']) < 10303) {
  1343. // Activation par défaut du captcha à la connexion
  1344. $this->setData(['config', 'captchaStrong', false]);
  1345. $this->setData(['core', 'dataVersion', 10303]);
  1346. }
  1347. // Version 10.3.04
  1348. if ($this->getData(['core', 'dataVersion']) < 10304) {
  1349. // Couleur des sous menus
  1350. $this->setData(['theme', 'menu', 'backgroundColorSub', $this->getData(['theme', 'menu', 'backgroundColor']) ]);
  1351. // Nettoyage du fichier de thème pour forcer une régénération
  1352. if (file_exists(self::DATA_DIR . '/theme.css')) { // On ne sait jamais
  1353. unlink (self::DATA_DIR . '/theme.css');
  1354. }
  1355. $this->setData(['core', 'dataVersion', 10304]);
  1356. }
  1357. // Version 10.3.06
  1358. if ($this->getData(['core', 'dataVersion']) < 10306) {
  1359. // Liste des pages
  1360. $pageList = array();
  1361. foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) {
  1362. $pageList [] = $parentKey;
  1363. foreach ($parentValue as $childKey) {
  1364. $pageList [] = $childKey;
  1365. }
  1366. }
  1367. // Mettre à jour les données des blogs les articles sont dans posts
  1368. foreach ($pageList as $parentKey => $parent) {
  1369. //La page a un blog
  1370. if ($this->getData(['page',$parent,'moduleId']) === 'blog' ) {
  1371. if (is_array($this->getData(['module', $parent]))) {
  1372. foreach ( $this->getData(['module', $parent]) as $blogKey => $blogItem) {
  1373. if ($blogKey === 'posts' OR $blogKey === 'config') {continue;}
  1374. $data = $this->getdata(['module',$parent,$blogKey]);
  1375. $this->deleteData(['module',$parent, $blogKey]);
  1376. $this->setData([ 'module', $parent, 'posts', $blogKey, $data ]);
  1377. }
  1378. }
  1379. }
  1380. }
  1381. foreach ($pageList as $parentKey => $parent) {
  1382. //La page a une news
  1383. if ($this->getData(['page',$parent,'moduleId']) === 'news' ) {
  1384. if (is_array($this->getData(['module', $parent]))) {
  1385. foreach ( $this->getData(['module', $parent]) as $newsKey => $newsItem) {
  1386. if ($blogKey === 'posts' OR $blogKey === 'config') {continue;}
  1387. $data = $this->getdata(['module',$parent,$newsKey]);
  1388. $this->deleteData(['module',$parent, $newsKey]);
  1389. $this->setData([ 'module', $parent, 'posts', $newsKey, $data ]);
  1390. }
  1391. }
  1392. }
  1393. }
  1394. $this->setData(['core', 'dataVersion', 10306]);
  1395. }
  1396. // Version 10.3.08
  1397. if ($this->getData(['core', 'dataVersion']) < 10308) {
  1398. // RAZ la mise à jour auto bug 10.3.07
  1399. $this->setData(['core','updateAvailable', false]);
  1400. $this->setData(['core', 'dataVersion', 10308]);
  1401. }
  1402. // Version 10.4.00
  1403. if ($this->getData(['core', 'dataVersion']) < 10400) {
  1404. // Ajouter le prénom comme pseudo et le pseudo comme signature
  1405. foreach($this->getData(['user']) as $userId => $userIds){
  1406. $this->setData(['user',$userId,'pseudo',$this->getData(['user',$userId,'firstname'])]);
  1407. $this->setData(['user',$userId,'signature',2]);
  1408. }
  1409. // Ajouter les champs de blog v3
  1410. // Liste des pages dans pageList
  1411. $pageList = array();
  1412. foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) {
  1413. $pageList [] = $parentKey;
  1414. foreach ($parentValue as $childKey) {
  1415. $pageList [] = $childKey;
  1416. }
  1417. }
  1418. // Parcourir pageList et rechercher les modules de blog
  1419. foreach ($pageList as $parentKey => $parent) {
  1420. //La page est un blog
  1421. if ($this->getData(['page',$parent,'moduleId']) === 'blog' ) {
  1422. $articleIds = array_keys(helper::arrayCollumn($this->getData(['module', $parent, 'posts']), 'publishedOn', 'SORT_DESC'));
  1423. foreach ($articleIds as $key => $article) {
  1424. // Droits les deux groupes
  1425. $this->setData(['module', $parent, 'posts', $article,'editConsent', 3]);
  1426. // Limite de taille 500
  1427. $this->setData(['module', $parent, 'posts', $article,'commentMaxlength', '500']);
  1428. // Pas d'approbation des commentaires
  1429. $this->setData(['module', $parent, 'posts', $article,'commentApproved', false ]);
  1430. // pas de notification
  1431. $this->setData(['module', $parent, 'posts', $article,'commentNotification', false ]);
  1432. // groupe de notification
  1433. $this->setData(['module', $parent, 'posts', $article,'commentGroupNotification', 3 ]);
  1434. }
  1435. // Traitement des commentaires
  1436. if ( is_array($this->getData(['module', $parent, 'posts', $article,'comment'])) ) {
  1437. foreach($this->getData(['module', $parent, 'posts', $article,'comment']) as $commentId => $comment) {
  1438. // Approbation
  1439. $this->setData(['module', $parent, 'posts', $article,'comment', $commentId, 'approval', true ]);
  1440. }
  1441. }
  1442. }
  1443. }
  1444. // Création du fichier locale.json
  1445. $this->setData(['locale','homePageId',$this->getData(['config','homePageId'])]);
  1446. $this->setData(['locale','page404',$this->getData(['config','page404'])]);
  1447. $this->setData(['locale','page403',$this->getData(['config','page403'])]);
  1448. $this->setData(['locale','page302',$this->getData(['config','page302'])]);
  1449. $this->setData(['locale','legalPageId',$this->getData(['config','legalPageId'])]);
  1450. $this->setData(['locale','searchPageId',$this->getData(['config','searchPageId'])]);
  1451. $this->setData(['locale','metaDescription',$this->getData(['config','metaDescription'])]);
  1452. $this->setData(['locale','title',$this->getData(['config','title'])]);
  1453. // Renommer les fichier de backup
  1454. if ($this->getInput('configAdvancedFileBackup', helper::FILTER_BOOLEAN) === false) {
  1455. $path = realpath('site/data');
  1456. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename)
  1457. {
  1458. if (strpos($filename,'back.json')) {
  1459. rename($filename, str_replace('back.json','backup.json',$filename));
  1460. }
  1461. }
  1462. }
  1463. // Supprimer les fichiers CSS devenus inutiles du module search
  1464. if (file_exists('module/search/ressource/theme.css') )
  1465. unlink('module/search/ressource/theme.css');
  1466. if (file_exists('module/search/ressource/vartheme.css') )
  1467. unlink('module/search/ressource/vartheme.css');
  1468. $this->deleteData(['theme','search','keywordColor']);
  1469. // Nettoyer les modules avec des données null
  1470. $modules = $this->getData(['module']);
  1471. foreach($modules as $key => $value) {
  1472. if (is_null($value) ) {
  1473. unset($modules[$key]);
  1474. }
  1475. }
  1476. $this->setData (['module',$modules]);
  1477. $this->setData(['core', 'dataVersion', 10400]);
  1478. }
  1479. // Version 10.5.02
  1480. if ($this->getData(['core', 'dataVersion']) < 10502) {
  1481. // Forcer la régénération du thème
  1482. if (file_exists(self::DATA_DIR.'theme.css')) {
  1483. unlink (self::DATA_DIR.'theme.css');
  1484. }
  1485. $this->setData(['core', 'dataVersion', 10502]);
  1486. }
  1487. // Version 10.6.00
  1488. if ($this->getData(['core', 'dataVersion']) < 10600) {
  1489. // Mise à jour des données des modules autonomes
  1490. // Liste des pages dans pageList
  1491. $pageList = array();
  1492. foreach ($this->getHierarchy(null,null,null) as $parentKey=>$parentValue) {
  1493. $pageList [] = $parentKey;
  1494. foreach ($parentValue as $childKey) {
  1495. $pageList [] = $childKey;
  1496. }
  1497. }
  1498. // Parcourir pageList et rechercher les modules au CSS autonomes
  1499. foreach ($pageList as $parentKey => $parent) {
  1500. if (
  1501. $this->getData(['page',$parent,'moduleId']) === 'search'
  1502. || $this->getData(['page',$parent,'moduleId']) === 'gallery'
  1503. || $this->getData(['page',$parent,'moduleId']) === 'news'
  1504. ){
  1505. if(class_exists($parent)) {
  1506. $module = new $moduleId;
  1507. $module->update($parent);
  1508. }
  1509. }
  1510. }
  1511. // Suppression de l'option d'objets par page gérées par les modules
  1512. $this->deleteData(['config','itemsperPage']);
  1513. // Valeur par défaut de la couleur du bouton Help
  1514. $this->setData(['admin', 'backgroundColorButtonHelp', 'rgba(255, 153, 0, 1)']);
  1515. $this->setData(['core', 'dataVersion', 10600]);
  1516. }
  1517. }
  1518. }
  1519. class core extends common {
  1520. /**
  1521. * Constructeur du coeur
  1522. */
  1523. public function __construct() {
  1524. parent::__construct();
  1525. // Token CSRF
  1526. if(empty($_SESSION['csrf'])) {
  1527. $_SESSION['csrf'] = bin2hex(openssl_random_pseudo_bytes(32));
  1528. }
  1529. // Fuseau horaire
  1530. self::$timezone = $this->getData(['config', 'timezone']); // Utile pour transmettre le timezone à la classe helper
  1531. date_default_timezone_set(self::$timezone);
  1532. // Supprime les fichiers temporaires
  1533. $lastClearTmp = mktime(0, 0, 0);
  1534. if($lastClearTmp > $this->getData(['core', 'lastClearTmp']) + 86400) {
  1535. $iterator = new DirectoryIterator(self::TEMP_DIR);
  1536. foreach($iterator as $fileInfos) {
  1537. if( $fileInfos->isFile() &&
  1538. $fileInfos->getBasename() !== '.htaccess' &&
  1539. $fileInfos->getBasename() !== '.gitkeep'
  1540. ) {
  1541. @unlink($fileInfos->getPathname());
  1542. }
  1543. }
  1544. // Date de la dernière suppression
  1545. $this->setData(['core', 'lastClearTmp', $lastClearTmp]);
  1546. // Enregistre les données
  1547. //$this->SaveData();
  1548. }
  1549. // Backup automatique des données
  1550. $lastBackup = mktime(0, 0, 0);
  1551. if(
  1552. $this->getData(['config', 'autoBackup'])
  1553. AND $lastBackup > $this->getData(['core', 'lastBackup']) + 86400
  1554. AND $this->getData(['user']) // Pas de backup pendant l'installation
  1555. ) {
  1556. // Copie des fichier de données
  1557. helper::autoBackup(self::BACKUP_DIR,['backup','tmp','file']);
  1558. // Date du dernier backup
  1559. $this->setData(['core', 'lastBackup', $lastBackup]);
  1560. // Supprime les backups de plus de 30 jours
  1561. $iterator = new DirectoryIterator(self::BACKUP_DIR);
  1562. foreach($iterator as $fileInfos) {
  1563. if(
  1564. $fileInfos->isFile()
  1565. AND $fileInfos->getBasename() !== '.htaccess'
  1566. AND $fileInfos->getMTime() + (86400 * 30) < time()
  1567. ) {
  1568. @unlink($fileInfos->getPathname());
  1569. }
  1570. }
  1571. }
  1572. // Crée le fichier de personnalisation avancée
  1573. if(file_exists(self::DATA_DIR.'custom.css') === false) {
  1574. file_put_contents(self::DATA_DIR.'custom.css', file_get_contents('core/module/theme/resource/custom.css'));
  1575. chmod(self::DATA_DIR.'custom.css', 0755);
  1576. }
  1577. // Crée le fichier de personnalisation
  1578. if(file_exists(self::DATA_DIR.'theme.css') === false) {
  1579. file_put_contents(self::DATA_DIR.'theme.css', '');
  1580. chmod(self::DATA_DIR.'theme.css', 0755);
  1581. }
  1582. // Crée le fichier de personnalisation de l'administration
  1583. if(file_exists(self::DATA_DIR.'admin.css') === false) {
  1584. file_put_contents(self::DATA_DIR.'admin.css', '');
  1585. chmod(self::DATA_DIR.'admin.css', 0755);
  1586. }
  1587. // Check la version rafraichissement du theme
  1588. $cssVersion = preg_split('/\*+/', file_get_contents(self::DATA_DIR.'theme.css'));
  1589. if(empty($cssVersion[1]) OR $cssVersion[1] !== md5(json_encode($this->getData(['theme'])))) {
  1590. // Version
  1591. $css = '/*' . md5(json_encode($this->getData(['theme']))) . '*/';
  1592. // Import des polices de caractères
  1593. $css .= '@import url("https://fonts.googleapis.com/css?family=' . $this->getData(['theme', 'text', 'font']) . '|' . $this->getData(['theme', 'title', 'font']) . '|' . $this->getData(['theme', 'header', 'font']) . '|' . $this->getData(['theme', 'menu', 'font']) . '");';
  1594. // Fond du body
  1595. $colors = helper::colorVariants($this->getData(['theme', 'body', 'backgroundColor']));
  1596. // Body
  1597. $css .= 'body{font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'text', 'font'])) . '",sans-serif}';
  1598. if($themeBodyImage = $this->getData(['theme', 'body', 'image'])) {
  1599. // Image dans html pour éviter les déformations.
  1600. $css .= 'html, .mce-menu.mce-in.mce-animate {background-image:url("../file/source/' . $themeBodyImage . '");background-position:' . $this->getData(['theme', 'body', 'imagePosition']) . ';background-attachment:' . $this->getData(['theme', 'body', 'imageAttachment']) . ';background-size:' . $this->getData(['theme', 'body', 'imageSize']) . ';background-repeat:' . $this->getData(['theme', 'body', 'imageRepeat']) . '}';
  1601. // Couleur du body transparente
  1602. $css .= 'body, .mce-menu.mce-in.mce-animate{background-color: rgba(0,0,0,0)}';
  1603. } else {
  1604. // Pas d'image couleur du body
  1605. $css .= 'html, .mce-menu.mce-in.mce-animate{background-color:' . $colors['normal'] . ';}';
  1606. // Même couleur dans le fond de l'éditeur
  1607. $css .= 'div.mce-edit-area {background-color:' . $colors['normal'] . ' !important}';
  1608. }
  1609. // Icône BacktoTop
  1610. $css .= '#backToTop {background-color:' .$this->getData(['theme', 'body', 'toTopbackgroundColor']). ';color:'.$this->getData(['theme', 'body', 'toTopColor']).';}';
  1611. // Site
  1612. $colors = helper::colorVariants($this->getData(['theme', 'text', 'linkColor']));
  1613. $css .= 'a{color:' . $colors['normal'] . '}';
  1614. // Couleurs de site dans TinyMCe
  1615. $css .= 'div.mce-edit-area {font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'text', 'font'])) . '",sans-serif}';
  1616. // Site dans TinyMCE
  1617. $css .= '.editorWysiwyg {background-color:' . $this->getData(['theme', 'site', 'backgroundColor']) . ';}';
  1618. //$css .= 'a:hover:not(.inputFile, button){color:' . $colors['darken'] . '}';
  1619. $css .= 'body,.row > div{font-size:' . $this->getData(['theme', 'text', 'fontSize']) . '}';
  1620. $css .= 'body{color:' . $this->getData(['theme', 'text', 'textColor']) . '}';
  1621. $css .= 'select,input[type=\'password\'],input[type=\'email\'],input[type=\'text\'],.inputFile,select,textarea{color:' . $this->getData(['theme', 'text', 'textColor']) .';background-color:'.$this->getData(['theme', 'site', 'backgroundColor']).';}';
  1622. // spécifiques au module de blog
  1623. $css .= '.blogDate {color:' . $this->getData(['theme', 'text', 'textColor']) . ';}.blogPicture img{border:1px solid ' . $this->getData(['theme', 'text', 'textColor']) . '; box-shadow: 1px 1px 5px ' . $this->getData(['theme', 'text', 'textColor']) . ';}';
  1624. // Couleur fixée dans admin.css
  1625. //$css .= '.button.buttonGrey,.button.buttonGrey:hover{color:' . $this->getData(['theme', 'text', 'textColor']) . '}';
  1626. $css .= '.container, .helpDisplayContent{max-width:' . $this->getData(['theme', 'site', 'width']) . '}';
  1627. $margin = $this->getData(['theme', 'site', 'margin']) ? '0' : '20px';
  1628. // Marge supplémentaire lorsque le pied de page est fixe
  1629. if ( $this->getData(['theme', 'footer', 'fixed']) === true &&
  1630. $this->getData(['theme', 'footer', 'position']) === 'body') {
  1631. //$css .= '@media (min-width: 769px) { #site {margin-bottom: ' . ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 31 ) . 'px}}';
  1632. //$css .= '@media (max-width: 768px) { #site {margin-bottom: ' . ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 93 ) . 'px}}';
  1633. $marginBottomLarge = ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 31 ) . 'px';
  1634. $marginBottomSmall = ((str_replace ('px', '', $this->getData(['theme', 'footer', 'height']) ) * 2 ) + 93 ) . 'px';
  1635. } else {
  1636. $marginBottomSmall = $margin;
  1637. $marginBottomLarge = $margin;
  1638. }
  1639. $css .= $this->getData(['theme', 'site', 'width']) === '100%'
  1640. ? '@media (min-width: 769px) {#site{margin:0 auto ' . $marginBottomLarge . ' 0 !important;}}@media (max-width: 768px) {#site{margin:0 auto ' . $marginBottomSmall . ' 0 !important;}}#site.light{margin:5% auto !important;} body{margin:0 auto !important;} #bar{margin:0 auto !important;} body > header{margin:0 auto !important;} body > nav {margin: 0 auto !important;} body > footer {margin:0 auto !important;}'
  1641. : '@media (min-width: 769px) {#site{margin: ' . $margin . ' auto ' . $marginBottomLarge . ' auto !important;}}@media (max-width: 768px) {#site{margin: ' . $margin . ' auto ' . $marginBottomSmall . ' auto !important;}}#site.light{margin: 5% auto !important;} body{margin:0px 10px;} #bar{margin: 0 -10px;} body > header{margin: 0 -10px;} body > nav {margin: 0 -10px;} body > footer {margin: 0 -10px;} ';
  1642. $css .= $this->getData(['theme', 'site', 'width']) === '750px'
  1643. ? '.button, button{font-size:0.8em;}'
  1644. : '';
  1645. $css .= '#site{background-color:' . $this->getData(['theme', 'site', 'backgroundColor']) . ';border-radius:' . $this->getData(['theme', 'site', 'radius']) . ';box-shadow:' . $this->getData(['theme', 'site', 'shadow']) . ' #212223;}';
  1646. $colors = helper::colorVariants($this->getData(['theme', 'button', 'backgroundColor']));
  1647. $css .= '.speechBubble,.button,.button:hover,button[type=\'submit\'],.pagination a,.pagination a:hover,input[type=\'checkbox\']:checked + label:before,input[type=\'radio\']:checked + label:before,.helpContent{background-color:' . $colors['normal'] . ';color:' . $colors['text'] . '}';
  1648. $css .= '.helpButton span{color:' . $colors['normal'] . '}';
  1649. $css .= 'input[type=\'text\']:hover,input[type=\'password\']:hover,.inputFile:hover,select:hover,textarea:hover{border-color:' . $colors['normal'] . '}';
  1650. $css .= '.speechBubble:before{border-color:' . $colors['normal'] . ' transparent transparent transparent}';
  1651. $css .= '.button:hover,button[type=\'submit\']:hover,.pagination a:hover,input[type=\'checkbox\']:not(:active):checked:hover + label:before,input[type=\'checkbox\']:active + label:before,input[type=\'radio\']:checked:hover + label:before,input[type=\'radio\']:not(:checked):active + label:before{background-color:' . $colors['darken'] . '}';
  1652. $css .= '.helpButton span:hover{color:' . $colors['darken'] . '}';
  1653. $css .= '.button:active,button[type=\'submit\']:active,.pagination a:active{background-color:' . $colors['veryDarken'] . '}';
  1654. $colors = helper::colorVariants($this->getData(['theme', 'title', 'textColor']));
  1655. $css .= 'h1,h2,h3,h4,h5,h6,h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:' . $colors['normal'] . ';font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'title', 'font'])) . '",sans-serif;font-weight:' . $this->getData(['theme', 'title', 'fontWeight']) . ';text-transform:' . $this->getData(['theme', 'title', 'textTransform']) . '}';
  1656. $css .= 'h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color:' . $colors['darken'] . '}';
  1657. // Les blocs
  1658. $colors = helper::colorVariants($this->getData(['theme', 'block', 'backgroundColor']));
  1659. $css .= '.block {border: 1px solid ' . $this->getdata(['theme','block','borderColor']) . ';}.block h4 {background-color:'. $colors['normal'] . ';color:' . $colors['text'] .';}';
  1660. $css .= '.mce-tinymce {border: 1px solid ' . $this->getdata(['theme','block','borderColor']) .' !important;}';
  1661. // Bannière
  1662. $colors = helper::colorVariants($this->getData(['theme', 'header', 'backgroundColor']));
  1663. if($this->getData(['theme', 'header', 'margin'])) {
  1664. if($this->getData(['theme', 'menu', 'position']) === 'site-first') {
  1665. $css .= 'header{margin:0 20px}';
  1666. }
  1667. else {
  1668. $css .= 'header{margin:20px 20px 0 20px}';
  1669. }
  1670. }
  1671. $css .= 'header{background-size:' . $this->getData(['theme','header','imageContainer']).'}';
  1672. $css .= 'header{background-color:' . $colors['normal'];
  1673. // Valeur de hauteur traditionnelle
  1674. $css .= ';height:' . $this->getData(['theme', 'header', 'height']) . ';line-height:' . $this->getData(['theme', 'header', 'height']) ;
  1675. $css .= ';text-align:' . $this->getData(['theme', 'header', 'textAlign']) . '}';
  1676. if($themeHeaderImage = $this->getData(['theme', 'header', 'image'])) {
  1677. $css .= 'header{background-image:url("../file/source/' . $themeHeaderImage . '");background-position:' . $this->getData(['theme', 'header', 'imagePosition']) . ';background-repeat:' . $this->getData(['theme', 'header', 'imageRepeat']) . '}';
  1678. }
  1679. $colors = helper::colorVariants($this->getData(['theme', 'header', 'textColor']));
  1680. $css .= 'header span{color:' . $colors['normal'] . ';font-family:"' . str_replace('+', ' ', $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']) . '}';
  1681. // Menu
  1682. $colors = helper::colorVariants($this->getData(['theme', 'menu', 'backgroundColor']));
  1683. $css .= 'nav,nav.navMain a{background-color:' . $colors['normal'] . '}';
  1684. $css .= 'nav a,#toggle span,nav a:hover{color:' . $this->getData(['theme', 'menu', 'textColor']) . '}';
  1685. $css .= 'nav a:hover{background-color:' . $colors['darken'] . '}';
  1686. $css .= 'nav a.active{color:' . $this->getData(['theme','menu','activeTextColor']) . ';}';
  1687. if ($this->getData(['theme','menu','activeColorAuto']) === true) {
  1688. $css .= 'nav a.active{background-color:' . $colors['veryDarken'] . '}';
  1689. } else {
  1690. $css .= 'nav a.active{background-color:' . $this->getData(['theme','menu','activeColor']) . '}';
  1691. /*$color2 = helper::colorVariants($this->getData(['theme', 'menu', 'textColor']));
  1692. $css .= 'nav a.active{color:' . $color2['text'] . '}';*/
  1693. }
  1694. $css .= 'nav #burgerText{color:' . $colors['text'] . '}';
  1695. // Sous menu
  1696. $colors = helper::colorVariants($this->getData(['theme', 'menu', 'backgroundColorSub']));
  1697. $css .= 'nav .navSub a{background-color:' . $colors['normal'] . '}';
  1698. $css .= 'nav .navMain a.active {border-radius:' . $this->getData(['theme', 'menu', 'radius']) . '}';
  1699. $css .= '#menu{text-align:' . $this->getData(['theme', 'menu', 'textAlign']) . '}';
  1700. if($this->getData(['theme', 'menu', 'margin'])) {
  1701. if(
  1702. $this->getData(['theme', 'menu', 'position']) === 'site-first'
  1703. OR $this->getData(['theme', 'menu', 'position']) === 'site-second'
  1704. ) {
  1705. $css .= 'nav{padding:10px 10px 0 10px;}';
  1706. }
  1707. else {
  1708. $css .= 'nav{padding:0 10px}';
  1709. }
  1710. } else {
  1711. $css .= 'nav{margin:0}';
  1712. }
  1713. if(
  1714. $this->getData(['theme', 'menu', 'position']) === 'top'
  1715. ) {
  1716. $css .= 'nav{padding:0 10px;}';
  1717. }
  1718. $css .= '#toggle span,#menu a{padding:' . $this->getData(['theme', 'menu', 'height']) .';font-family:"' . str_replace('+', ' ', $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']) . '}';
  1719. // Pied de page
  1720. $colors = helper::colorVariants($this->getData(['theme', 'footer', 'backgroundColor']));
  1721. if($this->getData(['theme', 'footer', 'margin'])) {
  1722. $css .= 'footer{padding:0 20px;}';
  1723. } else {
  1724. $css .= 'footer{padding:0}';
  1725. }
  1726. $css .= 'footer span, #footerText > p {color:' . $this->getData(['theme', 'footer', 'textColor']) . ';font-family:"' . str_replace('+', ' ', $this->getData(['theme', 'footer', 'font'])) . '",sans-serif;font-weight:' . $this->getData(['theme', 'footer', 'fontWeight']) . ';font-size:' . $this->getData(['theme', 'footer', 'fontSize']) . ';text-transform:' . $this->getData(['theme', 'footer', 'textTransform']) . '}';
  1727. $css .= 'footer {background-color:' . $colors['normal'] . ';color:' . $this->getData(['theme', 'footer', 'textColor']) . '}';
  1728. $css .= 'footer a{color:' . $this->getData(['theme', 'footer', 'textColor']) . '}';
  1729. $css .= 'footer #footersite > div {margin:' . $this->getData(['theme', 'footer', 'height']) . ' 0}';
  1730. $css .= 'footer #footerbody > div {margin:' . $this->getData(['theme', 'footer', 'height']) . ' 0}';
  1731. $css .= '@media (max-width: 768px) {footer #footerbody > div { padding: 2px }}';
  1732. $css .= '#footerSocials{text-align:' . $this->getData(['theme', 'footer', 'socialsAlign']) . '}';
  1733. $css .= '#footerText > p {text-align:' . $this->getData(['theme', 'footer', 'textAlign']) . '}';
  1734. $css .= '#footerCopyright{text-align:' . $this->getData(['theme', 'footer', 'copyrightAlign']) . '}';
  1735. // Enregistre la personnalisation
  1736. file_put_contents(self::DATA_DIR.'theme.css', $css);
  1737. // Effacer le cache pour tenir compte de la couleur de fond TinyMCE
  1738. header("Expires: Tue, 01 Jan 2000 00:00:00 GMT");
  1739. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
  1740. header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
  1741. header("Cache-Control: post-check=0, pre-check=0", false);
  1742. header("Pragma: no-cache");
  1743. }
  1744. // Check la version rafraichissement du theme admin
  1745. $cssVersion = preg_split('/\*+/', file_get_contents(self::DATA_DIR.'admin.css'));
  1746. if(empty($cssVersion[1]) OR $cssVersion[1] !== md5(json_encode($this->getData(['admin'])))) {
  1747. // Version
  1748. $css = '/*' . md5(json_encode($this->getData(['admin']))) . '*/';
  1749. $colors = helper::colorVariants($this->getData(['admin','backgroundColor']));
  1750. $css .= '#site{background-color:' . $colors['normal']. ';}';
  1751. $css .= '.row > div {font:' . $this->getData(['admin','fontSize']) . ' "' . $this->getData(['admin','fontText']) . '", sans-serif;}';
  1752. $css .= 'body h1, h2, h3, h4 a, h5, h6 {font-family:' . $this->getData(['admin','fontTitle' ]) . ', sans-serif;color:' . $this->getData(['admin','colorTitle' ]) . ';}';
  1753. $css .= 'body:not(.editorWysiwyg),span .zwiico-help {color:' . $this->getData(['admin','colorText']) . ';}';
  1754. $css .= 'table thead tr, table thead tr .zwiico-help{ background-color:'.$this->getData(['admin','colorText']).'; color:'.$colors['normal'].';}';
  1755. $colors = helper::colorVariants($this->getData(['admin','backgroundColorButton']));
  1756. $css .= 'input[type="checkbox"]:checked + label::before,.speechBubble{background-color:' . $colors['normal'] . ';color:' . $colors['text'] . ';}';
  1757. $css .= '.speechBubble::before {border-color:' . $colors['normal'] . ' transparent transparent transparent;}';
  1758. $css .= '.button {background-color:' . $colors['normal'] . ';color:' . $colors['text'] . ';}.button:hover {background-color:' . $colors['darken'] . ';color:' . $colors['text'] . ';}.button:active {background-color:' . $colors['veryDarken'] . ';color:' . $colors['text'] . ';}';
  1759. $colors = helper::colorVariants($this->getData(['admin','backgroundColorButtonGrey']));
  1760. $css .= '.button.buttonGrey {background-color: ' . $colors['normal'] . ';color: ' . $colors['text'] . ';}.button.buttonGrey:hover {background-color:' . $colors['darken'] . ';color:' . $colors['text'] . ';}.button.buttonGrey:active {background-color:' . $colors['veryDarken'] . ';color:' . $colors['text'] . ';}';
  1761. $colors = helper::colorVariants($this->getData(['admin','backgroundColorButtonRed']));
  1762. $css .= '.button.buttonRed {background-color: ' . $colors['normal'] . ';color: ' . $colors['text'] . ';}.button.buttonRed:hover {background-color:' . $colors['darken'] . ';color:' . $colors['text'] . ';}.button.buttonRed:active {background-color:' . $colors['veryDarken'] . ';color:' . $colors['text'] . ';}';
  1763. $colors = helper::colorVariants($this->getData(['admin','backgroundColorButtonHelp']));
  1764. $css .= '.button.buttonHelp {background-color: ' . $colors['normal'] . ';color: ' . $colors['text'] . ';}.button.buttonHelp:hover {background-color:' . $colors['darken'] . ';color:' . $colors['text'] . ';}.button.buttonHelp:active {background-color:' . $colors['veryDarken'] . ';color:' . $colors['text'] . ';}';
  1765. $colors = helper::colorVariants($this->getData(['admin','backgroundColorButtonGreen']));
  1766. $css .= '.button.buttonGreen, button[type=submit] {background-color: ' . $colors['normal'] . ';color: ' . $colors['text'] . ';}.button.buttonGreen:hover, button[type=submit]:hover {background-color: ' . $colors['darken'] . ';color: ' . $colors['text'] .';}.button.buttonGreen:active, button[type=submit]:active {background-color: ' . $colors['darken'] . ';color: ' .$colors['text'] .';}';
  1767. $colors = helper::colorVariants($this->getData(['admin','backgroundBlockColor']));
  1768. $css .= '.block {border: 1px solid ' . $this->getData(['admin','borderBlockColor']) . ';}.block h4 {background-color: ' . $colors['normal'] . ';color:' . $colors['text'] . ';}';
  1769. $css .= 'table tr,input[type=email],input[type=text],input[type=password],select:not(#barSelectPage),textarea:not(.editorWysiwyg),.inputFile{background-color: ' . $colors['normal'] . ';color:' . $colors['text'] . ';border: 1px solid ' . $this->getData(['admin','borderBlockColor']) . ';}';
  1770. // Bordure du contour TinyMCE
  1771. $css .= '.mce-tinymce{border: 1px solid '. $this->getData(['admin','borderBlockColor']) . '!important;}';
  1772. // Enregistre la personnalisation
  1773. file_put_contents(self::DATA_DIR.'admin.css', $css);
  1774. }
  1775. }
  1776. /**
  1777. * Auto-chargement des classes
  1778. * @param string $className Nom de la classe à charger
  1779. */
  1780. public static function autoload($className) {
  1781. $classPath = strtolower($className) . '/' . strtolower($className) . '.php';
  1782. // Module du coeur
  1783. if(is_readable('core/module/' . $classPath)) {
  1784. require 'core/module/' . $classPath;
  1785. }
  1786. // Module
  1787. elseif(is_readable('module/' . $classPath)) {
  1788. require 'module/' . $classPath;
  1789. }
  1790. // Librairie
  1791. elseif(is_readable('core/vendor/' . $classPath)) {
  1792. require 'core/vendor/' . $classPath;
  1793. }
  1794. }
  1795. /**
  1796. * Routage des modules
  1797. */
  1798. public function router() {
  1799. // Installation
  1800. if(
  1801. $this->getData(['user']) === []
  1802. AND $this->getUrl(0) !== 'install'
  1803. ) {
  1804. http_response_code(302);
  1805. header('Location:' . helper::baseUrl() . 'install');
  1806. exit();
  1807. }
  1808. // Journalisation
  1809. $dataLog = mb_detect_encoding(strftime('%d/%m/%y',time()), 'UTF-8', true)
  1810. ? strftime('%d/%m/%y',time()) . ';' . strftime('%R',time()) . ';'
  1811. : utf8_encode(strftime('%d/%m/%y',time())) . ';' . utf8_encode(strftime('%R',time())) . ';' ;
  1812. $dataLog .= helper::getIp() . ';';
  1813. $dataLog .= $this->getUser('id') ? $this->getUser('id') . ';' : 'anonyme' . ';';
  1814. $dataLog .= $this->getUrl();
  1815. $dataLog .= PHP_EOL;
  1816. if ($this->getData(['config','connect','log'])) {
  1817. file_put_contents(self::DATA_DIR . 'journal.log', $dataLog, FILE_APPEND);
  1818. }
  1819. // Force la déconnexion des membres bannis ou d'une seconde session
  1820. if (
  1821. $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  1822. AND ( $this->getUser('group') === self::GROUP_BANNED
  1823. OR $_SESSION['csrf'] !== $this->getData(['user',$this->getUser('id'),'accessCsrf']) )
  1824. ) {
  1825. $user = new user;
  1826. $user->logout();
  1827. }
  1828. // Mode maintenance
  1829. if(
  1830. $this->getData(['config', 'maintenance'])
  1831. AND in_array($this->getUrl(0), ['maintenance', 'user']) === false
  1832. AND $this->getUrl(1) !== 'login'
  1833. AND (
  1834. $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
  1835. OR (
  1836. $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  1837. AND $this->getUser('group') < self::GROUP_ADMIN
  1838. )
  1839. )
  1840. ) {
  1841. // Déconnexion
  1842. $user = new user;
  1843. $user->logout();
  1844. // Redirection
  1845. http_response_code(302);
  1846. header('Location:' . helper::baseUrl() . 'maintenance');
  1847. exit();
  1848. }
  1849. // Check l'accès à la page
  1850. $access = null;
  1851. $accessInfo['userName'] = '';
  1852. $accessInfo['pageId'] = '';
  1853. if($this->getData(['page', $this->getUrl(0)]) !== null) {
  1854. if(
  1855. $this->getData(['page', $this->getUrl(0), 'group']) === self::GROUP_VISITOR
  1856. OR (
  1857. $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  1858. AND $this->getUser('group') >= $this->getData(['page', $this->getUrl(0), 'group'])
  1859. )
  1860. ) {
  1861. $access = true;
  1862. }
  1863. else {
  1864. if($this->getUrl(0) === $this->getData(['locale', 'homePageId'])) {
  1865. $access = 'login';
  1866. }
  1867. else {
  1868. $access = false;
  1869. }
  1870. }
  1871. // Empêcher l'accès aux pages désactivées par URL directe
  1872. if ( ( $this->getData(['page', $this->getUrl(0),'disable']) === true
  1873. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
  1874. ) OR (
  1875. $this->getData(['page', $this->getUrl(0),'disable']) === true
  1876. AND $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  1877. AND $this->getUser('group') < self::GROUP_MODERATOR
  1878. )
  1879. ){
  1880. $access = false;
  1881. }
  1882. }
  1883. /**
  1884. * Contrôle si la page demandée est en édition ou accès à la gestion du site
  1885. * conditions de blocage :
  1886. * - Les deux utilisateurs qui accèdent à la même page sont différents
  1887. * - les URLS sont identiques
  1888. * - Une partie de l'URL fait partie de la liste de filtrage (édition d'un module etc..)
  1889. * - L'édition est ouverte depuis un temps dépassé, on considère que la page est restée ouverte et qu'elle ne sera pas validée
  1890. */
  1891. foreach($this->getData(['user']) as $userId => $userIds){
  1892. $t = explode('/',$this->getData(['user', $userId, 'accessUrl']));
  1893. if ( $this->getUser('id') &&
  1894. $userId !== $this->getUser('id') &&
  1895. $this->getData(['user', $userId,'accessUrl']) === $this->getUrl() &&
  1896. array_intersect($t,self::$accessList) &&
  1897. array_intersect($t,self::$accessExclude) !== false &&
  1898. time() < $this->getData(['user', $userId,'accessTimer']) + self::ACCESS_TIMER
  1899. ) {
  1900. $access = false;
  1901. $accessInfo['userName'] = $this->getData(['user', $userId, 'lastname']) . ' ' . $this->getData(['user', $userId, 'firstname']);
  1902. $accessInfo['pageId'] = end($t);
  1903. }
  1904. }
  1905. // Accès concurrent stocke la page visitée
  1906. if ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  1907. AND $this->getUser('id') ) {
  1908. $this->setData(['user',$this->getUser('id'),'accessUrl',$this->getUrl()]);
  1909. $this->setData(['user',$this->getUser('id'),'accessTimer',time()]);
  1910. }
  1911. // Breadcrumb
  1912. $title = $this->getData(['page', $this->getUrl(0), 'title']);
  1913. if (!empty($this->getData(['page', $this->getUrl(0), 'parentPageId'])) &&
  1914. $this->getData(['page', $this->getUrl(0), 'breadCrumb'])) {
  1915. $title = '<a href="' . helper::baseUrl() .
  1916. $this->getData(['page', $this->getUrl(0), 'parentPageId']) .
  1917. '">' .
  1918. ucfirst($this->getData(['page',$this->getData(['page', $this->getUrl(0), 'parentPageId']), 'title'])) .
  1919. '</a> &#8250; '.
  1920. $this->getData(['page', $this->getUrl(0), 'title']);
  1921. }
  1922. // Importe la page
  1923. if(
  1924. $this->getData(['page', $this->getUrl(0)]) !== null
  1925. AND $this->getData(['page', $this->getUrl(0), 'moduleId']) === ''
  1926. AND $access
  1927. ) {
  1928. $this->addOutput([
  1929. 'title' => $title,
  1930. 'content' => $this->getData(['page', $this->getUrl(0), 'content']),
  1931. 'metaDescription' => $this->getData(['page', $this->getUrl(0), 'metaDescription']),
  1932. 'metaTitle' => $this->getData(['page', $this->getUrl(0), 'metaTitle']),
  1933. 'typeMenu' => $this->getData(['page', $this->getUrl(0), 'typeMenu']),
  1934. 'iconUrl' => $this->getData(['page', $this->getUrl(0), 'iconUrl']),
  1935. 'disable' => $this->getData(['page', $this->getUrl(0), 'disable']),
  1936. 'contentRight' => $this->getData(['page',$this->getData(['page',$this->getUrl(0),'barRight']),'content']),
  1937. 'contentLeft' => $this->getData(['page',$this->getData(['page',$this->getUrl(0),'barLeft']),'content']),
  1938. ]);
  1939. }
  1940. // Importe le module
  1941. else {
  1942. // Id du module, et valeurs en sortie de la page si il s'agit d'un module de page
  1943. if($access AND $this->getData(['page', $this->getUrl(0), 'moduleId'])) {
  1944. $moduleId = $this->getData(['page', $this->getUrl(0), 'moduleId']);
  1945. $this->addOutput([
  1946. 'title' => $title,
  1947. // Meta description = 160 premiers caractères de l'article
  1948. 'metaDescription' => $this->getData(['page',$this->getUrl(0),'moduleId']) === 'blog' && !empty($this->getUrl(1))
  1949. ? strip_tags(substr($this->getData(['module',$this->getUrl(0),'posts',$this->getUrl(1),'content']) ,0,159))
  1950. : $this->getData(['page', $this->getUrl(0), 'metaDescription']),
  1951. 'metaTitle' => $this->getData(['page', $this->getUrl(0), 'metaTitle']),
  1952. 'typeMenu' => $this->getData(['page', $this->getUrl(0), 'typeMenu']),
  1953. 'iconUrl' => $this->getData(['page', $this->getUrl(0), 'iconUrl']),
  1954. 'disable' => $this->getData(['page', $this->getUrl(0), 'disable']),
  1955. 'contentRight' => $this->getData(['page',$this->getData(['page',$this->getUrl(0),'barRight']),'content']),
  1956. 'contentLeft' => $this->getData(['page',$this->getData(['page',$this->getUrl(0),'barLeft']),'content'])
  1957. ]);
  1958. $pageContent = $this->getData(['page', $this->getUrl(0), 'content']);
  1959. }
  1960. else {
  1961. $moduleId = $this->getUrl(0);
  1962. $pageContent = '';
  1963. }
  1964. // Check l'existence du module
  1965. if(class_exists($moduleId)) {
  1966. /** @var common $module */
  1967. $module = new $moduleId;
  1968. // Check l'existence de l'action
  1969. $action = '';
  1970. $ignore = true;
  1971. foreach(explode('-', $this->getUrl(1)) as $actionPart) {
  1972. if($ignore) {
  1973. $action .= $actionPart;
  1974. $ignore = false;
  1975. }
  1976. else {
  1977. $action .= ucfirst($actionPart);
  1978. }
  1979. }
  1980. $action = array_key_exists($action, $module::$actions) ? $action : 'index';
  1981. if(array_key_exists($action, $module::$actions)) {
  1982. $module->$action();
  1983. $output = $module->output;
  1984. // Check le groupe de l'utilisateur
  1985. if(
  1986. (
  1987. $module::$actions[$action] === self::GROUP_VISITOR
  1988. OR (
  1989. $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  1990. AND $this->getUser('group') >= $module::$actions[$action]
  1991. )
  1992. )
  1993. AND $output['access'] === true
  1994. ) {
  1995. // Enregistrement du contenu de la méthode POST lorsqu'une notice est présente
  1996. if(common::$inputNotices) {
  1997. foreach($_POST as $postId => $postValue) {
  1998. if(is_array($postValue)) {
  1999. foreach($postValue as $subPostId => $subPostValue) {
  2000. self::$inputBefore[$postId . '_' . $subPostId] = $subPostValue;
  2001. }
  2002. }
  2003. else {
  2004. self::$inputBefore[$postId] = $postValue;
  2005. }
  2006. }
  2007. }
  2008. // Sinon traitement des données de sortie qui requiert qu'aucune notice ne soit présente
  2009. else {
  2010. // Notification
  2011. if($output['notification']) {
  2012. if($output['state'] === true) {
  2013. $notification = 'ZWII_NOTIFICATION_SUCCESS';
  2014. }
  2015. elseif($output['state'] === false) {
  2016. $notification = 'ZWII_NOTIFICATION_ERROR';
  2017. }
  2018. else {
  2019. $notification = 'ZWII_NOTIFICATION_OTHER';
  2020. }
  2021. $_SESSION[$notification] = $output['notification'];
  2022. }
  2023. // Redirection
  2024. if($output['redirect']) {
  2025. http_response_code(301);
  2026. header('Location:' . $output['redirect']);
  2027. exit();
  2028. }
  2029. }
  2030. // Données en sortie applicables même lorsqu'une notice est présente
  2031. // Affichage
  2032. if($output['display']) {
  2033. $this->addOutput([
  2034. 'display' => $output['display']
  2035. ]);
  2036. }
  2037. // Contenu brut
  2038. if($output['content']) {
  2039. $this->addOutput([
  2040. 'content' => $output['content']
  2041. ]);
  2042. }
  2043. // Contenu par vue
  2044. elseif($output['view']) {
  2045. // Chemin en fonction d'un module du coeur ou d'un module
  2046. $modulePath = in_array($moduleId, self::$coreModuleIds) ? 'core/' : '';
  2047. // CSS
  2048. $stylePath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.css';
  2049. if(file_exists($stylePath)) {
  2050. $this->addOutput([
  2051. 'style' => file_get_contents($stylePath)
  2052. ]);
  2053. }
  2054. if ($output['style']) {
  2055. $this->addOutput([
  2056. 'style' => $this->output['style'] . file_get_contents($output['style'])
  2057. ]);
  2058. }
  2059. // JS
  2060. $scriptPath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.js.php';
  2061. if(file_exists($scriptPath)) {
  2062. ob_start();
  2063. include $scriptPath;
  2064. $this->addOutput([
  2065. 'script' => ob_get_clean()
  2066. ]);
  2067. }
  2068. // Vue
  2069. $viewPath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.php';
  2070. if(file_exists($viewPath)) {
  2071. ob_start();
  2072. include $viewPath;
  2073. $modpos = $this->getData(['page', $this->getUrl(0), 'modulePosition']);
  2074. if ($modpos === 'top') {
  2075. $this->addOutput([
  2076. 'content' => ob_get_clean() . ($output['showPageContent'] ? $pageContent : '')]);
  2077. }
  2078. else if ($modpos === 'free') {
  2079. if ( strstr($pageContent, '[MODULE]', true) === false) {
  2080. $begin = strstr($pageContent, '[]', true); } else {
  2081. $begin = strstr($pageContent, '[MODULE]', true);
  2082. }
  2083. if ( strstr($pageContent, '[MODULE]') === false ) {
  2084. $end = strstr($pageContent, '[]');} else {
  2085. $end = strstr($pageContent, '[MODULE]');
  2086. }
  2087. $cut=8;
  2088. $end=substr($end,-strlen($end)+$cut);
  2089. $this->addOutput([
  2090. 'content' => ($output['showPageContent'] ? $begin : '') . ob_get_clean() . ($output['showPageContent'] ? $end : '')]); }
  2091. else {
  2092. $this->addOutput([
  2093. 'content' => ($output['showPageContent'] ? $pageContent : '') . ob_get_clean()]);
  2094. }
  2095. }
  2096. }
  2097. // Librairies
  2098. if($output['vendor'] !== $this->output['vendor']) {
  2099. $this->addOutput([
  2100. 'vendor' => array_merge($this->output['vendor'], $output['vendor'])
  2101. ]);
  2102. }
  2103. if($output['title'] !== null) {
  2104. $this->addOutput([
  2105. 'title' => $output['title']
  2106. ]);
  2107. }
  2108. // Affiche le bouton d'édition de la page dans la barre de membre
  2109. if($output['showBarEditButton']) {
  2110. $this->addOutput([
  2111. 'showBarEditButton' => $output['showBarEditButton']
  2112. ]);
  2113. }
  2114. }
  2115. // Erreur 403
  2116. else {
  2117. $access = false;
  2118. }
  2119. }
  2120. }
  2121. }
  2122. // Erreurs
  2123. if($access === 'login') {
  2124. http_response_code(302);
  2125. header('Location:' . helper::baseUrl() . 'user/login/');
  2126. exit();
  2127. }
  2128. if($access === false) {
  2129. http_response_code(403);
  2130. if ($accessInfo['userName']) {
  2131. $this->addOutput([
  2132. 'title' => 'Accès verrouillé',
  2133. 'content' => template::speech('La page <strong>' . $accessInfo['pageId'] . '</strong> est ouverte par l\'utilisateur <strong>' . $accessInfo['userName'] . '</strong>')
  2134. ]);
  2135. } else {
  2136. if ( $this->getData(['locale','page403']) !== 'none'
  2137. AND $this->getData(['page',$this->getData(['locale','page403'])]))
  2138. {
  2139. header('Location:' . helper::baseUrl() . $this->getData(['locale','page403']));
  2140. } else {
  2141. $this->addOutput([
  2142. 'title' => 'Accès interdit',
  2143. 'content' => template::speech('Vous n\'êtes pas autorisé à consulter cette page (erreur 403)')
  2144. ]);
  2145. }
  2146. }
  2147. } elseif ($this->output['content'] === '') {
  2148. http_response_code(404);
  2149. if ( $this->getData(['locale','page404']) !== 'none'
  2150. AND $this->getData(['page',$this->getData(['locale','page404'])]))
  2151. {
  2152. header('Location:' . helper::baseUrl() . $this->getData(['locale','page404']));
  2153. } else {
  2154. $this->addOutput([
  2155. 'title' => 'Page indisponible',
  2156. 'content' => template::speech('Oups ! La page demandée n\'existe pas ou est introuvable (erreur 404)')
  2157. ]);
  2158. }
  2159. }
  2160. // Mise en forme des métas
  2161. if($this->output['metaTitle'] === '') {
  2162. if($this->output['title']) {
  2163. $this->addOutput([
  2164. 'metaTitle' => strip_tags($this->output['title']) . ' - ' . $this->getData(['locale', 'title'])
  2165. ]);
  2166. }
  2167. else {
  2168. $this->addOutput([
  2169. 'metaTitle' => $this->getData(['locale', 'title'])
  2170. ]);
  2171. }
  2172. }
  2173. if($this->output['metaDescription'] === '') {
  2174. $this->addOutput([
  2175. 'metaDescription' => $this->getData(['locale', 'metaDescription'])
  2176. ]);
  2177. }
  2178. switch($this->output['display']) {
  2179. // Layout vide
  2180. case self::DISPLAY_LAYOUT_BLANK:
  2181. require 'core/layout/blank.php';
  2182. break;
  2183. // Affichage en JSON
  2184. case self::DISPLAY_JSON:
  2185. header('Content-Type: application/json');
  2186. echo json_encode($this->output['content']);
  2187. break;
  2188. // RSS feed
  2189. case self::DISPLAY_RSS:
  2190. header('Content-type: application/rss+xml; charset=UTF-8');
  2191. echo $this->output['content'];
  2192. break;
  2193. // Layout allégé
  2194. case self::DISPLAY_LAYOUT_LIGHT:
  2195. require 'core/layout/light.php';
  2196. break;
  2197. // Layout principal
  2198. case self::DISPLAY_LAYOUT_MAIN:
  2199. require 'core/layout/main.php';
  2200. break;
  2201. // Layout brut
  2202. case self::DISPLAY_RAW:
  2203. echo $this->output['content'];
  2204. break;
  2205. }
  2206. }
  2207. }
  2208. class layout extends common {
  2209. private $core;
  2210. /**
  2211. * Constructeur du layout
  2212. */
  2213. public function __construct(core $core) {
  2214. parent::__construct();
  2215. $this->core = $core;
  2216. }
  2217. /**
  2218. * Affiche le script Google Analytics
  2219. */
  2220. public function showAnalytics() {
  2221. if($code = $this->getData(['config', 'analyticsId'])
  2222. AND $this->getInput('ZWII_COOKIE_CONSENT') === 'true') {
  2223. echo '<!-- Global site tag (gtag.js) - Google Analytics -->
  2224. <script async src="https://www.googletagmanager.com/gtag/js?id='. $code .'"></script>
  2225. <script>
  2226. window.dataLayer = window.dataLayer || [];
  2227. function gtag(){dataLayer.push(arguments);}
  2228. gtag("js", new Date());
  2229. gtag("config","'. $code .'",{ "anonymize_ip": true });
  2230. </script>';
  2231. }
  2232. }
  2233. /**
  2234. * Affiche le contenu
  2235. * @param Page par défaut
  2236. */
  2237. public function showContent() {
  2238. if(
  2239. $this->core->output['title']
  2240. AND (
  2241. $this->getData(['page', $this->getUrl(0)]) === null
  2242. OR $this->getData(['page', $this->getUrl(0), 'hideTitle']) === false
  2243. OR $this->getUrl(1) === 'config'
  2244. )
  2245. ) {
  2246. echo '<h1 id="sectionTitle">' . $this->core->output['title'] . '</h1>';
  2247. }
  2248. echo $this->core->output['content'];
  2249. }
  2250. /**
  2251. * Affiche le contenu de la barre gauche
  2252. *
  2253. */
  2254. public function showBarContentLeft() {
  2255. // Détermine si le menu est présent
  2256. if ($this->getData(['page',$this->getData(['page',$this->getUrl(0),'barLeft']),'displayMenu']) === 'none') {
  2257. // Pas de menu
  2258. echo $this->core->output['contentLeft'];
  2259. } else {
  2260. // $mark contient 0 le menu est positionné à la fin du contenu
  2261. $contentLeft = str_replace ('[]','[MENU]',$this->core->output['contentLeft']);
  2262. $contentLeft = str_replace ('[menu]','[MENU]',$contentLeft);
  2263. $mark = strrpos($contentLeft,'[MENU]') !== false ? strrpos($contentLeft,'[MENU]') : strlen($contentLeft);
  2264. echo substr($contentLeft,0,$mark);
  2265. echo '<div id="menuSideLeft">';
  2266. echo $this->showMenuSide($this->getData(['page',$this->getData(['page',$this->getUrl(0),'barLeft']),'displayMenu']) === 'parents' ? false : true);
  2267. echo '</div>';
  2268. echo substr($contentLeft,$mark+6,strlen($contentLeft));
  2269. }
  2270. }
  2271. /**
  2272. * Affiche le contenu de la barre droite
  2273. */
  2274. public function showBarContentRight() {
  2275. // Détermine si le menu est présent
  2276. if ($this->getData(['page',$this->getData(['page',$this->getUrl(0),'barRight']),'displayMenu']) === 'none') {
  2277. // Pas de menu
  2278. echo $this->core->output['contentRight'];
  2279. } else {
  2280. // $mark contient 0 le menu est positionné à la fin du contenu
  2281. $contentRight = str_replace ('[]','[MENU]',$this->core->output['contentRight']);
  2282. $contentRight = str_replace ('[menu]','[MENU]',$contentRight);
  2283. $mark = strrpos($contentRight,'[MENU]') !== false ? strrpos($contentRight,'[MENU]') : strlen($contentRight);
  2284. echo substr($contentRight,0,$mark);
  2285. echo '<div id="menuSideRight">';
  2286. echo $this->showMenuSide($this->getData(['page',$this->getData(['page',$this->getUrl(0),'barRight']),'displayMenu']) === 'parents' ? false : true);
  2287. echo '</div>';
  2288. echo substr($contentRight,$mark+6,strlen($contentRight));
  2289. }
  2290. }
  2291. /**
  2292. * Affiche le texte du footer
  2293. */
  2294. public function showFooterText() {
  2295. if($footerText = $this->getData(['theme', 'footer', 'text']) OR $this->getUrl(0) === 'theme') {
  2296. echo '<div id="footerText">' . $footerText . '</div>';
  2297. }
  2298. }
  2299. /**
  2300. * Affiche le copyright
  2301. */
  2302. public function showCopyright() {
  2303. // Ouverture Bloc copyright
  2304. $items = '<div id="footerCopyright">';
  2305. $items .= '<span id="footerFontCopyright">';
  2306. // Affichage de motorisé par
  2307. $items .= '<span id="footerDisplayCopyright" ';
  2308. $items .= $this->getData(['theme','footer','displayCopyright']) === false ? 'class="displayNone"' : '';
  2309. $items .= '>Motorisé&nbsp;par&nbsp;</span>';
  2310. // Toujours afficher le nom du CMS
  2311. $items .= '<span id="footerZwiiCMS">';
  2312. $items .= '<a href="https://zwiicms.fr/" onclick="window.open(this.href);return false" data-tippy-content="Zwii CMS sans base de données, très léger et performant">ZwiiCMS</a>';
  2313. $items .= '</span>';
  2314. // Affichage du numéro de version
  2315. $items .= '<span id="footerDisplayVersion"';
  2316. $items .= $this->getData(['theme','footer','displayVersion']) === false ? ' class="displayNone"' : '';
  2317. $items .= '><wbr>&nbsp;'. common::ZWII_VERSION ;
  2318. $items .= '</span>';
  2319. // Affichage du sitemap
  2320. $items .= '<span id="footerDisplaySiteMap"';
  2321. $items .= $this->getData(['theme','footer','displaySiteMap']) === false ? ' class="displayNone"' : '';
  2322. $items .= '><wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . 'sitemap" data-tippy-content="Plan du site" >Plan&nbsp;du&nbsp;site</a>';
  2323. $items .= '</span>';
  2324. // Affichage du module de recherche
  2325. $items .= '<span id="footerDisplaySearch"';
  2326. $items .= $this->getData(['theme','footer','displaySearch']) === false ? ' class="displayNone" >' : '>';
  2327. if ($this->getData(['locale','searchPageId']) !== 'none') {
  2328. $items .= '<wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . $this->getData(['locale','searchPageId']) . '" data-tippy-content="Rechercher dans le site" >Recherche</a>';
  2329. }
  2330. $items .= '</span>';
  2331. // Affichage des mentions légales
  2332. $items .= '<span id="footerDisplayLegal"';
  2333. $items .= $this->getData(['theme','footer','displayLegal']) === false ? ' class="displayNone" >' : '>';
  2334. if ($this->getData(['locale','legalPageId']) !== 'none') {
  2335. $items .= '<wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . $this->getData(['locale','legalPageId']) . '" data-tippy-content="Mentions légales">Mentions légales</a>';
  2336. }
  2337. $items .= '</span>';
  2338. // Affichage du lien de connexion
  2339. if(
  2340. (
  2341. $this->getData(['theme', 'footer', 'loginLink'])
  2342. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
  2343. )
  2344. OR $this->getUrl(0) === 'theme'
  2345. ) {
  2346. $items .= '<span id="footerLoginLink" ' .
  2347. ($this->getUrl(0) === 'theme' ? 'class="displayNone"' : '') .
  2348. '><wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . 'user/login/' .
  2349. strip_tags(str_replace('/', '_', $this->getUrl())) .
  2350. '" data-tippy-content="Connexion à l\'administration" rel="nofollow">' . template::ico('login') .'</a></span>';
  2351. }
  2352. // Affichage de la barre de membre simple
  2353. if ( $this->getUser('group') === self::GROUP_MEMBER
  2354. && $this->getData(['theme','footer','displayMemberBar']) === true
  2355. ) {
  2356. $items .= '<span id="footerDisplayMemberAccount"';
  2357. $items .= $this->getData(['theme','footer','displaymemberAccount']) === false ? ' class="displayNone"' : '';
  2358. $items .= '><wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . 'user/edit/' . $this->getUser('id'). '/' . $_SESSION['csrf'] . '" data-tippy-content="Gérer mon compte" >' . template::ico('user', 'all') . '</a>';
  2359. $items .= '<wbr><a id="barLogout" href="' . helper::baseUrl() . 'user/logout" data-tippy-content="Me déconnecter">' . template::ico('logout','left') . '</a>';
  2360. $items .= '</span>';
  2361. }
  2362. // Fermeture du bloc copyright
  2363. $items .= '</span></div>';
  2364. echo $items;
  2365. }
  2366. /**
  2367. * Affiche les réseaux sociaux
  2368. */
  2369. public function showSocials() {
  2370. $socials = '';
  2371. foreach($this->getData(['config', 'social']) as $socialName => $socialId) {
  2372. switch($socialName) {
  2373. case 'facebookId':
  2374. $socialUrl = 'https://www.facebook.com/';
  2375. $title = 'Facebook';
  2376. break;
  2377. case 'linkedinId':
  2378. $socialUrl = 'https://fr.linkedin.com/in/';
  2379. $title = 'Linkedin';
  2380. break;
  2381. case 'instagramId':
  2382. $socialUrl = 'https://www.instagram.com/';
  2383. $title = 'Instagram';
  2384. break;
  2385. case 'pinterestId':
  2386. $socialUrl = 'https://pinterest.com/';
  2387. $title = 'Pinterest';
  2388. break;
  2389. case 'twitterId':
  2390. $socialUrl = 'https://twitter.com/';
  2391. $title = 'Twitter';
  2392. break;
  2393. case 'youtubeId':
  2394. $socialUrl = 'https://www.youtube.com/channel/';
  2395. $title = 'Chaîne YouTube';
  2396. break;
  2397. case 'youtubeUserId':
  2398. $socialUrl = 'https://www.youtube.com/user/';
  2399. $title = 'YouTube';
  2400. break;
  2401. case 'githubId':
  2402. $socialUrl = 'https://www.github.com/';
  2403. $title = 'Github';
  2404. break;
  2405. default:
  2406. $socialUrl = '';
  2407. }
  2408. if($socialId !== '') {
  2409. $socials .= '<a href="' . $socialUrl . $socialId . '" onclick="window.open(this.href);return false" data-tippy-content="' . $title . '">' . template::ico(substr(str_replace('User','',$socialName), 0, -2)) . '</a>';
  2410. }
  2411. }
  2412. if($socials !== '') {
  2413. echo '<div id="footerSocials">' . $socials . '</div>';
  2414. }
  2415. }
  2416. /**
  2417. * Affiche le favicon
  2418. */
  2419. public function showFavicon() {
  2420. // Light scheme
  2421. $favicon = $this->getData(['config', 'favicon']);
  2422. if($favicon &&
  2423. file_exists(self::FILE_DIR.'source/' . $favicon)
  2424. ) {
  2425. echo '<link rel="shortcut icon" media="(prefers-color-scheme:light)" href="' . helper::baseUrl(false) . self::FILE_DIR.'source/' . $favicon . '">';
  2426. } else {
  2427. echo '<link rel="shortcut icon" media="(prefers-color-scheme:light)" href="' . helper::baseUrl(false) . 'core/vendor/zwiico/ico/favicon.ico">';
  2428. }
  2429. // Dark scheme
  2430. $faviconDark = $this->getData(['config', 'faviconDark']);
  2431. if(!empty($faviconDark) &&
  2432. file_exists(self::FILE_DIR.'source/' . $faviconDark)
  2433. ) {
  2434. echo '<link rel="shortcut icon" media="(prefers-color-scheme:dark)" href="' . helper::baseUrl(false) . self::FILE_DIR.'source/' . $faviconDark . '">';
  2435. //echo '<script src="https://unpkg.com/favicon-switcher@1.2.2/dist/index.js" crossorigin="anonymous" type="application/javascript"></script>';
  2436. echo '<script src="' . helper::baseUrl(false) . 'core/vendor/favicon-switcher/favicon-switcher.js" crossorigin="anonymous" type="application/javascript"></script>';
  2437. }
  2438. }
  2439. /**
  2440. * Affiche le menu
  2441. */
  2442. public function showMenu() {
  2443. // Met en forme les items du menu
  2444. $itemsLeft = '';
  2445. $currentPageId = $this->getData(['page', $this->getUrl(0)]) ? $this->getUrl(0) : $this->getUrl(2);
  2446. foreach($this->getHierarchy() as $parentPageId => $childrenPageIds) {
  2447. // Passer les entrées masquées
  2448. // Propriétés de l'item
  2449. $active = ($parentPageId === $currentPageId OR in_array($currentPageId, $childrenPageIds)) ? 'active ' : '';
  2450. $targetBlank = $this->getData(['page', $parentPageId, 'targetBlank']) ? ' target="_blank"' : '';
  2451. // Mise en page de l'item
  2452. $itemsLeft .= '<li>';
  2453. if ( ( $this->getData(['page',$parentPageId,'disable']) === true
  2454. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
  2455. ) OR (
  2456. $this->getData(['page',$parentPageId,'disable']) === true
  2457. AND $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  2458. AND $this->getUser('group') < self::GROUP_MODERATOR
  2459. )
  2460. ){
  2461. $itemsLeft .= '<a class="' . $parentPageId . '" href="'. helper::baseUrl() . $this->getUrl(0).'">';
  2462. } else {
  2463. $itemsLeft .= '<a class="' . $active . $parentPageId . '" href="' . helper::baseUrl() . $parentPageId . '"' . $targetBlank . '>';
  2464. }
  2465. switch ($this->getData(['page', $parentPageId, 'typeMenu'])) {
  2466. case '' :
  2467. $itemsLeft .= $this->getData(['page', $parentPageId, 'title']);
  2468. break;
  2469. case 'text' :
  2470. $itemsLeft .= $this->getData(['page', $parentPageId, 'title']);
  2471. break;
  2472. case 'icon' :
  2473. if ($this->getData(['page', $parentPageId, 'iconUrl']) != "") {
  2474. $itemsLeft .= '<img alt="'.$this->getData(['page', $parentPageId, 'title']).'" src="'. helper::baseUrl(false) .self::FILE_DIR.'source/'.$this->getData(['page', $parentPageId, 'iconUrl']).'" />';
  2475. } else {
  2476. $itemsLeft .= $this->getData(['page', $parentPageId, 'title']);
  2477. }
  2478. break;
  2479. case 'icontitle' :
  2480. if ($this->getData(['page', $parentPageId, 'iconUrl']) != "") {
  2481. $itemsLeft .= '<img alt="'.$this->getData(['page', $parentPageId, 'title']).'" src="'. helper::baseUrl(false) .self::FILE_DIR.'source/'.$this->getData(['page', $parentPageId, 'iconUrl']).'" data-tippy-content="';
  2482. $itemsLeft .= $this->getData(['page', $parentPageId, 'title']).'"/>';
  2483. } else {
  2484. $itemsLeft .= $this->getData(['page', $parentPageId, 'title']);
  2485. }
  2486. break;
  2487. }
  2488. // Cas où les pages enfants enfant sont toutes masquées dans le menu
  2489. // ne pas afficher de symbole lorsqu'il n'y a rien à afficher
  2490. $totalChild = 0;
  2491. $disableChild = 0;
  2492. foreach($childrenPageIds as $childKey) {
  2493. $totalChild += 1;
  2494. }
  2495. if($childrenPageIds && $disableChild !== $totalChild &&
  2496. $this->getdata(['page',$parentPageId,'hideMenuChildren']) === false) {
  2497. $itemsLeft .= template::ico('down', 'left');
  2498. }
  2499. // ------------------------------------------------
  2500. $itemsLeft .= '</a>';
  2501. if ($this->getdata(['page',$parentPageId,'hideMenuChildren']) === true ||
  2502. empty($childrenPageIds)) {
  2503. continue;
  2504. }
  2505. $itemsLeft .= '<ul class="navSub">';
  2506. foreach($childrenPageIds as $childKey) {
  2507. // Propriétés de l'item
  2508. $active = ($childKey === $currentPageId) ? 'active ' : '';
  2509. $targetBlank = $this->getData(['page', $childKey, 'targetBlank']) ? ' target="_blank"' : '';
  2510. // Mise en page du sous-item
  2511. $itemsLeft .= '<li>';
  2512. if ( ( $this->getData(['page',$childKey,'disable']) === true
  2513. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
  2514. ) OR (
  2515. $this->getData(['page',$childKey,'disable']) === true
  2516. AND $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
  2517. AND $this->getUser('group') < self::GROUP_MODERATOR
  2518. )
  2519. ){
  2520. $itemsLeft .= '<a class="' . $parentPageId . '" href="'.helper::baseUrl() . $this->getUrl(0).'">';
  2521. } else {
  2522. $itemsLeft .= '<a class="' . $active . $parentPageId . '" href="' . helper::baseUrl() . $childKey . '"' . $targetBlank . '>';
  2523. }
  2524. switch ($this->getData(['page', $childKey, 'typeMenu'])) {
  2525. case '' :
  2526. $itemsLeft .= $this->getData(['page', $childKey, 'title']);
  2527. break;
  2528. case 'text' :
  2529. $itemsLeft .= $this->getData(['page', $childKey, 'title']);
  2530. break;
  2531. case 'icon' :
  2532. if ($this->getData(['page', $childKey, 'iconUrl']) != "") {
  2533. $itemsLeft .= '<img alt="'.$this->getData(['page', $parentPageId, 'title']).'" src="'. helper::baseUrl(false) .self::FILE_DIR.'source/'.$this->getData(['page', $childKey, 'iconUrl']).'" />';
  2534. } else {
  2535. $itemsLeft .= $this->getData(['page', $parentPageId, 'title']);
  2536. }
  2537. break;
  2538. case 'icontitle' :
  2539. if ($this->getData(['page', $childKey, 'iconUrl']) != "") {
  2540. $itemsLeft .= '<img alt="'.$this->getData(['page', $parentPageId, 'title']).'" src="'. helper::baseUrl(false) .self::FILE_DIR.'source/'.$this->getData(['page', $childKey, 'iconUrl']).'" data-tippy-content="';
  2541. $itemsLeft .= $this->getData(['page', $childKey, 'title']).'"/>';
  2542. } else {
  2543. $itemsLeft .= $this->getData(['page', $childKey, 'title']);
  2544. }
  2545. break;
  2546. case 'icontext' :
  2547. if ($this->getData(['page', $childKey, 'iconUrl']) != "") {
  2548. $itemsLeft .= '<img alt="'.$this->getData(['page', $parentPageId, 'title']).'" src="'. helper::baseUrl(false) .self::FILE_DIR.'source/'.$this->getData(['page', $childKey, 'iconUrl']).'" />';
  2549. $itemsLeft .= $this->getData(['page', $childKey, 'title']);
  2550. } else {
  2551. $itemsLeft .= $this->getData(['page', $childKey, 'title']);
  2552. }
  2553. break;
  2554. }
  2555. $itemsLeft .= '</a></li>';
  2556. }
  2557. $itemsLeft .= '</ul>';
  2558. }
  2559. // Lien de connexion
  2560. $itemsRight = '';
  2561. if(
  2562. (
  2563. $this->getData(['theme', 'menu', 'loginLink'])
  2564. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
  2565. )
  2566. OR $this->getUrl(0) === 'theme'
  2567. ) {
  2568. $itemsRight .= '<li id="menuLoginLink" ' .
  2569. ($this->getUrl(0) === 'theme' ? 'class="displayNone"' : '') .
  2570. '><a href="' . helper::baseUrl() . 'user/login/' .
  2571. strip_tags(str_replace('/', '_', $this->getUrl())) .
  2572. '">' . template::ico('login') .'</a></li>';
  2573. }
  2574. // Commandes pour les membres simples
  2575. if($this->getUser('group') == self::GROUP_MEMBER
  2576. && ( $this->getData(['theme','menu','memberBar']) === true
  2577. || $this->getData(['theme','footer','displayMemberBar']) === false
  2578. )
  2579. ) {
  2580. $itemsRight .= '<li><a href="' . helper::baseUrl() . 'user/edit/' . $this->getUser('id'). '/' . $_SESSION['csrf'] . '" data-tippy-content="Gérer mon compte">' . template::ico('user', 'right') . '</a></li>';
  2581. $itemsRight .= '<li><a id="barLogout" href="' . helper::baseUrl() . 'user/logout" data-tippy-content="Me déconnecter">' . template::ico('logout') . '</a></li>';
  2582. }
  2583. // Retourne les items du menu
  2584. echo '<ul class="navMain" id="menuLeft">' . $itemsLeft . '</ul><ul class="navMain" id="menuRight">' . $itemsRight . '</ul>';
  2585. }
  2586. /**
  2587. * Générer un menu pour la barre latérale
  2588. * Uniquement texte
  2589. * @param onlyChildren n'affiche les sous-pages de la page actuelle
  2590. */
  2591. public function showMenuSide($onlyChildren = null) {
  2592. // Met en forme les items du menu
  2593. $items = '';
  2594. // Nom de la page courante
  2595. $currentPageId = $this->getData(['page', $this->getUrl(0)]) ? $this->getUrl(0) : $this->getUrl(2);
  2596. // Nom de la page parente
  2597. $currentParentPageId = $this->getData(['page',$currentPageId,'parentPageId']);
  2598. // Détermine si on affiche uniquement le parent et les enfants
  2599. // Filtre contient le nom de la page parente
  2600. if ($onlyChildren === true) {
  2601. if (empty($currentParentPageId)) {
  2602. $filterCurrentPageId = $currentPageId;
  2603. } else {
  2604. $filterCurrentPageId = $currentParentPageId;
  2605. }
  2606. } else {
  2607. $items .= '<ul class="menuSide">';
  2608. }
  2609. foreach($this->getHierarchy() as $parentPageId => $childrenPageIds) {
  2610. // Ne pas afficher les entrées masquées
  2611. if ($this->getData(['page',$parentPageId,'hideMenuSide']) === true ) {
  2612. continue;
  2613. }
  2614. // Filtre actif et nom de la page parente courante différente, on sort de la boucle
  2615. if ($onlyChildren === true && $parentPageId !== $filterCurrentPageId) {
  2616. continue;
  2617. }
  2618. // Propriétés de l'item
  2619. $active = ($parentPageId === $currentPageId OR in_array($currentPageId, $childrenPageIds)) ? ' class="active"' : '';
  2620. $targetBlank = $this->getData(['page', $parentPageId, 'targetBlank']) ? ' target="_blank"' : '';
  2621. // Mise en page de l'item;
  2622. // Ne pas afficher le parent d'une sous-page quand l'option est sélectionnée.
  2623. if ($onlyChildren === false) {
  2624. $items .= '<li class="menuSideChild">';
  2625. if ( $this->getData(['page',$parentPageId,'disable']) === true
  2626. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD') ) {
  2627. $items .= '<a href="'.$this->getUrl(1).'">';
  2628. } else {
  2629. $items .= '<a href="'. helper::baseUrl() . $parentPageId . '"' . $targetBlank . $active .'>';
  2630. }
  2631. $items .= $this->getData(['page', $parentPageId, 'title']);
  2632. $items .= '</a>';
  2633. }
  2634. $itemsChildren = '';
  2635. foreach($childrenPageIds as $childKey) {
  2636. // Passer les entrées masquées
  2637. if ($this->getData(['page',$childKey,'hideMenuSide']) === true ) {
  2638. continue;
  2639. }
  2640. // Propriétés de l'item
  2641. $active = ($childKey === $currentPageId) ? ' class="active"' : '';
  2642. $targetBlank = $this->getData(['page', $childKey, 'targetBlank']) ? ' target="_blank"' : '';
  2643. // Mise en page du sous-item
  2644. $itemsChildren .= '<li class="menuSideChild">';
  2645. if ( $this->getData(['page',$childKey,'disable']) === true
  2646. AND $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD') ) {
  2647. $itemsChildren .= '<a href="'.$this->getUrl(1).'">';
  2648. } else {
  2649. $itemsChildren .= '<a href="' . helper::baseUrl() . $childKey . '"' . $targetBlank . $active . '>';
  2650. }
  2651. $itemsChildren .= $this->getData(['page', $childKey, 'title']);
  2652. $itemsChildren .= '</a></li>';
  2653. }
  2654. // Concatène les items enfants
  2655. if (!empty($itemsChildren)) {
  2656. $items .= '<ul class="menuSideChild">';
  2657. $items .= $itemsChildren;
  2658. $items .= '</ul>';
  2659. } else {
  2660. $items .= '</li>';
  2661. }
  2662. }
  2663. if ($onlyChildren === false) {
  2664. $items .= '</ul>';
  2665. }
  2666. // Retourne les items du menu
  2667. echo $items;
  2668. }
  2669. /**
  2670. * Affiche le meta titre
  2671. */
  2672. public function showMetaTitle() {
  2673. echo '<title>' . $this->core->output['metaTitle'] . '</title>';
  2674. echo '<meta property="og:title" content="' . $this->core->output['metaTitle'