Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 125 additions & 23 deletions en/building-sites/contexts.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,158 @@
---
title: "Contexts"
_old_id: "66"
_old_uri: "2.x/administering-your-site/contexts"
description: "Contexts in MODX: how they override system settings, how to create one, and how access to them is granted."
---

Contexts allow MODX configuration settings to be overridden, extended, isolated, or shared across domains, sub-domains, sub-sites, multi-sites, cultural-specific sections, specific web applications, and so on.
A Context is a named set of configuration overrides that a request runs under. It has its own settings, its own branch of the resource tree and its own access rules.

You can easily create a context from the Contexts menu under Tools. The context will then show up in your resource tree. Resources can easily be dragged between contexts to move them from one context to another.

Note: there's nothing fundamentally _different_ about resources in different contexts, except that they now inherit the configuration settings of the context they are in. So, if you create a new context, you'll have to override the context settings in the context for any real, distinguishable change to appear.
MODX ships with two contexts: `web` (Website) for the front end and `mgr` (Manager) for the manager. The **key** is the value passed to `initialize()` and `switchContext()`. The **name** is a label in the interface.

## Creating a Context

First, go to the Contexts page, via System -> Contexts. Then, click on "Create New" in the grid. This will prompt you for a key and description. From there, right-click on your newly-created context, and click "Update Context".
Open **Admin** (the gear in the top menu) → **Contexts**. **Create** opens a form:

| Field | Required | Meaning |
| --- | --- | --- |
| **Context Key** | yes | the identifier of the context |
| **Name** | yes | the label shown in the manager and in the resource tree |
| **Description** | no | free text |
| **Rank** | no | the order of the context in the tree |

The keys `mgr`, `web` and `root` are reserved. A key starts with a letter. After that it may contain letters, digits, a hyphen, a dot and a slash. An underscore is rejected, so `domain_2` is not saved.

A new context gets access immediately:

- the **Administrator** user group receives the *Context* policy
- the anonymous user receives the *Load Only* policy: the right `load` and nothing else

This will bring you to a screen displaying the Context, and an empty grid of settings. From here you can add Context-specific settings that will override any System Settings. Your new context will be completely empty, requiring you to include any and all settings you will be using.
The anonymous entry shows as **(anonymous)** on the context's **Access Permissions** tab. Remove that entry to take the access away.

Note that new contexts don't automatically get "load" permission for Anonymous users in 2.2+ - you'll need to add that in manually.
Each context is its own branch in the resource tree. Drag a resource to another context.

When you create a new context besides the default "web" context, you may need to apply context settings to the web context as well.
**Duplicate** copies the settings, the access rules and the media sources. **Duplicate resources** is checked in that window and copies the resources. Clear the box to leave the resources where they are.

## Context Settings

You can create settings for any defined context by clicking on **System -> Contexts**, then right-click any context and select "Update Context".
A context setting overrides the [System setting](building-sites/settings) of the same name. A user group setting overrides the context setting. A user setting overrides the user group setting.

