Просмотр исходного кода

#73 - Download(Install)of Modules and Updates using Transient Cron.

AntonLV 12 лет назад
Родитель
Сommit
d03ec503cb

+ 80 - 0
inc/classes/BxDolCronQuery.php

@@ -0,0 +1,80 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolDb');
+
+/**
+ * Database queries for Cron
+ * @see BxDolCron
+ */
+class BxDolCronQuery extends BxDolDb implements iBxDolSingleton
+{
+    function __construct()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    /**
+     * Get singleton instance of the class
+     */
+    static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolCronQuery();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    public function getJobs()
+    {
+    	return $this->fromCache('sys_cron_jobs', 'getAll', "SELECT * FROM `sys_cron_jobs`");
+    }
+
+    public function addTransientJobClass($sName, $sClass, $sFile)
+    {
+    	$sQuery = $this->prepare("INSERT INTO `sys_cron_jobs` SET `name`=?, `time`='transient', `class`=?, `file`=?", $sName, $sClass, $sFile);
+        return (int)$this->query($sQuery) > 0;
+    }
+
+	public function addTransientJobService($sName, $mixedService)
+    {
+    	if(is_array($mixedService))
+    		$mixedService = call_user_func_array('BxDolService::getSerializedService', $mixedService);
+
+    	if(!BxDolService::isSerializedService($mixedService))
+    		return false;
+
+    	$sQuery = $this->prepare("INSERT INTO `sys_cron_jobs` SET `name`=?, `time`='transient', `service_call`=?", $sName, $mixedService);
+        return (int)$this->query($sQuery) > 0;
+    }
+
+    public function getTransientJobs()
+    {
+    	return $this->getAllWithKey("SELECT * FROM `sys_cron_jobs` WHERE `time`='transient'", 'name');
+    }
+
+    public function deleteTransientJobs()
+    {
+    	return $this->query("DELETE FROM `sys_cron_jobs` WHERE `time`='transient'");
+    }
+}
+
+/** @} */

+ 1 - 30
inc/classes/BxDolFile.php

@@ -195,36 +195,7 @@ class BxDolFile extends BxDol implements iBxDolSingleton
 
 	protected function _mkDirR($sPath)
     {
-		$sPathFull = '';
-        $sPathCurrent = getcwd();
-
-        $aParts = explode('/', $sPath);
-    	if($sPath[0] == '/') {
-            $sPathFull = '/';
-            @chdir($sPathFull);
-        }
-
-        foreach($aParts as $sPart) {
-            if(!$sPart)
-				continue;
-
-            $sPathFull .= $sPart;
-            if($sPart == '..')
-                @chdir($sPart);
-            else if(!@chdir($sPart)) {
-                if(!@mkdir($sPart)) {
-                    @chdir($sPathCurrent);
-                    return false;
-                }
-
-                @chdir($sPart);
-            }
-
-            $sPathFull .= '/';
-        }
-
-        @chdir($sPathCurrent);
-        return true;
+        return @mkdir($sPath, 0777, true);
     }
 }
 

+ 21 - 0
inc/classes/BxDolIO.php

@@ -14,6 +14,27 @@ class BxDolIO extends BxDol
         parent::__construct();
     }
 
+    public static function isRealOwner()
+    {
+    	if(defined('BX_DOL_CRON_EXECUTE'))
+    		trigger_error('Function can\'t be called under cron', E_USER_ERROR);
+
+		$sName = mktime() . rand(0, 999999999);
+		$sFilePath = BX_DIRECTORY_PATH_TMP . $sName . '.txt';
+		if(!$rHandler = fopen($sFilePath, 'w'))
+            return false;
+
+		if(!fwrite($rHandler, $sName))
+            return false;
+
+		fclose($rHandler);
+
+		$bResult = fileowner(BX_DIRECTORY_PATH_INC . 'utils.inc.php') === fileowner($sFilePath);
+		@unlink($sFilePath);
+
+		return $bResult;
+    }
+
     public static function isExecutable($sFile)
     {
         clearstatcache();

+ 19 - 0
inc/classes/BxDolService.php

@@ -89,6 +89,25 @@ class BxDolService extends BxDol
     {
         return preg_match('/^a:[\d+]:\{/', $s);
     }
+
+    /**
+     * Serialized service call array
+     */
+    public static function getSerializedService($mixedModule, $sMethod, $aParams = array(), $sClass = '')
+    {
+		$aService = array(
+			'module' => $mixedModule,
+			'method' => $sMethod,
+		);
+
+		if(!empty($aParams))
+			$aService['params'] = $aParams;
+
+		if(!empty($sClass))
+			$aService['class'] = $sClass;
+
+		return serialize($aService);
+    }
 }
 
 /** @} */

