Thursday, 2 August 2007

Single Quotes & Query of Query

Well that was fun. Since I'm working in Ireland at the moment there's about a gazillion people with single quotes in their names.

"Query of Query" really doesn't cope very well with that.

CF automagically escapes them with 2 single quotes ('') and then any where clause doesn't find them. Not very useful.

If you use preservesinglequotes, the query is syntactically incorrect. So you get an error. Also, not very useful.

However, if you replace each single quote with 2 single quotes you end up with a param that looks like this 'O''''Reilly'. Somewhat bizarrely, that works a treat.

So, if you want to search for O'Reilly and his mates (from parameter searchValue) using Query of Query, you need to do this: -

#replace(ucase(searchValue),"'","''","all")#

That's rather horrible to read, isn't it. So to make it readable (by adding a bunch of spaces): -

#replace(
ucase(searchValue),
" ' ",
" ' ' ",
"all"
)#


But obviously, you don't want the spaces there in reality.

So there you go. Query of Query is completely mental.
Dave

Wednesday, 18 July 2007

Boy sent £44,000 in eBay parcel

According to the Beeb, somebody slipped whilst doing their ebay packing, and instead of dropping a PS2 in the box, they accidentally put £44,000 in there instead. And sent it off to an unwitting teenager.

"Result", you might think.

But no! He phoned the police instead. I bet he does always his homework and is never home late either!

More here -> bbc news article

Wednesday, 11 July 2007

Fun with Helium Balloons - Economy Class

Some nutter has just flown over 193 miles in a chair suspended from a bunch of helium balloons.

Sounds like fun...

more ->

Tuesday, 10 July 2007

It's Snowing in Buenos Aires!

"Argentina's capital, Buenos Aires, has seen snow for the first time in 89 years, as a cold snap continues to grip several South American nations.

Temperatures plunged to -22C (-8F) in parts of Argentina's province of Rio Negro, while snow fell on Buenos Aires for several hours on Monday.

Two deaths from exposure were reported in Argentina and one in Chile."

I'd just like to take a moment to say "huh?"

See full article on BBC News


Thursday, 5 July 2007

Live in a cave. Missed opportunity!

Dammit. I just found out I missed a chance to buy a cave to live in. With a door and windows and everything.

After all the times I've threatened to give it all up and go live in a cave somewhere I really missed the ball there *sigh*

Hermit heaven.. Photo's here :-
The £25,000 cave home

Big garden too... (according to the Beeb it sold for 100k)

Wednesday, 4 July 2007

Facebook

Oh yeah, another thing. I've started using Facebook (www.facebook.com).

It's a bit irritating in many ways (mostly the "I didn't think of it first" way) but kinda funky. It may steal your life though...

Want to find me on Facebook? Just search for Dave Cozens - I'm the one with the smoking monkey.

Missing, presumed missing...

Ooops. I totally forgot this was here. But I made a post today. Woo!

I'll try to remember...

Dave

Strong Encryption in MX7

Bloody hell, that was a trial that was.

I needed to encrypt some data to transfer a user to another system using a specified encryption algorithm where the service provider has supplied both the key and the initialisation vector.

Documentation is limited at best, but after beating my head against ColdFusion and Google I finally got it to work.

And it's good to share, so here we go...

1. First you need to get the Sun Unlimited Strength Jurisdiction Policy Files for Java (SUSJPFJ?)

You can get these from http://java.sun.com/j2se/1.4.2/download.html. Unzip jce_policy-1_4_2-1.zip into {jre}\lib\security\ - but it's probably a plan to back it up first. Restart CF. You'll have to restart it again in a minute, but softly softly catchy monkey...

2. Next Stop - BouncyCastle

Go and get the latest 1.4 release of the BouncyCastle service provider files from
http://www.bouncycastle.org/latest_releases.html . Last time I looked it was bcprov-jdk14-137.jar. Drop the file into {jre}\lib\ext\

Now you have to edit
runtime\jre\lib\security\java.security to include the new service provider. Assuming that you currently have the default 5 service providers, add the following immediately below: -

security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider


Restart CF again. You should be ready to go.

3. An Example

It would be mean to get you this far without some example code. So here goes.
Inline CFML:

<cfset stringToEncrypt ="MSISDN=5551112225,Culture=en-IE">
<cfset encryptionKey =tobase64("1BF03AB0CEF0AB4A7E793CE0")>
<cfset algorithm ="AES/CBC/PKCS7Padding">
<cfset initialisationVector = "C7D9769F6F261A41">
<cfset encrypted = Encrypt(stringToEncrypt, encryptionKey, algorithm, "Hex", initialisationVector)>

CFSCRIPT

stringToEncrypt = "ooh, ooh, it's a bit secret";
encryptionKey = tobase64("1CF03CF0CEF1CF4A7E733CE0");
algorithm = "AES/CBC/PKCS7Padding";
initialisationVector = "C6D3799F1F111A41";
encrypted = Encrypt(stringToEncrypt, encryptionKey, algorithm, "Hex", initialisationVector);


Tuesday, 20 February 2007

Using GetTickCount() More Effectively in ColdFusion

We've got some legacy code that we're wrapping into a Model Glue: Unity (http://model-glue.com/) environment at the moment. Some of it was behaving badly and running like the proverbial dog.

I needed something to measure performance of sections of code, possibly sections spanning multilpe templates. I needed it to report in a helpful way. So I knocked together Daves Tick Counting Machine (tm).

It enables you to drop 2 lines of code in with existing code to start performance measuring (first line=start, 2nd lin=stop, like a stopwatch).

It gives you a nice sequential, nested output at the end. And it lets you display the results either on screen, to a log file, or both.

Here's the code: -

In application.cfm: -

<cfset request.tcm=createObject("component","DavesTickCountingMachine").init(enabled=true,outputToScreen=true,outputToLog=false)>


To start/stop monitoring: -

<cfset request.tcm.toggleCounter("Counter Name")>


To view output: -

<cfoutput>#request.tcm.report()#</cfoutput>


DavesTickCountingMachine.cfc: -

<Cfcomponent name="davesTickCountingMachine" hint="Provides a method of recording performance data">
<cffunction name="init" hint="creates the tick counting machine">
<cfargument name="enabled" default="true" type="boolean" hint="Turns the TCM on (true) and off (false)">
<cfargument name="outputToScreen" default="true" type="boolean" hint="Turns the TCM screen output on (true) and off (false)">
<cfargument name="outputToLog" default="false" type="boolean" hint="Turns the TCM csv file output on (true) and off (false). File is saved in root of calling file.">
<cfset this.enabled=arguments.enabled>
<cfset this.outputToScreen=arguments.outputToScreen>
<cfset this.outputToLog=arguments.outputToLog>
<!---holds performance data--->
<cfset this.TCM=structNew()>
<cfset this.seq=0>
<Cfreturn this>
</cffunction>

<cffunction name="toggleCounter" hint="Start or Stop timing an item">
<Cfargument name="counterName" hint="The unique identifier for the section of code being analysed">
<cfset var whatTimeIsIt=getTickCount()>
<cfif this.enabled eq "true">
<cfset request.seq=this.seq+1><!--- increment seq no--->
<Cfif not structKeyExists(this.TCM,arguments.counterName)>
<!--- if the counter doesn't exist, create a new struct--->
<cfset this.TCM[arguments.counterName]=structnew()>
<cfset this.TCM[arguments.counterName].start=whatTimeIsIt>
<cfset this.TCM[arguments.counterName].seq=this.seq>
<cfset this.TCM[arguments.counterName].name=arguments.counterName>
<cfset this.TCM[arguments.counterName].stop=0>
<Cfelseif this.TCM[arguments.counterName].stop eq 0>
<!--- it's an existing counter, so this must be the end time--->
<cfset this.TCM[arguments.counterName].stop=whatTimeIsIt>
</Cfif>
<Cfelse>
<!--- it's turned off. return fast--->
<Cfreturn "">
</cfif>
</cffunction>

<cffunction name="report" hint="Returns the analysis report" output="true">
<cfif this.enabled eq "true">
<style>
.tcm{
background:##eeeeee;
padding:5px;
margin:5px;
background-repeat:no-repeat;
border:1px solid navy;
}
</style>
<!--- calculate total times --->
<Cfloop collection="#this.tcm#" item="fn">
<Cfif structKeyExists(this.tcm[fn],"start") and structKeyExists(this.tcm[fn],"start")>
<cfset this.tcm[fn].time=this.tcm[fn].stop-this.tcm[fn].start>
<Cfelse>
<cfset this.tcm[fn].err=true>
</Cfif>
</Cfloop>

<cfset tickData=arrayNew(1)>
<Cfloop collection="#this.tcm#" item="fn">
<!--- build an array so we can sort the data --->
<cfset log="">
<cfset log=structNew()>
<cfset log.fn=fn>
<cfset log.seq=this.tcm[fn].seq>
<cfset log.time=this.tcm[fn].start>
<cfset log.action="start">
<cfset arrayAppend(tickData,log)>
<cfset log="">
<cfset log=structNew()>
<cfset log.fn=fn>
<cfset log.seq=this.tcm[fn].seq>
<cfset log.time=this.tcm[fn].stop>
<cfset log.action="stop">
<cfset arrayAppend(tickData,log)>
</Cfloop>

<cfset MovedSomething=1>
<!--- now sort the data so it can be represented in the order it actually happened--->
<Cfloop condition="MovedSomething eq 1">
<cfset MovedSomething=0>
<Cfloop from=2 to="#arraylen(tickData)#" index="i">
<cfset leftItem=tickData[i-1]>
<cfset rightItem=tickData[i]>
<cfif leftItem.time gt rightItem.time>
<!--- if first item started after 2nd, then swap --->
<cfset arraySwap(tickData,i-1,i)>
<cfset movedSomething=1>
<Cfelseif leftItem.time eq rightItem.time>
<!--- They started at same time. --->
<cfif leftItem.action eq rightItem.action>
<!--- they are both the same action --->
<Cfif leftItem.action eq "stop">
<!--- they are both EOF --->
<Cfif leftItem.seq lt rightItem.seq>
<!--- assume that the item that started first, stopped last - i.e. nested --->
<cfset arraySwap(tickData,i-1,i)>
<cfset movedSomething=1>
</Cfif>
<Cfelse>
<!--- they are both SOF --->
<Cfif leftItem.seq gt rightItem.seq>
<!--- move to start seq order--->
<cfset arraySwap(tickData,i-1,i)>
<cfset movedSomething=1>
</Cfif>
</Cfif>
<Cfelseif leftItem.seq gt rightItem.seq>
<cfset arraySwap(tickData,i-1,i)>
<cfset movedSomething=1>
</cfif>
</cfif>
</Cfloop>
</Cfloop>

<!--- display it. nest child processes --->
<cfif this.outputToScreen eq "true">
<div class="tcm" >
<div style="background:navy;color:white;padding:5px;">Dave's TickCounting Machine (tm)</div>
<div style="float:left;">
<Cfif arrayLen(tickData) neq 0>
<cfset base=tickData[1].time-1>
<cfset depth=0>
<TABLE>
<Cfloop from=1 to="#arrayLen(tickData)#" index="i">
<Cfif tickData[i].action eq "start"><cfset depth=depth+1></Cfif>
<tr>
<td>#repeatString("   ",depth)##tickData[i].fn#</td>
<td>(#tickData[i].action#)</td>
<td align=right>#tickData[i].time-base#ms</td>
</tr>
<Cfif tickData[i].action eq "stop"><cfset depth=depth-1></Cfif>
</Cfloop>
</TABLE>
<Cfelse>
No data was recorded for this request
</Cfif>
</div>
<div style="float:left;padding-left:20px">
<strong>How's It Work?</strong>
<br><br>
Simple.
<br><br>1. Drop a ##request.tcm.toggleCounter("ID String")## at the point you want to start timing.
<br>2. Drop a ##request.tcm.toggleCounter("ID String")## at the point you want to end timing.
<br>3. Relax - Dave's TickCounting Machine (tm) does the rest...
<br><br>
</div>
<br style="clear:both">
</div>
</cfif>
<cfif this.outputToLog eq "true">
<div class="tcm" >
<div style="background:navy;color:white;padding:5px;">Dave's TickCounting Machine (tm)</div>
<cfset date=dateformat(now(),"dd-mmm-yyyy")>
<cfset time=timeformat(now(),"HH:MM:SS")>
<cfset path_trans=cgi.path_translated>
<cfset url_path=url.path>

<Cfif arrayLen(tickData) neq 0>
<Cfloop from=1 to="#arrayLen(tickData)#" index="i">
<Cfif tickData[i].action eq "start"><cfset depth=depth+1></Cfif>
<cfset logrow="">
<cfset logRow=listAppend(logRow,date)>
<cfset logRow=listAppend(logRow,time)>
<cfset logRow=listAppend(logRow,depth)>
<cfset logRow=listAppend(logRow,path_trans)>
<cfset logRow=listAppend(logRow,url_path)>
<cfset logRow=listAppend(logRow,tickData[i].fn)>
<cfset logRow=listAppend(logRow,tickData[i].action)>
<cfset logRow=listAppend(logRow,tickData[i].time-base)>
<Cffile action="append" file="#expandpath("tcm.log.csv")#" output="#logRow#" addnewline="true" mode="775">
<Cfif tickData[i].action eq "stop"><cfset depth=depth-1></Cfif>
</Cfloop>
TCM Data logged to: <a target="_new" href="tcm.log.csv">#expandpath("tcm.log.csv")#</a>
<Cfelse>
No data was recorded for this request
</Cfif>
</div>
</cfif>
</cfif>
</cffunction>
<cffunction name="deleteLog" hint="Deletes the log file">
<Cffile action="delete" file="#expandpath("tcm.log.csv")#">
</cffunction>
</Cfcomponent>

Monday, 19 February 2007

Rant About The Royal Mail

Oh yeah. Before I go I want to have a rant about the Royal Mail.

I ordered a bunch of stuff for my lovely wife for Valentines last week (hello lovely wife!). One of the things was a box with some balloons and some other bits and pieces (just novelty stuff). For fun.

The supplier (http://www.balloonsbypost.com/ - who incidentally have some great gift balloon packages and will bend over backwards to be helpful - and they're a charity too!) duly posted everything off in plenty of time.

Whereupon the Royal Mail decided the best thing to do with a "Next Day, Before Midday" package would be to ship it to the wrong hub, leave it in a corner and deliver it almost a week later.

They finally managed to deliver the package this morning. Rather than the requested last Wednesday. So maybe when they say "next day" they really mean "next monday".

So well done Royal Mail. Thanks very much.

/rant

Website Habits...

Some websites are like crack. You just can't give them up.

This is my daily list... just in case anybody cares...

www.thedailywtf.com
www.ajaxian.com
www.theregister.co.uk
www.bbc.co.uk/news
www.slashdot.org

Right. I'll be off. I've got a bunch of other CF and JS stuff to post later, but I'm going to ration it. If I post it all at once I'll have nothing to talk about...

Cut, Copy & Paste the ColdFusion PageContext Buffer

I've been messing about with some localisation stuff lately and I wanted a way to intercept the ColdFusion output buffer so I could mess with it.

Now I know you can sort of use <CFSAVECONTENT> for that. But it's mildy irritating since it only lets you use it on a single page. You can’t (for example) start it on one template and end it on another. Tsk!

So what I needed was a solution. After much poking things with sticks (metaphorical sticks) I found the answer I was looking for.

I’ve knocked up a CFC that gives the ability to manipulate the contents of the output buffer of ColdFusion exposing the following methods: -

  • Cut(item) – Write the buffer contents to variable named item and clear the buffer.
  • Copy(item) – Write the buffer contents to variable named item.
  • Paste(item) – Past the contents of variable named item into the buffer.
  • Get(item) – Get the contents of variable named item
  • Put(item) – Update the contents of variable named item
  • Clear() – Clear the buffer.

Here’s the code: -

Buffer.cfc

<cfcomponent name="buffer" hint="Tools for messing with the ColdFusion string buffer">
<cffunction name="init" hint="I initialise the cfc">
<cfset this.capturedBuffers=structNew()>
<cfreturn this>
</cffunction>

<cffunction name="copy" hint="Copy contents of the buffer to the named item">
<Cfargument name="target" type="string" default="clipboard" hint="Item to copy to. Default is clipboard.">
<cfset tmpBuffer=GetPageContext().getOut().getBuffer().toString()>
<cfset this.capturedBuffers[arguments.target]=duplicate(tmpBuffer)>
</cffunction>

<cffunction name="paste" hint="Paste the selected buffer item to the output stream">
<Cfargument name="source" type="string" default="clipboard">
<cfoutput>#this.capturedBuffers[arguments.source]#</cfoutput>
</cffunction>

<cffunction name="cut" hint="Cut contents of the buffer to the named item">
<Cfargument name="target" type="string" default="clipboard">
<cfset tmpBuffer=GetPageContext().getOut().getBuffer().toString()>
<cfset this.capturedBuffers[arguments.target]=duplicate(tmpBuffer)>
<cfset this.clear()>
</cffunction>

<cffunction name="get" hint="Returns the contents of the named buffer item">
<Cfargument name="source" type="string" default="clipboard">
<cfreturn this.capturedBuffers[arguments.source]>
</cffunction>

<cffunction name="put" hint="Overwrites the contents of the named buffer item">
<Cfargument name="content" type="string" default="" required=true>
<Cfargument name="target" type="string" default="clipboard">
<cfset this.capturedBuffers[arguments.target]=arguments.content>
</cffunction>

<cffunction name="clear" hint="Resets the buffer">
<Cfcontent reset="yes">
</cffunction>
</cfcomponent>

So, how do I use this buffer.cfc thing? - Here’s an example

<!--- create the cfc --->

<cfset bufferObj=createObject("component","buffer").init()>

<!--- clear the buffer --->

<cfset bufferObj.clear()>

<!--- output some text --->

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

<!--- cut the text from the output buffer --->

<cfset bufferObj.cut("firstBit")>

<!--- output some more text --->

Lorem Ipsum is simply dummy text of the printing and typesetting industry.

<!--- cut the text from the output buffer --->

<cfset bufferObj.cut("secondBit")>

<!--- Retrieve the "secondBit" from the cfc, bold all the e characters, update the cfc data--->

<cfset tmpText=bufferObj.get("secondBit")>

<cfset tmpTextp=replace(tmpText,"e","<strong>e</strong>","all")>

<cfset bufferObj.put(tmpText,"secondBit")>

<!--- Paste secondBit and firstBit back into the stream - but in reverse order --->

<cfset bufferObj.paste("secondBit")>

<cfset bufferObj.paste("firstBit")>