Each context can have its own settings that override or extend the [System Settings](building-sites/settings "System Settings"). Contextual settings can in turn be overridden or extended by [User Settings](administering-your-site/security/users#Users-UsersUserSettings). The hierarchy to remember is this:
```
System setting → Context setting → User Group setting → User setting
```

System Setting -> Context Setting -> User Setting
Edit a setting on the **Context Settings** tab: **Contexts** → right-click a context → **Edit**. Changing a setting clears that context's cache. You do not need to clear the cache by hand.

## Context Access
Settings a context usually overrides:

When creating a new context, make sure your desired user-groups have access to it:
| Setting | What it changes |
| --- | --- |
| `base_url` | the path in the context's URLs |
| `http_host` | the host the URLs are built for |
| `site_url` | the context's full URL: scheme, host and path |
| `site_start` | the resource that opens the context |
| `cultureKey`, `locale` | the language and the locale of the request |

`Security/Permissions -> Edit desired User Group -> Permissions -> Context Access`
These settings control the context's session cookie. They keep the core values unless the context overrides them:

## Retrieving Context Settings
| Setting | Value by default |
| --- | --- |
| `session_name` | empty: PHP keeps its own default |
| `session_cookie_path` | `MODX_BASE_URL`, or the `base_url` setting when it is set |
| `session_cookie_domain` | empty |
| `session_cookie_secure` | `false` |
| `session_cookie_httponly` | `true` |
| `session_cookie_samesite` | empty |
| `session_cookie_lifetime` | `604800` (7 days). A context can set `0`, and then the cookie lasts until the browser closes |

## Context Access

Retrieving a Context Setting is no different than retrieving any other System Setting -- in fact, you may not be aware that a given setting is being set at the System or Context (or at the User) level.
Access is granted per user group. Open the lock icon in the user menu → **Access Control Lists** → a user group → **Access Permissions** → **Contexts**.

In a template or Chunk:
A user group's access panel has five tabs: **Contexts**, **Resource Groups**, **Element Categories**, **Media Sources** and **Namespaces**. A policy does not pick the tab. The policy template has to belong to the same group as the tab you are editing.

## Reading Context Settings

Read the current context's setting like any other setting. The call does not show which level supplied the value:

``` php
[[++my_context_setting]]
```

Programmatically in a Snippet:

``` php
$setting = $modx->getOption('my_context_setting');
```

For a specific context:

``` php
$setting = $modx->context->getOption('my_context_setting');
$setting = $modx->getContext('web')->getOption('my_context_setting');
```

The request language comes from the first source that has a value:

1. the request's `cultureKey` parameter
2. the session's `cultureKey`
3. the user setting `cultureKey`
4. the context's `cultureKey`
5. the `cultureKey` system setting

If every source is empty, or the value fails the character check, the language is `en`. The check allows `A-Z`, `a-z`, `0-9`, `_` and `-`. In the manager, `manager_language` replaces that result.

## Choosing a Context at Runtime

A request starts in the context the gateway chooses. Two mechanisms change that:

- the gateway file `config.context.php` next to `index.php`. The file returns the context key the request starts in. See [Using Virtual Hosts](building-sites/contexts/virtual-host);
- a Plugin on `OnHandleRequest` that calls `switchContext()`. See [Using a Gateway Plugin](building-sites/contexts/gateway-plugin).

``` php
if ($modx->switchContext('domain2')) {
// the request continues in the context "domain2"
}
```

`switchContext($contextKey, $reload = false)` returns `false` when the context was not loaded, and when the key is already the current context. In the second case the context is not loaded again. Check the result: on a request that is already in `domain2`, the example above takes the failure branch.

Switching the context during the request does not load a custom session handler, and it does not change the language, the locale or the session cookie. Those come from the context that `index.php` started in. A domain that needs its own language or cookie uses its own directory with `config.context.php`. See [Using Virtual Hosts](building-sites/contexts/virtual-host).

## Events

| Event | When it fires |
| --- | --- |
| `OnContextInit` | a context is loaded. A successful `switchContext()` to a different context loads it. A call for the context that is already current does not |
| `OnContextBeforeSave` | before a context is saved |
| `OnContextSave` | after a context is saved |
| `OnContextBeforeRemove` | before a context is removed |
| `OnContextRemove` | after a context is removed |
| `OnContextFormPrerender` | before a context form is rendered |
| `OnContextFormRender` | after a context form is rendered |

MODX 2 events often named in older material do not exist in MODX 3: `OnContextSwitch`, `OnSiteInit`, `OnSiteRender`, `OnManagerInit` and `OnBeforeRequest`.

## System Settings

| Setting | Value by default | What it does |
| --- | --- | --- |
| `default_context` | `web` | the context the create form uses when the resource is not created inside another context's branch. A branch of the tree uses that branch's context |
| `access_context_enabled` | `true` | checks the access to a context on every request; switching it off ignores every context access rule |
| `allow_forward_across_contexts` | `false` | lets a symlink and `sendForward()` reach a resource in another context |
| `cache_context_settings` | `true` | caches context settings |
| `use_context_resource_table` | `true` | keeps the table of resources that belong to several contexts |
| `context_tree_sort` | `true` | turns sorting of contexts in the resource tree on. The field is `context_tree_sortby` |
| `context_tree_sortby` | `rank` | the field the contexts of the tree are sorted by |
| `context_tree_sortdir` | `ASC` | the direction of the sorting |

## Media Sources of a Context

You can set an element's media source for one context. The element uses its own context's source. With no source on the context, it keeps the element's default source.

## One Resource in Several Contexts

A resource normally belongs to one context. With `use_context_resource_table` on, the `context_resource` table stores resources that belong to several contexts. MODX finds such a resource in each listed context.

## See Also

1. [Creating a Subdomain from a Folder using Virtual Hosts](building-sites/contexts/virtual-host)
2. [Using One Gateway Plugin to Manage Multiple Domains](building-sites/contexts/gateway-plugin)
1. [Using Virtual Hosts](building-sites/contexts/virtual-host)
2. [Using a Gateway Plugin](building-sites/contexts/gateway-plugin)
3. [System Settings](building-sites/settings)
4. [ACLs](building-sites/client-proofing/security/policies/acls)
65 changes: 36 additions & 29 deletions en/building-sites/contexts/gateway-plugin.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,55 @@
---
title: "Using a GatewayManager Plugin"
_old_id: "333"
_old_uri: "2.x/administering-your-site/contexts/using-one-gateway-plugin-to-manage-multiple-domains"
title: "Using a Gateway Plugin"
description: "Serving several domains from one MODX installation: the Plugin that switches the context by host, and the approach with a virtual host and a directory for every domain."
---

You have a choice when sharing a single database and manager across multiple domains. You can choose to use the primary front-end context (known as 'web') to handle all domains or you can create a unique gateway file for each domain to directly initialize a specific context. If you use a single gateway, you would use a plugin to switch contexts registered to the `OnHandleRequest` event, something like so:
One installation can serve several domains. The request's context tells them apart. Two approaches do that:

- **One virtual host, several domains.** Every domain points at the same virtual host. A Plugin on `OnHandleRequest` reads the host and switches the context. This page covers this approach.
- **A virtual host and a directory for every domain.** Each domain gets its own virtual host and a gateway directory with its own `config.context.php`. See [Using Virtual Hosts](building-sites/contexts/virtual-host).

The first approach needs only the Plugin.

## Switching the Context by Host

`OnHandleRequest` fires at the start of every request, on the front end and in the manager.

``` php
<?php
/* don't execute if in the Manager */
if ($modx->context->get('key') == 'mgr') {
return;
if ($modx->context->get('key') === 'mgr') {
return;
}

switch ($_SERVER['HTTP_HOST']) {
case 'domain2.tld':
// if the http_host is of a specific domain, switch the context
$modx->switchContext('domain2.tld');
break;
case 'domain3.tld':
// if the http_host is of a specific domain, switch the context
$modx->switchContext('domain3.tld');
break;
default:
// by default, don't do anything
break;
$hosts = [
'domain2.tld' => 'domain2',
'domain3.tld' => 'domain3',
];

$host = $_SERVER['HTTP_HOST'] ?? '';
if (isset($hosts[$host])) {
$modx->switchContext($hosts[$host]);
}
?>
```

Alternatively, you would simply copy the index.php file from the default web context (along with the core.config.php and .htaccess for rewrite rules altered appropriately) to another directory and change the line
The `return` keeps the manager in the `mgr` context.

`switchContext($contextKey, $reload = false)` returns `false` when the context was not loaded, and when the key is already the current context. In the second case the context is not loaded again. Check the result before the rest of the request relies on the new context:

``` php
$modx->initialize('web');
if (!$modx->switchContext('domain2')) {
$modx->log(modX::LOG_LEVEL_ERROR, 'The context "domain2" was not found.');
}
```

to
On a request that is already in `domain2`, that check also fails, because the context was not switched.

``` php
$modx->initialize('aContextNameOfYourChoice');
```
## The Session Handler

Note that you could also just copy the index.php in the same directory and rename it to do this, but your rewrite rules would have to be smart enough to route requests to the appropriate context gateway, and you would need to configure the `request_controller` option in Context Settings appropriately.
Switching the context at runtime does not load a custom session handler. It does not change the language, the locale or the session cookie. Those come from the context that `index.php` started in. A domain that needs its own language or cookie uses its own directory with `config.context.php`. See [Using Virtual Hosts](building-sites/contexts/virtual-host).

You can also still use a custom core location in either of these scenarios; this is independent of the context-driven multi-site capabilities.
## See Also

Checkout the [GatewayManager component](extras/gatewaymanager)
1. [Contexts](building-sites/contexts)
2. [Using Virtual Hosts](building-sites/contexts/virtual-host)
3. [OnHandleRequest](extending-modx/plugins/system-events/onhandlerequest)
4. [GatewayManager](extras/gatewaymanager)
Loading
Loading