How to Strip/Remove the SiteID From the URL in Mura CMS

The first step in removing the SiteID from the URL in Mura CMS is to edit the file located at /config/settings.ini.cfm. Find the 'siteidinurls' attribute and set it to read siteidinurls=0. If you don't see this attribute, you might be using an older version of Mura, and you should probably upgrade your install. If for some reason, you cannot upgrade your install, then read this Mura blog posting titled Removing the SiteID from URLs in Mura.

Once this is done, you're usually pretty good to go. However, you can still actually navigate to your pages with the SiteID in the URL. In fact, when you preview your site from the Admin area, it usually includes the SiteID and someone expressed a desire to "fix" this for search engine optimzation (SEO), analytics, etc.

This is actually pretty easy to do by adding a few lines of code to your Mura CMS templates. The easiest thing to do would be to probably just add this to your 'html_head.cfm' file if you use it.


<cfscript>
    hasSiteIDinURL = false;
    if ( not application.configBean.getSiteIDinURLs() ) {
        urlstr = getPageContext().getRequest().getRequestURL();
        idx = listFindNoCase(getPageContext().getRequest().getRequestURL(), event.getSite().getSiteID(), '/');
        if ( idx gt 0 ) {
            hasSiteIDinURL = true;
            urlstr = listDeleteAt(urlstr, idx, '/');
            if ( len(trim(cgi.query_string)) ) {
                urlstr = urlstr & '?' & cgi.query_string;
            };
        };
    };
</cfscript>
<cfif hasSiteIDinURL><cflocation url="#urlstr#" addtoken="false" statuscode="301" /></cfif>

Hope this helps!

Using ColdFusion to Generate Meta Keywords and Description For Mura CMS

As with most content management systems, Mura CMS offers users the ability to enter a "summary" and "keywords" for each page (among several other options). The problem is, many users fail to utilize these fields. So I whipped up a couple of simple custom display objects to address this problem.

The meta keyword generator will read the main "content" of the page, parse it and then dynamically create a unique listing of keywords. I also use a custom list of keywords to delete common keywords such as "a, in, the," etc., which can easily be modified. This makes for good SEO (search engine optimization) too since you're actually generating keywords based on the actual contents of the page! Let's save the following file as "[siteid]/includes/display_objects/custom/dspMetaKeywordGenerator.cfm"


<cfsilent>
<!---

    Document:        dspMetaKeywordGenerator.cfm
    Version:        20091023.01
    Author:            Stephen Withington | www.stephenwithington.com
    
    Purpose:        I generate meta keywords based on the body content.
    
    Instructions:    Assuming you save this file to:
                    "[siteid]/includes/display_objects/custom/dspMetaKeywordGenerator.cfm"
                    
                    Locate your current meta keywords tag and replace with the following code:
        
                    <cfif len(trim(event.getContentRenderer().getMetaKeywords()))>
                        <meta name="keywords" content="#HTMLEditFormat(event.getContentRenderer().getMetaKeywords())#" />
                        <cfelse>
                        #event.getContentRenderer().dspInclude('display_objects/custom/dspMetaKeywordGenerator.cfm')#
                    </cfif>
                    
                    Typically, the meta keywords tag is found in this file:
                    "[siteid]/includes/themes/[themeName (i.e., "merced")]/templates/inc/html_head.cfm"
                    
    Notes:            Currently ONLY reads the body content and does not take any other content
                    into consideration. (i.e., content objects, etc.)

                    META KEYWORD GENERATOR PROCESS
                    Take the body content then:
                    1.    remove html
                    2.    remove puncuation
                    3.    remove whitespace, line breaks and convert to a list
                    4.    remove duplicate words
                    5.    remove common words (i.e., a, an, the, etc.)
                        - list can be modified in the variable "commonWords" below.
                    6.    sort the list (not necessary, but useful)
    
    Revision Log:    
    20091005.01 - sjw - first draft.
    20091023.01 - sjw - updated mura methods to reflect current best practice: event.getContentRenderer() and event.getContentBean() vs. renderer and request.contentbean, etc.

--->


<!--- LIST OF COMMON WORDS TO BE REMOVED --->
<cfset commonWords = "a, also, an, and, and, are, as, at, be, but, by, for, from, had, have, he, his, i, in, is, it, of, on, or, that, the, they, this, to, too, was, what, who, with" />

<!--- GRAB THE BODY CONTENT --->
<cfset str = event.getContentBean().getBody() />

