<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0">
	<channel>
		<title>Blog</title>
		<link>http://www.sitecrafting.com/blog//</link>
		<description></description>
		<language>en-us</language>
		<pubDate>Wed, 16 May 2012 23:16:25 PDT</pubDate>
		<lastBuildDate>Wed, 16 May 2012 23:16:25 PDT</lastBuildDate>
		<generator>SiteCrafting.com GearBox 1.1 (beta)</generator>
		
		<item>
			<title>Baby Congrats Lunch and Bowling</title>
			<link>http://www.sitecrafting.com/blog/baby-congrats-lunch-bowling-1/</link>
			<description>So last Friday was a great afternoon. After visiting our customer&amp;nbsp; Primo Grill for a celebration lunch, we proceeded to Chalet Bowl for a couple games of bowling. It was good to get out of the office for a bit to celebrate the upcoming birth of Hiromi's baby (no, we don't know if it is a boy or a girl yet, but will soon). We all pitched in and purchased a Baby Bjorn and a baby wipe warmer. Kevin claims that a happy baby is a baby with warm wipes. Speaking from experience, I'm not so sure. Sometimes nothing, not even promises of hundred dollar bills will silence a crying newborn.We had a fantastic time, complete with an out of body bowling experience by SiteCrafting's own David Poole. Dave's penchant for turkeys (three strikes in a row) allowed our team to coast to victory in both games. To view more pics, have a look at Kevin's blog</description>
			<pubDate>Mon, 09 Oct 2006 09:51:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Strange Behavior with Opera 9 and Safari</title>
			<link>http://www.sitecrafting.com/blog/strange-behavior-opera-9-safari/</link>
			<description>While I was working on the latest site for Airstream, I came on some extremely weird behavior concerning the navigation. First off, the designers did an extremely good job with the visuals. On the other hand, I had to be really creative to figure out how to make various parts of the site work.Here's some background for how the navigation was supposed to work: there are five sections to the site, and each section has sub sections that show up in the navigation bar, depending on what your current location is. Navigation and sub-navigation. Also, the navigation area has a graphical background that can't change. Since the PNG spec isn't fully supported by IE yet, we had to go with GIF or JPG graphics. Here's what it is supposed to look like (and what we ended up with):Initially, I had decided to put all the navigation data on each page.  Each navigation section would have the section image, and a div for the  sub-navigation. The sub-navigation would be set to display: none; unless the parent div had the class active, when it would be display: inline;#nav_list div {&amp;nbsp;&amp;nbsp; &amp;nbsp;float: left;&amp;nbsp;&amp;nbsp; &amp;nbsp;border-right: 2px dotted #555;}#nav_list div div&amp;nbsp; {&amp;nbsp;&amp;nbsp; &amp;nbsp;display: none;&amp;nbsp;&amp;nbsp; &amp;nbsp;border: none;&amp;nbsp;&amp;nbsp; &amp;nbsp;padding: 0;&amp;nbsp;&amp;nbsp; &amp;nbsp;margin: 0;}#nav_list div.active .subnav {&amp;nbsp;&amp;nbsp; &amp;nbsp;display: inline;&amp;nbsp;&amp;nbsp; &amp;nbsp;margin: 0;&amp;nbsp;&amp;nbsp; &amp;nbsp;padding: 0;&amp;nbsp;&amp;nbsp; &amp;nbsp;margin-top: -140px;&amp;nbsp;&amp;nbsp; &amp;nbsp;height: 141px;&amp;nbsp;&amp;nbsp; &amp;nbsp;width: 241px;&amp;nbsp;&amp;nbsp; &amp;nbsp;background: url('../images/nav_background_off.gif') repeat-y 30px;&amp;nbsp;&amp;nbsp; &amp;nbsp;margin-left: 6px;&amp;nbsp;&amp;nbsp; &amp;nbsp;border-right: 2px dotted #555;}There were a few issues that came up while creating the site that related to this CSS,  such as if the overall height of the sub-navigation was zero (the sum  of the margin and height), the layout breaks. I didn't understand at  the time why this was happening, but after going to An Event Apart  Seattle, I can see why it happens. (A zero height means that an item  doesn't exist. This might be an interesting way to show and hide  elements...)Anyhoo, this all worked fine in Firefox while I was developing it. IE was another issue, but IE is an issue in itself.  (The active sub-nav item has a lime background, so it's easier to see what I'm talking about.)When I went to test this site in Safari and Opera, the craziness started to happen. Here's what it looked like in Opera:And here's Safari:The strangest part of this behavior is that both Opera 9 and Safari have passed the ACID 2 test, and yet here they have totally different results. Fixing this didn't involve any CSS  trickery, it actually ended up being somewhat less-than-elegant.  Because I didn't have to be too concerned about the page resizing  (fixed width design), I simply set position: absolute; on the active .subnav. This forced it to start at a specific location, rather than where ever it pleased. I am guessing that the .subnav was set to display: block; although I didn't specifically define it. Setting the it to position: relative;  may have worked as well, but I didn't try it. I'm really curious to  find out why Opera 9 and Safari behaved so differently with the same CSS and HTML, and why Firefox was able to render the CSS as expected.</description>
			<pubDate>Mon, 09 Oct 2006 11:31:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Welcome to Our Blog!</title>
			<link>http://www.sitecrafting.com/blog/to-blog-1/</link>
			<description>Welcome, and thanks for coming to the long-anticipated Blah Blah Blog! What you're going to find here is a rare peek behind the curtain at the inner workings of a leading Website and Web Application Development company. Our aim is to make this Blog different than the billions of other Blogs you could be reading. So start by expecting the unexpected.  For starters, we won't be afraid to reveal secrets or point out how we've learned from our mistakes. The main focus will be technology ? but we'll also include proprietary Web tips &amp;amp; tricks, relevant business news, stories from the workbench, and more. Use the navigation on the right to get around, and check back regularly. Your category choices will grow as we add posts. And you never know what you'll find from day to day.</description>
			<pubDate>Wed, 11 Oct 2006 12:24:00 PDT</pubDate>
		</item>
			
		<item>
			<title>The Three 'M'-igos</title>
			<link>http://www.sitecrafting.com/blog/butter-fingers-break-apps/</link>
			<description>Happens all the time, right? One minute you're cranking out line after line of quality code then the next it all comes to a screeching halt for no apparent reason. I immediately go into troubleshooting mode. A quick and simple redirect using a header(&quot;Location: ...&quot;) function should pose no danger of messing up my app -- but today it did.It kept throwing me to a &quot;Page Not Found&quot; message and, since it was  technically only moving me to the exact same script, I couldn't see why. For such a simple line of code to push things so far out of whack seemed preposterous. Well, it's always good to check your digital ego at the door and go through it all rationally.Here's how I thought this out:the redirect tossed me to the same script yet gave me a &quot;Page Not Found&quot;any GET variables I passed were fine and weren't even being relied upon heavilythe server was running fine since the previous and other pages were displaying just fineI was redirecting to the correct URL -- wait a minute!Turns out I wasn't at all. I was routing the script from comments.php to commments.php and that just ain't right. I suppose when you have so many of the same characters in a row the brain tends to skip over them once in a while. Whatever the reason, it took me a few minutes to catch and I just had to chalk it up to another hard lesson learned since I neglected to believe in Occam's Razor. Next time, I'll check to see if it's spelled correctly first.</description>
			<pubDate>Wed, 11 Oct 2006 19:29:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Using What We Build</title>
			<link>http://www.sitecrafting.com/blog/we-build-1/</link>
			<description>It's not often that I use the stuff we build. It's not that I don't support the businesses we do business with, it's more that I spend so much time online thinking of how we build things for our customers that I don't generally get a chance to actually use the websites we build in the way a customer would.I'm selling my house and as part of that process in Pierce County a  seller needs to complete a Report of System Status (RSS) and Operation  and Maintenance Report for a septic system. In order for your house to  close, you need to have a certified septic inspector pump your septic  system as well as fill out a form with the Tacoma Pierce County Health  Department. Once your system is marked as &quot;A-OK&quot; your sale is able to  go through. Prior to our intervention, a seller would need to  get an inspection, deliver the completed form to the Health Department  (don't forget the long line), wait for a response, then get a clean  bill of health to pass on to the new owner. This is much  easier now, thanks to SiteCrafting, although at the time it was built I  was not so sure how much easier it would make it. The answer is, a lot  easier. I looked up my as-built drawing here.I registered and added my septic company here.They posted my completed report here.I paid...and it was done...no lines, no problems...kinda makes me wish others built things to work so well.Other cool things we've built for TPCHD include:Online Permitting for Food Service OrganizationsFood Service Establishment ReportsA pretty cool Website Management Application</description>
			<pubDate>Thu, 12 Oct 2006 23:13:00 PDT</pubDate>
		</item>
			
		<item>
			<title>A Piece of Query Cake</title>
			<link>http://www.sitecrafting.com/blog/piece-query-cake/</link>
			<description>If you've ever created SQL queries with PHP, you probably know what a pain it can be to create insert and update statements. I really, really (really) don't like it. As I was working on my personal site, and exploring possible frameworks to use, I came across CodeIgniter. They have a great database interaction library, especially the function for creating the insert queries.Today, armed with only the descriptions of CodeIgniter's query helper functions, I spent 20 minutes trying to duplicate some the effect of the insert and update functions. I've never seen the code, or even used it, but I didn't have to see the code to write a similar function. Both functions take a table name and an associative array of column names and values. The update function also requires a WHERE statement, and it can't be blank. This is different from CodeIgniter, and that's so you don't accidentally reset all of the passwords in the mysql users table, or any table for that matter. And then, *poof*, the function gives you a nice sql statement.I'll never have to write another &quot;INSERT blah blah blah&quot; again. Yay!&amp;lt;?php/**&amp;nbsp;*&amp;nbsp;Generates&amp;nbsp;an&amp;nbsp;insert&amp;nbsp;sql&amp;nbsp;query&amp;nbsp;from&amp;nbsp;the&amp;nbsp;parameters&amp;nbsp;*&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$table&amp;nbsp;The&amp;nbsp;name&amp;nbsp;of&amp;nbsp;the&amp;nbsp;table&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;array&amp;nbsp;$array&amp;nbsp;An&amp;nbsp;associative&amp;nbsp;array&amp;nbsp;with&amp;nbsp;the&amp;nbsp;values&amp;nbsp;similar&amp;nbsp;to&amp;nbsp;column=&amp;gt;value&amp;nbsp;*&amp;nbsp;@return&amp;nbsp;string&amp;nbsp;The&amp;nbsp;sql&amp;nbsp;query&amp;nbsp;*/function&amp;nbsp;getInsertSQL($table,&amp;nbsp;$array)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;'INSERT&amp;nbsp;INTO&amp;nbsp;'.$table;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$columns&amp;nbsp;=&amp;nbsp;'';&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$values&amp;nbsp;=&amp;nbsp;'';&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach($array&amp;nbsp;as&amp;nbsp;$key&amp;nbsp;=&amp;gt;&amp;nbsp;$value)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$columns&amp;nbsp;.=&amp;nbsp;$key.',&amp;nbsp;';&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if($value&amp;nbsp;!=&amp;nbsp;&amp;nbsp;&quot;&quot;)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$values&amp;nbsp;.=&amp;nbsp;&quot;'&quot;.addslashes($value).&quot;',&amp;nbsp;&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$values&amp;nbsp;.=&amp;nbsp;&quot;'NULL',&amp;nbsp;&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$columns&amp;nbsp;=&amp;nbsp;substr($columns,&amp;nbsp;0,&amp;nbsp;-2);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$values&amp;nbsp;=&amp;nbsp;substr($values,&amp;nbsp;0,&amp;nbsp;-2);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;.=&amp;nbsp;&quot;($columns)&amp;nbsp;VALUES&amp;nbsp;($values)&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$sql;}/**&amp;nbsp;*&amp;nbsp;Generates&amp;nbsp;an&amp;nbsp;update&amp;nbsp;sql&amp;nbsp;query&amp;nbsp;from&amp;nbsp;the&amp;nbsp;parameters&amp;nbsp;*&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$table&amp;nbsp;The&amp;nbsp;name&amp;nbsp;of&amp;nbsp;the&amp;nbsp;table&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;array&amp;nbsp;$array&amp;nbsp;An&amp;nbsp;associative&amp;nbsp;array&amp;nbsp;with&amp;nbsp;the&amp;nbsp;values&amp;nbsp;similar&amp;nbsp;to&amp;nbsp;column=&amp;gt;value&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;string&amp;nbsp;$where&amp;nbsp;What&amp;nbsp;to&amp;nbsp;limit&amp;nbsp;the&amp;nbsp;update&amp;nbsp;to.&amp;nbsp;Cannot&amp;nbsp;be&amp;nbsp;blank.&amp;nbsp;*&amp;nbsp;@return&amp;nbsp;string&amp;nbsp;The&amp;nbsp;sql&amp;nbsp;query&amp;nbsp;*/function&amp;nbsp;getUpdateSQL($table,&amp;nbsp;$array,&amp;nbsp;$where)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(trim($where)&amp;nbsp;==&amp;nbsp;&quot;&quot;)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;'UPDATE&amp;nbsp;'.$table.'&amp;nbsp;SET&amp;nbsp;';&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach($array&amp;nbsp;as&amp;nbsp;$key&amp;nbsp;=&amp;gt;&amp;nbsp;$value)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if($value&amp;nbsp;!=&amp;nbsp;&amp;nbsp;&quot;&quot;)&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;.=&amp;nbsp;$key.&quot;='&quot;.addslashes($value).&quot;',&amp;nbsp;&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;.=&amp;nbsp;$key.&quot;='NULL',&amp;nbsp;&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;=&amp;nbsp;substr($sql,&amp;nbsp;0,&amp;nbsp;-2);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sql&amp;nbsp;.=&amp;nbsp;&quot;&amp;nbsp;WHERE&amp;nbsp;$where&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;$sql;}?&amp;gt;</description>
			<pubDate>Fri, 13 Oct 2006 10:59:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Cross Table Content Search</title>
			<link>http://www.sitecrafting.com/blog/cross-table-content-search-1/</link>
			<description>One of the more interesting adventures working with data storage is trying to aggregate information meaningfully from multiple very different data structures. Imagine you've got a website filled with content (say, a few hundred pages). All the content is stored and output dynamically - who wants to create and maintain 500 static html pages, anyway? And of course, you need a bit of variety, so all this content is spread across five different page designs, each requiring its own template and data structure.Now you say you want to search your site? All the content? And you want the results all together in one big happy sorted-by-relevancy list? How on earth am I going to do that?Well... like this.BackgroundOur basic data structures are pretty well cemented for dynamic front-end content. Every page has an entry in a core table (we'll call it page). The page entry has all the data that is universal. ID number, meta information, title, as well as parent and order numbers (for navigation hierarchy. In addition, we have a template id and a content id.The template id points to an entry in page_templates. Here you find the template names and, more importantly, the content table names. Remember that each template needs its own table as content block layout varies. With content table name and content id handy, you can leap to the actual page content.The SearchSearching a single table's content is a relatively easy matter. Put a FULLTEXT index on the relevant fields and runSELECT idFROM tableWHERE MATCH(search_columns) AGAINST (search_string)Since you designed the table, you know what fields to search on, right? Heck, this would work even for multiple tables. Just loop through the searches, writing a custom set of search indices for each straight into your queries.This is all well and good when you're working on a search for a single website. But working for a fairly prolific development company, who conveniently use the same basic table layout for the vast majority of their content managed front-end sites, the smart move is to write a function that discovers these indices on its own. This can be achieved using SHOW INDEX FROM content_table. From there, parse the relevant indices like so:while($row = mysql_fetch_assoc($res)){if($row['Index_type'] == 'FULLTEXT'){$search_cols .= $row['Column_name'].', ';}}Stick $search_cols up there in your single-table search, and suddenly you've got a dynamic FULLTEXT search that you can use on any table.Aggregating the DataUnfortunately, on a seven template site you've still got seven different sets of search results. The FULLTEXT search will return each result set in weighted order, but the user doesn't care what's using what template. They want the whole list to be in weighted order. To handle this, I tossed in a bit of array processing. First, restructure the query a bit to actually return the weight value itself:&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT id, MATCH(search_columns) AGAINST('search_string') AS weight&amp;nbsp;&amp;nbsp;&amp;nbsp; FROM content_table&amp;nbsp;&amp;nbsp;&amp;nbsp; WHERE MATCH(search_columns) AGAINST('search_string')I ran this for each content table, and appended all the results onto a single array. From there I just wrote a simple php usort comparison function to sort on the table weight (and obviously the queries were expanded a bit to draw some actual useful information, beyond just the page id).Of course, with anything that works, the big question is always &quot;How can I make it better?&quot; So... how can I make it better? Right now our production servers are running MySQL 4.0 (to paraphrase Peter Zaistev, as long as it works, why put a whole lot of data in danger for an upgrade that isn't necessary?). I've got some notions regarding putting more of the load on the database side, but those will take stored routines (5.0) and/or subqueries (4.1). But with where we're at now, does anyone else have a notion of how to improve this solution? Or perhaps a completely different solution to the same problem?</description>
			<pubDate>Fri, 27 Oct 2006 11:34:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Into the Nested Table Abyss</title>
			<link>http://www.sitecrafting.com/blog/nested-table-abyss-1/</link>
			<description>While I was taking some time to evaluate a 3rd-party application for a customer I took a peak at their site's HTML. I was pretty shocked when I found some very strictly organized code that contained gobs of nested tables. At one count I found a structure that was 10 tables deep! I suppose since enjoying creating clean mark-up and mostly table-less designs for the last few years I've forgotten my roots.Warning: Don't click to enlarge the following unless you have a strong stomach.Table-heavy  layouts used to be big and I was certainly guilty of a few back 'round  the turn of the century. Nevertheless, it's pretty shocking for me to  see pages laid out like this today. They are even kind enough to  include plenty of inline styles so we know what's what color. Yikes!  I'm pretty sure I speak for everyone here in the office when I say,  &quot;Good riddance!&quot;Tables are good for propping food up to where it's easily eaten not for propping up modern web designs.</description>
			<pubDate>Fri, 13 Oct 2006 16:56:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Smart Keyword Searching</title>
			<link>http://www.sitecrafting.com/blog/smart-keyword-searching-1/</link>
			<description>Earlier this week the brand new Pierce County Library website officially launched. It was a pretty complex build that took a lot of effort between ourselves and the library's staff to make sure that everything worked right for the site's users. One of the more important features on any website with a large amount of content is a solid search function, and this site was no different. With over 300 pages (and still growing), a user could easily have some difficulty tracking down the information that they are looking for. So what can you do to make finding information easier?Well, an intuitive structure for the pages on the site is a good start. Well-written copy and Meta information can also make it easier for a user or your search function to key in on what is important on a page. But even doing all this can still end up with more clicks and additional searching than are really needed.To try to alleviate some of this, we decided to use a slightly modified method for searching all the content on the site. In addition to searching page content and Meta information, the search function we developed now checks a table of keywords for matches. These keywords can be associated with a specific page on the site, which allows administrators a way to create some &quot;most likely&quot; matches.Administrator view of the smart keywords and the associated pagesPerforming a search for &quot;catalog&quot; will show a list of pages on the site that have something to do with the library catalog. What sets this search apart from any other search, say &quot;summer reading,&quot; is that there is a &quot;Quick Results&quot; section at the top of the list. If the search term that you've searched for is present in the keyword table, this section will highlight the page that administrators think is most relevant.Of course, this requires a bit of legwork up front to create a number of keyword and page associations, but it can greatly cut down on the frustration that your users can experience. The flexibility of the keyword mapping function also allows administrators to keep their search function current as certain keywords become more popular. When there is a large amount of data to search through, sometimes it can be tough to find the best info to show a user. By setting up these associations, you can help users find the information they're looking for a little quicker.</description>
			<pubDate>Tue, 17 Oct 2006 11:33:00 PDT</pubDate>
		</item>
			
		<item>
			<title>File Extensions</title>
			<link>http://www.sitecrafting.com/blog/file-extensions-1/</link>
			<description>I've been using a fairly complicated function to grab the extension of an uploaded file. I have run across many instances where I have wanted to split up the file name from the file extension, or just find the file type. Since MIME types are not reliable enough for grabbing a true extension, I have been using this aforementioned function.The following is a snippet of code that I had been using to retrieve the extension of a file:&amp;lt;?php$filename = 'image.jpg'; //name of file$nombres = split('.', strtolower($filename));$nombre_start = '';for($k=0; $k&amp;lt;count($nombres)-1; $k++){&amp;nbsp;&amp;nbsp;&amp;nbsp; $nombre_start .= $nombres[$k];&amp;nbsp;&amp;nbsp;&amp;nbsp; }$nombre_ext = $nombres[count($nombres)-1];?&amp;gt;However, there had to be an easier, cleaner way to get this information. So here is the code I came up with the following code for retrieving the extension of a file:&amp;lt;?php$filename = 'image.jpg'; //name of file$ext = substr(strrchr($filename, &quot;.&quot;), 1);?&amp;gt;In this case, printing $ext would give you &quot;jpg&quot;.Grabbing just the filename was a bit more involved, but still relied on the same principal:&amp;lt;?php$filename = 'image.jpg'; //name of file$name = substr($filename, 0, strpos($filename,substr(strrchr($filename, &quot;.&quot;), 0)));?&amp;gt;    In this case, printing $name would give you &quot;image&quot;.</description>
			<pubDate>Mon, 16 Oct 2006 12:57:00 PDT</pubDate>
		</item>
			
		<item>
			<title>What Customer Service Should be Like</title>
			<link>http://www.sitecrafting.com/blog/customer-service-be/</link>
			<description>I recently received a gift certificate to a popular online merchant, aka Company. I'm not telling who Company is, because I like them, and I don't want my comments to be taken the wrong way. The entire thing was a fiasco, but I ended up with a nice warm and fuzzy feeling, simply because the merchant actually cared about my experience. Here's the important parts:If something goes wrong, tell the customer whyNever have an action with out a success or error messageIf you can reasonably accomodate the customer's request, do soRespect your customer's scheduleIf the customer isn't happy, ask them whyI won my gift certificate in a raffle, and to actually get it, I had to email one of Company's marketing people. That part was easy, and so I pointed my browser to Company's online store. I happily added a bunch of stuff to my cart. Once I had filled up my cart with schwag, I went through the registration and information process, and ended up at the payment page.On that page, there was a nice field for adding gift certificate numbers, and so I put mine in and hit 'Submit'. Nothing happened. No error message, no confirmation, no indication that I'd entered a valid number. There was no way that I would proceed from here, because I didn't want to deal with a refund hassle, so I just emailed my contact at Company. She told me that she would get in touch with the people that manage their store, and let me know when it was fixed.The next day, she sends me an email saying that the store is now fixed, and that I should try again and let her know if the problem persists. It does, so I return the email. Then she connects me directly with the store managers at the merchant (Merchant), and I get a new contact (my third, there was one in-between the lady at Company and this one), and am told that I'll either have to call their toll free ordering phone, or order via email.I much prefer to email people than to call them, especially if I don't know the person I'm calling, so I email off my order. I ask that they remove my credit card number from their mail program when the order is complete, and they happily oblige. Finally, my free stuff is coming.About a day later, I receive a shipping notification from Merchant, which lists one of the items $2.00 more than is displayed on the site. Normally, I'd be a bit ticked off, but this time I decide the fight is not worth the effort, especially because I am getting a ton of free stuff from them. The following Monday, my stuff arrives, and I'm happy. The next day, I receive a request to fill out a survey about my experience, and I let them have it. I give them the lowest possible score on their webstore, and on product knowledge (the difference in price caused that). I also put in that I'd like to see trinkety stuff in the store - keychain, stickers, pens and the lot - because it would have been fun to plaster Company stickers all over everything I own (I know, I'm a tool). I send off the survey, thinking &quot;That's the end of that, no one follows up on these things&quot;. Two days later, I receive a phone call from a guy named Jason (name changed), right as I start my lunch break. Nice timing, Jason. That shows that they care enough to not interrupt my work day.Jason wants to know if he can ask me some questions about my survey results. After a my brain resets, I answer his questions. (Conversation is paraphrased)Jason: Why did you give us a low mark on product knowledge?Me:&amp;nbsp; Because the pricing was inconsistent between the website and manual ordering. I didn't want to pursue it because I got a free certificate, and it didn't seem worth it.J: Oh, ok. And why the low mark on our website?Me: Because your gift certificate processing system didn't work, forcing me to order via email. (It's probably because I got the certificate direct from Company, and something wonky happened with the database.)J: I see. Well, we are working on that, and hopefully it won't happen again. Also, I want to let you know we're looking into getting the stuff you recommended.Me: Wow, cool. Even though the ordering experience sucked, I like how customer friendly both companies are. They were prompt, answered my questions, and made sure that I was a valued customer, even though I was getting most of the stuff on their dime. The survey follow up was especially important, because you have to actually pay people to call up customers and ask why they had a bad experience with the process. I imagine that job is terrible, but it made me feel like they care about me. And that's something you don't see very much in the Internet industry.
</description>
			<pubDate>Fri, 20 Oct 2006 09:17:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Navigation Nightmare pt. 1</title>
			<link>http://www.sitecrafting.com/blog/navigation-nightmare-pt-1-1/</link>
			<description>foreach ($left_elements AS $le) {&amp;nbsp;&amp;nbsp;&amp;nbsp; ?&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;table class=&quot;leftnav_&amp;lt;?=$le['class']?&amp;gt;&quot;&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;tr&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;td class=&quot;leftnav_&amp;lt;?=$le['class']?&amp;gt;&quot;&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;a href=&quot;&amp;lt;?=$le['target']?&amp;gt;&quot; class=&quot;&amp;lt;?=$le['theme']?&amp;gt;&quot;&amp;gt;&amp;lt;?=$le['name']?&amp;gt;&amp;lt;/a&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/td&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/tr&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/table&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?}Nobody wants to see that. Especially not seven different times with seven tiny variants covering seven different if conditions.Each higher 'class' number is a deeper indentation. Apparently it's necessary for this class identification to be repeated for the table, table cell and link. Converting this to a nested list format (part of my current task) is going to be an adventure.Well... at least it's indented nicely.</description>
			<pubDate>Wed, 18 Oct 2006 10:46:00 PDT</pubDate>
		</item>
			
		<item>
			<title>IE 7 Out and About</title>
			<link>http://www.sitecrafting.com/blog/ie-7-1/</link>
			<description>Seems like the cat's out of the bag. Well, IE 7 is now available for download, anyway. Good to see Microsoft update their browser software after five years away from the party. Can't say I'm personally looking forward to this install but even if the only thing better about IE 7 is PNG support, I'm all for it.Download IE 7 and let us know what you think! You can be sure we'll be doing extensive client testing and will post our thoughts as they solidify.</description>
			<pubDate>Wed, 18 Oct 2006 22:57:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Firefox 2.0</title>
			<link>http://www.sitecrafting.com/blog/firefox-20/</link>
			<description>Hot on the heels of the Internet Explorer 7 release, Mozilla Firefox 2.0 is slated for official release tomorrow, but, as often happens, they have posted the full release version up on the mirrors already, so you can find what you're looking for at ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/2.0So, some first thoughts... most of it is same old Firefox. Slightly different look (not that it matters to those who skin their browser anyway). I haven't gone out seeking fraudulent sites to see how well Firefox warns me of them, so at this point I'll have to take their word for that one. The inline spellchecker is quite nice. It manages to be visible without being obtrusive when marking misspellings, with word recommendations available via right-click.The biggest advantage that Firefox 2 has over something like IE 7 (beyond all the things Firefox already had over IE) is this: it works. Some articles I've read have wondered why Mozilla didn't push a little harder to beat IE to the punch, release-wise. Well, the reason is simple: they wanted to release Firefox when it worked, and not a moment before. IE 7 is out there crashing Windows and shutting off audio plug-ins (or so my co-workers' testimonials would have it). Meanwhile, I downloaded Firefox today, and the only things that didn't work instantly were a couple of user-created extensions, for which the application immediately sought out, found and installed the updated 2.0 compatible versions. Everything is working as smoothly as (or smoother than) ever.Microsoft is far from alone in this kind of error. Apple's iTunes 7.0 was, quite frankly, a disaster. If you have to start releasing such major bug fixes a week later, well... wouldn't you be better off releasing your application a week later, actually working? I sure think so.I'm sure I or someone else here will be back for a more thorough review once we've had a little more time to properly digest the new features.</description>
			<pubDate>Mon, 23 Oct 2006 10:50:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Group By Queries for MS SQL Server</title>
			<link>http://www.sitecrafting.com/blog/group-by-queries-ms-sql/</link>
			<description>Back in the day, we worked on developing a specialty application that was basically a lead generation system with a database that stored over 200,000 records, with a potential for a lot more. The web application displayed numerous reports that calculated totals from disparate sources. We discovered that once our client began adding all their data that those reports were running slower and slower and slower. The problem was that we had one primary query to pull the records out, then, as the code looped through each record, several other queries were needed to calculate the disparate totals. That resulted in numerous database calls that slowed the entire web application. That's when Mike discovered MySQL Wizardry, that used the SUM(IF()) and the GROUP BY clause, problem solved.One way to improve the speed and efficiency of a dynamic page is to pull all the needed information that is to be displayed in one query, that's where the MySQL Wizardry comes into play. This wizardry performs cross tabulations, which are great for statistical analysis. If you're planning on creating reports, I strongly suggest you read this article.Ever since we discovered this methodology, I've used it on numerous specialty applications, from calculating amounts and costs for a shopping list to determining totals for outstanding invoices.Below is an example of the MySQL Wizardry query.MySQL Query SELECT SUM( IF( company_size = &quot;large&quot;, 1, 0) ) as vLargeCount, SUM( IF(company_size = &quot;medium&quot;, 1, 0) ) as vMediumCount, SUM( IF(company_size = &quot;small&quot;, 1, 0) ) as vSmallCount, SUM( IF( company_age &amp;lt; = 4, 1, 0) ) as vYoungCompaniesCount, SUM( IF( company_age &amp;gt; 4, 1, 0) ) as vOlderComaniesCount, COUNT(companies.id) AS vTotalCompanyCountFROM companiesThe key here is the SUM(IF()) statements, this simple little piece of query magic aggregated a number of different queries into one query and turned out to be extremely efficient. As a side note, it's also important to index fields that are are used for the LEFT JOIN, in this case campaign.id and contacts_mail.campaign.Now, the Pierce County Library was developed in ASP.NET 2.0 and MS SQL Server 2005, but I wanted to apply the MySQL Wizardry magic to MS SQL server. The problem was that MS SQL Server does not have an IF statement, but this was easily remedied by using the CASE statement. Below is an example of the MySQL Wizardry for MS SQL Server.MS SQL Server QuerySELECT movies.title, movies.date,COUNT(movie_reviews.review_id) AS vReviewCount, SUM(CASE movie_reviews.thumbs WHEN &quot;UP&quot; THEN 1 ELSE 0 END) AS vThumbsUp, SUM(CASE movie_reviews.thumbs WHEN &quot;DOWN&quot; THEN 1 ELSE 0 END) AS vThumbsDown, FROM&amp;nbsp; movies LEFT OUTER JOIN movie_reviews&amp;nbsp; ON movies.code = movie_reviews.movie_codeGROUP BY movies.title, movies.dateORDER BY movies.date DESCVoil�, SUM(IF()) is replaced by SUM(CASE) and we now have MS SQL Server Wizardry. There are two things that are important to note about this query. First is that MS SQL Server GROUP BY queries are much more restrictive than MySQL's GROUP BY functionality. Each data field (NOT expressions) in the SELECT statement MUST BE in the GROUP BY clause. The second item is that MS SQL Server does not allow ntext, text, and xml data types in the SELECT statement when performing a GROUP BY. There may be a way to include these data types, but since I was short on time and didn't really need those fields and decided to tackle this problem at a later date.Tweet</description>
			<pubDate>Tue, 24 Oct 2006 09:41:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Printing without the Dialog</title>
			<link>http://www.sitecrafting.com/blog/printing-dialog/</link>
			<description>One day, one of our clients came to us with a very unusual request - they wanted to be able to print something directly from the browser, but without displaying the usual print dialog box. I don't have much time in the webdev business, but I've never heard of this kind of request, and neither had anyone else in the office.Initially, we didn't think that it was possible. The browser File &amp;gt; Print command uses a dialog box. So does the javascript print() function. The only reason that we knew this was possible was that the client's current system was already doing it. With a little research, we figured out how to print directly from the browser without displaying a dialog box.Tools you'll need:1 local Unix or Linux serverA pinch of Javascript1 PHP exec() callPHP to format your dataFirst: your server MUST be a local server, on the same network as the printer. This is because we'll be using the lp command to shoot off the print job.Second, to get this to work from the browser, you need a little javascript to call a file using XMLHttpRequest that will call the lp command and execute the print job.The actual command to start off the print job looks something like this: lpr -p printer file.txtThe final ingredient of this system is to use PHP to format the data you need to print, and then save it to a file. The lpr command then works on that file.In the end, we were able to get this whole thing working, but only because Dustin over at out co-location facility knew what needed to be done. Part of the command line printing involves manually creating a print spool - a task not for the faint of heart. The spool is defined in /etc/printcap, and looks something like this:printer:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :ml=0:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :mx=0:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :sd=/var/spool/lpd/printer:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :af=/var/spool/lpd/printer/printer.acct:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :sh:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :rm=XXX.XXX.XXX.XXX:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :rp=text:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :if=/etc/magicfilter/ibmpro-filter:\&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :lpd_bounce=true:Replace the XXX string with your printer's IP address. Then, when you click on a print button, the javascript fires off the XMLHttpRequest, which calls the PHP script to create the print file, and executes the command to print that file. Or, you can call the script with a PHP function on a page load.Shout outs to Dustin and Andrzej, for making the printing work, and for getting the scripts to call the print action.</description>
			<pubDate>Mon, 09 Apr 2007 16:36:00 PDT</pubDate>
		</item>
			
		<item>
			<title>MS SQL Server 2005 text and ntext</title>
			<link>http://www.sitecrafting.com/blog/ms-sql-server-2005-ntext-1/</link>
			<description>How I discovered that text, ntext, and image data types have been deprecated and replaced by varchar(max), ntext(max), and varbinary(max).Recently, I needed to perform a query to update a number of links for the Pierce Count Library website. I thought I could use a simple REPLACE() string function, unfortunately, this turned out not to be the case. In order to update text and ntext datatypes using a query you're limited to a few functions, in my case I would be forced to use SUBSTRING() or UPDATETEXT().My original plan was to update all links in a field using a query like this:UPDATE web_pages SET web_pages.article1 = REPLACE( web_pages.article1, &quot;http://bad_link//&quot;, &quot;http://&quot;)Of course, when I tried this, I got an error that ntext datatypes are not allowed for the REPLACE function. So, I went online and begin searching for a function similar to REPLACE() that would work with ntext, that's when I found the msdn page for working with text, ntext, and image data types. I could have figured out a way to use SUBSTRING() or UPDATETEXT(), but  I determined that it would be much quicker to just look for the records that contained the bad links and update them manually.Later, I decided to do some research on the subject, because I just couldn't believe that there wasn't a way to use simple string functions with text and ntext. That's when I came across an informit article about Date, Math and Text Functions in SQL Server 2000 that explains how string functions don't work with text and ntext. Undaunted, I continued researching some more and that's when I found out that text, ntext, and image data types have been depracated for SQL 2005 and replaced by varchar(max), ntext(max), and varbinary(max). In fact, in future releases text, ntext, and image will no longer be supported. View the list at Deprecated Database Engine Features in SQL Server 2005.If I had only known. I would have designed all the fields to use nvarchar(max) instead of ntext. This would have solved a couple of problems for me. The first problem is that ntext data types can't be used in GROUP BY clauses and the second I would have been able to update the bad links a whole lot quicker by being able to use string functions. And if there was a need, I would have been able to use string functions in select statements to manipulate the text.</description>
			<pubDate>Fri, 27 Oct 2006 11:25:00 PDT</pubDate>
		</item>
			
		<item>
			<title>Why We Build Our Stuff from Scratch</title>
			<link>http://www.sitecrafting.com/blog/we-build-stuff-scratch/</link>
			<description>I had an interesting conversation with a potential client last Friday and it prompted me to think or at least explain our development rational.&amp;nbsp; He was looking to hire SiteCrafting to assemble a robust application for his client consisting of a design he came up with as well as some &quot;open-source&quot; technologies, and a few custom scripts generated by us. Long and the short of it...it seemed a disaster in the making. I mentioned to him that SiteCrafting builds our Web applications from the ground up, for each customer as an individual. We do not offer one-size fits all solutions, because just like anything one-size fits all it never fits anyone quite right.Using pre-built software in these instances can on the surface seem to be a cost-savings, but in fact it will most likely cost more money trying to piece technology together. A system built on workarounds from a variety of technologies will consistently need to be tweaked and modified. At SiteCrafting, we design our solutions to fit the organization perfectly, not just  most of the time, all of the time. Our process is simple, we learn about  a project, we design a solution, we build, test and deploy. The result is custom application built for you...not for the masses. Very few people utilize all of the features in an off the shelf solution like Microsoft Word or PowerPoint, but everyone pays for all of the features. Our model is different, establish a secure, scalable foundation, build what you need and will use, and pay for only that. When needs change in the future our flexible systems will be able to grow with you to adapt to your organizations needs.</description>
			<pubDate>Mon, 30 Oct 2006 13:04:00 PST</pubDate>
		</item>
			
		<item>
			<title>Bulletproof HTML Critique</title>
			<link>http://www.sitecrafting.com/blog/bulletproof-critique/</link>
			<description>I ran across a neat article from Sitepoint via Stylegala that you all should read on how to make your HTML 'perfect'.&amp;nbsp;In the world of design, the concept of perfect is non-existant. There is no solution that will work for solving every problem, and there are exceptions to every rule. This article is no different. Read the full article, and then check out my comments.FAQ #1-#7I can't really say anything about these, because they are factual definitions.#8    Yes, HTML 4.01 Strict is a good choice, but XHTML 1.0 Strict, or even better, XHTML 1.1, is preferable. If you need to use deprecated tags or attributes, you really should use HTML 4.01 Transitional. We use XHTML 1.1, for a number of reasons, but we do use some prohibited attrributes (title on tags other than abbr and acronym, target) and syntax (our rich text editor in the cms mucks up the code). We do this because we know the strengths and weaknesses of each decision we make, and we don't make them lightly.#9It's good to make sure your markup validates, but I know that this article won't and I know why. Some errors can be tolerated (like the ending '/' on image tags.), and some can't - a misplaced &amp;lt;/table&amp;gt; destroys your layout.#10Too many times I've seen sites where the HTML was terrible, but it rendered properly. Most designers want to make their sites look right, but it's the attention to detail that separates the good from the great.#11-#14Again with factual definitions. I also got bored halfway through #12 - Charset.#15Be careful with using character entities outsite your charset, because The entire range from &amp;amp;#129; through &amp;amp;#159; are invalid characters. I bet you didn't know that, did you? It's ok, I didn't either until I read this article on A List Apart.#16-#18Not a whole lot I can say about these, but there are some details to pay attention to, like using h# tags in sequential order.#19You can make block level elements look like inline elements, or inline elements look like they are block I am a div displayed inline, and so am I.and I am a span displayed block.The article states this, but I wanted to make a little clearer.#20 &amp;amp; #21If you didn't know this already, please make sure to read it again.#22This item should have been much earlier in the article, at least #8. They've been using semantic for awhile, but they just get around to defining it&amp;nbsp; now?#23Again, make sure you are using the tags as they were meant to be used. If something needs to be italic, use &amp;lt;i&amp;gt;, if it needs emphasis, use &amp;lt;em&amp;gt;.#24This deserves to be said again: table layouts are bad, for all the reasons in the article.#25&amp;lt;div&amp;gt; is not the new &amp;lt;table&amp;gt;, and documents should not have &amp;lt;div&amp;gt; soup. The div element is a generic block-level tag. It is best used to store other elements in meaningfully connected ways. For example, &amp;lt;div id=&quot;header&quot;&amp;gt; is common here, but having six div's within that for each the page title, navigation, login information, and to separate those items is bad. Be like us, resist the urge to replace &amp;lt;table&amp;gt; with &amp;lt;div&amp;gt;#26Just remember to use &amp;lt;table&amp;gt; for the right reason.#27-#31This is all very useful information, much of which I didn't know. Make sure you get it all.#32I would much rather use target=&quot;_blank&quot; than Javascript to open new windows, especially because all browsers support the target attribute, and most don't let javascript open new windows with user intervention anymore. In this case, the ideal solution is broken because of the era of the pop-up.#33Actually, the alt attribute should be omitted, but only rarely. At SiteCrafting, we use image icons to add meaning to links, for example, a plus sign on links to add new enteries to a database. If I added an alt attribute to that image, and it didn't display, my link would look like [Add] Add Item rather than Add Item. #34You can have many elements of a given class, but only one element (on a page) with a given id. This does make you wonder why getElementsByClassName isn't in the Javascript core.#35 &amp;amp; #36Essential, but I can't add anything to these.#37This reminds me of the era of frames. Frames were bad, and undead frames like object include and iframes should not be allowed to work. But we had to use iframes a couple of times, because there was no other option to make the system work. Here's the bottom line: Learn the rules of HTML, and this article is a good place to start. Then when you have learned the rules, you can bend them, or even break them, as long as you know why you are doing it.</description>
			<pubDate>Wed, 01 Nov 2006 09:16:00 PST</pubDate>
		</item>
			
		<item>
			<title>Easy to Use Web Tools</title>
			<link>http://www.sitecrafting.com/blog/easy-to-web-tools/</link>
			<description>Give a man a fish he eats for a day, teach a man to fish he eats for a lifetime. - Chinese ProverbAs a flyfisherman, I can say this proverb does not mean a lot to me. I've been taught, but rarely catch fish, and those I do catch, I release. However, this entry is not so much about fishing or learning to fish,&amp;nbsp; it is about sharing knowledge and giving customers tools for success.When we first started SiteCrafting, our plan was to create websites for customers, establish a maintenance contract whereby all their website changes needed to flow through us, and then bill them. The goal was: Build Website, Provide Maintenance, Invoice, Repeat. Keeping the Web mysterious in this way did not serve us or our customers well, not to mention we could never go on vacation.&amp;nbsp; In fact, we found that a customer's website budget often went toward tedious updates, for which very little value was added to their website.Charging an hourly rate to change a photo, add some body copy, update a Flash movie, served only to insure dependence upon us, but it was fake. We were the keepers of knowledge and we kept their website a secret from them, nothing could get onto their site without it first being converted from a Word document to HTML. A task only we, the experts, could perform. What a crock! What's sad is there are still TONS of people with this business model, and there are customers willing to pay for it. We made a conscious decision to move away from this model several years ago, however it did require a leap of faith and confidence in our customers. After all, if I give my client a tool they might just use it without me. What was funny though was the number of people who loved our tools so much they asked for more, and more&amp;nbsp; and more. The Moral: Give a man (or woman) powerful, easy-to-use tools to manage their website and they will create a vibrant, usable site for their business. Furthermore, they will ask you for more tools AND tell their friends all about you.As a disclaimer, I should mention that SiteCrafting does continue maintenance services  for a select group of customers who prefer it this way. If you're one of those folks and are now ready to take the plunge into easy-to-use tools, let us know.</description>
			<pubDate>Thu, 02 Nov 2006 13:45:00 PST</pubDate>
		</item>
			
	</channel>
</rss>
		
