diff --git a/administrator/components/com_installer/models/manage.php b/administrator/components/com_installer/models/manage.php index 8df46b2446af4..086d56213d512 100644 --- a/administrator/components/com_installer/models/manage.php +++ b/administrator/components/com_installer/models/manage.php @@ -231,7 +231,7 @@ public function remove($eid = array()) $rowtype = $row->type; } - if ($row->type && $row->type != 'language') + if ($row->type) { $result = $installer->uninstall($row->type, $id); @@ -251,14 +251,6 @@ public function remove($eid = array()) continue; } - if ($row->type == 'language') - { - // One should always uninstall a language package, not a single language - $msgs[] = JText::_('COM_INSTALLER_UNINSTALL_LANGUAGE'); - - continue; - } - // There was an error in uninstalling the package $msgs[] = JText::sprintf('COM_INSTALLER_UNINSTALL_ERROR', $rowtype); } diff --git a/administrator/language/en-GB/en-GB.com_installer.ini b/administrator/language/en-GB/en-GB.com_installer.ini index 28f3a80a31345..a0b0e9a4c9cf8 100644 --- a/administrator/language/en-GB/en-GB.com_installer.ini +++ b/administrator/language/en-GB/en-GB.com_installer.ini @@ -243,6 +243,7 @@ COM_INSTALLER_TYPE_TYPE_TEMPLATE="template" COM_INSTALLER_UNABLE_TO_FIND_INSTALL_PACKAGE="Unable to find install package" COM_INSTALLER_UNABLE_TO_INSTALL_JOOMLA_PACKAGE="The Joomla package cannot be installed through the Extension Manager. Please use the Joomla! Update component to update Joomla." COM_INSTALLER_UNINSTALL_ERROR="Error uninstalling %s." +; This string is deprecated and will be removed with 4.0. COM_INSTALLER_UNINSTALL_LANGUAGE="A language should always have been installed as a package.
To uninstall a language, filter type by package and uninstall the package." COM_INSTALLER_UNINSTALL_SUCCESS="Uninstalling the %s was successful." COM_INSTALLER_UPDATE_FILTER_SEARCH_DESC="Search in extension name. Prefix with ID:, UID: or EID: to search for a update ID, update site ID or extension ID." diff --git a/administrator/language/en-GB/en-GB.lib_joomla.ini b/administrator/language/en-GB/en-GB.lib_joomla.ini index 10d44b61317fb..168df39066093 100644 --- a/administrator/language/en-GB/en-GB.lib_joomla.ini +++ b/administrator/language/en-GB/en-GB.lib_joomla.ini @@ -588,6 +588,7 @@ JLIB_INSTALLER_PURGED_UPDATES="Cleared updates" JLIB_INSTALLER_FAILED_TO_PURGE_UPDATES="Failed to clear updates." JLIB_INSTALLER_DEFAULT_STYLE="%s - Default" JLIB_INSTALLER_DISCOVER="Discover" +JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE="The %s extension is part of a package which does not allow individual extensions to be uninstalled." JLIB_INSTALLER_ERROR_COMP_DISCOVER_STORE_DETAILS="Component Discover install: Failed to store component details." JLIB_INSTALLER_ERROR_COMP_FAILED_TO_CREATE_DIRECTORY="Component %1$s: Failed to create folder: %2$s." JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT="Component Install: The XML file did not contain an administration element." diff --git a/administrator/manifests/packages/pkg_en-GB.xml b/administrator/manifests/packages/pkg_en-GB.xml index f54deffd38c69..d1526b6900d77 100644 --- a/administrator/manifests/packages/pkg_en-GB.xml +++ b/administrator/manifests/packages/pkg_en-GB.xml @@ -13,6 +13,7 @@ Joomla! Project www.joomla.org + true site_en-GB admin_en-GB diff --git a/language/en-GB/en-GB.lib_joomla.ini b/language/en-GB/en-GB.lib_joomla.ini index 4983d6423ca8d..f0cb47306c17c 100644 --- a/language/en-GB/en-GB.lib_joomla.ini +++ b/language/en-GB/en-GB.lib_joomla.ini @@ -588,6 +588,7 @@ JLIB_INSTALLER_PURGED_UPDATES="Cleared updates" JLIB_INSTALLER_FAILED_TO_PURGE_UPDATES="Failed to clear updates." JLIB_INSTALLER_DEFAULT_STYLE="%s - Default" JLIB_INSTALLER_DISCOVER="Discover" +JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE="The %s extension is part of a package which does not allow individual extensions to be uninstalled." JLIB_INSTALLER_ERROR_COMP_DISCOVER_STORE_DETAILS="Component Discover install: Failed to store component details." JLIB_INSTALLER_ERROR_COMP_FAILED_TO_CREATE_DIRECTORY="Component %1$s: Failed to create folder: %2$s." JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT="Component Install: The XML file did not contain an administration element." diff --git a/libraries/cms/installer/adapter.php b/libraries/cms/installer/adapter.php index e6f423126a99f..1ca29ebe6f30b 100644 --- a/libraries/cms/installer/adapter.php +++ b/libraries/cms/installer/adapter.php @@ -132,6 +132,41 @@ public function __construct(JInstaller $parent, JDatabaseDriver $db, array $opti } } + /** + * Check if a package extension allows its child extensions to be uninstalled individually + * + * @param integer $packageId The extension ID of the package to check + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + * @note This method defaults to true to emulate the behavior of 3.6 and earlier which did not support this lookup + */ + protected function canUninstallPackageChild($packageId) + { + $package = JTable::getInstance('extension'); + + // If we can't load this package ID, we have a corrupt database + if (!$package->load((int) $packageId)) + { + return true; + } + + $manifestFile = JPATH_MANIFESTS . '/packages/' . $package->element . '.xml'; + + $xml = $this->parent->isManifest($manifestFile); + + // If the manifest doesn't exist, we've got some major issues + if (!$xml) + { + return true; + } + + $manifest = new JInstallerManifestPackage($manifestFile); + + return $manifest->blockChildUninstall === false; + } + /** * Method to check if the extension is already present in the database * diff --git a/libraries/cms/installer/adapter/component.php b/libraries/cms/installer/adapter/component.php index 83e42a6d135bb..267f4bf48e567 100644 --- a/libraries/cms/installer/adapter/component.php +++ b/libraries/cms/installer/adapter/component.php @@ -688,6 +688,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($this->extension->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($this->extension->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $this->extension->name), JLog::WARNING, 'jerror'); + + return false; + } + // Get the admin and site paths for the component $this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->extension->element)); $this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->extension->element)); diff --git a/libraries/cms/installer/adapter/file.php b/libraries/cms/installer/adapter/file.php index 545e122fe2bdd..9765e3ef0e410 100644 --- a/libraries/cms/installer/adapter/file.php +++ b/libraries/cms/installer/adapter/file.php @@ -304,6 +304,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($row->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($row->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $row->name), JLog::WARNING, 'jerror'); + + return false; + } + $retval = true; $manifestFile = JPATH_MANIFESTS . '/files/' . $row->element . '.xml'; diff --git a/libraries/cms/installer/adapter/language.php b/libraries/cms/installer/adapter/language.php index 64585ab18ea92..19b4db3643e85 100644 --- a/libraries/cms/installer/adapter/language.php +++ b/libraries/cms/installer/adapter/language.php @@ -623,6 +623,17 @@ public function uninstall($eid) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($extension->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($extension->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $extension->name), JLog::WARNING, 'jerror'); + + return false; + } + // Construct the path from the client, the language and the extension element name $path = $client->path . '/language/' . $element; diff --git a/libraries/cms/installer/adapter/library.php b/libraries/cms/installer/adapter/library.php index 1f2037a563a8a..b6e4abcb98e48 100644 --- a/libraries/cms/installer/adapter/library.php +++ b/libraries/cms/installer/adapter/library.php @@ -378,6 +378,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($row->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($row->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $row->name), JLog::WARNING, 'jerror'); + + return false; + } + $manifestFile = JPATH_MANIFESTS . '/libraries/' . $row->element . '.xml'; // Because libraries may not have their own folders we cannot use the standard method of finding an installation manifest diff --git a/libraries/cms/installer/adapter/module.php b/libraries/cms/installer/adapter/module.php index 3b428fdfe7d55..b2454dcb47747 100644 --- a/libraries/cms/installer/adapter/module.php +++ b/libraries/cms/installer/adapter/module.php @@ -533,6 +533,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($this->extension->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($this->extension->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $this->extension->name), JLog::WARNING, 'jerror'); + + return false; + } + // Get the extension root path $element = $this->extension->element; $client = JApplicationHelper::getClientInfo($this->extension->client_id); diff --git a/libraries/cms/installer/adapter/package.php b/libraries/cms/installer/adapter/package.php index 85948c7083baf..69230e801124c 100644 --- a/libraries/cms/installer/adapter/package.php +++ b/libraries/cms/installer/adapter/package.php @@ -543,6 +543,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($row->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($row->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $row->name), JLog::WARNING, 'jerror'); + + return false; + } + $manifestFile = JPATH_MANIFESTS . '/packages/' . $row->get('element') . '.xml'; $manifest = new JInstallerManifestPackage($manifestFile); @@ -620,6 +631,8 @@ public function uninstall($id) foreach ($manifest->filelist as $extension) { $tmpInstaller = new JInstaller; + $tmpInstaller->setPackageUninstall(true); + $id = $this->_getExtensionId($extension->type, $extension->id, $extension->client, $extension->group); $client = JApplicationHelper::getClientInfo($extension->client, true); diff --git a/libraries/cms/installer/adapter/plugin.php b/libraries/cms/installer/adapter/plugin.php index 1bca17deef825..cc5a8dea79d47 100644 --- a/libraries/cms/installer/adapter/plugin.php +++ b/libraries/cms/installer/adapter/plugin.php @@ -482,6 +482,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($row->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($row->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $row->name), JLog::WARNING, 'jerror'); + + return false; + } + // Get the plugin folder so we can properly build the plugin path if (trim($row->folder) == '') { diff --git a/libraries/cms/installer/adapter/template.php b/libraries/cms/installer/adapter/template.php index b24ae7431e092..6cacff3b36d1e 100644 --- a/libraries/cms/installer/adapter/template.php +++ b/libraries/cms/installer/adapter/template.php @@ -425,6 +425,17 @@ public function uninstall($id) return false; } + /* + * Does this extension have a parent package? + * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled + */ + if ($row->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($row->package_id)) + { + JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $row->name), JLog::WARNING, 'jerror'); + + return false; + } + $name = $row->element; $clientId = $row->client_id; diff --git a/libraries/cms/installer/installer.php b/libraries/cms/installer/installer.php index 3093f25bcc238..e1e4f341cb9d1 100644 --- a/libraries/cms/installer/installer.php +++ b/libraries/cms/installer/installer.php @@ -102,6 +102,14 @@ class JInstaller extends JAdapter */ protected $redirect_url = null; + /** + * Flag if the uninstall process was triggered by uninstalling a package + * + * @var boolean + * @since __DEPLOY_VERSION__ + */ + protected $packageUninstall = false; + /** * JInstaller instance container. * @@ -225,6 +233,32 @@ public function setRedirectUrl($newurl) $this->redirect_url = $newurl; } + /** + * Get whether this installer is uninstalling extensions which are part of a package + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public function isPackageUninstall() + { + return $this->packageUninstall; + } + + /** + * Set whether this installer is uninstalling extensions which are part of a package + * + * @param boolean $uninstall True if a package triggered the uninstall, false otherwise + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function setPackageUninstall($uninstall) + { + $this->packageUninstall = $uninstall; + } + /** * Get the upgrade switch * diff --git a/libraries/cms/installer/manifest/package.php b/libraries/cms/installer/manifest/package.php index cee1a5eff82ae..c15e310b8e990 100644 --- a/libraries/cms/installer/manifest/package.php +++ b/libraries/cms/installer/manifest/package.php @@ -40,6 +40,14 @@ class JInstallerManifestPackage extends JInstallerManifest */ public $scriptfile = ''; + /** + * Flag if the package blocks individual child extensions from being uninstalled + * + * @var boolean + * @since __DEPLOY_VERSION__ + */ + public $blockChildUninstall = false; + /** * Apply manifest data from a SimpleXMLElement to the object. * @@ -63,6 +71,16 @@ protected function loadManifestFromData(SimpleXMLElement $xml) $this->scriptfile = (string) $xml->scriptfile; $this->version = (string) $xml->version; + if (isset($xml->blockChildUninstall)) + { + $value = (string) $xml->blockChildUninstall; + + if ($value === '1' || $value === 'true') + { + $this->blockChildUninstall = true; + } + } + if (isset($xml->files->file) && count($xml->files->file)) { foreach ($xml->files->file as $file)