This Plugin allows one or more resources specfied (by template) to be accessed across all Contexts.
Uses include: having a single Login page (without having to worry about full urls, typed urls, etc.), serving AJAX style pages (without specifying jsonp), and combining the two. Proper use of this Plugin may reduce code, maintenance and the number of Resources within your Resource Tree.
See Also:
Related Tutorials/Plugins
Updated as of 8/19/2012
- Respects Parent resource paths
- Respects Child Resources now
- Simplified code logic for readability.
- Added System Settings for Compatibility, Configuration, Security and Performance
Summary
As one progresses further into the ModX platform, there will be some increasingly apparent limitations imposed by the system. Whether or how these limitations affect your development depends largely on the type of site that you are building. One such limitation is the lack of the ability to create a Resource that is accessible from every Context with the same path.
Under the ModX framework, we typically accomplish this by creating a copy of the Resource in each Context that we would like to access it from. This works well, as long as you don't have 50 contexts or do not want to have multiple copies. It is not easily supportable, as when we change one page, it is not necessarily changed across the board. In other words, for large web-applications, this is can actually be a hinderance to your site's development. This tutorial shows how to create a Resource or series of Resources that are accessible from the same path, regardless of the Context.
The Process
This tutorial is really simple. It takes about 15 minutes to setup. Honestly, you will probably spend more time reading than you will doing the actual work. Unlike the original version, you will not have to adjust any code whatsoever and it may be used out of the "box", so to speak. First, we'll create some System Settings. Next, we'll make a Template. After this, we'll create the Plugin and input some code. Once we have completed the Plugin, we can test using any Resource of said Template. Finally, we'll address compatibility with other Plugins that use the OnPageNotFound system event.
Create the Template
Create a new Template with the name "Alias (Site)". We will not delve into which content to place here, as it will be largely dependent upon what you desire. Since we often use Site Aliases for AJAX calls, our content is simple:
Click Save and move on to the next step.
System Settings
We have to adjust one already provided System Setting, and then add a couple more. The System Settings we will be using will make the Plugin considerate to other Plugins (particularly our own). Additionally, one in particular, will reduce processing considerably if using Plugins from my other tutorials.
Allow Cross-Context Forwarding
Go to System Settings in the MODx Manager. In the search box, type 'allow_forward_across_contexts' and press Enter. Double-Click on the value and choose "Yes".
Link to Template
Go to System Settings in the MODx Manager. Click Create New Setting. Set the options as follows:
- Key: id_site_alias
- Type: Template
- Name: Alias: Cross-Context Template
- Description: The ID of the Template that may be used as a cross context resource.
- Value: Alias (Site)
Click Save and it is done.
Improve Processing
Go to System Settings in the MODx Manager. In the search box, type 'key_doc_found' and press Enter. If it exists (from another tutorial), we're done. If it doesn't exist, click Create New Setting. Set the options as follows:
- Key: key_doc_found
- Type: Text
- Name: Page Found Placeholder
- Description: The key of the "found page" flag in the $_REQUEST global variable. This defaults to 'hasResource'.
- Value: hasResource
Click Save and it is done.
Create the Plugin
This is where the magic is performed. Like the AJAX Framework (referenced above), we will use the OnPageNotFound event as this is the event that will fire when it cannot find your Resource outside of its normal Context. If it is an AJAX Request, it will fire every time you request it.
Create a new Plugin with the name 'onFindSiteAlias'. Copy and Paste the following code into the content:
//Get the System Settings (if we haven't already...)
$keyURL = !empty($keyURL) ? $keyURL : $modx->getOption('key_url_request', null, 'toURL');
$keyFound = !empty($keyFound) ? $keyFound : $modx->getOption('key_doc_found', null, 'hasResource');
// Get "passed" variables
$isFound = empty($_REQUEST[$keyFound])
? 'false'
: $_REQUEST[$keyFound];
// Only do this if we need to scan.
if ($isFound == 'false')
{//Get the Request URL
$toURL = !empty($_REQUEST[$keyURL])
? $_REQUEST[$keyURL]
: $_SERVER['REQUEST_URI'];
// Remove GET Parameters
$toURL = reset(explode('?', $toURL));
$path = explode('/', trim($toURL, '/'));
/*
* Checks if the alias is a Cross Context Alias
*/
function isSiteAlias($alias)
{//Get MODx instance
global $modx;
// Get System Setting
$idAliasTemplate = $modx->getOption('id_site_alias', null, 1);
$q = $modx->newQuery(
'modResource',
array(
'published'=>1,
'alias'=>$alias,
'template'=>$idAliasTemplate,
)
);
$q->select(array('id','alias'));
$q->prepare();
// Iterate through potential matches
$matches = $modx->getCollection('modResource',$q);
if (!empty($matches))
foreach($matches as $res)
// Respect parents
return $res;
// Return empty variable
return null;
}
/*
* Checks if the alias is a child of the previous path.
*/
function isChildOf($alias, $child)
{// Strip the extension
$alias = reset(explode('.',trim($alias)));
$parent = $child->getOne('Parent');
// Simple climb
if (!empty($parent))
if ($alias == $parent->get('alias'))
return $parent;
// Return empty result
return null;
}
/*
* Checks if the alias is a parent of the next path.
*/
function isParentOf($alias, $parent)
{// Strip the extension
$alias = reset(explode('.',trim($alias)));
$children = $parent->getMany('Children');
// Simple climb
foreach($children as $key => $res)
{ if (!empty($res))
if ($alias == $res->get('alias'))
return $res;
}
// Return empty result
return null;
}
// Initialize Tracking Variables
$toResource = null;
// Find the Alias (one alias per Request)
foreach($path as $key => $segment)
{//Check if it could be an Alias
$toResource = isSiteAlias($segment);
if (!empty($toResource))
{
// Split into two arrays
$parents = explode('/', reset(explode($segment.'/', $toURL)));
$children = explode('/', end(explode('/'.$segment, $toURL)));
break;
}
}
if (!empty($toResource) && !empty($parents))
{ $child = $toResource;
foreach($parents as $key => $segment)
{ if (!empty($segment))
{ $tmp = isChildOf($segment, $child);
if (empty($tmp))
{ $toResource = null;
break;
}
else $child = $tmp;
}
}
}
// Keep track of Children (for Paths deeper than Alias)
if (!empty($toResource) && !empty($children))
{ foreach($children as $key => $segment)
{ if (!empty($segment))
{ $tmp = isParentOf($segment, $toResource);
if (empty($tmp))
{ $toResource = null;
break;
}
else $toResource = $tmp;
}
}
}
// Send to the Nearest found Child.
if (!empty($toResource))
{ $_REQUEST[$keyFound] = 'true';
$modx->sendForward($toResource->get('id'));
}
else $_REQUEST[$keyFound] = 'false';
}
Save the plugin. Click on the System Events tab and scroll down to 'OnPageNotFound'. Click the check box and set a priority of 3 (or higher).
Note: If you are using any of my other Custom Aliasing Plugins, AJAX Plugins, or the sponsored Articles Add-on, see On Compatibility below.
Testing the Plugin
Create the Resource
Create a Resource anywhere in your resource tree. For demonstration, we will also keep this quite simple. Name it whatever you want, but give it an alias of "site-alias". Place some silly content in here.
<h1>The proverbial tree did indeed make a sound!!</h1>
Click Save. Now, right-click the Resource and choose "View Resource".
Test One: Contexts
Once the Resource has appeared, go to your Address Bar in your browser. Change the base url to the base url for another context. Sub-Domain Contexts change only the sub-domain in the URL. Domain Contexts change the domain. Press Enter and it should load just fine.
Test Two: Parent Pathing
Go to the Address Bar and change the parent path (the segments before your alias) to "/kittens/are/fuzzy/". The page should not load.
Test Three: Child Pathing
Add a Child Resource to your new Cross Context Resource (it may be any Template you choose). Save it and refresh the cache. Right-Click on the Child Resource and choose "View Resource". Perform Test One. Test Two is already successful. Between the Site Alias segment and the Child Alias segment, add "/puppies/are/fuzzy/too/". Press Enter. The page will not load (404 Error).
On Compatibility
Supports
Does not interfere with GET or POST.
Supports Static Resources. Since our call uses sendForward, static resources are no problem.
All Content Types and their content are supported. If you use other Content Types, depending on setup you may run into issues with extensions or extra/missing slashes. If this is the case, you may also
Remove Extensions and Extra Slashes.
System Settings
The System Settings used here (getOption()) are used by my other Plugins, as well. You don't need their Settings unless you are using their Plugins. If you would
like to create them, refer to the updated
Custom Aliasing: AJAX Framework.
FuzzicalLogic's AJAX Tutorials
Priority for this plugin must be after (higher than) all other AJAX Framework plugins that use OnPageNotFound. This includes the following:
onGetRequestType,
onParseURLParams. For ensured compatibility, make sure you "upgrade" them using the updated tutorials.
FuzzicalLogic's Aliasing Tutorials
Priority for this plugin must be before (lower than)
Template-Based Actions. While it is not required to upgrade this plugin, updating provides much better compatibility, tighter security, optimized processing, and configuration options.
Articles Plugin
If you use Articles, you will need to change the priority of ArticlesPlugin to be after all of my plugins.
Further Exploration
There is a Tutorial for making
Login a Cross-Context AJAX page. This demonstrates how to use the plugins practically with little effort.
Conclusion
This technique works particularly well for Contexts developed for use with Domains or Subdomains. It may have to be adjusted for Internationalized sites (particularly if using Babel). Using this, I am able to support multiple sites with the same document. Further, with the System Settings, I can set different values for different Contexts or Users, meaning that the violation of one site will not expose another unnecessarily.
[ed. note: fuzzicallogic last edited this post 11 years, 7 months ago.]