Getting the full file path of a Coldfusion Component in dot-notation

April 11th, 2010 - by Matthew Cook

When you are creating an instance of a cfc using the <cfobject> tag or createObject() method you need to specify the path to the desired component via a dot-notation file path.  All-in-all this is little more than a file path where the back-slashes are replaced with periods.  However, these paths are not full local paths like when using an absolute path for an image for example.  So the file path will not begin with ‘/Applications’ (if on a mac) or ‘C:/’ (if on windows).  Instead the path begins in the server root, which for Coldfusion is the ‘wwwroot’ directory.

If you are running a package of cfcs or are creating instances from different parts of your application I would recommend creating a mapping to the package or directory containing the cfc(s).  This will save you some work in the future if, say, the application directory structure changes.  You can get the full dot-notation path by introspecting the cfc you are creating an instance of.  I’ve created a short video showing a few ways of getting this file path pretty quickly.  Watch the video.

Stripping out HTML tags from text with Coldfusion 9

April 7th, 2010 - by Matthew Cook

I’ve built a fairly simple UDF for stripping out HTML from a chunk of text. I’ll quickly explain my logic and what this UDF can do.

First, there are two types of tags in HTML. One is a wrapping tag. An example of this is the bold tag.

<b>Some content</b>

Next is a self-contained tag.

<img src="my/path/image.jpg" />

If the tag is a wrapping tag we need to make the decision of whether or not to delete the content the tag is wrapping. For example the content inside a bold tag we will most likely want to keep. While the content inside a script tag we will want to delete.

When it comes right down to it, we need to make three decisions about the HTML being deleted.

  1. What tag needs to be deleted.
  2. Is this a wrapping tag.
  3. If a wrapping tag, should the content be deleted.

So let’s take a look at the UDF.

<cfcomponent displayname="Strip HTML" hint="Strips out of a set of given text any specified html tags." output="false">

	<!--- ************************  STRIP HTML  ************************ --->
	<cffunction name="stripHtml" displayname="Strip HTML" description="Strips out specified HTML tags." access="public" output="false" returntype="struct">

		<!--- ARGUMENTS --->
		<cfargument name="text" displayName="Text" type="string" hint="Text to strip out html tags from." required="true" />
		<cfargument name="tags" displayName="Tags" type="string" hint="Tags to be striped from the text.  Ex. '[string:tag name],[what to remove - {string:tag | string:content}],[is it a wrapping tag? {boolean}]'. Tags are delimited with semi-colons." required="true" />

		<!--- SET SOME LOCAL VARS --->
		<cfset var textbytes = "">
		<cfset var counter = 1>
		<cfset var delete = false>
		<cfset var temp = "">
		<cfset var tagtoberemoved = "">
		<cfset var whatgetsremoved = "">
		<cfset var wrappingtag = "">

		<!--- BUILD STRUCT --->
		<cfset var data = structNew()>
		<cfset data.success = true>
		<cfset data.message = "">
		<cfset data.orginaltext = ARGUMENTS.text>
		<cfset data.strippedtext = ARGUMENTS.text>

		<!--- CHECK IF ALL CONTENT SHOULD BE REMOVED --->
		<cfif ARGUMENTS.tags eq "all">
			<!--- REMOVE HTML TAGS --->
			<cfset data.strippedtext = rereplaceNoCase(ARGUMENTS.text, "<[^>]*>", "", "all")>
		<cfelse>
			<!--- LOOP OVER THE LIST OF TAGS TO BE REMOVED --->
			<cfloop list="#ARGUMENTS.tags#" index="VARIABLES.i" delimiters=";">
				<!--- SET ATTRIBUTES OF TAG TO BE DELETED --->
				<cfset tagtoberemoved = listFirst(VARIABLES.i, ",")>
				<cfset whatgetsremoved = listGetAt(VARIABLES.i, 2, ",")>
				<cfset wrappingtag = listLast(VARIABLES.i, ",")>

				<!--- IF REMOVING JUST THE TAG --->
				<cfif whatgetsremoved eq "tag">
					<!--- CHECK IF IT IS A WRAPPING TAG --->
					<cfif wrappingtag eq true>
						<!--- REMOVE WRAPPING TAG, BUT NOT THE CONTENT --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved#>", "", "all")>
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "</#tagtoberemoved#>", "", "all")>
					<cfelse>
						<!--- REMOVE CONTAINED TAG --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved# />", "", "all")>
					</cfif>

				<!--- IF REMOVING TAG AND CONTENT --->
				<cfelseif whatgetsremoved eq "content">
					<!--- CHECK IF IT IS A WRAPPING TAG --->
					<cfif wrappingtag eq true>
						<!--- REMOVE THE TAG AND CONTENT --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved#>.*</#tagtoberemoved#>", "", "all")>
					<cfelse>
						<!--- REMOVE CONTAINED TAG --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved# />", "", "all")>
					</cfif>
				</cfif>
			</cfloop>
		</cfif>

		<!--- RETURN STRUCT --->
		<cfreturn data>

	</cffunction>

