Posts in Web Design

< View all categories

The trace attribute of cfhttp

Posted: September 20, 2013 in Web Design | Add Comment

I occasionally find myself using the <cfhttp> tag for link testing.  In our CMS at work, I added a feature to check for valid links in topics, and flag the pages in the database to which a page was no longer associated (sometimes pages will get moved or deleted, but the database entry will remain, like some sort of e-zombie).  There's a tool that checks for these missing pages and deletes the entries, but the other day I noticed a "bug".

If the page has been redirected to a new location, the <cfhttp> tag sees the redirect as a valid link.  Even though the header is sent back as a 301 (Moved Permanently), <cfhttp> reports 200, OK.  This is obviously not ideal.  After doing some debugging, I looked at the tag options for <cfhttp>.  I'd always known about the head and get methods, but I found one I'd never used before.  method="trace".

Using the trace method, <cfhttp> reports the redirects back as 501 errors (Not Implemented?).  I edited the CMS so that rather than checking for a 301 code, I checked for the absense of a 200, and voila! The zombies are eradicated!

I took a look at the livedocs, and here's what it says about "trace":

TRACE: requests that the server echo the received HTTP headers back to the sender in the response body. Trace requests cannot have bodies. This method enables the ColdFusion application to see what is being received at the server, and use that data for testing or diagnostic information

Interesting...  or not.  Despite that rather cryptic explanation (cryptic for me at least), the thing works, so I can live with the livedocs description, and trace doesn't seem any slower than head, which is what I used previously.


Securing your ORDER BY statements (without divulging column names)

Posted: April 06, 2013 in Web Design | Add Comment

One of the most important parts of writing database-driven web pages is securing your queries.  Hopefully everyone now is familiar with SQL injection, and each language has its methods for curtailing it.  In ColdFusion, we have <cfqueryparam>, which is super cool, because it's pretty flexible and actually makes writing queries easier.  But the tag is not all-powerful, and one of the places where it's pretty much useless is in the ORDER BY clause.

To get around this, there are a few options.  One is to have various IF statements, which can get quite cluttered.  The method I prefer is to use a ListFind().  You basically supply a list of acceptable values, and if you have a match, you order by it, otherwise you order in some predetermined way.

SELECT firstname, lastname, address
FROM staff
ORDER BY <cfif listfind(URL.order,"firstname,lastname,address")>#URL.order#<cfelse>lastname</cfif>

This works well, except there's one thing I don't like about it.  If you write it in the above way, you're essentially advertising some of your column names.  That gives a wouldbe hacker some extra info he probably doesn't need to have, and that's not necessary.  I prefer something like this:

SELECT firstname, lastname, address
FROM staff
ORDER BY <cfif listfind(URL.order,"f,l,a")>#replacelist(URL.order,"f,l,a","firstname,lastname,address")#<cfelse>lastname</cfif>

This accomplishes the same thing as the first example, but hides the column names.  It also shortens your URL by allowing you to just pass a single letter.  Handy!

 


Binding data to ColdFusion form fields

Posted: March 29, 2013 in Web Design | Add Comment

I recently was working on a web application that included a form with two <select> elements.  One for a category, and the second for a subcategory.   I needed the options in the second element to change depending on what was chosen in the first element.  Some people call these "linked" or "dependent" form fields.

Getting the first element populated was easy.  I just used a query.

<cfquery name="GetCats" datasource="mydsn">
SELECT cat_id, cat_name
FROM categories
ORDER BY cat_name
</cfquery>

Getting the second one populated was of course a little trickier, and is generally done using JavaScript.  But just as there's always "an app for that" with smartphones, there's always "a function for that" in ColdFusion.  In this case, it was the bind attribute of <cfselect>.  Basically you write a component that contains the code you want executed, then refer to that component in the bind attribute.  Like with most things, it's easier to show by example.

First, the form code.

<cfform name="myform" action="#CGI.SCRIPT_NAME#" method="post">
<p>
<label for="cat">Category:</label></br>
<cfselect
        name="cat"
        id="cat"
        query="GetCat"
        value="cat_id"
        display="cat_name"
        selected="#FORM.cat#"
        queryPosition="below">
