diff --git a/core/core.php b/core/core.php index d268d52b..ca013a77 100644 --- a/core/core.php +++ b/core/core.php @@ -35,6 +35,7 @@ class common { const DATA_DIR = 'site/data/'; const FILE_DIR = 'site/file/'; const TEMP_DIR = 'site/tmp/'; + const MODULE_DIR = 'module/'; // Miniatures de la galerie const THUMBS_SEPARATOR = 'mini_'; @@ -180,7 +181,7 @@ class common { ]; public static $fontsWebSafe = [ - 'arial' => [ + 'arial' => [ 'name' => 'Arial', 'font-family' => 'Arial, Helvetica, sans-serif', 'resource' => 'websafe' @@ -1137,30 +1138,52 @@ class common { * @param string $dst dossier destination * @return bool */ - public function copyDir($src, $dst) { - // Ouvrir le dossier source - $dir = opendir($src); - // Créer le dossier de destination - if (!is_dir($dst)) - $success = mkdir($dst, 0755, true); - else - $success = true; + function copyDir( string $sourceDirectory, string $destinationDirectory, string $childFolder = '') { - // Boucler dans le dossier source en l'absence d'échec de lecture écriture - while( $success - AND $file = readdir($dir) ) { - if (( $file != '.' ) && ( $file != '..' )) { - if ( is_dir($src . '/' . $file) ){ - // Appel récursif des sous-dossiers - $success = $success OR $this->copyDir($src . '/' . $file, $dst . '/' . $file); + $success = true; + $directory = opendir($sourceDirectory); + + if (is_dir($destinationDirectory) === false) { + mkdir($destinationDirectory); + } + + if ($childFolder !== '') { + if (is_dir("$destinationDirectory/$childFolder") === false) { + mkdir("$destinationDirectory/$childFolder"); + } + + while (($file = readdir($directory)) !== false) { + if ($file === '.' || $file === '..') { + continue; } - else { - $success = $success OR copy($src . '/' . $file, $dst . '/' . $file); + + if (is_dir("$sourceDirectory/$file") === true) { + $success = $success && $this->copyDir("$sourceDirectory/$file", "$destinationDirectory/$childFolder/$file"); + } else { + $success = $success && copy("$sourceDirectory/$file", "$destinationDirectory/$childFolder/$file"); } } + + closedir($directory); + + return; } - closedir($dir); - return $success; + + while (($file = readdir($directory)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + + if (is_dir("$sourceDirectory/$file") === true) { + $success = $success && $this->copyDir("$sourceDirectory/$file", "$destinationDirectory/$file"); + } + else { + $success = $success && copy("$sourceDirectory/$file", "$destinationDirectory/$file"); + } + } + + closedir($directory); + return($success); } @@ -2160,9 +2183,9 @@ class common { elseif( $moduleId AND in_array($moduleId, self::$coreModuleIds) === false - AND file_exists('module/' . $moduleId . '/vendor/' . $vendorName . '/inc.json') + AND file_exists(self::MODULE_DIR . $moduleId . '/vendor/' . $vendorName . '/inc.json') ) { - $vendorPath = 'module/' . $moduleId . '/vendor/' . $vendorName . '/'; + $vendorPath = self::MODULE_DIR . $moduleId . '/vendor/' . $vendorName . '/'; } // Sinon continue else { @@ -2660,8 +2683,8 @@ class core extends common { require 'core/module/' . $classPath; } // Module - elseif(is_readable('module/' . $classPath)) { - require 'module/' . $classPath; + elseif(is_readable(self::MODULE_DIR . $classPath)) { + require self::MODULE_DIR . $classPath; } // Librairie elseif(is_readable('core/vendor/' . $classPath)) { @@ -2810,7 +2833,7 @@ class core extends common { 'title' => $title, 'content' => $this->getPage($this->getUrl(0), self::$i18n) . // Concatène avec les paramètres avancés. - $this->getData(['page', $this->getUrl(0), 'css']) . + $this->getData(['page', $this->getUrl(0), 'css']) . $this->getData(['page', $this->getUrl(0), 'js']), 'metaDescription' => $this->getData(['page', $this->getUrl(0), 'metaDescription']), 'metaTitle' => $this->getData(['page', $this->getUrl(0), 'metaTitle']), @@ -2945,7 +2968,7 @@ class core extends common { // Chemin en fonction d'un module du coeur ou d'un module $modulePath = in_array($moduleId, self::$coreModuleIds) ? 'core/' : ''; // CSS - $stylePath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.css'; + $stylePath = $modulePath . self::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.css'; if(file_exists($stylePath)) { $this->addOutput([ 'style' => file_get_contents($stylePath) @@ -2958,7 +2981,7 @@ class core extends common { } // JS - $scriptPath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.js.php'; + $scriptPath = $modulePath . self::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.js.php'; if(file_exists($scriptPath)) { ob_start(); include $scriptPath; @@ -2967,7 +2990,7 @@ class core extends common { ]); } // Vue - $viewPath = $modulePath . 'module/' . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.php'; + $viewPath = $modulePath . self::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.php'; if(file_exists($viewPath)) { ob_start(); include $viewPath; diff --git a/core/module/plugin/plugin.php b/core/module/plugin/plugin.php index 553acb81..510a9f6b 100644 --- a/core/module/plugin/plugin.php +++ b/core/module/plugin/plugin.php @@ -31,7 +31,7 @@ class plugin extends common { // URL des modules const BASEURL_STORE = 'https://store.zwiicms.fr/'; - const MODULE_STORE = '?modules/'; + const MODULE_STORE = 'modules-pour-zwii-v12/'; // Gestion des modules public static $modulesData = []; @@ -100,14 +100,9 @@ class plugin extends common { */ private function install($moduleFileName, $checkValid){ - /** - * Initialisation des variables - */ - //$tempFolder = uniqid() . '/'; - $tempFolder = 'truc/'; - $notification = ''; - // Numéro de version du module déjà installé - $versionInstalled = ''; + // Dossier temporaire + $tempFolder = uniqid() . '/'; + //$tempFolder = 'truc/'; /** * Désarchivage @@ -115,123 +110,143 @@ class plugin extends common { $zip = new ZipArchive(); if ($zip->open($moduleFileName) === TRUE) { - /** - * Création du dossier temporaire - */ + + //Création du dossier temporaire et extraction if (!is_dir (self::TEMP_DIR . $tempFolder) ) { mkdir (self::TEMP_DIR . $tempFolder, 0755); } - $zip->extractTo(self::TEMP_DIR . $tempFolder ); - // Lecture du descripteur de ressource - $modulePath = self::TEMP_DIR . $tempFolder . 'module'; - if (file_exists(self::TEMP_DIR . $tempFolder . 'desc.json') - ) { - $res = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . 'desc.json'), true); - } else { - return([ - 'success' => false, - 'notification'=> 'Cette archive est invalide' - ]); - } + /** - * Structure lue - * $res ['name'] = id du module, correspond à la classe - * $res ['realname'] = Nom complet du module - * $res ['version'] = version du module - * $res ['dirs'] @array + * Lecture du descripteur de ressource + * $module ['name'] = id du module, correspond à la classe + * $module ['realname'] = Nom complet du module + * $module ['version'] = version du module + * $module ['dirs'] @array * 'dossier' => 'destination', * 'download" => 'module/download' - * - * Attention le dossier source est celui dans l'archive, la destination correspond à l’arborescence cible */ - echo "
";
-			// Affectation
-			$moduleName = $res ['name'];
-			$modulePath = $res ['name'] . '/';
-			$moduleFile = $res ['name'] . '.php' ;
+
+			if (file_exists(self::TEMP_DIR . $tempFolder . 'desc.json')
+				) {
+					$module = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . 'desc.json'), true);
+				} else {
+					// Message de retour
+					$this->removeDir(self::TEMP_DIR . $tempFolder);
+					$zip->close();
+					return([
+						'success' => false,
+						'notification'=> 'Archive invalide, le descripteur est absent.'
+					]);
+			}
 
 			/**
 			 * Validation des informations du descripteur
 			 */
