This plugin allows you to add shared "children" pages to any Resource that has a Template. (Including Articles and ArticlesContainers). A common use for this would be to add an Edit page (with alias "edit") to all Articles using a single Resource. Another example would be to add a "buy" AJAX page to all pages of template Product. Proper use of this plugin can streamline development, reduce load times, and allow for better site support and debugging.
See Also:
Updated as of 8/19/2012
- Simplified code logic for readability.
- Supports my AJAX Framework now.
- Added System Settings for Compatibility, Configuration, Security and Performance
Summary
Ever wonder how Articles does that neat stuff with URLs that you never knew you had? Well, it all comes down to aliasing. Articles creates false URLs and uses them whenever your container is in the requested URL. That's why if you add a path segment that Articles cannot process, it goes to the Container again and you may get different results. Extra URLs that provide additional content or functionality and you don't have to program? This is darn handy functionality! So, how do we replicate it without getting overly complicated?
This tutorial will teach you how to make "Child Resources" that are added to every Resource of a given Template, just like Articles does for its containers and children (but in a much cleaner way). Child Resources may be full documents or AJAX partial HTML. They may even be Redirects or additional Scripts.
The Process
This tutorial looks longer than it takes to apply. You can complete the steps here in less than 10 minutes. First, we will create a Template. Next, we will create some System Settings (these will improve compatibility and performance). After the Settings are in place, it comes to the Plugin and its code. Then, testing begins for demonstration. This will involve creating a couple of Resources as Children. Finally, we'll address compatibility with other Plugins.
Create the Action Template
This part is simple and doesn't require any code, whatsoever. It uses yet another Template-aware trick to get the desired effect. Create a new Template. Name it "Alias (Template)". For ease, set the content to the below (this is optimal for AJAX calls, as well) and Save:
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.
(Required) Link to Template
Go to System Settings in the MODx Manager. Click Create New Setting. Set the options as follows:
- Key: id_template_action
- Type: Template
- Name: Alias: Template Action Template
- Description: The ID of the Template that is used to define a sub-page for all parents of the same type as its parent.
- Value: Alias (Template)
Click Save and it is done.
Note: It has come to my attention that Template Type is not always retrieved correctly between different MODx versions. In this case, set the type to Number. If it still doesn't work, there is another option above the code for the Plugin.
(Required) Improve Processing
If you are using Cross Context Resources (link in my signature), this is already set.
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.
(Recommended) Re-use Simple Aliases
This needs to be set if you want to have simple aliases. An example is if you want 3 Templates to have an "edit" action, then this needs to be "Yes"
Go to System Settings in the MODx Manager. In the search box, type "Use Friendly Alias Path".
Double Click on the value and choose "Yes".
(Optional) Allow Cross-Context Forwarding
This needs to be set if you are going to have a single definition serve multiple contexts. If you are using Cross Context Resources (link in my signature), this is already set.
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".
Create the Plugin
Create a new Plugin Element. Name it "onTemplateAction". Like my other Aliasing tricks, it uses OnPageNotFound as that is the event that will fire when you access the Action from a Resource that it is not specifically defined in. Now copy and paste the following code into the Content and Save:
Important: Some versions of MODx do not read the Template type correctly. You have two options. 1) Convert id_template_action to a Number type; 2) Manually add the line "$idAction = #;" where # is the ID of Alias (Template). If you choose option 2, place the new line above the "$idAction = empty(...) ...;" (line 5)
<?php
//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');
$idAction = !empty($idAction) ? $idAction : $modx->getOption('id_template_action', null, 'hasResource');
// Get "passed" variables
$isFound = empty($_REQUEST[$keyFound])
? 'false'
: $_REQUEST[$keyFound];
// Only do this if we need to scan.
if ($isFound == 'false')
{//See if a previous plugin has set the URL.
$toURL = !empty($_REQUEST[$keyURL])
? $_REQUEST[$keyURL]
: $_SERVER['REQUEST_URI'];
$toParams = end(explode('?', $toURL));
$segments = explode('/', trim($toURL, '/'));
if (!empty($segments))
{//Get the relevant alias
$toAlias = trim(end($segments));
if (empty($toAlias) || $toAlias == '')
$toAlias = strtolower(trim(prev($segments)));
if (empty($toAlias) || $toAlias == '')
return;
// Get the attached Resource
$q = $modx->newQuery
( 'modResource',
array
( 'alias'=>strval($toAlias),
'template'=>$idAction,
'published'=>1
)
);
$pages = $modx->getCollection('modResource', $q);
if (!empty($pages))
{//Get the Parent Resource
$toParent = trim(prev($segments));
if (!empty($toParent))
{ $q = $modx->newQuery
( 'modResource',
array
( 'alias'=>strval($toParent),
'published'=>1,
'context_key'=>$modx->context->key
)
);
$toParent = $modx->getObject('modResource', $q);
if (!empty($toParent))
{ $forID = $toParent->get('id');
$toParent = $toParent->get('template');
}
}
// if (empty($toParent) || $toParent == '' || $toParent == 0)
// return;
// Compare the results
foreach ($pages as $key => $res)
{ if (!empty($res))
{ $chkParent = $res->getOne('Parent')->get('template');
if ($chkParent == $toParent)
{ $_REQUEST[$keyFound] = 'true';
$_REQUEST['forID'] = $forID;
$modx->sendForward($res->get('id'));
break;
}
}
}
}
}
}
Click the "System Events" tab and scroll down to OnPageNotFound. Check the box, and choose a priority of 3 (or higher). Click Save.
Note: If you have: a) my AJAX Framework plugins, my other Custom Alias plugins, or the Articles Add-on, see the section On Compatibility.
Testing the Plugin
This testing process is a little involved and requires you to make a choice or two. This is because the versatility and flexibility of MODx allows just a single Template for many purposes or many Templates for singular purposes.
Test Setup: Action Containers[/u]
Look through your Template list. If you have multiple Templates, good job! Pick two that have at least one Resource that is accessible in your Resource Tree.
Note: Do not pick Article, ArticleContainer or Alias (Site). While it will work with all of them, this will only complicate the tests and confuse you, at first. For Articles and ArticlesContainers, you must create a new one without using the CMP and by setting the template manually.
Test Setup: Create the Actions
For each of your Templates chosen above, pick a Resource that uses that Template (you
will do the following for each one). Inside it, make a Resource. Set a
unique title for each. For demonstration, set the alias for each to "test-action". This should not conflict as they are under different parents. Set the Templates to "Alias (Template)". After it reloads, set the content for both to:
Test One: Test the Actions
Right Click on each of the Actions and choose "View Resource". Each should show up with its respective title.
Test Two: Test another Resource
Pick another Resource of one of your Template from above. Right-click on it and choose "View Resource". After it loads, go to the Address Bar of your browser. Add "/test-action/" to the end and press Enter. You should see the appropriate *pagetitle of the correct Action.
Rinse and repeat for the second template from the setup. Once you view the resource, and go to "test-action", you should see the other *pagetitle! Now you have reused functionality but made it specific to each template.
On Compatibility
Supports
Does not interfere with GET or POST.
Supports Static Resources as Parents (haven't tested this with Children). Since our call uses sendForward, static resources are no problem.
Works across contexts, if you want to keep your "object/class" definitions separate from the documents. You may split aliases between multiple resources of the same template, though it is not recommended.
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 install my Remove Extension and Slash Issues in URLs Plugin. (link in signature)
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 Tutorial on my AJAX Framework (link in my signature).
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" using the updated Tutorial. (links in my signature)
FuzzicalLogic's Aliasing Tutorials
Priority for this plugin must be after (higher than) Cross Context Resources. While it is not required to upgrade this plugins, updating provides much better compatibility, tighter security, optimized processing, and configuration options. (links in my signature)
Articles Plugin
If you use Articles, you will need to change the priority of ArticlesPlugin to be after all of my plugins. (also see Further Exploration below)
Further Exploration
If you are using AJAX Framework, you now have shared actions without having to include an additional parameter (URL or otherwise). Additionally, the snippets they call may exhibit entirely different functionality.
If you use Articles and have multiple Container Templates, you can have different actions for each. To avoid "losing" the Aliases in the Resource Tree, you may create a blank Resource. Set the Template to your Container and add the actions to it.
You may add actions to custom user pages!
Front-end editing can be more secure by being tied only to the documents that they should be. The Parent ID is automatically sent to the Action, so there is no need to send it in a GET, POST or URL parameter. Simply remove access to the link and perform a little security checking.
Further, this single URL may be excluded from your site-map easily.
Conclusion
This technique has made my life so much easier. No more duplication of code/functionality. Additionally, I have a separate Context (with no url) with nothing but blank resources containing Template Actions for "class definition". Super clean and tight functionality.
[ed. note: fuzzicallogic last edited this post 14 years, 1 month ago.]