public function installPlugin($source, $isUrl = false) { X_Debug::i("Installing plugin from {{$source}}: isUrl = {{$isUrl}}"); if ($isUrl) { // perform a download in a temp file $http = new Zend_Http_Client($source, array('headers' => array('User-Agent' => "vlc-shares/" . X_VlcShares::VERSION . " plugininstaller/" . X_VlcShares::VERSION))); $http->setStream(true); $source = $http->request()->getStreamName(); } try { // unzip and manifest parse $egg = X_Egg::factory($source, APPLICATION_PATH . '/../', APPLICATION_PATH . '/../data/plugin/tmp/', true); $pluginKey = $egg->getKey(); // first we must check if key already exists in the db $plugin = new Application_Model_Plugin(); Application_Model_PluginsMapper::i()->fetchByKey($pluginKey, $plugin); if ($plugin->getId() !== null) { throw new Exception(X_Env::_('plugin_err_installerror_keyexists') . ": {$pluginKey}"); } // time to check if plugin support this vlc-shares version $vFrom = $egg->getCompatibilityFrom(); $vTo = $egg->getCompatibilityTo(); if (version_compare(X_VlcShares::VERSION_CLEAN, $vFrom, '<') || $vTo !== null && version_compare(X_VlcShares::VERSION_CLEAN, $vTo, '>=')) { throw new Exception(X_Env::_('plugin_err_installerror_unsupported') . ": {$vFrom} - {$vTo}"); } // copy the files: first check if some file exists... $toBeCopied = array(); foreach ($egg->getFiles() as $file) { /* @var $file X_Egg_File */ if (!$file->getProperty(X_Egg_File::P_REPLACE, false) && file_exists($file->getDestination())) { throw new Exception(X_Env::_('plugin_err_installerror_fileexists') . ": {$file->getDestination()}"); } if (!file_exists($file->getSource())) { if (!$file->getProperty(X_Egg_File::P_IGNOREIFNOTEXISTS, false)) { throw new Exception(X_Env::_('plugin_err_installerror_sourcenotexists') . ": {$file->getSource()}"); } // ignore this item if P_IGNOREIFNOTEXISTS is true and file not exists continue; } $toBeCopied[] = array('src' => $file->getSource(), 'dest' => $file->getDestination(), 'resource' => $file); } // before copy act, i must be sure to be able to revert changes $plugin = new Application_Model_Plugin(); $plugin->setLabel($egg->getLabel())->setKey($pluginKey)->setDescription($egg->getDescription())->setFile($egg->getFile())->setClass($egg->getClass())->setType(Application_Model_Plugin::USER)->setVersion($egg->getVersion()); Application_Model_PluginsMapper::i()->save($plugin); // so i must copy uninstall information inside a uninstall dir in data $dest = APPLICATION_PATH . '/../data/plugin/_uninstall/' . $pluginKey; // i have to create the directory if (!mkdir($dest, 0777, true)) { throw new Exception(X_Env::_('plugin_err_installerror_uninstalldircreation') . ": {$dest}"); } if (!copy($egg->getManifestFile(), "{$dest}/manifest.xml")) { throw new Exception(X_Env::_('plugin_err_installerror_uninstallmanifestcopy') . ": " . $egg->getManifestFile() . " -> {$dest}/manifest.xml"); } $uninstallSql = $egg->getUninstallSQL(); if ($uninstallSql !== null && file_exists($uninstallSql)) { if (!copy($uninstallSql, "{$dest}/uninstall.sql")) { throw new Exception(X_Env::_('plugin_err_installerror_uninstallsqlcopy') . ": {$dest}"); } } // ... then copy foreach ($toBeCopied as $copyInfo) { $copied = false; if (!file_exists(dirname($copyInfo['dest']))) { @mkdir(dirname($copyInfo['dest']), 0777, true); } if (!copy($copyInfo['src'], $copyInfo['dest'])) { $this->_helper->flashMessenger(array('text' => X_Env::_('plugin_err_installerror_copyerror') . ": <br/>" . $copyInfo['src'] . '<br/>' . $copyInfo['dest'], 'type' => 'error')); } else { X_Debug::i("File copied {{$copyInfo['dest']}}"); $copied = true; } /* @var $xeggFile X_Egg_File */ $xeggFile = $copyInfo['resource']; if ($copied) { // check permission $permission = $xeggFile->getProperty(X_Egg_File::P_PERMISSIONS, false); if ($permission !== false) { if (!chmod($copyInfo['dest'], octdec($permission))) { X_Debug::e("Chmod {{$permission}} failed for file {{$copyInfo['dest']}}"); } else { X_Debug::i("Permissions set to {{$permission}} for file {{$copyInfo['dest']}} as required"); } } } else { if ($xeggFile->getProperty(X_Egg_File::P_HALTONCOPYERROR, false)) { X_Debug::f("File not copied {{$copyInfo['dest']}} and flagged as HaltOnCopyError"); break; } } } // change database $installSql = $egg->getInstallSQL(); if ($installSql !== null && file_exists($installSql)) { try { $dataSql = file_get_contents($installSql); if (trim($dataSql) !== '') { $bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap'); $db = $bootstrap->getResource('db'); $db->getConnection()->exec($dataSql); } } catch (Exception $e) { X_Debug::e("DB Error while installind: {$e->getMessage()}"); $this->_helper->flashMessenger(X_Env::_('plugin_err_installerror_sqlerror') . ": {$e->getMessage()}"); //throw $e; } } // process acl fragment $aclHelper = X_VlcShares_Plugins::helpers()->acl(); // new classes $accounts = Application_Model_AuthAccountsMapper::i()->fetchAll(); foreach ($egg->getAclClasses() as $aclClass) { /* @var $aclClass X_Egg_AclClass */ $res = $aclHelper->addClass($aclClass->getName(), $aclClass->getProperty(X_Egg_AclClass::P_DESCRIPTION, '')); if (!$res) { $this->_helper->flashMessenger(array('text' => X_Env::_('plugin_err_installerror_aclclass', $aclClass->getName()), 'type' => 'warning')); continue; } $extends = $aclClass->getExtends(); if (count($extends)) { foreach ($accounts as $account) { /* @var $account Application_Model_AuthAccount */ foreach ($extends as $baseClass) { if (in_array($baseClass, $aclHelper->getPermissions($account->getUsername()))) { $aclHelper->grantPermission($account->getUsername(), $aclClass->getName()); } } } } } //new resources foreach ($egg->getAclResources() as $resource) { /* @var $resource X_Egg_AclResource */ $aclHelper->addResource($resource->getKey(), $resource->getClass(), $egg->getKey(), false); } $egg->cleanTmp(); unlink($source); return true; } catch (Exception $e) { if ($egg !== null) { $egg->cleanTmp(); } // delete the uploaded file unlink($source); //$this->_helper->flashMessenger(array('text' => X_Env::_('plugin_err_installerror').": ".$e->getMessage(), 'type' => 'error')); //return false; throw $e; } }
/** * Factory of X_Egg: accepts a manifest file or a xegg (zip) archive * @param string $eggfile path to manifest file or xegg file * @param string $dest_basepath destination root path * @param string $tmp_path temp dir for xegg unpack (ignored if $eggfile is a manifest) * @param bool $disableExtCheck allow to disable zip xegg file type check * @return X_Egg */ public static function factory($eggfile, $dest_basepath, $tmp_path = false, $disableExtCheck = false) { $egg = new X_Egg(); // i have to unzip egg file if it is not a xml $extension = strtolower(pathinfo($eggfile, PATHINFO_EXTENSION)); if ($extension == 'xml') { // i only have to read it $manifestFile = $eggfile; $basepath = dirname($eggfile); } elseif ($disableExtCheck || $extension == 'zip' || $extension == 'xegg') { // i have to unpack the zip file $pclzip = new PclZip($eggfile); if ($tmp_path === false) { $tmp_path = sys_get_temp_dir() . '/x_egg_unzip/'; } else { $tmp_path = rtrim($tmp_path, '\\/') . '/'; } $egg->_cleanFlag = true; $pclzip->extract(PCLZIP_OPT_PATH, $tmp_path); $manifestFile = $tmp_path . 'manifest.xml'; $basepath = $tmp_path; } else { throw new Exception("Invalid X_Egg file extensions. Valid extension are 'xegg' or 'zip'"); } $egg->setDestinationPath($dest_basepath)->setBasePath($basepath)->parse($manifestFile); return $egg; }
private function _uninstall($manifest) { /* @var $egg X_Egg */ $egg = X_Egg::factory($manifest, APPLICATION_PATH . '/../'); foreach ($egg->getFiles() as $file) { /* @var $file X_Egg_File */ // if file doesn't exists, ignore it // this prevent errors when you retry to uninstall // a partially installed plugin if (!file_exists($file->getDestination())) { continue; } if (!$file->getProperty(X_Egg_File::P_REPLACE, false) || $file->getProperty(X_Egg_File::P_REMOVEREPLACEDONUNINSTALL, false)) { $unlinkStatus = @unlink($file->getDestination()); if (!$file->getProperty(X_Egg_File::P_IGNOREUNLINKERROR, true) && !$unlinkStatus) { X_Debug::e("File not unlinked: {{$file->getDestination()}}"); throw new Exception("Cannot unlink file {{$file->getDestination()}} and ignoreUnlinkError flag is FALSE. Remove it manually and try again"); } } else { X_Debug::i("Replaced file {{$file->getDestination()}} left because removeReplacedOnUninstall is FALSE"); } } $uninstallSql = $egg->getUninstallSQL(); if ($uninstallSql !== null && file_exists(dirname($manifest) . "/uninstall.sql")) { try { $dataSql = file_get_contents(dirname($manifest) . "/uninstall.sql"); if (trim($dataSql) !== '') { $bootstrap = $this->getFrontController()->getParam('bootstrap'); $db = $bootstrap->getResource('db'); $db->getConnection()->exec($dataSql); } } catch (Exception $e) { X_Debug::e("DB Error while uninstalling: {$e->getMessage()}"); $this->_helper->flashMessenger(X_Env::_('plugin_err_uninstallerror_sqlerror') . ": {$e->getMessage()}"); } @unlink(dirname($manifest) . "/uninstall.sql"); } // process acl fragment $aclHelper = X_VlcShares_Plugins::helpers()->acl(); // added classes foreach ($egg->getAclClasses() as $aclClass) { /* @var $aclClass X_Egg_AclClass */ $resources = Application_Model_AclResourcesMapper::i()->fetchByClassNotGenerated($aclClass->getName(), $egg->getKey()); $restore = $aclClass->getOnDelete(); foreach ($resources as $resource) { /* @var $resource Application_Model_AclResource */ $resource->setClass($restore); try { Application_Model_AclResourcesMapper::i()->save($resource); } catch (Exception $e) { X_Debug::e("Can't restore class {{$restore}} for resource {{$resource->getKey()}}: {$e->getMessage()}"); } } $aclHelper->removeClass($aclClass->getName()); } // acl resources are automatically removed by foreign key with generator @unlink($manifest); @rmdir(dirname($manifest)); return true; }