+ 1 - 3
inc/classes/BxDolUpgrader.php

@@ -178,9 +178,7 @@ class BxDolUpgrader extends BxDol
     public function setTransientUpgradeCronJob ($sUnpackedPath)
     {
         $sUnpackedPath = rtrim($sUnpackedPath, '/') . '/';
-        $oDb = BxDolDb::getInstance();
-        $sQuery = $oDb->prepare("INSERT INTO `sys_cron_jobs` SET `name` = 'sys_perform_upgrade', `time` = 'transient', `class` = 'BxDolUpgradeCron', `file` = ?", bx_ltrim_str($sUnpackedPath, BX_DIRECTORY_PATH_ROOT) . 'BxDolUpgradeCron.php');
-        return $oDb->query($sQuery);
+        return BxDolCronQuery::getInstance()->addTransientJobClass('sys_perform_upgrade', 'BxDolUpgradeCron', bx_ltrim_str($sUnpackedPath, BX_DIRECTORY_PATH_ROOT) . 'BxDolUpgradeCron.php');
     }
 
     public function getError()

+ 3 - 1
modules/boonex/english/data/langs/system.xml

@@ -822,6 +822,7 @@
 	<string name="_adm_mod_err_cannot_process_action"><![CDATA[Cannot process action. It does not have an associated handler.]]></string>
     <string name="_adm_mod_err_only_admin_can_perform_operations_with_modules"><![CDATA[Only admin can perform operations with modules]]></string>
 	<string name="_adm_mod_err_process_operation_failed"><![CDATA[Cannot {0} module from "{1}" directory]]></string>
+	<string name="_adm_mod_msg_process_operation_scheduled"><![CDATA[Operation was scheduled and will be performed in a few minutes. Please wait.]]></string>	
 	<string name="_adm_msg_modules_success_added_to_cart"><![CDATA[The product was successfully added to shopping cart.]]></string>
 	<string name="_adm_msg_modules_success_checkouted"><![CDATA[The payment was successfully accepted and should be processed automatically in a few minutes. Then the purchased product(s) will be available in Store -> Purchases.<br />Note. If it doesn't happen for some reason, you need to contact with product's vendor and provide him with your payment's details. He'll be able to process payment manually if the payment was received.]]></string>
 	<string name="_adm_msg_modules_success_delete"><![CDATA[The package was deleted successfully]]></string>
@@ -1171,7 +1172,8 @@
 	<string name="_adm_str_err_no_product_info"><![CDATA[Cannot load product info. Try again later.]]></string>
 	<string name="_adm_str_err_wrong_package_format"><![CDATA[Wrong package format.]]></string>
 	<string name="_adm_str_err_zip_not_available"><![CDATA[ZIP library is not available.]]></string>
-	<string name="_adm_str_msg_download_successfully"><![CDATA[Module was successfully downloaded. You may install it via Store -> Downloaded.]]></string>
+	<string name="_adm_str_msg_download_scheduled"><![CDATA[Download was scheduled. In a few minutes downloaded package will be available to install via Store -> Downloaded.]]></string>
+	<string name="_adm_str_msg_download_successfully"><![CDATA[Download was successfully finished. You may install it via Store -> Downloaded.]]></string>
 	<string name="_adm_str_txt_discount_csign"><![CDATA[({0}{1} for Premium)]]></string>
 	<string name="_adm_str_txt_items_by"><![CDATA[item(s) by]]></string>
 	<string name="_adm_str_txt_price_csign"><![CDATA[{0}{1}]]></string>

+ 6 - 9
periodic/cron.php

@@ -117,14 +117,14 @@ function runJob($aJob)
         BxDolService::callSerialized($aJob['service_call']);
 }
 
-
-$oDb = BxDolDb::getInstance();
+bx_import('BxDolLanguages');
+bx_import('BxDolCronQuery');
+$oDb = BxDolCronQuery::getInstance();
 
 // run one time transient jobs