-			if ( !is_dir ( self::TEMP_DIR . $tempFolder . $modulePath) ||
-				 !is_file (self::TEMP_DIR . $tempFolder . $modulePath . $moduleFile)
-				) {
+			foreach ($module['dirs'] as $src => $dest) {
+				// Vérification de la présence des dossier décrits
+				if ( !is_dir (self::TEMP_DIR . $tempFolder . $src )) {
+					// Message de retour
+					$this->removeDir(self::TEMP_DIR . $tempFolder);
+					$zip->close();
 					return([
 						'success' => false,
-						'notification'=> 'Cette archive est invalide'
+						'notification'=> 'Archive invalide, les dossiers ne correspondent pas au descripteur.'
 					]);
+				}
+				// Interdire l'écriture dans le dossier core
+				if ( strstr($dest, 'core') !== false ) {
+					// Message de retour
+					$this->removeDir(self::TEMP_DIR . $tempFolder);
+					$zip->close();
+					return([
+						'success' => false,
+						'notification'=> 'Archive invalide, l\'écriture dans le dossier core est interdite'
+					]);
+				}
 			}
+
 			/**
-			 * Le module est-il installé ? Lire le numéro de version
+			 * Validation de la présence du fichier de base du module
 			 */
-			if (is_file( 'module/' . $modulePath . $moduleFile) ) {
+			if ( !file_exists(self::TEMP_DIR . $tempFolder . $module['name'] . '/' . $module['name'] . '.php')) {
+				// Message de retour
+				$this->removeDir(self::TEMP_DIR . $tempFolder);
+				$zip->close();
+				return([
+					'success' => false,
+					'notification'=> 'Cette archive est invalide, le fichier de classe est absent.'
+				]);
+			}
+
+
+			/**
+			 * Le module est-il déjà installé ?
+			 * Si oui lire le numéro de version et le stocker dans $versionInstalled
+			 */
+			if (is_file( self::MODULE_DIR .  $module['name'] . '/' .  $module['name'] . '.php') ) {
 				$c = helper::getModules();
-				if (array_key_exists($moduleName, $c)) {
-					$versionInstalled = $c[$moduleName]['version'];
+				if (array_key_exists($module['name'], $c)) {
+					$versionInstalled = $c[$module['name']]['version'];
 				}
 			}
-					// 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';
-							}
-						}
+			// Le module est installé, contrôle de la version
+			$installOk = false;
+			if ( isset($versionInstalled) === false ) {
+				$installOk = true;
+			} elseif (	version_compare($module['version'], $versionInstalled) >= 0 ) {
+				$installOk = true;
+			} else {
+				if (version_compare($module['version'], $versionInstalled) === -1 ) {
+					// Contrôle du forçage
+					if ($this->getInput('configModulesCheck', helper::FILTER_BOOLEAN) === true) {
+						$installOk = true;
+					} else {
+						// Message de retour
+						$this->removeDir(self::TEMP_DIR . $tempFolder);
+						$zip->close();
+						return([
+							'success' => false,
+							'notification'=> 'La version installée est plus récente, la mise à jour peut être forcée en cochant l\'option.'
+						]);
 					}
 				}
-			// Supprimer le dossier temporaire même si le module est invalide
-			$this->removeDir(self::TEMP_DIR . $tempFolder);
-			$zip->close();
+			}
+
+			// Installation ou mise à jour du module valides
+			if ($installOk) {
+				// Copie récursive des dossiers
+				foreach ($module['dirs'] as $src => $dest) {
+					if (!is_dir (self::TEMP_DIR . $tempFolder . $src)) {
+						mkdir(self::TEMP_DIR . $tempFolder . $src);
+					}
+					$success = $this->copyDir( self::TEMP_DIR . $tempFolder . $src, $dest );
+				}
+				// Message de retour
+				$t = isset($versionInstalled) ? ' actualisé' : 'installé';
+				$this->removeDir(self::TEMP_DIR . $tempFolder);
+				$zip->close();
+				return(['success' => $success,
+						'notification'=> $success ? 'Le module '.$module['name'].' a été ' . $t
+												  : 'Erreur inconnue, le module n\'est pas installé'
+				]);
+			} else {
+				return([
+					'success' => false,
+					'notification'=> 'Une erreur inconnue s\est produite !'
+				]);
+				// Supprimer le dossier temporaire
+				$this->removeDir(self::TEMP_DIR . $tempFolder);
+				$zip->close();
+			}
 		} else {
-			// erreur à l'ouverture
-			$success = false;
-			$notification = 'Impossible d\'ouvrir l\'archive';
+			// Message de retour
+			return(['success' => $success,
+					'notification'=> 'Impossible d\'ouvrir l\'archive'
+			]);
 		}