</cfcomponent>

The method takes two arguments. First is the chunk of text to have HTML stripped out of. Second is information about the HTML to strip out of the text. The latter argument accepts a string. Multiple tags can be specified by separating them with semi-colons. For each tag that needs deleted three pieces of information needs to be provided. Each of the three pieces are delimited with a comma. These are the three arguments listed above. First is the name of the tag. So if we are wanting to remove an img tag we would specify “img”, for bold “b”. Second, we specify if we want just the tag removed or the tag and the content it wraps. There are two acceptable strings here – “tag” (for just removing the tag) and “content” (for removing the tag and the content). Lastly, specify whether this is a wrapping or contained tag. For an image tag we would specify “false” because it is not a wrapping tag. For bold we would specify “true” because it is a wrapping tag.

One last note, if you want to strip out all HTML tags you can just pass in the string “all” to the method. But note that this will just strip out the HTML and any content being wrapped by the tags will be left alone.

So let’s look at an example of how to call this tag.

<!--- TEXT TO STRIP HTML FROM --->
<cfset VARIABLES.text = "Lorem <img />ipsum <b>dolor</b> sit amet, <em>consectetur</em> adipiscing elit.>

<!--- STRIP OUT ALL HTML --->
<cfset stripHtml(VARIABLES.text, "all")>

<!--- STRIP OUT IMG, B, AND EM TAGS --->
<cfset stripHtml(VARIABLES.text, "img,tag,false;b,tag,true;em,content,true")>

Feel free to copy this and use it as you please.

Directory Watcher Gateway for file management logging

April 4th, 2010 - by Matthew Cook

I wanted to play with gateways a little bit in Coldfusion 9 this weekend, but wanted to build something at least slightly practical.  What I ended up with is  a gateway that keeps up with file additions, changes, and deletions for a given project and logs those changes to a file.  This was surprisingly easy to accomplish with the directory-watcher gateway that ships with Coldfusion.  All in all this is a tool I may implement for development purposes just to have a running log of file changes that occur within a given project over the given amount of time.

So let’s get this ball rolling by creating a couple projects under the ‘wwwroot’ folder in our Coldfusion9 directory.  If you are still using Coldfusion 8 I don’t see any reason why this won’t work for you as well.  I haven’t tested it though.  For me, I named the first project ‘my_site’ and the second ‘my_othersite’.  Now set up two projects in Coldfusion Builder that respectively points to these two directories.

