Thursday, February 08, 2007

DevNotes On AJAX

As a long-time user of the FLiP methodology, there have been many times where I've created a prototype and used Hal Helms' DevNotes as a plonk-and-play way of capturing clients' thoughts and feedback.

It's a nifty little tool that does the job it sets out to do - but since it was released way back when, teh intarweb has moved on, and I've had a few niggly things that I had to work around when using it :

  • it's rendered using "old-school" HTML, with things like FONT tags

  • sometimes you can get conflicts between the DevNotes form submissions and parameters that the containing prototype app requires

  • the dependency on cfx_make_tree.dll didn't play nicely with a non-Windows OS

So, while noodling around over the weekend recently, I decided to give it a bit of a facelift. One thing led to another, and it ended up as a pretty major update. So laydeez and gennullmun, allow me to present....(drum roll)

DevNotes On AJAX!

Main Changes:

  • Removed dependence on CFX_Make_Tree.dll,
    replaced with udfMakeTree.cfm from, by Michael Dinowitz
  • Ported to be based on Ajax requests,
    de-coupling the DevNotes requests from the Prototype page requests, and allowing people to stay on the same Prototype page while adding notes
  • Added RSS syndication of notes.
  • Prefixed all request variables and JS functions with DEVNOTES
    to prevent any potential conflicts with the containing app
    Required parameters in the containing app are now:

    // these fields required for DevNotes
    request.DEVNOTES.devAppName = "UniqueAppname";
    request.DEVNOTES.attributeToKeyOn = "page";
    request.DEVNOTES.DevNotesDSN = "YourDSNName";
    request.DEVNOTES.devnotesRSSTitle = "Title for the RSS feed";
    request.DEVNOTES.DevNotesRSSDescription = "Description for the RSS feed";

  • Updated HTML to be more semantic and standards-based.
    Admittedly it might not be 100% XHTML-compliant, but I don't really have time to check it on that - be my guest...
  • Introduced CSS styling
    and grayscale mini icons from Brand Spanking New
  • Re-factored the code into a fusebox3-stylee app structure.

Example usage:

Extract the archive into a subfolder called "DevNotes" under the root of your prototype app.

Make sure that the following variables are set up somewhere in your app - fbx_settings.cfm is usually the best place :

// these fields required for DevNotes
request.DEVNOTES.devAppName = "UniqueAppname";
request.DEVNOTES.attributeToKeyOn = "page";
request.DEVNOTES.DevNotesDSN = "YourDSNName";
request.DEVNOTES.devnotesRSSTitle = "Title for the RSS feed";
request.DEVNOTES.DevNotesRSSDescription = "Description for the RSS feed";

Then in your prototype's LAYOUT file, just add
<cfinclude template="../DevNotes/DevNotes.cfm" />
(replace the path as necessary)

NOTE: don't put this in OnRequestEnd.cfm, as it will get triggered for each AJAX request and land you in an infinite recursive loop! The layout file is the best place for it, as it's display-generating code.

Usual usage policy : you can do what you like with this code, so long as
a) you maintain the attribution comments
b) you accept that it's provided entirely as-is, with no warranty of any sort
c) you don't violate any terms in the other code that it includes (see credits within the code)
and most importantly
d) you don't have too much of a go at me for not commenting it particularly well!

Enjoy !

DevNotes On AJAX


JC said...

Seems pretty buggy. Might be because I'm using SQL server 2000 and you were apparenly testing on MySQL.

First it couldn't find some of the required variables so I had to hardcode them in the fbx_settings file for devnotes, then it broke on "showNote = TRUE" which should probably be showNote = 'TRUE', now it's saying "Invalid operator for data type. Operator equals add, type equals text."

And I'm getting the messages in two places... one above the form, which only updates if I reload the page; one below the form, which updates on the fly.

jc said...