<!--- helper functions from CFLib.org --->
<cfscript>
/**
* Case-insensitive function for removing duplicate entries in a list.
* Based on dedupe by Raymond Camden
*
* @param list List to be modified. (Required)
* @return Returns a list.
* @author Jeff Howden (cflib@jeffhowden.com)
* @version 1, July 2, 2008
*/

function listDeleteDuplicatesNoCase(list) {
var i = 1;
var delimiter = ',';
var returnValue = '';
if(ArrayLen(arguments) GTE 2)
    delimiter = arguments[2];
    list = ListToArray(list, delimiter);
    for(i = 1; i LTE ArrayLen(list); i = i + 1)
    if(NOT ListFindNoCase(returnValue, list[i], delimiter))
        returnValue = ListAppend(returnValue, list[i], delimiter);
    return returnValue;
}

/**
* Delete items from a list.
*
* @param variable An item, or a list of items, to remove from the list. (Required)
* @param qs The actual list to parse. Can be blank. (Optional)
* @return Returns a string.
* @author Alessandro Chisari (ruchizzy@hotmail.com)
* @version 1, May 17, 2006
*/

function listdelete(variable){
//var to hold the final string
var string = "";
//vars for use in the loop, so we don't have to evaluate lists and arrays more than once
var ii = 1;
var thisVar = "";
var thisIndex = "";
var array = "";
var qs = "";
if(arrayLen(arguments) GT 1)
qs = arguments[2];
//put the query string into an array for easier looping
array = listToArray(qs,",");
//now, loop over the array and rebuild the string
for(ii = 1; ii lte arrayLen(array); ii = ii + 1){
thisIndex = array[ii];
thisVar = thisIndex;
//if this is the var, edit it to the value, otherwise, just append
if(not listFindnocase(variable,thisVar))
string = listAppend(string,thisIndex,",");
}
//return the string
return string;
}
</cfscript>