-		return(['success' => $success,
-				'notification'=> $notification
-		]);
+
 	}
 
 	/***
@@ -294,21 +309,19 @@ class plugin extends common {
 			// 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);
-			*}
-			*/
+			// Installation directe
+			if ( file_exists(self::FILE_DIR . 'source/modules/' . $moduleFile) ) {
+				$r = $this->install(self::FILE_DIR . 'source/modules/' . $moduleFile, false);
+			} else {
+				$r['notification'] = 'Un problème est survenu, le module n\'est pas installé';
+				$r['success'] = false;
+			}
+			// Valeurs en sortie
 			$this->addOutput([
 				'redirect' => helper::baseUrl()  . 'plugin/store',
-				'notification' => $moduleFile . ' téléchargé dans le dossier modules du gestionnaire de fichiers',
-				'state' => true
+				'notification' => $r['notification'],
+				'state' => $r['success']
 			]);
-
 		}
 		// Valeurs en sortie
 		$this->addOutput([
@@ -548,7 +561,7 @@ class plugin extends common {
 
 			//Nom de l'archive
 			$fileName =  $this->getUrl(3) . '.zip';
-			$this->makeZip ($tmpFolder . '/' . $fileName, 'module/' .  $this->getUrl(3));
+			$this->makeZip ($tmpFolder . '/' . $fileName, self::MODULE_DIR .  $this->getUrl(3));
 
 			switch ($this->getUrl(2)) {
 				case 'filemanager':
diff --git a/core/module/plugin/view/index/index.php b/core/module/plugin/view/index/index.php
index 30939f06..0c33f68d 100644
--- a/core/module/plugin/view/index/index.php
+++ b/core/module/plugin/view/index/index.php
@@ -15,11 +15,18 @@
 			'help' => 'Consulter l\'aide en ligne'
 		]); ?>
 	
-	
+
helper::baseUrl() . 'plugin/store', - 'ico' => 'shopping-basket', - 'value' => 'Catalogue en ligne' + 'value' => template::ico('shopping-basket'), + 'help' => 'Installer depuis le catalogue en ligne' + ]); ?> +
+
+ helper::baseUrl() . 'plugin/upload', + 'value' => template::ico('file-archive'), + 'help' => 'Installer depuis une archive' ]); ?>
diff --git a/core/module/plugin/view/store/store.php b/core/module/plugin/view/store/store.php index 8134b91c..f06bf536 100644 --- a/core/module/plugin/view/store/store.php +++ b/core/module/plugin/view/store/store.php @@ -6,12 +6,6 @@ 'value' => template::ico('left') ]); ?> -
- helper::baseUrl() . 'plugin/upload', - 'value' => 'Installer' - ]); ?> -
diff --git a/module/version/version.php b/module/version/version.php new file mode 100644 index 00000000..284675e6 --- /dev/null +++ b/module/version/version.php @@ -0,0 +1,36 @@ + * + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @link http://zwiicms.com/ + */ + +class version extends common { + + const VERSION = '1.0'; + const REALNAME = 'Version'; + const DELETE = true; + const UPDATE = '0.0'; + const DATADIRECTORY = ''; // Contenu localisé inclus par défaut (page.json et module.json) + + public static $actions = [ + 'index'=> self::GROUP_VISITOR + + ]; + + /** + * Retourne le numéro de version + */ + + public function index() { + exit( common::ZWII_VERSION); + } + +} diff --git a/module/version/view/index/index.php b/module/version/view/index/index.php new file mode 100644 index 00000000..2263730f --- /dev/null +++ b/module/version/view/index/index.php @@ -0,0 +1 @@ +