-
-$aJobsTransient = $oDb->getAllWithKey("SELECT * FROM `sys_cron_jobs` WHERE `time` = 'transient'", 'name');
+$aJobsTransient = $oDb->getTransientJobs();
 if (!empty($aJobsTransient)) {
-    $oDb->query("DELETE FROM `sys_cron_jobs` WHERE `time` = 'transient'");
+	$oDb->deleteTransientJobs();
 
     foreach ($aJobsTransient as $aRow)
         runJob($aRow);
@@ -137,10 +137,7 @@ if (bx_check_maintenance_mode()) // don't run regular cron jobs when site is in
     exit;
 
 // run regular cron jobs
-
-bx_import('BxDolLanguages');
-
-$aJobs = $oDb->fromCache('sys_cron_jobs', 'getAll', "SELECT * FROM `sys_cron_jobs`");
+$aJobs = $oDb->getJobs();
 $aDate = getdate(time());
 foreach($aJobs as $aRow) {
     if (checkCronJob($aRow['time'], $aDate))

+ 119 - 40
studio/classes/BxDolStudioInstallerUtils.php

@@ -42,34 +42,14 @@ class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSin
         return $GLOBALS['bxDolClasses'][__CLASS__];
     }
 
-    public function perform($sDirectory, $sOperation, $aParams = array())
+    public function serviceDownloadFile($aItem, $bUseFtp)
     {
-        $sConfigFile = 'install/config.php';
-        $sInstallerFile = 'install/installer.php';
-        $sInstallerClass = $sOperation == 'update' ? 'Updater' : 'Installer';
-
-        $aConfig = self::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $sDirectory . $sConfigFile);
-
-        $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $sDirectory . $sInstallerFile;
-        if(!empty($aConfig) && file_exists($sPathInstaller)) {
-            require_once($sPathInstaller);
-
-            $sClassName = $aConfig['class_prefix'] . $sInstallerClass;
-            $oInstaller = new $sClassName($aConfig);
-            $aResult = $oInstaller->$sOperation($aParams);
-
-            $aResult = array(
-                'code' => $aResult['result'] ? 0 : 1,
-                'message' => $aResult['message'],
-            );
-        } 
-        else
-            $aResult = array(
-                'code' => 2,
-                'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory),
-            );
+    	return $this->performDownload($aItem, $bUseFtp);
+    }
 
-        return $aResult;
+	public function servicePerform($sDirectory, $sOperation, $aParams)
+    {
+    	return $this->performAction($sDirectory, $sOperation, $aParams);
     }
 
     public function getModules()
@@ -155,6 +135,21 @@ class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSin
         return $aUpdates;
     }
 
+    public function perform($sDirectory, $sOperation, $aParams = array())
+    {
+    	if(self::isRealOwner())
+    		return $this->performAction($sDirectory, $sOperation, $aParams);
+
+    	bx_import('BxDolCronQuery');
+		if(BxDolCronQuery::getInstance()->addTransientJobService('sys_perform_install', array('system', 'perform', array($sDirectory, $sOperation, $aParams), 'DolStudioInstallerUtils')))
+			return array('code' => 1, 'message' => _t('_adm_mod_msg_process_operation_scheduled'));
+
+		return array(
+			'code' => 2,
+            'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory),
+		);
+    }
+
     public function checkUpdates($bAuthorizedAccess = false)
     {
 		bx_import('BxDolModuleQuery');
@@ -214,39 +209,96 @@ class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSin
     }
 
     protected function downloadFile($aItem, $bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER)
+    {
+    	if(self::isRealOwner())
+    		return $this->performDownload($aItem, $bUseFtp);
+
+		bx_import('BxDolCronQuery');
+		if(BxDolCronQuery::getInstance()->addTransientJobService('sys_perform_download', array('system', 'download_file', array($aItem, $bUseFtp), 'DolStudioInstallerUtils')))
+			return _t('_adm_str_msg_download_scheduled');
+
+		return false;
+    }
+
+    protected function performDownload($aItem, $bUseFtp)
     {
         if(empty($aItem) || !is_array($aItem))
             return $aItem;
 
         //--- write ZIP archive.
-        $sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
-        if (!$rHandler = fopen($sFilePath, 'w'))
+        $sFilePath = '';
+        $mixedResult = $this->performWrite($aItem, $sFilePath);
+
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+        //--- Unarchive package.
+        $sPackagePath = '';
+        $mixedResult = $this->performUnarchive($sFilePath, $sPackagePath);
+
+        @unlink($sFilePath);
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+        //--- Copy unarchived package.
+        $mixedResult = $this->performCopy($sPackagePath, $bUseFtp);
+
+        @bx_rrmdir($sPackagePath);
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+        return true;
+    }
+
+    protected function performWrite($aItem, &$sFilePath)
+    {
+    	$sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
+
+    	if(file_exists($sFilePath))
+        	@unlink($sFilePath);
+
+        if(!$rHandler = fopen($sFilePath, 'w'))
             return _t('_adm_str_err_cannot_write');
 
-        if (!fwrite($rHandler, urldecode($aItem['content'])))
+        if(!fwrite($rHandler, urldecode($aItem['content'])))
             return _t('_adm_str_err_cannot_write');
 
         fclose($rHandler);
+        
+        return true;
+    }
 