<cfif len(trim(str))>
    <!--- 1. remove html (using built-in mura method) --->
    <cfset str = event.getContentRenderer().stripHTML(str) />
    <!--- 2. remove punctuation (this regex ain't pretty, but it works) --->
    <cfset str = REReplace(str, "[;\\/:""*?<>|\!\+\-\=\.`\##\&_\(\)\[\]\%\^\$\@~\',\{\}]+", "", "ALL") />

    <!--- 3. remove whitespace, line breaks and convert to a list --->
    <cfset str = REReplace(str, "\s|\r?\n", ",", "ALL") />
    <!--- 4. remove duplicate words --->
    <cfset str = listDeleteDuplicatesNoCase(str) />
    <!--- 5. remove common words --->    
    <cfset str = listdelete(commonWords,str) />
    <!--- 6. sort the list --->
    <cfset str = listSort(lcase(str), "text") />
</cfif>

</cfsilent>
<cfoutput><meta name="keywords" content="#str#" /></cfoutput>

The meta description generator will also read the main "content" of the page and grab the first twenty-five (25) words to automatically create the meta description. This also makes for good SEO since the description matches what's actually found on the page. Let's save the following file at "[siteid]/includes/display_objects/custom/dspMetaDescriptionGenerator.cfm"


<cfsilent>
<!---

    Document:        dspMetaDescriptionGenerator.cfm
    Version:        20091023.01
    Author:            Stephen Withington | www.stephenwithington.com
    
    Purpose:        I generate a meta description based on the body content.
    
    Instructions:    Assuming you save this file to:
                    "[siteid]/includes/display_objects/custom/dspMetaDescriptionGenerator.cfm"
                    
                    Locate your current meta description tag and replace with the following code:
        
                    <cfif len(trim(event.getContentRenderer().getMetaDesc()))>
                        <meta name="description" content="#HTMLEditFormat(event.getContentRenderer().getMetaDesc())#" />
                        <cfelse>
                        #event.getContentRenderer().dspInclude('display_objects/custom/dspMetaDescriptionGenerator.cfm')#
                    </cfif>

                    Typically, the meta description tag is found in this file:
                    "[siteid]/includes/themes/[themeName (i.e., "merced")]/templates/inc/html_head.cfm"
                    
    Notes:            Currently ONLY reads the body content and does not take any other content
                    into consideration. (i.e., content objects, etc.)

                    META DESCRIPTION GENERATOR PROCESS
                    Take the body content then:
                    1.    remove html
                    2.    remove whitespace, line breaks
                    3.    since Google will cut off anything more than
                        155(roughly) characters, let's limit this to the first 25 words.
    
    Props:            Ben Nadel's Post "Displaying A Blog Teaser":
                    http://www.bennadel.com/index.cfm?dax=blog:1718.view
                    John Whish's (www.aliaspooryorik.com/blog/) comments on Ben's Post above.
    
    Revision Log:    
    20091005.01 - sjw - first draft.
    20091023.01 - sjw - updated mura methods to reflect current best practice: event.getContentRenderer() and event.getContentBean() vs. renderer and request.contentbean, etc.
    
--->


<!--- GRAB THE BODY CONTENT --->
<cfset str = event.getContentBean().getbody() />

<cfif len(trim(str))>
    <!--- 1. remove html (using built-in mura method) --->
    <cfset str = event.getContentRenderer().stripHTML(str) />
    <!--- 2. remove whitespace, line breaks and replace with a space between each word --->
    <cfset str = REReplace(str, "[\s|\r?\n]+", " ", "ALL") />    
    <!--- 3. limit to the first 25 words --->
    <cfset javaArray = CreateObject("java","java.util.Arrays") />
    <cfset wordArray = javaArray.copyOf(str.Split( " " ), 26) />
    <cfset str = ArrayToList(wordArray, " ") />
</cfif>

</cfsilent>
<cfoutput><meta name="description" content="#trim(str)#" /></cfoutput>

How To Use

Since there may be users who actually do use the description and/or keywords fields in the admin area of Mura CMS, you want to be sure to check for that first. So, all we need to do is locate where the meta tags are being displayed at, and then replace with some simple code. In most installations, the meta tags can be found in this file "[siteid]/includes/themes/[themeName (i.e., "merced")]/templates/inc/html_head.cfm."


<cfif len(trim(event.getContentRenderer().getMetaDesc()))>
    <meta name="description" content="#HTMLEditFormat(event.getContentRenderer().getMetaDesc())#" />
    <cfelse>
    #event.getContentRenderer().dspInclude('display_objects/custom/dspMetaDescriptionGenerator.cfm')#
</cfif>

<cfif len(trim(event.getContentRenderer().getMetaKeywords()))>
    <meta name="keywords" content="#HTMLEditFormat(event.getContentRenderer().getMetaKeywords())#" />
    <cfelse>
    #event.getContentRenderer().dspInclude('display_objects/custom/dspMetaKeywordGenerator.cfm')#
</cfif>

Hope this helps!

Using ColdFusion to Handle HTTP 404 Page Not Found Errors

This article will not address the onMissingTemplate method which only runs when a CFML page does not exist (i.e., wwwroot/directory/thisPageDoesNotExist.cfm). In addition, this article will not address using custom headers or a .htaccess file with Apache. This article will address those other "page not found" errors often found in Windows hosted environments.

Let's start by assuming someone tries to visit www.yourdomain.com/thisDirectoryDoesNotExist, or www.yourdomain.com/thisPageDoesNotExist.html and neither the directory, nor the page actually exist on your site. Or, better yet, you've just spent months building a new, dynamic site for a client using ColdFusion and their old site was an old, yucky plain HTML site with pages everywhere. Now, your client, who's a little savvy in the SEO department, (that's search engine optimization for you non-seo-knowing folk), has requested you setup redirects for each of his old pages to the new ones you've created.

If you're anything like me, I used to dread the thought of taking care of this. I used to spend a ton of time on this by creating a separate page for each of the "old" pages and directories, then using either JavaScript to handle the redirects (if the old page was vanilla HTML) or the "old" dynamic language (i.e., ASP, PHP, etc.) to create custom headers, etc. Regardless, it was a major pain to do this, especially because it cluttered my site with a bunch of extra files that were essentially useless.

Then, I had one of those "light bulb moments." I thought, "Hey, I'm already using a custom tag to handle 'page not found' requests, so if I could somehow capture the requested page, match it against a list of 'known-to-be-missing' pages, then I could code a way to forward to the 'new' destination." Sounds simple enough, eh?

