Note: Experimental release
Okay, I whipped up some new code that reads a Template Variable rather than a configurable array.
Installation:
1) Create a new Plugin and copy and paste the below PHP code.
2) In the System Events Tab check "OnWebPageInit" & "OnWebPagePrerender"
3) Create a new Template Variable, the default name is seoOverride but this can be modified by editing the configuration option in the plugin’s php code.
4) Select DropDown List Menu for the Input Type and enter "Disabled==-1||Base Name==0||Append Extension==1||Folder==2" for Input Option Values (excluding quotes).
5) Check the desired templates to associate the new Template Variable with them.
Finally, edit your pages, modifying the new Template Variable to override rewrite preferences.
NOTE: Remove the first <?php and last ?> from the code when saving as a plugin.
<?php
// Strict URLs
// version 0.7.2
// Enforces the use of strict urls to prevent dup content.
// By Jeremy Luebke @ www.xuru.com
// Override configuration added by Brian Stanback @ www.stanback.net
// On Install: Check the "OnWebPageInit" & "OnWebPagePrerender" boxs in the System Events tab.
// For overriding documents, create a new template variabe (TV) named seoOverride with the following options:
// Input Type: DropDown List Menu
// Input Option Values: Disabled==-1||Base Name==0||Append Extension==1||Folder==2
// Default Value: -1
// # Include the following in your .htaccess file
// # Replace "example.com" & "example\.com" with your domain info
// RewriteCond %{HTTP_HOST} .
// RewriteCond %{HTTP_HOST} !^www\.example\.com [NC]
// RewriteRule (.*) http://www.example.com/$1 [R=301,L]
// Config //
$editMenuLinks = 1; // Set to 1 to manipulate menu links. Set to 0 to leave them alone
$makeFolder = 0; // Set to 1 to enforce folders to display as /dir/ and not /dir.html
$override = 1; // Enable manual overrides
$overrideTV = "seoOverride"; // Name of the template var that contains the override value
$editOverrideLinks = 1; // Set to 1 to manipulate links for overridden documents. Set to 0 to leave them alone
$editOverrideCase = ""; // Set to "" to replace links beginning with lower case href= only or "i" to replace all cases of href (slower)
// Start Main Code //
$e = &$modx->event;
if($e->name=='OnWebPageInit')
{
$myProtocol = $_SERVER['HTTPS'] == on ? "https" : "http"; // Replace with $myProtocol = $modx->config['server_protocol'] to force the configured protocol
$s = $_SERVER['REQUEST_URI'];
$parts = explode("?", $s);
$documentIdentifier = ($modx->documentIdentifier) ? $modx->documentIdentifier : $modx->config['error_page']; // Set error page document ID if page is not found
$results = $modx->db->select("isfolder, alias", $modx->getFullTableName('site_content'), "id = " . $documentIdentifier);
$row = $modx->fetchRow($results);
$isfolder = $row['isfolder'];
$alias = $row['alias'];
if ($override && $overrideOption = $modx->getTemplateVarOutput($overrideTV,$documentIdentifier))
{
switch ($overrideOption[$overrideTV])
{
case 0:
$isoverride = 1;
break;
case 1:
$isfolder = 0;
break;
case 2:
$makeFolder = 1;
$isfolder = 1;
}
}
if ($isoverride)
{
$strictURL = preg_replace('/[^\/]+$/', $alias, $modx->makeUrl($documentIdentifier));
}
elseif($isfolder && $makeFolder)
{
$strictURL = preg_replace('/[^\/]+$/', $alias, $modx->makeUrl($documentIdentifier)) . "/";
}
else
{
$strictURL = $modx->makeUrl($documentIdentifier);
}
$myDomain = $myProtocol . "://" . $_SERVER['HTTP_HOST'];
$newURL = $myDomain . $strictURL;
$requestedURL = $myDomain . $parts[0];
if($documentIdentifier == $modx->config['site_start'] && $requestedURL != $modx->config['site_url'])
{
// Force use of root redirect
header("HTTP/1.1 301 Moved Permanently");
$qstring = preg_replace("#(^|&)(q|id)=[^&]+#", '', $parts[1]); // Strip conflicting id/q from query string
if ($qstring) header('Location: ' . $modx->config['site_url'] . '?' . $qstring);
else header('Location: ' . $modx->config['site_url']);
exit;
}
elseif($parts[0] != $strictURL && $documentIdentifier != $modx->config['site_start'])
{
// Force specific page redirect
header("HTTP/1.1 301 Moved Permanently");
$qstring = preg_replace("#(^|&)(q|id)=[^&]+#", '', $parts[1]); // Strip conflicting id/q from query string
if ($qstring) header('Location: ' . $strictURL . '?' . $qstring);
else header('Location: ' . $strictURL);
exit;
}
}
elseif($e->name=='OnWebPagePrerender')
{
if ($editMenuLinks)
{
$o = &$modx->documentOutput; // get a reference of the output
$base_str = basename($modx->makeUrl($modx->config['site_start']));
$o = preg_replace("#/?$base_str#",$modx->config['base_url'],$o);
}
if ($override && $editOverrideLinks)
{
if (!$editMenuLinks) $o = &$modx->documentOutput; // get a reference of the output
$myDomain = $_SERVER['HTTP_HOST'];
$sql = "SELECT tvc.contentid as id, tvc.value as value FROM " . $modx->getFullTableName('site_tmplvars') . " tv ";
$sql .= "INNER JOIN " . $modx->getFullTableName('site_tmplvar_templates') . " tvtpl ON tvtpl.tmplvarid = tv.id ";
$sql .= "LEFT JOIN " . $modx->getFullTableName('site_tmplvar_contentvalues') . " tvc ON tvc.tmplvarid = tv.id ";
$sql .= "LEFT JOIN " . $modx->getFullTableName('site_content') . " sc ON sc.id = tvc.contentid ";
$sql .= "WHERE sc.published = 1 AND tvtpl.templateid = sc.template AND tv.name = '$overrideTV'";
$results = $modx->dbQuery($sql);
while ($row = $modx->fetchRow($results))
{
$overrideURL = basename($modx->makeUrl($row['id']));
switch ($row['value'])
{
case 0:
$o = preg_replace("#(href=\"/?|$myDomain/)$overrideURL#$editOverrideCase", "$1" . str_replace(strrchr($overrideURL, '.'), '', $overrideURL),$o);
break;
case 2:
$o = preg_replace("#(href=\"/?|$myDomain/)$overrideURL#$editOverrideCase", "$1" . str_replace(strrchr($overrideURL, '.'), '/', $overrideURL),$o);
break;
}
}
}
}
else
{
return;
}
?>
The $editOverrideLinks option is still pretty experimental. In the future, it would be worthwhile to write/use a dedicated parser class for possible gains in efficiency as well as proper editing/replacing of relative links (../somedoc.html) and support for nested alias parsing.
@Edit:
I modified the code to use the API for database calls and table name/prefix derivation.
I also added support for query string forwarding on redirects (needed for some snippets, such as QuickEdit to work). The id and q parameters are stripped from the resulting query string upon initial forward.