* @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Factory; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extension Helper class. * * @since 3.7.4 * * @deprecated 4.0 Replace class with a non static methods for better testing */ class ExtensionHelper { /** * The loaded extensions. * * @var array * @since 4.0.0 */ public static $extensions = [ModuleInterface::class => [], ComponentInterface::class => [], PluginInterface::class => []]; /** * The loaded extensions. * * @var array * @since 4.0.0 */ private static $loadedExtensions = []; /** * Array of core extensions * Each element is an array with elements "type", "element", "folder" and * "client_id". * * @var array * @since 3.7.4 */ protected static $coreExtensions = array( // Format: `type`, `element`, `folder`, `client_id` // Core component extensions array('component', 'com_actionlogs', '', 1), array('component', 'com_admin', '', 1), array('component', 'com_ajax', '', 1), array('component', 'com_associations', '', 1), array('component', 'com_banners', '', 1), array('component', 'com_cache', '', 1), array('component', 'com_categories', '', 1), array('component', 'com_checkin', '', 1), array('component', 'com_config', '', 1), array('component', 'com_contact', '', 1), array('component', 'com_content', '', 1), array('component', 'com_contenthistory', '', 1), array('component', 'com_cpanel', '', 1), array('component', 'com_fields', '', 1), array('component', 'com_finder', '', 1), array('component', 'com_installer', '', 1), array('component', 'com_joomlaupdate', '', 1), array('component', 'com_languages', '', 1), array('component', 'com_login', '', 1), array('component', 'com_mails', '', 1), array('component', 'com_media', '', 1), array('component', 'com_menus', '', 1), array('component', 'com_messages', '', 1), array('component', 'com_modules', '', 1), array('component', 'com_newsfeeds', '', 1), array('component', 'com_plugins', '', 1), array('component', 'com_postinstall', '', 1), array('component', 'com_privacy', '', 1), array('component', 'com_redirect', '', 1), array('component', 'com_scheduler', '', 1), array('component', 'com_tags', '', 1), array('component', 'com_templates', '', 1), array('component', 'com_users', '', 1), array('component', 'com_workflow', '', 1), array('component', 'com_wrapper', '', 1), // Core file extensions array('file', 'joomla', '', 0), // Core language extensions - administrator array('language', 'en-GB', '', 1), // Core language extensions - site array('language', 'en-GB', '', 0), // Core language extensions - API array('language', 'en-GB', '', 3), // Core library extensions array('library', 'joomla', '', 0), array('library', 'phpass', '', 0), // Core module extensions - administrator array('module', 'mod_custom', '', 1), array('module', 'mod_feed', '', 1), array('module', 'mod_frontend', '', 1), array('module', 'mod_latest', '', 1), array('module', 'mod_latestactions', '', 1), array('module', 'mod_logged', '', 1), array('module', 'mod_login', '', 1), array('module', 'mod_loginsupport', '', 1), array('module', 'mod_menu', '', 1), array('module', 'mod_messages', '', 1), array('module', 'mod_multilangstatus', '', 1), array('module', 'mod_popular', '', 1), array('module', 'mod_post_installation_messages', '', 1), array('module', 'mod_privacy_dashboard', '', 1), array('module', 'mod_privacy_status', '', 1), array('module', 'mod_quickicon', '', 1), array('module', 'mod_sampledata', '', 1), array('module', 'mod_stats_admin', '', 1), array('module', 'mod_submenu', '', 1), array('module', 'mod_title', '', 1), array('module', 'mod_toolbar', '', 1), array('module', 'mod_user', '', 1), array('module', 'mod_version', '', 1), // Core module extensions - site array('module', 'mod_articles_archive', '', 0), array('module', 'mod_articles_categories', '', 0), array('module', 'mod_articles_category', '', 0), array('module', 'mod_articles_latest', '', 0), array('module', 'mod_articles_news', '', 0), array('module', 'mod_articles_popular', '', 0), array('module', 'mod_banners', '', 0), array('module', 'mod_breadcrumbs', '', 0), array('module', 'mod_custom', '', 0), array('module', 'mod_feed', '', 0), array('module', 'mod_finder', '', 0), array('module', 'mod_footer', '', 0), array('module', 'mod_languages', '', 0), array('module', 'mod_login', '', 0), array('module', 'mod_menu', '', 0), array('module', 'mod_random_image', '', 0), array('module', 'mod_related_items', '', 0), array('module', 'mod_stats', '', 0), array('module', 'mod_syndicate', '', 0), array('module', 'mod_tags_popular', '', 0), array('module', 'mod_tags_similar', '', 0), array('module', 'mod_users_latest', '', 0), array('module', 'mod_whosonline', '', 0), array('module', 'mod_wrapper', '', 0), // Core package extensions array('package', 'pkg_en-GB', '', 0), // Core plugin extensions - actionlog array('plugin', 'joomla', 'actionlog', 0), // Core plugin extensions - API Authentication array('plugin', 'basic', 'api-authentication', 0), array('plugin', 'token', 'api-authentication', 0), // Core plugin extensions - authentication array('plugin', 'cookie', 'authentication', 0), array('plugin', 'joomla', 'authentication', 0), array('plugin', 'ldap', 'authentication', 0), // Core plugin extensions - behaviour array('plugin', 'taggable', 'behaviour', 0), array('plugin', 'versionable', 'behaviour', 0), // Core plugin extensions - captcha array('plugin', 'recaptcha', 'captcha', 0), array('plugin', 'recaptcha_invisible', 'captcha', 0), // Core plugin extensions - content array('plugin', 'confirmconsent', 'content', 0), array('plugin', 'contact', 'content', 0), array('plugin', 'emailcloak', 'content', 0), array('plugin', 'fields', 'content', 0), array('plugin', 'finder', 'content', 0), array('plugin', 'joomla', 'content', 0), array('plugin', 'loadmodule', 'content', 0), array('plugin', 'pagebreak', 'content', 0), array('plugin', 'pagenavigation', 'content', 0), array('plugin', 'vote', 'content', 0), // Core plugin extensions - editors array('plugin', 'codemirror', 'editors', 0), array('plugin', 'none', 'editors', 0), array('plugin', 'tinymce', 'editors', 0), // Core plugin extensions - editors xtd array('plugin', 'article', 'editors-xtd', 0), array('plugin', 'contact', 'editors-xtd', 0), array('plugin', 'fields', 'editors-xtd', 0), array('plugin', 'image', 'editors-xtd', 0), array('plugin', 'menu', 'editors-xtd', 0), array('plugin', 'module', 'editors-xtd', 0), array('plugin', 'pagebreak', 'editors-xtd', 0), array('plugin', 'readmore', 'editors-xtd', 0), // Core plugin extensions - extension array('plugin', 'joomla', 'extension', 0), array('plugin', 'namespacemap', 'extension', 0), array('plugin', 'finder', 'extension', 0), // Core plugin extensions - fields array('plugin', 'calendar', 'fields', 0), array('plugin', 'checkboxes', 'fields', 0), array('plugin', 'color', 'fields', 0), array('plugin', 'editor', 'fields', 0), array('plugin', 'imagelist', 'fields', 0), array('plugin', 'integer', 'fields', 0), array('plugin', 'list', 'fields', 0), array('plugin', 'media', 'fields', 0), array('plugin', 'radio', 'fields', 0), array('plugin', 'sql', 'fields', 0), array('plugin', 'subform', 'fields', 0), array('plugin', 'text', 'fields', 0), array('plugin', 'textarea', 'fields', 0), array('plugin', 'url', 'fields', 0), array('plugin', 'user', 'fields', 0), array('plugin', 'usergrouplist', 'fields', 0), // Core plugin extensions - filesystem array('plugin', 'local', 'filesystem', 0), // Core plugin extensions - finder array('plugin', 'categories', 'finder', 0), array('plugin', 'contacts', 'finder', 0), array('plugin', 'content', 'finder', 0), array('plugin', 'newsfeeds', 'finder', 0), array('plugin', 'tags', 'finder', 0), // Core plugin extensions - installer array('plugin', 'folderinstaller', 'installer', 0), array('plugin', 'override', 'installer', 0), array('plugin', 'packageinstaller', 'installer', 0), array('plugin', 'urlinstaller', 'installer', 0), array('plugin', 'webinstaller', 'installer', 0), // Core plugin extensions - media-action array('plugin', 'crop', 'media-action', 0), array('plugin', 'resize', 'media-action', 0), array('plugin', 'rotate', 'media-action', 0), // Core plugin extensions - Multi-factor Authentication array('plugin', 'email', 'multifactorauth', 0), array('plugin', 'fixed', 'multifactorauth', 0), array('plugin', 'totp', 'multifactorauth', 0), array('plugin', 'webauthn', 'multifactorauth', 0), array('plugin', 'yubikey', 'multifactorauth', 0), // Core plugin extensions - privacy array('plugin', 'actionlogs', 'privacy', 0), array('plugin', 'consents', 'privacy', 0), array('plugin', 'contact', 'privacy', 0), array('plugin', 'content', 'privacy', 0), array('plugin', 'message', 'privacy', 0), array('plugin', 'user', 'privacy', 0), // Core plugin extensions - quick icon array('plugin', 'downloadkey', 'quickicon', 0), array('plugin', 'extensionupdate', 'quickicon', 0), array('plugin', 'joomlaupdate', 'quickicon', 0), array('plugin', 'overridecheck', 'quickicon', 0), array('plugin', 'phpversioncheck', 'quickicon', 0), array('plugin', 'privacycheck', 'quickicon', 0), // Core plugin extensions - sample data array('plugin', 'blog', 'sampledata', 0), array('plugin', 'multilang', 'sampledata', 0), // Core plugin extensions - system array('plugin', 'accessibility', 'system', 0), array('plugin', 'actionlogs', 'system', 0), array('plugin', 'cache', 'system', 0), array('plugin', 'debug', 'system', 0), array('plugin', 'fields', 'system', 0), array('plugin', 'highlight', 'system', 0), array('plugin', 'httpheaders', 'system', 0), array('plugin', 'jooa11y', 'system', 0), array('plugin', 'languagecode', 'system', 0), array('plugin', 'languagefilter', 'system', 0), array('plugin', 'log', 'system', 0), array('plugin', 'logout', 'system', 0), array('plugin', 'logrotation', 'system', 0), array('plugin', 'privacyconsent', 'system', 0), array('plugin', 'redirect', 'system', 0), array('plugin', 'remember', 'system', 0), array('plugin', 'schedulerunner', 'system', 0), array('plugin', 'sef', 'system', 0), array('plugin', 'sessiongc', 'system', 0), array('plugin', 'shortcut', 'system', 0), array('plugin', 'skipto', 'system', 0), array('plugin', 'stats', 'system', 0), array('plugin', 'tasknotification', 'system', 0), array('plugin', 'updatenotification', 'system', 0), array('plugin', 'webauthn', 'system', 0), // Core plugin extensions - task scheduler array('plugin', 'checkfiles', 'task', 0), array('plugin', 'demotasks', 'task', 0), array('plugin', 'requests', 'task', 0), array('plugin', 'sitestatus', 'task', 0), // Core plugin extensions - user array('plugin', 'contactcreator', 'user', 0), array('plugin', 'joomla', 'user', 0), array('plugin', 'profile', 'user', 0), array('plugin', 'terms', 'user', 0), array('plugin', 'token', 'user', 0), // Core plugin extensions - webservices array('plugin', 'banners', 'webservices', 0), array('plugin', 'config', 'webservices', 0), array('plugin', 'contact', 'webservices', 0), array('plugin', 'content', 'webservices', 0), array('plugin', 'installer', 'webservices', 0), array('plugin', 'languages', 'webservices', 0), array('plugin', 'media', 'webservices', 0), array('plugin', 'menus', 'webservices', 0), array('plugin', 'messages', 'webservices', 0), array('plugin', 'modules', 'webservices', 0), array('plugin', 'newsfeeds', 'webservices', 0), array('plugin', 'plugins', 'webservices', 0), array('plugin', 'privacy', 'webservices', 0), array('plugin', 'redirect', 'webservices', 0), array('plugin', 'tags', 'webservices', 0), array('plugin', 'templates', 'webservices', 0), array('plugin', 'users', 'webservices', 0), // Core plugin extensions - workflow array('plugin', 'featuring', 'workflow', 0), array('plugin', 'notification', 'workflow', 0), array('plugin', 'publishing', 'workflow', 0), // Core template extensions - administrator array('template', 'atum', '', 1), // Core template extensions - site array('template', 'cassiopeia', '', 0), ); /** * Array of core extension IDs. * * @var array * @since 4.0.0 */ protected static $coreExtensionIds; /** * Gets the core extensions. * * @return array Array with core extensions. * Each extension is an array with following format: * `type`, `element`, `folder`, `client_id`. * * @since 3.7.4 */ public static function getCoreExtensions() { return self::$coreExtensions; } /** * Returns an array of core extension IDs. * * @return array * * @since 4.0.0 * @throws \RuntimeException */ public static function getCoreExtensionIds() { if (self::$coreExtensionIds !== null) { return self::$coreExtensionIds; } $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')); foreach (self::$coreExtensions as $extension) { $values = $query->bindArray($extension, [ParameterType::STRING, ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER]); $query->where( '(' . $db->quoteName('type') . ' = ' . $values[0] . ' AND ' . $db->quoteName('element') . ' = ' . $values[1] . ' AND ' . $db->quoteName('folder') . ' = ' . $values[2] . ' AND ' . $db->quoteName('client_id') . ' = ' . $values[3] . ')', 'OR' ); } $db->setQuery($query); self::$coreExtensionIds = $db->loadColumn(); return self::$coreExtensionIds; } /** * Check if an extension is core or not * * @param string $type The extension's type. * @param string $element The extension's element name. * @param integer $clientId The extension's client ID. Default 0. * @param string $folder The extension's folder. Default ''. * * @return boolean True if core, false if not. * * @since 3.7.4 */ public static function checkIfCoreExtension($type, $element, $clientId = 0, $folder = '') { return \in_array(array($type, $element, $folder, $clientId), self::$coreExtensions); } /** * Returns an extension record for the given name. * * @param string $element The extension element * @param string $type The extension type * @param integer|null $clientId The client ID * @param string|null $folder Plugin folder * * @return \stdClass|null The object or null if not found. * * @since 4.0.0 * @throws \InvalidArgumentException */ public static function getExtensionRecord(string $element, string $type, ?int $clientId = null, ?string $folder = null): ?\stdClass { if ($type === 'plugin' && $folder === null) { throw new \InvalidArgumentException(sprintf('`$folder` is required when `$type` is `plugin` in %s()', __METHOD__)); } if (\in_array($type, ['module', 'language', 'template'], true) && $clientId === null) { throw new \InvalidArgumentException( sprintf('`$clientId` is required when `$type` is `module`, `language` or `template` in %s()', __METHOD__) ); } $key = $element . '.' . $type . '.' . $clientId . '.' . $folder; if (!\array_key_exists($key, self::$loadedExtensions)) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('element') . ' = :element', $db->quoteName('type') . ' = :type', ] ) ->bind(':element', $element) ->bind(':type', $type); if ($clientId !== null) { $query->where($db->quoteName('client_id') . ' = :clientId') ->bind(':clientId', $clientId, ParameterType::INTEGER); } if ($folder !== null) { $query->where($db->quoteName('folder') . ' = :folder') ->bind(':folder', $folder); } $query->setLimit(1); $db->setQuery($query); self::$loadedExtensions[$key] = $db->loadObject(); } return self::$loadedExtensions[$key]; } }