For those of us using a shared-host provider such as CrystalTech or HostMySite, they're kind enough to allow us to create custom pages to handle certain HTTP errors, such as 404. It's usually found in the service provider's "control panel" under something like "IIS > Custom Error Pages." Once there, you usually have three options to pick from: 1) Default, 2) File and 3) URL. In most cases, "Default" loads the standard error page provided by our friends at Microsoft. "File" is just that, a flat, static, HTML page. What we want to use is "URL," so that we can use ColdFusion to help us out. In fact, I'll be nice and even provide a link to both CrystalTech's process and HostMySite's process. But before you do this, you'll need to at least set up a .CFM file somewhere on your site so you know what to enter into the URL path, right? So for now, let's assume you've created a file call "404.cfm" and placed in a directory structure like so: "/extensions/customtags/404.cfm"

So, assuming you've got your custom error page setup properly, you will now be able to capture a query string which contains the "requested URL" that really didn't exist. The easiest way to test this would be to enter this code onto your 404.cfm file, upload it to your site, then try a non-existent directory such as www.yourdomain.com/abcd/.


<cfset request.queryString = getPageContext().getRequest().getQueryString() />
<cfoutput>#request.queryString#</cfoutput>
<cfabort />

You should see something like this: "404;http://www.yourdomain.com:80/abcd/"

There's some great information coming through here. First, you'll notice that you're receiving the error number (404). Next, you'll see your domain also includes the port number being used. For example, if using HTTP, then you will most likely see the number 80, or if your using HTTPS, then you should probably see 443.

For this next part, you might have to adjust this if you ever use a colon ( : ) in your directory or page naming conventions (this is not generally used, so I wouldn't expect this to be much of an issue).

Look again at the query string that we've been given, what we really want to grab is everything after the port number. Or, the absolute path to the page or directory being requested. So this is what I came up with:


<!--- perform some manipulations to the 'requested url' to get at the actual request --->
<cfif isDefined("request.queryString")>
    <cfset requestedPage = listlast(request.queryString, ":") />
</cfif>
<cfif len(trim(requestedPage))>
    <!--- after removing everything before the port, we now need to remove the port number --->
    <cfset requestedPage = listdeleteat(requestedPage, "1", "/") />
    <!--- quick fix to create an absolute path from the site root to the requested page --->
    <cfset requestedPage = "/" & requestedPage />
</cfif>
<cfoutput>#requestedPage#</cfoutput>
<cfabort />

So, now if we use the same url we tried earlier, I should see something like: "/abcd/" Now, I've got a variable that I can use to match against to see if this is a "known" directory or page from our old site. Now we just need something to handle the "matching" and then receive a response to make decisions against.

Luckily, I've created a rather simple .CFC which can easily be modified to accommodate any "known" directories and/or pages, including .ASP, .PHP, etc.


<!------------------------------------------------------------------------------------------------------------

    Document:        /extensions/components/redirect.cfc
    Author:            Steve Withington
    Creation Date:    12/30/2008
    Copyright:        (c) 2008 Stephen J. Withington, Jr. | www.stephenwithington.com
    
    Purpose:        Handles redirects of old pages to new ones.

    METHODS/VAR:    1) getLocation() / newLocation
    
    Revision Log:    
    MM/DD/YYYY - sjw - comments.

------------------------------------------------------------------------------------------------------------->

<cfcomponent displayname="Handles redirects" hint="Pass me an old location, I'll give you the new location." output="no">

    <!---    1) getLocation()    --->
    <cffunction name="getLocation"
                    displayname="Get a new location for old links."
                    access="public"
                    returntype="string"
                    output="no">

    
        <cfargument name="oldLocation" required="false" default="" />
    
        <cfset var newLocation = "" />
    
        <cfswitch expression="#arguments.oldLocation#">

            <cfcase value="/index.html">
                <cfset newLocation = "/index.cfm" />
            </cfcase>

            <cfcase value="/about/news/default.php,/about/news/,/about/news">
                <cfset newLocation = "/news/index.cfm" />
            </cfcase>

            <cfcase value="/contact-us.asp">
                <cfset newLocation = "/contact/index.cfm" />
            </cfcase>

            <cfdefaultcase>
                <cfset newLocation = "" />
            </cfdefaultcase>

        </cfswitch>
    
        <cfreturn newLocation />
    
    </cffunction>

</cfcomponent>

Now, we just need to invoke the ColdFusion Component to see if there's a match.


