X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FExtension%2FManager.php;h=234b3d8fc03cc5041dc1931d916ee35c46ac10c1;hb=491e51af3abb85d803cfb72cf0d8fbd5ecbf09cb;hp=6524c0a16fae66123837aeafc9df3952308642ae;hpb=01f7363f74d44b4ff35161c02d7975b7993a2863;p=civicrm-core.git diff --git a/CRM/Extension/Manager.php b/CRM/Extension/Manager.php index 6524c0a16f..234b3d8fc0 100644 --- a/CRM/Extension/Manager.php +++ b/CRM/Extension/Manager.php @@ -54,12 +54,12 @@ class CRM_Extension_Manager { const STATUS_UNKNOWN = 'unknown'; /** - * The extension is fully installed and enabled + * The extension is installed but the code is not accessible */ const STATUS_INSTALLED_MISSING = 'installed-missing'; /** - * The extension is fully installed and enabled + * The extension was installed and is now disabled; the code is not accessible */ const STATUS_DISABLED_MISSING = 'disabled-missing'; @@ -71,13 +71,17 @@ class CRM_Extension_Manager { public $fullContainer; /** - * @var CRM_Extension_Container_Basic|FALSE + * Default container. + * + * @var CRM_Extension_Container_Basic|false * * Note: Treat as private. This is only public to facilitate debugging. */ public $defaultContainer; /** + * Mapper. + * * @var CRM_Extension_Mapper * * Note: Treat as private. This is only public to facilitate debugging. @@ -85,24 +89,34 @@ class CRM_Extension_Manager { public $mapper; /** - * @var array (typeName => CRM_Extension_Manager_Interface) + * Type managers. + * + * @var array + * + * Format is (typeName => CRM_Extension_Manager_Interface) * * Note: Treat as private. This is only public to facilitate debugging. */ public $typeManagers; /** - * @var array (extensionKey => statusConstant) + * Statuses. + * + * @var array + * + * Format is (extensionKey => statusConstant) * * Note: Treat as private. This is only public to facilitate debugging. */ public $statuses; /** + * Class constructor. + * * @param CRM_Extension_Container_Interface $fullContainer * @param CRM_Extension_Container_Basic|FALSE $defaultContainer * @param CRM_Extension_Mapper $mapper - * @param $typeManagers + * @param array $typeManagers */ public function __construct(CRM_Extension_Container_Interface $fullContainer, $defaultContainer, CRM_Extension_Mapper $mapper, $typeManagers) { $this->fullContainer = $fullContainer; @@ -135,7 +149,8 @@ class CRM_Extension_Manager { 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 + // throws Exception + list ($oldInfo, $typeManager) = $this->_getInfoTypeHandler($newInfo->key); $tgtPath = $this->fullContainer->getPath($newInfo->key); if (!CRM_Utils_File::isChildPath($this->defaultContainer->getBaseDir(), $tgtPath)) { // force installation in the default-container @@ -152,7 +167,8 @@ class CRM_Extension_Manager { 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 + // throws Exception + list ($oldInfo, $typeManager) = $this->_getMissingInfoTypeHandler($newInfo->key); break; case self::STATUS_UNKNOWN: @@ -199,18 +215,32 @@ class CRM_Extension_Manager { /** * Add records of the extension to the database -- and enable it * - * @param array $keys - * List of extension keys. + * @param string|array $keys + * One or more extension keys. * @throws CRM_Extension_Exception */ public function install($keys) { + $keys = (array) $keys; $origStatuses = $this->getStatuses(); // TODO: to mitigate the risk of crashing during installation, scan // keys/statuses/types before doing anything + // Check compatibility + $incompatible = []; + foreach ($keys as $key) { + if ($this->isIncompatible($key)) { + $incompatible[] = $key; + } + } + if ($incompatible) { + throw new CRM_Extension_Exception('Cannot install incompatible extension: ' . implode(', ', $incompatible)); + } + foreach ($keys as $key) { - list ($info, $typeManager) = $this->_getInfoTypeHandler($key); // throws Exception + /** @var CRM_Extension_Info $info */ + /** @var CRM_Extension_Manager_Base $typeManager */ + list ($info, $typeManager) = $this->_getInfoTypeHandler($key); switch ($origStatuses[$key]) { case self::STATUS_INSTALLED: @@ -254,7 +284,8 @@ class CRM_Extension_Manager { $schema->fixSchemaDifferences(); foreach ($keys as $key) { - list ($info, $typeManager) = $this->_getInfoTypeHandler($key); // throws Exception + // throws Exception + list ($info, $typeManager) = $this->_getInfoTypeHandler($key); switch ($origStatuses[$key]) { case self::STATUS_INSTALLED: @@ -290,13 +321,14 @@ class CRM_Extension_Manager { } /** - * Add records of the extension to the database -- and enable it + * Disable extension without removing record from db. * - * @param array $keys - * List of extension keys. + * @param string|array $keys + * One or more extension keys. * @throws CRM_Extension_Exception */ public function disable($keys) { + $keys = (array) $keys; $origStatuses = $this->getStatuses(); // TODO: to mitigate the risk of crashing during installation, scan @@ -304,7 +336,8 @@ class CRM_Extension_Manager { sort($keys); $disableRequirements = $this->findDisableRequirements($keys); - sort($disableRequirements); // This munges order, but makes it comparable. + // This munges order, but makes it comparable. + sort($disableRequirements); if ($keys !== $disableRequirements) { throw new CRM_Extension_Exception_DependencyException("Cannot disable extension due dependencies. Consider disabling all these: " . implode(',', $disableRequirements)); } @@ -312,14 +345,16 @@ class CRM_Extension_Manager { foreach ($keys as $key) { switch ($origStatuses[$key]) { case self::STATUS_INSTALLED: - list ($info, $typeManager) = $this->_getInfoTypeHandler($key); // throws Exception + // throws Exception + list ($info, $typeManager) = $this->_getInfoTypeHandler($key); $typeManager->onPreDisable($info); $this->_setExtensionActive($info, 0); $typeManager->onPostDisable($info); break; case self::STATUS_INSTALLED_MISSING: - list ($info, $typeManager) = $this->_getMissingInfoTypeHandler($key); // throws Exception + // throws Exception + list ($info, $typeManager) = $this->_getMissingInfoTypeHandler($key); $typeManager->onPreDisable($info); $this->_setExtensionActive($info, 0); $typeManager->onPostDisable($info); @@ -345,13 +380,12 @@ class CRM_Extension_Manager { /** * Remove all database references to an extension. * - * Add records of the extension to the database -- and enable it - * - * @param array $keys - * List of extension keys. + * @param string|array $keys + * One or more extension keys. * @throws CRM_Extension_Exception */ public function uninstall($keys) { + $keys = (array) $keys; $origStatuses = $this->getStatuses(); // TODO: to mitigate the risk of crashing during installation, scan @@ -364,14 +398,16 @@ class CRM_Extension_Manager { throw new CRM_Extension_Exception("Cannot uninstall extension; disable it first: $key"); case self::STATUS_DISABLED: - list ($info, $typeManager) = $this->_getInfoTypeHandler($key); // throws Exception + // throws Exception + list ($info, $typeManager) = $this->_getInfoTypeHandler($key); $typeManager->onPreUninstall($info); $this->_removeExtensionEntry($info); $typeManager->onPostUninstall($info); break; case self::STATUS_DISABLED_MISSING: - list ($info, $typeManager) = $this->_getMissingInfoTypeHandler($key); // throws Exception + // throws Exception + list ($info, $typeManager) = $this->_getMissingInfoTypeHandler($key); $typeManager->onPreUninstall($info); $this->_removeExtensionEntry($info); $typeManager->onPostUninstall($info); @@ -398,7 +434,7 @@ class CRM_Extension_Manager { * @param $key * * @return string - * constant (STATUS_INSTALLED, STATUS_DISABLED, STATUS_UNINSTALLED, STATUS_UNKNOWN) + * constant self::STATUS_* */ public function getStatus($key) { $statuses = $this->getStatuses(); @@ -410,6 +446,17 @@ class CRM_Extension_Manager { } } + /** + * Check if a given extension is incompatible with this version of CiviCRM + * + * @param $key + * @return bool|array + */ + public function isIncompatible($key) { + $info = CRM_Extension_System::getCompatibilityInfo(); + return $info[$key] ?? FALSE; + } + /** * Determine the status of all extensions. * @@ -418,6 +465,8 @@ class CRM_Extension_Manager { */ public function getStatuses() { if (!is_array($this->statuses)) { + $compat = CRM_Extension_System::getCompatibilityInfo(); + $this->statuses = []; foreach ($this->fullContainer->getKeys() as $key) { @@ -437,7 +486,10 @@ class CRM_Extension_Manager { catch (CRM_Extension_Exception $e) { $codeExists = FALSE; } - if ($dao->is_active) { + if (!empty($compat[$dao->full_name]['force-uninstall'])) { + $this->statuses[$dao->full_name] = self::STATUS_UNINSTALLED; + } + elseif ($dao->is_active) { $this->statuses[$dao->full_name] = $codeExists ? self::STATUS_INSTALLED : self::STATUS_INSTALLED_MISSING; } else { @@ -450,7 +502,8 @@ class CRM_Extension_Manager { public function refresh() { $this->statuses = NULL; - $this->fullContainer->refresh(); // and, indirectly, defaultContainer + // and, indirectly, defaultContainer + $this->fullContainer->refresh(); $this->mapper->refresh(); } @@ -463,10 +516,11 @@ class CRM_Extension_Manager { * * @throws CRM_Extension_Exception * @return array - * (0 => CRM_Extension_Info, 1 => CRM_Extension_Manager_Interface) + * [CRM_Extension_Info, CRM_Extension_Manager_Interface] */ private function _getInfoTypeHandler($key) { - $info = $this->mapper->keyToInfo($key); // throws Exception + // throws Exception + $info = $this->mapper->keyToInfo($key); if (array_key_exists($info->type, $this->typeManagers)) { return [$info, $this->typeManagers[$info->type]]; } @@ -482,7 +536,7 @@ class CRM_Extension_Manager { * * @throws CRM_Extension_Exception * @return array - * (0 => CRM_Extension_Info, 1 => CRM_Extension_Manager_Interface) + * [CRM_Extension_Info, CRM_Extension_Manager_Interface] */ private function _getMissingInfoTypeHandler($key) { $info = $this->createInfoFromDB($key); @@ -595,8 +649,10 @@ class CRM_Extension_Manager { */ public function findInstallRequirements($keys) { $infos = $this->mapper->getAllInfos(); - $todoKeys = array_unique($keys); // array(string $key). - $doneKeys = []; // array(string $key => 1); + // array(string $key). + $todoKeys = array_unique($keys); + // array(string $key => 1); + $doneKeys = []; $sorter = new \MJS\TopSort\Implementations\FixedArraySort(); while (!empty($todoKeys)) {