Disclaimer: Of course, most likely, much of the one presented in this article will seem like captaincy to knowledgeable people. However, perhaps, it will help someone ...Introduction
So, what is MODX (by the way, it is written this way - MODX, and not as the name of the hub - MODx)? If you read the official site - it is a CMS. However, this is only part of the truth. In fact, MODX is roughly midway between the CMS and CMF. However, anyone who would be interested in MODX would quickly find out from other articles, so I will not dwell on this point in more detail.
Since MODX is midway between CMS and CMF, it is not as easy to master as a simple CMS, like Wordpress or Joomla. Perhaps this article is written in order to reveal some subtleties that seem unobvious at first glance.
')
Installation
Personally, I installed MODX Revo 2.2.4 on a server equipped with nginx and PHP-FPM.
Since in general MODX is sharpened for Apache + PHP extension, additional configuration is required for nginx. My configuration looks like this:
nginx configurationserver { listen 80; server_name example.com *.example.com;
In fact, this config, I'm just sure that it is not ideal, but for the initial configuration, it will probably work. You can stop here only on two things, which, in fact, connect MODX (so that the CNC works correctly):
server_name example.com *.example.com;
Actually, this seems to be enough for correct operation both for MODX and for everything else (loading nginx CSS, JS, images and other things without PHP).
The installation itself is very simple, just unpack the files into the desired folder and type in the address bar example.com/setup/, after which a simple installer will be loaded.
Man-Friendly URL (CNC)
By default, in CNC MODX is disabled. It turns on very simply: System -> System Settings -> we drive into the filter “friendly_urls” and press Enter (you can of course find the handles, but this is faster) -> Switch the value to “Yes” and click “Save” at the top.
In general, do not forget that in MODX the save button is located in the UPPER RIGHT CORNER. Sometimes you can forget about it, and then the settings can be lost - because in MODX, an autosave sometimes works, and sometimes not.
Similarly, we do for the “automatic_alias” function for automatic CNC generation.
We have successfully included the CNC, and now the links will be formed as follows:
example.com/resourcename.html
Pay attention that in the end .html is substituted! It depends on the Content-Type of each individual resource (changes in the properties of the resource itself). Content-Type settings themselves can be found in System -> Content Types.
However, all additional GET parameters still remain the same, and it works like this:
example.com/news.html?date=05092012&nid=1
Personally, I did not like this system, and I began to look for ways to make these parameters part of the CNC. And found!
To begin with, it is worth telling a little about the various elements allowing to expand MODX. There are several:
- Template Variables - additional fields of resource properties. We will return to them later
- Chunks are blocks of HTML code (with possible MODX markup) that can be inserted into any other chunk, resource, or anywhere else. Attention! PHP can not be inserted here!
- Snippets are blocks of PHP code that output some data to the place where they are inserted (using MODX markup). Peculiar PHP-analog chunks.
- Plugins are blocks of PHP code executed in accordance with certain events occurring inside MODX. Something like Drupal-ovsky hooks, perhaps.
In this case, we will need snippets and plugins.
So, how to integrate into MODX with its own URL format? in fact, it’s very simple to let MODX try to find a page on a given URL, and when it doesn’t find it, take control and parse it, sending the MODX to the right place. To do this, you need to use a plugin for the
OnPageNotFound event.
Plugin code for "improved" CNC <?php if($modx->request->getResourceMethod()!="alias") return; $uri = $modx->resourceIdentifier; $uriChunks = explode("/", $uri); $paramNum=0; $uriChunksCount = count($uriChunks); $paramDelimiter="-";
Actually, here it is probably worthwhile to dwell on the following things:
$modx->request->getResourceMethod()
Check whether the CNC is turned on at all.
$modx->resourceIdentifier
This not so obvious variable stores the requested URL.
$paramDelimiter="-";
This variable sets the delimiter between the GET parameter name and its value. You can change to anything except "/".
$modx->request->parameters['GET'][$parameter[0]]=$parameter[1]; if(empty($modx->request->parameters['POST'][$parameter[0]])) $modx->request->parameters['REQUEST'][$parameter[0]]=$parameter[1];
The fact is that with each request, MODX saves all the information about the GET and POST parameters into its own arrays, after which it forms a common array in the following way:
$modx->request->parameters['REQUEST'] = array_merge($modx->request->parameters['GET'], ($modx->request->parameters['POST']);
To save this structure, we have to check each time whether the data from the POST array is not stored in the REQUEST array, and only in the absence of such data to replace the data in the REQUEST array.
if (array_key_exists($uri, $modx->aliasMap)) { $modx->sendForward($modx->aliasMap[$uri]); }
And this is actually a redirect to the desired resource. The entire NC table is stored in $ modx-> aliasMap, and we check it with it, figuring out whether it’s time to go to the right resource, or whether the contents of our underpriced uri still consist of parameters. $ modx-> sendForward and performs this redirection.
However, you probably want to immediately provide links to, for example, the current page, using this URL generation mechanism! For this, I wrote a simple snippet, just generating the URL of the current page. Of course, it can be expanded for the generation of other links, but this will be the personal homework of the one who needs it - there is nothing difficult about it :)
<?php $currentURL=""; foreach($modx->request->getParameters() as $key => $value) { $currentURL.=$key."=".$value."&"; } $currentURL = $modx->makeUrl($modx->resource->get("id"), $modx->context->get("key"), $currentURL); return $currentURL;
Actually, nothing special to explain. $ modx-> makeUrl just forms the URL. For reference, it forms it relative to the context - the second parameter sets the context.
Multisite in MODX
Processing multiple domains with different contexts by one MODX
Actually, this topic has already been raised, for example,
in this topic and
on the Internet . I would like to present my decision, it seems to me, quite simple. But first, let's list the main methods for solving this problem:
- edit index.php. The index.php explicitly states which context is loaded at startup. It can be completely corrected, and you can substitute your conditions there with a choice of your context. Why I don’t like this method: editing the original modx files. Why don't I like this fact? Difficulty with updates. For what I love modx (and just as much I do not like the SMF engine of the forum ), this is because it DOES NOT NEED to change any files of the original MODX, which is just as easy to update the engine as possible.
- Creating a plug-in for the OnHandleRequest event (common drawback: a bit more resources are used - the web context is always loaded first, and only then the necessary context is loaded):
- Similar to editing index.php, but without editing it. Everything is almost the same - a rigidly defined list of domains that are processed by a rigidly given list of contexts. As an option, the context name contains part of the domain name (the context of the forum for the subdomain forum.example.com for example), on the basis of which the search of the necessary context is performed. For simple websites it is suitable, but there is not enough flexibility.
- Scanning context settings to find the right one. Maximum flexibility, auto-creation of contexts, no need to edit the plugin after its creation.
I am going to describe here just the last option. My plug-in text (remember, the
OnHandleRequest event):
Plugin-gateway <?php if($modx->context->key!="mgr") { $object = $modx->getObject('modContextSetting', array('key' => 'multisite_http_host', 'value' => $modx->getOption('http_host'))); if($object) $modx->switchContext($object->get('context_key')); }
To use it, it is necessary to put down the “multisite_http_host” parameter in the context properties with the value of what domain it processes. If the plugin does not find any suitable context, it uses the default web context.
Here I want to focus on two things.
First, in order to successfully use MODX, you need to understand the principle of xPDO. xPDO is a mechanism for communication between a database and PHP objects. Working with an object in PHP we get access to the database. In fact, everything in MODX uses xPDO to communicate with the database.
$object = $modx->getObject('modContextSetting', array('key' => 'multisite_http_host', 'value' => $modx->getOption('http_host')));
This code just calls the database to search for the context settings object containing certain conditions (namely, the “multisite_http_host” setting key with the value of our http host). After that we get the context key that we need to load from the received object ($ object-> get ("context_key")).
Secondly, the hacker
XanderBass asked the right question, the answer to which I would like to duplicate here. So why do I use the parameter name “multisite_http_host” and not just “http_host”?
Everything is very simple, it follows from the mechanism of combining the settings of the system, the context and the user. The fact is that the system settings are overwritten by the context settings, and those, in turn, by the user settings.
In this case, http_host is a system setting. If I used http_host instead of multisite_http_host in the context settings, it would be overwritten by the context setting. Of course, in this particular example, it is not so scary. But it is enough just to rewrite this plugin a bit to handle all subdomains of this domain with one context! Or, make processing the same context of several different domains (for example, driving into a parameter in about the following format: sub1.example.com; sub2.example.com; sub3.example.com ", etc.). In this case, in http_host, it would not be the http_host used in the request, but this is the setting itself, but you never know, maybe we will need the original http_host somewhere else ...
Multi-context authentication
Sometimes you want to authenticate the user in several contexts at once. By default, a user who logs into the system (for example, using the Login mode), logs in to the system only in a single context. The following authorization plugin automatically enters several contexts (
OnWebLogin and
OnWebLogout events ):
Multi-authentication <?php $currentSiteGroup = $modx->getOption("multisite_site_group"); if(empty($currentSiteGroup)) return; $currentContext = $modx->context->get("key"); $currentContextSettings = $modx->getCollection('modContextSetting', array('key' => "multisite_site_group", "value" => $currentSiteGroup)); foreach($currentContextSettings as $currentContextSetting) { $contextKey = $currentContextSetting->get('context_key'); if($contextKey!="mgr" && $contextKey!=$currentContext) { if($user) { if($modx->event->name=="OnWebLogout") { $modx->user->removeSessionContext($contextKey); } else if($modx->event->name=="OnWebLogin") { $modx->user->addSessionContext($contextKey); $_SESSION['modx.'.$contextKey.'.session.cookie.lifetime']=$attributes["lifetime"]; } } } }
To use, you need to put down in all contexts on which you want the user to simultaneously go, the parameter “multisite_site_group” with the same value for all necessary contexts.
Just a few comments:
Here, the search for the necessary contexts is very similar to how we implemented them in the previous plugin, the only difference is in the use of getCollection instead of getObject. By doing so, we get an array of objects that follow the specified conditions.
$modx->user->addSessionContext($contextKey);
and
$modx->user->removeSessionContext($contextKey);
just authorize the user in a particular context.
$_SESSION['modx.'.$contextKey.'.session.cookie.lifetime']=$attributes["lifetime"];
puts down the "lifetime" authorization for all other contexts the same as for the current one.
At the moment, everything seems to be what I wanted to tell. Practices with language switching will be possible later, when I finish them a little. Thank you all for your attention!