<!--- check to see if the page requested matches any 'known' pages from the old site --->
<cftry>

    <cfinvoke component="extensions.components.redirect" method="getLocation" returnvariable="newLocation">

        <cfif isDefined("requestedPage") and len(trim(requestedPage))>
            <cfinvokeargument name="oldLocation" value="#requestedPage#" />
        </cfif>

    </cfinvoke>

    <cfif isDefined("newLocation") and len(trim(newLocation))>
        <cfset newPage = newLocation />
    </cfif>

    <cfcatch>
        <cfset newPage = "" />
    </cfcatch>

</cftry>

As you can see, if no match has been found, a variable called "newPage" is merely left blank. So, now we can write a little more code to accommodate both "known-to-be-missing" and "truly-unknown-and-really-missing" directories and pages. If a page is "known-to-be-missing", I can pass a search engine friendly header so that everything runs as smoothly as possible.

Here's the final document, aside from the .CFC above which should be kept separate.


<cfsilent>
<!------------------------------------------------------------------------------------------------------------

    Document:        /extensions/customtags/404.cfm
    Author:            Steve Withington
    Creation Date:    12/30/2008
    Copyright:        (c) 2008 Stephen J. Withington, Jr. | www.stephenwithington.com
    
    Purpose:        Handles requests for missing pages (HTTP 404).

    Notes:            Test this set up before using in a live environment. The 404 custom error page needs to be
                    set up in IIS (or via control panel in a third-party hosted environment).
    
    Revision Log:    
    MM/DD/YYYY - sjw - comments.

------------------------------------------------------------------------------------------------------------->

<!--- scope local variables --->
<cfparam name="requestedPage" default="" />
<cfparam name="newPage" default="" />
<!--- use a little of the underlying java to grab the queryString --->
<cfset request.queryString = getPageContext().getRequest().getQueryString() />
<!--- perform some manipulations to the 'requested url' to get at the actual request --->
<cfif isDefined("request.querystring")>
    <cfset requestedPage = listlast(request.queryString, ":") />
</cfif>
<cfif len(trim(requestedPage))>
    <!--- after removing everything before the port, we now need to remove the port number --->
    <cfset requestedPage = listdeleteat(requestedPage, "1", "/") />
    <!--- quick fix to create an absolute path from the site root to the requested page --->
    <cfset requestedPage = "/" & requestedPage />
</cfif>
<!--- check to see if the page requested matches any 'known' pages from the old site --->
<cftry>
    <cfinvoke component="extensions.components.redirect" method="getLocation" returnvariable="newLocation">
    <cfif isDefined("requestedPage") and len(trim(requestedPage))>
        <cfinvokeargument name="oldLocation" value="#requestedPage#" />
    </cfif>
    </cfinvoke>
    <cfif isDefined("newLocation") and len(trim(newLocation))>
        <cfset newPage = newLocation />
    </cfif>
    <cfcatch>
        <cfset newPage = "" />
    </cfcatch>
</cftry>
</cfsilent>
<cfif isDefined("newPage") and len(trim(newPage))>
<!--- requested page is a known 'old page' from prior site --->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<cfheader statuscode="301" statustext="Moved permanently" />
<cfheader name="Location" value="http://www.yourDomainHere.com#newPage#" />
<title>This page has moved to http://www.yourDomainHere.com<cfoutput>#newPage#</cfoutput></title>
</head>
<body>
This page has moved to <cfoutput><a href="http://www.yourDomainHere.com#newPage#">http://www.yourDomainHere.com#newPage#</a></cfoutput>
</body>
</html>
<cfelse>
<!--- requested page is NOT a known 'old page' from prior site and doesn't exist --->
<cf_layout     title="Hmm, the page you're looking for can't be found."
                keywords="404,page,not,found"
                description="Hmm, the page you're looking for can't be found. You may have clicked a bad link or mistyped the web address.">

    <h1>Hmm, the page you're looking for can't be found.</h1>
    <p>You may have clicked a bad link or mistyped the web address.</p>
    <ul>
        <li><a href="/">Return home</a></li>
        <li><a href="javascript:history.back();">Go back to the previous page</a></li>
    </ul>
</cf_layout>
</cfif>

As you can see, I like to use a custom tag to handle the layout of my pages, etc. You can find out more about that at Raymond Camden's site.

I hope this helps a fellow ColdFusion developer. Thanks for reading!

More Entries

© 2021, Stephen J. Withington, Jr.  |  BlogCFC was created by Raymond Camden – Version 5.9.004

Creative Commons License  |  This work is licensed under a Creative Commons Attribution 3.0 Unported License.  |  Hosted by Hostek.com