CFC Instantiation

I was looking for a good introduction to instantiating a CFC to link to from a blog entry. I couldn't find one that covered what I wanted, so I decided I would just write one myself.

[More]

ExpandPath and Dynamic File Names

This is a short one. I just discovered today, that if you have a dynamic file name from a file that a user has uploaded, you should not include it in ExpandPath.

Take for example, the following variables (the first one being retrieved from a query):

<cfset filename = qRecord.FileName>
<cfset folder = "/files/">

Historically, I have retrieved the full path of the file using code similar to this:

<cfset filepath = ExpandPath("#folder##filename#")>

It turns out, however, that this code is better:

<cfset filepath = "#ExpandPath(folder)##filename#">

The reason for this is that the file name could be anything. In my case, I ran into a filename that included the characters "..", causing ExpandPath() to get the wrong folder for the first example (but not the second).

Just thought I would share this to save others the same trouble.

CFZIP Part 1: Introduction

CFZIP is powerful and easy to use. Better than that, it can work with zip files or jar files in just the same way.

Listing the Contents of a Zip file 

Finding out what is in a zip file is now as easy as everything else in ColdFusion:

<cfzip action="list" file="C:\software\DataMgr\DataMgr_2_0_2.zip" name="qFiles">

This will return a recordset similar to the recordset returned from CFDIRECTORY.

Reading a File in a Zip File

You can also read a file from within the zip file without unzipping the file. So, if I wanted to read the contents of DataMgr.cfc from the above file:

<cfzip
  action="read"
  entrypath="DataMgr.cfc"
  file="C:\software\DataMgr\DataMgr_2_0_2.zip"
  variable="DataMgrFile"
>

To read a binary file, use the same code, but change the action to "readBinary".

Unzipping a file

To unzip a file, just tell CFZIP where to put the files. 

<cfzip
  action = "unzip"
  file = "C:\software\DataMgr\DataMgr_2_0_2.zip"
  destination = "C:\software\DataMgr\unzip\"
>

Zipping a file

Zipping is file is easy as well:

<cfzip
  file="C:\software\DataMgr\DataMgr_ZipTest.zip"
  source="C:\software\DataMgr\unzip\"
>

At this point, you might be thinking that this doesn't provide you enough options. In fact, CFZIP provides many more options than I showed here. It has several more optional attributes as well as a a CFZIPPARAM tag to give you more flexibility. All of which I will try to cover in a future entry.

Alternatives

If you need the ability to deal with zip files now, there are other alternatives to CFZIP:

Make sure that you check the licensing restrictions on all of these before you use them.

Searcher.cfc 1.0 Beta

When I first started developing public web sites, I noticed that I often needed the same functionality on several sites. It was fairly easy to package those up as programs that I could copy from site to site. What I discovered, however, was that each one required a bit of set up.

I didn't like that I had to create (or copy) the required tables and set up other prerequisites. At the time, I was using Fusebox 3, so I also had to create circuits. All told, it always took me more time that I wanted to set up a program - and that was boring work.

I decided that I wanted my programs to be somewhat self-installing. DataMgr was one step toward that vision (which you can see actualized to a large degree in CodeCop). Now I can use DataMgr to install the tables that I need.

Searcher.cfc expands on that vision by using the same principal for verity collections. It does use cfcollection, so if that isn't available to you it won't do much good.

With Searcher.cfc, you can use the indexPath or indexQuery methods to index a query. If the collection doesn't exist, Searcher.cfc will automatically create it for you.

Additionally, Searcher.cfc will keep track of searches performed on your site. 

In order to use Searcher.cfc, you must first initialize it with the init() method, which has the following arguments:

  • CollectionPath (required): The full file path of the folder in which you want Searcher.cfc to create collections.
  • DataMgr (required): An instantiated DataMgr component.
  • sendpage (required): The browser path to the page that will redirect the user to the results of their search (more on this later).
  • excludedirs (optional): a list of folders to exclude from search results.
  • excludefiles (optional): a list of files to exclude from search results.

Searcher.cfc uses two tables to track searches: srchSearches and srchSelections, which it creates via DataMgr.

The "sendpage" argument must be the browser path to a file with the following code (assuming you have Searcher.cfc instantiated into Application.Searcher):

<cfif isDefined("url.searchid") AND isDefined("url.to")>
  <cfset Application.Searcher.send(url.searchid,url.to)>
</cfif>

