From 6412e0440da02b945b1d94dcd28809f1b16778f8 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sun, 21 Sep 2025 21:00:00 -0400 Subject: [PATCH 1/2] Update modResource.php Remove dot in final segment when restriction is the preset 'alpha' or 'alphanumeric' --- core/src/Revolution/modResource.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/Revolution/modResource.php b/core/src/Revolution/modResource.php index 45cbe93c9e..a43a972896 100644 --- a/core/src/Revolution/modResource.php +++ b/core/src/Revolution/modResource.php @@ -266,6 +266,10 @@ public static function filterPathSegment(&$xpdo, $segment, array $options = []) } } + if (in_array($restrictchars, ['alpha', 'alphanumeric']) && strpos($segment, '.') !== false) { + $segment = str_replace('.', $delimiter, $segment); + } + /* replace one or more space characters with word delimiter */ $segment = preg_replace('/\s+/u', $delimiter, $segment); From 9c66c4266963956497bd2262e3bef60f1038a5db Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sun, 21 Sep 2025 21:43:05 -0400 Subject: [PATCH 2/2] Format modResource.php Code quality fixes --- core/src/Revolution/modResource.php | 185 +++++++++++++++++----------- 1 file changed, 116 insertions(+), 69 deletions(-) diff --git a/core/src/Revolution/modResource.php b/core/src/Revolution/modResource.php index a43a972896..9d4fb7b554 100644 --- a/core/src/Revolution/modResource.php +++ b/core/src/Revolution/modResource.php @@ -178,17 +178,20 @@ public static function filterPathSegment(&$xpdo, $segment, array $options = []) { /* setup the various options */ $iconv = function_exists('iconv'); - $mbext = function_exists('mb_strlen') && (boolean)$xpdo->getOption('use_multibyte', $options, false); + $mbext = function_exists('mb_strlen') && (bool)$xpdo->getOption('use_multibyte', $options, false); $charset = strtoupper((string)$xpdo->getOption('modx_charset', $options, 'UTF-8')); $delimiter = $xpdo->getOption('friendly_alias_word_delimiter', $options, '-'); $delimiters = $xpdo->getOption('friendly_alias_word_delimiters', $options, '-_'); - $maxlength = (integer)$xpdo->getOption('friendly_alias_max_length', $options, 0); - $stripElementTags = (boolean)$xpdo->getOption('friendly_alias_strip_element_tags', $options, true); + $maxlength = (int)$xpdo->getOption('friendly_alias_max_length', $options, 0); + $stripElementTags = (bool)$xpdo->getOption('friendly_alias_strip_element_tags', $options, true); $trimchars = $xpdo->getOption('friendly_alias_trim_chars', $options, '/.' . $delimiters); $restrictchars = $xpdo->getOption('friendly_alias_restrict_chars', $options, 'pattern'); - $restrictcharspattern = $xpdo->getOption('friendly_alias_restrict_chars_pattern', $options, - '/[\0\x0B\t\n\r\f\a&=+%#<>"~`@\?\[\]\{\}\|\^\'\\\\]/'); - $lowercase = (boolean)$xpdo->getOption('friendly_alias_lowercase_only', $options, true); + $restrictcharspattern = $xpdo->getOption( + 'friendly_alias_restrict_chars_pattern', + $options, + '/[\0\x0B\t\n\r\f\a&=+%#<>"~`@\?\[\]\{\}\|\^\'\\\\]/' + ); + $lowercase = (bool)$xpdo->getOption('friendly_alias_lowercase_only', $options, true); $translit = $xpdo->getOption('friendly_alias_translit', $options, $iconv ? 'iconv' : 'none'); $translitClass = $xpdo->getOption('friendly_alias_translit_class', $options, 'translit.modTransliterate'); @@ -218,21 +221,21 @@ public static function filterPathSegment(&$xpdo, $segment, array $options = []) break; case 'iconv': /* if iconv is available, use the built-in transliteration it provides */ - $segment = iconv($mbext ? mb_detect_encoding($segment) : $charset, $charset . '//TRANSLIT//IGNORE', - $segment); - $ampersand = iconv($mbext ? mb_detect_encoding($segment) : $charset, $charset . '//TRANSLIT//IGNORE', - $ampersand); + $segment = iconv($mbext ? mb_detect_encoding($segment) : $charset, $charset . '//TRANSLIT//IGNORE', $segment); + $ampersand = iconv($mbext ? mb_detect_encoding($segment) : $charset, $charset . '//TRANSLIT//IGNORE', $ampersand); break; case 'iconv_ascii': /* if iconv is available, use the built-in transliteration to ASCII it provides */ - $segment = iconv(($mbext) ? mb_detect_encoding($segment) : $charset, 'ASCII//TRANSLIT//IGNORE', - $segment); + $segment = iconv(($mbext) ? mb_detect_encoding($segment) : $charset, 'ASCII//TRANSLIT//IGNORE', $segment); break; default: /* otherwise look for a transliteration service class that will accept named transliteration tables */ if ($xpdo instanceof modX) { - $translitClassPath = $xpdo->getOption('friendly_alias_translit_class_path', $options, - $xpdo->getOption('core_path', $options, MODX_CORE_PATH) . 'components/'); + $translitClassPath = $xpdo->getOption( + 'friendly_alias_translit_class_path', + $options, + $xpdo->getOption('core_path', $options, MODX_CORE_PATH) . 'components/' + ); if ($xpdo->getService('translit', $translitClass, $translitClassPath, $options)) { $segment = $xpdo->translit->translate($segment, $translit); $ampersand = $xpdo->translit->translate($ampersand, $translit); @@ -398,7 +401,7 @@ public static function getTemplateVarCollection(modResource &$resource) */ public static function refreshURIs(modX &$modx, $parent = 0, array $options = []) { - $resetOverrides = array_key_exists('resetOverrides', $options) ? (boolean)$options['resetOverrides'] : false; + $resetOverrides = array_key_exists('resetOverrides', $options) ? (bool)$options['resetOverrides'] : false; $contexts = array_key_exists('contexts', $options) ? explode(',', $options['contexts']) : null; $criteria = $modx->newQuery(modResource::class, ['parent' => $parent]); if (!$resetOverrides) { @@ -441,8 +444,14 @@ public static function updateContextOfChildren(modX &$modx, $parent, array $opti if ($child->save()) { $count++; } else { - $modx->log(modX::LOG_LEVEL_ERROR, "Could not change Context of child resource {$child->get('id')}", '', - __METHOD__, __FILE__, __LINE__); + $modx->log( + modX::LOG_LEVEL_ERROR, + "Could not change Context of child resource {$child->get('id')}", + '', + __METHOD__, + __FILE__, + __LINE__ + ); } } @@ -452,10 +461,10 @@ public static function updateContextOfChildren(modX &$modx, $parent, array $opti /** * @param xPDO $xpdo A reference to the xPDO|modX instance */ - function __construct(xPDO & $xpdo) + public function __construct(xPDO &$xpdo) { parent:: __construct($xpdo); - $this->_contextKey = isset ($this->xpdo->context) ? $this->xpdo->context->get('key') : 'web'; + $this->_contextKey = isset($this->xpdo->context) ? $this->xpdo->context->get('key') : 'web'; $this->_cacheKey = "[contextKey]/resources/[id]"; } @@ -485,6 +494,7 @@ public function process() $this->xpdo->getParser(); /** @var modTemplate $baseElement */ if ($baseElement = $this->getOne('Template')) { + /** @disregard P1013 */ if ($baseElement->process()) { $this->_content = $baseElement->_output; $this->_processed = true; @@ -492,8 +502,7 @@ public function process() } else { $this->_content = $this->getContent(); $maxIterations = intval($this->xpdo->getOption('parser_max_iterations', null, 10)); - $this->xpdo->parser->processElementTags('', $this->_content, false, false, '[[', ']]', [], - $maxIterations); + $this->xpdo->parser->processElementTags('', $this->_content, false, false, '[[', ']]', [], $maxIterations); $this->_processed = true; } } @@ -617,10 +626,11 @@ public function getCacheKey($context = '') */ public function & getMany($alias, $criteria = null, $cacheFlag = false) { + /** @disregard P1006 */ if ($alias === 'TemplateVars' || $alias === 'modTemplateVar' && ($criteria === null || strtolower($criteria) === 'all')) { $collection = $this->getTemplateVars(); } else { - $collection = parent:: getMany($alias, $criteria, $cacheFlag); + $collection = parent::getMany($alias, $criteria, $cacheFlag); } return $collection; @@ -647,7 +657,7 @@ public function getTemplateVars() public function set($k, $v = null, $vType = '') { switch ($k) { - case 'alias' : + case 'alias': $v = $this->cleanAlias($v); break; } @@ -739,7 +749,7 @@ public function getProcessed() */ public function setProcessed($processed) { - $this->_processed = (boolean)$processed; + $this->_processed = (bool)$processed; } /** @@ -762,8 +772,11 @@ public function addLock($user = 0, array $options = []) $lockedBy = $this->getLock(); if (empty($lockedBy) || ($lockedBy == $user)) { $this->xpdo->registry->locks->subscribe('/resource/'); - $this->xpdo->registry->locks->send('/resource/', [md5($this->get('id')) => $user], - ['ttl' => $this->xpdo->getOption('lock_ttl', $options, 360)]); + $this->xpdo->registry->locks->send( + '/resource/', + [md5($this->get('id')) => $user], + ['ttl' => $this->xpdo->getOption('lock_ttl', $options, 360)] + ); $locked = true; } elseif ($lockedBy != $user) { $locked = $lockedBy; @@ -839,9 +852,9 @@ public function findPolicy($context = '') $enabled = true; $context = !empty($context) ? $context : $this->xpdo->context->get('key'); if ($context === $this->xpdo->context->get('key')) { - $enabled = (boolean)$this->xpdo->getOption('access_resource_group_enabled', null, true); + $enabled = (bool)$this->xpdo->getOption('access_resource_group_enabled', null, true); } elseif ($this->xpdo->getContext($context)) { - $enabled = (boolean)$this->xpdo->contexts[$context]->getOption('access_resource_group_enabled', true); + $enabled = (bool)$this->xpdo->contexts[$context]->getOption('access_resource_group_enabled', true); } if ($enabled) { if (empty($this->_policies) || !isset($this->_policies[$context])) { @@ -915,7 +928,7 @@ public function getTVValue($pk) } else { $tv = $this->xpdo->getObject(modTemplateVar::class, $byName ? ['name' => $pk] : $pk); } - + /** @disregard P1013 */ return $tv == null ? null : $tv->renderOutput($this->get('id')); } @@ -981,7 +994,7 @@ public function getAliasPath($alias = '', array $fields = []) $isHtml = (strpos($contentType->get('mime_type'), 'html') !== false); } /* set extension to container suffix if Resource is a folder, HTML content type, and the container suffix is set */ - if (!empty($fields['isfolder']) && $isHtml && !empty ($containerSuffix)) { + if (!empty($fields['isfolder']) && $isHtml && !empty($containerSuffix)) { $extension = $containerSuffix; } $aliasPath = ''; @@ -991,8 +1004,12 @@ public function getAliasPath($alias = '', array $fields = []) $pathParentId = $fields['parent']; $parentResources = []; $query = $this->xpdo->newQuery(modResource::class); - $query->select($this->xpdo->getSelectColumns(modResource::class, '', '', - ['parent', 'alias', 'alias_visible', 'uri', 'uri_override'])); + $query->select($this->xpdo->getSelectColumns( + modResource::class, + '', + '', + ['parent', 'alias', 'alias_visible', 'uri', 'uri_override'] + )); $query->where("{$this->xpdo->escape('id')} = ?"); $query->prepare(); $query->stmt->execute([$pathParentId]); @@ -1008,7 +1025,7 @@ public function getAliasPath($alias = '', array $fields = []) } $parentAlias = $currResource['alias']; - if (empty ($parentAlias)) { + if (empty($parentAlias)) { $parentAlias = "{$pathParentId}"; } @@ -1022,7 +1039,7 @@ public function getAliasPath($alias = '', array $fields = []) $query->stmt->execute([$pathParentId]); $currResource = $query->stmt->fetch(PDO::FETCH_ASSOC); } - $aliasPath = !empty ($parentResources) ? implode('/', array_reverse($parentResources)) : ''; + $aliasPath = !empty($parentResources) ? implode('/', array_reverse($parentResources)) : ''; if (strlen($aliasPath) > 0 && $aliasPath[strlen($aliasPath) - 1] !== '/') { $aliasPath .= '/'; } @@ -1063,7 +1080,7 @@ public function isDuplicateAlias($aliasPath = '', $contextKey = '') $criteria->prepare(); $duplicate = $this->xpdo->getValue($criteria->stmt); - return $duplicate > 0 ? (integer)$duplicate : false; + return $duplicate > 0 ? (int)$duplicate : false; } /** @@ -1081,10 +1098,12 @@ public function duplicate(array $options = []) /* duplicate resource */ $prefixDuplicate = !empty($options['prefixDuplicate']) ? true : false; - $newName = !empty($options['newName']) ? $options['newName'] : ($prefixDuplicate ? $this->xpdo->lexicon('duplicate_of', - [ - 'name' => $this->get('pagetitle'), - ]) : $this->get('pagetitle')); + $newName = !empty($options['newName']) + ? $options['newName'] + : ($prefixDuplicate + ? $this->xpdo->lexicon('duplicate_of', ['name' => $this->get('pagetitle')]) + : $this->get('pagetitle')) + ; /** @var modResource $newResource */ $newResource = $this->xpdo->newObject($this->get('class_key')); $newResource->fromArray($this->toArray('', true), '', false, true); @@ -1131,8 +1150,10 @@ public function duplicate(array $options = []) if (!($preserve_alias)) { /* auto assign alias */ $aliasPath = $newResource->getAliasPath($newName); - $dupeContext = $this->xpdo->getOption('global_duplicate_uri_check', $options, - false) ? '' : $newResource->get('context_key'); + $dupeContext = $this->xpdo->getOption('global_duplicate_uri_check', $options, false) + ? '' + : $newResource->get('context_key') + ; if ($newResource->isDuplicateAlias($aliasPath, $dupeContext)) { $alias = ''; if ($newResource->get('uri_override')) { @@ -1207,9 +1228,7 @@ public function duplicate(array $options = []) } } } - return $newResource; - } /** @@ -1217,8 +1236,8 @@ public function duplicate(array $options = []) * * @access public * - * @param mixed $resourceGroupPk Either the ID, name or object of the Resource Group - * @param boolean $byName Force the criteria to check by name for Numeric usergroup's name + * @param mixed $resourceGroupPk Either the ID, name or object of the Resource Group + * @param boolean $byName Force the criteria to check by name for Numeric usergroup's name * * @return boolean True if successful. */ @@ -1238,7 +1257,6 @@ public function joinGroup($resourceGroupPk, $byName = false) $resourceGroup = $this->xpdo->getObject(modResourceGroup::class, $c); if (empty($resourceGroup) || !is_object($resourceGroup) || !($resourceGroup instanceof modResourceGroup)) { $this->xpdo->log(modX::LOG_LEVEL_ERROR, __METHOD__ . ' - No resource group: ' . $resourceGroupPk); - return false; } } else { @@ -1246,9 +1264,10 @@ public function joinGroup($resourceGroupPk, $byName = false) } if ($this->isMember($resourceGroup->get('name'))) { - $this->xpdo->log(modX::LOG_LEVEL_ERROR, - __METHOD__ . ' - Resource ' . $this->get('id') . ' already in resource group: ' . $resourceGroupPk); - + $this->xpdo->log( + modX::LOG_LEVEL_ERROR, + __METHOD__ . ' - Resource ' . $this->get('id') . ' already in resource group: ' . $resourceGroupPk + ); return false; } /** @var modResourceGroupResource $resourceGroupResource */ @@ -1277,9 +1296,13 @@ public function leaveGroup($resourceGroupPk) /** @var modResourceGroup $resourceGroup */ $resourceGroup = $this->xpdo->getObject(modResourceGroup::class, $c); if (empty($resourceGroup) || !is_object($resourceGroup) || !($resourceGroup instanceof modResourceGroup)) { - $this->xpdo->log(modX::LOG_LEVEL_ERROR, - __METHOD__ . ' - No resource group: ' . (is_object($resourceGroupPk) ? $resourceGroupPk->get('name') : $resourceGroupPk)); - + $this->xpdo->log( + modX::LOG_LEVEL_ERROR, + __METHOD__ . ' - No resource group: ' . (is_object($resourceGroupPk) + ? $resourceGroupPk->get('name') + : $resourceGroupPk + ) + ); return false; } } else { @@ -1287,9 +1310,13 @@ public function leaveGroup($resourceGroupPk) } if (!$this->isMember($resourceGroup->get('name'))) { - $this->xpdo->log(modX::LOG_LEVEL_ERROR, - __METHOD__ . ' - Resource ' . $this->get('id') . ' is not in resource group: ' . (is_object($resourceGroupPk) ? $resourceGroupPk->get('name') : $resourceGroupPk)); - + $this->xpdo->log( + modX::LOG_LEVEL_ERROR, + __METHOD__ . ' - Resource ' . $this->get('id') . ' is not in resource group: ' . (is_object($resourceGroupPk) + ? $resourceGroupPk->get('name') + : $resourceGroupPk + ) + ); return false; } /** @var modResourceGroupResource $resourceGroupResource */ @@ -1325,8 +1352,11 @@ public function getResourceGroupNames() { $resourceGroupNames = []; - $resourceGroups = $this->xpdo->getCollectionGraph(modResourceGroup::class, '{"ResourceGroupResources":{}}', - ['ResourceGroupResources.document' => $this->get('id')]); + $resourceGroups = $this->xpdo->getCollectionGraph( + modResourceGroup::class, + '{"ResourceGroupResources":{}}', + ['ResourceGroupResources.document' => $this->get('id')] + ); if ($resourceGroups) { /** @var modResourceGroup $resourceGroup */ @@ -1447,8 +1477,10 @@ public function getProperty($key, $namespace = 'core', $default = null) $properties = $this->get('properties'); $properties = !empty($properties) ? $properties : []; - return array_key_exists($namespace, $properties) && array_key_exists($key, - $properties[$namespace]) ? $properties[$namespace][$key] : $default; + return array_key_exists($namespace, $properties) && array_key_exists($key, $properties[$namespace]) + ? $properties[$namespace][$key] + : $default + ; } /** @@ -1521,16 +1553,31 @@ public function clearCache($context = '') $cache = $this->xpdo->cacheManager->getCacheProvider( $this->xpdo->getOption('cache_resource_key', null, 'resource'), [ - xPDO::OPT_CACHE_HANDLER => $this->xpdo->getOption('cache_resource_handler', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_HANDLER, null, 'xPDO\Cache\xPDOFileCache')), - xPDO::OPT_CACHE_EXPIRES => (integer)$this->xpdo->getOption('cache_resource_expires', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_EXPIRES, null, 0)), - xPDO::OPT_CACHE_FORMAT => (integer)$this->xpdo->getOption('cache_resource_format', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_FORMAT, null, xPDOCacheManager::CACHE_PHP)), - xPDO::OPT_CACHE_ATTEMPTS => (integer)$this->xpdo->getOption('cache_resource_attempts', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_ATTEMPTS, null, 10)), - xPDO::OPT_CACHE_ATTEMPT_DELAY => (integer)$this->xpdo->getOption('cache_resource_attempt_delay', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, null, 1000)), + xPDO::OPT_CACHE_HANDLER => $this->xpdo->getOption( + 'cache_resource_handler', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_HANDLER, null, 'xPDO\Cache\xPDOFileCache') + ), + xPDO::OPT_CACHE_EXPIRES => (int)$this->xpdo->getOption( + 'cache_resource_expires', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_EXPIRES, null, 0) + ), + xPDO::OPT_CACHE_FORMAT => (int)$this->xpdo->getOption( + 'cache_resource_format', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_FORMAT, null, xPDOCacheManager::CACHE_PHP) + ), + xPDO::OPT_CACHE_ATTEMPTS => (int)$this->xpdo->getOption( + 'cache_resource_attempts', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_ATTEMPTS, null, 10) + ), + xPDO::OPT_CACHE_ATTEMPT_DELAY => (int)$this->xpdo->getOption( + 'cache_resource_attempt_delay', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, null, 1000) + ), ] ); $key = $this->getCacheKey($context);