To learn all about Skin Spider, click here.
To view the most updated application, click here.
To view the current code base, click here.
In my last update, I had created an entry page to verify age of the user. This was for the protection of our youngsters. It did this checking the user's http referer URL. Someone let me know that their firewall or router was not sending out any http referer information. This basically cut them off from entering the site entirely because I kept assuming that they were just entering the site (and not coming from within it).
To fix this, I am now only including the age verification on the index.cfm page. All other pages on the site can be freely accessed. This is not great security, but at least people can get into the demo application. This touches on a good example though: the difference between an index.cfm home page and a home.cfm home page. In my applications, traditionally, my main home page is home.cfm. My index.cfm page then just includes the home.cfm file. I have found this very useful for creating splash pages and intermediary pages. This age verification is just such a case in point. By having all internal home page links go to home.cfm, we can choose to (or choose not to) treat calls to index.cfm differently than calls to home.cfm (on initial site entry). This is some nice flexibility.
In the Application.cfm file, also regarding the age verification, I had to move the age verification check to the very bottom of the Application.cfm file. Originally, it was very near the top (for tighter security). The problem this was causing is that calls to reset the application (ex. index.cfm?reset) were never being executed because the age verification was taking over control before the reset code was being executed. Now that I have moved it down to the bottom, the reset code can execute and THEN the age verification stuff can be run.
I have made several changes to the DatabaseService.cfc ColdFusion component. Firstly, I finally started using the UpdateRecordByKey() method and was able to start debugging it. There were some issues with the code I wrote, but nothing huge that debugging wasn't able to point out. Of particular note, I was not referencing my query properly and I was not flagging the table as dirty when the query was updated.
The biggest change to the DatabaseService.cfc ColdFusion component was the references to the Commit() function. In the component, when you add or delete a record, you have the option to delay committing the data to file. This allows the application to make several database changes on a single page before the "Dirty" data is written to file for persistence (this is performance optimization). Originally, I was just calling "Commit()". This started to cause errors because there was a naming conflict; since I was NOT scoping the call to commit, ColdFusion started to crawl up the scope chain to find the reference "Commit". The first scopes it hit were the local function scope and the ARGUMENTS scope. Well, in those functions, I had an argument "Commit" that was a boolean flagging delayed committal. ColdFusion though that this is the one I was referring to (first reference found) and tried to call it like it was a method (ex. ARGUMENTS.Commit()). It of course was NOT a function pointer and threw an error.
To overcome this, I started to scope the calls to commit: THIS.Commit(). This took ColdFusion out of the guessing game and starting calling the right method pointer at the right time.
I got my first "add / edit" page up and running. This one just adds or edits a tag in the database. Tags are used to categorize video files. This page will act as either an add or an edit page depending on whether or not it has a valid tag ID passed to it. If no ID is passed, then it thinks it is adding a new tag. If an ID is passed to the page and it corresponds to an ID in the database, then the page thinks that it is updating a tag and acts accordingly.
As you can see in the code, the FORM simply submits back to itself with a submission flag (a hidden field, submitted). This allows us to keep the modification logic for this one item in a single place. Not to say this won't change later, but for now, it is working very nicely.
My forms can always handle a refresh of the page without valid data. In the case of invalid data, the form is allowed to re-render with the user-entered data displayed. This gives the user the chance to modify what they have entered and re-submit. When a form page is initially loaded, I get the information out of the database and store it in the form fields. This way, when the form renders it just uses FORM data; it doesn't have to concern itself with where the data came from (the database or a form submission process).
I am sure I have touched on this before, but you will see that on all my form pages, the page errors template is included. This is so that we can add any errors to the errors array during pre-page processing and they will show up nicely. But, more than just for display, we are using the REQUEST.PageErrors array to determine if we have valid data. Once we submit a form, we validate the data. After we validate the data, we check to see if there are any errors in the REQUEST.PageErrors array. If there are, then we know the data is not valid. If there are no errors, then we know the data IS valid and we can update the database.
Once the data processing is completed, I simply return the user to the listing page from whence they came. I am not currently displaying any confirmation message. Perhaps I will work that in later. This is something that I ususally do when I have session management, but as that is not in use, no messages.
I have my first delete page up and running. This one just deletes a tag from the database. This page requires a valid ID (an ID that corresponds to an ID in the database) because we cannot delete something that doesn't exist. If no valid ID was passed to the page, then I simply relocate the user back to the listing page.
When it comes to deleting an item, you have to worry about its relation to other items in the database. In this case, tags can be associated with videos. This is a one to many relationship because multiple videos can be associated with the same tag. In some cases we do not want to allow relationships to be broken. In this case, however, I have deemed these relationships to be of low importance. Therefore, when a tag is deleted, I am automatically deleting all associations to it.
You will see that on the delete_tag.cfm page, I am listing out all the videos that are linked to this tag. This is merely to provide the user with some feedback as to what changes they are about to effect. In cases where you do not want to allow the user to destroy these types of relationships, you can use this list to allow the user to jump to linked items to systematically remove the associations in a more appropriate way (ex. editing each video and un-associating that tag from it prior to deletion).
This page is also just a simple demonstration of how to perform INNER JOINS in ColdFusion query of queries. I join the video table to the tag_video_jn table to figure out which videos are joined to the current tag.
Image And Video Spideirng
One of the biggest "break throughs" of the day, though, had to go to an improved spidering system. I had some serious issues with my CFHttp tag and CFHttpParam tags. It boiled down to me no understanding how CGI variables were represented in ColdFusion vs. how they are passed around outside of ColdFusion. Basically, I was passing in, as the referer url, the ColdFusion CGI.http_referer value. Some sites were not recognizing this. As it turns out, and you can see this in the old Macromedia help files (See: http://livedocs.macromedia.com/coldfusion/6/CFML_Reference/Tags-pt155.htm), when you pass a CGI variable via CFHttpParam, you don't include the "HTTP_" part of the name. Check out spider_gallery.cfm and spider_video.cfm to see more about this. It's a really minor detail with a HUGE impact.