From 5e9044526977b559d74067ca2818230e9518ef33 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Thu, 26 Feb 2026 10:47:52 +0600 Subject: [PATCH] feat(setup): allow alternative context key instead of 'web' - Add context_web_key option to CLI and web installer - Validate key: alphanumeric + underscore, forbid mgr/root - Write config.context.php for index.php context resolution - Rename web context in DB when custom key is used Resolves #14462 --- setup/cli-install.php | 13 ++++++ setup/controllers/contexts.php | 28 ++++++++++++ setup/includes/new.install.php | 44 ++++++++++++++++--- .../request/modinstallclirequest.class.php | 1 + .../request/modinstallrequest.class.php | 1 + .../runner/modinstallrunner.class.php | 30 ++++++++++++- .../runner/modinstallrunnerweb.class.php | 1 + setup/lang/en/default.inc.php | 4 ++ setup/templates/contexts.tpl | 15 +++++++ 9 files changed, 129 insertions(+), 8 deletions(-) diff --git a/setup/cli-install.php b/setup/cli-install.php index 511e7d4540e..cb79f999643 100644 --- a/setup/cli-install.php +++ b/setup/cli-install.php @@ -1,4 +1,5 @@ dirname($path) . '/', 'context_web_url' => '/', + 'context_web_key' => [ + 'prompt' => 'context_web_key', + 'default' => 'web', + ], 'core_path' => dirname($path) . '/core/', 'context_mgr_path' => [ 'prompt' => 'context_manager_path', @@ -180,6 +185,14 @@ } unset($rmdir); +$contextWebKey = isset($data['context_web_key']) ? trim($data['context_web_key']) : ''; +$isInvalidKey = $contextWebKey === '' + || !preg_match('/^[a-zA-Z0-9_]+$/', $contextWebKey) + || in_array(strtolower($contextWebKey), ['mgr', 'root'], true); +if ($isInvalidKey) { + $data['context_web_key'] = 'web'; +} + // Generate config file $xml = new SimpleXMLElement(''); foreach ($data as $key => $value) { diff --git a/setup/controllers/contexts.php b/setup/controllers/contexts.php index 221fc94a9cf..b132c5afc25 100644 --- a/setup/controllers/contexts.php +++ b/setup/controllers/contexts.php @@ -24,6 +24,32 @@ unset($_POST['proceed']); $webUrl= substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], 'setup/')); + $contextWebKey = isset($_POST['context_web_key']) ? trim($_POST['context_web_key']) : 'web'; + if ($contextWebKey === '') { + $contextWebKey = 'web'; + } + $setFormWithError = function ($errorMsg) use ($parser, $contextWebKey, $webUrl) { + $parser->set('context_web_key_error', $errorMsg); + $parser->set('context_web_key', $contextWebKey); + $parser->set('context_web_path', $_POST['context_web_path'] ?? MODX_INSTALL_PATH); + $parser->set('context_web_url', $_POST['context_web_url'] ?? $webUrl); + $connPath = $_POST['context_connectors_path'] ?? MODX_INSTALL_PATH . 'connectors/'; + $parser->set('context_connectors_path', $connPath); + $connUrl = $_POST['context_connectors_url'] ?? $webUrl . 'connectors/'; + $parser->set('context_connectors_url', $connUrl); + $mgrPath = $_POST['context_mgr_path'] ?? MODX_INSTALL_PATH . 'manager/'; + $parser->set('context_mgr_path', $mgrPath); + $parser->set('context_mgr_url', $_POST['context_mgr_url'] ?? $webUrl . 'manager/'); + return $parser->render('contexts.tpl'); + }; + if (!preg_match('/^[a-zA-Z0-9_]+$/', $contextWebKey)) { + return $setFormWithError($install->lexicon('context_web_key_err_invalid')); + } + if (in_array(strtolower($contextWebKey), ['mgr', 'root'], true)) { + return $setFormWithError($install->lexicon('context_web_key_err_reserved')); + } + $_POST['context_web_key'] = $contextWebKey; + $_POST['context_web_path'] = !empty($_POST['context_web_path']) ? rtrim($_POST['context_web_path'],'/').'/' : MODX_INSTALL_PATH; $_POST['context_web_url'] = !empty($_POST['context_web_url']) ? rtrim($_POST['context_web_url'],'/').'/' : $webUrl; $_POST['context_mgr_path'] = !empty($_POST['context_mgr_path']) ? rtrim($_POST['context_mgr_path'],'/').'/' : MODX_INSTALL_PATH . 'manager/'; @@ -59,8 +85,10 @@ $parser->set('context_connectors_url', defined('MODX_CONNECTORS_URL') ? MODX_CONNECTORS_URL : $webUrl . 'connectors/'); $parser->set('context_mgr_path', defined('MODX_MANAGER_PATH') ? MODX_MANAGER_PATH : MODX_INSTALL_PATH . 'manager/'); $parser->set('context_mgr_url', defined('MODX_MANAGER_URL') ? MODX_MANAGER_URL : $webUrl . 'manager/'); + $parser->set('context_web_key', $install->settings->get('context_web_key', 'web')); } else { $parser->set('context_web_path', MODX_INSTALL_PATH); + $parser->set('context_web_key', $install->settings->get('context_web_key', 'web')); $parser->set('context_web_url', $webUrl); $parser->set('context_connectors_path', MODX_INSTALL_PATH . 'connectors/'); $parser->set('context_connectors_url', $webUrl . 'connectors/'); diff --git a/setup/includes/new.install.php b/setup/includes/new.install.php index f7ead1ba057..b18d6a3b155 100644 --- a/setup/includes/new.install.php +++ b/setup/includes/new.install.php @@ -13,6 +13,8 @@ use MODX\Revolution\modAccessContext; use MODX\Revolution\modAccessPolicy; +use MODX\Revolution\modContext; +use MODX\Revolution\modContextSetting; use MODX\Revolution\modDocument; use MODX\Revolution\modResource; use MODX\Revolution\modSystemSetting; @@ -22,6 +24,34 @@ use MODX\Revolution\modUserGroupMember; use MODX\Revolution\modUserProfile; +$contextWebKey = $install->settings->get('context_web_key', 'web'); +if (!preg_match('/^[a-zA-Z0-9_]+$/', $contextWebKey) || in_array(strtolower($contextWebKey), ['mgr', 'root'], true)) { + $contextWebKey = 'web'; +} + +$defaultContextSetting = $modx->getObject(modSystemSetting::class, ['key' => 'default_context']); +if ($defaultContextSetting) { + $defaultContextSetting->set('value', $contextWebKey); + $defaultContextSetting->save(); +} + +if ($contextWebKey !== 'web') { + $context = $modx->getObject(modContext::class, ['key' => 'web']); + if ($context) { + $context->set('key', $contextWebKey); + $context->save(); + $quotedKey = $modx->quote($contextWebKey); + $ctxSettingTbl = $modx->getTableName(modContextSetting::class); + $modx->exec("UPDATE {$ctxSettingTbl} SET context_key = {$quotedKey} WHERE context_key = 'web'"); + $ctxResourceTbl = $modx->getTableName(\MODX\Revolution\modContextResource::class); + $modx->exec("UPDATE {$ctxResourceTbl} SET context_key = {$quotedKey} WHERE context_key = 'web'"); + $accessCtxTbl = $modx->getTableName(modAccessContext::class); + $modx->exec("UPDATE {$accessCtxTbl} SET target = {$quotedKey} WHERE target = 'web'"); + $resourceTbl = $modx->getTableName(modResource::class); + $modx->exec("UPDATE {$resourceTbl} SET context_key = {$quotedKey} WHERE context_key = 'web'"); + } +} + /* add settings_version */ $currentVersion = include MODX_CORE_PATH . 'docs/version.inc.php'; @@ -109,11 +139,11 @@ /** @var modAccessContext $access */ $access= $modx->newObject(modAccessContext::class); $access->fromArray([ - 'target' => 'web', - 'principal_class' => modUserGroup::class, - 'principal' => 0, - 'authority' => 9999, - 'policy' => $loadOnly->get('id'), + 'target' => $contextWebKey, + 'principal_class' => modUserGroup::class, + 'principal' => 0, + 'authority' => 9999, + 'policy' => $loadOnly->get('id'), ]); $access->save(); unset($access); @@ -146,7 +176,7 @@ $access= $modx->newObject(modAccessContext::class); $access->fromArray([ - 'target' => 'web', + 'target' => $contextWebKey, 'principal_class' => modUserGroup::class, 'principal' => $adminGroup->get('id'), 'authority' => 0, @@ -199,7 +229,7 @@ 'createdby' => 1, 'hidemenu' => false, 'class_key' => modDocument::class, - 'context_key' => 'web', + 'context_key' => $contextWebKey, 'content_type' => 1, ]); diff --git a/setup/includes/request/modinstallclirequest.class.php b/setup/includes/request/modinstallclirequest.class.php index 9faeac178b8..a0b800b35cb 100644 --- a/setup/includes/request/modinstallclirequest.class.php +++ b/setup/includes/request/modinstallclirequest.class.php @@ -225,6 +225,7 @@ public function prepareSettings(array &$settings) { $this->setDefaultSetting('mgr_url',$this->install->settings->get('context_mgr_url')); $this->setDefaultSetting('web_path',$this->install->settings->get('context_web_path')); $this->setDefaultSetting('web_url',$this->install->settings->get('context_web_url')); + $this->setDefaultSetting('context_web_key', $this->install->settings->get('context_web_key', 'web')); $this->setDefaultSetting('assets_path',$this->install->settings->get('context_assets_path',$this->install->settings->get('context_web_path').'assets/')); $this->setDefaultSetting('assets_url',$this->install->settings->get('context_assets_url',$this->install->settings->get('context_web_url').'assets/')); } diff --git a/setup/includes/request/modinstallrequest.class.php b/setup/includes/request/modinstallrequest.class.php index efa0d21f6c8..bb80c101fd1 100644 --- a/setup/includes/request/modinstallrequest.class.php +++ b/setup/includes/request/modinstallrequest.class.php @@ -130,6 +130,7 @@ public function setDefaultPaths(array $config = []) { $defaults = []; $defaults['context_web_path'] = rtrim(MODX_INSTALL_PATH,'/').'/'; $defaults['context_web_url'] = $webUrl; + $defaults['context_web_key'] = 'web'; $defaults['context_mgr_path'] = rtrim(MODX_INSTALL_PATH,'/') . '/manager/'; $defaults['context_mgr_url'] = $webUrl . 'manager/'; $defaults['context_connectors_path'] = rtrim(MODX_INSTALL_PATH,'/') . '/connectors/'; diff --git a/setup/includes/runner/modinstallrunner.class.php b/setup/includes/runner/modinstallrunner.class.php index f24822bde45..7a45cadbc7c 100644 --- a/setup/includes/runner/modinstallrunner.class.php +++ b/setup/includes/runner/modinstallrunner.class.php @@ -204,7 +204,10 @@ public function writeConfig() { } $perms = $this->install->settings->get('new_file_permissions', sprintf("%04o", 0666 & (0777 - umask()))); if (is_string($perms)) $perms = octdec($perms); - $chmodSuccess = @ chmod($configFile, $perms); + $chmodSuccess = false; + if ($written) { + $chmodSuccess = @ chmod($configFile, $perms); + } if ($written) { $this->addResult(modInstallRunner::RESULT_SUCCESS,'