Now we’ll create a set of directories in each of these projects.  Under the project you should create a directory called ‘gateways’.  Under that directory create another directory called ‘cfcs’ and another one called ‘config’.  Note that this is how that I’ve chosen to do this.  You can set up your file structure however you want.  The main thing to note is that you will need the configuration file for the directory-watcher gateway in your project.  This way when you create your instance of the gateway in the Coldfusion Administrator you can point it to this specific project.  You will then be able to set a new configuration file in another project (which we’ll do) allowing you to watch multiple directories at once.  Inside of the ‘cfcs’ directory you can create a component called ‘directory_watcher.cfc’, and under the ‘config’ directory create a file called ‘directory-watcher.cfg’.  So your file structure should look like the following.

File Structure

File Structure

Now we’ll set up the directory-watcher.cfg file.  Under your ‘ColdFusion9′ directory navigate to ‘gateway/config’ and open the ‘directory-watcher.cfg’ file.  Copy the contents to your file in the my_site project.  Note: you could also just copy the entire file to your ‘config’ directory if you like.  The contents will look like the following.

directory-watcher.cfg

#
# DirectoryWatcherGateway configuration file
#

# The directory you want to watch.  If you are entering a Windows path
# either use forward slashes (C:/mydir) or escape the back slashes (C:\\mydir).
directory=

# Should we watch the directory and all subdirectories too
# Default is no.  Set to 'yes' to do the recursion.
recurse=no

# The interval between checks, in miliseconds
# Default is 60 seconds
interval=60000

# The comma separated list of extensions to match.
# Default is * - all files
extensions=*

# CFC Function for file Change events
# Default is onChange, set to nothing if you don't want to see these events
changeFunction=onChange

# CFC Function for file Add events
# Default is onAdd, set to nothing if you don't want to see these events
addFunction=onAdd

# CFC Function for file Delete events
# Default is onDelete, set to nothing if you don't want to see these events
deleteFunction=onDelete

We’re going to make just a couple changes to this file.  First and foremost we need to add the directory for the gateway to watch.  This is the my_site directory we created under the ‘wwwroot’ directory.  I’m on a mac so I would write ‘/Applications/ColdFusion9/wwwroot/my_site’ after the ‘directory=’ at the beginning of the file.  Next we need to set ‘recurse=no’ to ‘recurse=yes’.  This will allow our gateway to watch this directory and all directories beneath it.  Lastly, just so we can see our changes more quickly we can set the interval to ‘10000′ milliseconds.  By default it runs every minute.  So our new file will look like:

Read the rest of this entry »

Hiking the George B. Parker Trail in RI

April 4th, 2010 - by Matthew Cook

Hi all, just a few photos from the hiking trip Adri and I took to the George B. Parker Refuge here in RI.  Enjoy.

Deleting from multiple SQL tables with a single query

March 31st, 2010 - by Matthew Cook

I’m by no means an expert using SQL, but as of yet I’ve found no way to delete data from multiple tables without writing a single query specifically for each table that data needs deleted from. So if you’re reading this expecting a native SQL statement that will delete data from multiple tables then I have nothing to offer. However, I do have a solution. One which requires a programming language, Coldfusion in my case.

The idea is that we have a number of tables.  Lets say we have five tables holding data about products.  So we’ll have tables named ‘products_tbl’, ‘products_name_tbl’, ‘products_description_tbl’.  Using just SQL we would need to write three delete statements like:

Straight SQL

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_tbl
	WHERE product_id = 9
</cfquery>

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_name_tbl
	WHERE product_id = 9
</cfquery>

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_description_tbl
	WHERE product_id = 9
</cfquery>

Now let’s say that halfway through the project the client suddenly wants the price to be included. Really not a big deal, but when we now go to delete the product we realize that we need to go find our component or whatever that is handling the deleting transactions and add a new delete statement.

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_price_tbl
	WHERE product_id = 9
</cfquery>

I personally hate this, and like most things the more complicated the data becomes the more burdensome it gets on the developer. So what’s the solution?

The idea is that we need to bundle all of these related sets of data into a group. When I say related I simply mean that in this case all the tables containing the parts and pieces of a product. The product is an object, and when that object is deleted we need to visit each of the tables containing information for that product and get rid of it. If we could simply group each of the table names we could run a loop with a delete statement inside where the table name dynamically changes with each loop. This would allow us to update our group and not have to worry about updating any actual SQL.

