cftopper.com

eTag()

eTags are cool - read more about them here - eTags.

eTag() is a handy function that you can use to perform client-side programmable caching of your coldfusion pages.

To get the benefit just call <cfset eTag( "My Page" )> and the next time a user visits this page, they will get a 403 not modified response and execution will stop here.

Getting advanced... add in the date..

<cfset eTag( "#pageName#_#lastUpdatedDate#" )>

... this will regenerate the page IF the lastUpdatedDate has changed.

We use this extensively in Teamwork Project Manager to reserve dynamic pages almost instantly. It also reduces server load because processing stops.

Here is the code:

<!--- eTag will return a 304 not modified response if possible --->
<cffunction name="eTag" output="no">
    <cfargument name="eTag" type="string" required="yes">
    <cfargument name="useHash" type="boolean" required="no" default="yes">
   
    <cfif useHash>
        <cfset ARGUMENTS.eTag = Hash( ARGUMENTS.eTag )>
    </cfif>
   
    <cfheader name="ETag" value="""#ARGUMENTS.eTag#""">

    <cfif ( StructKeyExists( CGI, 'HTTP_IF_NONE_MATCH' ) and CGI.HTTP_IF_NONE_MATCH contains ARGUMENTS.eTag )>
        <cfcontent reset="yes">
        <!--- nothing has changed, return nothing --->
        <cfheader statuscode="304" statustext="Not Modified">           
        <cfabort>
    </cfif>
   
</cffunction>
Tags: ColdFusion | Tips | Tools

Tip: Protect your MySQL from SQL Injection

Here's an important tip for everybody using MySQL, you need to set the following option in your my.ini or my.cnf configuration file to prevent SQL injection attacks.

sql-mode=NO_BACKSLASH_ESCAPES

Before you do this, If you have a simple search on your site and a user inserts

test\'' OR 1 = 1   --

instead of just

test

The \'' will get escaped and your SQL statment will turn from something like:

WHERE variable LIKE 'test' AND userId = 55;

to something like:

WHERE variable LIKE 'test' OR 1 = 1 -- AND userId = 55;

Because -- is the start of a a comment in MySQL, the AND userId = 55 bit will be ignored.
Tags: ColdFusion | MySQL

Supporting Multiple Languages

The Translation engine at work
Google API Translation at work
Over the weekend, I wrote a resuable script that converts snippets of English text to 20 other languages using the new Google translate Ajax API for our Teamwork Project Manager software.

The script was hacked together quickly and isn't the best most structred code in the world but it works well - it cycles through any strings in the languages table that haven't been converted yet and one by one, converts them to a foreign language. Every time, the script has 20 results, it saves the changes to the database.

Watching it at work is pretty cool with strings coming back in Arabic, Chinese, Russian etc.

The strings are stored in the following table MySQL table:

CREATE TABLE `languagestrings` (
  `languageStringId` int(10) unsigned NOT NULL auto_increment,
  `languageStringRef` varchar(255) collate utf8_bin NOT NULL,
  `languageCode` char(2) character set utf8 NOT NULL,
  `languageString` text character set utf8 NOT NULL,
  `languageStringUsageCount` int(10) NOT NULL default '0',
  PRIMARY KEY  (`languageStringId`),
  UNIQUE KEY `langStringRef` (`languageStringRef`,`languageCode`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

And translating the strings is done with the function
translate( ref, english_default, bUpdateNow, language ) which works like so:

#APPLICATION.teamworkpm.translate( "Your Projects", "Your Projects", false, "ES" )# gives "Su Proyectos".

Here is the translate function code:

<cffunction name="translate" returnType="string" output="No">
<cfargument name="ref" type="string" required="yes">
<cfargument name="englishDefault" type="string" required="Yes">
<cfargument name="updateNow" type="boolean" default="no">
<cfargument name="lang" type="string" default="#SESSION.lang#">
<cfset var getLangString = "">
<cfset var insLangString = "">
 
<!--- Ensure there is a structure for the currency language code --->
<cfif NOT StructKeyExists( APPLICATION.langStrings, ARGUMENTS.lang )>
	<cfset APPLICATION.langStrings[ ARGUMENTS.lang ] = StructNew()>
</cfif>
 
<cfif NOT StructKeyExists( APPLICATION.langStrings[ ARGUMENTS.lang ], ref )>
	<cfset ARGUMENTS.updateNow = true>
</cfif>
 
<cfif ARGUMENTS.updateNow>
 
	<!--- Check to see if the value is in the database --->
	<cfquery name="getLangString" datasource="teamworkpm_master">
	SELECT	languageString
	FROM	languagestrings
	WHERE	languageStringRef = <cfqueryparam value="#ref#" cfsqltype="CF_SQL_VARCHAR">
			AND languageCode = <cfqueryparam value="#ARGUMENTS.lang#" cfsqltype="CF_SQL_VARCHAR">
	</cfquery>
	
	<cfif getLangString.recordCount>
 
		<cfset APPLICATION.langStrings[ ARGUMENTS.lang ][ ref ] = getLangString.languageString>
		
		<cfquery name="updateLangStringCount" datasource="teamworkpm_master">
		UPDATE	languagestrings
		SET		languageStringUsageCount = languageStringUsageCount + 1
		WHERE	languageStringRef = <cfqueryparam value="#Left(ref,80)#" cfsqltype="CF_SQL_VARCHAR">
				AND languageCode = <cfqueryparam value="#ARGUMENTS.lang#" cfsqltype="CF_SQL_VARCHAR">
		</cfquery>
		
	<cfelse>
		
		<!--- Store the value in the field --->
		<cfset APPLICATION.langStrings[ ARGUMENTS.lang ][ ref ] = ARGUMENTS.englishDefault>
 
		<!--- Insert the default english value into the database --->
		<cfquery name="insLangString" datasource="teamworkpm_master">
		INSERT INTO languagestrings( languageStringRef, languageCode,
		languageString, languageStringUsageCount )
		VALUES( <cfqueryparam value="#ARGUMENTS.ref#" cfsqltype="CF_SQL_VARCHAR">,
		<cfqueryparam value="#ARGUMENTS.lang#" cfsqltype="CF_SQL_VARCHAR">,
		<cfqueryparam value="#ARGUMENTS.englishDefault#" cfsqltype="CF_SQL_VARCHAR">, 1 )
		ON DUPLICATE KEY UPDATE languageString =
		<cfqueryparam value="#ARGUMENTS.englishDefault#" cfsqltype="CF_SQL_VARCHAR">
		</cfquery>
 
	</cfif>
 
</cfif>
 
<cfreturn TRIM(APPLICATION.langStrings[ ARGUMENTS.lang ][ ref ])>
 
</cffunction>


You can see that if the string has already been looked up, the result is returned immediately so that this function is nice and fast.

If a string doesn't exist for a given language, the default english version is inserted for that language, so that it can be translated later.

The languageStringUsageCount is updated whenever a string has to be looked up, usually after a server reboot. This will be useful when we want to strip the database of strings that are no longer used in a few months.

The "update now" argument allows programmers to quickly change the value of english strings directly from the code base during development.

Hopefully this will help you get your own multi-language systems up and running in ColdFusion.
Tags: ColdFusion | Tips | WebDev

Tip: Easily protect your include files

Hi All. Sorry I haven't posted in a while, I've been very very busy. Thanks to everyone who has been encouraging me to release our Content Management Software I demonstrated as CFUnited EUrope... soon I promise.

I just thought I'd share this quick and useful tip:

Put the following code in all your Application.cfm or Application.cfc fles to automatically protect your include files such like "inc_test.cfm" or "act_deleteUser.cfm" from being called directly.

UPDATE: This code had a bug which was fixed 30th April 2008
<!--- Any script such as inc_file.cfm or dsp_file.cfm can not be called directly --->
<cfif mid( ListLast( cgi.script_name, "/" ), 4, 1 ) IS "_">
    <cflocation url="#APPLICATION.siteURL#" addtoken="no" />
</cfif>

There is some code in fusebox that prevents calling files other than index.cfm but we have found this too limiting.
Tags: ColdFusion | Tips

CFUnited Europe 2008

Mission accomplished - I spoke at CFUnited and survived.

Charlie Arehart was nice enough to give me some tips and help me rearrange and improve my Powerpoint presentation for the 2 hours before my talk.

With minutes to go, I saved my Powerpoint and suspended my laptop, then headed upstairs to the conference room. Now this was my first time speaking in front of more than 6 people and I was extremely nervous.

I get to the conference room and there are a few people in there already. I head up to the stage, laptop in hand and start setting up... I hit the power button to restore my laptop and.... what the hell... my laptop is rebooting, not restoring.

OK, not too bad, I'll just have to start ColdFusion, open the presentation and Firefox windows again. My laptop takes an age to start up.

So I am 3 minutes into the presentation and still waiting for my laptop to warm up. Fortunately there are still a few stragglers filing into the small conference room.

I double click to open the presentation and wait. And wait. It is taking ages to open. My laptop is being projected onto the 8 foot screen; everybody in the room feels my pain when Powerpoint reports that the file is corrupt and asks if I would like to restore it. Hell yes, restore that thing and fast.

I start the presentation. My freyed nerves are shattered but the show must go on. I start into the slides and notice something strange on the "About Me" slide. It's not exactly right.

Then I get to the "Contents" slide and notice that all the changes Charlie helped me make have been lost. Now this is a major curveball - there were entire topics that I had dropped, new sections I added and the whole thing had been rearranged.

Next disaster. The first section in my presentation should have been the last - it's all about "Source Control". I ask for a show of hands to make a point - "How many people in this room use source control". I had expected about 4 hands. I am dismayed to see that of the room of maybe 35 people, only 1 does not have his hand up.

Anyhow, I persevered and bluffed my way through the rest of the presentation. I'd like to thank everybody who was in the audience for their support. As promised here is the real presentation. Hopefully you all got something from the rest of the presentation. Topics covered:
  • Quick Tip: AJAX debugging
  • Supporting Different Timezones
  • Foreign Language Support
  • Advanced File Uploading
  • Quick Tip: CSS Hacking for IE6
  • Working with Multiple Domains
  • Database “Sharding”
  • Source Control.
I also gave a demo of our content management system, TeamworkCMS, and it's powerful plug-in architecture. As promised this will be released in May 2008. Stayed tuned.

I learned a lot from this experience and i'm sure that next time, i'll be more prepared and much more confident. It was a great experience all-in-all.
This is the latest CF9 prototype (codename MoHawk):
CF9 (foot) at CFUnited 2008
Tags: ColdFusion

I'm speaking at CF-United Europe!

I'm going to be speaking at CF-United Europe! It's my first real public-speaking effort so please lend your support.

The topic is:
How we made it: Teamwork Project Manager - tips for development of highly-scalable web2.0 apps in ColdFusion.

We developed Teamwork Project Manager over the last 7 months. There aren't many web2.0 CF apps taking on the Ruby crowd these days and I think my speech will be interesting.

Some items would include:

  • Industry Analysis
  • Ajax & ColdFusion
    • Debugging techniques
    • Processing the response
    • Using JSON with CF
  • Handling Multiple Domains with one application
  • Advanced file uploading - hundreds of files in one go
  • Database design
  • Scalability
  • Support
  • Sales & marketing

Teamwork Project Manager - The Sstory

We hear a lot about people building web2.0 apps like Twitter using Ruby on rails. Rarely do we hear a mention of ColdFusion being use to build a scalable high-availability ajax enabled application. But at Digital Crew we recently launched Teamwork Project Manager - an application that as the name suggests, allows organisations to manage their internal and external projects and get productive.

The whole thing is built on our own 'SiteEngine' framework using ColdFusion and MySQL.

Here is the story so far.


The Teamwork Project Manager Story

January 2007


Peter Coppinger & Dan Mackey founded their company Digital Crew almost 8 years earlier and have made a living building websites, intranets and custom web-based solutions for clients in Cork, Ireland. At this point their company has a good reputation internationally and they sell website components online.

However they found themselves up to their eyeballs in client projects.
Peter Coppinger says, "Every Monday we held a meeting and discussed the current projects - reassigned tasks and set milestones. We maintained a large whiteboard separated into 5 segments - Active Projects. Upcoming Projects, Sales, Meetings and Billing.

The whiteboard was neatly organised and gave us a sense that we were organised.... but we weren't.

The meetings were taking longer and longer. We knew we needed a better system.
".

March 2007


Peter and Dan spent some time reviewing and using project management software. Many are very expensive and all are overly complex. They wanted something intuitive that doesn't take time to learn and maintain. Something everybody in the company can use - not just the project manager.

Peter dreams of a software system that would basically manage a company. Call it a project management system, if you will - something extremely easy-to-use and generic enough that it could be used by all sorts of companies. He starts scribbling ideas on paper. In the morning he returns to work and the confused whiteboard overlord.

May 2007


Peter and Dan discuss the software they want to make almost daily now. And they have a name - Teamwork Project Manager (styled after TeamworkCMS - Digital Crew's bespoke website content management system).

A credos is set - "Project Management Made Easy!"

Peter and Dan are too busy with 'real work' to dedicate time to developing the product idea. But fate intercedes and a manager from a multinational client calls Peter to ask if he would have time to do a "simple" project management system. They want something extremely easy-to-use that would list upcoming and late milestones. This is the opportunity Peter has been waiting for. He eagerly tells the client about the software he has been thinking about making for a while and the client agrees that it is exactly what they need.

Peter first reviews other popular project management systems to find out what they are doing right and what they are doing wrong. Although other Project Management solutions exist, they are all too slow, clunky and badly designed. None are what the client wants and Peter has in his head. He makes a list what he likes and dislikes and puts pen to paper designing the software.

While on holiday to visit his sister in Boston for 3 weeks, Peter shamelessly neglects holidaying to stay up night-and-day working on the 'Project Management System'. Dan, busy with other client work back in Cork, checks in every morning. Screenshots fly back and fourth but Peter is reluctant to show the software running "until it's ready". Dan, also passionate about the development of their first "real" product, provides a steady steam of encouragement and suggestions.


June 2007


The software is installed for the client. Peter nervously waits for their feedback. Will they love it or loath it? Peter checks back an hour later and the client manager has already set-up several projects, added staff, assigned tasks and milestones. Just then, the phone rings. The client manager tells Peter that this is exactly what he was looking for. He was able to use it right off-the-bat with no instruction manual. Software, the way it should be.


August 2007


Dan works post-haste on TeamworkPM adding his fair share of sleepless-nights to the tally. The software is lovingly sculpted bit-by-bit with sometimes heated 'discussions' over items such as the color of a link. Their mission is simple - to make the worlds most easy-to-use, fastest and best Project Management System.


September 2007


Weeks of preparation. Servers are set-up. Database is optimized. Software continually improved. The promotional website is made. And finally the launched date is set.


October 2007


Everything is ready and has been tested a hundred times.
Teamwork Project Manager is launched on October 4th 2007.

Almost immediately, through their respective software development blogs, curious users start browsing the website and Teamwork Project Manager receives it first sign up within a hour of launch!

It's only been 3 weeks so far and the feedback has been great. We didn't expect the sales to start until at least the 30-day mark but we things are going better than expected. We believe we have a great product and we are now working hard on the marketing side of things.
Stay tuned to the blog. I look forward to updating this story.

Thanks for reading the story. So far we have had a great response and we are continually improving the software. We believe that our software lives up to our credos - "Project Management Made Easy". We want the software to be so easy-to-use, our own computer-shy mothers could use it (and indeed they do).

We are working extremely hard to deliver more features and continually improve TeamworkPM. Please try Teamwork Project Manager for free.

We have launched!

We have finally launched!!

And what a wild ride it has been getting everything ready.

Check out Teamwork Project Manager at www.teamworkpm.net

The Website - Final Days

About a 5 days ago we had this marketing website almost complete. We stood back and looked at what we had created... and we decided... it sucked. Against the pressure of the looming launch date chiseled in stone on the preview website, we started again. We threw away the old website and said to our designer, Adam, "Hey Buddy, um... we don't like the site. Can you cook us up something prettier?".

Lo and behold, he disappeared off the face of the Earth for 2 days and returned with the new website design. And we saw that it was good. We worked round the clock slicing and dicing, XHTMLizing and CSSing, content-managing, tweaking and testing.

A few hours behind the intended launch time, we made the critial change to the IIS web server and set the new site live, flying open the doors allowing all to sign up for the shiny new kid on the block - Teamwork Project Manager.

Here we are in my office seconds after putting the website live. We are tired, we are hungry, we have sore backs and aching asses... but we're happy. That's me, Topper, on the left and Dan on the right.
We are holding the camera in our out-stretched hands (attempt number 10 to take a photo with both of us in the frame. The bad quality reflects that this was taken on my trusty K750i camera phone.)



We still have a lot to work to do on this website of course but we are proud of what we have created.

Teamwork Project Manager - Final Days

We started making some important changes with just days left to launch.

For example, our testing had revealed that user were getting confused between project view and top-level/dashboard view. They would just blink wildly and we've have to explain that you have to click-into a project. Also in project view they wondered where all the other projects had gone.

We fixed this by redesigning the navigation system into two bands of tabs in stead of just one with and extra "Project View" tab at the top level to make it obvious that the user was now indeed, in project view. It's so much more intuitive this way.

What else - oh yes, Dan whinged on about the need for starring of projects for so long that I finally relented. I didn't plan on getting this done for launch but I'm very glad I did now. Now, when you are members of a whole host of projects and the dashboard is getting a little busy, the user can switch to custom view where he will only see the projects he has starred.

Also when there are loads of active projects such as here at Digital Crew, we now only list the projects that have been active in the last 14 days on the sidebar. Other projects can then be shown by clicking "All projects". It looks so simple but we had a good 10 minute discussion over the best way to present this to the user. We considered tabs for "Recent Projects" and "All Projects"  but in the end, I think the way we have it now is much better.

TeamworkPM is launched at last and Dan is a happy little camper

Après Launch

This morning I woke up and eagerly turned on the computer, anxious to see if we got any sign-ups or feedback. I was delighted to see a very positive email - " I have started playing with the app and am incredibly impressed with the ui and the streamlined approach to project management.". He then asked us to provide a roadmap and listed a few more features he would love to see.

This type of feedback just hours after launch is great motivation for me. I'm going to get busy cooking up the extra feature this guy wants and serve it hot! Stay tuned.

TeamworkPM Launched - Topper has his legs up! A job well done.

PS. I will be adding a roadmap page to the Teamwork site before the end of the day,
PSS. I just added a "Chat Now" link in the footer of this website so feel free to get in touch.

Check out Teamwork Project Manager at www.teamworkpm.net

Quick Tip: I Forgot the ColdFusion (CFMX7) Administration Password?

OK, It happens - you forgot the password. Maybe your working on a client web server and nobody knows what the password was set to 2 years ago when they set it up. Maybe your just suffering brain-freeze. Or maybe your just thick. Messing

Anyhow, you've tried everything and your pulling your hair out.
Here's the solution, it works with CFMX6/CFMX7.
  1. Open the file C:\Program Files\ColdFusionMX\lib\neo-security.xml.
  2. Change:

    <var name="admin.security.enabled">
       <boolean value="true" />
    </var>

    to read

    <var name="admin.security.enabled">
       <boolean value="false" />
    </var>
  3. Restart ColdFusion and you can now log into the administration without any password.
  4. Make sure to pick a new password and turn the security back on. You can do this in the administration.
Tags: ColdFusion | Tips

How to Change the CFMX Dev Server Port

I installed a demo website on one of our sales guys laptops earlier today and needed to change the port on the CFMX built-in server from 8500 to 80. Here's how it's done for posterity:
  1. Open C:\CFusionMX7\runtime\servers\coldfusion\SERVER-INF\jrun.xml
  2. Find "8500", replace with "80"
  3. Save file and restart CF.
Tags: ColdFusion

Password Generator

If you ever need a quick random password, just visit:

Aside: I knocked this up in 30 seconds using dcRandomPass.

Free Query to Excel Function

generateExcel SampleI'm working on extending a system with excel reports so I knocked up a handy function that will turn any Query into an excel view.

Download


Syntax


The function generateQuery takes 4 arguments: the query, the columns, the mode and the filename. The syntax is:

generateExcel( theQuery, columnList, mode, filename  )

But you can call it with just the name of the query if you want, e.g.:

generateExcel( myQuery  )

Options


  • You can be specific about the columns:

    generateExcel( products, "productname, productCost" )

    The Column list argument allows you to specify the order you want the columns displayed in, or omit columns you don't want. If you want you can just leave it blank and it will just use the query column list - but the columns could be in any order.
  • There are 3 output modes: "display", "save" and "debug":

    generateExcel( products, "productId, productname, productCost", "save" )

    The default mode is "display".

    Mode "display": This will try to display the excel chart within the browser. If the end-user's browser does not allow this, Excel will launch and show the output instead.

    Mode: "save": This will just do a download and will not attempt to display within the browser.

    Mode: "debug": Will display the columns as a normal HTML page with <pre> tags.

  • The filename attribute allows you to specify the filename that the excel should be saved as. The default is "report.xls".

If you find this function useful, please just send me an email to say thanks peterATdigital-crew.com

Enjoy!

Update May 15 2007


I made some improvements yesterday - the code uses <cfcontent reset="yes"> so that it will reset the output before rendering the excel view and now supports exporting dates properly.
Tags: ColdFusion | Tools

My Lastest Masterpiece: ProFlashDownload

Cue Drumroll... my latest ColdFusion component is complete!

ProFlashDownload allows developers to make a list of files available for download to a user. These files will download with a progress bar and the developer can have a page called in the background when a file has completed downloading.

ProFlashDownload - Click for real demo

I was specifically asked to develop this component based on my existing work on ProFlashUpload.

Just like ProFlashUpload, ProFlashDownload is highly customizable, easy-to-use and easy-to-program.

Sidenote: You would think that Flash programming is browser-independent, but with this sucker, I had a bunch of issues with Internet Explorer causing all sorts of strange behaviour that I had to tediously program around.

Sidenote 2: It has been suggested that I combine ProFlashDownload and ProFlashUpload to make the mother of all web based file management utilities... and i'm considering it.

I'll be making this tag available for purchase at $50 on www.cftagstore.com in the coming days.

GZip Encode your CFML

There is an excellent article in the latest ColdFusion Developers Journal on how to enable GZip compression on your ColdFusion pages for free.
First, HTTP Compression is a great way to speed up your Website and lower bandwidth utilization all at the same time. In this example, your ColdFusion server (6.1 and above) will encode CFML output using GZIP and have the browser decompress this data on the fly. In a corporate environment, Webmasters may choose to go with third-party software such as Port80 Software's httpZip, which can compress other files as well, including .js, .css, and your HTML files. In this example, we are only compressing the generated HTML output from a .cfm template execution.

I just implemented it on one of our servers in 2 minutes. It reduced a 7Kb page to only 2306 bytes.
Date: Wed, 28 Mar 2007 23:01:45 GMT
Server: Microsoft-IIS/6.0
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Content-Length: 2306

200 OK
Tags: ColdFusion | Tips | WebDev

Image Gallery in 30 Seconds

How long would it take you to create an image gallery on your client's website?

Even using off-the-shelve scripts or purchased components, your probably still looking at an hours work, minimum, right? Maybe even a half-day or days work when you take into consideration the development of the administration section for the gallery.

How about reducing that time to 30 seconds?

With the plug-in architecture we developed for our in-house content management system, TeamworkCMS, you can! See for yourself...




Complete with administration. The database tables are created for you automatically when you install your first gallery. All aspects of the gallery including image size, thumbnail size and the CSS can be easily tweaked to suit your client's website.

This is just one of a myriad of page add-ons that we have developed.
Plug-n-play web development = saved time = money.

We will be releasing TeamworkCMS to the ColdFusion community FOR FREE in the coming weeks. BETA Testers wanted. Comments, feedback and suggestions would be great.

Topper Contributes to Open Source

I *think* this is the first time I have ever actually contributed to an Open Source project. Sure, I've given out free code, but I've never actually contributed to a community based software project until today.

I didn't want to have to use a CFX tag for image resizing anymore (even If I did make CFX_Jimage) so I tracked down Rick Root's ImageCFC.

In the project I was working on, I needed to create thumbnails that were exactly 90x90 with no whitespace from any size of source image. The source image could be any size.

To do this correctly, you need to figure out whether to resize on the X or Y axis, do so, then and clip off the remaining bit.

Figuring out the Axis to Resize on


You have to get the scale of the destination width over the current width and compare it to the destination height over the current height:

if( deltaX/currX < deltaY/currY ) then Resize on X Axis
else Resize on Y Axis

Then you have to clip the center piece of the remaining image:

        <cfset cropOffsetX = max( Int( (imgInfo.width/2) - (newWidth/2) ), 0 )>
        <cfset cropOffsetY = max( Int( (imgInfo.height/2) - (newHeight/2) ), 0 )>

It was a little tricky to get right (mainly because my brain was refusing to cooperate yesterday), but eventually I cracked it and it work's perfectly.

I submitted my new resizeBestFit function to Rick. Within a few short hours, he has integrated it nicely into the existing resize function and added an extra option, cropToExact.

ImageCFC Version 2.14 is available now. Cudos Rick.
Tags: ColdFusion | Tools | WebDev

Teamwork CMS Demo Online!

This weekend I got the thumb out and put the documentation website we build for our own documentation portal up on CFTagStore.com as a full product complete with CMS. As part of putting the product online, I also setup a working demo of the Portal and CMS.

 This is the first time that we have put a complete working demo of the latest version of our Teamwork Content Management System online for the general public to play with.

I invite you to log in and mess around with TeamworkCMS and then to let us know what you think:

The TeamworkCMS Demo is available here:
http://demos.digital-crew.com/documentationportal/cms/

Login with "admin" and password "admin".

You can view the pages you add or edit on the Demo Website here:
http://demos.digital-crew.com/documentationportal/

The database and codebase will reset automatically every 2 days (or when we get complaints that some Ejit has messed it up too much).

Prevent URL access to CFML include pages

Hackers can link directly to your ColdFusion include pages to bypass security or gleam information about the application from resulting errors.

For example, suppose you had 2 files - login.cfm and inc_success.cfm - the hacker could bypass the login.cfm and try to execute inc_success.cfm.
This is a bit of a strange example but I'm sure you get the idea.

There is a brilliant piece of logic in the Fusebox application.cfm file that prevents would-be hackers from directly accessing any CFML file other than "index.cfm". The code is blindingly simple and it goes a little something like this:
  1. <cfif right(cgi.script_name, len("index.cfm")) neq "index.cfm"
  2. and right(cgi.script_name, 3) neq "cfc">
  3.     <cflocation url="index.cfm" addtoken="no" />
  4. </cfif>

Regardless of what framework, if any, that you are using, I recommend adding this code to you application.cfm.

ps. Bear in mind that Ajax or flash movie calls to CFML pages will only be able to get data from pages named "index.cfm". Also any popup window pages must now be accessed through or named "index.cfm".

How to Beat Form Spam-bots

...without resorting to an annoying CAPTCHA.

Those bloody form spam bots were driving our clients crazy with hundred of bs emails arriving every day. I came up with this simple but effective method to beat these form spam-bots.

Part 1 - Duping New Bots


Warning: This method assumes needs JavaScript enabled and is thus not blah-blah compliant.

  1. Change the action of your form to "about:blank". (Remember the real action for later.) Robots indexing this will now go to "about:blank" instead of your action handler page.
  2. If your form has a JavaScript validation function, add this line just before the form is OKed or submitted:

    document.[form name].action = "[path to form handler page]"

    Otherwise just stick in something like this in the form:

    onsubmit="this.action='[Path to handler page here]'"

It's pretty simple, but since implementing this at the crew, we haven't encountered any robots smart enough to figure out that we are duping them with the about:blank. If the robots evolve, then so will we.

Part 2 - Blocking Old Bots (they're using Form Caching!)


Part 1 will prevent robots indexing your form action URL; but what about the robots who have already indexed your form and are storing the form details in their database? Here's how to block them:

  1. Add this line to your form:

    <input type="hidden" name="formSecurity" value="<cfoutput>#Hash( DateFormat( now(), "dd/mm/yyyy" ) & "YOUR_SECRET_KEY" )#</cfoutput>"/>

  2. In your handler page insert the following at the start:

    <!--- FORM SECURITY --->
    <cfif NOT isdefined( "FORM.formSecurity" ) OR ( FORM.formSecurity IS NOT Hash( DateFormat( now(), "dd/mm/yyyy" ) & "YOUR_SECRET_KEY" ) )>
        <cfabort>
    </cfif>

Tada! Your done. Welcome to zero-spam.
Alternative method: Cut the internet cable running out of Nigeria.

ColdFusion-alike List Functions for Javascript

Those ColdFusion list functions are pretty useful so I made a Javascript version of them. Enjoy.
Download
/* ColdFusion List Functions
   These functions manipulate lists just list ColdFusion does.
   (c) Digital Crew
   Happy Christmas. Knock yourself out.
   email: topper@digital-crew.com
   web: www.digital-crew.com   /   www.cftopper.com
*/
function listFind(c,n,d)
{
	if(arguments.length<3)d=",";
	var e = c.split(d);
	for(var i=0;i= 1 && p <= e.length )
	{
		return e[p-1];
	}
	alert("Out of range");
	return "";
}
function listSetAt(c,p,v,d)
{
	if(arguments.length<4) d=",";
	var e = c.split(d);
	if( p >= 1 && p <= e.length )
	{
		e[p-1] = v;//set the value
		//rebuild the string
		for( var i=0,c=""; i < e.length; i++ )
			c = (c!=""?(c+d):"")+e[i];
	}
	else alert("listSetAt: Index " + p + " is Out of range");
	return c;
}
function listInsertAt(c,p,v,d)
{
	if(arguments.length<4) d=",";
	var e = c.split(d);
	if( p >= 1 && p <= e.length+1 )
	{
		e.splice( p-1, 0, v );//insert the value
		//rebuild the string
		for( var i=0,c=""; i < e.length; i++ )
			c = (c!=""?(c+d):"")+e[i];
	}
	else alert("listSetAt: Index " + p + " is Out of range");
	return c;
}
function listLen(c,d)
{
	if( c == "" ) return 0;
	if(arguments.length<2)d=",";
	return c.split(d).length;
}

Good coding style #1: Use Whitespace

Coding readability is important! But only to to a degree - when people get religious on this issue I just zone out.

IMHO, if you have decided that you are a professional programmer, why not be the best programmer you can be. Try to evolve everyday. Look for things to learn. Improve, improve, improve. Get faster, learn the shortcuts. Every little time saver adds up.

#1 Use Whitespace

Which is easier to read, the first SQL statement or the second. Also most editors will allow the use of control-left, control-right to skip keywords with spaces between them but without the spaces, it skips the entire line.

There are basic examples, and as-such probably bad examples, when more code is involved the readability gets much harder when spaces aren't used.

SELECT moduletitle,modulelongtitle,moduleStatus,modulelevel
FROM modules
WHERE moduleId=#moduleId#
or
SELECT moduletitle, modulelongtitle, moduleStatus, modulelevel
FROM modules
WHERE moduleId = #moduleId#
 
Example 2:
html+='<option value="'+this.freqOptionVals[i]+'">'+this.freqOptions[i];

or

html += '<option value="' + this.freqOptionVals[i] + '">' + this.freqOptions[i];

You decide.

Tags: ColdFusion | WebDev

ColdFusion MX7 European Cost BS

Dear Macromedia Adobe, any chance you could stop raping us European customers.

ColdFusion MX7 Standard Edition pricing analysis

Price for Ireland:

American price:

Difference:

  • Inc. VAT: $672.48
  • Ex. VAT: $330.32

Your European price should be €1,035.87.
note: 1 USD = 0.797434 EUR 23 Oct 2006

Conclusion

<cfif $1,299 IS NOT €1,299>
       <cf_rape suckers="Europe">
</cfif>
Tags: ColdFusion

Tabs for UDI

Those of you familiar with my UDI product should be delighted to hear I've added tab functionality to the interface!

I've been working flat-out on a rush job for a local college and they wanted tabs so I bit the bullet and made tabs a feature of UDI. It's great when I can use paid work to extend one of my components.

Seen below is a screenshot if the tabs in action on this project. You can add as many tabs as you want, it's cross browser and it makes for nicer interfaces.

UDI now with Tabs

If anybody wants to email me directly I will send them on the latest version of UDI. Otherwise expect an update available on www.cftagstore.com within a week or two.

My Scorpio (ColdFusion 8) Wish List

OK OK, I know this has been done to death, but here is my 2c. I think a lot of the requested features from other developers wouldn't really benefit me on a day to day basis but here are some things I would find useful:

  1. Components that aren't just code - Build-in support for the something like dcCom architecture I came up with. This architecture allows 'components' to store image files, flash files, javascript file etc as parts of a 'component' and allow the component to easily be invoked. One component might be a visual Ajax-style file manager, another might be a sexual flash image gallery. Having build-in support for this level of code reuse with zero configuration would be outstanding! Components are often more that use a bunch of CFML files and that should be considered.
  2. CFImage - Even though I have made and sell online my CFX_JImage component, I would prefer to see comprehensive support for CFImage built into the ColdFusion runtime, no setup required. We need to be able to crop, resize, rotate etc images easily. This feature is very important for community websites or Content Management Systems that need to resize/modify images.
  3. Better reporting in the Administration - I want to see how much memory each application is consuming, which are the most used applications, how many sessions are in each, the request frequency, the load a given application is causing on the server, the load a given template is causing on the server, queries that are slow to execute and any other useful metric you can think of.
  4. Application Level CustomTags/Library - The CustomTags directory is often not used because it is way down there at C:\CFusionMX7\CustomTags and we want our applications nicely portable. How about allowing a "_lib" folder within an applications root to contain libraries, functions and CFCs which would then be automatically available within the application. With this, users would build, share, download and use useful libraries more readily.
  5. Advanced scheduled Tasks - others have commented about how schedule tasks can be mission critical for business related software and I tend to agree. We need scheduled task that are bulletproof and that will send a notification email if it fails. Also the executed code CFML should be able to notify the ColdFusion engine that everything went OK and this should be visible from within the ColdFusion administration.
  6. Code Lockdown and Redeployment - Many developers resell their products through sites such as www.cftagstore.com. At the moment the only way to lock-down your code to prevent dabbling is to encrypt it which of course can be easily decrypted.
  7. Upgrade CFML - Take the best 100 libraries on www.cflib.org and build them in.
  8. Better Local Variables - Allow a local variable in a function to be created anywhere inside in, instead of just at the top - it's a little thing that's quite annoying.
  9. Command-line execution - of CFCs and other correctly configured CFML files. This would make ColdFusion so handy for all sorts of one-time or maintenance tasks.
  10. Speed - Make ColdFusion even faster.
  11. Programmable caching - I wrote my own dcCache component for ColdFusion which i can programmable reset through a list of reset words. This type of functionality would be great to have build into ColdFusion. Limiting caching to time-spans simply isn't powerful enough.
  12. Inter-application communication - One application should be able to send a message to another application on the same-server without resorting to SERVER variables or HTTP requests.

And here are a handful of other feature requests, I scanned from other sites and agree with:

  1. Better management of whitespace.
  2. JDK 1.5 support.
  3. Deprecate cfscript.
  4. IMAP support
  5. Ability to throw objects rather than strings
  6. Syndication support - built-in RSS/ATOM tags.
Tags: ColdFusion

Tip: Use Local Variables for Queries in Recursive Functions

Groan, it's 1.06am and my ass is sore from sitting in this position. I've been trying to figure out what the hell is wrong with my recursive ColdFusion function that copies the structure of a site into a ColdFusion structure...

I was convinced that something was wrong with ColdFusion, hell I even went as far as downloading the updater for 7.01.

Then it struck me - maybe I also have to define the variable used with <cfquery> as a local within the function. I somehow assumed that a <cfquery> would automatically be local. One <cfset Var getSubPages2 = ""> near the rest of the local variables and it was fixed. Hours later. Sore Ass. Lesson learnt.

I think I'm a ColdFusion master Messing but this was a neat blow to my ego - I should have known that. Goodnight world.

Tags: ColdFusion | Tips

..Not the best 'Pages' Design in the World...

..oh no, this is just a tribute! This is the latest Digital Crew database design for storing website pages.

I have evolved this design over the years and I'm pretty happy with this result - this design supports multiple sites, page redirection, page short name (for navigation) and page long name, page HTML (for basic pages or intro text on complex pages), meta information, visibility, security, published status, alternative URLs, alternative files, making page for special (dynamic) processing and the ability to open the page in a new window.

Use it, if you like.

Design view of MySQL 'Pages' table

CREATE TABLE `pages` (
  `pageid` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `siteId` int(11) UNSIGNED NOT NULL DEFAULT '1',
  `pageparentid` int(10) UNSIGNED DEFAULT '0',
  `pageCode` varchar(50) character SET latin1 NOT NULL,
  `pageNavName` varchar(100) character SET latin1 NOT NULL DEFAULT '',
  `pageName` varchar(150) character SET latin1 NOT NULL DEFAULT '',
  `pageHTML` text character SET latin1 NOT NULL,
  `pageDisplayOrder` smallint(5) UNSIGNED NOT NULL DEFAULT '2000',
  `pageIsPublished` tinyint(1) NOT NULL DEFAULT '1',
  `pageDescription` varchar(255) character SET latin1 DEFAULT NULL,
  `pageKeywords` varchar(255) character SET latin1 DEFAULT NULL,
  `pageIsFolder` tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
  `pageIsDynamic` tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
  `pageIsVisible` tinyint(3) UNSIGNED NOT NULL DEFAULT '1',
  `pageIsLocked` tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
  `pageAccess` enum('O','I','A') character SET latin1 NOT NULL DEFAULT 'A' COMMENT 'A=All, I=Logged In, O = Logged Out',
  `pageRedirectPage` varchar(50) character SET latin1 DEFAULT NULL,
  `pageAltFile` varchar(255) character SET latin1 DEFAULT NULL,
  `pageAltURL` varchar(255) character SET latin1 DEFAULT NULL,
  `pageOpenNewWin` tinyint(3) UNSIGNED DEFAULT '0',
  `pageShowOnInPageNav` tinyint(3) UNSIGNED DEFAULT '1'
  PRIMARY KEY  (`pageid`),
  KEY `pageparendId-pageId` (`pageparentid`),
  CONSTRAINT `pageparendId-pageId` FOREIGN KEY (`pageparentid`) REFERENCES `pages` (`pageid`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Tracking RSS Feeds

I was thinking that I'd love to have some sort of idea how many people are subscribed to this site.

The Solution

Solution 1: Some people suggest using a HTTP 301 Moved Permanently header to redirect RSS Readers to a unique URL that you can use to track the feed.

Solution 2: Others recommend that you display/make-available a unique RSS feed URL every time you display your blog.

The Problem

The problem with solution 1 is that RSS readers *should* remember that the page is permanently redirected - if they don't you'll get users always being redirected to unique URLs looking like unique subscribers all-the-time.

The problem with solution 2 is that it doesn't track your existing subscribers.

What I Decided to Do

I decided to combine both solutions and hope that there aren't too many stupid RSS readers out there. Now, If you browse to to rss/ you will be redirected to a unique URL.

Also when you view this page, both the RSS feed in the header and the feed displayed on the right hand side are unique.

The Code (in Brief)

For the redirect if URL.ref isn't defined, I used:

<CFHEADER statuscode="301" statustext="Moved Permanently">
<CFHEADER name="Location" value="#UniqueRSSURL#">

For the unique Id in the RSS URL I simply used:

replace(CreateUUID(),"-","","ALL"Wink

For logging to the MySQL RSSHits table I used:

<cfquery name="updateBlogHitCounter" ...>
INSERT INTO RSSHits(ref,blogId,hits)
VALUES (<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#ref#">,
<cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#URL.blogId#">,1)
ON DUPLICATE KEY UPDATE hits = hits + 1;
</cfquery>
Tags: ColdFusion | Tips

Source Control - 3 months later

For years we knew that we should be using source control. But we weren't. We didn't want the hassle of using source control.

But here's the thing: We make software components for ColdFusion. Some of these components are the cornerstones of our content management solutions which are in turn the cornerstone of our successful website and Intranet development business. In the past, when I made a change to UDI, a component for managing database content, I'd manually copy the code back to a central location. Then I would get the code again when starting a new project. The current version and change log were stored within the main.cfm file of each component. Painful.

What were we thinking?! About 3 months ago, I decided to take the bull by the horns and investigate the incredible complex, tedium world of source control. I got some manuals on Subversion and read a lot about CVS and source control systems. All pretty scary stuff to begin with.

But in truth, with the most excellent amazing fantastically brilliant TortoiseSNV extension for windows, source control is absolutely painless. Made changes? Just right click on the directory and select check-in, provide a short message and your done. Need the latest version, just right click and choose SVN Update. Wow, painless and simple.

And here is the thing I've come to notice, and it's a compelling reason to use source control - and not just because you should. You will be one hundred times more productive. How? You see, with the hassle of maintenance gone, I find myself making hundreds of little fixes and improvements all-the-time now.

If you make software and your not using some sort of proper source control, your officially living in the dark ages.

Tags: ColdFusion | Tips | Tools | WebDev

Chaos Blog Entry #1

OK, it's 12.16am on 4th September 2006.

I changed how projects work - I have removed most project tasks functionality from the dashboard - the logic being that somebody should really have a single project selected before things get more complex.

I updated the navigation with the list of projects. I succumbed to the argument with Dan and put the New Project link in the project list also. I'm thinking that only starred ie. active projects will appear in the main nav. To access long term projects and such, a "view other projects" link will be provided off the dashboard... or maybe in the navigation - yeah that'd make more sence... "more projects".

The goal is to keep this lightning speed like GMail - what I'm calling GSpeed. I've been analysing how GMail works and they have some pretty clever shit going on. They load an almost blank page when you click an email to open it - this makes it seem to the user that the browser is doing the usual loading thing with proper loading message and I imagine that it allows the app to track the user moving backwards and forwards in the app.

But I won't have GSpeed initially - Only the header and footer are being spat out from javascript at the moment. I want to get the functionality and tweaking and changing done right before I convert all the forms to script-based template loading - basically only raw data should ever be outputted for the main screens of the app.

I'm thinking that a top frame will hold the app keeping the URL constant. A middle frame will hold the primary data and the javascript files - preventing nasty updates-since server hits. The data frame will then load a current view data screen.

When adding a new task, I am fading out the background with a 50% transparent black "cover" div then displaying an absolutely positioned div with a higher z-index on top of it. It all works great except that the bloody cursor on the input text box ceases to work. Where I am at the moment, I don't have access to the Internet so I'll have to leave this for tomorrow nights work.

I would like to have absolutely no real hyperlinks in the application - i don't want users right clicking. At the moment, when a user hovers over a project, they get extra options appear - it allows for a nice, clean interface. At every turn I'm pausing and spending ages thinking about the design - should I remove the background color - should the star option be kept in the same place, what is the best way to list this, should this option be in a 'pop-up window' or inline? If it seems like a really simple application when I'm done, then I've achieved my goal. Definitely going to get different people to use it while I watch them and fix any issues before the first release.

For the first release, I plan to get the bare bones working - none of the niceties that Dan and Myself would love. For example there will be no sub-tasks in V1. Definitely something I want. But something we can live without for now. Also I would like to able to say, hey, "use this project as a template called X" and then users could select that template when creating a new project.

I think our app will be significantly easier to use and must faster that all the other pm apps out there. I really hope this is received well! Shit we still need a good name.

Progress has been slow. It's just me working on this so far and I have to fit this side-project in around my real job developing websites, maintaining and supporting our sites, tags and components.

Tags: ColdFusion

Rant Warning: Ruby on Rails Sucks

I think Ruby on Rails sucks. I'm sick of hearing about it.

I just watched all the videos at:

http://www.rubyonrails.org/screencasts

and i'm not excited. OK maybe "sucks" is extreme, but in fairness, it definitely is way over-hyped. It's nowhere near as great as it hype. Really!

One thing I will say, the documentation doesn't make RoR look like a piece-of-cake.

It's late - I'm too tried, lazy and inarticulate to make a decent argument so I'll leave it to the experts... just do a google search for "ruby on rails sucks". I just did... wow apparently I'm not the first to blog this exact opinion - people are sick of reading "yet-another-ruby-sucks-bait-post" so i'll shut up now.

But there, I said it. I'm happy now.

ColdFusion is a much better platform in my humble, biased opinion.

Update

Excellent - Joel shares my opinion.... and he's actually used it. And it makes interesting reading. Aside from all the hype, it looks like RoR ain't no smoking joe.

The solution is to stop playing and start over at the beginning and DO THINGS RIGHT THIS TIME, DAMMIT! Here is an actual entry from the Rails FAQ:

"Q012:That stinks... I want the scaffold code without starting over!

Too bad. Start a new rails project, run scaffold, then manually add the missing stuff to your new project from your old project with copy and paste."

Are we having fun yet?

Tags: ColdFusion | Rant

Finding the filename of the parent tag

Sometimes I've needed a custom tag to perform an operation on the actual code file that called it. However in coldfusion, there is no way (that I know of) to easily get the name or file path of the parent CFM file.

So I used to have to pass in the current file name like this:

<cf_someTag thisFile="#GetCurrentTemplatePath()#">

But yesterday, I cheated. When making the QueryWizard, I thought that the full list of executed templates is available and displayed when an error is thrown. Eureka! I simply throw a deliberate error within the tag, catch the error and extract the data I need like so:

<!--- Get the path to the caller template path by creating a
deliberate error and getting the path from the stack trace --->
<!--- I don't know of a better way! --->
<cfif NOT isdefined( "ATTRIBUTES.thisfile" )>
<cftry>
 <cfset abc = TheVarWontExist>
 <cfcatch type="Any">
  <cfset ATTRIBUTES.thisfile = cfcatch.tagContext[4].template>
 </cfcatch>
</cftry>
</cfif>

I know it probably won't work in everything (read:Bluedragon/CF5) so I still allow the attribute 'thisFile' to be passed, if required.

If there is a better way I am ignorant of, please educate me.

Tags: ColdFusion | Tips

CF_ProFlashUpload - Live at Last

I've finally released the flash upload component - the development of which I was blogging about here. Took a while for myself and Dan to come up with a good name. We were throwing all sorts of crazy ideas out there. Anyhow, please please go ahead and try it out. The demo is fully functional apart from a banner that asks you to purchase it.

I'm selling this baby for $50 per website - a fair price I think given the amount of work that went into this sucker.

What Is CF_ProFlashUpload? This widget creates a interface that allows your site user to easily upload multiple, even hundreds of files to your server. Each file has an individual progress bar and there is also an overall progress bar. The upload rate is also displayed.

 

The component allows you to use a multiple file upload component of any size as part of any ColdFusion application. It is extremely easy-to-use. You can call the component passing in just the upload directory and nothing else. Or you can use any of the advanced options to customise security options, file-type options and the look-and-feel of the component.

One of the nicest features of this component are per-file progress bars. The upload rate is also displayed along with the overall progress.

The demo is completely functional - just download it and you'll be up and running in minutes.

InfinityCMS Button for Google Toolbar

I'm just finished another small website project and it should be launched within a matter of days:

WebWorks Cork

But that's not the reason I'm posting this. Yesterday, I was searching for something completely different when I came across some pages on Google's site describing how to add your own custom buttons to the new Google BETA toolbar.

My imagination was captured (=all real work ceased Messing). An hour later (OK to be honest, about 3 hours later) and I had my very own icon for our own InfinityCMS Content Management System. Check it out:

Simply click this icon and it launches InfinityCMS

Installation: I added a page to our CMS system where the user can click a link to install this button in his/her Google Toolbar.

What Happens? Once clicked, the button sends the current URL to infinitycms.digital-crew.com. A ColdFusion page there checks the URL and performs a test to see if InfinityCMS is installed on that website.

If it isn't, the user gets a quick message displayed and is returned to where he/she was with history.back().

But if InfinityCMS is detected, a new window launches InfinityCMS and in the old window the user is again returned to where he was with history.back().

The Cool Bit: InfinityCMS is passed the URL of the page you were on when you clicked the icon... There are a series of regular expressions in the database (which can easily be updated in InfinityCMS by an administrator) that handle the request in a smart way...

For example, if the user was on the home page and clicks the icon, a window will appear allowing the user to quickly edit just the home page without all the other elements of the CMS solution:

InfinityCMS Quick Eidt Mode

And if there are several corresponding 'Quick Edit' options for a page, then the user will get a list of options. For example, on the following Quick Edit of the "Contact Us" page, the user can choose to edit the page or review messages from the contact form which have been logged in the database:

InfinityCMS Quick Edit of Contact Page - Options

Now that I've been using it for a bit, I find this little icon invaluable for making quick changes here and there. Of course if the user isn't logged into the CMS system, the login page appears first. I must add on the ability for the user to switch from Quick Edit more to full edit mode which looks like this:

InfinityCMS for WebWorks in Full Mode

Would love to know what you think of this little hack?

Update

I was giving a demo of this to Cormac and his popup blocker (Google Toobar - haha - the irony) was preventing the Quick Edit window from opening.

I remembered when the other day, GMail popped up a message for me saying that a popup blocker was blocking a window from opening so I knew it was possible to detect this. I did a bit of googling and it turns out it's pretty easy to detect:

<script language='JavaScript'>
<!-- <![CDATA[
function userHasPopupBlocker() {
var oWin = window.open('','child','width=50,height=50,status=no,resizable=yes');
if( oWin==null || typeof(oWin)=="undefined" ) return true;
else oWin.close();
return false;
}
//    ]]> -->
</script>

So now my script displays a friendly message asking to user to disable the popup blocker for the redirect site and "click here to try again". All working as it should again.

Flash Security Wrecking My Head

FFS, all I want to do is ask the license server if this component is OK to run on this website. My code was working perfectly from localhost - cummunicating perfectly with the licensing server but when I put it out in the real world, it couldn't get a connection.

Turns out Flash's security model is preventing loading data from another domain.

There are three methods (I've found so far) to allow the communication, but all of these require that the license server know who the requesting server is, in advance. So not too good for my needs then.

I think the WebService API might be able to get around this restriction... It's 2.18am however and that's another days work. This article looks positive...

http://www.adobe.com/devnet/flash/articles/flmxpro_webservices_03.html

MultiUpload Demo - 171 Files

Interesting to see that, since yesterday, users of this site have uploaded 171 files messing around with the Multiple Upload Demo.

Among the files are a plethora of interesting MP3s, excel sheets, PSD files and of course some porno - thanks Guys.

Whomever uploaded "film_ze_mucha_nie_siada.mpg" - that's just weird.

Updated Demo

I've updated the demo with a few more options and started updating the descriptions. I haven't tested all these options yet, so please let me know if you find a bug.

IE users, you may want to reset you cache as I updated the flash control and IE is brutal for not updating the cached version flash movies.

Some Minor Updates

I changed the names of some attributes such as "color" which is now "statusTextColor".

I also re-jigged some of the internal Flash default parameter coding to make the flash control smaller and more robust in that it can handle color codes in any of the following formats, "0xFF4466", "#FF4466" or just "FF4466".

It's all these little things that are delaying the launch but will ultimately make this incredibly easy-to-use and dummy-proof.

Multiple Upload Demo Updated.

Last night, I added some options and fixed one of the last remaining bugs in the Multiple File Upload Widget Demo.

Tonight I'll add more options to the demo.

I was thinking of add a 'tic' sound as each file uploads. Also don't you think the default text for the select files button should be "Select Files" and not "Browse".

One of the last things we want to add to the Flash control is licence control. From the top of my head, here's an overview of how the licensing might work:

  1. The flash component will have a secret key and the licensing server will also have this same secret key.
  2. When the component is called on a commercial website, the component will ask the server if the current website has a licence registered using its secret key to build a hash of the domain and time-stamp. The server will respond yes or no.
  3. Once the flash gets the response, it will be able to authenticate that it was the real server it was talking to based on the hash.
  4. If the server responded 'no', the "unregistered version, click here to register" text will display.
  5. The website will be sent a unique license hash for the current website that it will store and pass into the flash control for instant authentication in the future without the need to communicate with the licensing server.
  6. It will also be possible for users to download licence files for their website that prevent the need to communicate with the licensing server.

Of course all the falls flat on it's face if somebody cracks open the SWF file and obtains the secret key; and understands the licensing mechanism; and goes to extreme lengths to replica the licensing server or rebuild the licence key. But it's going to sell for $50, so so I don't think anybody would be bothered going to those lengths.

Flash Upload Demo is Up

I know some of you are looking forward to checking out the demo of the flash file uploader component for ColdFusion so without further ado, here you go:

ps. I haven't got around to adding the licence management code yet so for security I've just slapped "demo" on it for now. This demo uses exactly the following code and also shows how javascript callbacks can be used. C&C very welcome.

<cf_dccom component="dcFlashUpload" folder="#folder#"

width="500"
height="400"

jsOnFileUpload="onFileUpload"
jsOnComplete="onComplete"

color="0xFFFFFF"

themeColor="haloGreen"

blockExtensions="cfm,cfml,exe,cgi,php,php3"

></cf_dccom>

Flash Multiple File Upload Widget Preview Part II

I've been working on the Flash Multiple File Upload Widget - I think it's really getting there now that I've added in a few neat features. Here is an updated screenshot.
 
Oh Yeah, it's gonna rock
 
I'm learning a lot of advanced flash on this project; I had no idea what a CellRenderer class was just a few days ago. If I know it can be done, and I want it done bad enough, I'll bloody well keep at it until I get it done.

I managed to get the file type icons loading dynamically and I just had to put in the per-file progress bar (I was up till 3.30am last night at that).

Dan and I have agreed a final list of changes to make to this before we release it to the world. Here are the remaining work items:

  • Allow per-file callback through variable jsOnFileUpload(success,fileNo,fileName)
  • Allow user to set the nameConflict - default is "overwrite"
  • Put the stylish per-file progress bar at the bottom of the control - dump the built-in one
  • Change footer text to something like "Uploading file 1 of 20, Upload Rate: 20KB/s"
  • Allow user to turn on/off the size column through showSizeColumn="no"
  • Prevent duplicate selection for uploading of the same file
  • Add and implement maxTotalFileSize limit
  • Put striped background colours in
  • Change style on header of columns to glass effect, if thats possible
  • Put icons in for the buttons
  • Fix the sorting of the columns
  • implement stopOnError="yes"

Since I'm doing the Flash programming, Dan has agreed to make a masterpiece of the packaging and marketing of this baby. I'm hoping this baby will sell like cupcakes.

Now back to it...

Flash MultiUpload Control in the Works

On our site, CFTagStore.com, we have a ColdFusion widget called CF_MultiUpload Advanced which was programmed by Dan and I.

The advanced part of the widget uses an activeX control in a pop-up window to allow the user to easily select and upload hundreds of files.

However because Flash 8 now has powerful support for selecting and uploading multiple files, I spent Tuesday and Wednesday working on a sexual flash replacement for our old CF_MultiUpload Advanced widget. Here is a preview:

Flash Multi Upload Preview

The Flash version will mean that this will work on all platforms and browsers without the need to install an activeX control.

As you can see, I only have icons in for JPG images so far - I was to try to dynamically load the needed icons to keep the controls load-size minimal.

Also I plan to add in a second progress bar for the current file along with some stats on the current upload rate and possibly the average upload rate.

Have a few bugs to fix also before I add any more features.

Dan assures me this will sell like cupcakes.

The Last Form

Can you imagine the luxury of never having to program a form again! Well. I am here adding some functionality to a 5 year old website (the programming behind of which is so... awkward; that it really highlights how much we, Digital Crew, have developed as CF programmers over the years) and...

I was just about to program another "input form, validation, update database, send emails" combination when I sighed and thought fuck this.

I've been comtemplating updating my UDI project to be able to work standalone with single forms so that I never have to do this shit. Just pop UDI onto a page, mess with the wizard that appears and bingo - job is done.

Stay tuned, I'm about an hour into this upgrade - the time invested will reap major rewards.

Tags: ColdFusion | WebDev

Advanced Caching wit' dcCache

So it's been a week since my last post. That's pretty bad - I made a rule for myself that I'd post at least once every 2 working days. Guess I didn't have anything of note. So what have I been up to? Advanced caching for one thing...

Last Monday I decided to finally get around to adding content-caching to the Digital Crew site engine. With proper advanced caching, a single server could host up to 10 times the amount of websites, leading to cost savings and an overall improvement in website speeds. So I pulled out my old dcCache tag. I then re-factored (to use some bullshitty programming jargon) the tag bringing the code into the CFMX age. The tag works like so:

<dcCom component="dcCache" key="stuff"> CONTENT HERE </dcCom>

If the tag detects a cached version of "stuff", it just loads up that HTML page and spits it out, then exits the tag using <cfexit method="exittag">.

By the way, I had an old note in the tag saying that there was a bug with <cfexit> in which if the exittag option was called, it would still execute the content nested with the tag. I tested and this bug seems to be gone so I reimplemented the <cfexit> code.

This tag is used to cache various items around a web page. A "Top 10 Movies" content snippet could be cached for example so that it only updates when the database behind it is updated saving the execution of a query and parsing of result on every page of the site. Also an entire page could be cached. That entire page might contain the "Top 10 Movies" cached item. I got thinking about this and wanted a way so that if the "Top 10 Movies" snippet is reset, the parent cached item, the full page in this example is also reset.... I won't bored you with the details but with a REQUEST.dcCacheCurrentList variable I was able to make this magic happen.

I'm a little concerned that repeated loading the cached content from disk might be slow - it might be better to cache the most popular content in memory, but then again the operating system should know that certain disk items are being repeated accessed and make them easily available.

To reset the cache, the dcCache component is called using

<dcCom component="dcCache" action="resetlist" resetList="Top10Movies">

I used all this caching milarchy for Digital Warehouse and after some teething problem, the site now seems to be nice and fast - it's basically just spitting out pre-rendered HTML code for 90% of the web page. That's the beauty of dcCache, it can be used for just parts of a web page.

In our implementation of www.digitalwarehouse.ie, the CMS is one application and the website is another so I use CFHTTP to send a request from one app to the other to reset items when an update is made in the CMS. So far, it works pretty well. Anyone have a better idea for inter-application communication than CFHTTP and I don't want to hear about using SERVER variables - that's a no-no on a server that could host hundred of sites. :D

Oops, this post is way too long. Way to rant. I was going to give an overview of my entire week but that explanation of one days work has done me in. Stay tuned for another post later today.

Tags: ColdFusion | WebDev

My Work: NMCI

NMCII've spent the last 2 weeks working on the website for the new National Maritime College of Ireland (NMCI). It was a bit of a struggle to meet a tight deadline for their launch but we did it.

The website itself ain't bad but the CMS behind it is another evolution of our custom built InfinityCMS content management system and is pretty sweet (if I do say so myself).

One neat feature is this ability for the user to type [include:subnav] in the WYSIWYG and the website will automagically spit out an in-page navigation. Other options are [include:date], [include:datetime] etc.

Also an editor can choose up to 5 image and write [include:image1] in their text to get a nicely rounded captioned image on the page. The rounded edges are done using the nifty corners flash I showed here last week.

Then theres the right hand side items - editors can create and assign right hand side items for any page and the images are automatically scaled and rounded for them.

Oh and the site also uses the useful script I wrote to extract URL variables from the pathe path so the site is more search engine friendly with URLs like http://www.nmci.ie/index.cfm/page/undergraduatecourses as opposed to http://www.nmci.ie/index.cfm?page=undergraduatecourses

All in all, I think I did well to add all these features and still get the job done on-time.
Check out the site and hit me back, comments appreciated (Keith, this time round, please try to say something positive before you unleash hell's fury).

Code: Friendly URLs

Ever notice the way some sites would write a URl like:

index.cfm?page=somepage&x=1

as something a little more URL friendly like:

index.cfm/page/somepage/x/1

To get this, simply include the following code in your application.cfm file!:

<cfif ListLast(CGI.PATH_INFO,".") NEQ "cfm">
  <cfif Find(".cfm",CGI.PATH_INFO)>
      <cfset lenRHSofDotCFM = Len(CGI.PATH_INFO) - (Find(".cfm",CGI.PATH_INFO)+4)>
  <cfelse>
      <cfset lenRHSofDotCFM = Len(CGI.PATH_INFO)>
  </cfif>
  <cfif lenRHSofDotCFM GT 0>
      <cfset RHSofDotCFM = Right( CGI.PATH_INFO, lenRHSofDotCFM )>
      <cfset numURLItems = listLen(RHSofDotCFM,'/')>
      <cfset numURLItems = numURLItems - (numURLItems mod 2)>
      <cfloop from="1" to="#numURLItems#" index="i" step="2">
          <cfset URL[ GetToken(RHSofDotCFM,i,'/') ] = GetToken(RHSofDotCFM,i+1,'/')>
      </cfloop>
  </cfif>
</cfif>

PS. I made this site wider since I figure that anybody reading this site must be working with at least a 1024x768 resolution and the text is easier to read this way, don'tcha think.  If the site looks a little odd, try deleting your browser cache - the flash headers on the blog posts are caching big time. I've deleted Internet Explorers cache a few times now and its still cached. Looks fine in Firefox though so I guess it'll sort itself out.

Tags: ColdFusion | Tips | WebDev

Tool: Regular Expression Tester

Here is a good tool I made eons ago that from time to time I find invaluable.Topper's Regular Expression Test Tool

Next time your knocking out a regular expression for ColdFusion, load up this, paste in some sample code and keep tweaking your expression until you get it right.

Tags: ColdFusion | Tools

Developer Services Batch Files

If like me, you don't want to wait ages for all the developer services such as IIS, ColdFusion, MySQL and MS SQL to start up when you simply want to check your email/browse the web; then you might appreciate these 2 simple batch files I keep handy  to start these services if and when I want them (assumes ColdFusion 7 on windows):

Note: I appended ".txt" to these file names to allow them to download; batch files are blocked by IIS6. Just rename them and remove the ".txt" bit.

Armed with these you can set all the following services to manual in the services control so that they only start when you run the Start Developer Services batch file.

  • w3svc (IIS Web Server)
  • ColdFusion 7
    • ColdFusion MX 7 Application Server
      ColdFusion MX 7 ODBC Agent
      ColdFusion MX 7 ODBC Server
  • MySQL
  • MSSQLSERVER

PS. Didn't have a chance to work on the comments for this site over the weekend - coming soon I promise.

Tags: ColdFusion | Tips

cfTopper CMS

I think the site administration for cfTopper is pretty sweet. I should point out that the CMS for cfTopper is build on Digital Crew's InfinityCMS platform yada yada... we've been continually developing it for a number of years, slightly improving it for each new client site we develop. InfinityCMS makes is easy to knock out a custom CMS for just about anything.

Here is quick screen of what the CMS for cfTopper looks like:

cfTopper CMS

About Topper on ColdFusion

Peter Coppinger aka Topper is a neurotic web monster who spends most of his chaotic life developing ColdFusion web applications when not drinking himself into a stupor and scheming his plans for world dominance.

Peter founded Digital Crew way back in 1999. Digital Crew run CFTagStore.com and have also produced lots of powerful ColdFusion tools like ProFlashUpload and CFMyAdmin.

I made this site to share my thoughts, tips and tools with fellow ColdFusion developers.

If your a ColdFusion developer, go ahead and subscribe to this site and in exchange i'll try to provide quality content to make it worth your while.
RSS Feed for Topper on ColdFusion

    I'm speaking at CF-United Europe!

    CFDevCon I'm going to be speaking at CFDevCon08! It's my second time speaking in front of more than 10 people so please lend your support.

    The topic is:
    Introducting TeamworkCMS and Site Engine - Building better websites in half the time or something like that..

    Digging

    My Work - Just Finished

    • modules.cit.ie
      Web-=based modules/programmes designer tool and database system for Cork institute of technology.
    • Teamwork Project Manager
      The top secret project is finally released. The project management app will rock your world - give it a go.
    • PMG
      New website for Project Management Group website.
    • Digital Warehouse Wholesale
      Added wholesale products to existing client website.
    • New Digital Crew documentation website
      New version of documentation.digital-crew.com using new InfinityCMS site engine. It's done now. Just add content.
    • PFH Company Webite
      New website/CMS/Newsletter System for prestigious Irish IT company.
    • Module Manager for CIT
      CIT is switching to module based courses. We are making an application for managing/submitting these modules. Gettig there.
    • Bons Secours Cork Hospital Intranet
      New Intranet for Bons Secours hospital in Cork. Considering turning this Intranet system into stand-alone product.
    • Revamping InfinityCMS
      I'm making major improvements to our content management solution, InfinityCMS. Making it faster, more powerful and easier to check into/out-of source control. Done but it's always going to be evolving.
    • BPC Update
      Minor functionality update for internal Pfizer Best Process Chemistry project.