Example #1
0
 /**
  * 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;
 }
Example #2
0
 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));
 }
Example #3
0
 /**
  * @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;
 }
Example #4
0
 /**
  * @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;
 }
Example #5
0
 /**
  * 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);
 }
Example #6
0
 /**
  * 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;
 }
Example #7
0
 /**
  * @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];
 }
Example #8
0
 /**
  * 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;
 }
Example #9
0
 /**
  * @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;
 }