<option value="0"<cfif FORM.cat eq 0> selected="selected"</cfif>>All</option>
</cfselect></p>

<p><label for="subcat">Sub Category:</label></br>
<cfselect
        name="subcat"
        id="subcat"
        bind="cfc:path.to.my.component.fetchcats.getsubcat({cat})"
        bindonload="True"
        display="subcat_name"
        value="subcat_id"
        selected="#FORM.subcat#">
<option value=""></option>
</cfselect></p>
<p><input type="submit" name="sendcats" value="Submit" /></p>
</cfform>

Here we're binding our second select element to the first using the bind attribute.  The bind attribute calls a CFC that contains a query that gets our subcatrgories based on what's been selected in the first select element.  The CFC contains this:

fetchcats.cfc
<cfcomponent>
    <cffunction name="getsubcat" access="remote" securejson="false" returnformat="json">
    <cfargument name="cat_id" required="true" default="0" />
        <cfquery name="subs" datasource="mydsn">
        SELECT subcat_id, subcat_name
        FROM subcategories
        WHERE parent_id = <cfqueryparam value="#arguments.cat_id#" cfsqltype="cf_sql_numeric">
        ORDER BY subcat_name
        </cfquery>  
    <cfreturn subs>
    </cffunction>
</cfcomponent>

Cool eh?  Well, it falls flat in a couple of spots.  For one, unless you're running CF9 or above, there's no good way to retain the selected option in the second list.  THAT'S annoying!  Second, there is no obvious way to have a "select all" option in the second element, because as soon as you select your category in the first element, the second is overwritten with the options from the query in the CFC.  To me the best way around this was to add an "All" option to the query itself.  It works quite well.

fetchcats.cfc
<cfcomponent>
    <cffunction name="getsubcat" access="remote" securejson="false" returnformat="json">
    <cfargument name="cat_id" required="true" default="0" />
      <cfquery name="subs" datasource="mydsn">
      SELECT subcat_id, subcat_name, 1 AS selectorder
      FROM subcategories
      WHERE parent_id = <cfqueryparam value="#arguments.cat_id#" cfsqltype="cf_sql_numeric">
      UNION
      SELECT 0 AS subcat_id
            , 'All' AS subcat_name
            , 0 AS selectorder
      FROM subcategories
      ORDER BY selectorder,
subcat_name
     </cfquery>  
    <cfreturn subs>
    </cffunction>
</cfcomponent>

What I basically did here was to add some fake entries to the result set via a UNION, and a fake column called "selectorder" to ensure that the "All" option always appears first, even if it's not always alphabetically first.

Some people might think that linking two selects is easier to do with JavaScript, but for me it's easier to use CF's built-in toys.  Yeah, cfform isn't the greatest, but sometimes it makes for fast coding.


JQuery and the art of not reading books

Posted: February 24, 2013 in Web Design | Add Comment

A while ago (2011 I think), I picked up a Sitepoint book on JQuery.  I love Sitepoint books - they're almost always good - and this one promised to be pretty helpful.  The problem is, I still haven't finished it.

One thing about books for me, especially tech books, is that I need to have an active project I'm working on in order to get the most out of it.  When I ready Simply SQL (which you should read, now, if you need to learn anything at all about SQL), I had a project at work that I was involved with that allowed me to go right to my IDE and apply what I had learned on the spot.

But JQuery is different.  We don't really use much JavaScript at work, mostly because it's not 508 compliant.  I've started using it more lately, as a supplement to coding that is 508 compliant, because I think it improves the useability for most people.  This site uses a bit of JQuery, but that doesn't meen I couldn't make better use of it - if only I'd finish that damn book!

I have a pretty cool project coming up at work this summer that'll give me more opportunities to use JQuery.  I've also begun using it in our CMS, because since I know exactly who will be using it, 508 isn't much of an issue.

So everytime I find myself needing JQuery for something, I end up Googling my exact problem to find my solution.  This doesn't always work out well, as I end up finding code that doesn't always work.  I've also found that JQuery can be very finicky about how it's written.  A piece of code in my photo gallery that scrolls the thumbnail menu in the sidebar took me an entire weekend to get working.  If I bothered to read That Damn Book I'd have probably got it working in under an hour.

