diff --git a/administrator/components/com_installer/forms/filter_manage.xml b/administrator/components/com_installer/forms/filter_manage.xml
index acf8a4dcb9e34..977a73c9ae01d 100644
--- a/administrator/components/com_installer/forms/filter_manage.xml
+++ b/administrator/components/com_installer/forms/filter_manage.xml
@@ -28,6 +28,16 @@
+
+
+
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+namespace Joomla\Component\Installer\Administrator\Field;
+
+use Joomla\CMS\Form\Field\ListField;
+use Joomla\Component\Installer\Administrator\Helper\InstallerHelper;
+
+/**
+ * Package field.
+ *
+ * Selects the extension ID of an extension of the "package" type.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class PackageField extends ListField
+{
+ /**
+ * Method to get the field options.
+ *
+ * @return array The field option objects.
+ * @since __DEPLOY_VERSION__
+ */
+ protected function getOptions()
+ {
+ $options = InstallerHelper::getPackageOptions();
+
+ return array_merge(parent::getOptions(), $options);
+ }
+}
diff --git a/administrator/components/com_installer/src/Helper/InstallerHelper.php b/administrator/components/com_installer/src/Helper/InstallerHelper.php
index 83c7f39d6efa9..501b9adec5642 100644
--- a/administrator/components/com_installer/src/Helper/InstallerHelper.php
+++ b/administrator/components/com_installer/src/Helper/InstallerHelper.php
@@ -16,6 +16,7 @@
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
+use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use SimpleXMLElement;
@@ -119,6 +120,66 @@ public static function getStateOptions()
return $options;
}
+ /**
+ * Get a list of filter options for extensions of the "package" type.
+ *
+ * @return array
+ * @since __DEPLOY_VERSION__
+ */
+ public static function getPackageOptions(): array
+ {
+ $options = [];
+
+ /** @var DatabaseDriver $db The application's database driver object */
+ $db = Factory::getContainer()->get(DatabaseDriver::class);
+ $query = $db->getQuery(true)
+ ->select(
+ $db->quoteName(
+ [
+ 'extension_id',
+ 'name',
+ 'element',
+ ]
+ )
+ )
+ ->from($db->quoteName('#__extensions'))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('package'));
+ $extensions = $db->setQuery($query)->loadObjectList() ?: [];
+
+ if (empty($extensions))
+ {
+ return $options;
+ }
+
+ $language = Factory::getApplication()->getLanguage();
+ $arrayKeys = array_map(
+ function (object $entry) use ($language): string
+ {
+ $language->load($entry->element, JPATH_ADMINISTRATOR);
+
+ return Text::_($entry->name);
+ },
+ $extensions
+ );
+ $arrayValues = array_map(
+ function (object $entry): int
+ {
+ return $entry->extension_id;
+ },
+ $extensions
+ );
+
+ $extensions = array_combine($arrayKeys, $arrayValues);
+ ksort($extensions);
+
+ foreach ($extensions as $label => $id)
+ {
+ $options[] = HTMLHelper::_('select.option', $id, $label);
+ }
+
+ return $options;
+ }
+
/**
* Get a list of filter options for the application statuses.
*
diff --git a/administrator/components/com_installer/src/Model/ManageModel.php b/administrator/components/com_installer/src/Model/ManageModel.php
index 456cc38e76100..dccc8903d5769 100644
--- a/administrator/components/com_installer/src/Model/ManageModel.php
+++ b/administrator/components/com_installer/src/Model/ManageModel.php
@@ -81,6 +81,7 @@ protected function populateState($ordering = 'name', $direction = 'asc')
// Load the filter state.
$this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string'));
$this->setState('filter.client_id', $this->getUserStateFromRequest($this->context . '.filter.client_id', 'filter_client_id', null, 'int'));
+ $this->setState('filter.package_id', $this->getUserStateFromRequest($this->context . '.filter.package_id', 'filter_package_id', null, 'int'));
$this->setState('filter.status', $this->getUserStateFromRequest($this->context . '.filter.status', 'filter_status', '', 'string'));
$this->setState('filter.type', $this->getUserStateFromRequest($this->context . '.filter.type', 'filter_type', '', 'string'));
$this->setState('filter.folder', $this->getUserStateFromRequest($this->context . '.filter.folder', 'filter_folder', '', 'string'));
@@ -330,11 +331,12 @@ protected function getListQuery()
->where('state = 0');
// Process select filters.
- $status = $this->getState('filter.status', '');
- $type = $this->getState('filter.type');
- $clientId = $this->getState('filter.client_id', '');
- $folder = $this->getState('filter.folder');
- $core = $this->getState('filter.core', '');
+ $status = $this->getState('filter.status', '');
+ $type = $this->getState('filter.type');
+ $clientId = $this->getState('filter.client_id', '');
+ $folder = $this->getState('filter.folder');
+ $core = $this->getState('filter.core', '');
+ $packageId = $this->getState('filter.package_id', '');
if ($status !== '')
{
@@ -368,6 +370,16 @@ protected function getListQuery()
->bind(':clientid', $clientId, ParameterType::INTEGER);
}
+ if ($packageId !== '')
+ {
+ $packageId = (int) $packageId;
+ $query->where(
+ '((' . $db->quoteName('package_id') . ' = :packageId1) OR '
+ . '(' . $db->quoteName('extension_id') . ' = :packageId2))'
+ )
+ ->bind([':packageId1',':packageId2'], $packageId, ParameterType::INTEGER);
+ }
+
if ($folder)
{
$folder = $folder === '*' ? '' : $folder;
diff --git a/administrator/language/en-GB/com_installer.ini b/administrator/language/en-GB/com_installer.ini
index 7ecb634a9d45a..7012fa0c5d09e 100644
--- a/administrator/language/en-GB/com_installer.ini
+++ b/administrator/language/en-GB/com_installer.ini
@@ -102,6 +102,8 @@ COM_INSTALLER_LANGUAGES_AVAILABLE_LANGUAGES="Available Languages"
COM_INSTALLER_LANGUAGES_FILTER_SEARCH_DESC="Search in language name and language tag."
COM_INSTALLER_LANGUAGES_FILTER_SEARCH_LABEL="Search Languages"
COM_INSTALLER_LANGUAGES_TABLE_CAPTION="Table of Available Languages"
+COM_INSTALLER_MANAGE_FILTER_PACKAGE_ID_LABEL="Package"
+COM_INSTALLER_MANAGE_FILTER_PACKAGE_ID_DESC="Search for a package extension and extensions included with that package extension."
COM_INSTALLER_MANAGE_FILTER_SEARCH_DESC="Search in extension name. Prefix with ID: to search for an extension ID."
COM_INSTALLER_MANAGE_FILTER_SEARCH_LABEL="Search Extensions"
COM_INSTALLER_MANAGE_TABLE_CAPTION="Table of Extensions"
@@ -280,6 +282,7 @@ COM_INSTALLER_VALUE_CORE_SELECT="- Select Extensions -"
COM_INSTALLER_VALUE_CORE_YES="Core Extensions"
COM_INSTALLER_VALUE_FOLDER_NONAPPLICABLE="N/A"
COM_INSTALLER_VALUE_FOLDER_SELECT="- Select Folder -"
+COM_INSTALLER_VALUE_PACKAGE_ID_SELECT="- Select Package -"
COM_INSTALLER_VALUE_STATE_SELECT="- Select Status -"
COM_INSTALLER_VALUE_SUPPORTED_EXISTS="Download Key valid"
COM_INSTALLER_VALUE_SUPPORTED_MISSING="Download Key invalid"