The RSS bit seems not to work properly, had to hardcode the paths for those to work (unix system you're working on I suppose?).

It also doesn't play well at all with OnRequestEnd.cfm, which was where I was hoping to place it. Infinite recursion is not a friend. :(

Alistair Davidson said...


Thanks for your comments, all debugging help is welcome, and sorry it didn't work for you more easily.

Which required variables were missing? I re-scoped them to be "request.DEVNOTES.XXXX" to remove any chance for accidental conflicts, and although I said that in the "Main Changes" section of the post, I should probably have explicitly said that in the "Example Usage" section - now added, sorry if that was unclear.

As part of hacking this together, I went through all the queries and put cfqueryparam's in - I guess I missed one! Yep - in qry/qry_allnotesrss.cfm - now fixed.

Where does that "invalid operator" error occur? If you could send me a stacktrace, I'll be able to sort it out. Email address is on the right.

The messages appearing in two places is actually an AJAX debugging aid. If the AJAX request returns a 404 or 500 status code, it logs whatever content it receives out to a div below the form.

NOTE: this is from a call to DEVNOTES_log("whatever")

So if the AJAX requests are baulking on a query issue, then that might be the cause.

What's the problem you're having with RSS? I was developing on a Windows PC and deploying on UNIX, so I'd be surprised if it's a cross-platform issue.

An OnRequestEnd.cfm is likely to get triggered for all the AJAX requests aswell, so yep, that's a recipe for infinite recursion. The layout file is the best place to put display-generating includes anyway. I've added a cautionary note into the instructions now.

I've uploaded a new version with those fixes in - If you have any more trouble, give me a shout.

JC said...

Thanks for the response Alistair. At the moment I've popped Hal's version in, since it works quite reliably and the only place I can put it for this project is OnRequestEnd.cfm. I'm not working strictly with Fusebox, though several of the applications I'm integrating were done in various versions of fusebox (couple in some FuseBox 2 variant, couple in FuseBox 3).

If I can get yours working, that'd be the best choice on any new development we do since that would be primarily in fusebox, or if nothing else something where there's a single global footer.

I'll grab the latest and see what happens when I get a few.

Re: your questions...
All, or at least many, of the required variables were missing (and yes, with that specific scope). I think it's probably an issue with how CF processes Application.cfm, OnRequestEnd.cfm, and other code between the two... I've had problems with other code as well, where a value would be available at one point and then no longer available later on.

I ended uphaving to change showNote = TRUE to showNote = 1, IIRC... just a difference between MySQL and MSSQL I suppose.

IIRC the invalid operator was from concatenating text in SQL, which should work fine but apparently CFMX + MSSQL didn't like (stuff like Author + ':' + NoteText AS vcTitle; attributeToKeyOn + '=' + aKeyValue AS vcLink).

The RSS issue I mentioned was that it wouldn't write the RSS file. I had to hardcode the path before it would write the file out.

Alistair Davidson said...

A thought just occurred to me about the OnRequestEnd.cfm thing:

How about if you put an Application.cfm AND OnRequestEnd.cfm in the DevNotes directory?

They don't have to do anything much, but so long as those files were there in the subdirectory, they should override the files from the containing app. That should get round the infinite recursion problem.

As for the text concatenation problem in MSSQL, maybe the best way round that would be to just query for the "raw" fields from the DB, and then Query-of-Queries to concatenate the text. I'll sort that out when I get half an hour or so.

Shaun W. said...

Hello Alistair.

Great idea to revamp the old devnotes!

However after installing and following your example usage I'm getting the following error...

Variable is undefined.

On this line...

request.DEVNOTESPARAMS.keyValue = evaluate( 'request.DEVNOTESPARAMS.' & request.DEVNOTES.attributeToKeyOn );

...of the fbx_settings.cfm file

Any ideas?

Thanks in advance.

Shaun W. said...

This is only compatible with IE6?

In FF all of the notes and note input areas are over lapping...

Alistair Davidson said...

Hi Shawn,

that's funny, I do all my dev on Firefox and it works fine on mine - although there may be some conflicting CSS attributes, I guess.

If you have a URL, I'll take a look over the weekend. You can email me privately if you'd prefer (click the link on the right side panel for my address)

Randy998 said...

I am getting the same error as Shaun above.

Randy998 said...

I added this a couple lines above the error and it works now.

cfparam name="" default=""

Alistair Davidson said...

Thanks Randy!

Randy998 said...

Here is another tidbit for those of you that want to put this in onRequestEnd.cfm

cfif getFileFromPath(cgi.CF_TEMPLATE_PATH) is not "index.cfm"
cfinclude template="devnotes/devnotes.cfm"

The idea here is that the ajax calls call index.cfm so we don't want to fire the ajax notes on that page.

This has worked for me so far.

Randy998 said...

One more comment. I created a framed page that has the links of every page in the left pane and the right pane loads the pages. You need to pass in the page name via the URL so the notes program will store the notes for that page, otherwise every page will have every note.

example: test2.cfm?page=page2.cfm

I haven't checked but I would imagine you could use anything for the page identifier as long as you were consistent.