public static \Joomla\CMS\Access\Rules
getAssetRules
(mixed $assetKey, mixed $recursive = false, mixed $recursiveParentAsset = true, mixed $preload = true)
/**
* 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 = array();
// If not in any recursive mode. We only want the asset rules.
if (!$recursive && !$recursiveParentAsset) {
$collected = array(self::$assetPermissionsParentIdMapping[$extensionName][$assetId]->rules);
} else {
$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->getQuery(true)->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 <strong>Slower</strong> (assetKey:' . $assetKey . ')');
return $rules;
}