bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER; } /** * 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 */ public static function getInstance() { if (!isset($GLOBALS['bxDolClasses'][__CLASS__])) $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolStudioInstallerUtils(); return $GLOBALS['bxDolClasses'][__CLASS__]; } public function serviceDownloadFileComplete($sFilePath, $sAutoAction = '') { return $this->downloadFileComplete($sFilePath, $sAutoAction); } public function servicePerformAction($sDirectory, $sOperation, $aParams) { return $this->perform($sDirectory, $sOperation, $aParams); } public function getModules($bTitleAsKey = true) { $aModules = array(); $oTemplate = BxDolStudioTemplate::getInstance(); $aInstalledInfo = $this->getInstalledInfo(); $sPath = BX_DIRECTORY_PATH_MODULES; if(($rHandleVendor = opendir($sPath)) !== false) { while(($sVendor = readdir($rHandleVendor)) !== false) { if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor)) continue; if(($rHandleModule = opendir($sPath . $sVendor)) !== false) { while(($sModule = readdir($rHandleModule)) !== false) { if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.') continue; $sModulePath = $sVendor . '/' . $sModule . '/'; $aModule = $this->getConfigModule($sPath . $sModulePath . 'install/config.php', !empty($aInstalledInfo[$sModulePath]) ? $aInstalledInfo[$sModulePath] : array()); if(empty($aModule)) continue; $aModules[$bTitleAsKey ? $aModule['title'] : $aModule['name']] = $aModule; } closedir($rHandleModule); } } closedir($rHandleVendor); } ksort($aModules); return $aModules; } public function getUpdates() { $aUpdates = array(); bx_import('BxDolStudioTemplate'); $oTemplate = BxDolStudioTemplate::getInstance(); $aInstalledInfo = $this->getInstalledInfo(); $sPath = BX_DIRECTORY_PATH_MODULES; if(($rHandleVendor = opendir($sPath)) !== false) { while(($sVendor = readdir($rHandleVendor)) !== false) { if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor)) continue; if(($rHandleModule = opendir($sPath . $sVendor . '/')) !== false) { while(($sModule = readdir($rHandleModule)) !== false) { if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.') continue; $sModulePath = $sVendor . '/' . $sModule . '/'; if(empty($aInstalledInfo[$sModulePath])) continue; $aUpdate = $this->getUpdate($aInstalledInfo[$sModulePath]); if(!empty($aUpdate)) $aUpdates[$aUpdate['title']] = $aUpdate; } closedir($rHandleModule); } } closedir($rHandleVendor); } ksort($aUpdates); return $aUpdates; } public function getUpdate($mixedModule) { $aResult = array(); if(!is_array($mixedModule)) { bx_import('BxDolModuleQuery'); $mixedModule = BxDolModuleQuery::getInstance()->{is_numeric($mixedModule) ? 'getModuleById' : 'getModuleByName'}($mixedModule); } if(empty($mixedModule) || !is_array($mixedModule)) return $aResult; $sPathModule = BX_DIRECTORY_PATH_MODULES . $mixedModule['path']; $sPathUpdates = $sPathModule . 'updates/'; $rHandleUpdate = @opendir($sPathUpdates); if($rHandleUpdate === false) return $aResult; while(($sUpdate = readdir($rHandleUpdate)) !== false) { $sPathUpdate = $sPathUpdates . $sUpdate . '/'; if(substr($sUpdate, 0, 1) == '.' || !is_dir($sPathUpdate)) continue; $aUpdate = $this->getConfigUpdate($sPathModule . 'install/config.php', $sPathUpdate . 'install/config.php', $mixedModule); if(empty($aUpdate) || !is_array($aUpdate) || version_compare($aUpdate['module_version'], $aUpdate['version_from']) != 0) continue; $aResult = $aUpdate; break; } closedir($rHandleUpdate); return $aResult; } public function perform($sDirectory, $sOperation, $aParams = array()) { if(!defined('BX_DOL_CRON_EXECUTE') && !self::isRealOwner() && !in_array($sOperation, array('install', 'uninstall', 'enable', 'disable'))) { if($this->addTransientJob('perform_action', array($sDirectory, $sOperation, $aParams))) return array('code' => 1, 'message' => _t('_adm_mod_msg_process_operation_scheduled')); else return array('code' => 2, 'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory)); } $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']); } public function checkModules($bAuthorizedAccess = false) { if($bAuthorizedAccess) { bx_import('BxDolStudioOAuth'); $aProducts = BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'purchased_products', 'dol_domain' => BX_DOL_URL_ROOT)); } else { bx_import('BxDolStudioJson'); $aProducts = BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_purchased', array( 'key' => getParam('sys_oauth_key') )); } bx_import('BxDolModuleQuery'); $oModuleDb = BxDolModuleQuery::getInstance(); $oModuleDb->updateModule(array('hash' => '')); if(!empty($aProducts) && is_array($aProducts)) foreach ($aProducts as $aProduct) $oModuleDb->updateModule(array('hash' => $aProduct['hash']), array('name' => $aProduct['name'])); return $aProducts; } public function checkUpdates($bAuthorizedAccess = false) { bx_import('BxDolModuleQuery'); $aModules = BxDolModuleQuery::getInstance()->getModules(); $aProducts = array(); foreach($aModules as $aModule) { if(empty($aModule['name'])) continue; $aProducts[] = array( 'name' => $aModule['name'], 'version' => $aModule['version'], 'hash' => $aModule['hash'], ); } $sProducts = base64_encode(serialize($aProducts)); if($bAuthorizedAccess) { bx_import('BxDolStudioOAuth'); return BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'available_updates', 'dol_products' => $sProducts)); } bx_import('BxDolStudioJson'); return BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_updates', array( 'key' => getParam('sys_oauth_key'), 'products' => $sProducts )); } public function downloadFileAuthorized($iFileId) { bx_import('BxDolStudioOAuth'); $aItem = BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'product_file', 'dol_file_id' => $iFileId)); return $this->downloadFileInit($aItem); } public function downloadUpdatePublic($sModuleName, $bAutoUpdate = false) { bx_import('BxDolModuleQuery'); $aModule = BxDolModuleQuery::getInstance()->getModuleByName($sModuleName); bx_import('BxDolStudioJson'); $aItem = BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_download_update', array( 'key' => getParam('sys_oauth_key'), 'product' => base64_encode(serialize(array( 'name' => $aModule['name'], 'version' => $aModule['version'], 'hash' => $aModule['hash'], ))), )); return $this->downloadFileInit($aItem, $bAutoUpdate ? 'update' : ''); } protected function downloadFileInit($aItem, $sAutoAction = '') { if(empty($aItem) || !is_array($aItem)) return $aItem; //--- write ZIP archive. $sFilePath = ''; $mixedResult = $this->performWrite($aItem, $sFilePath); if($mixedResult !== true) return $mixedResult; return $this->downloadFileComplete($sFilePath, $sAutoAction); } protected function downloadFileComplete($sFilePath, $sAutoAction = '') { $bAutoAction = !empty($sAutoAction); if(!defined('BX_DOL_CRON_EXECUTE') && !self::isRealOwner()) return _t($this->addTransientJob('download_file_complete', array($sFilePath, $sAutoAction)) ? '_adm_str_msg_download' . ($bAutoAction ? '_and_install' : '') . '_scheduled' : '_adm_str_err_download_failed'); //--- Unarchive package. $sPackagePath = ''; $mixedResult = $this->performUnarchive($sFilePath, $sPackagePath); @unlink($sFilePath); if($mixedResult !== true) return $mixedResult; //--- Copy unarchived package. $sHomePath = ''; $mixedResult = $this->performCopy($sPackagePath, $sHomePath); @bx_rrmdir($sPackagePath); if($mixedResult !== true) return $mixedResult; if(!$bAutoAction) return true; //--- Autoinstall the downloaded package if it's needed. $aResult = $this->perform($sHomePath, $sAutoAction); if((int)$aResult['code'] != 0) return $aResult['message']; return true; } protected function performWrite($aItem, &$sFilePath) { $iUmaskSave = umask(0); $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'); $sContent = urldecode($aItem['content']); if(!fwrite($rHandler, $sContent, strlen($sContent))) return _t('_adm_str_err_cannot_write'); fclose($rHandler); umask($iUmaskSave); return true; } protected function performUnarchive($sFilePath, &$sPackagePath) { $iUmaskSave = umask(0); 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'); $sPackageFolder = ''; if($oZip->numFiles > 0) $sPackageFolder = $oZip->getNameIndex(0); 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(); umask($iUmaskSave); return true; } protected function performCopy($sPackagePath, &$sHomePath) { if($this->bUseFtp) { $sLogin = getParam('sys_ftp_login'); $sPassword = getParam('sys_ftp_password'); $sPath = getParam('sys_ftp_dir'); if(empty($sLogin) || empty($sPassword) || empty($sPath)) return _t('_adm_str_err_no_ftp_info'); bx_import('BxDolFtp'); $oFile = new BxDolFtp($_SERVER['HTTP_HOST'], $sLogin, $sPassword, $sPath); if(!$oFile->connect()) return _t('_adm_str_err_cannot_connect_to_ftp'); if(!$oFile->isDolphin()) return _t('_adm_str_err_destination_not_valid'); } else { bx_import('BxDolFile'); $oFile = BxDolFile::getInstance(); } $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'); $sHomePath = $aConfig['home_dir']; if(!$oFile->copy($sPackagePath, 'modules/' . $sHomePath)) return _t('_adm_str_err_files_copy_failed'); return true; } protected function getConfigModule($sConfigPath, $aInstalled = array()) { $aConfig = self::getModuleConfig($sConfigPath); if(empty($aConfig) || !is_array($aConfig)) return array(); $bInstalled = !empty($aInstalled); $bEnabled = $bInstalled && (int)$aInstalled['enabled'] == 1; $sVersion = $bInstalled ? $aInstalled['version'] : $aConfig['version']; return array( 'type' => $aConfig['type'], 'name' => isset($aConfig['name']) ? $aConfig['name'] : $aConfig['home_uri'], 'title' => bx_process_output($aConfig['title']), 'vendor' => $aConfig['vendor'], 'version' => $sVersion, 'uri' => $aConfig['home_uri'], 'dir' => $aConfig['home_dir'], 'note' => isset($aConfig['note']) ? bx_process_output($aConfig['note']) : '', 'installed' => $bInstalled, 'enabled' => $bEnabled ); } protected function getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledModule = array()) { $aModule = $this->getConfigModule($sConfigPathModule, $aInstalledModule); if(empty($aModule) || !$aModule['installed']) return array(); $aConfig = self::getModuleConfig($sConfigPathUpdate); if(empty($aConfig) || !is_array($aConfig)) return array(); return array( 'title' => bx_process_output($aConfig['title']), 'vendor' => $aConfig['vendor'], 'version_from' => $aConfig['version_from'], 'version_to' => $aConfig['version_to'], 'dir' => $aConfig['home_dir'], 'module_type' => $aModule['type'], 'module_name' => $aModule['name'], 'module_dir' => $aModule['dir'], 'module_version' => $aModule['version'] ); } private function getInstalledInfo() { bx_import('BxDolModuleQuery'); $aModules = BxDolModuleQuery::getInstance()->getModules(); $aInstalledInfo = array(); foreach($aModules as $aModule) $aInstalledInfo[$aModule['path']] = $aModule; return $aInstalledInfo; } private function addTransientJob($sAction, $aParams) { bx_import('BxDolCronQuery'); if(BxDolCronQuery::getInstance()->addTransientJobService('sys_' . $sAction, array('system', $sAction, $aParams, 'DolStudioInstallerUtils'))) return true; return false; } } /** @} */