From b9bbc7f19de43814794eb9f0b8500d2c2245494f Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Wed, 25 Feb 2026 09:24:43 +0600 Subject: [PATCH 1/2] feat(resource): add childTemplate property for default child template (#13753) - Add resolveDefaultTemplate() and getChildTemplateIdFromParent() in Create processor - Add getChildTemplateIdFromParentTemplate() in resource create controller - When creating a child resource, use parent template's childTemplate property if set Resolves #13753 --- .../Revolution/Processors/Resource/Create.php | 65 ++++++++++- .../default/resource/create.class.php | 104 +++++++++++++----- 2 files changed, 143 insertions(+), 26 deletions(-) diff --git a/core/src/Revolution/Processors/Resource/Create.php b/core/src/Revolution/Processors/Resource/Create.php index 1737358248d..d60c707b780 100644 --- a/core/src/Revolution/Processors/Resource/Create.php +++ b/core/src/Revolution/Processors/Resource/Create.php @@ -247,7 +247,9 @@ public function cleanup() public function setFieldDefaults() { $scriptProperties = $this->getProperties(); - $scriptProperties['template'] = !isset($scriptProperties['template']) ? (int)$this->workingContext->getOption('default_template', 0) : (int)$scriptProperties['template']; + $scriptProperties['template'] = !isset($scriptProperties['template']) + ? $this->resolveDefaultTemplate() + : (int)$scriptProperties['template']; $scriptProperties['hidemenu'] = !isset($scriptProperties['hidemenu']) ? (int)$this->workingContext->getOption('hidemenu_default', 0) : (empty($scriptProperties['hidemenu']) ? 0 : 1); $scriptProperties['isfolder'] = empty($scriptProperties['isfolder']) ? 0 : 1; $scriptProperties['richtext'] = !isset($scriptProperties['richtext']) ? (int)$this->workingContext->getOption('richtext_default', 1) : (empty($scriptProperties['richtext']) ? 0 : 1); @@ -315,6 +317,67 @@ public function setFieldDefaults() return true; } + /** + * Resolve default template for the new resource. + * Uses parent template's childTemplate property when present and valid; otherwise context default. + * + * @return int Template ID + */ + protected function resolveDefaultTemplate(): int + { + $childTemplateId = $this->getChildTemplateIdFromParent(); + if ($childTemplateId > 0) { + return $childTemplateId; + } + + return (int)$this->workingContext->getOption('default_template', 0); + } + + /** + * Get template ID from parent resource's template property "childTemplate" when set and valid. + * + * @return int|null Template ID or null when not applicable + */ + protected function getChildTemplateIdFromParent(): ?int + { + if ($this->parentResource === null) { + return null; + } + + $parentTemplateId = (int)$this->parentResource->get('template'); + if ($parentTemplateId <= 0) { + return null; + } + + $parentTemplate = $this->modx->getObject(modTemplate::class, $parentTemplateId); + if ($parentTemplate === null) { + return null; + } + + $properties = $parentTemplate->get('properties'); + if (empty($properties) || !is_array($properties)) { + return null; + } + + $childTemplateProp = $properties['childTemplate'] ?? null; + $value = is_array($childTemplateProp) ? ($childTemplateProp['value'] ?? null) : $childTemplateProp; + if ($value === null || $value === '') { + return null; + } + + $templateId = (int)$value; + if ($templateId <= 0) { + return null; + } + + $templateExists = $this->modx->getObject( + modTemplate::class, + $templateId + ) !== null; + + return $templateExists ? $templateId : null; + } + /** * Handle if parent is a context */ diff --git a/manager/controllers/default/resource/create.class.php b/manager/controllers/default/resource/create.class.php index d48b03c8ece..74699ea271e 100644 --- a/manager/controllers/default/resource/create.class.php +++ b/manager/controllers/default/resource/create.class.php @@ -13,6 +13,7 @@ use MODX\Revolution\modFormCustomizationProfileUserGroup; use MODX\Revolution\modFormCustomizationSet; use MODX\Revolution\modResource; +use MODX\Revolution\modTemplate; use xPDO\Om\xPDOQuery; require_once __DIR__ . '/resource.class.php'; @@ -162,8 +163,11 @@ public function process(array $scriptProperties = []) $this->resourceArray = array_merge($this->resourceArray, $reloadData); $this->resourceArray['resourceGroups'] = []; $this->resourceArray['parents'] = $this->getParents(); - $this->resourceArray['resource_groups'] = $this->modx->getOption('resource_groups', - $this->resourceArray, []); + $this->resourceArray['resource_groups'] = $this->modx->getOption( + 'resource_groups', + $this->resourceArray, + [] + ); $this->resourceArray['resource_groups'] = is_array($this->resourceArray['resource_groups']) ? $this->resourceArray['resource_groups'] : json_decode($this->resourceArray['resource_groups'], true); @@ -224,31 +228,38 @@ public function getDefaultTemplate() if (isset($this->scriptProperties['template'])) { $defaultTemplate = $this->scriptProperties['template']; } else { - switch ($this->context->getOption('automatic_template_assignment', 'parent', $this->modx->_userConfig)) { - case 'parent': - if (!empty($this->parent->id)) - $defaultTemplate = $this->parent->get('template'); - break; - case 'sibling': - if (!empty($this->parent->id)) { - $c = $this->modx->newQuery(modResource::class); - $c->where(['parent' => $this->parent->id, 'context_key' => $this->ctx]); - $c->sortby('id', 'DESC'); - $c->limit(1); - if ($siblings = $this->modx->getCollection(modResource::class, $c)) { - /** @var modResource $sibling */ - foreach ($siblings as $sibling) { - $defaultTemplate = $sibling->get('template'); + $childTemplateId = $this->getChildTemplateIdFromParentTemplate(); + if ($childTemplateId > 0) { + $defaultTemplate = $childTemplateId; + } else { + switch ($this->context->getOption('automatic_template_assignment', 'parent', $this->modx->_userConfig)) { + case 'parent': + if (!empty($this->parent->id)) { + $defaultTemplate = $this->parent->get('template'); + } + break; + case 'sibling': + if (!empty($this->parent->id)) { + $c = $this->modx->newQuery(modResource::class); + $c->where(['parent' => $this->parent->id, 'context_key' => $this->ctx]); + $c->sortby('id', 'DESC'); + $c->limit(1); + if ($siblings = $this->modx->getCollection(modResource::class, $c)) { + /** @var modResource $sibling */ + foreach ($siblings as $sibling) { + $defaultTemplate = $sibling->get('template'); + } + } else { + if (!empty($this->parent->id)) { + $defaultTemplate = $this->parent->get('template'); + } } - } else { - if (!empty($this->parent->id)) - $defaultTemplate = $this->parent->get('template'); } - } - break; - case 'system': - // already established - break; + break; + case 'system': + // already established + break; + } } } $userGroups = $this->modx->user->getUserGroups(); @@ -299,6 +310,49 @@ public function getDefaultTemplate() return $defaultTemplate; } + /** + * Get template ID from parent resource's template property "childTemplate" when set and valid. + * + * @return int Template ID or 0 when not applicable + */ + protected function getChildTemplateIdFromParentTemplate(): int + { + if ($this->parent === null || empty($this->parent->get('id'))) { + return 0; + } + + $parentTemplateId = (int)$this->parent->get('template'); + if ($parentTemplateId <= 0) { + return 0; + } + + $parentTemplate = $this->modx->getObject(modTemplate::class, $parentTemplateId); + if ($parentTemplate === null) { + return 0; + } + + $properties = $parentTemplate->get('properties'); + if (empty($properties) || !is_array($properties)) { + return 0; + } + + $childTemplateProp = $properties['childTemplate'] ?? null; + $value = is_array($childTemplateProp) + ? ($childTemplateProp['value'] ?? null) + : $childTemplateProp; + if ($value === null || $value === '') { + return 0; + } + + $templateId = (int)$value; + if ($templateId <= 0) { + return 0; + } + + $templateExists = $this->modx->getObject(modTemplate::class, $templateId) !== null; + + return $templateExists ? $templateId : 0; + } /** * Return the pagetitle From 82b6f2f989d52c31b35da7741c9782948a1ffa21 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Thu, 26 Feb 2026 07:26:28 +0600 Subject: [PATCH 2/2] refactor(template): centralize child template ID retrieval logic - Introduced `getChildTemplateId()` method in `modTemplate` to encapsulate logic for retrieving the child template ID from a parent resource. - Updated `Create` processor and resource create controller to utilize the new method, simplifying the code by removing redundant methods. - Ensured that the child template ID is correctly resolved when creating child resources. This change enhances code maintainability and readability by consolidating the child template retrieval logic into a single method. --- .../Revolution/Processors/Resource/Create.php | 49 ++----------------- core/src/Revolution/modTemplate.php | 47 ++++++++++++++++++ .../default/resource/create.class.php | 47 ++---------------- 3 files changed, 53 insertions(+), 90 deletions(-) diff --git a/core/src/Revolution/Processors/Resource/Create.php b/core/src/Revolution/Processors/Resource/Create.php index d60c707b780..c0648a8a843 100644 --- a/core/src/Revolution/Processors/Resource/Create.php +++ b/core/src/Revolution/Processors/Resource/Create.php @@ -325,7 +325,9 @@ public function setFieldDefaults() */ protected function resolveDefaultTemplate(): int { - $childTemplateId = $this->getChildTemplateIdFromParent(); + $childTemplateId = $this->parentResource !== null + ? modTemplate::getChildTemplateId($this->parentResource) + : 0; if ($childTemplateId > 0) { return $childTemplateId; } @@ -333,51 +335,6 @@ protected function resolveDefaultTemplate(): int return (int)$this->workingContext->getOption('default_template', 0); } - /** - * Get template ID from parent resource's template property "childTemplate" when set and valid. - * - * @return int|null Template ID or null when not applicable - */ - protected function getChildTemplateIdFromParent(): ?int - { - if ($this->parentResource === null) { - return null; - } - - $parentTemplateId = (int)$this->parentResource->get('template'); - if ($parentTemplateId <= 0) { - return null; - } - - $parentTemplate = $this->modx->getObject(modTemplate::class, $parentTemplateId); - if ($parentTemplate === null) { - return null; - } - - $properties = $parentTemplate->get('properties'); - if (empty($properties) || !is_array($properties)) { - return null; - } - - $childTemplateProp = $properties['childTemplate'] ?? null; - $value = is_array($childTemplateProp) ? ($childTemplateProp['value'] ?? null) : $childTemplateProp; - if ($value === null || $value === '') { - return null; - } - - $templateId = (int)$value; - if ($templateId <= 0) { - return null; - } - - $templateExists = $this->modx->getObject( - modTemplate::class, - $templateId - ) !== null; - - return $templateExists ? $templateId : null; - } - /** * Handle if parent is a context */ diff --git a/core/src/Revolution/modTemplate.php b/core/src/Revolution/modTemplate.php index 3ae17a0d4af..b7459a7b4d3 100644 --- a/core/src/Revolution/modTemplate.php +++ b/core/src/Revolution/modTemplate.php @@ -224,6 +224,53 @@ public function getTemplateVarList(array $sort = ['name' => 'ASC'], $limit = 0, return $this->xpdo->call(modTemplate::class, 'listTemplateVars', [&$this, $sort, $limit, $offset, $conditions]); } + /** + * Get the template ID from the childTemplate property for a parent resource. + * Returns the template ID that should be used for child resources, or 0 if not set or invalid. + * + * @param modResource $parent The parent resource. + * @return int Template ID or 0 when not applicable. + */ + public static function getChildTemplateId(modResource $parent): int + { + $xpdo = $parent->xpdo; + if (!$xpdo instanceof modX) { + return 0; + } + + $parentTemplateId = (int)$parent->get('template'); + if ($parentTemplateId <= 0) { + return 0; + } + + $parentTemplate = $xpdo->getObject(modTemplate::class, $parentTemplateId); + if ($parentTemplate === null) { + return 0; + } + + $properties = $parentTemplate->get('properties'); + if (empty($properties) || !is_array($properties)) { + return 0; + } + + $childTemplateProp = $properties['childTemplate'] ?? null; + $value = is_array($childTemplateProp) ? ($childTemplateProp['value'] ?? null) : $childTemplateProp; + if ($value === null || $value === '') { + return 0; + } + + $templateId = (int)$value; + if ($templateId <= 0) { + return 0; + } + + if ($xpdo->getCount(modTemplate::class, ['id' => $templateId]) === 0) { + return 0; + } + + return $templateId; + } + /** * Check to see if this Template is assigned the specified Template Var * diff --git a/manager/controllers/default/resource/create.class.php b/manager/controllers/default/resource/create.class.php index 74699ea271e..06ada981221 100644 --- a/manager/controllers/default/resource/create.class.php +++ b/manager/controllers/default/resource/create.class.php @@ -228,7 +228,9 @@ public function getDefaultTemplate() if (isset($this->scriptProperties['template'])) { $defaultTemplate = $this->scriptProperties['template']; } else { - $childTemplateId = $this->getChildTemplateIdFromParentTemplate(); + $childTemplateId = $this->parent !== null + ? modTemplate::getChildTemplateId($this->parent) + : 0; if ($childTemplateId > 0) { $defaultTemplate = $childTemplateId; } else { @@ -310,49 +312,6 @@ public function getDefaultTemplate() return $defaultTemplate; } - /** - * Get template ID from parent resource's template property "childTemplate" when set and valid. - * - * @return int Template ID or 0 when not applicable - */ - protected function getChildTemplateIdFromParentTemplate(): int - { - if ($this->parent === null || empty($this->parent->get('id'))) { - return 0; - } - - $parentTemplateId = (int)$this->parent->get('template'); - if ($parentTemplateId <= 0) { - return 0; - } - - $parentTemplate = $this->modx->getObject(modTemplate::class, $parentTemplateId); - if ($parentTemplate === null) { - return 0; - } - - $properties = $parentTemplate->get('properties'); - if (empty($properties) || !is_array($properties)) { - return 0; - } - - $childTemplateProp = $properties['childTemplate'] ?? null; - $value = is_array($childTemplateProp) - ? ($childTemplateProp['value'] ?? null) - : $childTemplateProp; - if ($value === null || $value === '') { - return 0; - } - - $templateId = (int)$value; - if ($templateId <= 0) { - return 0; - } - - $templateExists = $this->modx->getObject(modTemplate::class, $templateId) !== null; - - return $templateExists ? $templateId : 0; - } /** * Return the pagetitle