This is what allows Searcher.cfc to see what pages a users selects from a search.

In order to index a search against a file path, use the indexPath() method, with the following arguments:

  • CollectionName (required): The name of the collection that you are indexing.
  • Key (required): The full file path to the folder that you want to index.

 In order to index a recordset, use the indexQuery() method, with the following arguments:

  • CollectionName (required): The name of the collection that you are indexing.
  • query (required): The query you are indexing (pass in the actual recordset, not just the name of the query) .
  • Key (required): The name of the key field from the recordset.
  • Title (required): The name of the field holding the title you want to display in your search results.
  • Body (required): The name (or list of names) of the field(s) holding the content you want to search.
  • URLPath (optional): The URL Path of the page that will result from the search (just like in verity, this will have the value of the key appended to it).
  • Custom1 (optional): The field for a Custom1 value, if you want one.
  • Custom2 (optional): The field for a Custom2 value, if you want one.

If you are calling the indexing methods from within a CFC and want to make sure that they are indexed regularly, I would suggest using Scheduler.cfc.

In order to run a search and get back a recordset of results, use the run() method with the following arguments:

  • searchterm (required): The word for which the user is searching.
  • collections (optional): The collections to search (defaults to searching all collections that Searcher.cfc is aware of).

This will log the search and return a recordset of the results. If you want to run the same search again without logging the search (for subsequent pages of a search, for example), use the reRun() method with the following argument:

  • searchid (required): The id of search you want to rerun ("SearchID" will be a column in the query returned from run() and reRun().

If you want to get search data, just call the getSearchData() method with the following arguments:

  • startdate (optional): The date from which the report should start.
  • enddate (optional): The date at which the report should end.

To get details about the landing pages chosen for a search phrase, you the getLandingPages() method with the followig arguments: 

  • searchterm (required): The phrase for which you want details.
  • startdate (optional): The date from which the report should start.
  • enddate (optional): The date at which the report should end.

I have been using Searcher.cfc for a few years and I have found that it has made the creation and deployment for Verity searches relatively quick and easy.

Searcher.cfc is open source and free for any use.

FormSaver.cfc 1.0

I often need to create a multi-page form or load a form with old data. FormSaver helps with both by providing a generic way to save form data.

FormSaver is built to save the data for a form, not the data that a form might represent (which will hopefully make sense shortly).

FormSaver can save the data using either DataMgr or SessionMgr (which is basically a SessionFacade). Just pass in the one you want to use to the init() method of FormSaver (with an argument name of "DataMgr" or "SessionMgr" respectively).

In order to save a form's data, use the store() method with the following arguments:

  • formdata: The Form structure itself (so, theoretically, FormSaver could save any simple structure)
  • formname: A name by which to reference this form (used for retrieving the value later)
  • usertoken: Any string to identify the user (required only if using DataMgr)

 In order to get back the saved structure, use the retrieve() method with the following arguments:

  • formname: The name to reference the form you want to retrieve (from the argument of the same name above)
  • usertoken: A string to identify the user (again, required only if using DataMgr)

You can also delete a stored form using the delete method with the following arguments: 

  • formname: The name to reference the form you want to retrieve (from the argument of the same name above)
  • usertoken: A string to identify the user (again, required only if using DataMgr)

When using DataMgr, FormSaver saves data to a table that it creates (via DataMgr). The name of this table is set via the "tablename" argument of the init() method (it defaults to "frmSaveds").

FormSaver is a very simple component that can help you temporarily store form values. This can be helpful for multi-page forms, especially if you want a user to be able to return to a form where they left off in a previous visit.

FormSaver is open source and free for any use. 

 

Using the Derby Database

When I read Ben Forta's announcement (and follow up) that ColdFusion 8 would ship with support for the Derby database, my first thought was "I should add support for that in DataMgr". This proved to be a good introduction to Derby.

I decided that I would get my local copy of my demonstration site running on Derby.

[More]

PhoneFormatter.cfc 1.0

I typically store phone numbers in one field (instead of using a separate field for each part of a phone number). While this usually works quite well, I have discovered that it can cause issues when I want a phone number to display in the same format regardless of how it was originally entered.

This is relatively easy to do if you handle it as the data comes in, but if you don't (either as oversight or because the data comes from outside the system) then it is a more difficult task.

I created PhoneFormatter to handle this task. Actually, it can handle formatting the data on an individual string or on a field across a whole table.

PhoneFormatter does require DataMgr. It also requires you to pass in the format you want to use for the phone number (using zeros as place-holders for digits). You should also pass in a default area code for numbers that don't have one. So the arguments for the init() method are:

  • DataMgr: An instantiated DataMgr component
  • Format: The desired format for phone number (using zeros as place holders for digits)
  • DefaultAreaCode: (optional) The area code you want PhoneFormatter to assign to phone numbers that don't have one.

To format a single phone number, pass it to the fixPhoneNumber() function (optionally passing in an area code as a separate argument).

To format every row for one field in a table, use the fixPhoneNumbers() method.

  • table: The name of the table in which you have the phone number.
  • idfield: The primary key field for the table.
  • phonefield: The field in the table in which you have the phone number.

PhoneFormatter may not solve a problem that you have, but if it does it can certainly be useful.

It is open source and free for any use. Feel free to download PhoneFormatter and try it out. 

 

Capitalizer.cfc 1.0

Recently, a client of mine started working on printing up badges for users on their system. As soon as we started testing the system, they complained that the badges looked amateurish.

The problem was that the badges were using information originally entered by the users themselves. Sometimes these users enter their information with caps-lock on. So, "Joe Smith" could come out "JOE SMITH" or even "jOE sMITH". Naturally, both of these look pretty unprofessional on a printed badge.

With more than three thousand users already on the system (and more signing up all the time), it wasn't really practical to fix each entry by hand.

At the same time, these seemed like a problem that I was likely to run into again, so I wanted a general solution that I could use again in the future.

The result is Capitalizer.cfc, the heart of which is Adrian Lynch's TitleCaseList() UDF. The component does require DataMgr, but only to keep the code brief.

The main method of Capitalizer is fixFieldCase(). This method can correct the capitalization for a selected field in a table, using the following arguments:

  • table: The name of the table in which you want to fix the capitalization.
  • field: The field in the table in which you want to fix the capitalization.
  • pkfields: A comma delimited list of the primary key fields in the table.

You can also use Capitalizer to correct the case for a single string. You can pass in any string to the fixCase() method and it will return the string with the capitalization fixed.

Capitalizer watches for words that shouldn't be capitalized (like "and,or,the") and makes sure that they aren't. It also makes sure that directions (NE,SW) are capitalized. It also make sure that "mc" names (like "McWilliams") are capitalized correctly.

Capitalizer.cfc is open source and free for any use. Feel free to try it out. 

Scheduler.cfc 1.0 Beta

Using service components, one of the challenges that I have run into (and I think others have as well) is that I need to schedule a task from a component, but it doesn't make sense to do so.

So, I have a Scheduler.cfc to handle scheduling a task from a component. It should be loaded into a persistent scope (Application scope preferably) and does require that the runTasks() method should be run from a ColdFusion template that is called every hour (probably by scheduled task).

I typically pass Scheduler into a component that needs to schedule a task. From there I can call the setTask() method of Scheduler.cfc with the following arguments:

  • Name: A unique name for the scheduled task.
  • ComponentPath: A path to the calling component (just used as a unique indicator for the component).
  • Component: The calling component, on which Scheduler will call a method (usually This).
  • MethodName: The name of the method in your component that Scheduler should call when it fires the task.
  • Interval: The interval Scheduler should use for calling the method with the following options.
    • once: Scheduler will call the task immediately, but only one time.
    • hourly: Scheduler will call the task every hour.
    • daily: Scheduler will call the task every 24 hours.
    • weekly: Scheduler will call the task once every 7days.
    • monthy: Scheduler will call the task once a month.
    • A number of seconds between each call.
  • Args: A structure of arguments that Scheduler should pass to the method that it is calling for this task.
  • Hours: A comma-delimited list of the hours during which the task can be called (on a 24 hours clock). So, if the value is "2,3" then Scheduler will only call the task during the hours of 2:00AM-2:59AM or 3:00AM-3:59AM.
Scheduler does require (the free) DataMgr component set in order to work. It will record the tasks that it is to run in a table called "schTasks" and record each time it calls a task in a table called "schActions". It will use DataMgr to create these table and can use any database that DataMgr support.

Scheduler.cfc is open source and free for any use. Feel free to download it and try it out.

John Farrar on COOP

Yesterday John Farrar presented to the Online ColdFusion Meetup Group on his forms custom tag set, COOP. Actually, he describes it as a framework to separate page markup from processing logic.

[More]

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.