In my case, I’ve created the group under the APPLICATION scope. This let’s me access the group from anywhere within my application. As such my Application file would contain inside the onApplicationStart() method, for this case, the following.

<cfparam name="APPLICATION.producttables.productTbl" type="string" default="product_tbl">
<cfparam name="APPLICATION.producttables.productNameTbl" type="string" default="product_name_tbl">
<cfparam name="APPLICATION.producttables.productDescriptionTbl" type="string" default="product_description_tbl">

Now that we have our table names stored in the APPLICATION scope under a group called ‘producttables’ we can build a function that will get all the data in the ‘producttables’ group and set it into an array. We can then loop over this array of table names deleting from each the product data associated with the specified product id. In this case our function would look like.

<cfcomponent displayname="Looping Delete" hint="Loops over a portion of a struct with table names and deletes from those tables." output="false">

	<cffunction name="deleteProduct" displayname="Delete Product" description="Deletes a product." access="public" output="false" returntype="void">

		<!--- PRODUCT ID --->
		<cfargument name="productid" displayname="Product ID" hint="Id of the product to be deleted." type="numeric" required="true" />

		<!--- CREATE AN ARRAY OF TABLE NAMES STORED IN APPLICATION SCOPE --->
		<cfset var tableArray = structkeyarray(APPLICATION.producttables)>

		<!--- LOOP OVER THE ARRAY --->
		<cfloop array="#tblArray#" index="VARIABLES.i">
			<cfquery name="deleteproduct" datasource="#APPLICATION.dataSource#">
				DELETE
				FROM schema_name.#APPLICATION.producttables[VARIABLES.i]#
				WHERE product_id = <cfqueryparam value="#ARGUMENTS.productid#" cfsqltype="cf_sql_integer" maxlength="10">
			</cfquery>
		</cfloop>

	</cffunction>

</cfcomponent>

So what we’ve done here is made it much easier to add data to the product without ever having to touch our component. Instead all we really need to do is add the data to the APPLICATION scope to the producttables group. So for instance now if we need to add a price to our product all we have to do is go into the Application.cfc file and set

<cfparam name="APPLICATION.producttables.productPriceTbl" type="string" default="product_price_tbl">

That’s all that we need now to make the update. The loop will now find this table in our group and delete from it the same as the others.

Cflayout first tab does not show content bug

March 29th, 2010 - by Matthew Cook

Another issue I ran into when converting some older Coldfusion 8 applications over to a Coldfusion 9 server concerning tabs using the tag. Unlike getDataSource() this issue is a Coldfusion 9 bug. The issue is that if you create a tab layout the first tab does not have an auto-height property. That being said, no matter how much content you put inside the region it will never expand to show the content.

MrTee posted on the Coldfusion 9 docs both the problem and the solution. This is not my solution, I’m just spreading the word here. The link to his comment is here. He has also posted the correct javascript file here.

The problem needs to be corrected in the layout.js file. If you are in your wwwroot folder look under CFIDE/scripts/ajax/package directory. Once you have this file open you can make the changes as expressed by MrTee himself.

The change I made is on line 70 and changed from

var _70=new Ext.Panel({title:_6a,contentEl:_68,_cf_body:_68,id:_69,closable:_6c,tabTip:_6b, autoScroll:_6f,autoShow:true});

to

var _70=new Ext.Panel({title:_6a,contentEl:_68,_cf_body:_68,id:_69,closable:_6c,tabTip:_6b, autoScroll:_6f,autoShow:true,autoHeight:true});

I hope this helps anyone with the same problem out.

All in all an ‘autoHeight:true’ needs added to the Panel() method. As far as I know this has not been addressed in an update by Adobe, but has been acknowledged. I used this solution and it worked perfect with no known side-effects on my own applications.