So my JQuery book will henceforth be known as, That Damn Book, at least until I finally finish reading it, and hopefully that'll be sometime before 2014.


Replacing items in a list with... items from another list?

Posted: February 06, 2013 in Web Design | Add Comment

My office is just finishing up a major website overhaul.  One of the main points of the overhaul (other than providing a fresh "look" to the website) was to add some automation in.  As such, we put together a partial CMS (I say partial, because the content is still on the pages and not in a database) that holds contact info for each page, the page title, and a few other items.  The CMS also contains the staff listing, which helps to supply the contact details for the pages.

One of the cool things about the new system is that it automatically builds "breadcrumbs".  A trail of links that show how you got to where you are.  They build from the URL, so something like this:

/food/sandwiches/turkey_and_swiss/

...becomes:

Food → Sandwiches → Turkey and Swiss

After a few sweeps of a regex replace that swaps the underscores for spaces and provides the initial caps.

It all worked swimmingly, until of course it didn't.  Being a govenment agency, our office has acronyms.  LOTS of acronyms.  Also, the "and" up there in the previous example would have actually been "And".  I realized pretty quickly that we needed a list of exceptions, so I started compiling one, first as a list in ColdFusion, then as an XML file, then finally as a table in the database.

The exeptions list worked fine at first, but then it got crowded, and because of the way one of our former developers wrote the code (two recursive loops, one that looped every item in the breadcrumb list, and one that for each item in that list looped through every item in the exemption list).  This had its problems, the main one going something like this:

path: /food/sandwiches/pjb/pbj_on_wheat/

Let's pretent the exception list containes the following:

Pbj > PBJ

Pbj_On_Wheat > PBJ on Wheat

