From a16550b0cdf416014f597bc2feabe0eb50c4c6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Tempez?= Date: Thu, 17 Feb 2022 15:45:25 +0100 Subject: [PATCH] =?UTF-8?q?Module=20plugin=20qui=20a=20saut=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/core.php | 4 +- core/module/addon/addon.php | 594 -------------- core/module/addon/view/import/import.php | 31 - core/module/addon/view/index/index.help.html | 8 - core/module/addon/view/index/index.js.php | 22 - core/module/addon/view/index/index.php | 40 - core/module/addon/view/store/store.php | 15 - .../module/addon/view/upload/upload.help.html | 6 - core/module/plugin/plugin.php | 750 ++++++++++++++++++ .../view/dataImport/dataImport.css} | 0 .../plugin/view/dataImport/dataImport.php | 35 + .../{addon => plugin}/view/index/index.css | 6 +- core/module/plugin/view/index/index.js.php | 50 ++ core/module/plugin/view/index/index.php | 105 +++ .../{addon => plugin}/view/item/item.css | 0 .../{addon => plugin}/view/item/item.php | 0 .../{addon => plugin}/view/store/store.css | 0 core/module/plugin/view/store/store.php | 14 + .../{addon => plugin}/view/upload/upload.css | 0 .../{addon => plugin}/view/upload/upload.php | 23 +- 20 files changed, 971 insertions(+), 732 deletions(-) delete mode 100644 core/module/addon/addon.php delete mode 100644 core/module/addon/view/import/import.php delete mode 100644 core/module/addon/view/index/index.help.html delete mode 100644 core/module/addon/view/index/index.js.php delete mode 100644 core/module/addon/view/index/index.php delete mode 100644 core/module/addon/view/store/store.php delete mode 100644 core/module/addon/view/upload/upload.help.html create mode 100644 core/module/plugin/plugin.php rename core/module/{addon/view/import/import.css => plugin/view/dataImport/dataImport.css} (100%) create mode 100644 core/module/plugin/view/dataImport/dataImport.php rename core/module/{addon => plugin}/view/index/index.css (89%) create mode 100644 core/module/plugin/view/index/index.js.php create mode 100644 core/module/plugin/view/index/index.php rename core/module/{addon => plugin}/view/item/item.css (100%) rename core/module/{addon => plugin}/view/item/item.php (100%) rename core/module/{addon => plugin}/view/store/store.css (100%) create mode 100644 core/module/plugin/view/store/store.php rename core/module/{addon => plugin}/view/upload/upload.css (100%) rename core/module/{addon => plugin}/view/upload/upload.php (70%) diff --git a/core/core.php b/core/core.php index e1e9a755..1a8f0ee3 100644 --- a/core/core.php +++ b/core/core.php @@ -58,7 +58,7 @@ class common { 'theme', 'user', 'translate', - 'addon' + 'plugin' ]; public static $accessList = [ 'user', @@ -2072,7 +2072,7 @@ class common { } if($this->getUser('group') >= self::GROUP_ADMIN) { $rightItems .= '
  • ' . template::ico('brush') . '
  • '; - $rightItems .= '
  • ' . template::ico('puzzle') . '
  • '; + $rightItems .= '
  • ' . template::ico('puzzle') . '
  • '; if ($this->getData(['config', 'i18n', 'enable']) === true) { $rightItems .= '
  • ' . template::ico('flag') . '
  • '; } diff --git a/core/module/addon/addon.php b/core/module/addon/addon.php deleted file mode 100644 index 2aff2f7a..00000000 --- a/core/module/addon/addon.php +++ /dev/null @@ -1,594 +0,0 @@ - - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @author Frédéric Tempez - * @copyright Copyright (C) 2018-2022, Frédéric Tempez - * @author Sylvain Lelièvre - * @copyright Copyright (C) 2020-2021, Sylvain Lelièvre - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - -class addon extends common { - - public static $actions = [ - 'index' => self::GROUP_ADMIN, - 'delete' => self::GROUP_ADMIN, - 'export' => self::GROUP_ADMIN, - 'import' => self::GROUP_ADMIN, - 'store' => self::GROUP_ADMIN, - 'item' => self::GROUP_ADMIN, - 'upload' => self::GROUP_ADMIN, - 'uploadItem'=> self::GROUP_ADMIN - ]; - - // URL des modules - const BASEURL_STORE = 'https://store.zwiicms.fr/'; - const MODULE_STORE = '?modules/'; - - // Gestion des modules - public static $modInstal = []; - - // pour tests - public static $valeur = []; - - // le catalogue - public static $storeList = []; - public static $storeItem = []; - - - /* - * Effacement d'un module installé et non utilisé - */ - public function delete() { - - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon', - 'state' => false, - 'notification' => 'Action non autorisée' - ]); - } - else{ - // Suppression des dossiers - $infoModules = helper::getModules(); - $module = $this->getUrl(2); - //Liste des dossiers associés au module non effacés - if( $this->removeDir('./module/'.$module ) === true ){ - $success = true; - $notification = 'Module '. $module .' désinstallé'; - if(($infoModules[$this->getUrl(2)]['dataDirectory']) ) { - if ( - is_dir($infoModules[$this->getUrl(2)]['dataDirectory']) - && !$this->removeDir($infoModules[$this->getUrl(2)]['dataDirectory']) - ){ - $notification = 'Module '.$module .' désinstallé, il reste des données dans ' . $infoModules[$this->getUrl(2)]['dataDirectory']; - } - } - } - else{ - $success = false; - $notification = 'La suppression a échouée'; - } - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon', - 'notification' => $notification, - 'state' => $success - ]); - } - } - - /*** - * Installation d'un module - * Fonction utilisée par upload et storeUpload - */ - private function install ($moduleName, $checkValid){ - $tempFolder = 'datamodules';//uniqid(); - $zip = new ZipArchive(); - if ($zip->open($moduleName) === TRUE) { - $notification = 'Archive ouverte'; - mkdir (self::TEMP_DIR . $tempFolder, 0755); - $zip->extractTo(self::TEMP_DIR . $tempFolder ); - // Archive de module ? - $success = false; - $notification = 'Ce n\'est pas l\'archive d\'un module !'; - $moduleDir = self::TEMP_DIR . $tempFolder . '/module'; - $moduleName = ''; - if ( is_dir( $moduleDir )) { - // Lire le nom du module - if ($dh = opendir( $moduleDir )) { - while ( false !== ($file = readdir($dh)) ) { - if ($file != "." && $file != "..") { - $moduleName = $file; - } - } - closedir($dh); - } - // Module normalisé ? - if( is_file( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php' ) AND is_file( $moduleDir.'/'.$moduleName.'/view/index/index.php' ) ){ - - // Lecture de la version et de la validation d'update du module pour validation de la mise à jour - // Pour une version <= version installée l'utilisateur doit cocher 'Mise à jour forcée' - $version = '0.0'; - $update = '0.0'; - $valUpdate = false; - $file = file_get_contents( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php'); - $file = str_replace(' ','',$file); - $file = str_replace("\t",'',$file); - $pos1 = strpos($file, 'constVERSION'); - if( $pos1 !== false){ - $posdeb = strpos($file, "'", $pos1); - $posend = strpos($file, "'", $posdeb + 1); - $version = substr($file, $posdeb + 1, $posend - $posdeb - 1); - } - $pos1 = strpos($file, 'constUPDATE'); - if( $pos1 !== false){ - $posdeb = strpos($file, "'", $pos1); - $posend = strpos($file, "'", $posdeb + 1); - $update = substr($file, $posdeb + 1, $posend - $posdeb - 1); - } - // Si version actuelle >= version indiquée dans UPDATE la mise à jour est validée - $infoModules = helper::getModules(); - if( $infoModules[$moduleName]['update'] >= $update ) $valUpdate = true; - - // Module déjà installé ? - $moduleInstal = false; - foreach($infoModules as $key=>$value ){ - if($moduleName === $key){ - $moduleInstal = true; - } - } - - // Validation de la maj si autorisation du concepteur du module ET - // ( Version plus récente OU Check de forçage ) - $valNewVersion = floatval($version); - $valInstalVersion = floatval( $infoModules[$moduleName]['version'] ); - $newVersion = false; - if( $valNewVersion > $valInstalVersion ) $newVersion = true; - $validMaj = $valUpdate && ( $newVersion || $checkValid); - - // Nouvelle installation ou mise à jour du module - if( ! $moduleInstal || $validMaj ){ - // Copie récursive des dossiers - $this->copyDir( self::TEMP_DIR . $tempFolder, './' ); - $success = true; - if( ! $moduleInstal ){ - $notification = 'Module '.$moduleName.' installé'; - } - else{ - $notification = 'Module '.$moduleName.' mis à jour'; - } - } - else{ - $success = false; - if( $valNewVersion == $valInstalVersion){ - $notification = ' Version détectée '.$version.' = à celle installée '.$infoModules[$moduleName]['version']; - } - else{ - $notification = ' Version détectée '.$version.' < à celle installée '.$infoModules[$moduleName]['version']; - } - if( $valUpdate === false){ - if( $infoModules[$moduleName]['update'] === $update ){ - $notification = ' Mise à jour par ce procédé interdite par le concepteur du module'; - } - else{ - $notification = ' Mise à jour par ce procédé interdite, votre version est trop ancienne'; - } - } - } - } - } - // Supprimer le dossier temporaire même si le module est invalide - $this->removeDir(self::TEMP_DIR . $tempFolder); - $zip->close(); - } else { - // erreur à l'ouverture - $success = false; - $notification = 'Impossible d\'ouvrir l\'archive'; - } - return(['success' => $success, - 'notification'=> $notification - ]); - } - - /*** - * Installation d'un module à partir du gestionnaire de fichier - */ - public function upload() { - // Soumission du formulaire - if($this->isPost()) { - // Installation d'un module - $checkValidMaj = $this->getInput('configModulesCheck', helper::FILTER_BOOLEAN); - $zipFilename = $this->getInput('configModulesInstallation', helper::FILTER_STRING_SHORT); - if( $zipFilename !== ''){ - $success = [ - 'success' => false, - 'notification'=> '' - ]; - $state = $this->install(self::FILE_DIR.'source/'.$zipFilename, $checkValidMaj); - } - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(), - 'notification' => $state['notification'], - 'state' => $state['success'] - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Téléverser un module', - 'view' => 'upload' - ]); - } - - /*** - * Installation d'un module par le catalogue - */ - public function uploadItem() { - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'store', - 'state' => false, - 'notification' => 'Action non autorisée' - ]); - } else { - // Récupérer le module en ligne - $moduleName = $this->getUrl(2); - // Informations sur les module en ligne - $store = json_decode(helper::getUrlContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true); - // Url du module à télécharger - $moduleFilePath = $store[$moduleName]['file']; - // Télécharger le fichier - $moduleData = helper::getUrlContents(self::BASEURL_STORE . self::FILE_DIR . 'source/' . $moduleFilePath); - // Extraire de l'arborescence - $d = explode('/',$moduleFilePath); - $moduleFile = $d[count($d)-1]; - // Créer le dossier modules - if (!is_dir(self::FILE_DIR . 'source/modules')) { - mkdir (self::FILE_DIR . 'source/modules', 0755); - } - // Sauver les données du fichiers - file_put_contents(self::FILE_DIR . 'source/modules/' . $moduleFile, $moduleData); - - /** - * $if( $moduleFile !== ''){ - * $success = [ - * 'success' => false, - * 'notification'=> '' - * ]; - * $state = $this->install(self::FILE_DIR.'source/modules/'.$moduleFile, false); - *} - */ - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon/store', - 'notification' => $moduleFile . ' téléchargé dans le dossier modules du gestionnaire de fichiers', - 'state' => true - ]); - - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Catalogue de modules', - 'view' => 'store' - ]); - } - - /** - * Catalogue des modules sur le site ZwiiCMS.fr - */ - public function store() { - $store = json_decode(helper::getUrlContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true); - if ($store) { - // Modules installés - $infoModules = helper::getModules(); - // Clés moduleIds dans les pages - $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); - foreach( $inPages as $key=>$value){ - $inPagesTitle[ $this->getData(['page', $key, 'title' ]) ] = $value; - } - // Parcourir les données des modules - foreach ($store as $key=>$value) { - // Module non installé - $ico = template::ico('download'); - $class = ''; - // Le module est installé - if (array_key_exists($key,$infoModules) === true) { - $class = 'buttonGreen'; - $ico = template::ico('update'); - } - // Le module est installé et utilisé - if (in_array($key,$inPages) === true) { - $class = 'buttonRed'; - $ico = template::ico('update'); - } - self::$storeList [] = [ - $store[$key]['category'], - ''.$store[$key]['title'].'', - $store[$key]['version'], - mb_detect_encoding(strftime('%d %B %Y', $store[$key]['versionDate']), 'UTF-8', true) - ? strftime('%d %B %Y', $store[$key]['versionDate']) - : utf8_encode(strftime('%d %B %Y', $store[$key]['versionDate'])), - implode(', ', array_keys($inPagesTitle,$key)), - template::button('moduleExport' . $key, [ - 'class' => $class, - 'href' => helper::baseUrl(). $this->getUrl(0) . '/uploadItem/' . $key.'/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre - 'value' => $ico - ]) - ]; - } - } - - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Catalogue de modules en ligne', - 'view' => 'store' - ]); - } - - /** - * Détail d'un objet du catalogue - */ - public function item() { - $store = json_decode(helper::getUrlContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true); - self::$storeItem = $store [$this->getUrl(2)] ; - self::$storeItem ['fileDate'] = mb_detect_encoding(strftime('%d %B %Y',self::$storeItem ['fileDate']), 'UTF-8', true) - ? strftime('%d %B %Y', self::$storeItem ['fileDate']) - : utf8_encode(strftime('%d %B %Y', self::$storeItem ['fileDate'])); - // Valeurs en sortie - $this->addOutput([ - 'title' =>'Module ' . self::$storeItem['title'], - 'view' => 'item' - ]); - } - - /** - * Gestion des modules - */ - public function index() { - - // Lister les modules - // $infoModules[nom_module]['realName'], ['version'], ['update'], ['delete'], ['dataDirectory'] - $infoModules = helper::getModules(); - - // Clés moduleIds dans les pages - $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); - foreach( $inPages as $key=>$value){ - $inPagesTitle[ $this->getData(['page', $key, 'title' ]) ] = $value; - } - - // Parcourir les données des modules - foreach ($infoModules as $key=>$value) { - // Construire le tableau de sortie - self::$modInstal[] = [ - $key, - $infoModules[$key]['realName'], - $infoModules[$key]['version'], - implode(', ', array_keys($inPagesTitle,$key)), - //|| ('delete',$infoModules[$key]) && $infoModules[$key]['delete'] === true && implode(', ',array_keys($inPages,$key)) === '' - $infoModules[$key]['delete'] === true && implode(', ',array_keys($inPages,$key)) === '' - ? template::button('moduleDelete' . $key, [ - 'class' => 'moduleDelete buttonRed', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $key . '/' . $_SESSION['csrf'], - 'value' => template::ico('cancel') - ]) - : '', - implode(', ',array_keys($inPages,$key)) !== '' - ? template::button('moduleExport' . $key, [ - 'href' => helper::baseUrl(). $this->getUrl(0) . '/export/' . $key . '/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre - 'value' => template::ico('download') - ]) - : '', - implode(', ',array_keys($inPages,$key)) === '' - ? template::button('moduleExport' . $key, [ - 'href' => helper::baseUrl(). $this->getUrl(0) . '/import/' . $key . '/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre - 'value' => template::ico('upload') - ]) - : '' - ]; - } - - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Gestion des modules', - 'view' => 'index' - ]); - } - - /* - * Export des données d'un module externes ou interne à module.json - */ - public function export(){ - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon', - 'state' => false, - 'notification' => 'Action non autorisée' - ]); - } - else { - // Lire les données du module - $infoModules = helper::getModules(); - // Créer un dossier par défaut - $tmpFolder = self::TEMP_DIR . uniqid(); - //$tmpFolder = self::TEMP_DIR . 'test'; - if (!is_dir($tmpFolder)) { - mkdir($tmpFolder, 0755); - } - // Clés moduleIds dans les pages - $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); - // Parcourir les pages utilisant le module - foreach (array_keys($inPages,$this->getUrl(2)) as $pageId) { - // Export des pages hébergeant le module - $pageParam[$pageId] = $this->getData(['page',$pageId]); - // Export du contenu de la page - //$pageContent[$pageId] = file_get_contents(self::DATA_DIR . self::$i18n . '/content/' . $this->getData(['page', $pageId, 'content'])); - $pageContent[$pageId] = $this->getPage($pageId, self::$i18n); - // Export de fr/module.json - $moduleId = 'fr/module.json'; - $moduleDir = str_replace('site/data/','',$infoModules[$this->getUrl(2)]['dataDirectory']); - // Création de l'arborescence des langues - // Pas de nom dossier de langue - dossier par défaut - $t = explode ('/',$moduleId); - if ( is_array($t)) { - $lang = 'fr'; - } else { - $lang = $t[0]; - } - // Créer le dossier temporaire si inexistant sinon le nettoie et le créer - if (!is_dir($tmpFolder . '/' . $lang)) { - mkdir ($tmpFolder . '/' . $lang, 0755, true); - } else { - $this->removeDir($tmpFolder . '/' . $lang); - mkdir ($tmpFolder . '/' . $lang, 0755, true); - } - // Créer le dossier temporaire des données du module - if ($infoModules[$this->getUrl(2)]['dataDirectory']) { - if (!is_dir($tmpFolder . '/' . $moduleDir)) { - mkdir ($tmpFolder . '/' . $moduleDir, 0755, true) ; - } - } - // Sauvegarde si données non vides - $tmpData [$pageId] = $this->getData(['module',$pageId ]); - if ($tmpData [$pageId] !== null) { - file_put_contents($tmpFolder . '/' . $moduleId, json_encode($tmpData)); - } - // Export des données localisées dans le dossier de données du module - if ($infoModules[$this->getUrl(2)]['dataDirectory'] && - is_dir($infoModules[$this->getUrl(2)]['dataDirectory'])) { - $this->copyDir ($infoModules[$this->getUrl(2)]['dataDirectory'], $tmpFolder . '/' . $moduleDir); - } - } - // Enregistrement des pages dans le dossier de langue identique à module - if (!file_exists($tmpFolder . '/' . $lang . '/page.json')) { - file_put_contents($tmpFolder . '/' . $lang . '/page.json', json_encode($pageParam)); - mkdir ($tmpFolder . '/' . $lang . '/content', 0755); - file_put_contents($tmpFolder . '/' . $lang . '/content/' . $this->getData(['page', $pageId, 'content']), $pageContent); - } - // création du zip - $fileName = $this->getUrl(2) . '.zip'; - $this->makeZip ($fileName, $tmpFolder, []); - if (file_exists($fileName)) { - ob_start(); - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment; filename="' . $fileName . '"'); - header('Content-Length: ' . filesize($fileName)); - ob_clean(); - ob_end_flush(); - readfile( $fileName); - unlink($fileName); - $this->removeDir($tmpFolder); - exit(); - } else { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon', - 'notification' => 'Quelque chose s\'est mal passé', - 'state' => false - ]); - } - } - } - - /* - * Importer des données d'un module externes ou interne à module.json - */ - public function import(){ - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon', - 'state' => false, - 'notification' => 'Action non autorisée' - ]); - } - else { - // Soumission du formulaire - if($this->isPost()) { - // Récupérer le fichier et le décompacter - $zipFilename = $this->getInput('addonImportFile', helper::FILTER_STRING_SHORT, true); - $tempFolder = uniqid(); - mkdir (self::TEMP_DIR . $tempFolder, 0755); - $zip = new ZipArchive(); - if ($zip->open(self::FILE_DIR . 'source/' . $zipFilename) === TRUE) { - $zip->extractTo(self::TEMP_DIR . $tempFolder ); - } - // Import des données localisées page.json et module.json - // Pour chaque dossier localisé - $dataTarget = array(); - $dataSource = array(); - // Liste des pages de même nom dans l'archive et le site - $list = ''; - foreach (self::$i18nList as $key=>$value) { - // Les Pages et les modules - foreach (['page','module'] as $fileTarget){ - if (file_exists(self::TEMP_DIR . $tempFolder . '/' .$key . '/' . $fileTarget . '.json')) { - // Le dossier de langue existe - // faire la fusion - $dataSource = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . '/' .$key . '/' . $fileTarget . '.json'), true); - // Des pages de même nom que celles de l'archive existent - if( $fileTarget === 'page' ){ - foreach( $dataSource as $keydataSource=>$valuedataSource ){ - foreach( $this->getData(['page']) as $keypage=>$valuepage ){ - if( $keydataSource === $keypage){ - $list === '' ? $list .= ' '.$this->getData(['page', $keypage, 'title']) : $list .= ', '.$this->getData(['page', $keypage, 'title']); - } - } - } - } - $dataTarget = json_decode(file_get_contents(self::DATA_DIR . $key . '/' . $fileTarget . '.json'), true); - $data [$fileTarget] = array_merge($dataTarget[$fileTarget], $dataSource); - if( $list === ''){ - file_put_contents(self::DATA_DIR . '/' .$key . '/' . $fileTarget . '.json', json_encode( $data ,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT|LOCK_EX) ); - } - // copie du contenu de la page - $this->copyDir (self::TEMP_DIR . $tempFolder . '/' .$key . '/content', self::DATA_DIR . '/' .$key . '/content'); - // Supprimer les fichiers importés - unlink (self::TEMP_DIR . $tempFolder . '/' .$key . '/' . $fileTarget . '.json'); - } - } - } - - // Import des fichiers placés ailleurs que dans les dossiers localisés. - $this->copyDir (self::TEMP_DIR . $tempFolder,self::DATA_DIR ); - - // Supprimer le dossier temporaire - $this->removeDir(self::TEMP_DIR . $tempFolder); - $zip->close(); - if( $list !== '' ){ - $success = false; - strpos( $list, ',') === false ? $notification = 'Import impossible la page suivante doit être renommée :'.$list : $notification = 'Import impossible les pages suivantes doivent être renommées :'.$list; - } - else{ - $success = true; - $notification = 'Import réussi'; - } - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . 'addon', - 'state' => $success, - 'notification' => $notification - ]); - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Importer des données de module', - 'view' => 'import' - ]); - } - } - -} diff --git a/core/module/addon/view/import/import.php b/core/module/addon/view/import/import.php deleted file mode 100644 index 7cc41dac..00000000 --- a/core/module/addon/view/import/import.php +++ /dev/null @@ -1,31 +0,0 @@ - -
    -
    - 'buttonGrey', - 'href' => helper::baseUrl() . 'addon', - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
    -
    - 'Appliquer' - ]); ?> -
    -
    -
    -
    -
    -

    Installer des données de module

    -
    -
    - 'Archive ZIP :', - 'type' => 2 - ]); ?> -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/core/module/addon/view/index/index.help.html b/core/module/addon/view/index/index.help.html deleted file mode 100644 index 8af49c96..00000000 --- a/core/module/addon/view/index/index.help.html +++ /dev/null @@ -1,8 +0,0 @@ -

    MODULES INSTALLES

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

    EXPORTER IMPORTER

    -

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

    -

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

    -

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

    diff --git a/core/module/addon/view/index/index.js.php b/core/module/addon/view/index/index.js.php deleted file mode 100644 index 4dd572cc..00000000 --- a/core/module/addon/view/index/index.js.php +++ /dev/null @@ -1,22 +0,0 @@ -/** - * This file is part of Zwii. - * For full copyright and license information, please see the LICENSE - * file that was distributed with this source code. - * - * @author Rémi Jean - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @author Frédéric Tempez - * @copyright Copyright (C) 2018-2022, Frédéric Tempez - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - -/** - * Confirmation de suppression - */ -$(".moduleDelete").on("click", function() { - var _this = $(this); - return core.confirm("Êtes-vous sûr de vouloir supprimer ce module ?", function() { - $(location).attr("href", _this.attr("href")); - }); -}); \ No newline at end of file diff --git a/core/module/addon/view/index/index.php b/core/module/addon/view/index/index.php deleted file mode 100644 index e1e9c61f..00000000 --- a/core/module/addon/view/index/index.php +++ /dev/null @@ -1,40 +0,0 @@ -
    -
    - 'buttonGrey', - 'href' => helper::baseUrl(), - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
    -
    - 'https://doc.zwiicms.fr/les-modules', - 'target' => '_blank', - 'ico' => 'help', - 'value' => 'Aide', - 'class' => 'buttonHelp' - ]); ?> -
    -
    - helper::baseUrl() . 'addon/store', - 'value' => 'Catalogue en ligne' - ]); ?> -
    -
    - helper::baseUrl() . 'addon/upload', - 'value' => 'Installer' - ]); ?> -
    -
    - -
    - -
    - - - - - diff --git a/core/module/addon/view/store/store.php b/core/module/addon/view/store/store.php deleted file mode 100644 index e1324a6a..00000000 --- a/core/module/addon/view/store/store.php +++ /dev/null @@ -1,15 +0,0 @@ -
    -
    - 'buttonGrey', - 'href' => helper::baseUrl() . 'addon/upload', - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
    -
    - - Mettre à jour']); ?> - - - \ No newline at end of file diff --git a/core/module/addon/view/upload/upload.help.html b/core/module/addon/view/upload/upload.help.html deleted file mode 100644 index fbf87085..00000000 --- a/core/module/addon/view/upload/upload.help.html +++ /dev/null @@ -1,6 +0,0 @@ -

    INSTALLER OU METTRE A JOUR

    -

    Avant de choisir le fichier ZIP du module à installer vous devez le télécharger sur votre serveur en utilisant le 'Catalogue en ligne'.

    -

    D'autres modules sont également disponibles sur le forum de ZwiiCMS, -téléversez les sur votre serveur avec 'Gérer les fichiers'.

    -

    Lors d'une mise à jour Zwii contrôle la version du module à installer, pour réinstaller un module de même numéro de version vous devez cocher 'Mise à jour forcée'. - Il est déconseillé d'installer un module plus ancien.

    diff --git a/core/module/plugin/plugin.php b/core/module/plugin/plugin.php new file mode 100644 index 00000000..436d3e37 --- /dev/null +++ b/core/module/plugin/plugin.php @@ -0,0 +1,750 @@ + + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2020-2021, Sylvain Lelièvre + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + * @author Sylvain Lelièvre + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +class plugin extends common { + + public static $actions = [ + 'index' => self::GROUP_ADMIN, + 'delete' => self::GROUP_ADMIN, + 'save' => self::GROUP_ADMIN, // Sauvegarde le module dans un fichier ZIP ou dans le gestionnaire + 'dataExport' => self::GROUP_ADMIN, // Fonction muette d'exportation + 'dataImport' => self::GROUP_ADMIN, // les données d'un module + 'dataDelete' => self::GROUP_ADMIN, + 'store' => self::GROUP_ADMIN, + 'item' => self::GROUP_ADMIN, // détail d'un objet + 'upload' => self::GROUP_ADMIN, // Téléverser catalogue + 'uploadItem'=> self::GROUP_ADMIN // Téléverser par archive + ]; + + // URL des modules + const BASEURL_STORE = 'https://store.zwiicms.fr/'; + const MODULE_STORE = '?modules/'; + + // Gestion des modules + public static $modulesData = []; + public static $modulesOrphan = []; + public static $modulesInstalled = []; + + // pour tests + public static $valeur = []; + + // le catalogue + public static $storeList = []; + public static $storeItem = []; + + // Liste de pages + public static $pagesList = []; + + + /* + * Effacement d'un module installé et non utilisé + */ + public function delete() { + + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => false, + 'notification' => 'Action non autorisée' + ]); + } + else{ + // Suppression des dossiers + $infoModules = helper::getModules(); + $module = $this->getUrl(2); + //Liste des dossiers associés au module non effacés + if( $this->removeDir('./module/'.$module ) === true ){ + $success = true; + $notification = 'Module '. $module .' désinstallé'; + if(($infoModules[$this->getUrl(2)]['dataDirectory']) ) { + if ( + is_dir($infoModules[$this->getUrl(2)]['dataDirectory']) + && !$this->removeDir($infoModules[$this->getUrl(2)]['dataDirectory']) + ){ + $notification = 'Module '.$module .' désinstallé, il reste des données dans ' . $infoModules[$this->getUrl(2)]['dataDirectory']; + } + } + } + else{ + $success = false; + $notification = 'La suppression a échouée'; + } + + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'notification' => $notification, + 'state' => $success + ]); + } + } + + /*** + * Installation d'un module + * Fonction utilisée par upload et storeUpload + */ + private function install ($moduleName, $checkValid){ + $tempFolder = 'datamodules';//uniqid(); + $zip = new ZipArchive(); + if ($zip->open($moduleName) === TRUE) { + $notification = 'Archive ouverte'; + mkdir (self::TEMP_DIR . $tempFolder, 0755); + $zip->extractTo(self::TEMP_DIR . $tempFolder ); + // Archive de module ? + $success = false; + $notification = 'Ce n\'est pas l\'archive d\'un module !'; + $moduleDir = self::TEMP_DIR . $tempFolder . '/module'; + $moduleName = ''; + if ( is_dir( $moduleDir )) { + // Lire le nom du module + if ($dh = opendir( $moduleDir )) { + while ( false !== ($file = readdir($dh)) ) { + if ($file != "." && $file != "..") { + $moduleName = $file; + } + } + closedir($dh); + } + // Module normalisé ? + if( is_file( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php' ) AND is_file( $moduleDir.'/'.$moduleName.'/view/index/index.php' ) ){ + + // Lecture de la version et de la validation d'update du module pour validation de la mise à jour + // Pour une version <= version installée l'utilisateur doit cocher 'Mise à jour forcée' + $version = '0.0'; + $update = '0.0'; + $valUpdate = false; + $file = file_get_contents( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php'); + $file = str_replace(' ','',$file); + $file = str_replace("\t",'',$file); + $pos1 = strpos($file, 'constVERSION'); + if( $pos1 !== false){ + $posdeb = strpos($file, "'", $pos1); + $posend = strpos($file, "'", $posdeb + 1); + $version = substr($file, $posdeb + 1, $posend - $posdeb - 1); + } + $pos1 = strpos($file, 'constUPDATE'); + if( $pos1 !== false){ + $posdeb = strpos($file, "'", $pos1); + $posend = strpos($file, "'", $posdeb + 1); + $update = substr($file, $posdeb + 1, $posend - $posdeb - 1); + } + // Si version actuelle >= version indiquée dans UPDATE la mise à jour est validée + $infoModules = helper::getModules(); + if( $infoModules[$moduleName]['update'] >= $update ) $valUpdate = true; + + // Module déjà installé ? + $moduleInstal = false; + foreach($infoModules as $key=>$value ){ + if($moduleName === $key){ + $moduleInstal = true; + } + } + + // Validation de la maj si autorisation du concepteur du module ET + // ( Version plus récente OU Check de forçage ) + $valNewVersion = floatval($version); + $valInstalVersion = floatval( $infoModules[$moduleName]['version'] ); + $newVersion = false; + if( $valNewVersion > $valInstalVersion ) $newVersion = true; + $validMaj = $valUpdate && ( $newVersion || $checkValid); + + // Nouvelle installation ou mise à jour du module + if( ! $moduleInstal || $validMaj ){ + // Copie récursive des dossiers + $this->copyDir( self::TEMP_DIR . $tempFolder, './' ); + $success = true; + if( ! $moduleInstal ){ + $notification = 'Module '.$moduleName.' installé'; + } + else{ + $notification = 'Module '.$moduleName.' mis à jour'; + } + } + else{ + $success = false; + if( $valNewVersion == $valInstalVersion){ + $notification = ' Version détectée '.$version.' = à celle installée '.$infoModules[$moduleName]['version']; + } + else{ + $notification = ' Version détectée '.$version.' < à celle installée '.$infoModules[$moduleName]['version']; + } + if( $valUpdate === false){ + if( $infoModules[$moduleName]['update'] === $update ){ + $notification = ' Mise à jour par ce procédé interdite par le concepteur du module'; + } + else{ + $notification = ' Mise à jour par ce procédé interdite, votre version est trop ancienne'; + } + } + } + } + } + // Supprimer le dossier temporaire même si le module est invalide + $this->removeDir(self::TEMP_DIR . $tempFolder); + $zip->close(); + } else { + // erreur à l'ouverture + $success = false; + $notification = 'Impossible d\'ouvrir l\'archive'; + } + return(['success' => $success, + 'notification'=> $notification + ]); + } + + /*** + * Installation d'un module à partir du gestionnaire de fichier + */ + public function upload() { + // Soumission du formulaire + if($this->isPost()) { + // Installation d'un module + $checkValidMaj = $this->getInput('configModulesCheck', helper::FILTER_BOOLEAN); + $zipFilename = $this->getInput('configModulesInstallation', helper::FILTER_STRING_SHORT); + if( $zipFilename !== ''){ + $success = [ + 'success' => false, + 'notification'=> '' + ]; + $state = $this->install(self::FILE_DIR.'source/'.$zipFilename, $checkValidMaj); + } + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'notification' => $state['notification'], + 'state' => $state['success'] + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Téléverser un module', + 'view' => 'upload' + ]); + } + + /*** + * Installation d'un module depuis le catalogue + */ + public function uploadItem() { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'store', + 'state' => false, + 'notification' => 'Action non autorisée' + ]); + } else { + // Récupérer le module en ligne + $moduleName = $this->getUrl(2); + // Informations sur les module en ligne + $store = json_decode(helper::urlGetContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true); + // Url du module à télécharger + $moduleFilePath = $store[$moduleName]['file']; + // Télécharger le fichier + $moduleData = helper::urlGetContents(self::BASEURL_STORE . self::FILE_DIR . 'source/' . $moduleFilePath); + // Extraire de l'arborescence + $d = explode('/',$moduleFilePath); + $moduleFile = $d[count($d)-1]; + // Créer le dossier modules + if (!is_dir(self::FILE_DIR . 'source/modules')) { + mkdir (self::FILE_DIR . 'source/modules', 0755); + } + // Sauver les données du fichiers + file_put_contents(self::FILE_DIR . 'source/modules/' . $moduleFile, $moduleData); + + /** + * $if( $moduleFile !== ''){ + * $success = [ + * 'success' => false, + * 'notification'=> '' + * ]; + * $state = $this->install(self::FILE_DIR.'source/modules/'.$moduleFile, false); + *} + */ + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin/store', + 'notification' => $moduleFile . ' téléchargé dans le dossier modules du gestionnaire de fichiers', + 'state' => true + ]); + + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Catalogue de modules', + 'view' => 'store' + ]); + } + + /** + * Catalogue des modules sur le site ZwiiCMS.fr + */ + public function store() { + $store = json_decode(helper::urlGetContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true); + if ($store) { + // Modules installés + $infoModules = helper::getModules(); + // Clés moduleIds dans les pages + $inPages = helper::arrayCollumn($this->getData(['page']),'moduleId', 'SORT_DESC'); + foreach( $inPages as $key=>$value){ + $pagesInfos[ $this->getData(['page', $key, 'title' ]) ] = $value; + } + // Parcourir les données des modules + foreach ($store as $key=>$value) { + // Module non installé + $ico = template::ico('download'); + $class = ''; + $help = 'Télécharger le module'; + // Le module est installé + if (array_key_exists($key,$infoModules) === true) { + $class = 'buttonGreen'; + $ico = template::ico('update'); + $help = 'Mettre à jour ce module'; + } + // Le module est installé et utilisé + if (in_array($key,$inPages) === true) { + $class = 'buttonRed'; + $ico = template::ico('update'); + $help = 'Mettre à jour le module'; + } + self::$storeList [] = [ + $store[$key]['category'], + ''.$store[$key]['title'].'', + $store[$key]['version'], + mb_detect_encoding(strftime('%d %B %Y', $store[$key]['versionDate']), 'UTF-8', true) + ? strftime('%d %B %Y', $store[$key]['versionDate']) + : utf8_encode(strftime('%d %B %Y', $store[$key]['versionDate'])), + implode(', ', array_keys($pagesInfos,$key)), + template::button('moduleExport' . $key, [ + 'class' => $class, + 'href' => helper::baseUrl(). $this->getUrl(0) . '/uploadItem/' . $key.'/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre + 'value' => $ico, + 'help' => $help + ]) + ]; + } + } + + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Catalogue de modules en ligne', + 'view' => 'store' + ]); + } + + /** + * Détail d'un objet du catalogue + */ + public function item() { + $store = json_decode(helper::urlGetContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true); + self::$storeItem = $store [$this->getUrl(2)] ; + self::$storeItem ['fileDate'] = mb_detect_encoding(strftime('%d %B %Y',self::$storeItem ['fileDate']), 'UTF-8', true) + ? strftime('%d %B %Y', self::$storeItem ['fileDate']) + : utf8_encode(strftime('%d %B %Y', self::$storeItem ['fileDate'])); + // Valeurs en sortie + $this->addOutput([ + 'title' =>'Module ' . self::$storeItem['title'], + 'view' => 'item' + ]); + } + + /** + * Gestion des modules + */ + public function index() { + + // Tableau des langues rédigées + foreach (self::$i18nList as $key => $value) { + if ($this->getData(['config','i18n', $key]) === 'site' || + $key === 'fr') { + $i18nSites[$key] = $value; + } + } + // Lister les modules installés + $infoModules = helper::getModules(); + + // Parcourir les langues du site traduit et recherche les modules affectés à des pages + foreach ($i18nSites as $keyi18n=>$valuei18n) { + + // Clés moduleIds dans les pages de la langue + $pages = json_decode(file_get_contents(self::DATA_DIR . $keyi18n . '/' . 'page.json'), true); + + // Extraire les clés des modules + $pagesModules [$keyi18n] = array_filter(helper::arrayCollumn($pages['page'],'moduleId', 'SORT_DESC'), 'strlen'); + + // Générer ls liste des pages avec module pour la sauvegarde ou le backup + foreach( $pagesModules [$keyi18n] as $key=>$value ) { + if (!empty($value)) { + $pagesInfos [$keyi18n] [$key] ['pageId'] = $key ; + $pagesInfos [$keyi18n] [$key] ['title'] = $this->getData(['page', $key, 'title' ]) ; + $pagesInfos [$keyi18n] [$key] ['moduleId'] = $value; + } + } + } + + + // Recherche des modules orphelins dans toutes les langues + $orphans = $installed = array_flip(array_keys ($infoModules)); + foreach ($i18nSites as $keyi18n=>$valuei18n) { + // Générer la liste des modules orphelins + foreach ($infoModules as $key=>$value) { + // Supprimer les éléments affectés + if (array_search($key, $pagesModules[$keyi18n]) ) { + unset($orphans [$key]); + } + + } + } + $orphans = array_flip($orphans); + + // Mise en forme du tableau des modules orphelins + if (isset($orphans)) { + foreach ($orphans as $key) { + // Construire le tableau de sortie + self::$modulesOrphan [] = [ + $infoModules [$key] ['realName'], + $key, + $infoModules [$key] ['version'], + '', + $infoModules[$key] ['delete'] === true + ? template::button('moduleDelete' . $key, [ + 'class' => 'moduleDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' .$key . '/' . $_SESSION['csrf'], + 'value' => template::ico('cancel'), + 'help' => 'Supprimer le module' + ]) + : '', + + ]; + } + } + + // Modules installés non orphelins + // Mise en forme du tableau des modules utilisés + if (isset($installed)) { + foreach (array_flip($installed) as $key) { + // Construire le tableau de sortie + self::$modulesInstalled [] = [ + $infoModules [$key] ['realName'], + $key, + $infoModules [$key] ['version'], + '', + template::button('moduleSave' . $key, [ + 'href' => helper::baseUrl() . $this->getUrl(0) . '/save/filemanager/' .$key . '/' . $_SESSION['csrf'], + 'value' => template::ico('download-cloud'), + 'help' => 'Sauvegarder le module dans le gestionnaire de fichiers' + ]), + template::button('moduleDownload' . $key, [ + 'href' => helper::baseUrl() . $this->getUrl(0) . '/save/download/' .$key . '/' . $_SESSION['csrf'], + 'value' => template::ico('download'), + 'help' => 'Sauvegarder et télécharger le module' + ]) + + ]; + } + } + + + // Mise en forme du tableau des modules employés dans les pages + // Avec les commandes de sauvegarde et de restauration + //foreach ($pagesInfos as $keyi18n=>$valueI18n) { + + $keyi18n = self::$i18n; + $valueI18n = $pagesInfos[self::$i18n]; + foreach ($valueI18n as $keyPage=>$value) { + // Construire le tableau de sortie + self::$modulesData[] = [ + $infoModules[$pagesInfos[$keyi18n][$keyPage]['moduleId']] ['realName'], + $pagesInfos[$keyi18n][$keyPage]['moduleId'], + $infoModules[$pagesInfos [$keyi18n][$keyPage]['moduleId']] ['version'], + //template::flag($keyi18n, '20px'), + '' . $pagesInfos [$keyi18n][$keyPage]['title'] . ' (' .$keyPage . ')', + template::button('dataExport' . $keyPage, [ + 'href' => helper::baseUrl(). $this->getUrl(0) . '/dataExport/' . $keyi18n . '/' . $pagesInfos[$keyi18n][$keyPage]['moduleId'] . '/' . $keyPage . '/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre + 'value' => template::ico('download'), + 'help' => 'Exporter les données du module' + ]), + template::button('dataDelete' . $keyPage, [ + 'href' => helper::baseUrl(). $this->getUrl(0) . '/dataDelete/' . $keyi18n . '/' . $pagesInfos[$keyi18n][$keyPage]['moduleId'] . '/' . $keyPage . '/' . $_SESSION['csrf'],// appel de fonction vaut exécution, utiliser un paramètre + 'value' => template::ico('cancel'), + 'class' => 'buttonRed dataDelete', + 'help' => 'Détacher le module de la page', + ]) + + ]; + } + + //} + + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Gestion des modules', + 'view' => 'index' + ]); + } + + + /** + * Sauvegarde un module sans les données + */ + + public function save() { + // Jeton incorrect + if ($this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => false, + 'notification' => 'Action non autorisée' + ]); + } else { + + // Créer un dossier temporaire + $tmpFolder = self::TEMP_DIR . uniqid(); + if (!is_dir($tmpFolder)) { + mkdir($tmpFolder, 0755); + } + + //Nom de l'archive + $fileName = $this->getUrl(3) . '.zip'; + $this->makeZip ($tmpFolder . '/' . $fileName, 'module/' . $this->getUrl(3)); + + switch ($this->getUrl(2)) { + case 'filemanager': + if (!file_exists(self::FILE_DIR . 'source/modules')) { + mkdir(self::FILE_DIR . 'source/modules'); + } + $success = copy($tmpFolder . '/' . $fileName , self::FILE_DIR . 'source/modules/' . $this->getUrl(3) . '.zip' ); + + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'notification' => $success ? $this->getUrl(3) . '.zip copié dans le dossier Module du gestionnaire de fichier' : 'Erreur de copie', + 'state' => $success + ]); + break; + case 'download': + default: + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . $fileName . '"'); + header('Content-Length: ' . filesize($tmpFolder . '/' . $fileName)); + ob_clean(); + ob_end_flush(); + readfile( $tmpFolder . '/' .$fileName); + exit(); + break; + } + // Nettoyage + unlink($tmpFolder . '/' . $fileName); + $this->removeDir($tmpFolder); + } + } + + + /* + * Détacher un module d'une page en supprimant les données du module + * 2 : i18n id + * 3 : moduleId + * 4 : pageId + * 5 : CSRF + */ + public function dataDelete() { + // Jeton incorrect + if ($this->getUrl(5) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => false, + 'notification' => 'Action non autorisée' + ]); + } else { + $this->setData(['page', $this->getUrl(4), 'moduleId', '']); + $this->deleteData(['module', $this->getUrl(4)]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'notification' => 'Le module ' . $this->getUrl(3) . ' de la page '. $this->getUrl(4) . ' a été supprimé.', + 'state' => true + ]); + } + + + } + + + /* + * Export des données d'un module + * Structure de l'adresse reçue + * 2 : i18n id + * 3 : moduleId + * 4 : pageId + * 5 : CSRF + */ + public function dataExport() { + // Jeton incorrect + if ($this->getUrl(5) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => false, + 'notification' => 'Action non autorisée' + ]); + } else { + + // Créer un dossier temporaire + $tmpFolder = self::TEMP_DIR . uniqid(); + if (!is_dir($tmpFolder)) { + mkdir($tmpFolder, 0755); + } + + + // Copie des infos sur le module + $modulesData = json_decode(file_get_contents(self::DATA_DIR . $this->getUrl(2) . '/module.json' ), true); + $moduleData = $modulesData['module'] [$this->getUrl(4)]; + $success = file_put_contents ($tmpFolder . '/module.json', json_encode($moduleData)); + + // Le dossier du module s'il existe + if (is_dir(self::DATA_DIR . $this->getUrl(3) . '/' . $this->getUrl(4) ) ) { + // Copier le dossier des données + $success .= $this->copyDir(self::DATA_DIR . $this->getUrl(3) . '/' . $this->getUrl(4), $tmpFolder); + } + + // Descripteur de l'archive + $infoModule = helper::getModules(); + $success .= file_put_contents ($tmpFolder . '/descripteur.json', json_encode( [$this->getUrl(3) => $infoModule [$this->getUrl(3)]] )); + + + // création du zip + if ($success) + { + $fileName = $this->getUrl(2) . '-' . $this->getUrl(3) . '-' . $this->getUrl(4) . '.zip'; + $this->makeZip ($fileName, $tmpFolder); + if (file_exists($fileName)) { + ob_start(); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . $fileName . '"'); + header('Content-Length: ' . filesize($fileName)); + ob_clean(); + ob_end_flush(); + readfile( $fileName); + unlink($fileName); + $this->removeDir($tmpFolder); + exit(); + } + } else { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'notification' => 'Quelque chose s\'est mal passé', + 'state' => false + ]); + } + } + } + + /* + * Importer des données d'un module externes ou interne à module.json + */ + public function dataImport(){ + + // Soumission du formulaire d'importation du module dans une page libre + if($this->isPost()) { + // Récupérer le fichier et le décompacter + $zipFilename = $this->getInput('pluginImportFile', helper::FILTER_STRING_SHORT, true); + $targetPage = $this->getInput('pluginImportPage', helper::FILTER_STRING_SHORT, true); + $tempFolder = uniqid(); + + // Extraction dans un dossier temporaire + mkdir (self::TEMP_DIR . $tempFolder, 0755); + $zip = new ZipArchive(); + if ($zip->open(self::FILE_DIR . 'source/' . $zipFilename) === TRUE) { + $zip->extractTo(self::TEMP_DIR . $tempFolder ); + } + + // Lire le descripteur + $descripteur = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . '/descripteur.json'), true); + + // Lecture des données du module + $moduleData = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . '/module.json'), true ); + // Chargement des données du module importé + $this->setData(['module', $targetPage, $moduleData ]); + // Intégration des données du module importé dans la page + $this->setData(['page', $targetPage ,'moduleId', array_key_first($descripteur) ]); + + // Supprimer le dossier temporaire + $this->removeDir(self::TEMP_DIR . $tempFolder); + $zip->close(); + + + + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => true, + 'notification' => 'Import des données effectué' + ]); + } + // Bouton d'importation des données d'un module spécifique + if (count(explode('/',$this->getUrl())) === 6) { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => false, + 'notification' => 'Action non autorisée' + ]); + } + + // Traitement + + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . 'plugin', + 'state' => true, + 'notification' => 'Okay' + ]); + } + + + /** + * Liste des pages sans module + * et ne sont pas des barres latérales + */ + self::$pagesList = $this->getHierarchy(null, null, null); + foreach(self::$pagesList as $page => $pageId) { + if ($this->getData(['page',$page,'block']) === 'bar' || + //$this->getData(['page',$page,'disable']) === true || + $this->getData(['page',$page,'moduleId']) !== '' + ) { + unset(self::$pagesList[$page]); + } + } + self::$pagesList = array_keys(self::$pagesList); + + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Importer des données de module', + 'view' => 'dataImport' + ]); + } + + + +} diff --git a/core/module/addon/view/import/import.css b/core/module/plugin/view/dataImport/dataImport.css similarity index 100% rename from core/module/addon/view/import/import.css rename to core/module/plugin/view/dataImport/dataImport.css diff --git a/core/module/plugin/view/dataImport/dataImport.php b/core/module/plugin/view/dataImport/dataImport.php new file mode 100644 index 00000000..43b00552 --- /dev/null +++ b/core/module/plugin/view/dataImport/dataImport.php @@ -0,0 +1,35 @@ + +
    +
    + 'buttonGrey', + 'href' => helper::baseUrl() . 'plugin', + 'value' => template::ico('left') + ]); ?> +
    +
    + 'Appliquer' + ]); ?> +
    +
    +
    +
    +
    +

    Installer des données de module

    +
    +
    + 'Archive ZIP :', + 'type' => 2 + ]); ?> +
    +
    + 'Importer le module dans la page ' . template::flag('site', '20px') + ]); ?> +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/core/module/addon/view/index/index.css b/core/module/plugin/view/index/index.css similarity index 89% rename from core/module/addon/view/index/index.css rename to core/module/plugin/view/index/index.css index 52709ea6..fdb57b6f 100644 --- a/core/module/addon/view/index/index.css +++ b/core/module/plugin/view/index/index.css @@ -15,4 +15,8 @@ /** NE PAS EFFACER * admin.css -*/ \ No newline at end of file +*/ + +.activeButton { + filter : brightness(150%); +} \ No newline at end of file diff --git a/core/module/plugin/view/index/index.js.php b/core/module/plugin/view/index/index.js.php new file mode 100644 index 00000000..060d7e18 --- /dev/null +++ b/core/module/plugin/view/index/index.js.php @@ -0,0 +1,50 @@ +/** + * This file is part of Zwii. + * For full copyright and license information, please see the LICENSE + * file that was distributed with this source code. + * + * @author Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2022, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Confirmation de suppression + */ +$(".moduleDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir désinstaller ce module ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation de suppression + */ + $(".dataDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer le module de cette page ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +// Sélecteur de fonctions + +$("#configManageModuleButton").on("click", function () { + console.log("clic"); + $("#manageDatas").hide(); + $("#manageModules").show(); + $("#configManageModuleButton").addClass("activeButton"); + $("#configManageDatasButton").removeClass("activeButton"); + setCookie("pluginLayout", "module"); +}); +$("#configManageDatasButton").on("click", function () { + $("#manageModules").hide(); + $("#manageDatas").show(); + $("#configManageModuleButton").removeClass("activeButton"); + $("#configManageDatasButton").addClass("activeButton"); + setCookie("pluginLayout", "data"); +}); diff --git a/core/module/plugin/view/index/index.php b/core/module/plugin/view/index/index.php new file mode 100644 index 00000000..24ca3fe7 --- /dev/null +++ b/core/module/plugin/view/index/index.php @@ -0,0 +1,105 @@ +
    +
    + 'buttonGrey', + 'href' => helper::baseUrl(), + 'value' => template::ico('left') + ]); ?> +
    +
    + 'https://doc.zwiicms.fr/gestion-des-modules', + 'target' => '_blank', + 'value' => template::ico('help'), + 'class' => 'buttonHelp', + 'help' => 'Consulter l\'aide en ligne' + ]); ?> +
    +
    +
    +
    +
    +
    + 'Modules installés', + 'class' => 'activeButton' + ]); ?> +
    +
    + 'Données des modules' + ]); ?> +
    +
    +
    +
    +
    +
    +
    +
    +

    Installation / mise à jour d'un module

    +
    +
    + helper::baseUrl() . 'plugin/store', + 'value' => template::ico('shopping-basket') . ' Catalogue en ligne' + ]); ?> +
    +
    + helper::baseUrl() . 'plugin/upload', + 'value' => template::ico('upload') . ' Depuis une archive ZIP' + ]); ?> +
    +
    +
    +
    +
    + +
    +
    +
    +

    Sauvegarde des modules installés

    + +
    +
    +
    + + + + +
    +
    +
    +

    Désinstallation des modules orphelins

    + +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Modules configurés

    +
    +
    + helper::baseUrl() . 'plugin/dataImport', + 'value' => template::ico('upload'), + "help" => 'Importer des données de module dans une page libre' + ]); ?> +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/core/module/addon/view/item/item.css b/core/module/plugin/view/item/item.css similarity index 100% rename from core/module/addon/view/item/item.css rename to core/module/plugin/view/item/item.css diff --git a/core/module/addon/view/item/item.php b/core/module/plugin/view/item/item.php similarity index 100% rename from core/module/addon/view/item/item.php rename to core/module/plugin/view/item/item.php diff --git a/core/module/addon/view/store/store.css b/core/module/plugin/view/store/store.css similarity index 100% rename from core/module/addon/view/store/store.css rename to core/module/plugin/view/store/store.css diff --git a/core/module/plugin/view/store/store.php b/core/module/plugin/view/store/store.php new file mode 100644 index 00000000..f06bf536 --- /dev/null +++ b/core/module/plugin/view/store/store.php @@ -0,0 +1,14 @@ +
    +
    + 'buttonGrey', + 'href' => helper::baseUrl() . 'plugin', + 'value' => template::ico('left') + ]); ?> +
    +
    + + + + + \ No newline at end of file diff --git a/core/module/addon/view/upload/upload.css b/core/module/plugin/view/upload/upload.css similarity index 100% rename from core/module/addon/view/upload/upload.css rename to core/module/plugin/view/upload/upload.css diff --git a/core/module/addon/view/upload/upload.php b/core/module/plugin/view/upload/upload.php similarity index 70% rename from core/module/addon/view/upload/upload.php rename to core/module/plugin/view/upload/upload.php index 016418c6..c4104450 100644 --- a/core/module/addon/view/upload/upload.php +++ b/core/module/plugin/view/upload/upload.php @@ -1,30 +1,27 @@
    -
    +
    'buttonGrey', - 'href' => helper::baseUrl() . 'addon', - 'ico' => 'left', - 'value' => 'Retour' + 'href' => helper::baseUrl() . 'plugin', + 'value' => template::ico('left') ]); ?>
    -
    - + 'https://doc.zwiicms.fr/installation-d-un-module', + 'target' => '_blank', 'class' => 'buttonHelp', - 'ico' => 'help', - 'value' => 'Aide' + 'value' => template::ico('help'), + 'help' => 'Consulter l\'aide en ligne' ]); ?>
    -
    +
    'Valider', 'ico' => 'check' ]); ?>
    -
    - -
    -