Coldfusion 9 getDataSource() now getStore()

March 29th, 2010 - by Matthew Cook

This doesn’t directly pertain to Coldfusion 9, but to the ExtJs library.  That being said, Coldfusion 8 runs off of an older version of the ExtJs library while Coldfusion 9 updated to the latest version 3 of the library.  If you are converting any Coldfusion 8 applications over to a Coldfusion 9 server keep in mind that your use of the Coldfusion AJAX components will no longer work for any components that looked for the underlying datasource.  So, for example, if you had a Coldfusion 8 grid implemented using the <cfgrid> tag and for whatever reason needed to grab some of the underlying ExtJs functionality of that grid by grabbing the grid datasource, you will need to make the update in your javascript.  Let’s pretend we have a grid named ‘mygrid’.  The Coldfusion 8 javascript code probably looked something like the following.

Coldfusion 8

//get our grid object
var mygrid = ColdFusion.Grid.getGridObject("mygrid");

//get the datasource of our grid
var mygrid_ds = mygrid.getDataSource();

//get data when the grids load
mygrid_ds.on("load", get_my_grid_data);

Once you move that code to the Coldfusion 9 server the ‘getDatasource()’ method will need to be changed to ‘getStore()’. So our new javascript would look like the following.

Coldfusion 9

//get our grid object
var mygrid = ColdFusion.Grid.getGridObject("mygrid");

//get the datasource of our grid
var mygrid_ds = mygrid.getStore();

//get data when the grids load
mygrid_ds.on("load", get_my_grid_data);

No idea why this change was made, but certainly no reason to panic. Simple fix.

MySQL Workbench doesn’t have Query Browser or Administrator in 5.1

March 19th, 2010 - by Matthew Cook

If you’re lazy like me and prefer to work in the mysql Administrator and Query Browser as opposed to the command prompt, you may be surprised if you go to the website now to download the GUI tools.  They have changed now to a single application called the Workbench.  This is great and a welcome change in my opinion.  The only problem I found was that they have removed the old GUI tools from the downloads page and have replaced it with the current stable release (5.1) of the Workbench.  This is fine except that the current stable release only contains the new data visualizer.  If you want the Workbench that contains the data visualizer and the Query Browser and Administrator you need to download the beta 5.2.  Unfortunately the beta is not readily available under the downloads page of the main site.  As such, the link to the download at the time of this writing is http://dev.mysql.com/downloads/workbench/.  So if you need the Query Browser and the Administrator you’ll need to download this version, or a later version depending on what is available at the time of your reading.

Tags

Entity has incorrect type for being called as a function in Coldfusion 8

March 17th, 2010 - by Matthew Cook

There’s some stuff on the web about this error in Coldfusion that I found helpful, but because it is such a useless error in the sense of telling you what the problem is I figured I would post my specific problem and solution.

In my case I was running a primary public function that called a few other private functions. The gist of my problem was that I had an argument in my primary public function with the name ‘generatepassword’. One of my private functions was also named ‘generatepassword’. When the private function was being called I suppose it wasn’t able to determine whether the argument or the method was to be called, and hence threw the error that really gives no explanation of this whatsoever.

I suppose that the ‘incorrect type’ is the argument that it was trying to call as a function. Who knows. Whatever the error may say, that was the problem. My search through google turned up similar cases. If you find yourself having this problem check your variable names and make sure that none of them conflict with your method names.

Random Password Generater UDF

March 16th, 2010 - by Matthew Cook

I realize that the web is full of password generators, but thought I’d add to the clutter with my own. It’s fairly simple with a few user-defined options for how the password is generated. Also you’ll need Coldfusion to run this method. Let’s take a quick look at the method, and as always feel free to use the method. Just drop it into a cfc, or on the page if that’s what fits your application best, call it with any special instructions via the arguments, and it will return back a struct with the appropriate data.

password_generator.cfc