The first pass across the list matches up Pbj and replaces it with the caps version, but it also matches the Pbj in Pbj_On_Wheat, making it PBJ_On_Wheat.  When the second pass across the list comes, the Pbj_On_Wheat finds no matches (it's case-sensitive), so the change never gets made (and the O stays caps!).

Clearly, I needed something more accurate.  I came up with this:

(This assumes we've already parsed the URL with the first pass that replaces the underscores with spaces and adds the title case)

<cfset dirlist = "/Food/Sandwiches/Pbj/Pbj On Wheat/">
<cfloop query="exceptions">
   <cfif ListValueCount (dirlist,old,"/")>
   <cfloop from="1" to="#ListValueCount (dirlist,old,"/")#" index="i">
      <cfset dirlist = listsetat(dirlist,Listfind(dirlist,old,"/"),new,"/")>
   </cfloop>
   </cfif>
</cfloop>

The query "exceptions" is, as you'd expect, the list of exceptions, and it's cached, so there's only one database hit on the first page view.  ListValueCount() counts the number of matches each exception has in the list (which is the url), but ONLY the exact matches.  It won't find the Pbj in "Pbj 0n Wheat" if it's only looking for "Pbj". 

If there are no matches, it returns zero, so the second loop is avoided.  If it does find one (or more) matches, the next loop makes a pass for each match (ex. one passes for one match, two passes for two matches, etc), and the ListSetAt() replaces only the item at the spot that was matched.  In our case, Pbj is the 3rd item, so the 3rd item is replaced with the exception from the list.

Since it only finds exact matches, and only replaces what it finds, it's pretty accurate (and pretty speedy, since the exceptions list is cached), so it's ended up being a pretty good solution, and not a huge amount of code.  Can't ask for much more!


No, my blog is not yet dead!

Posted: January 27, 2013 in Web Design | Add Comment

Yes, it's been a good long time since I've made a blog post, and for good reason.  Not only have I been super busy at work, with one of my colleagues retiring and my having to fill in for him, but I spend pretty much the entirety of last summer RIDING!  For 2012 I shifted more to mountain biking than I have in previous years, even signing up for, and doing the MoCo Epic (more on that in a later post).

Another thing keeping me from posting was that the forum that used to host my blog ended up with a corrupted database.  Since no one ever really posted on that forum anymore, I decided I'd go ahead and get a stand-alone blog (and this is it!).  Originally I tried to get WordPress to integrate into my existing website (boy was that a pain in the ass!), but in the end I decided to go ahead and copy the blog software I created for Jessica's website (which she has yet to use!)

Another problem with my website was my photo album.  The new version of Java that Linux ships with rendered my mass-upload feature inoperable, so if I was going to upload files to my old blog, they'd have to be one-by-one.  Well, with almost 500 pics to upload between last Xmas, this Xmas, and Temagami, you can see why I was in no hurry to start uploading.

So... in addition to creating new blog software was building an entirely new photo album (from scratch), which was no small task.  But in the end, the entire project took around two weeks of evenings and around three weekends, and here we are!

I'd also like to thank Hostek for providing me a bullet-proof hosting solution for the new site (which aside from the identical design is a total re-write).  They're a class operation.

So on to blogging, and don't forget that you can actually reply to blog posts (and comment on photos now).  As time goes, I may build in a registration/login system, but for now, we'll see how many people actually leave comments.


Figuring out the JQuery UI plugin

Posted: June 03, 2011 in Web Design | Add Comment

I've been working on the website for Jessica's new dance company, and I'm at the part where customers register for classes. Since I'm building the site using ColdFusion (otherwise I'd be working on it until the end of time), I had a few options for how to let customers pick their class date.

 

1. form text field

How lazy can I be, right?

Advantage: Mindlessly easy to set up
Drawbacks: Not very user friendly, not to mention having to deal with typos, formatting (6/3 vs 6-3), and how to block off weekends and holidays

 

2. ColdFusion datefield

This is the type="datefield" version of cfinput

Advantage: Neat calendar box popup. Easy to force formatting.
Drawbacks: No good way to block off dates, and IE likes to position the clicker around 100 pixels into outer space.

 

3. ColdFusion's <cfcalendar>

Advantage: Ability to block off dates
Drawback: Couldn't get the silly thing to work at all on my shared host.

 

So I went with option #4. The JQuery UI Datepicker plugin. This (once I figured out how to set it up, which was like figuring out how trig works by looking at an equation) gave me endless options, like the ability to block off weekends and certain dates (holidays, our vacations, etc) and even set the earliest available booking date. I was even able to integrate some ColdFusion code into it.

Voila! (this is a modified version of Christopher Altman's mod)

CODE
<cfset year = dateformat(now(),"yyyy")>
$(document).ready(function (){
  var d         = new Date();
  var natDays   = [
                     [7,4,<cfoutput>#year#</cfoutput>]
                    ,[1,1,<cfoutput>#year#</cfoutput>]
                    ,[12,31,<cfoutput>#year#</cfoutput>]
                    ,[1,19,<cfoutput>#year#</cfoutput>]];

  function nationalDays(date) {
    var m = date.getMonth();
    var d = date.getDate();
    var y = date.getFullYear();

    for (i = 0; i < natDays.length; i++) {
      if ((m == natDays[i][0] - 1) && (d == natDays[i][1]) && (y == natDays[i][2]))
      {
        return [false];
      }
    }
    return [true];
  }
  function noWeekendsOrHolidays(date) {
    var noWeekend = $.datepicker.noWeekends(date);
      if (noWeekend[0]) {
        return nationalDays(date);
      } else {
        return noWeekend;
    }
  }
  $(function() {
    $("#datepicker").datepicker({

      minDate: '<cfoutput>#dateformat(dateadd("d",2,now()),"mm/dd/yyyy")#</cfoutput>',
      maxDate: '+2Y',

      hideIfNoPrevNext: true,
      beforeShowDay: noWeekendsOrHolidays,
     });
  });
});

Now what could be cooler than that?


A small note about the background image on this page

Posted: October 09, 2010 in Web Design | Add Comment

A few people have asked me how I made the background image of this page. Well, it's basically this image, which I took in Temagami this year:

Temagami at night

The process went like this:

  •  Select the dark area below the tree line, and invert the selection to get the sky.
  •  Delete the sky, and apply a greyscaled gradient to this area.
  •  Re-select the tree area, and make sure it's all black and clean up some various noise.
  •  Apply a blur effect a few times, then re-sharpen it (this gave it a distinct separation between the trees and sky).
  •  Mirror the image to keep the light area out of the blog posts.
  •  Resize it, then crop it at a point that would create a repeating pattern, with no obvious seam.


For the bottom "reflection" half:

  •  Flip the image from the top of the page.
  •  Apply a wave effect to it.
  •  Resize it to make it around 40% shorter for the same width.
  •  Toss in a few CSS tricks to be able to have two background images on the same page.


That's pretty much it, other than repeating the process for the "Light" page scheme. I like the effect, and it resizes perfectly for different size pages.


A new look and feel

Posted: October 05, 2010 in Web Design | Add Comment

If it's been a while since you stopped by (like more than a few days) you'll probably notice a few changes. Well, more than a few. After sticking with my old layout for at least five years, I decided it was time for a change.

I've given the site a new look, and I've reduced the number of pages (that I have to update, haha) in order to simplify things. Besides, most people come here to read my occasional rants, and to check out my pics. And of course, I had to throw in a bit about my bikes.

I'm not finished yet, but the big stuff is done. I'll undoubtedly make a few tweaks as time wears on. But for now I think the site is a bit less cumbersome.

Enjoy, and if the dark background is a bit hard on your eyes, slide over to the right and switch to the light page style. I like to be all-inclusive.



Using CSS to style tables

Posted: June 13, 2009 in Web Design | Add Comment

Ok, I haven't made any tech geeky posts in a while, so I thought I'd go ahead and throw this in. If nothing else, it'll give me an easy place to find it when I forget it myself.

Even though using tables for web page layout is pretty much considered a no-no these days, we still need tables when presenting tabular data. The problem is, if you're using an XHTML strict doctype, attributes like border="1" are no longer valid mark-up. So, how do we handle tables in CSS? Here's a quick reference list to most of the common table attributes.

HTMLCSS
cellpadding="0"td { padding: 0;}
cellspacing="1"table { border-spacing: 1px; }
border="1"td { border: 1px solid #000; }
valign="top"td { vertical-align: top; }
align="right"td { text-align: right; }


So this:

<table cellpadding="5" cellspacing="0" border="1" width="600">
<tr>
<td align="center" valign="top">

Can be be written as:

<table>
<tr>
<td>

With the following CSS:

table { border-spacing: 0; width: 600px; }
td { padding: 5px; text-align: center; vertical-align: top; }

Other useful tidbits:

border-collapse (collapse|separate|inherit): When using the border property on both the table and the td, you'll get a double border between cells. Use border-collapse to compress it into a single border.

empty-cells (show|hide|inherit): Specifies whether to show empty cells or not. No more non-breaking spaces required!

Also remember that you can set top, bottom, left, and right padding to different values.There are advantages to using CSS over the old method.

  • Different padding values for all sides of the cell.
    With cellpadding you can only have a single value, but with CSS, you can have different values for each side.
  • Different border thicknesses
    With the old border= method, you had a single value. CSS borders can not only be thinner, but they can be dashed, dotted, or a number of different things. Plus, you can have different borders for each side, or no border for some sides. And the table itself can have a different border than the cells.
  • Table margins
    You can also use CSS to add margins around the outside of your table. This is not possible with attributes at all. If you have two tables on top of each other, you would normally need to use a space between them to separate them. With CSS, you don't.

The sky is pretty much the limit when using CSS. Experiment and see what you can come up with.


Web hosting hell

Posted: February 27, 2007 in Web Design | Add Comment

Good web hosting is getting harder and harder to find. I host a number of websites - my own, a political forum, plus a few odd and end sites. For a while I had these pages scattered across several different hosts - all on plans that expired at different times. A real pain.

In the past year or so, I noticed some hosts offering the option to host multiple domains on one account, so I decided to consolidate. After poking around, I chose a host called BlueHost.com. The first couple of months were fine, but then I started to notice problems, mainly a recurring 500 server error affecting all my sites. Having all of my sites down for hours at a time, a few days a week, wasn't too much fun, so I contacted tech support. Unfortunately, I got a canned answer: "Clean up your php code".

Of course, the code on my site was/is the same code I've been using trouble-free for years, and the forum is off-the-shelf software, so I knew neither of those were the problem. Not to mention the dozens of other people on Bluehost's support forums with the same exact problem. I'm thinking maybe they had a problem with Zend or something - hard to say.

As my hosting plan at Bluehost approached the end of its year, I started looking for alternate hosting. It's a jungle out there, and there's more hosts than you can shake a stick at. After a dubious recommendation from a friend, I reluctantly signed up for a hosting plan with GoDaddy.com. Danica Patrick stared me in the eye as I filled in my info, as if to say, "Haha, sucker...". I completed the purchase process, only to find a nightmarish control panel, rife with flashing ads and check boxes trying to sell me additional features. I tried to ignore them, and struggled to set things up.

After a day of fiddling, I phoned tech support. I have to admit, their tech support is great, but it had better be, because you will need it. I spent most of the second day uploading my sites, and trying to create databases. I say "trying" because even that's no picnic. I'm used to ssh access (not offered by godaddy) so I had to rely on their hellish control panel to set things up. Their database upload utility was useless. I had to hand-edit all the comments out of my exported database files, and they had a max database size of 2mb (the forum db is 45+ mb) so you have to have tech support create anything larger.

After several calls and a few emails to tech support, I was ready to preview what I had uploaded. No can do, unless you purchase a dedicated IP address for six bucks a month (almost as much as the hosting itself). After learning that I could cancel the dedicated IP after a month, I bit.

Then it was time to create email accounts. I created the accounts for my primary domain without incident, but when it came to creating the accounts for my 4 other domains, I got stuck. Another call to tech support revealed that I needed to purchase (of course) additional email packages for my remaining accounts.

Now things were getting expensive. Far more expensive than a typical host. I decided to cut my losses and cancel the account. Of course, godaddy is probably the only host in existence with no money-back guarantee, so I ended up eating the hosting costs. Lesson learned.

But honestly, I've been doing websites for a while, and have been on and off several different hosts. They all include pretty much the same thing, so I was pretty amazed to learn that email for each domain was not included with godaddy. It's like they took the standard webhosting package and cut it into pieces to sell separately. "Your car is ready sir. No, sorry, there's no radio, AC, spare tire, wipers, or headlights. Those are extra".

If you ever need a good web host, check out a site called HostSearch.com. You can plug in exactly the options you're looking for, and it gives you results that match your criteria. They also have actual customer reviews, so you can sort by the highest rated. Discard the results with less than 10 reviews, and you've got some good results. A quick check of the customer forums (assuming they have them) will give you an idea of how the customers like the service.

I ended up choosing Downtownhost.com, and they seem good so far. Really good in fact. Very quick to respond to email inquiries, very quick to reply to my one tech support question, and very personable. The servers seem lighting fact compared to my previous two hosts, and all the standard features were included at no additional cost.

I signed up at 3pm on Monday and by 6am Tuesday morning, all my sites were live. That includes going out for dinner Monday night (while my files uploaded) and going to bed at 11pm. So we're talking a few hours worth of work. Good tech support is nice, but not needing it at all is even nicer.


How weird will this blog be?

Posted: March 23, 2006 in Web Design | Add Comment

I'm still trying to decide how to handle this new blog setup. Until now, I kept a seperate blog on my website that existed only on my website, and thus had a limited (a very limited) audience. Now my blog is integrated into this politically themed website, and that throws a wrench into things.

First off, I have been trying to de-politicize my blog (and in fact my entire website) for a while now, so this new method will inject more politics back into things. Second, I have two completely unrelated groups of people who will be viewing. It's like a big Venn Diagram (which is really just a fancy spin put on the old "intersection and union" thing we all learned in grade school) where you have these two huge circles and they're joined by one little dot: me.

So now there will be circumstances where I may be talking about something that's happening on the forum that the non-forum people won't have a clue about, or I could be talking about a biking thing that has no place on the forum. And on top of that, my website will show comments that cannot be read unless one is a member of the forum. It all has the makings of a huge shemozzle.

I do have the ability to create seperate categories, so I could have a category for my webpage that's hidden from the forum, and vice versa, but the whole point of this was to have one blog and not a bunch of blogs, whether they're combined in the same place or not.

I still have no idea what to do. (P.S. I'd love to use a smiley here, but they look like crap when displayed on my website)

View all categories >