/** * Load extension info a string. * * @param string $string * XML content. * * @throws CRM_Extension_Exception_ParseException * @return CRM_Extension_Info */ public static function loadFromString($string) { list($xml, $error) = CRM_Utils_XML::parseString($string); if ($xml === FALSE) { throw new CRM_Extension_Exception_ParseException("Failed to parse info XML: {$string}"); } $instance = new CRM_Extension_Info(); $instance->parse($xml); return $instance; }
public function testBad_string() { // <file> vs file> $data = "<extension key='test.foo' type='module'>file>foo</file></extension>"; $exc = NULL; try { $info = CRM_Extension_Info::loadFromString($data); } catch (CRM_Extension_Exception $e) { $exc = $e; } $this->assertTrue(is_object($exc)); }
/** * @return array * @throws CRM_Extension_Exception_ParseException */ private function _discoverRemote() { $tsPath = $this->getTsPath(); $timestamp = FALSE; if (file_exists($tsPath)) { $timestamp = file_get_contents($tsPath); } // 3 minutes ago for now $outdated = (int) $timestamp < time() - 180 ? TRUE : FALSE; if (!$timestamp || $outdated) { $remotes = json_decode($this->grabRemoteJson(), TRUE); } else { $remotes = json_decode($this->grabCachedJson(), TRUE); } $this->_remotesDiscovered = array(); foreach ($remotes as $id => $xml) { $ext = CRM_Extension_Info::loadFromString($xml); $this->_remotesDiscovered[] = $ext; } if (file_exists(dirname($tsPath))) { file_put_contents($tsPath, (string) time()); } return $this->_remotesDiscovered; }
/** * @param $dir * @return null * @throws \CRM_Extension_Exception_ParseException */ protected function whoAmI($dir) { while ($dir && dirname($dir) !== $dir && !file_exists("{$dir}/info.xml")) { $dir = dirname($dir); } if (file_exists("{$dir}/info.xml")) { $info = \CRM_Extension_Info::loadFromFile("{$dir}/info.xml"); $name = $info->key; return $name; } return $name; }
/** * Install or upgrade the code for an extension -- and perform any * necessary database changes (eg replacing extension metadata). * * This only works if the extension is stored in the default container. * * @param string $tmpCodeDir * Path to a local directory containing a copy of the new (inert) code. * @throws CRM_Extension_Exception */ public function replace($tmpCodeDir) { if (!$this->defaultContainer) { throw new CRM_Extension_Exception("Default extension container is not configured"); } $newInfo = CRM_Extension_Info::loadFromFile($tmpCodeDir . DIRECTORY_SEPARATOR . CRM_Extension_Info::FILENAME); $oldStatus = $this->getStatus($newInfo->key); // find $tgtPath, $oldInfo, $typeManager switch ($oldStatus) { case self::STATUS_UNINSTALLED: case self::STATUS_INSTALLED: case self::STATUS_DISABLED: // There is an old copy of the extension. Try to install in the same place -- but it must go somewhere in the default-container list($oldInfo, $typeManager) = $this->_getInfoTypeHandler($newInfo->key); // throws Exception $tgtPath = $this->fullContainer->getPath($newInfo->key); if (!CRM_Utils_File::isChildPath($this->defaultContainer->getBaseDir(), $tgtPath)) { // force installation in the default-container $oldPath = $tgtPath; $tgtPath = $this->defaultContainer->getBaseDir() . DIRECTORY_SEPARATOR . $newInfo->key; CRM_Core_Session::setStatus(ts('A copy of the extension (%1) is in a system folder (%2). The system copy will be preserved, but the new copy will be used.', array(1 => $newInfo->key, 2 => $oldPath))); } break; case self::STATUS_INSTALLED_MISSING: case self::STATUS_DISABLED_MISSING: // the extension does not exist in any container; we're free to put it anywhere $tgtPath = $this->defaultContainer->getBaseDir() . DIRECTORY_SEPARATOR . $newInfo->key; list($oldInfo, $typeManager) = $this->_getMissingInfoTypeHandler($newInfo->key); // throws Exception break; case self::STATUS_UNKNOWN: // the extension does not exist in any container; we're free to put it anywhere $tgtPath = $this->defaultContainer->getBaseDir() . DIRECTORY_SEPARATOR . $newInfo->key; $oldInfo = $typeManager = NULL; break; default: throw new CRM_Extension_Exception("Cannot install or enable extension: {$newInfo->key}"); } // move the code! switch ($oldStatus) { case self::STATUS_UNINSTALLED: case self::STATUS_UNKNOWN: // There are no DB records to worry about, so we'll just put the files in place if (!CRM_Utils_File::replaceDir($tmpCodeDir, $tgtPath)) { throw new CRM_Extension_Exception("Failed to move {$tmpCodeDir} to {$tgtPath}"); } break; case self::STATUS_INSTALLED: case self::STATUS_INSTALLED_MISSING: case self::STATUS_DISABLED: case self::STATUS_DISABLED_MISSING: // There are DB records; coordinate the file placement with the DB updates $typeManager->onPreReplace($oldInfo, $newInfo); if (!CRM_Utils_File::replaceDir($tmpCodeDir, $tgtPath)) { throw new CRM_Extension_Exception("Failed to move {$tmpCodeDir} to {$tgtPath}"); } $this->_updateExtensionEntry($newInfo); $typeManager->onPostReplace($oldInfo, $newInfo); break; default: throw new CRM_Extension_Exception("Cannot install or enable extension: {$newInfo->key}"); } $this->refresh(); CRM_Core_Invoke::rebuildMenuAndCaches(TRUE); }
/** * Scan $basedir for a list of extension-keys * * @return array * ($key => $relPath) */ protected function getRelPaths() { if (!is_array($this->relPaths)) { if ($this->cache) { $this->relPaths = $this->cache->get($this->cacheKey); } if (!is_array($this->relPaths)) { $this->relPaths = array(); $infoPaths = CRM_Utils_File::findFiles($this->baseDir, 'info.xml'); foreach ($infoPaths as $infoPath) { $relPath = CRM_Utils_File::relativize(dirname($infoPath), $this->baseDir); try { $info = CRM_Extension_Info::loadFromFile($infoPath); } catch (CRM_Extension_Exception_ParseException $e) { CRM_Core_Session::setStatus(ts('Parse error in extension: %1', array(1 => $e->getMessage())), '', 'error'); CRM_Core_Error::debug_log_message("Parse error in extension: " . $e->getMessage()); continue; } $this->relPaths[$info->key] = $relPath; } if ($this->cache) { $this->cache->set($this->cacheKey, $this->relPaths); } } } return $this->relPaths; }
/** * @param string $key * Extension fully-qualified-name. * @param bool $fresh * * @throws CRM_Extension_Exception * @throws Exception * @return CRM_Extension_Info */ public function keyToInfo($key, $fresh = FALSE) { if ($fresh || !array_key_exists($key, $this->infos)) { try { $this->infos[$key] = CRM_Extension_Info::loadFromFile($this->container->getPath($key) . DIRECTORY_SEPARATOR . CRM_Extension_Info::FILENAME); } catch (CRM_Extension_Exception $e) { // file has more detailed info, but we'll fallback to DB if it's missing -- DB has enough info to uninstall $this->infos[$key] = CRM_Extension_System::singleton()->getManager()->createInfoFromDB($key); if (!$this->infos[$key]) { throw $e; } } } return $this->infos[$key]; }
/** * Validate that $extractedZipPath contains valid for extension $key * * @param $key * @param $extractedZipPath * * @return bool */ public function validateFiles($key, $extractedZipPath) { $filename = $extractedZipPath . DIRECTORY_SEPARATOR . CRM_Extension_Info::FILENAME; if (!is_readable($filename)) { CRM_Core_Session::setStatus(ts('Failed reading data from %1 during installation', array(1 => $filename)), ts('Installation Error'), 'error'); return FALSE; } try { $newInfo = CRM_Extension_Info::loadFromFile($filename); } catch (Exception $e) { CRM_Core_Session::setStatus(ts('Failed reading data from %1 during installation', array(1 => $filename)), ts('Installation Error'), 'error'); return FALSE; } if ($newInfo->key != $key) { CRM_Core_Error::fatal('Cannot install - there are differences between extdir XML file and archive XML file!'); } return TRUE; }
/** * @return array * @throws CRM_Extension_Exception_ParseException */ private function _discoverRemote() { $tsPath = $this->getTsPath(); $timestamp = FALSE; if (file_exists($tsPath)) { $timestamp = file_get_contents($tsPath); } // 3 minutes ago for now $outdated = (int) $timestamp < time() - 180 ? TRUE : FALSE; if (!$timestamp || $outdated) { $remotes = $this->grabRemoteKeyList(); $cached = FALSE; } else { $remotes = $this->grabCachedKeyList(); $cached = TRUE; } $this->_remotesDiscovered = array(); foreach ($remotes as $id => $rext) { $xml = $this->grabRemoteInfoFile($rext['key'], $cached); if ($xml != FALSE) { $ext = CRM_Extension_Info::loadFromString($xml); $this->_remotesDiscovered[] = $ext; } } if (file_exists(dirname($tsPath))) { file_put_contents($tsPath, (string) time()); } return $this->_remotesDiscovered; }