missionsParentIdMapping[$asset->name][$asset->id] = $asset; unset($asset); break; } } // Now create save the components asset tree to preload memory. foreach ($assets as $asset) { if (!isset(self::$assetPermissionsParentIdMapping[$asset->name])) { self::$assetPermissionsParentIdMapping[$asset->name] = [$rootAsset->id => $rootAsset, $asset->id => $asset]; self::$preloadedAssets[$asset->id] = $asset->name; } } // Mark all components asset type as preloaded. self::$preloadedAssetTypes['components'] = true; !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::preloadComponents (all components)'); return $components; } /** * Method to check if a group is authorised to perform an action, optionally on an asset. * * @param integer $groupId The path to the group for which to check authorisation. * @param string $action The name of the action to authorise. * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $preload Indicates whether preloading should be used. * * @return boolean True if authorised. * * @since 1.7.0 */ public static function checkGroup($groupId, $action, $assetKey = null, $preload = true) { // Sanitize input. $groupId = (int) $groupId; $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action))); return self::getAssetRules($assetKey, true, true, $preload)->allow($action, self::getGroupPath($groupId)); } /** * Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree * (including the leaf group id). * * @param mixed $groupId An integer or array of integers representing the identities to check. * * @return mixed True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ protected static function getGroupPath($groupId) { // Load all the groups to improve performance on intensive groups checks $groups = UserGroupsHelper::getInstance()->getAll(); if (!isset($groups[$groupId])) { return []; } return $groups[$groupId]->path; } /** * Method to return the Rules object for an asset. The returned object can optionally hold * only the rules explicitly set for the asset or the summation of all inherited rules from * parent assets and explicit rules. * * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $recursive True to return the rules object with inherited rules. * @param boolean $recursiveParentAsset True to calculate the rule also based on inherited component/extension rules. * @param boolean $preload Indicates whether preloading should be used. * * @return Rules Rules object for the asset. * * @since 1.7.0 * @note The non preloading code will be removed in 4.0. All asset rules should use asset preloading. */ public static function getAssetRules($assetKey, $recursive = false, $recursiveParentAsset = true, $preload = true) { // Auto preloads the components assets and root asset (if chosen). if ($preload) { self::preload('components'); } // When asset key is null fallback to root asset. $assetKey = self::cleanAssetKey($assetKey); // Auto preloads assets for the asset type (if chosen). if ($preload) { self::preload(self::getAssetType($assetKey)); } // Get the asset id and name. $assetId = self::getAssetId($assetKey); // If asset rules already cached em memory return it (only in full recursive mode). if ($recursive && $recursiveParentAsset && $assetId && isset(self::$assetRules[$assetId])) { return self::$assetRules[$assetId]; } // Get the asset name and the extension name. $assetName = self::getAssetName($assetKey); $extensionName = self::getExtensionNameFromAsset($assetName); // If asset id does not exist fallback to extension asset, then root asset. if (!$assetId) { if ($extensionName && $assetName !== $extensionName) { Log::add('No asset found for ' . $assetName . ', falling back to ' . $extensionName, Log::WARNING, 'assets'); return self::getAssetRules($extensionName, $recursive, $recursiveParentAsset, $preload); } if (self::$rootAssetId !== null && $assetName !== self::$preloadedAssets[self::$rootAssetId]) { Log::add('No asset found for ' . $assetName . ', falling back to ' . self::$preloadedAssets[self::$rootAssetId], Log::WARNING, 'assets'); return self::getAssetRules(self::$preloadedAssets[self::$rootAssetId], $recursive, $recursiveParentAsset, $preload); } } // Almost all calls can take advantage of preloading. if ($assetId && isset(self::$preloadedAssets[$assetId])) { !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules (id:' . $assetId . ' name:' . $assetName . ')'); // Collects permissions for each asset $collected = []; // If not in any recursive mode. We only want the asset rules. if (!$recursive && !$recursiveParentAsset) { $collected = [self::$assetPermissionsParentIdMapping[$extensionName][$assetId]->rules]; } else { // If there is any type of recursive mode. $ancestors = array_reverse(self::getAssetAncestors($extensionName, $assetId)); foreach ($ancestors as $id) { // There are no rules for this ancestor if (!isset(self::$assetPermissionsParentIdMapping[$extensionName][$id])) { continue; } // If full recursive mode, but not recursive parent mode, do not add the extension asset rules. if ($recursive && !$recursiveParentAsset && self::$assetPermissionsParentIdMapping[$extensionName][$id]->name === $extensionName) { continue; } // If not full recursive mode, but recursive parent mode, do not add other recursion rules. if ( !$recursive && $recursiveParentAsset && self::$assetPermissionsParentIdMapping[$extensionName][$id]->name !== $extensionName && (int) self::$assetPermissionsParentIdMapping[$extensionName][$id]->id !== $assetId ) { continue; } // If empty asset to not add to rules. if (self::$assetPermissionsParentIdMapping[$extensionName][$id]->rules === '{}') { continue; } $collected[] = self::$assetPermissionsParentIdMapping[$extensionName][$id]->rules; } } /** * Hashing the collected rules allows us to store * only one instance of the Rules object for * Assets that have the same exact permissions... * it's a great way to save some memory. */ $hash = md5(implode(',', $collected)); if (!isset(self::$assetRulesIdentities[$hash])) { $rules = new Rules(); $rules->mergeCollection($collected); self::$assetRulesIdentities[$hash] = $rules; } // Save asset rules to memory cache(only in full recursive mode). if ($recursive && $recursiveParentAsset) { self::$assetRules[$assetId] = self::$assetRulesIdentities[$hash]; } !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::getAssetRules (id:' . $assetId . ' name:' . $assetName . ')'); return self::$assetRulesIdentities[$hash]; } // Non preloading code. Use old slower method, slower. Only used in rare cases (if any) or without preloading chosen. Log::add('Asset ' . $assetKey . ' permissions fetch without preloading (slower method).', Log::INFO, 'assets'); !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules (assetKey:' . $assetKey . ')'); // There's no need to process it with the recursive method for the Root Asset ID. if ((int) $assetKey === 1) { $recursive = false; } // Get the database connection object. $db = Factory::getDbo(); // Build the database query to get the rules for the asset. $query = $db->createQuery() ->select($db->quoteName($recursive ? 'b.rules' : 'a.rules', 'rules')) ->from($db->quoteName('#__assets', 'a')); // If the asset identifier is numeric assume it is a primary key, else lookup by name. if (is_numeric($assetKey)) { $query->where($db->quoteName('a.id') . ' = :asset', 'OR') ->bind(':asset', $assetKey, ParameterType::INTEGER); } else { $query->where($db->quoteName('a.name') . ' = :asset', 'OR') ->bind(':asset', $assetKey); } if ($recursiveParentAsset && ($extensionName !== $assetKey || is_numeric($assetKey))) { $query->where($db->quoteName('a.name') . ' = :extension') ->bind(':extension', $extensionName); } // If we want the rules cascading up to the global asset node we need a self-join. if ($recursive) { $query->where($db->quoteName('a.parent_id') . ' = 0') ->join( 'LEFT', $db->quoteName('#__assets', 'b'), $db->quoteName('b.lft') . ' <= ' . $db->quoteName('a.lft') . ' AND ' . $db->quoteName('b.rgt') . ' >= ' . $db->quoteName('a.rgt') ) ->order($db->quoteName('b.lft')); } // Execute the query and load the rules from the result. $result = $db->setQuery($query)->loadColumn(); // Get the root even if the asset is not found and in recursive mode if (empty($result)) { $rootId = (new Asset($db))->getRootId(); $query->clear() ->select($db->quoteName('rules')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('id') . ' = :rootId') ->bind(':rootId', $rootId, ParameterType::INTEGER); $result = $db->setQuery($query)->loadColumn(); } // Instantiate and return the Rules object for the asset rules. $rules = new Rules(); $rules->mergeCollection($result); !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules Slower (assetKey:' . $assetKey . ')'); return $rules; } /** * Method to clean the asset key to make sure we always have something. * * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * * @return integer|string Asset id or asset name. * * @since 3.7.0 */ protected static function cleanAssetKey($assetKey = null) { // If it's a valid asset key, clean it and return it. if ($assetKey) { return strtolower(preg_replace('#[\s\-]+#', '.', trim($assetKey))); } // Return root asset id if already preloaded. if (self::$rootAssetId !== null) { return self::$rootAssetId; } // No preload. Return root asset id from Assets. $assets = new Asset(Factory::getDbo()); return $assets->getRootId(); } /** * Method to get the asset id from the asset key. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return integer The asset id. * * @since 3.7.0 */ protected static function getAssetId($assetKey) { static $loaded = []; // If the asset is already an id return it. if (is_numeric($assetKey)) { return (int) $assetKey; } if (!isset($loaded[$assetKey])) { // It's the root asset. if (self::$rootAssetId !== null && $assetKey === self::$preloadedAssets[self::$rootAssetId]) { $loaded[$assetKey] = self::$rootAssetId; } else { $preloadedAssetsByName = array_flip(self::$preloadedAssets); // If we already have the asset name stored in preloading, example, a component, no need to fetch it from table. if (isset($preloadedAssetsByName[$assetKey])) { $loaded[$assetKey] = $preloadedAssetsByName[$assetKey]; } else { // Else we have to do an extra db query to fetch it from the table. $table = new Asset(Factory::getDbo()); $table->load(['name' => $assetKey]); $loaded[$assetKey] = $table->id ?? 0; } } } return (int) $loaded[$assetKey]; } /** * Method to get the asset name from the asset key. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The asset name (ex: com_content.article.8). * * @since 3.7.0 */ protected static function getAssetName($assetKey) { static $loaded = []; // If the asset is already a string return it. if (!is_numeric($assetKey)) { return $assetKey; } if (!isset($loaded[$assetKey])) { // It's the root asset. if (self::$rootAssetId !== null && $assetKey === self::$rootAssetId) { $loaded[$assetKey] = self::$preloadedAssets[self::$rootAssetId]; } elseif (isset(self::$preloadedAssets[$assetKey])) { // If we already have the asset name stored in preloading, example, a component, no need to fetch it from table. $loaded[$assetKey] = self::$preloadedAssets[$assetKey]; } else { // Else we have to do an extra db query to fetch it from the table fetch it from table. $table = new Asset(Factory::getDbo()); $table->load($assetKey); $loaded[$assetKey] = $table->name; } } return $loaded[$assetKey]; } /** * Method to get the extension name from the asset name. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The extension name (ex: com_content). * * @since 1.6 */ public static function getExtensionNameFromAsset($assetKey) { static $loaded = []; if (!isset($loaded[$assetKey])) { $assetName = self::getAssetName($assetKey); $firstDot = strpos($assetName, '.'); if ($assetName !== 'root.1' && $firstDot !== false) { $assetName = substr($assetName, 0, $firstDot); } $loaded[$assetKey] = $assetName; } return $loaded[$assetKey]; } /** * Method to get the asset type from the asset name. * * For top level components this returns "components": * 'com_content' returns 'components' * * For other types: * 'com_content.article.1' returns 'com_content.article' * 'com_content.category.1' returns 'com_content.category' * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The asset type (ex: com_content.article). * * @since 1.6 */ public static function getAssetType($assetKey) { // If the asset is already a string return it. $assetName = self::getAssetName($assetKey); $lastDot = strrpos($assetName, '.'); if ($assetName !== 'root.1' && $lastDot !== false) { return substr($assetName, 0, $lastDot); } return 'components'; } /** * Method to return the title of a user group * * @param integer $groupId Id of the group for which to get the title of. * * @return string The title of the group * * @since 3.5 */ public static function getGroupTitle($groupId) { // Cast as integer until method is typehinted. $groupId = (int) $groupId; // Fetch the group title from the database $db = Factory::getDbo(); $query = $db->createQuery(); $query->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); return $db->loadResult(); } /** * Method to return a list of user groups mapped to a user. The returned list can optionally hold * only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited * by the user. * * @param integer $userId Id of the user for which to get the list of groups. * @param boolean $recursive True to include inherited user groups. * * @return array List of user group ids to which the user is mapped. * * @since 1.7.0 */ public static function getGroupsByUser($userId, $recursive = true) { // Cast as integer until method is typehinted. $userId = (int) $userId; // Creates a simple unique string for each parameter combination: $storeId = $userId . ':' . (int) $recursive; if (!isset(self::$groupsByUser[$storeId])) { // @todo: Uncouple this from ComponentHelper and allow for a configuration setting or value injection. $guestUsergroup = (int) ComponentHelper::getParams('com_users')->get('guest_usergroup', 1); // Guest user (if only the actually assigned group is requested) if (empty($userId) && !$recursive) { $result = [$guestUsergroup]; } else { // Registered user and guest if all groups are requested $db = Factory::getDbo(); // Build the database query to get the rules for the asset. $query = $db->createQuery() ->select($db->quoteName($recursive ? 'b.id' : 'a.id')); if (empty($userId)) { $query->from($db->quoteName('#__usergroups', 'a')) ->where($db->quoteName('a.id') . ' = :guest') ->bind(':guest', $guestUsergroup, ParameterType::INTEGER); } else { $query->from($db->quoteName('#__user_usergroup_map', 'map')) ->where($db->quoteName('map.user_id') . ' = :userId') ->join('LEFT', $db->quoteName('#__usergroups', 'a'), $db->quoteName('a.id') . ' = ' . $db->quoteName('map.group_id')) ->bind(':userId', $userId, ParameterType::INTEGER); } // If we want the rules cascading up to the global asset node we need a self-join. if ($recursive) { $query->join( 'LEFT', $db->quoteName('#__usergroups', 'b'), $db->quoteName('b.lft') . ' <= ' . $db->quoteName('a.lft') . ' AND ' . $db->quoteName('b.rgt') . ' >= ' . $db->quoteName('a.rgt') ); } // Execute the query and load the rules from the result. $db->setQuery($query); $result = $db->loadColumn(); // Clean up any NULL or duplicate values, just in case $result = ArrayHelper::toInteger($result); if (empty($result)) { $result = [1]; } else { $result = array_unique($result); } } self::$groupsByUser[$storeId] = $result; } return self::$groupsByUser[$storeId]; } /** * Method to return a list of user Ids contained in a Group * * @param integer $groupId The group Id * @param boolean $recursive Recursively include all child groups (optional) * * @return array * * @since 1.7.0 * @todo This method should move somewhere else */ public static function getUsersByGroup($groupId, $recursive = false) { // Cast as integer until method is typehinted. $groupId = (int) $groupId; // Get a database object. $db = Factory::getDbo(); $test = $recursive ? ' >= ' : ' = '; // First find the users contained in the group $query = $db->createQuery() ->select('DISTINCT(' . $db->quoteName('user_id') . ')') ->from($db->quoteName('#__usergroups', 'ug1')) ->join( 'INNER', $db->quoteName('#__usergroups', 'ug2'), $db->quoteName('ug2.lft') . $test . $db->quoteName('ug1.lft') . ' AND ' . $db->quoteName('ug1.rgt') . $test . $db->quoteName('ug2.rgt') ) ->join('INNER', $db->quoteName('#__user_usergroup_map', 'm'), $db->quoteName('ug2.id') . ' = ' . $db->quoteName('m.group_id')) ->where($db->quoteName('ug1.id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); $result = $db->loadColumn(); // Clean up any NULL values, just in case $result = ArrayHelper::toInteger($result); return $result; } /** * Method to return a list of view levels for which the user is authorised. * * @param integer $userId Id of the user for which to get the list of authorised view levels. * * @return array List of view levels for which the user is authorised. * * @since 1.7.0 */ public static function getAuthorisedViewLevels($userId) { // Only load the view levels once. if (empty(self::$viewLevels)) { // Get a database object. $db = Factory::getDbo(); // Build the base query. $query = $db->createQuery() ->select($db->quoteName(['id', 'rules'])) ->from($db->quoteName('#__viewlevels')); // Set the query for execution. $db->setQuery($query); // Build the view levels array. foreach ($db->loadAssocList() as $level) { self::$viewLevels[$level['id']] = (array) json_decode($level['rules']); } } // Initialise the authorised array. $authorised = [1]; // Check for the recovery mode setting and return early. $user = User::getInstance($userId); $root_user = Factory::getApplication()->get('root_user'); if (($user->username && $user->username == $root_user) || (is_numeric($root_user) && $user->id > 0 && $user->id == $root_user)) { // Find the super user levels. foreach (self::$viewLevels as $level => $rule) { foreach ($rule as $id) { if ($id > 0 && self::checkGroup($id, 'core.admin')) { $authorised[] = $level; break; } } } return array_values(array_unique($authorised)); } // Get all groups that the user is mapped to recursively. $groups = self::getGroupsByUser($userId); // Find the authorised levels. foreach (self::$viewLevels as $level => $rule) { foreach ($rule as $id) { if (($id < 0) && (($id * -1) == $userId)) { $authorised[] = $level; break; } if (($id >= 0) && \in_array($id, $groups)) { // Check to see if the group is mapped to the level. $authorised[] = $level; break; } } } return array_values(array_unique($authorised)); } /** * Method to return a list of actions from a file for which permissions can be set. * * @param string $file The path to the XML file. * @param string $xpath An optional xpath to search for the fields. * * @return boolean|array False if case of error or the list of actions available. * * @since 3.0.0 */ public static function getActionsFromFile($file, $xpath = "/access/section[@name='component']/") { if (!is_file($file) || !is_readable($file)) { // If unable to find the file return false. return false; } // Else return the actions from the xml. $xml = simplexml_load_file($file); return self::getActionsFromData($xml, $xpath); } /** * Method to return a list of actions from a string or from an xml for which permissions can be set. * * @param string|\SimpleXMLElement $data The XML string or an XML element. * @param string $xpath An optional xpath to search for the fields. * * @return boolean|array False if case of error or the list of actions available. * * @since 3.0.0 */ public static function getActionsFromData($data, $xpath = "/access/section[@name='component']/") { // If the data to load isn't already an XML element or string return false. if ((!($data instanceof \SimpleXMLElement)) && (!\is_string($data))) { return false; } // Attempt to load the XML if a string. if (\is_string($data)) { try { $data = new \SimpleXMLElement($data); } catch (\Exception) { return false; } // Make sure the XML loaded correctly. if (!$data) { return false; } } // Initialise the actions array $actions = []; // Get the elements from the xpath $elements = $data->xpath($xpath . 'action[@name][@title]'); // If there some elements, analyse them if (!empty($elements)) { foreach ($elements as $element) { // Add the action to the actions array $action = [ 'name' => (string) $element['name'], 'title' => (string) $element['title'], ]; if (isset($element['description'])) { $action['description'] = (string) $element['description']; } $actions[] = (object) $action; } } // Finally return the actions array return $actions; } } An Error Occurred: Whoops, looks like something went wrong.

Sorry, there was a problem we could not recover from.

The server returned a "500 - Whoops, looks like something went wrong."

Help me resolve this