-        //--- Unarchive package.
-        if(!class_exists('ZipArchive'))
+    protected function performUnarchive($sFilePath, &$sPackagePath)
+    {
+    	if(!class_exists('ZipArchive'))
             return _t('_adm_str_err_zip_not_available');
 
         $oZip = new ZipArchive();
         if($oZip->open($sFilePath) !== true)
             return _t('_adm_str_err_cannot_unzip_package');
 
-        $sPackageRootFolder = $oZip->numFiles > 0 ? $oZip->getNameIndex(0) : false;
-        if($sPackageRootFolder && file_exists(BX_DIRECTORY_PATH_TMP . $sPackageRootFolder)) // remove existing tmp folder with the same name
-            bx_rrmdir(BX_DIRECTORY_PATH_TMP . $sPackageRootFolder);
+        $sPackageFolder = '';
+        if($oZip->numFiles > 0)
+        	$sPackageFolder = $oZip->getNameIndex(0);
 
-        if($sPackageRootFolder && !$oZip->extractTo(BX_DIRECTORY_PATH_TMP))
+        if(empty($sPackageFolder))
+        	return _t('_adm_str_err_cannot_unzip_package');
+        
+		$sPackagePath = BX_DIRECTORY_PATH_TMP . $sPackageFolder;
+        if(file_exists($sPackagePath)) // remove existing tmp folder with the same name
+            @bx_rrmdir($sPackagePath);
+
+        if(!$oZip->extractTo(BX_DIRECTORY_PATH_TMP))
             return _t('_adm_str_err_cannot_unzip_package');
 
         $oZip->close();
 
-        //--- Move unarchived package.
-        if($bUseFtp) {
+        return true;
+    }
+
+    protected function performCopy($sPackagePath, $bUseFtp)
+    {
+    	if($bUseFtp) {
 	        $sLogin = getParam('sys_ftp_login');
 	        $sPassword = getParam('sys_ftp_password');
 	        $sPath = getParam('sys_ftp_dir');
@@ -267,14 +319,41 @@ class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSin
         	$oFile = BxDolFile::getInstance();
         }
 
-        $aConfig = self::getModuleConfig(BX_DIRECTORY_PATH_TMP . $sPackageRootFolder . 'install/config.php');
+        $aConfig = self::getModuleConfig($sPackagePath . 'install/config.php');
         if(empty($aConfig) || !is_array($aConfig) || empty($aConfig['home_dir']))
             return _t('_adm_str_err_wrong_package_format');
 
-        if(!$oFile->copy(BX_DIRECTORY_PATH_TMP . $sPackageRootFolder, 'modules/' . $aConfig['home_dir']))
+        if(!$oFile->copy($sPackagePath, 'modules/' . $aConfig['home_dir']))
             return _t('_adm_str_err_files_copy_failed');
 
-        return true;
+		return true;
+    }
+
+    protected function performAction($sDirectory, $sOperation, $aParams = array())
+    {
+        $sConfigFile = 'install/config.php';
+        $sInstallerFile = 'install/installer.php';
+        $sInstallerClass = $sOperation == 'update' ? 'Updater' : 'Installer';
+
+        $aConfig = self::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $sDirectory . $sConfigFile);
+
+        $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $sDirectory . $sInstallerFile;
+        if(empty($aConfig) || !file_exists($sPathInstaller))
+        	return array(
+        		'code' => 2, 
+        		'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory)
+        	); 
+
+		require_once($sPathInstaller);
+
+		$sClassName = $aConfig['class_prefix'] . $sInstallerClass;
+		$oInstaller = new $sClassName($aConfig);
+		$aResult = $oInstaller->$sOperation($aParams);
+
+        return array(
+        	'code' => $aResult['result'] ? 0 : 2,
+        	'message' => $aResult['message']
+        );
     }
 
     protected function getConfigModule($sConfigPath, $aInstalledPathes = array(), $aInstalledInfo = array())