<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>randy hook</title>
	<atom:link href="http://randallhook.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://randallhook.com</link>
	<description>coding, design and optimism</description>
	<lastBuildDate>Thu, 06 Jun 2013 15:30:10 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>A Faceted Search Solution for Drupal</title>
		<link>http://randallhook.com/2013/02/14/a-faceted-search-solution-for-drupal/</link>
		<comments>http://randallhook.com/2013/02/14/a-faceted-search-solution-for-drupal/#comments</comments>
		<pubDate>Thu, 14 Feb 2013 14:29:30 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[drupal]]></category>
		<category><![CDATA[ecommerce]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=447</guid>
		<description><![CDATA[I recently had to add a search filter to a Drupal site where the user can filter down criteria to find a reduced listing of products. This is called Faceted Search and is a common navigation technique on ecommerce sites. Think of the left side of Amazon&#8217;s site where you can pick Toys then filter [...]]]></description>
				<content:encoded><![CDATA[<p>I recently had to add a search filter to a Drupal site where the user can filter down criteria to find a reduced listing of products. This is called Faceted Search and is a common navigation technique on ecommerce sites. Think of the left side of Amazon&#8217;s site where you can pick Toys then filter down to a specific department and age group.</p>
<div id="attachment_456" class="wp-caption alignnone" style="width: 451px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Exposed-Filters-Preview.jpg"><img class="size-full wp-image-456 " title="Exposed Filters" alt="Exposed Filters" src="http://randallhook.com/wp-content/uploads/2013/02/Exposed-Filters-Preview.jpg" width="441" height="256" /></a><p class="wp-caption-text">Exposed Filters</p></div>
<p>After searching the Drupal contrib library for something that would work, and wanting to stay away from any extra server-side configurations, I decided to write my own. This was an intense battle with the Drupal API and I am publishing the solution here to help others out and hopefully get some feedback on things I could have done better.</p>
<h2>The Test Site</h2>
<p>OK, so let&#8217;s lay some groundwork here for the test site. It is built on Drupal 7.15 and includes the following contrib modules worth making note of:</p>
<ul>
<li><a title="Drupal Better Exposed Filters Project" href="http://drupal.org/project/better_exposed_filters" target="_blank">Better Exposed Filters</a></li>
<li><a title="Drupal CTools Project" href="http://drupal.org/project/ctools" target="_blank">CTools</a></li>
<li><a title="Drupal Panels Project" href="http://drupal.org/project/panels" target="_blank">Panels</a></li>
<li><a title="Drupal Token Project" href="http://drupal.org/project/token">Token</a></li>
<li><a title="Drupal Views Project" href="http://drupal.org/project/views" target="_blank">Views</a></li>
</ul>
<p>I&#8217;ll make note of when these modules are used and for what purpose throughout the article.</p>
<p>We will be mimicking a small portion of Amazon&#8217;s website by building a Drupal site where you can shop for toys. To start, I have created a couple of Taxonomy Vocabularies &#8212; Department and Age Range &#8212; and populated them with a few terms (e.g. Action Figures for Department and 2 to 4 Years for Age Range). I then created a Content Type called Toy which includes the following fields:</p>
<ul>
<li>Title</li>
<li>Body</li>
<li>Department &#8211; term reference</li>
<li>Age Range &#8211; term reference</li>
</ul>
<p>I have then created several toys from a couple of different departments and age ranges. We are now all set to create some views to show us the toys and work on the tools that will help us filter down the list.</p>
<h2>Initial Setup of &#8220;Shop Toys By&#8221; Views</h2>
<p>We are going to create a page that lists the different filter terms available for the user to shop by. For example, we&#8217;d like the user to see a heading called Shop By Department with the various departments listed underneath. We will do this by creating two views &#8212; one for shopping by department and the other for shopping by age range. Make sure you have the <a title="Drupal Views Module" href="http://drupal.org/project/views" target="_blank">Views</a> module installed with Views and Views UI enabled.</p>
<p>Create a view called &#8220;Shop Toys By&#8221; which is a listing of Taxonomy Terms from the Department vocabulary. We want this created as a block.</p>
<div id="attachment_449" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Shop-Toys-By-View.jpg"><img class="size-full wp-image-449" alt="Shop Toys By View" src="http://randallhook.com/wp-content/uploads/2013/02/Shop-Toys-By-View-e1360779239772.jpg" width="500" height="366" /></a><p class="wp-caption-text">The Shop Toys By View</p></div>
<p>Click &#8220;Continue &amp; Edit&#8221;. Change the Display Name to &#8220;Department&#8221; and the Title to &#8220;Shop By Department&#8221;. If you look in the preview area at the bottom of the page, you should see a listing of Departments.</p>
<div id="attachment_450" class="wp-caption alignnone" style="width: 267px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Shop-By-Department-Preview.jpg"><img class="size-full wp-image-450" alt="Shop By Department Preview" src="http://randallhook.com/wp-content/uploads/2013/02/Shop-By-Department-Preview.jpg" width="257" height="226" /></a><p class="wp-caption-text">Shop By Department Preview</p></div>
<p>We also want to be able to shop toys by age range, so clone the Department block and change the new block&#8217;s Display Name to &#8220;Age Range&#8221; and the Title to &#8220;Shop By Age&#8221;.</p>
<div id="attachment_451" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Shop-Toys-By-View-Blocks.jpg"><img class="size-full wp-image-451" alt="Shop Toys By View Blocks" src="http://randallhook.com/wp-content/uploads/2013/02/Shop-Toys-By-View-Blocks-e1360779798518.jpg" width="500" height="458" /></a><p class="wp-caption-text">Shop Toys By View Blocks</p></div>
<h2>The Shop Toys Page</h2>
<p>We would now like a page to display both of these views to give the user all of these options for starting to shop. There are a couple of ways to do this but I have decided to use <a title="Drupal Panels Project" href="http://drupal.org/project/panels" target="_blank">Panels</a> to create a page then drop the two views onto the page. Make sure the Panels module is installed as well as the <a title="Drupal CTools Project" href="http://drupal.org/project/ctools" target="_blank">CTools-Page Manager</a> module so we can create a Panel Page. Also make sure that<a title="Drupal CTools Project" href="http://drupal.org/project/ctools" target="_blank"> CTools-Views content panes</a> module is enabled so we can drop views output into panels as you will see in a moment.</p>
<p>Navigate to Structure|Pages and click &#8220;Add custom page&#8221;. I created a page with the path &#8220;shop-toys&#8221;. In the Content section of the Panel Page, drop the two &#8220;Shop By&#8221; views.</p>
<div id="attachment_452" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Creating-the-Shop-Toys-Page.jpg"><img class="size-full wp-image-452" alt="Creating the Shop Toys Page" src="http://randallhook.com/wp-content/uploads/2013/02/Creating-the-Shop-Toys-Page-e1360780484967.jpg" width="500" height="445" /></a><p class="wp-caption-text">Creating the Shop Toys Page</p></div>
<p>If you navigate to /shop-toys now you should see something like this:</p>
<div id="attachment_453" class="wp-caption alignnone" style="width: 290px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Shop-Toys-Page.jpg"><img class="size-full wp-image-453" alt="Shop Toys Page" src="http://randallhook.com/wp-content/uploads/2013/02/Shop-Toys-Page.jpg" width="280" height="239" /></a><p class="wp-caption-text">Shop Toys Page</p></div>
<p>OK, that&#8217;s a start. We have the first step toward shopping for toys via filters.</p>
<h2>The Search Results Page</h2>
<p>Let&#8217;s now create the Search Results page, which will show a listing of all toys that match the incoming filters. Again, there&#8217;s a couple of ways of creating this page. This time, I created a view as a page with the path /toy-search-results. To keep the output simple for now, we will simply list all toys that have been published and show their teaser content.</p>
<p>If you preview this view now, you will of course see a listing of all the toys you have entered into the site. Being that this is a search results page though, we will want some way to tell the listing to limit its results based on certain criteria. We want the user to arrive at this page from somewhere else via a url formatted such as /toy-search-results/department/age-range (e.g. /toy-search-results/action-figures/2-to-4-years). To accomplish this, we need to add Contextual Filters to this view.</p>
<p>We add the first Contextual Filter for the Department Field. We set it up as follows:</p>
<ul>
<li>When the filter value is NOT in the URL: Provide a default value, Raw value from URL, Path component &#8211; 1</li>
<li>When the filter value IS in the URL or a default is provided: Specify validation criteria, Validator &#8211; taxonomy term, Vocabularies &#8211; department, Filter value type &#8211; term name converted to term id, Transform dashes in URL to spaces in term name filter values, Action to take if filter value does not validate &#8211; display contents of &#8220;no results found&#8221;</li>
</ul>
<p>What all of this means is the view is expecting the first argument in the URL to represent a toy department. The first argument is coming in as a taxonomy term (e.g. action-figures), it&#8217;s from the Department Taxonomy Vocabulary and we want it converted to the actual Term ID.</p>
<p>We then add the second Contextual Filter for the Age Range Field, using similar settings as above.</p>
<p>Looking at the preview for the view, you will see nothing until you enter some arguments into the Preview With Contextual Filters textbox. For example, I entered &#8220;action-figures/2-to-4-years&#8221; and received a result set.</p>
<p>So at this point we are able to enter a URL in the format of /toy-search-results/department/age-range and get a filtered listing of toys. So let&#8217;s make sure we can get there from our Shop Toys page.</p>
<h2>Modifying the &#8220;Shop Toys By&#8221; Views</h2>
<p>If you now navigate back to the Shop Toys page and hover over a link, say, Action Figures, you will see that the link points to something like &#8220;/taxonomy/term/15&#8243;. This is not the behavior we want of course. We&#8217;d rather have it go to &#8220;/toy-search-results/action-figures&#8221;. To do this, we need to modify how our &#8220;Shop Toys By&#8221; views are writing their links.</p>
<p>Within the view, under the Fields section, you will see &#8220;Taxonomy term: Name&#8221;. Click it to adjust its settings. Uncheck &#8220;Link this field to its taxonomy term page&#8221; which is causing it to point to the wrong location. Under the &#8220;Rewrite Results&#8221; section, check &#8220;Output this field as a link&#8221;. Now we can define where we want the link to point to. In the &#8220;Link path&#8221; textbox we can specify the target URL. Notice under the &#8220;Link path&#8221; textbox the message &#8220;You may enter data from this view as per the &#8216;Replacement patterns&#8217; below.&#8221; We have one option which is &#8220;[name]&#8220;. That&#8217;s exactly what we want because we want to send the taxonomy term name to the results page. <em>Note: the <a title="Drupal Token Project" href="http://drupal.org/project/token" target="_blank">Token</a> module must be installed to get replacement patterns.</em></p>
<p>Enter the following in the &#8220;Link path&#8221; textbox: toy-search-results/[name]/all. OK, why the &#8220;all&#8221; term at the end? The view will not like it if it is missing any of its contextual filters, so &#8220;all&#8221; will satisfy it. This is what we want at this point anyway. If we were to click on the &#8220;Action Figures&#8221; link, we are looking for all action figure toys regardless of age range. Also make sure the &#8220;Replace spaces with dashes&#8221; checkbox is checked since we know our search results view is expecting dashes in the URL.</p>
<div id="attachment_455" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Rewrite-Results.jpg"><img class="size-full wp-image-455" alt="Rewrite Results" src="http://randallhook.com/wp-content/uploads/2013/02/Rewrite-Results-e1360783467929.jpg" width="500" height="200" /></a><p class="wp-caption-text">Rewrite Results</p></div>
<p>Do the same thing for your Age Range block except your &#8220;Link path&#8221; should read &#8220;toy-search-results/all/[name]&#8220;. Save the view. Now if you navigate to the Shop Toys page, you will see that the terms are linked correctly. Click one of them and you will be taken to the Toy Search Results page with a filtered listing of toys.</p>
<h2>Filtering by More Than One Value</h2>
<p>OK, this is great so far, but what if you want to get a listing of action figures for 5 to 7 year olds? Let&#8217;s give the user a place on the search results page to add more filters. Navigate to your Toy Search Results view. What we want to do here is add filter criteria to allow the user to select a department AND an age range if they desire. Add a new filter criteria for &#8220;Content: Department&#8221;. Select &#8220;Dropdown&#8221; in the next window but we will override this in a minute. In the next window, check the box for &#8220;Expose this filter to visitors, to allow them to change it&#8221;. That is all we need here. In the preview area, you should see a droplist allowing the user to select a department.</p>
<p>Being that this is an ecommerce-like filter, we would really rather have radio buttons for the selections instead of dropdown lists. For this, you need to install the <a title="Drupal Better Exposed Filters Project" href="http://drupal.org/project/better_exposed_filters" target="_blank">Better Exposed Filters</a> module. You may then go over to the &#8220;Exposed form style&#8221; in the Advanced section of the view and select &#8220;Better Exposed Filters&#8221;. Select &#8220;checkboxes/radio buttons&#8221; for the field controls. The view preview will now look something like this:</p>
<div id="attachment_456" class="wp-caption alignnone" style="width: 451px"><a href="http://randallhook.com/wp-content/uploads/2013/02/Exposed-Filters-Preview.jpg"><img class="size-full wp-image-456" alt="Exposed Filters Preview" src="http://randallhook.com/wp-content/uploads/2013/02/Exposed-Filters-Preview.jpg" width="441" height="256" /></a><p class="wp-caption-text">Exposed Filters Preview</p></div>
<p>Save the view and navigate to the Shop Toys page. Click a link and the Search Results page will show the results and include filters for further refinement. Unfortunately, at this point, both Department and Age Range radio button sets have -Any- selected, even though we already selected our first filter criteria. This does not make for a good user experience. Also, that Apply button is not doing what we want either, so we&#8217;ll have to do something about that.</p>
<h2>Updating the Filters</h2>
<p>OK, so let&#8217;s tackle those radio buttons first. When we arrive at this page with filters in the URL, we would like the radio buttons to reflect that. In order to do this, we are going to need to write a custom module and modify the behavior of this view. Note that I am not going to go into the basics of creating a Drupal module here. You can refer to <a title="Drupal 7 Developing a Custom Module Tutorial" href="http://drupal.org/node/1074362" target="_blank">this tutorial</a> if you need some basic instruction.</p>
<p>I created a custom module called &#8220;toysearch&#8221;. In the module file I implement the form_alter hook so I can modify the exposed filter form.</p>
<p>&nbsp;</p>
<pre>function toysearch_form_alter(&amp;$form, &amp;$form_state, $form_id) {

}</pre>
<p>&nbsp;</p>
<p>In this next code snippet you will see that I am simply parsing the incoming request URL, finding the available options in the $form parameter to match terms against, then altering the form via the $form_state parameter to select the correct radio buttons.</p>
<p>&nbsp;</p>
<pre>function toysearch_form_alter(&amp;$form, &amp;$form_state, $form_id) {
    //make sure this is the toy search filter form
    if (strpos($_SERVER['REQUEST_URI'], 'toy-search-results') != 1 || 
        $form_id != 'views_exposed_form') {
        return;
    }

        //get the page arguments
        $argStartPos = strpos($_SERVER['REQUEST_URI'], 'toy-search-results') + 19;
        $argString = substr($_SERVER['REQUEST_URI'], $argStartPos);
        $args = explode('/', $argString);

        //select the appropriate filter value for each argument
        $argCounter = 0;
        foreach ($args as $a) {
            //replace the hyphens with spaces so we can match the arg
            $a = str_replace('-', ' ', $a);

            switch ($argCounter++) {
                //department
                case 0:
                    $matchFound = false;
                    foreach ($form['field_department_tid']['#options'] as $optionId =&gt; $optionValue) {
                        if ($optionValue == $a) {
                            $form_state['input']['field_department_tid'] = $optionId;

                            $matchFound = true;
                            break;
                        }
                    }

                    if (!$matchFound) {
                        $form_state['input']['field_department_tid'] = 'All';
                    }

                    break;
                //age range
                case 1:
                    $matchFound = false;
                    foreach ($form['field_age_range_tid']['#options'] as $optionId =&gt; $optionValue) {
                        if (strtolower($optionValue) == strtolower($a)) {
                            $form_state['input']['field_age_range_tid'] = $optionId;
                            $matchFound = true;
                            break;
                        }
                    }

                    if (!$matchFound) {
                        $form_state['input']['field_age_range_tid'] = 'All';
                    }

                    break;
                default:
                    break;
            }
        }

        //if trailing args are not specified, set the radio group to All
        if (count($args) &lt; 1) {
            $form_state['input']['field_department_tid'] = 'All';
            $form_state['input']['field_age_range_tid'] = 'All';        
        }
        else if (count($args) &lt; 2) {
            $form_state['input']['field_age_range_tid'] = 'All';
        }        

}</pre>
<p>&nbsp;</p>
<p>Note that during the process I insert the &#8220;All&#8221; term if a certain term is not found, then at the end of the function I append the &#8220;All&#8221; term if a URL comes in that doesn&#8217;t specify all of the search criteria. For example, if the URL is &#8220;toy-search-results/action-figures&#8221; we append &#8220;/all&#8221; to cover the age range requirement.</p>
<p>If you now select a link from the Shop Toys page, you will see the correct radio buttons automatically selected. This is because we reacted to the form_alter hook and modified the form state before it rendered.</p>
<p>You still can&#8217;t select further filters and click the Apply button and get the expected behavior, so we need to do something about that.</p>
<h2>Modifying the Apply Button</h2>
<p>The supplied Apply button does not act like we want, so we are going to add some code in our form_alter function to add our own button and behavior.</p>
<p>The first thing to note is that because we wanted exposed filters in the view, we were required to enable AJAX for the view. The default behavior for the Apply button is to send an AJAX request with the new filter. We do not want this to happen. We want to request the toy-search-results page again with the new search parameters instead. So first, we want to disable the default behavior of the form.</p>
<p>&nbsp;</p>
<pre>//set the form action to search-results page instead of the ajax views handler
$form['#action'] = 'toy-search-results';</pre>
<p>&nbsp;</p>
<p>Then we will hide the default button</p>
<p>&nbsp;</p>
<pre>    //hide the default filter button
    $form['submit']['#attributes'] = array(
        'style' =&gt; 'display:none;'
    );</pre>
<p>&nbsp;</p>
<p>and add our own button</p>
<p>&nbsp;</p>
<pre>    //add our own filter button that calls a custom function to set
    //up the appropriate search url
    $form['buttons']['apply_filter'] = array(
        '#type' =&gt; 'submit',
        '#value' =&gt; 'Apply filter',
        '#submit' =&gt; array(
            'toysearch_apply_filter'
        )
    );</pre>
<p>&nbsp;</p>
<p>Notice the &#8220;#submit&#8221; index in the $form['buttons']['apply_filter'] array. This specifies a function that we want called when the user clicks our custom button. This is where we are going to redirect the default action to the URL we want.</p>
<p>In the following function we look at the radio buttons that have been selected and construct a URL consisting of &#8220;toy-search-results&#8221; plus the additional search terms based on the currently selected radio buttons. We then call Drupal&#8217;s goto function to redirect the user to the results page.</p>
<p>&nbsp;</p>
<pre>function toysearch_apply_filter(&amp;$form, &amp;$form_state) {
    //get the filter value ids
    $ids = array();
    $ids['field_department_tid'] = $form_state['values']['field_department_tid'];
    $ids['field_age_range_tid'] = $form_state['values']['field_age_range_tid'];

    //get the actual filter values
    $urlTerms = array();
    foreach ($ids as $fieldName =&gt; $fieldId) {
        foreach ($form[$fieldName]['#options'] as $optionId =&gt; $optionValue) {
            if ($optionId == $fieldId) {
                if ($optionId == 'All') {
                    $urlTerms[] = 'all';
                }
                else {
                    $term = $optionValue;
                    $term = str_replace(array(' '), '-', $term);
                    $urlTerms[] = $term;
                }
                break;
            }
        }
    }

    $urlString = implode('/', $urlTerms);
    drupal_goto('toy-search-results/' . $urlString);
}</pre>
<p>&nbsp;</p>
<p>One problem remains. When that Apply Filter button is clicked, a query string is passed in to set the form state. All we need to do now though is test for the presence of a query string and parse the search arguments that way in addition to parsing a clean URL. Here is the entire form_alter function.</p>
<p>&nbsp;</p>
<pre>function toysearch_form_alter(&amp;$form, &amp;$form_state, $form_id) { 
    //make sure this is the toy search filter form
    if (strpos($_SERVER['REQUEST_URI'], 'toy-search-results') != 1 || 
        $form_id != 'views_exposed_form') {
        return;
    }

    //set the form action to search-results page instead of the ajax views handler
    $form['#action'] = 'toy-search-results';

    //hide the default filter button
    $form['submit']['#attributes'] = array(
        'style' =&gt; 'display:none;'
    );    

    //add our own filter button that calls a custom function to set
    //up the appropriate search url
    $form['buttons']['apply_filter'] = array(
        '#type' =&gt; 'submit',
        '#value' =&gt; 'Apply filter',
        '#submit' =&gt; array(
            'toysearch_apply_filter'
        )
    );

    //check if this page has a query string
    //
    //this would signify that the user has used the apply filter button
    //on this filter instead of arriving at the search results directly, using
    //a clean url
    $queryString = $_SERVER['QUERY_STRING'];

    if (!empty($queryString)) {
        $args = explode('&amp;', $queryString);

        //select the appropriate filter value for each argument
        foreach ($args as $a) {
            $kvp = explode('=', $a);
            switch ($kvp[0]) {
                case 'field_department_tid':
                    $form_state['input']['field_department_tid'] = $kvp[1];
                    break;
                case 'field_age_range_tid':
                    $form_state['input']['field_age_range_tid'] = $kvp[1];
                    break;
                default:
                    break;
            }
        }        
    }    
    else {
        //get the page arguments
        $argStartPos = strpos($_SERVER['REQUEST_URI'], 'toy-search-results') + 19;
        $argString = substr($_SERVER['REQUEST_URI'], $argStartPos);
        $args = explode('/', $argString);

        //select the appropriate filter value for each argument
        $argCounter = 0;
        foreach ($args as $a) {
            //replace the hyphens with spaces so we can match the arg
            $a = str_replace('-', ' ', $a);

            switch ($argCounter++) {
                //department
                case 0:
                    $matchFound = false;
                    foreach ($form['field_department_tid']['#options'] as $optionId =&gt; $optionValue) {
                        if ($optionValue == $a) {
                            $form_state['input']['field_department_tid'] = $optionId;

                            $matchFound = true;
                            break;
                        }
                    }

                    if (!$matchFound) {
                        $form_state['input']['field_department_tid'] = 'All';
                    }

                    break;
                //age range
                case 1:
                    $matchFound = false;
                    foreach ($form['field_age_range_tid']['#options'] as $optionId =&gt; $optionValue) {
                        if (strtolower($optionValue) == strtolower($a)) {
                            $form_state['input']['field_age_range_tid'] = $optionId;
                            $matchFound = true;
                            break;
                        }
                    }

                    if (!$matchFound) {
                        $form_state['input']['field_age_range_tid'] = 'All';
                    }

                    break;
                default:
                    break;
            }
        }

        //if trailing args are not specified, set the radio group to All
        if (count($args) &lt; 1) {
            $form_state['input']['field_department_tid'] = 'All';
            $form_state['input']['field_age_range_tid'] = 'All';        
        }
        else if (count($args) &lt; 2) {
            $form_state['input']['field_age_range_tid'] = 'All';
        }        
    } //no query string

}</pre>
<p>&nbsp;</p>
<h2>Conclusion</h2>
<p>Everything is now functioning properly. The user can come to this page via the Shop Toys page which constructs a clean URL. The filter radio buttons are automatically set. The user can then add filters and the radio buttons will persist with the new choices and a new, clean URL will be constructed which can easily be shared.</p>
<p>I&#8217;ve got a couple of hacks in here and some hardcoded values (yuck) which I would ideally like to fix, but I welcome your feedback on any improvements I could make.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2013/02/14/a-faceted-search-solution-for-drupal/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Happy Birthday Ludwig Mies van der Rohe</title>
		<link>http://randallhook.com/2012/03/27/happy-birthday-ludwig-mies-van-der-rohe/</link>
		<comments>http://randallhook.com/2012/03/27/happy-birthday-ludwig-mies-van-der-rohe/#comments</comments>
		<pubDate>Tue, 27 Mar 2012 13:57:42 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[design]]></category>
		<category><![CDATA[modeling]]></category>
		<category><![CDATA[model]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=433</guid>
		<description><![CDATA[Seeing Google&#8217;s doodle today, honoring Ludwig Mies van der Rohe&#8217;s birthday, reminded me that several years ago I was playing around with a desktop package called Visual Reality. I&#8217;ve always admired the clean lines and simplicity of his work, so I used the application to loosely recreate the basics of the Seagram Building found in [...]]]></description>
				<content:encoded><![CDATA[<p>Seeing Google&#8217;s doodle today, honoring <a title="Ludwig Mies van der Rohe" href="http://en.wikipedia.org/wiki/Ludwig_Mies_van_der_Rohe" target="_blank">Ludwig Mies van der Rohe&#8217;s</a> birthday, reminded me that several years ago I was playing around with a desktop package called Visual Reality. I&#8217;ve always admired the clean lines and simplicity of his work, so I used the application to loosely recreate the basics of the <a title="Seagram Building" href="http://en.wikipedia.org/wiki/Seagram_Building" target="_blank">Seagram Building</a> found in New York City.</p>
<div id="attachment_434" class="wp-caption alignnone" style="width: 203px"><a href="http://randallhook.com/wp-content/uploads/2012/03/segram_building_overview.jpg"><img class="size-full wp-image-434" title="Seagram Building Overview" src="http://randallhook.com/wp-content/uploads/2012/03/segram_building_overview.jpg" alt="Seagram Building Overview" width="193" height="296" /></a><p class="wp-caption-text">Seagram Building Overview</p></div>
<div id="attachment_435" class="wp-caption alignnone" style="width: 306px"><a href="http://randallhook.com/wp-content/uploads/2012/03/segram_building_facade.jpg"><img class="size-full wp-image-435" title="Seagram Building Facade" src="http://randallhook.com/wp-content/uploads/2012/03/segram_building_facade.jpg" alt="Seagram Building Facade" width="296" height="221" /></a><p class="wp-caption-text">Seagram Building Facade</p></div>
<div id="attachment_436" class="wp-caption alignnone" style="width: 308px"><a href="http://randallhook.com/wp-content/uploads/2012/03/segram_building_lobby.jpg"><img class="size-full wp-image-436" title="Seagram Building Lobby" src="http://randallhook.com/wp-content/uploads/2012/03/segram_building_lobby.jpg" alt="Seagram Building Lobby" width="298" height="223" /></a><p class="wp-caption-text">Seagram Building Lobby</p></div>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2012/03/27/happy-birthday-ludwig-mies-van-der-rohe/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Shortening Links with Google</title>
		<link>http://randallhook.com/2011/12/14/shortening-links-with-google/</link>
		<comments>http://randallhook.com/2011/12/14/shortening-links-with-google/#comments</comments>
		<pubDate>Wed, 14 Dec 2011 18:46:43 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[api]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=419</guid>
		<description><![CDATA[A recent project necessitated the use of a link shortener, which I had never used before, so I first turned to http:://goo.gl to see what was involved. I was pleasantly surprised with the ease of integrating Google&#8217;s API into my code. The following screenshots detail this simple process. 1. Get your API key from your [...]]]></description>
				<content:encoded><![CDATA[<p>A recent project necessitated the use of a link shortener, which I had never used before, so I first turned to <a href="http://goo.gl">http:://goo.gl</a> to see what was involved. I was pleasantly surprised with the ease of integrating Google&#8217;s API into my code. The following screenshots detail this simple process.</p>
<h3>1. Get your API key from your Google API console</h3>
<div id="attachment_421" class="wp-caption alignnone" style="width: 610px"><a href="http://randallhook.com/wp-content/uploads/2011/12/api-key1.png"><img class="size-full wp-image-421 " title="Get Your API Key" src="http://randallhook.com/wp-content/uploads/2011/12/api-key1.png" alt="Get your API key" width="600" height="394" /></a><p class="wp-caption-text">Get Your API Key</p></div>
<h3>2. Turn on the link shortening service via the console</h3>
<div id="attachment_422" class="wp-caption alignnone" style="width: 610px"><a href="http://randallhook.com/wp-content/uploads/2011/12/services-panel.png"><img class="size-full wp-image-422 " title="Turn On the Service" src="http://randallhook.com/wp-content/uploads/2011/12/services-panel.png" alt="Turn on the service" width="600" height="262" /></a><p class="wp-caption-text">Turn On the Service</p></div>
<h3>3. Make a call to the service from your code</h3>
<pre>function shortenUrl( $sLongUrl ) {
	$ch = curl_init( 'https://www.googleapis.com/urlshortener/v1/url?key=' . _GOOGLE_API_KEY );
	curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
	curl_setopt( $ch, CURLOPT_POST, true );
	curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Content-type: application/json' ) );
	curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( array( 'longUrl' =&gt; $sLongUrl ) ) );

	$result = curl_exec( $ch );
	curl_close( $ch );

	return $result;
}</pre>
<p>All we are doing here is using PHP&#8217;s curl to make a post to the Google API by passing a JSON-encoded array, loaded with our long URL.</p>
<h3>4. Handle the result</h3>
<pre>        .
        .
        .
	success: function( response ) {

		if ( response[ 'error' ] ) {

			shortUrl = '';

		}
		else {

		        shortUrl = response[ 'id' ];

                }

	},
	failure: function( response ) {

		shortUrl = '';

	}

} );</pre>
<p>I am making an AJAX call from jQuery here, but the important part is that the response comes back as an array. If the &#8220;error&#8221; index is set, there was a problem. Otherwise, your shortened URL will be contained in the &#8220;id&#8221; index.</p>
<p>And that&#8217;s all there is to it!</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/12/14/shortening-links-with-google/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>First Steps with CakePHP</title>
		<link>http://randallhook.com/2011/10/19/first-steps-with-cakephp/</link>
		<comments>http://randallhook.com/2011/10/19/first-steps-with-cakephp/#comments</comments>
		<pubDate>Wed, 19 Oct 2011 12:43:49 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[learning]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=385</guid>
		<description><![CDATA[Starting a new, personal project in PHP, I wanted to try out a new framework for the fun of it. After doing a bit of review on many available frameworks, and ruling out the ones I have already used (Zend and OpenAvanti), I settled on CakePHP. Installation and basic setup were a breeze, and I [...]]]></description>
				<content:encoded><![CDATA[<p>Starting a new, personal project in PHP, I wanted to try out a new framework for the fun of it. After doing a bit of review on many available frameworks, and ruling out the ones I have already used (Zend and OpenAvanti), I settled on <a title="CakePHP" href="http://cakephp.org">CakePHP</a>.</p>
<div id="attachment_413" class="wp-caption alignnone" style="width: 410px"><a href="http://randallhook.com/wp-content/uploads/2011/10/cake-logo.jpg"><img src="http://randallhook.com/wp-content/uploads/2011/10/cake-logo.jpg" alt="CakePHP" title="CakePHP" width="400" height="300" class="size-full wp-image-413" /></a><p class="wp-caption-text">CakePHP</p></div>
<p>Installation and basic setup were a breeze, and I found the <a title="Online CakePHP 2.0 Book" href="http://book.cakephp.org/2.0/en/">online documentation</a> and <a title="CakePHP 2.0 Tutorials" href="http://book.cakephp.org/2.0/en/tutorials-and-examples.html">tutorials</a> helpful. Very early on, however, as I thought I was still trying to accomplish basic site setup tasks, I began to run into a few road blocks. Luckily, the API documentation is quite thorough and a pleasure to read, so I eventually got through the initial &#8220;new framework pains&#8221;. In this post, I will walk you through my basic setup and hopefully answer a few questions that may pop up for you after those initial inquiries into the docs.</p>
<p>Here is what I wanted to accomplish (This example isn&#8217;t meant to showcase fantastic architecture. It&#8217;s just to serve the purpose of making sure I knew how Cake was going to handle things. And believe me, it took several tries):</p>
<ul>
<li>Have a public home page with a link to the admin panel</li>
<li>The admin panel requires user login</li>
</ul>
<p>Simple, right? So let&#8217;s get started. Note that I&#8217;m using CakePHP 2.0.</p>
<p>The first thing we want to do is tell Cake that we intend to use the built-in Authorization Component. I chose to do that in the AppController, so that it is available to all controllers. Copy the default AppController file from the lib/Cake/Controller folder into the app/Controller folder. Within the class declaration, add the following code:</p>
<pre>var $components = array('Auth', 'Session');</pre>
<p>The $components variable takes an array of components, one of which we&#8217;ve specified as Auth.</p>
<p>Next we want the site to use our home controller as the default. Cake comes pre-built to use the pages controller, specified in the app/Config/routes.php file:</p>
<pre>	Router::connect('/', array('controller' =&gt; 'pages', 'action' =&gt; 'display', 'home'));</pre>
<p>We are going to change this to:</p>
<pre>	Router::connect('/', array('controller' =&gt; 'home', 'action' =&gt; 'index'));</pre>
<p>Now that we are setup to view home/index once we navigate to our site, let&#8217;s define the home controller in app/Controller/home_controller.php:</p>
<pre>class HomeController extends AppController {
	var $uses = null;

	function beforeFilter() {	
		$this-&gt;Auth-&gt;allow( 'index' );
	}

	function index() {
	}
}</pre>
<p>Since the Auth component is in force for all controllers, we need to tell it to relax for the home/index action. We do this with the beforeFilter() function.</p>
<p>Also note the setting of $uses to null. Controllers usually have this set to the table they will be referencing. In this case, it makes no sense to have a &#8220;home&#8221; table in the database, so we must tell the controller that we are not going to be using a table.</p>
<p>Now we just need a view to show our admin panel link (located in app/View/home/index.ctp):</p>
<pre>
&lt;p&gt;This is the home/index view&lt;/p&gt;
&lt;p&gt;
	&lt;a href="/admin"&gt;Admin Panel&lt;/a&gt;	
&lt;/p&gt;
</pre>
<p>If you navigate to your site, you should see the admin panel link.</p>
<div id="attachment_392" class="wp-caption alignnone" style="width: 569px"><a href="http://randallhook.com/wp-content/uploads/2011/10/home.png"><img class="size-full wp-image-392" title="The Home Page" src="http://randallhook.com/wp-content/uploads/2011/10/home.png" alt="Home Page" width="559" height="212" /></a><p class="wp-caption-text">Home Page</p></div>
<p>What we want to happen here is for a login screen to display if the anonymous user clicks the admin panel link. We&#8217;ve already told Cake that we are using Auth. Now we just have to do a couple of things to hook up a login screen and authenticate a user.</p>
<p>First, Auth is expecting a users controller (app/controller/users_controller.php):</p>
<pre>class UsersController extends AppController {
	var $name = 'Users';

	function login() {
		if ($this-&gt;request-&gt;is('post')) {
			if ($this-&gt;Auth-&gt;login()) {
				return $this-&gt;redirect($this-&gt;Auth-&gt;redirect());
			}
			else {
				$this-&gt;Session-&gt;setFlash(__('Username or password is incorrect'), 'default', array(), 'auth');
			}
		}
	}

	function logout() {
		$this-&gt;redirect( $this-&gt;Auth-&gt;logout() );
	}
}</pre>
<p>Setup the login view (app/View/users/login.ctp):</p>
<pre> echo $this-&gt;Session-&gt;flash( 'Auth' ); 
 echo $this-&gt;Form-&gt;create( 'User' );
 echo $this-&gt;Form-&gt;input( 'username' );
 echo $this-&gt;Form-&gt;input( 'password' );
 echo $this-&gt;Form-&gt;end( 'Login' );</pre>
<p><em>Note: To insert a password into your database that can use for login, use Cake's built-in AuthComponent::password('your_password') function to determine the hash</em>.</p>
<p>Let's setup the admin panel. Create the admin controller (app/Controller/admin_controller.php):</p>
<pre>class AdminController extends AppController {
	var $uses = null;

	function index() {		
	}
}</pre>
<p>And the view (app/View/admin/index.ctp):</p>
<pre>&lt;p&gt;This is the admin panel&lt;/p&gt;</pre>
<p>Now if you click the admin panel link on the home page, you should be greeted by a login screen.</p>
<div id="attachment_395" class="wp-caption alignnone" style="width: 508px"><a href="http://randallhook.com/wp-content/uploads/2011/10/login.png"><img class="size-full wp-image-395" title="Login" src="http://randallhook.com/wp-content/uploads/2011/10/login.png" alt="Login Screen" width="498" height="338" /></a><p class="wp-caption-text">Login Screen</p></div>
<p>After a successful login, you should then see the admin panel.</p>
<div id="attachment_398" class="wp-caption alignnone" style="width: 443px"><a href="http://randallhook.com/wp-content/uploads/2011/10/adminpanel.png"><img class="size-full wp-image-398" title="Admin Panel" src="http://randallhook.com/wp-content/uploads/2011/10/adminpanel.png" alt="Admin Panel" width="433" height="166" /></a><p class="wp-caption-text">Admin Panel</p></div>
<p>So that is how I got a very basic setup running with CakePHP. Check out the great <a title="CakePHP 2.0 Book" href="http://book.cakephp.org/2.0/en/">documentation</a> at the official site if you run into trouble, and even start <a title="CakePHP 2.0 API" href="http://api20.cakephp.org/">browsing the API</a>. It's good stuff.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/10/19/first-steps-with-cakephp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Mindscape in LEGO</title>
		<link>http://randallhook.com/2011/09/29/mindscape-in-lego/</link>
		<comments>http://randallhook.com/2011/09/29/mindscape-in-lego/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 11:57:29 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[design]]></category>
		<category><![CDATA[modeling]]></category>
		<category><![CDATA[creativity]]></category>
		<category><![CDATA[model]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=368</guid>
		<description><![CDATA[While shopping the LEGO website the other day, I ran across a free application they provide called LEGO Digital Designer. With it, you can assemble bricks from the included palette, then package and upload your creation back to the site. This looked pretty fun so I gave the software a whirl. Now, I am not [...]]]></description>
				<content:encoded><![CDATA[<p>While shopping the <a title="LEGO" href="http://lego.com">LEGO</a> website the other day, I ran across a free application they provide called <a title="LEGO Digital Designer" href="http://designbyme.lego.com/en-us/Default.aspx?icmp=COCreateShareSpotlightUSDBM">LEGO Digital Designer</a>. With it, you can assemble bricks from the included palette, then package and upload your creation back to the site. This looked pretty fun so I gave the software a whirl.</p>
<p>Now, I am not a professional 3D modeler, but I do have experience in a few tools, such as <a title="Blender" href="http://www.blender.org">Blender</a>, and expect a few necessities in a 3D modeling application. LEGO Digital Designer does not have them. But that&#8217;s OK, I thought. I&#8217;ll just play around with it.</p>
<p>For an experiment, I decided to model the building I work in, <a title="Google Map - 25 Ottawa, Grand Rapids, MI" href="http://maps.google.com/maps?q=25+Ottawa+Ave+SW,+Grand+Rapids+Charter+Township,+MI+49503+(mindscape+at+hanon+mckendry:+web+marketing%2Fdesign)&amp;hl=en&amp;sll=37.0625,-95.677068&amp;sspn=56.856075,79.013672&amp;vpsrc=0&amp;t=m&amp;z=17">25 Ottawa SW, Grand Rapids, MI</a>, the home of <a title="Mindscape at Hanon McKendry" href="http://www.mindscape-hm.com">Mindscape at Hanon McKendry</a>. My goal was to capture the feel of the building (being that it is LEGO and not a precise, 3D replica) without getting into too much detail. Well, that&#8217;s hard for me to do, and before too long, I found myself agonizing over placing bricks exactly where they should be to accurately reflect the building&#8217;s layout. Then I would take a step back, take a deep breath, and remind myself that I&#8217;m just capturing the building&#8217;s essence. Then it would get fun again.</p>
<p>However, the application does have a lot of shortcomings, and as soon as the brick count started rising, I found it more and more difficult to place bricks, even in seemingly simple scenarios. Time to put this together started skyrocketing and I decided to call it quits for now before this turns into a lifelong project. I may have to look into other tools such as <a title="LDraw" href="http://www.ldraw.org">LDraw</a> and see what they have to offer.</p>
<p>That being said, this is where the model stands right now, at 4,368 bricks. This view is from the Southeast (you can click the pictures to see the full-size view).</p>
<div id="attachment_372" class="wp-caption alignnone" style="width: 310px"><a href="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot2.png"><img class="size-medium wp-image-372" title="25 Ottawa SW, Grand Rapids, MI" src="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot2-300x203.png" alt="25 Ottawa SW, Grand Rapids, MI" width="300" height="203" /></a><p class="wp-caption-text">25 Ottawa SW, Grand Rapids, MI</p></div>
<p>Here is a view from the Northeast.</p>
<div id="attachment_375" class="wp-caption alignnone" style="width: 310px"><a href="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot5.png"><img class="size-medium wp-image-375" title="25 Ottawa from the Northeast" src="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot5-300x203.png" alt="25 Ottawa from the Northeast" width="300" height="203" /></a><p class="wp-caption-text">25 Ottawa from the Northeast</p></div>
<p>As you can see, it captures the building&#8217;s essence on a basic level. Here are a few more detailed views.</p>
<p>In this view, we&#8217;ve zoomed in to look through the first floor to see the Mindscapers hard at work in &#8220;the pit&#8221;. If you look through the small window on the right, you can even see the bearded Matt, hard at work on a website.</p>
<div id="attachment_376" class="wp-caption alignnone" style="width: 310px"><a href="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot3.png"><img class="size-medium wp-image-376 " title="&quot;The Pit&quot; at Mindscape" src="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot3-300x203.png" alt="&quot;The Pit&quot; at Mindscape" width="300" height="203" /></a><p class="wp-caption-text">&quot;The Pit&quot; at Mindscape</p></div>
<p>Here we are taking a bird&#8217;s eye view through the roof. You can see the entire &#8220;pit&#8221; on the first floor, and several other items under construction. The stairwell and elevator shafts are positioned, and the Skywalk has been run through the building and ready to be connected to the adjacent buildings.</p>
<div id="attachment_377" class="wp-caption alignnone" style="width: 310px"><a href="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot6.png"><img class="size-medium wp-image-377" title="Bird's Eye View" src="http://randallhook.com/wp-content/uploads/2011/09/LDDScreenShot6-300x203.png" alt="Bird's Eye View" width="300" height="203" /></a><p class="wp-caption-text">Bird&#39;s Eye View</p></div>
<p>It would be nice to finish this, especially to add in the rest of Mindscape and Hanon McKendry on the sixth floor, and to build out 25 Kitchen on the first floor. But I think I&#8217;m going to need a new tool. Of course it would be nice to build it out of real LEGO, but I would probably have to win the lottery to buy 5,000+ bricks. Has anyone out there had experience with any other digital LEGO tools?</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/09/29/mindscape-in-lego/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Learning Has Always Been Fun</title>
		<link>http://randallhook.com/2011/09/01/learning-has-always-been-fun/</link>
		<comments>http://randallhook.com/2011/09/01/learning-has-always-been-fun/#comments</comments>
		<pubDate>Thu, 01 Sep 2011 11:56:10 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[brainstorm]]></category>
		<category><![CDATA[creativity]]></category>
		<category><![CDATA[ideas]]></category>
		<category><![CDATA[learning]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=337</guid>
		<description><![CDATA[I was reading a blog post today about how a few companies are collaborating on a new app for NASA that will integrate the playability of an MMORPG and the coolness of real-world science. This sounds incredible to me and I can&#8217;t wait to try it. The one thing that irked me though was how [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://randallhook.com/wp-content/uploads/2011/08/learning-collage3.png"><img class="alignnone size-full wp-image-361" title="Learning is Fun" src="http://randallhook.com/wp-content/uploads/2011/08/learning-collage3.png" alt="Learning is Fun" width="450" height="450" /></a></p>
<p>I was reading a <a title="NASA Astronaut - Moon, Mars and Beyond Preview" href="http://blog.games.com/2011/08/30/nasa-astronaut-moon-mars-beyond-preview/">blog post</a> today about how a few companies are collaborating on a <a title="Kickstarter Project" href="http://www.kickstarter.com/projects/1763206385/astronaut-moon-mars-and-beyond-the-nasa-mmo-online-0?ref=card">new app</a> for NASA that will integrate the playability of an <a title="MMORPG at Wikipedia" href="http://en.wikipedia.org/wiki/Mmorpg">MMORPG</a> and the coolness of real-world science. This sounds incredible to me and I can&#8217;t wait to try it. The one thing that irked me though was how when things like this come up, the cliche thing for marketers, journalists and the media to say is, &#8220;it will make learning fun&#8221; or &#8220;your kids won&#8217;t even know they are learning&#8221;.</p>
<p>News flash to those out there that think you have to disguise learning: LEARNING HAS ALWAYS BEEN FUN AND PEOPLE ENJOY IT.</p>
<p>With few exceptions, everyone loves to learn. Learning new things is what makes life exciting instead of the same old thing everyday. Talk to almost any kid in elementary school and they enjoy going to school and absorbing loads of new information. Post-elementary? Well, that&#8217;s where the excitement begins to diminish. Why?</p>
<p>That brings me to my theory of where this popular &#8220;It&#8217;s OK, they won&#8217;t know they&#8217;re learning&#8221; phrase came from. It probably stems from many kids&#8217; attitudes toward post-elementary school. Learning isn&#8217;t a drag, school is. Allow me to elaborate.</p>
<h2>The School Setting</h2>
<p>Do you remember what it was like walking into your elementary school classrooms? I remember there were heavily decorated walls. Posters of planets, ecosystems, history and far-off places invigorated the mind. Art projects hung from the ceiling. Who wouldn&#8217;t get excited to dive into knowledge in a setting such as this? These are the best years of school.</p>
<p>This week we took our kids to their school open houses. As expected, the classrooms for our two youngest in elementary school were comfortable, full of creative inspiration and poised to take in a roomful of energetic kids. Then came the middle school for our two oldest kids. What did those classrooms feel like? In a word, boring. White cinder-block walls. One or two posters. Nothing hanging from the ceiling. Desks in rigid rows. Cold and sterile. Wow. Let&#8217;s open up that social studies book and have fun!</p>
<p>Why can&#8217;t we carry that &#8220;elementary school room&#8221; mentality throughout middle school, high school and beyond? Do we think decorations and models are childish? I don&#8217;t understand. Why is it that the system thinks we don&#8217;t need to be fully stimulated anymore as we grow older?</p>
<h2>Following the Rules</h2>
<p>One of my most negative memories of school was during a high school English class where we were given a short story writing assignment. At that time I had a favorite author who I liked to emulate. One of his popular writing techniques was to end a paragraph with a very short &#8220;sentence&#8221; for impact. For example, the end of the paragraph might read, &#8220;Very cold.&#8221; Now, you know and I know that this is not a complete sentence in the sacred, formal rules of grammar. So what? I loved how it sounded and how it made me feel when I read it (I actually used a few in this post, <em>ahem</em>).</p>
<p>&#8220;No. That is not allowed. I&#8217;m marking points off&#8221;, was the teacher&#8217;s response. That&#8217;s interesting. I wonder if the author, who made millions of dollars from his books writing that way, realized his blatant disregard of the English language with his sentence fragments?</p>
<p>My point is that creativity is stifled when students aren&#8217;t allowed to push boundaries and bend rules. Isn&#8217;t history full of examples of greatness when so called &#8220;rules&#8221; are not followed precisely?</p>
<p>Sark feels my pain in her book, <a title="A Creative Companion" href="http://www.amazon.com/Creative-Companion-Free-Your-Spirit/dp/0890876517/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1314842704&amp;sr=1-1">A Creative Companion</a>.</p>
<blockquote><p>At school, things may have changed. The chairs were in rows, and tree trunks were to be colored brown, not purple. If you lived in a world of purple tree trunks, you probably learned to hide it.</p></blockquote>
<h2>Encourage Learning. Encourage Fun.</h2>
<p>Our education system should do everything it can to keep our students excited for the long term. I think two very simple steps could be taken right now. Make the school settings engaging throughout all levels of education and encourage creativity and rule bending.</p>
<p>Learning is not a drag. School is. Let&#8217;s change that.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/09/01/learning-has-always-been-fun/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Falling Into the Complexity Trap</title>
		<link>http://randallhook.com/2011/08/03/falling-into-the-complexity-trap/</link>
		<comments>http://randallhook.com/2011/08/03/falling-into-the-complexity-trap/#comments</comments>
		<pubDate>Wed, 03 Aug 2011 14:13:08 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=307</guid>
		<description><![CDATA[So many times I find myself, as a programmer, diving into a problem with an initial thought of a solution that ends up being way too complex. I don&#8217;t know if it&#8217;s programmers in general or just me, but it seems that as I gain more and more coding knowledge and can take on more [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://randallhook.com/wp-content/uploads/2011/08/equation.png"><img src="http://randallhook.com/wp-content/uploads/2011/08/equation.png" alt="" title="equation" width="477" height="434" class="alignnone size-full wp-image-334" /></a></p>
<p>So many times I find myself, as a programmer, diving into a problem with an initial thought of a solution that ends up being way too complex. I don&#8217;t know if it&#8217;s programmers in general or just me, but it seems that as I gain more and more coding knowledge and can take on more and more challenging problems, my brain can fall into a complexity trap. What I mean by that is I now have numerous tools (algorithms, patterns, etc.) at my disposal, and at times I tend to attack a problem with one or more of these tools without stepping back, taking a deep breath, and determining which is the simplest way to get something done.</p>
<p>These solutions usually work, but the code can become messy and hard to maintain versus the simple way that I failed to see at the beginning.</p>
<p>I could probably make this a series of articles, as unfortunately, I tend to do this more than I&#8217;d like. But perhaps, writing this down, will force me to think about this issue more and prevent further frustrations.</p>
<p>OK, so now for a real world example. I&#8217;ve been recently working on an admin panel for a web app using PHP and Javascript with the Prototype.js library. There is a section of the admin panel where the user can create polls. The user enters a question along with one or more answers and can then re-sort the answers if desired. The re-sorting issue is where I started losing control.</p>
<p>Each answer is stored in an answers table in the database, and to keep it simple, we&#8217;ll just define three columns:</p>
<ul>
<li>id</li>
<li>answer</li>
<li>sort_order</li>
</ul>
<p>For the UI, the answers look something like this:</p>
<div id="attachment_313" class="wp-caption alignnone" style="width: 464px"><a href="http://randallhook.com/wp-content/uploads/2011/08/answers.png"><img class="size-full wp-image-313" title="Poll Answers" src="http://randallhook.com/wp-content/uploads/2011/08/answers.png" alt="Poll Answers" width="454" height="126" /></a><p class="wp-caption-text">Poll Answers</p></div>
<p>As you would expect, the up and down arrows allow you to move the answers up and down the list, effectively changing their sort order in the database.</p>
<p>So I immediately started down the road of reacting to the click of an arrow button by determining the current order of answers, swapping the sort orders in the database, getting the new order and manipulating the HTML markup to show the new order. Here&#8217;s the code (Note: this is just for moving an answer up the list. I had a separate, similar function for moving an answer down. I hadn&#8217;t refactored yet):</p>
<pre>
function upAnswer(id) {
	//get sort order for this row
	var sortOrder = $('orderAnswer_' + id).innerHTML;

	//if this is the first row, no need to move it up
	if (sortOrder == lowestOrder) {
		return;
	}
	
	//find the row immediately above the selected row
	var idAbove = 0;
	$$('.answerRow').each(function(i) {
		var checkId = i.readAttribute('id').substr(7);

		if ( checkId == id ) {
			throw $break;
		}
		else {
			idAbove = checkId;
		}
	});
	
	// swap the display orders in the database
	new Ajax.Request('/AdminPoll/SwapAnswers/' + id + '/' + idAbove, {
		onSuccess: function(response) {
			
			//store the answer this is going to be moved
			var moveAnswer = $('answer_' + id);
			
			//store both display orders
			var origOrder = $('orderAnswer_' + id).innerHTML;
			var targetOrder = $('orderAnswer_' + idAbove).innerHTML;
			
			//insert the moved answer in the right place
			$('answer_' + idAbove).insert({before:
				'<div id="answer_' + id + '" class="answerRow">' + moveAnswer.innerHTML + '<div style="clear:both;"></div></div>'
			});
			
			//remove the moved answer
			moveAnswer.remove();
			
			//swap the display orders on the page
			$('orderAnswer_' + id).update(targetOrder);
			$('orderAnswer_' + idAbove).update(origOrder);
			
			//hook up the action buttons
			assignActions(id);
		},
		onFailure: function(response) {
			alert('Unable to move answer');
		}
	});
}
</pre>
<p>The code above can be summarized in the following steps:</p>
<ol>
<li>Determine the sort order for the row just clicked</li>
<li>Since we are in the &#8220;move up&#8221; function, exit the function if this row is already at the top</li>
<li>Loop through the rows to find the row immediately above this one</li>
<li>We now know the two rows involved in the reordering, so call the database to swap the two sort_order values</li>
<li>Copy the current row and move it to the position above the row just swapped with</li>
<li>Delete the original answer</li>
<li>Since we only moved the markup, call the assignActions function which wires up all of the events to the new input control and buttons</li>
</ol>
<p>Yikes. Knowing that this was getting ugly, fast, I stepped back and looked at it again. Two key concepts drove the next iteration:</p>
<ol>
<li>There is no need to maintain the actual sort orders. Just make sure the answers stay in order. For example, if the current order is
<ul>
<li>[answer1 => 5, answer2 => 8, answer3 => 9]</li>
</ul>
<p>and we are going to swap the first two answers, the new order does not have to be</p>
<ul>
<li>[answer2 => 5, answer1 => 8, answer3 => 9]</li>
</ul>
<p>It can be</p>
<ul>
<li>[answer2 => 1, answer1 => 2, answer3 => 3]</li>
</ul>
</li>
<li>Change thinking from altering the database first then the markup. Instead, swap the two rows in markup, determine what happened, then send the new order to the database.</li>
</ol>
<p>Here&#8217;s the new code:</p>
<pre>
function moveAnswer(id, moveUp) {
	//get the answer element that is going to be moved
	var answerSource = $('answer_' + id);
	
	//get the current order of the list
	var answerOrder = new Array();
	var iCounter = 0;
	$$('.answerRow').each(function(i) {
		answerOrder[iCounter++] = i.readAttribute('id').substr(i.readAttribute('id').indexOf('_') + 1);
	});
	
	if (moveUp) {
		//make sure that the answer the user wants moved isn't already at the top of the list
		if (id == answerOrder[0]) {
			return;
		}
		else {
			//get the element above the source element
			var answerTarget = $('answer_' + id).previous();
			answerTarget.insert({
				before: answerSource
			});
		}
	}
	//move down
	else {
		if (id == answerOrder[answerOrder.length - 1]) {
			return;
		}
		else {
			//get the element below the source element
			var answerTarget = $('answer_' + id).next();
			
			answerTarget.insert({
				after: answerSource
			});
		}
	}
	
	resortAnswers();
}

function resortAnswers() {
	//get the order of the answer ids
	var answerOrder = new Array();
	
	$$('.answerRow').each(function(i) {
		if (i.readAttribute('id') != null) {
			answerOrder.push(i.readAttribute('id').substr(i.readAttribute('id').indexOf('_') + 1));
		}
	});
	
	if (answerOrder.length > 1) {
		new Ajax.Request('/AdminPoll/SortAnswers/' + answerOrder.join(), {
			onSuccess: function(response) {
			},
			onFailure: function(response) {
				alert('Unable to sort answers');
			}
		});
	}
}

</pre>
<p>First, note that this function handles moves both up and down by passing in a boolean. Here is a summary of the new code:</p>
<ol>
<li>Get the answer that&#8217;s going to be moved</li>
<li>Get the current order of answers</li>
<li>If the answer is not at the end of the list already, insert it before or after the answer next to it, depending on which way we are moving</li>
<li>Call the database to store the new order</li>
</ol>
<p>Ahhh, this feels so much better. As you may have noticed, the new code really isn&#8217;t any smaller (although there&#8217;s still improvement to be made), but it is much cleaner and handles the resorting more efficiently.</p>
<p>A couple of key indicators that I&#8217;m about to fall into the complexity trap and should stop me in my tracks are:</p>
<ul>
<li>The code is getting ugly</li>
<li>The nagging feeling that there&#8217;s probably a function out there to handle something I&#8217;m trying to work out (in this case, Prototype&#8217;s next() and previous() functions)</li>
</ul>
<p>It felt good to get back in there and clean up that code. But if I can be more aware of the warning signs, maybe I can do it right in the first place.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/08/03/falling-into-the-complexity-trap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Finding the Bottom of the Page</title>
		<link>http://randallhook.com/2011/07/01/finding-the-bottom-of-the-page/</link>
		<comments>http://randallhook.com/2011/07/01/finding-the-bottom-of-the-page/#comments</comments>
		<pubDate>Fri, 01 Jul 2011 13:18:28 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[element]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[positioning]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=298</guid>
		<description><![CDATA[I ran into a challenge the other day where I had to calculate how much space was left between an HTML element and the bottom of the browser window. We were using Google&#8217;s Search-As-You-Type code (http://code.google.com/p/search-as-you-type/) which, according to a fellow developer, &#8220;worked like a dream&#8221;. He then handed it off to me to implement [...]]]></description>
				<content:encoded><![CDATA[<p>I ran into a challenge the other day where I had to calculate how much space was left between an HTML element and the bottom of the browser window. We were using Google&#8217;s Search-As-You-Type code (<a href="http://code.google.com/p/search-as-you-type/" target="_blank">http://code.google.com/p/search-as-you-type/</a>) which, according to a fellow developer, &#8220;worked like a dream&#8221;. He then handed it off to me to implement in another section of the application.</p>
<p>He had been using the search bar at the top of the page with no problems, whereas I needed it further down in a form. I dropped in the code and found that the JavaScript was somehow not calculating a dimension correctly. Depending on where you had scrolled the page to, the search box would either shrink the height of the dropdown results to barely anything, or sometimes nothing at all!</p>
<p>The Google code has a function called updateDimensionsAndShadow(), and that seemed to be the culprit. So after trying to modify what was in there and getting nowhere, I added a small section of code for the script to calculate the dropdown height correctly. Now, the big challenge for me here was that I&#8217;ve never had to try and find where the current &#8220;bottom of the page&#8221; was. I usually worry about the positioning of an element from the top of the page. So here is what I learned, and the code I wrote to fix the height issue.</p>
<div id="attachment_304" class="wp-caption alignnone" style="width: 610px"><a href="http://randallhook.com/wp-content/uploads/2011/07/JSFindingtheBottom.jpg"><img class="size-full wp-image-304" title="Finding the Bottom" src="http://randallhook.com/wp-content/uploads/2011/07/JSFindingtheBottom.jpg" alt="Finding the Bottom" width="600" height="450" /></a><p class="wp-caption-text">Finding the Bottom</p></div>
<p>The first thing we do is find the y-coordinate for the top of our input element of our search box, relative to the top of the document. This is done by first grabbing the offsetTop of the element which is it&#8217;s position relative to the container it&#8217;s in. We add the offsetHeight of the input because we are going to actually want the position of the bottom of the input box (that&#8217;s where the dropdown list will start).</p>
<pre>var sf = document.getElementById('searchField');</pre>
<pre>var searchTop = sf.offsetTop + sf.offsetHeight;
</pre>
<p>Next we will cycle through all of the element&#8217;s ancestor&#8217;s (via offsetParent) and continuously add each of their offsetTop coordinates to our caclulation. This will give us the y-coordinate of the bottom of the search box relative to the very top of the document.</p>
<pre>var sfParent = sf.offsetParent;</pre>
<pre>while (sfParent) {</pre>
<pre> searchTop += sfParent.offsetTop;</pre>
<pre> sfParent = sfParent.offsetParent;</pre>
<pre>};</pre>
<p>Next we will get the position of the bottom of the browser&#8217;s toolbar, relative to the top of the document.</p>
<pre>var yOffset = (window.pageYOffset) ? window.pageYOffset : document.body.scrollTop;</pre>
<p>We can now finally calculate the max height of our search results dropdown list (refer to the diagram).</p>
<pre>var maxSearchResultsHeight =
document.documentElement.clientHeight - (searchTop - window.pageYOffset) - searchAsYouTypeConfiguration.bottomPageMargin;</pre>
<p>What we are saying here is &#8220;The height we have available is [the visible height in the browser] minus [the difference between the top of the element in the document and the bottom of the browser toolbar in the document] minus [the margin we want between the droplist and bottom of the browser]&#8220;.</p>
<p>Now the one thing I do need to fix yet is cross-browser compatibility (cough, IE, cough, cough), but the above modifications appear to be working in the real browsers.</p>
<p>Please let me know if you have a better way of finding the bottom of the page.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/07/01/finding-the-bottom-of-the-page/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How To Write a Simple Facebook Share App</title>
		<link>http://randallhook.com/2011/05/26/how-to-write-a-simple-facebook-share-app/</link>
		<comments>http://randallhook.com/2011/05/26/how-to-write-a-simple-facebook-share-app/#comments</comments>
		<pubDate>Thu, 26 May 2011 19:40:38 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=286</guid>
		<description><![CDATA[Can I Just Cut Some Code Already? All I wanted to do was to let users share a video link from a web app I was building. I thought it would be easy. And you know what, it probably is. It&#8217;s probably easier than what I&#8217;m going to show you here. But is it simply [...]]]></description>
				<content:encoded><![CDATA[<div id="attachment_287" class="wp-caption alignnone" style="width: 262px"><a href="http://randallhook.com/wp-content/uploads/2011/05/developer-web-thing.png"><img class="size-full wp-image-287" title="developer web thing" src="http://randallhook.com/wp-content/uploads/2011/05/developer-web-thing.png" alt="facebook developer" width="252" height="271" /></a><p class="wp-caption-text">Developing Apps for Facebook</p></div>
<h2>Can I Just Cut Some Code Already?</h2>
<p>All I wanted to do was to let users share a video link from a web app I was building. I thought it would be easy. And you know what, it probably is. It&#8217;s probably easier than what I&#8217;m going to show you here. But is it simply stated somewhere in Facebook&#8217;s documentation? Not that I could find. So piecing together parts of their docs, plowing through search after search and reading a multitude of blog posts, I came up with this solution.</p>
<h2>Quick Overview</h2>
<p>The idea is a simple one. The web app let&#8217;s you choose a video and view it within the site. If the &#8220;share this&#8221; button is clicked, the familiar Facebook dialog will popup, letting the user add an additional message to the built-in metadata for the share. If the user has not yet logged into Facebook, they will be prompted to do so. OK, here we go.</p>
<h2>Set Up a Facebook App</h2>
<p>I thought that you could simply call a Facebook url with some extra information and you were all set, but all of the examples I saw included an application id as part of the call. So I needed to setup an app. For those of you that haven&#8217;t done this before, here is a quick summary.</p>
<p>Search for the Developer App in the search bar and install it to your Facebook account.</p>
<div id="attachment_290" class="wp-caption alignnone" style="width: 574px"><a href="http://randallhook.com/wp-content/uploads/2011/05/add-dev-app2.png"><img class="size-full wp-image-290" title="Add the Developer App" src="http://randallhook.com/wp-content/uploads/2011/05/add-dev-app2.png" alt="Add the Developer App" width="564" height="325" /></a><p class="wp-caption-text">Add the Developer App</p></div>
<p>After you install, you will be presented with a page that allows you to configure the app. There are a lot of settings here, and many of them pertain to applications with a lot more complexity. For our purposes, we are really interested in only a few things. Fill out the basic info as desired (such as website, support email, etc.) and add a logo to spruce up the share dialog box. You will notice on the main settings page that an application id is presented for you. You need that for your share code.</p>
<p>One other note is that apps can have sandbox mode enabled. When in sandbox mode, only you, as the developer, can use the app. This is of course useful for testing. Don&#8217;t forget to disable the sandbox when you are ready to go live.</p>
<div id="attachment_291" class="wp-caption alignnone" style="width: 521px"><a href="http://randallhook.com/wp-content/uploads/2011/05/sandbox.png"><img class="size-full wp-image-291" title="Sandbox Mode" src="http://randallhook.com/wp-content/uploads/2011/05/sandbox.png" alt="Sandbox Mode" width="511" height="114" /></a><p class="wp-caption-text">Sandbox Mode</p></div>
<h2>Writing the Code</h2>
<p>OK, now let&#8217;s get down to it. Like I mentioned, this is quick.</p>
<pre>$('#shareFacebook').click(function(event) {</pre>
<pre> window.open(FB_FEED_DIALOG_URL +</pre>
<pre> 'app_id=' + FB_APP_ID +</pre>
<pre> '&amp;redirect_uri=' + FB_LINK_DOMAIN +</pre>
<pre> '&amp;name=' + FB_TITLE_PREFIX + data.title +</pre>
<pre> '&amp;caption=' + data.title +</pre>
<pre> '&amp;description=' + data.description +</pre>
<pre> '&amp;source=' + FB_YOUTUBE_SOURCE_PREFIX + data.videoId +</pre>
<pre> '&amp;link=' + FB_YOUTUBE_LINK_PREFIX + data.videoId +</pre>
<pre> '&amp;picture=' + FB_YOUTUBE_PICTURE_PREFIX + data.videoId + FB_YOUTUBE_PICTURE_SUFFIX</pre>
<pre> );
});
</pre>
<p>I have the call to Facebook wrapped inside a jQuery click event which opens a new window. The heart of it is the URL inside of window.open().</p>
<ul>
<li>The call starts out with FB_FEED_DIALOG_URL which is set to &#8216;http://www.facebook.com/dialog/feed?&#8217;.</li>
<li> Set your application id which you obtained earlier.</li>
<li>Add the URL that you want Facebook to redirect the user to after they&#8217;ve shared the link. Note that Facebook will append a &#8216;/?postId=xxxx&#8217; to the URL so be prepared to handle that. <em>I simply ignored it by telling our routing rules to trim off the question mark and everything after before handing it to the dispatcher.</em></li>
<li>The name, caption, description, source, link and picture will show up in the share dialog box.</li>
</ul>
<p>As you may have noticed, I&#8217;m sharing a YouTube video. That also brought it&#8217;s own challenges on how to set these parameters correctly. I believe I obtained these settings from a blog somewhere, which I can&#8217;t remember (thank you to the author):</p>
<ul>
<li>FB_YOUTUBE_SOURCE_PREFIX = &#8216;http://www.youtube.com/v/&#8217;</li>
<li>FB_YOUTUBE_LINK_PREFIX = &#8216;http://www.youtube.com/watch?v=&#8217;</li>
<li>FB_YOUTUBE_PICTURE_PREFIX = &#8216;img.youtube.com/vi/&#8217;</li>
</ul>
<p>The YouTube video id is appended to each of the above constants.</p>
<p>The dialog looks like this:</p>
<div id="attachment_292" class="wp-caption alignnone" style="width: 577px"><a href="http://randallhook.com/wp-content/uploads/2011/05/share-dialog.png"><img class="size-full wp-image-292" title="Facebook Share Dialog" src="http://randallhook.com/wp-content/uploads/2011/05/share-dialog.png" alt="Facebook Share Dialog" width="567" height="313" /></a><p class="wp-caption-text">Facebook Share Dialog</p></div>
<p>After the user clicks the Publish button, the link is posted to their wall and they are redirected to the URL you specified.</p>
<h2>Conclusion</h2>
<p>I really hope this helps some of you out there as I could not figure out why something seemingly so simple was taking me forever to figure out. Also, I&#8217;m sure there are a lot of alternatives to this method &#8212; some much simpler I&#8217;m afraid. I&#8217;d be interested in hearing your solutions.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/05/26/how-to-write-a-simple-facebook-share-app/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>My Subversion and Dropbox Setup</title>
		<link>http://randallhook.com/2011/03/24/my-subversion-and-dropbox-setup/</link>
		<comments>http://randallhook.com/2011/03/24/my-subversion-and-dropbox-setup/#comments</comments>
		<pubDate>Thu, 24 Mar 2011 17:41:01 +0000</pubDate>
		<dc:creator>randy</dc:creator>
				<category><![CDATA[web coding]]></category>
		<category><![CDATA[svn]]></category>

		<guid isPermaLink="false">http://randallhook.com/?p=268</guid>
		<description><![CDATA[I&#8217;ve really come to love my version control setup that I&#8217;ve been using heavily over the last several months, and I wanted to share it. It&#8217;s really quite simple and it has saved me a lot of headaches. I had been using a central, cloud-based storage method for uploading my working code (Google Docs, Dropbox, [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve really come to love my version control setup that I&#8217;ve been using heavily over the last several months, and I wanted to share it. It&#8217;s really quite simple and it has saved me a lot of headaches. I had been using a central, cloud-based storage method for uploading my working code (Google Docs, Dropbox, etc.) so that I had access to the code wherever I was and whatever device I happened to be using.</p>
<p>Here is the problem, though. For example, I&#8217;m coding on my desktop and am about ready to head out the door to continue coding at the coffee shop. So I zip up all of my files, upload them to the cloud, then download them to my notebook. That&#8217;s not too horrible of a process, but how do I fit version control into this scenario? My first try ended in more steps and more headaches as I could never keep the working copies and repositories in sync.</p>
<p>Then I heard of Dropbox&#8217;s synced folder feature. This was the answer. Now I don&#8217;t even move or package my files at all! I simply commit them to the repository. Here&#8217;s how it works.</p>
<p>First, I setup Dropbox with the synced folder feature enabled on all of the devices that I intend to use.</p>
<div id="attachment_284" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-012.png"><img class="size-full wp-image-284" title="svn setup 01" src="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-012.png" alt="svn setup 01" width="500" height="400" /></a><p class="wp-caption-text">Setup Dropbox on all devices</p></div>
<p>Any files I drop into any of these folders will automatically replicate to the other folders. Next, I setup a Subversion repository in the Dropbox folder of any one of the devices, which of course then replicates.</p>
<div id="attachment_273" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-02.png"><img class="size-full wp-image-273" title="svn setup 02" src="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-02.png" alt="svn setup 02" width="500" height="400" /></a><p class="wp-caption-text">Create a Subversion repository in any of the Dropbox folders</p></div>
<p>So here I am, back on the desktop. I checkout the latest version of the project code from the repository in the Dropbox folder, make my changes, then commit those changes back to the repository. The repository is now updated on all devices.</p>
<div id="attachment_276" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-03.png"><img class="size-full wp-image-276" title="svn setup 03" src="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-03.png" alt="svn setup 03" width="500" height="400" /></a><p class="wp-caption-text">Checkout a working copy, make changes and commit</p></div>
<p>On over to my laptop, let&#8217;s say I&#8217;ve already been working on the code from there as well. I perform a Subversion update on my working copy of the project, which grabs all of the changes I made on my desktop, then merges them into my local working copy. As usual, I make changes and commit back to the repository, and so on and so on.</p>
<p>One final note. It&#8217;s good to have that repository backed up, so on one of my devices, I have Amazon&#8217;s Jungle Disk installed, which backs up my Dropbox folder every night.</p>
<div id="attachment_278" class="wp-caption alignnone" style="width: 510px"><a href="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-04.png"><img class="size-full wp-image-278" title="svn setup 04" src="http://randallhook.com/wp-content/uploads/2011/03/svn-setup-04.png" alt="svn setup 04" width="500" height="400" /></a><p class="wp-caption-text">Backup repository</p></div>
<p>So as you can see, once everything is set up, I simply hop on a device of my choice, update my working copy, make changes then commit back to the repository. Done. No headaches.</p>
]]></content:encoded>
			<wfw:commentRss>http://randallhook.com/2011/03/24/my-subversion-and-dropbox-setup/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