<cfcomponent displayname="Generate Password" hint="Generates a random password." output="false">

	<!--- ************************  GENERATE PASSWORD  ************************ --->
	<cffunction name="generatePassword" displayname="Generate Password" description="Generates a random password." access="public" output="false" returntype="struct">

		<!--- ARGUMENTS --->
		<cfargument name="pwdlength" displayName="Password Length" type="numeric" default="10" required="false" />
		<cfargument name="includenumbers" displayName="Include Numbers" type="boolean" hint="Whether or not to include numbers in password." default="true" required="false" />
		<cfargument name="includeuppercase" displayname="Include Upper Case" type="boolean" hint="Whether or not to include uppercase characters." default="false" required="false" />
		<cfargument name="hashpwd" displayname="Hash Password" type="boolean" hint="Whether or not a hashed version of the password should be returned." default="false" required="false" />
		<cfargument name="hashtype" displayname="Hash Type" type="string" hint="Type of hash to perform." default="SHA-256" required="false" />

		<!--- SET CHARACTER RANGES --->
		<cfset var number = "48,57">
		<cfset var uppercase = "65,90">
		<cfset var lowercase = "97,122">

		<!--- SET A COUNTER --->
		<cfset var counter = 1>

		<!--- BUILD STRUCT --->
		<cfset var data = structnew()>
		<cfset data.passwordraw = "">
		<cfset data.passwordhashed = "">

		<cfscript>
			/* WHILE THE PASSWORD LENGTH IS LESS THAN THE SPECIFIED LENGTH */
			do
			{
				/* RETURN A RANDOM CHARACTER */
				character = randrange(listfirst(number, ","), listlast(lowercase, ","));

				/* IF THE CHARACTER IS LOWERCASE */
				if(character gte listfirst(lowercase, ",") AND character lte listlast(lowercase, ","))
				{
					/* SET CHARACTER INTO STRING */
					data.passwordraw = data.passwordraw & chr(character);
				}

				/* CAN WE USE UPPERCASE CHARACTERS */
				if(ARGUMENTS.includeuppercase eq true)
				{
					/* IF CHARACTER IS UPPERCASE */
					if(character gte listfirst(uppercase, ",") AND character lte listlast(uppercase, ","))
					{
						/* SET CHARACTER INTO STRING */
						data.passwordraw = data.passwordraw & chr(character);
					}
				}

				/* CAN WE USE NUMBERS */
				if(ARGUMENTS.includenumbers eq true)
				{
					/* IF CHARACTER IS NUMBER */
					if(character gte listfirst(number, ",") AND character lte listlast(number, ","))
					{
						/* SET CHARACTER INTO STRING */
						data.passwordraw = data.passwordraw & chr(character);
					}
				}
			}
			while(len(data.passwordraw) lt ARGUMENTS.pwdlength);
		</cfscript>

		<!--- SHOULD WE HASH THE PASSWORD --->
		<cfif ARGUMENTS.hashpwd eq true>
			<cfset data.passwordhashed = hash(data.passwordraw, ARGUMENTS.hashtype, "us-ascii")>
		</cfif>

		<cfreturn data>

	</cffunction>

</cfcomponent>

It takes five arguments. First is the length of the password you want to generate. So do you want it to be 4 characters, 10 characters, or whatever. Second, do you want numbers to be included in the password? If so set it to true. This defaults to true if not specified. Third, do you want any letters that are uppercased? If using a case sensitive password you may wish to specify this to true. It defaults to false. Fourth, do you want to also return a hashed version of the password. If storing in a database you may want this. And if you are returning a hashed version, which algorithm should be used in the hash process. This defaults to “SHA-256″. You can use any algorithm supported by Coldfusion.

Next I set some variables that hold the lowest and highest byte code representing numbers, uppercase, and lowercase according to ascii. Build the struct to return, and then set some script that will pump out our random password according to the specifications.

Lastly, if the password should be hashed it is.

Enjoy.