'.$this->install->lexicon('config_file_written').'

'); } else { @@ -218,6 +221,31 @@ public function writeConfig() { return $written; } + /** + * Writes config.context.php with the web context key. + * + * @return bool + */ + public function writeContextConfig() + { + $contextWebKey = $this->install->settings->get('context_web_key', 'web'); + $webPath = $this->install->settings->get('context_web_path'); + $contextConfigFile = rtrim($webPath, '/') . '/config.context.php'; + $content = 'install->settings->get('new_file_permissions', sprintf("%04o", 0666 & (0777 - umask()))); + if (is_string($perms)) { + $perms = octdec($perms); + } + @ chmod($contextConfigFile, $perms); + } + return (bool) $written; + } abstract public function execute($mode); abstract public function initialize(); diff --git a/setup/includes/runner/modinstallrunnerweb.class.php b/setup/includes/runner/modinstallrunnerweb.class.php index a5ae3a9b133..af24cb3e9a3 100644 --- a/setup/includes/runner/modinstallrunnerweb.class.php +++ b/setup/includes/runner/modinstallrunnerweb.class.php @@ -33,6 +33,7 @@ public function initialize() { public function execute($mode) { /* write config file */ $this->writeConfig(); + $this->writeContextConfig(); /* get connection */ $this->install->getConnection($mode); diff --git a/setup/lang/en/default.inc.php b/setup/lang/en/default.inc.php index 7f9b6c6cf94..a4d3091b2c1 100644 --- a/setup/lang/en/default.inc.php +++ b/setup/lang/en/default.inc.php @@ -58,6 +58,10 @@ $_lang['context_manager_url'] = 'URL for mgr context'; $_lang['context_override'] = 'Leave these disabled to allow the system to auto-determine this information as shown. By enabling a specific value, regardless if you manually set the value, you are indicating that you want the path to be set explicitly to that value in the configuration.'; $_lang['context_web_options'] = 'Web Context Options (front-end web site)'; +$_lang['context_web_key'] = 'Web context key'; +$_lang['context_web_key_desc'] = 'Key for the front-end context (letters, numbers, underscores only). Cannot be "mgr" or "root". Default: web'; +$_lang['context_web_key_err_invalid'] = 'Invalid context key. Use only letters, numbers, and underscores.'; +$_lang['context_web_key_err_reserved'] = 'The keys "mgr" and "root" are reserved.'; $_lang['context_web_path'] = 'Filesystem path for web context'; $_lang['context_web_url'] = 'URL for web context'; $_lang['continue'] = 'Continue'; diff --git a/setup/templates/contexts.tpl b/setup/templates/contexts.tpl index 8eaa81d5891..2918f98c27e 100644 --- a/setup/templates/contexts.tpl +++ b/setup/templates/contexts.tpl @@ -16,6 +16,21 @@ Ext.onReady(function() {literal}{{/literal}

{$_lang.context_web_options}

+
+
+
+ +
+
+ +

{$_lang.context_web_key_desc}

+ {if $context_web_key_error} +

{$context_web_key_error}

+ {/if} +
+
+
+