<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Dave Dash</title>
 <link href="http://davedash.com/tag/reviewsby.us/atom.xml" rel="self"/>
 <link href="http://davedash.com/tag/reviewsby.us"/>
 <updated>2012-04-07T22:42:44-07:00</updated>
 <id>http://davedash.com/</id>
 <author>
   <name>Dave Dash</name>
   <email>dd+atom1@davedash.com</email>
 </author>

 
 <entry>
   <title>Resolving Django dumpdata errors</title>
   <link href="http://davedash.com/2009/09/13/resolving-django-dumpdata-errors/"/>
   <updated>2009-09-13T00:00:00-07:00</updated>
   <id>http://davedash.com/2009/09/13/resolving-django-dumpdata-errors</id>
   <content type="html">&lt;p&gt;Recently I recieved this wonderful piece of news when I ran &lt;code&gt;./manage.py dumpdata&lt;/code&gt; for the first time:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Error: Unable to serialize database: User matching query does not exist.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I knew this might not work out since I was dealing with a legacy database, but the resolution is quite simple.  First I had to narrow it down to which app was causing this.  Naturally I assumed it was one of the two apps I had, either &lt;code&gt;common&lt;/code&gt; or &lt;code&gt;restaurant&lt;/code&gt;.  So I ran: &lt;code&gt;./manage.py dumpdata common&lt;/code&gt; and &lt;code&gt;./manage.py dumpdata restaurant&lt;/code&gt;.  The latter had no problem whatsoever.&lt;/p&gt;

&lt;p&gt;This made sense, since my &lt;code&gt;common&lt;/code&gt; application was the only one that made any reference to a &lt;code&gt;User&lt;/code&gt;.  By looking in my &lt;code&gt;models.py&lt;/code&gt; for that application, I narrowed it down to my &lt;code&gt;Profile&lt;/code&gt; object.  Sure enough, commenting it out meant I could get my data.&lt;/p&gt;

&lt;p&gt;It ended up being a foreign key mismatch between the &lt;code&gt;profile&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; tables.  Since this is legacy data, this mismatch made sense.  A simple &lt;code&gt;SELECT id,userid FROM profile WHERE userid NOT IN (SELECT id FROM auth_user)&lt;/code&gt; gave me a list of bad profiles.  Removing them allowed me to create my Django fixtures.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Star Rating using YUI (and Django)</title>
   <link href="http://davedash.com/2008/03/04/star-rating-using-yui-and-django/"/>
   <updated>2008-03-04T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/03/04/star-rating-using-yui-and-django</id>
   <content type="html">&lt;p&gt;I have a very good star rater on &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;, but it was written using
some sloppy prototype code.  I wanted to redo star raters in a well thought out
manner and I wanted to use &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt;.  In this particular tutorial I will use
&lt;a href=&quot;http://djangoproject.com/&quot;&gt;Django&lt;/a&gt; although it is not a requirement.&lt;/p&gt;

&lt;p&gt;For some background information on star raters, see this
&lt;a href=&quot;http://developer.yahoo.com/ypatterns/pattern.php?pattern=ratinganobject&quot;&gt;Yahoo! Design Pattern&lt;/a&gt;.  Our pattern is more of a join star rater, similar
to what's found on Netflix: you see an average rating for a restaurant or dish
unless you yourself have rated it.&lt;/p&gt;

&lt;p&gt;This was a thought out design decision for our &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; redesign.
Our site is primarily a personal utility that answers the question, &quot;What
dishes do I like at a particular restaurant?&quot;  If you haven't rated something
the website can only offer up an average and you can use that as a decision as
to whether you should eat something or not.&lt;/p&gt;

&lt;p&gt;If you have eaten something however, that average rating is irrelevant.  You
don't need fellow meal advisors to tell you that you liked Chicken Makhani, you
already know that for yourself.  Therefore we show only your rating unless you
haven't rated something.&lt;/p&gt;

&lt;h3&gt;Working backwards&lt;/h3&gt;

&lt;p&gt;I like to &quot;work backwards&quot; as it were.  Meaning, I like to just write the code
that I ultimately will use to output a star rater.  From there I will work on
the supporting code that is necessary.  I find by using this strategy, I can
keep my code fairly clean and organized.&lt;/p&gt;

&lt;h3&gt;The template&lt;/h3&gt;

&lt;p&gt;So ultimately I want this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{\% star 'mything' 3 4 '/path/to/script' %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To show up as this:&lt;/p&gt;

&lt;div style=&quot;text-align:center&quot;&gt;
&lt;img src=&quot;http://spindrop.us/wp-content/uploads/2008/03/starrater.png&quot; alt=&quot;starrater.png&quot; border=&quot;0&quot; width=&quot;105&quot; height=&quot;31&quot; /&gt;
&lt;/div&gt;




&lt;!--more--&gt;


&lt;p&gt;Unfortunately &lt;a href=&quot;http://www.djangoproject.com/documentation/templates/&quot;&gt;Django templates&lt;/a&gt; doesn't seem to have named attributes for &lt;a href=&quot;http://www.djangoproject.com/documentation/templates_python/&quot;&gt;template tags&lt;/a&gt;, so I'll need to explain my syntax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;star&lt;/code&gt;: is the template tag which we define below&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'mything'&lt;/code&gt;: is an id string we will use for this rater and its associated objects&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&lt;/code&gt;: this is the second argument to star, it will be the users current rating, it can also be None&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4.1&lt;/code&gt;: this is the third argument, it will be the average rating, it can also be None&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/path/to/script&lt;/code&gt;: is the form that will process our rating&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;The HTML we want&lt;/h3&gt;

&lt;p&gt;Another developer had a &lt;a href=&quot;http://www.unessa.net/en/hoyci/projects/yui-star-rating/&quot;&gt;good approach for handling star ratings&lt;/a&gt; and for handling Javascript in general.  Create an underlying Javascript-free system, and then let the Javascript make it pretty.  This is way to degrade gracefully.&lt;/p&gt;

&lt;p&gt;Ultimately, I had my own approach to this problem, I wanted much of the visual lifting to happen on the CSS layer.  So, we'll use the following code:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;html&quot;&gt;
&lt;span class=&quot;joint_star_rater&quot;&gt;
    &lt;form id=&quot;rater_restaurant&quot; method=&quot;post&quot; action=&quot;/restaurant/pizza-luce/rate/&quot;&gt;
        &lt;fieldset&gt;
            &lt;legend&gt;Rating&lt;/legend&gt;
            &lt;ul&gt;

        &lt;li style=&quot;width: 100px;&quot; title=&quot;5&quot; class=&quot;current meta&quot;&gt;Current Rating: 5&lt;/li&gt;


        &lt;li title=&quot;Poor&quot; class=&quot;star_1 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_1&quot;&gt;Poor&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;1&quot; id=&quot;restaurant_rating_1&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Fair&quot; class=&quot;star_2 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_2&quot;&gt;Fair&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;2&quot; id=&quot;restaurant_rating_2&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Good&quot; class=&quot;star_3 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_3&quot;&gt;Good&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;3&quot; id=&quot;restaurant_rating_3&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Very Good&quot; class=&quot;star_4 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_4&quot;&gt;Very Good&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;4&quot; id=&quot;restaurant_rating_4&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Excellent&quot; class=&quot;star_5 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_5&quot;&gt;Excellent&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;5&quot; id=&quot;restaurant_rating_5&quot;/&gt;
        &lt;/li&gt;

            &lt;/ul&gt;
        &lt;/fieldset&gt;
        &lt;input type=&quot;submit&quot; name=&quot;rate&quot; value=&quot;rate it&quot; class=&quot;submit&quot;/&gt;
    &lt;/form&gt;
&lt;/span&gt;

&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;A couple things to note in our HTML.  Our unique string is &lt;code&gt;restaurant&lt;/code&gt;.  It's got an ID that is as unique as you want: &lt;code&gt;rater_restaurant&lt;/code&gt; where &lt;code&gt;restaurant&lt;/code&gt; was the first argument to our template tag.  We use &lt;code&gt;restaurant&lt;/code&gt; to create some other unique IDs as well.&lt;/p&gt;

&lt;p&gt;Also, this rating form makes a lot of sense semantically.  While this form in its current state is a far cry from some ajaxy goodness, it makes clear sense as to what is going on.&lt;/p&gt;

&lt;h3&gt;The template tag&lt;/h3&gt;

&lt;p&gt;Well we know what we want from the HTML side, so let's start coding our &lt;code&gt;star&lt;/code&gt; tag:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
@register.simple_tag
def star(id_string, current, average, path, spanfree=False):
    meta = None
    if current != None:
        meta = &quot;&quot;&quot;
        &lt;li class=&quot;current meta&quot; title=&quot;%d&quot; style=&quot;width:%dpx&quot;&gt;Current Rating: %d&lt;/li&gt;
        &quot;&quot;&quot; % (int(current), int(current)*20, int(current))
    else:
        meta = &quot;&quot;&quot;
        &lt;li class=&quot;average meta&quot; title=&quot;%.1f&quot; style=&quot;width:%dpx&quot;&gt;Average Rating: %.1f&lt;/li&gt;
        &quot;&quot;&quot; % (average, average*20,average)

    stars   = ''
    ratings = ['Poor', 'Fair', 'Good', 'Very Good', 'Excellent']

    for i in range(1,6):
        stars = stars + &quot;&quot;&quot;
        &lt;li class=&quot;star_%d star&quot; title=&quot;%s&quot;&gt;
            &lt;label for=&quot;%s_rating_%d&quot;&gt;%s&lt;/label&gt;
            &lt;input id=&quot;%s_rating_%d&quot; type=&quot;radio&quot; value=&quot;%d&quot; name=&quot;rating&quot;/&gt;
        &lt;/li&gt;
        &quot;&quot;&quot; % (i, ratings[i-1],id_string, i, ratings[i-1], id_string, i, i)
    html = &quot;&quot;&quot;
    &lt;form action=&quot;%s&quot; method=&quot;post&quot; id=&quot;rater_%s&quot;&gt;
        &lt;fieldset&gt;
            &lt;legend&gt;Rating&lt;/legend&gt;
            &lt;ul&gt;
            %s
            %s
            &lt;/ul&gt;
        &lt;/fieldset&gt;
        &lt;input type=&quot;submit&quot; class=&quot;submit&quot; value=&quot;rate it&quot; name=&quot;rate&quot;/&gt;
    &lt;/form&gt;
    &quot;&quot;&quot; % (path, id_string, meta,stars)
    if spanfree:
        return html
    else:
        return &quot;&quot;&quot;&lt;span class=&quot;joint_star_rater&quot;&gt;%s&lt;/span&gt;&quot;&quot;&quot; % html
    return html

&lt;/textarea&gt;&lt;/p&gt;


&lt;h3&gt;The CSS&lt;/h3&gt;

&lt;p&gt;A lot of work will happen via CSS.  The CSS will remove quite a bit of the textual data that can be interpreted graphically with stars.&lt;/p&gt;

&lt;p&gt;The strategy we use is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fix the &lt;code&gt;UL&lt;/code&gt; at a certain width with a background of grey stars&lt;/li&gt;
&lt;li&gt;decorate the &lt;code&gt;LI.average&lt;/code&gt; and &lt;code&gt;LI.current&lt;/code&gt; with repeating stars (blue and orange respectively) with a  &lt;code&gt;z-index&lt;/code&gt; of &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;decorate the &lt;code&gt;LI.average:hover&lt;/code&gt; and &lt;code&gt;LI.current:hover&lt;/code&gt; with a transparent background&lt;/li&gt;
&lt;li&gt;decorate &lt;code&gt;LI:hover input&lt;/code&gt; as a colored in star and a &lt;code&gt;z-index&lt;/code&gt; of &lt;code&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This might not make sense now, until you see the CSS in full action.  Also for the stars we'll use a sprite of 3 stars.  A grey defunct star as the default background, a blue star if it's the average rating for an item and an orange star if it's what the user wants.&lt;/p&gt;

&lt;p&gt;I use the following sprite:&lt;/p&gt;

&lt;div style=&quot;background: #000;text-align:center; padding:1em&quot;&gt;
&lt;img src=&quot;http://spindrop.us/wp-content/uploads/2008/03/stars.png&quot; alt=&quot;stars.png&quot; border=&quot;0&quot; width=&quot;20&quot; height=&quot;60&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;The following CSS will do some magic:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;css&quot;&gt;
.joint_star_rater {display:inline-block;}
.joint_star_rater ul{width:100px;position:relative;height:20px;background:url(../images/icons/stars.png) repeat-x 0 0}
.joint_star_rater li.meta{position:absolute;text-indent:-9999px;display:block;z-index:1;}
.joint_star_rater ul:hover li.meta{display:none;}
.joint_star_rater li.current{background:url(../images/icons/stars.png) repeat-x 0 -40px}
.joint_star_rater li.average{background:url(../images/icons/stars.png) repeat-x 0 -20px}
.joint_star_rater li{height:20px;width:20px;position:absolute;text-indent:-9999px;z-index:3;}
.joint_star_rater li.star_2{left:20px;}
.joint_star_rater li.star_3{left:40px}
.joint_star_rater li.star_4{left:60px}
.joint_star_rater li.star_5{left:80px}
.joint_star_rater li.star_1:hover{width:20px}
.joint_star_rater li.star_2:hover{width:40px}
.joint_star_rater li.star_3:hover{width:60px}
.joint_star_rater li.star_4:hover{width:80px}
.joint_star_rater li.star_5:hover{width:100px}
.joint_star_rater li.star:hover{background:url(../images/icons/stars.png) repeat-x 0 -40px;z-index:2;left:0;}
.joint_star_rater input.submit{display:none;}
&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;The &lt;code&gt;inline-block&lt;/code&gt; value for &lt;code&gt;display&lt;/code&gt; is not supported very well.  I recently switched to Firefox 3 Beta and it renders as expected.  Firefox 2 has problems with it.  I may revise the CSS later to accommodate it.&lt;/p&gt;

&lt;h3&gt;The Javascript&lt;/h3&gt;

&lt;p&gt;The fundamental drawback to the design here, is that it really only works well with the Javascript on.  In fact, with the CSS on and Javascript off, this code will not work very well for the end user.  This too will be revised in the future.&lt;/p&gt;

&lt;p&gt;Our Javascript needs to do something very simple:
* extract the star value you clicked on
* send it to the server
* redraw the stars&lt;/p&gt;

&lt;p&gt;It's a very simple operation, but I honestly think other libraries have an advantage to YUI in this regard.&lt;sup id=&quot;#fnr_1&quot;&gt;&lt;a href=&quot;#fn_1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;  Here's some unobtrusive code I came up with:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;js&quot;&gt;
var MA = {}; // MA namespace
MA.e = YAHOO.util.Event;
MA.d = YAHOO.util.Dom;
MA.c = YAHOO.util.Connect;

MA.star_rater = function() {
    var e = YAHOO.util.Event;
    var d = YAHOO.util.Dom;

    return {
        init: function() {
            e.onDOMReady(this.setup,this, true)
        },

        setup: function() {
            e.on(d.get('doc4'),'click',this.handleClick,this,true);
        },

        handleClick: function(ev) {
            var target = e.getTarget(ev);
            if (d.hasClass(target, 'star')
            &amp;&amp; d.hasClass(target.parentNode.parentNode.parentNode.parentNode, 'joint_star_rater')) {
                this.rate(target);
            }
        },

        rate: function(el) {
            if (MA.is_authenticated('Please sign in before rating =)')) {

                root   = el.parentNode.parentNode.parentNode.parentNode;
                action      = el.parentNode.parentNode.parentNode.action;
                input       = MA.d.getFirstChildBy(el, function(d) {return (d.tagName == 'input'||d.tagName=='INPUT')});
                this.value  = input.value;
                postdata    = &quot;value=&quot;+this.value;

                handleSuccess = function(o) { root.innerHTML = o.responseText }
                callback = {
                    success:handleSuccess,
                }


                var request = MA.c.asyncRequest('POST', action, callback, postdata);
                // construct a connection object to this and use it to make a post
                // retrieve the post and then replace it with the original span
            }
        },

    }
}();

MA.star_rater.init();
&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;Note: I intentionally left out irrelevant pieces of code, like the function definition of &lt;code&gt;MA.is_authenticated()&lt;/code&gt;, this code isn't meant for cutting and pasting, it's meant for cutting-pasting and then some careful editing.&lt;/p&gt;

&lt;h3&gt;The callback view&lt;/h3&gt;

&lt;p&gt;The callback script is what you specify when you call &lt;code&gt;{\% star ... %}&lt;/code&gt;.  The view I use is as follows:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
@login_required
def rate(request, slug):
    MyObject = get_my_object()
    value    = request['value']

    MyObject,rate(value);
    return render_to_response(&quot;rating.html&quot;, locals(), context_instance=RequestContext(request))

&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;That code is oversimplified... you have to write your own logic as it applies to our site.  The &lt;code&gt;rating.html&lt;/code&gt; is simply the call to your star tag:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{\% load tags %}
{\% star 'mything' restaurant.current_rating restaurant.average_rating restaurant.get_rating_url 1 %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the &lt;code&gt;1&lt;/code&gt; at the end.  It's a flag to turn off the outer &lt;code&gt;span&lt;/code&gt; so we can just insert the guts back into the original &lt;code&gt;span&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;The star-rater is really a large problem that's hard to tackle in one sitting and quite frankly is not documented well anywhere.  The code I've provided is a shadow of the real code I'll be using, but hopefully it's enough to get you started.&lt;/p&gt;

&lt;p&gt;I definitely will update my production code to solve a few outstanding issues, as I mentioned above.  I'll try to update this tutorial at the same time.  If there are questions about the examples given, feel free to ask and I'll attempt to answer.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn_1&quot;&gt;YUI has the &lt;code&gt;Selector&lt;/code&gt; and that might alleviate some problems, but jQuery has very nice selection capabilities.  In this future version I keep promising, I'll use the YUI &lt;code&gt;Selector&lt;/code&gt; &lt;a href=&quot;#fnr_1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Star Rating using YUI (and Django)</title>
   <link href="http://davedash.com/2008/03/04/star-rating-using-yui-and-django/"/>
   <updated>2008-03-04T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/03/04/star-rating-using-yui-and-django</id>
   <content type="html">&lt;p&gt;I have a very good star rater on &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;, but it was written using
some sloppy prototype code.  I wanted to redo star raters in a well thought out
manner and I wanted to use &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt;.  In this particular tutorial I will use
&lt;a href=&quot;http://djangoproject.com/&quot;&gt;Django&lt;/a&gt; although it is not a requirement.&lt;/p&gt;

&lt;p&gt;For some background information on star raters, see this
&lt;a href=&quot;http://developer.yahoo.com/ypatterns/pattern.php?pattern=ratinganobject&quot;&gt;Yahoo! Design Pattern&lt;/a&gt;.  Our pattern is more of a join star rater, similar
to what's found on Netflix: you see an average rating for a restaurant or dish
unless you yourself have rated it.&lt;/p&gt;

&lt;p&gt;This was a thought out design decision for our &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; redesign.
Our site is primarily a personal utility that answers the question, &quot;What
dishes do I like at a particular restaurant?&quot;  If you haven't rated something
the website can only offer up an average and you can use that as a decision as
to whether you should eat something or not.&lt;/p&gt;

&lt;p&gt;If you have eaten something however, that average rating is irrelevant.  You
don't need fellow meal advisors to tell you that you liked Chicken Makhani, you
already know that for yourself.  Therefore we show only your rating unless you
haven't rated something.&lt;/p&gt;

&lt;h3&gt;Working backwards&lt;/h3&gt;

&lt;p&gt;I like to &quot;work backwards&quot; as it were.  Meaning, I like to just write the code
that I ultimately will use to output a star rater.  From there I will work on
the supporting code that is necessary.  I find by using this strategy, I can
keep my code fairly clean and organized.&lt;/p&gt;

&lt;h3&gt;The template&lt;/h3&gt;

&lt;p&gt;So ultimately I want this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{\% star 'mything' 3 4 '/path/to/script' %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To show up as this:&lt;/p&gt;

&lt;div style=&quot;text-align:center&quot;&gt;
&lt;img src=&quot;http://spindrop.us/wp-content/uploads/2008/03/starrater.png&quot; alt=&quot;starrater.png&quot; border=&quot;0&quot; width=&quot;105&quot; height=&quot;31&quot; /&gt;
&lt;/div&gt;




&lt;!--more--&gt;


&lt;p&gt;Unfortunately &lt;a href=&quot;http://www.djangoproject.com/documentation/templates/&quot;&gt;Django templates&lt;/a&gt; doesn't seem to have named attributes for &lt;a href=&quot;http://www.djangoproject.com/documentation/templates_python/&quot;&gt;template tags&lt;/a&gt;, so I'll need to explain my syntax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;star&lt;/code&gt;: is the template tag which we define below&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'mything'&lt;/code&gt;: is an id string we will use for this rater and its associated objects&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&lt;/code&gt;: this is the second argument to star, it will be the users current rating, it can also be None&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4.1&lt;/code&gt;: this is the third argument, it will be the average rating, it can also be None&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/path/to/script&lt;/code&gt;: is the form that will process our rating&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;The HTML we want&lt;/h3&gt;

&lt;p&gt;Another developer had a &lt;a href=&quot;http://www.unessa.net/en/hoyci/projects/yui-star-rating/&quot;&gt;good approach for handling star ratings&lt;/a&gt; and for handling Javascript in general.  Create an underlying Javascript-free system, and then let the Javascript make it pretty.  This is way to degrade gracefully.&lt;/p&gt;

&lt;p&gt;Ultimately, I had my own approach to this problem, I wanted much of the visual lifting to happen on the CSS layer.  So, we'll use the following code:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;html&quot;&gt;
&lt;span class=&quot;joint_star_rater&quot;&gt;
    &lt;form id=&quot;rater_restaurant&quot; method=&quot;post&quot; action=&quot;/restaurant/pizza-luce/rate/&quot;&gt;
        &lt;fieldset&gt;
            &lt;legend&gt;Rating&lt;/legend&gt;
            &lt;ul&gt;

        &lt;li style=&quot;width: 100px;&quot; title=&quot;5&quot; class=&quot;current meta&quot;&gt;Current Rating: 5&lt;/li&gt;


        &lt;li title=&quot;Poor&quot; class=&quot;star_1 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_1&quot;&gt;Poor&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;1&quot; id=&quot;restaurant_rating_1&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Fair&quot; class=&quot;star_2 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_2&quot;&gt;Fair&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;2&quot; id=&quot;restaurant_rating_2&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Good&quot; class=&quot;star_3 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_3&quot;&gt;Good&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;3&quot; id=&quot;restaurant_rating_3&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Very Good&quot; class=&quot;star_4 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_4&quot;&gt;Very Good&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;4&quot; id=&quot;restaurant_rating_4&quot;/&gt;
        &lt;/li&gt;

        &lt;li title=&quot;Excellent&quot; class=&quot;star_5 star&quot;&gt;
            &lt;label for=&quot;restaurant_rating_5&quot;&gt;Excellent&lt;/label&gt;
            &lt;input type=&quot;radio&quot; name=&quot;rating&quot; value=&quot;5&quot; id=&quot;restaurant_rating_5&quot;/&gt;
        &lt;/li&gt;

            &lt;/ul&gt;
        &lt;/fieldset&gt;
        &lt;input type=&quot;submit&quot; name=&quot;rate&quot; value=&quot;rate it&quot; class=&quot;submit&quot;/&gt;
    &lt;/form&gt;
&lt;/span&gt;

&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;A couple things to note in our HTML.  Our unique string is &lt;code&gt;restaurant&lt;/code&gt;.  It's got an ID that is as unique as you want: &lt;code&gt;rater_restaurant&lt;/code&gt; where &lt;code&gt;restaurant&lt;/code&gt; was the first argument to our template tag.  We use &lt;code&gt;restaurant&lt;/code&gt; to create some other unique IDs as well.&lt;/p&gt;

&lt;p&gt;Also, this rating form makes a lot of sense semantically.  While this form in its current state is a far cry from some ajaxy goodness, it makes clear sense as to what is going on.&lt;/p&gt;

&lt;h3&gt;The template tag&lt;/h3&gt;

&lt;p&gt;Well we know what we want from the HTML side, so let's start coding our &lt;code&gt;star&lt;/code&gt; tag:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
@register.simple_tag
def star(id_string, current, average, path, spanfree=False):
    meta = None
    if current != None:
        meta = &quot;&quot;&quot;
        &lt;li class=&quot;current meta&quot; title=&quot;%d&quot; style=&quot;width:%dpx&quot;&gt;Current Rating: %d&lt;/li&gt;
        &quot;&quot;&quot; % (int(current), int(current)*20, int(current))
    else:
        meta = &quot;&quot;&quot;
        &lt;li class=&quot;average meta&quot; title=&quot;%.1f&quot; style=&quot;width:%dpx&quot;&gt;Average Rating: %.1f&lt;/li&gt;
        &quot;&quot;&quot; % (average, average*20,average)

    stars   = ''
    ratings = ['Poor', 'Fair', 'Good', 'Very Good', 'Excellent']

    for i in range(1,6):
        stars = stars + &quot;&quot;&quot;
        &lt;li class=&quot;star_%d star&quot; title=&quot;%s&quot;&gt;
            &lt;label for=&quot;%s_rating_%d&quot;&gt;%s&lt;/label&gt;
            &lt;input id=&quot;%s_rating_%d&quot; type=&quot;radio&quot; value=&quot;%d&quot; name=&quot;rating&quot;/&gt;
        &lt;/li&gt;
        &quot;&quot;&quot; % (i, ratings[i-1],id_string, i, ratings[i-1], id_string, i, i)
    html = &quot;&quot;&quot;
    &lt;form action=&quot;%s&quot; method=&quot;post&quot; id=&quot;rater_%s&quot;&gt;
        &lt;fieldset&gt;
            &lt;legend&gt;Rating&lt;/legend&gt;
            &lt;ul&gt;
            %s
            %s
            &lt;/ul&gt;
        &lt;/fieldset&gt;
        &lt;input type=&quot;submit&quot; class=&quot;submit&quot; value=&quot;rate it&quot; name=&quot;rate&quot;/&gt;
    &lt;/form&gt;
    &quot;&quot;&quot; % (path, id_string, meta,stars)
    if spanfree:
        return html
    else:
        return &quot;&quot;&quot;&lt;span class=&quot;joint_star_rater&quot;&gt;%s&lt;/span&gt;&quot;&quot;&quot; % html
    return html

&lt;/textarea&gt;&lt;/p&gt;


&lt;h3&gt;The CSS&lt;/h3&gt;

&lt;p&gt;A lot of work will happen via CSS.  The CSS will remove quite a bit of the textual data that can be interpreted graphically with stars.&lt;/p&gt;

&lt;p&gt;The strategy we use is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fix the &lt;code&gt;UL&lt;/code&gt; at a certain width with a background of grey stars&lt;/li&gt;
&lt;li&gt;decorate the &lt;code&gt;LI.average&lt;/code&gt; and &lt;code&gt;LI.current&lt;/code&gt; with repeating stars (blue and orange respectively) with a  &lt;code&gt;z-index&lt;/code&gt; of &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;decorate the &lt;code&gt;LI.average:hover&lt;/code&gt; and &lt;code&gt;LI.current:hover&lt;/code&gt; with a transparent background&lt;/li&gt;
&lt;li&gt;decorate &lt;code&gt;LI:hover input&lt;/code&gt; as a colored in star and a &lt;code&gt;z-index&lt;/code&gt; of &lt;code&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This might not make sense now, until you see the CSS in full action.  Also for the stars we'll use a sprite of 3 stars.  A grey defunct star as the default background, a blue star if it's the average rating for an item and an orange star if it's what the user wants.&lt;/p&gt;

&lt;p&gt;I use the following sprite:&lt;/p&gt;

&lt;div style=&quot;background: #000;text-align:center; padding:1em&quot;&gt;
&lt;img src=&quot;http://spindrop.us/wp-content/uploads/2008/03/stars.png&quot; alt=&quot;stars.png&quot; border=&quot;0&quot; width=&quot;20&quot; height=&quot;60&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;The following CSS will do some magic:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;css&quot;&gt;
.joint_star_rater {display:inline-block;}
.joint_star_rater ul{width:100px;position:relative;height:20px;background:url(../images/icons/stars.png) repeat-x 0 0}
.joint_star_rater li.meta{position:absolute;text-indent:-9999px;display:block;z-index:1;}
.joint_star_rater ul:hover li.meta{display:none;}
.joint_star_rater li.current{background:url(../images/icons/stars.png) repeat-x 0 -40px}
.joint_star_rater li.average{background:url(../images/icons/stars.png) repeat-x 0 -20px}
.joint_star_rater li{height:20px;width:20px;position:absolute;text-indent:-9999px;z-index:3;}
.joint_star_rater li.star_2{left:20px;}
.joint_star_rater li.star_3{left:40px}
.joint_star_rater li.star_4{left:60px}
.joint_star_rater li.star_5{left:80px}
.joint_star_rater li.star_1:hover{width:20px}
.joint_star_rater li.star_2:hover{width:40px}
.joint_star_rater li.star_3:hover{width:60px}
.joint_star_rater li.star_4:hover{width:80px}
.joint_star_rater li.star_5:hover{width:100px}
.joint_star_rater li.star:hover{background:url(../images/icons/stars.png) repeat-x 0 -40px;z-index:2;left:0;}
.joint_star_rater input.submit{display:none;}
&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;The &lt;code&gt;inline-block&lt;/code&gt; value for &lt;code&gt;display&lt;/code&gt; is not supported very well.  I recently switched to Firefox 3 Beta and it renders as expected.  Firefox 2 has problems with it.  I may revise the CSS later to accommodate it.&lt;/p&gt;

&lt;h3&gt;The Javascript&lt;/h3&gt;

&lt;p&gt;The fundamental drawback to the design here, is that it really only works well with the Javascript on.  In fact, with the CSS on and Javascript off, this code will not work very well for the end user.  This too will be revised in the future.&lt;/p&gt;

&lt;p&gt;Our Javascript needs to do something very simple:
* extract the star value you clicked on
* send it to the server
* redraw the stars&lt;/p&gt;

&lt;p&gt;It's a very simple operation, but I honestly think other libraries have an advantage to YUI in this regard.&lt;sup id=&quot;#fnr_1&quot;&gt;&lt;a href=&quot;#fn_1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;  Here's some unobtrusive code I came up with:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;js&quot;&gt;
var MA = {}; // MA namespace
MA.e = YAHOO.util.Event;
MA.d = YAHOO.util.Dom;
MA.c = YAHOO.util.Connect;

MA.star_rater = function() {
    var e = YAHOO.util.Event;
    var d = YAHOO.util.Dom;

    return {
        init: function() {
            e.onDOMReady(this.setup,this, true)
        },

        setup: function() {
            e.on(d.get('doc4'),'click',this.handleClick,this,true);
        },

        handleClick: function(ev) {
            var target = e.getTarget(ev);
            if (d.hasClass(target, 'star')
            &amp;&amp; d.hasClass(target.parentNode.parentNode.parentNode.parentNode, 'joint_star_rater')) {
                this.rate(target);
            }
        },

        rate: function(el) {
            if (MA.is_authenticated('Please sign in before rating =)')) {

                root   = el.parentNode.parentNode.parentNode.parentNode;
                action      = el.parentNode.parentNode.parentNode.action;
                input       = MA.d.getFirstChildBy(el, function(d) {return (d.tagName == 'input'||d.tagName=='INPUT')});
                this.value  = input.value;
                postdata    = &quot;value=&quot;+this.value;

                handleSuccess = function(o) { root.innerHTML = o.responseText }
                callback = {
                    success:handleSuccess,
                }


                var request = MA.c.asyncRequest('POST', action, callback, postdata);
                // construct a connection object to this and use it to make a post
                // retrieve the post and then replace it with the original span
            }
        },

    }
}();

MA.star_rater.init();
&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;Note: I intentionally left out irrelevant pieces of code, like the function definition of &lt;code&gt;MA.is_authenticated()&lt;/code&gt;, this code isn't meant for cutting and pasting, it's meant for cutting-pasting and then some careful editing.&lt;/p&gt;

&lt;h3&gt;The callback view&lt;/h3&gt;

&lt;p&gt;The callback script is what you specify when you call &lt;code&gt;{\% star ... %}&lt;/code&gt;.  The view I use is as follows:&lt;/p&gt;

&lt;p&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
@login_required
def rate(request, slug):
    MyObject = get_my_object()
    value    = request['value']

    MyObject,rate(value);
    return render_to_response(&quot;rating.html&quot;, locals(), context_instance=RequestContext(request))

&lt;/textarea&gt;&lt;/p&gt;


&lt;p&gt;That code is oversimplified... you have to write your own logic as it applies to our site.  The &lt;code&gt;rating.html&lt;/code&gt; is simply the call to your star tag:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{\% load tags %}
{\% star 'mything' restaurant.current_rating restaurant.average_rating restaurant.get_rating_url 1 %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the &lt;code&gt;1&lt;/code&gt; at the end.  It's a flag to turn off the outer &lt;code&gt;span&lt;/code&gt; so we can just insert the guts back into the original &lt;code&gt;span&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;The star-rater is really a large problem that's hard to tackle in one sitting and quite frankly is not documented well anywhere.  The code I've provided is a shadow of the real code I'll be using, but hopefully it's enough to get you started.&lt;/p&gt;

&lt;p&gt;I definitely will update my production code to solve a few outstanding issues, as I mentioned above.  I'll try to update this tutorial at the same time.  If there are questions about the examples given, feel free to ask and I'll attempt to answer.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fn_1&quot;&gt;YUI has the &lt;code&gt;Selector&lt;/code&gt; and that might alleviate some problems, but jQuery has very nice selection capabilities.  In this future version I keep promising, I'll use the YUI &lt;code&gt;Selector&lt;/code&gt; &lt;a href=&quot;#fnr_1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>py vs php: stemming</title>
   <link href="http://davedash.com/2008/02/16/py-vs-php-stemming/"/>
   <updated>2008-02-16T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/16/py-vs-php-stemming</id>
   <content type="html">&lt;p&gt;I've been porting some PHP to python during SuperHappyDevHouse and was amazed at how little code I needed to write since python makes list manipulation a breeze.&lt;/p&gt;

&lt;p&gt;Today I was working on stemming (ala &lt;a href=&quot;http://tartarus.org/martin/PorterStemmer/&quot;&gt;Porter Stemming algorithm&lt;/a&gt;).  &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; uses stemming in the search engine to make queries:&lt;/p&gt;

&lt;p&gt;Stemming turns &lt;code&gt;hello everybody how are you guy's&lt;/code&gt; into a collection &lt;code&gt;'everybodi', 'gui', 'hello'&lt;/code&gt;.  To produce this in php I do the following:&lt;/p&gt;

&lt;!--more--&gt;




&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public static function stemPhrase($phrase)
    {
        // remove apostrophe's and periods
        $phrase = strtolower(str_replace(array('\'', '.'), null, $phrase));
        
        // split into words
        $words = str_word_count($phrase, 1);

        // ignore stop words
        $words = array_diff($words, STOP_WORDS_ARRAY);

        // stem words
        $stemmed_words = array();

        foreach ($words as $word)
        {
            $stemmed_words[] = PorterStemmer::stem($word, true);
        }

        return $stemmed_words;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;With some magic python:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
def stem_phrase(phrase):
    words = phrase.lower().replace('.','').replace(&quot;'&quot;,'').split()

    # ignore stop words
    words = list(set(words)-set(STOP_WORDS))

    p = PorterStemmer()
    
    return [p.stem(word,0,len(word)-1) for word in words]

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The magic here is list mappings.  Learning about them, they don't seem that great, but as soon as you start coding you stop using a lot of for loops.&lt;/p&gt;

&lt;p&gt;I'm sure my PHP can be cleaned up and reduced as well, but its fun exploiting the magic of languages.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>GeoDjango</title>
   <link href="http://davedash.com/2008/01/27/geodjango/"/>
   <updated>2008-01-27T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/27/geodjango</id>
   <content type="html">&lt;p&gt;[tags]geodjango, django, gis[/tags]&lt;/p&gt;

&lt;p&gt;I just discovered &lt;a href=&quot;http://code.djangoproject.com/wiki/GeoDjango&quot;&gt;GeoDjango&lt;/a&gt;.  The existence of this is awesome.&lt;/p&gt;

&lt;p&gt;The GIS layer of &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; is incredibly complicated, and if I do another project of that magnitude, it'll be nice to know that a framework exists to tackle problems.  It looks functional, but not quite documentation complete for usage, but if I endeavor to learn more about GIS, I'm going to dive-in.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>FBML and CSS background images</title>
   <link href="http://davedash.com/2007/08/11/fbml-and-css-background-images/"/>
   <updated>2007-08-11T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/08/11/fbml-and-css-background-images</id>
   <content type="html">&lt;p&gt;I've been tediously building out the new &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; app for Facebook and I ran into some problem when I tried to embed this CSS:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;css&quot;&gt;
input.openid_login {
            background: url(&quot;http://openid.net/login-bg.gif&quot;) no-repeat #fff;
   background-position: 0 50%;
          padding-left: 18px;
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The CSS worked fine, just now background image.  The fix is mentioned in &lt;a href=&quot;http://www.facebook.com/topic.php?uid=2205007948&amp;amp;topic=5140&amp;amp;post=50818&amp;amp;pwstdfy=0434dec4ce75745f05b7396611234b7c#post50818&quot;&gt;this post&lt;/a&gt; which is to repeat the &lt;code&gt;background&lt;/code&gt; statement:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;css&quot;&gt;
input.openid_login {
            background: url(&quot;http://openid.net/login-bg.gif&quot;) no-repeat #fff;
            background: url(&quot;http://openid.net/login-bg.gif&quot;) no-repeat #fff;
   background-position: 0 50%;
          padding-left: 18px;
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Voila, it works.  Hopefully this hack won't be necessary in later versions of the Platform.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>FBML and embedded CMS</title>
   <link href="http://davedash.com/2007/08/04/fbml-and-embedded-cms/"/>
   <updated>2007-08-04T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/08/04/fbml-and-embedded-cms</id>
   <content type="html">&lt;p&gt;[tags]fbml, css, reviewsby.us, partials, symfony, sfFacebookPlatformPlugin[/tags]&lt;/p&gt;

&lt;p&gt;One problem of going the FBML route&lt;sup id=&quot;#fbmlcss_fnr_1&quot;&gt;&lt;a href=&quot;#fbmlcss_fn_1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is CSS styles.  You can't link to external style sheets so you need to embed everything.&lt;/p&gt;

&lt;p&gt;I took the liberty of using a partial that contains all the useful CSS that I use in our app.  Now we can just embed it in our layout by doing:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php include_partial('sfFacebook/css');?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The following classes are useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.app_content&lt;/code&gt; is the div surrounding the main content of the page.  It gives the canvas some padding (actually it gives itself some margin as not to butt-up against the canvas.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.box&lt;/code&gt; this defines the classic facebook box with a dark blue border at the top and with headers that have the lighter blue background and dark blue text.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.box .header&lt;/code&gt; the header for the box described above.  Use an &lt;code&gt;h2&lt;/code&gt; for the title.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.box .content&lt;/code&gt; the main content section of a box, has a bit of padding.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fbmlcss_fn_1&quot;&gt;I'm not completely convinced that the
&lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; app for facebook should be using FBML.

&lt;a href=&quot;#fbmlcss_fnr_1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote  in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>FBML and embedded CMS</title>
   <link href="http://davedash.com/2007/08/04/fbml-and-embedded-cms/"/>
   <updated>2007-08-04T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/08/04/fbml-and-embedded-cms</id>
   <content type="html">&lt;p&gt;[tags]fbml, css, reviewsby.us, partials, symfony, sfFacebookPlatformPlugin[/tags]&lt;/p&gt;

&lt;p&gt;One problem of going the FBML route&lt;sup id=&quot;#fbmlcss_fnr_1&quot;&gt;&lt;a href=&quot;#fbmlcss_fn_1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is CSS styles.  You can't link to external style sheets so you need to embed everything.&lt;/p&gt;

&lt;p&gt;I took the liberty of using a partial that contains all the useful CSS that I use in our app.  Now we can just embed it in our layout by doing:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php include_partial('sfFacebook/css');?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The following classes are useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.app_content&lt;/code&gt; is the div surrounding the main content of the page.  It gives the canvas some padding (actually it gives itself some margin as not to butt-up against the canvas.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.box&lt;/code&gt; this defines the classic facebook box with a dark blue border at the top and with headers that have the lighter blue background and dark blue text.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.box .header&lt;/code&gt; the header for the box described above.  Use an &lt;code&gt;h2&lt;/code&gt; for the title.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.box .content&lt;/code&gt; the main content section of a box, has a bit of padding.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class=&quot;footnotes&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;fbmlcss_fn_1&quot;&gt;I'm not completely convinced that the
&lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; app for facebook should be using FBML.

&lt;a href=&quot;#fbmlcss_fnr_1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote  in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Facebook Markup Language: the Dashboard and Action links</title>
   <link href="http://davedash.com/2007/07/25/facebook-markup-language-the-dashboard-and-action-links/"/>
   <updated>2007-07-25T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/25/facebook-markup-language-the-dashboard-and-action-links</id>
   <content type="html">&lt;p&gt;[tags]facebook, fbml, apps, reviewsby.us, symfony, sfFacebookPlatformPlugin, plugins[/tags]&lt;/p&gt;

&lt;p&gt;Facebook has the concept of the dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://developers.facebook.com/images/fbml_dashboard.gif&quot; alt=&quot;dashboard&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In case the documentation isn't clear, these are where the top buttons for your app go.&lt;/p&gt;

&lt;p&gt;I created the FBMLHelper to help you write links from symfony to facebook.&lt;/p&gt;

&lt;p&gt;The dashboard itself is easy to create:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;fb:dashboard&amp;gt;
&amp;lt;/fb:dashboard&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The links are fairly simple, but if you use &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;...  we like helpers...&lt;/p&gt;

&lt;p&gt;So the &lt;code&gt;FBMLHelper&lt;/code&gt; has an &lt;code&gt;fb_action&lt;/code&gt; method which is similar to &lt;code&gt;link_to&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php echo fb_action('My Dining', '@homepage') ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Will actually render as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;http://apps.facebook.com/reviewsbyus/&quot;&amp;gt;My Dining&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Provided the &lt;code&gt;app_facebook_canvas_url&lt;/code&gt; is set to your canvas url (for example, our's is http://apps.facebook.com/reviewsbyus/).&lt;/p&gt;

&lt;p&gt;This helper takes care of routing and rewriting the URL to something that will work within Facebook's canvas.&lt;/p&gt;

&lt;p&gt;... so start writing some apps.  This plugin will be developed further and I'll try to publish tutorials whenever possible.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Facebook Markup Language: the Dashboard and Action links</title>
   <link href="http://davedash.com/2007/07/25/facebook-markup-language-the-dashboard-and-action-links/"/>
   <updated>2007-07-25T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/25/facebook-markup-language-the-dashboard-and-action-links</id>
   <content type="html">&lt;p&gt;[tags]facebook, fbml, apps, reviewsby.us, symfony, sfFacebookPlatformPlugin, plugins[/tags]&lt;/p&gt;

&lt;p&gt;Facebook has the concept of the dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://developers.facebook.com/images/fbml_dashboard.gif&quot; alt=&quot;dashboard&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In case the documentation isn't clear, these are where the top buttons for your app go.&lt;/p&gt;

&lt;p&gt;I created the FBMLHelper to help you write links from symfony to facebook.&lt;/p&gt;

&lt;p&gt;The dashboard itself is easy to create:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;fb:dashboard&amp;gt;
&amp;lt;/fb:dashboard&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The links are fairly simple, but if you use &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;...  we like helpers...&lt;/p&gt;

&lt;p&gt;So the &lt;code&gt;FBMLHelper&lt;/code&gt; has an &lt;code&gt;fb_action&lt;/code&gt; method which is similar to &lt;code&gt;link_to&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php echo fb_action('My Dining', '@homepage') ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Will actually render as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;http://apps.facebook.com/reviewsbyus/&quot;&amp;gt;My Dining&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Provided the &lt;code&gt;app_facebook_canvas_url&lt;/code&gt; is set to your canvas url (for example, our's is http://apps.facebook.com/reviewsbyus/).&lt;/p&gt;

&lt;p&gt;This helper takes care of routing and rewriting the URL to something that will work within Facebook's canvas.&lt;/p&gt;

&lt;p&gt;... so start writing some apps.  This plugin will be developed further and I'll try to publish tutorials whenever possible.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dynamically adjusting your page title in symfony</title>
   <link href="http://davedash.com/2007/07/18/dynamically-adjusting-your-page-title-in-symfony/"/>
   <updated>2007-07-18T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/18/dynamically-adjusting-your-page-title-in-symfony</id>
   <content type="html">&lt;p&gt;[tags]view, view.yml, symfony, reviewsby.us, title, seo[/tags]&lt;/p&gt;

&lt;p&gt;A lot of the content on &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsBy.us&lt;/a&gt; and other sites we make using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; have dynamic content.  We try to have our page titles reflect the content by prepending the name of the specific restaurant, document or menu item before the site name.&lt;/p&gt;

&lt;p&gt;To do this we use a method called &lt;code&gt;prependTitle&lt;/code&gt;.  I define this in a file called &lt;code&gt;myActions.class.php&lt;/code&gt; which almost all of my actions subclass in my projects.  This way I can enhance all the actions simply by adjusting the common ancestor, &lt;code&gt;myActions&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
      public function prependTitle($title)
      {
        $r = $this-&gt;getResponse();
        $d = sfConfig::get('app_title_delimiter', ' &amp;laquo; ');
        $t = sfConfig::get('app_title');
        $r-&gt;setTitle($title.$d.$t, false);
      }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The page title isn't stored anywhere, so we have to put it in &lt;code&gt;app.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app:
  title: reviewsby.us
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Voila!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dynamically adjusting your page title in symfony</title>
   <link href="http://davedash.com/2007/07/18/dynamically-adjusting-your-page-title-in-symfony/"/>
   <updated>2007-07-18T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/18/dynamically-adjusting-your-page-title-in-symfony</id>
   <content type="html">&lt;p&gt;[tags]view, view.yml, symfony, reviewsby.us, title, seo[/tags]&lt;/p&gt;

&lt;p&gt;A lot of the content on &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsBy.us&lt;/a&gt; and other sites we make using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; have dynamic content.  We try to have our page titles reflect the content by prepending the name of the specific restaurant, document or menu item before the site name.&lt;/p&gt;

&lt;p&gt;To do this we use a method called &lt;code&gt;prependTitle&lt;/code&gt;.  I define this in a file called &lt;code&gt;myActions.class.php&lt;/code&gt; which almost all of my actions subclass in my projects.  This way I can enhance all the actions simply by adjusting the common ancestor, &lt;code&gt;myActions&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
      public function prependTitle($title)
      {
        $r = $this-&gt;getResponse();
        $d = sfConfig::get('app_title_delimiter', ' &amp;laquo; ');
        $t = sfConfig::get('app_title');
        $r-&gt;setTitle($title.$d.$t, false);
      }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The page title isn't stored anywhere, so we have to put it in &lt;code&gt;app.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app:
  title: reviewsby.us
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Voila!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Caching REST with sfFunctionCache</title>
   <link href="http://davedash.com/2007/04/07/caching-rest-with-sffunctioncache/"/>
   <updated>2007-04-07T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/07/caching-rest-with-sffunctioncache</id>
   <content type="html">&lt;p&gt;[tags]geocoding, caching, REST, symfony, Cache_Lite, php, cache, sfFunctionCache[/tags]&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; we do a lot of geocoding.  To facilitate we use &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Yahoo! Geocoding API&lt;/a&gt;.  This helps us normalize data, obtain latitude and longitude, interpret location specific searches.&lt;/p&gt;

&lt;p&gt;These &lt;acronym title=&quot;REpresentational State Transfer&quot;&gt;REST&lt;/acronym&gt; queries happen a lot and will continue to happen, but this data that Yahoo! provides is fairly static.  We're basically querying a database of sorts.  So it makes sense that we should cache this data.&lt;/p&gt;

&lt;p&gt;We'll demonstrate how to cache these queries using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;'s &lt;a href=&quot;http://www.symfony-project.com/book/trunk/18-Performance#Caching%20the%20Result%20of%20a%20Function%20Call&quot;&gt;&lt;code&gt;sfFunctionCache&lt;/code&gt;&lt;/a&gt; class.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;I wrote a wrapper (I'll release it as a plugin if requested) for the &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Geocoding API&lt;/a&gt;, the bulk of the work (the &lt;acronym title=&quot;REpresentational State Transfer&quot;&gt;REST&lt;/acronym&gt; call) occurs in a function called &lt;code&gt;doQueryGIS&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
        public static function doQueryGIS($location)
        {
            $url               = sfConfig::get('app_yahoo_geocode');

            $query             = array();
            $query['appid']    = sfConfig::get('app_yahoo_app_id');
            $query['location'] = $location;
            $query['output']   = 'php';

            $url .= '?' . http_build_query($query,null,'&amp;');    
              $curl = curl_init($url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exe c($curl); 
              // note there should be no space between curl_exe and c($curl) 
              // wordpress is just dumb
        

            if ($response != 'Array') 
            {
                return unserialize($response);
            } 
            else 
            {
                throw new sfException('Yahoo! GeoCoder does not understand: &quot;'. $location . &quot;\&quot;\n&quot;);
            }
            return false;   
            
            
        }

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The call to this function is always wrapped with &lt;code&gt;queryGIS&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
      protected function queryGIS()
        {
            $function_cache_dir = sfConfig::get('sf_cache_dir').'/function';
            $cache = new sfFunctionCache($function_cache_dir);
            $this-&gt;rawData = $cache-&gt;call(array('YahooGeo','doQueryGIS'), $this-&gt;rawLocation);
            return $this-&gt;rawData;
            
        }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;This wrapper creates a &lt;code&gt;sfFunctionCache&lt;/code&gt; objet and calls the function and caches it for subsequent queries.&lt;/p&gt;

&lt;p&gt;What this means is once Yahoo! teaches &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; that India is located at (25.42&amp;deg;, 77.830002&amp;deg;) and that the precision is 'country' we remember it in the future.&lt;/p&gt;

&lt;p&gt;These features will be incorporated into future versions of &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Parsing a list of Key:Value pairs</title>
   <link href="http://davedash.com/2007/02/24/parsing-a-list-of-kv-pairs/"/>
   <updated>2007-02-24T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/02/24/parsing-a-list-of-kv-pairs</id>
   <content type="html">&lt;p&gt;[tags]best practices,php,openID[/tags]
I'm working on implementing &lt;a href=&quot;http://openid.net/&quot;&gt;openID&lt;/a&gt; for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; and for use in &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; apps.  One thing I was having trouble with was parsing key value pairs, which is one of the requirements to reading responses.  It's a fairly easy task, but &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; offers so many ways to do this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://openid.net/&quot;&gt;openID&lt;/a&gt; calls for the following &lt;a href=&quot;http://openid.net/specs/openid-authentication-1_1.html#anchor32&quot;&gt;Key-Value format&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Lines of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; some_key:some value&lt;/li&gt;
&lt;li&gt; There MUST NOT be a space before or after the colon.&lt;/li&gt;
&lt;li&gt; Newline characters MUST be Unix-style, just ASCII character 10 (&quot;\n&quot;).&lt;/li&gt;
&lt;li&gt; Newlines MUST BE at end of each line as well as between lines.&lt;/li&gt;
&lt;li&gt; MIME type is unspecified, but text/plain is RECOMMENDED.&lt;/li&gt;
&lt;li&gt; Character encoding MUST BE UTF-8.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here is my attempt at parsing something like this as efficiently and error free as possible:&lt;/p&gt;

&lt;!--more--&gt;


&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
function splitKV($response) 
{
    $r = array();
    preg_match_all('|^\s*([^:]+):([^:\n]+)[ ]*$|m', $kvs, $matches);
    for($i = 0; $i &lt; count($matches[0]); $i++) {
        $r[$matches[1][$i]] = $matches[2][$i];
    }
    return $r;
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;I wrote this function as I was writing the post... and it came out way faster than my previous implementations using &lt;code&gt;strtok&lt;/code&gt; or a combination of &lt;code&gt;explode&lt;/code&gt; and &lt;code&gt;trim&lt;/code&gt;.  Which is good to hear since I do use &lt;code&gt;preg_&lt;/code&gt; functions quite a bit in PHP and they definitely have their place.&lt;/p&gt;

&lt;p&gt;I'm curious if people have found a faster way of parsing through a string of Key-Value pairs.  I'll run it in a test harness and stand corrected if someone comes through with something faster ;).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OpenID</title>
   <link href="http://davedash.com/2006/10/03/openid/"/>
   <updated>2006-10-03T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/10/03/openid</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt; is a wonderful concept.  If I visit a web site, Acme Widgets, I only need to supply a &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; that belongs to me in order to log in.  The web site at that &lt;acronym title=&quot;URL&quot;&gt;URL&lt;/acronym&gt; will provide a place where I can authorize Acme Widgets to log me in with this id.&lt;/p&gt;

&lt;p&gt;The benefit of this type of identity system is now you don't need to create new username's and passwords for each site you'd like to use.  We initially began &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; with just &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt; for that reason.  Now we can target places like &lt;a href=&quot;http://livejournal.com/&quot;&gt;livejournal&lt;/a&gt; and make it exceptionally easy for their users to register with our site.&lt;/p&gt;

&lt;p&gt;As of this writing, and even more robust system is being developed, &lt;a href=&quot;http://whobar.org/&quot;&gt;whobar&lt;/a&gt;.  &lt;a href=&quot;http://whobar.org/&quot;&gt;Whobar&lt;/a&gt; supports multiple identification sites, not just &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt;.  Rather than walking through explaining how to do a sign-in system that integrates &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt;, I'll direct people to &lt;a href=&quot;http://whobar.org/&quot;&gt;whobar&lt;/a&gt;.  It is in the plans for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; to integrate &lt;a href=&quot;http://whobar.org/&quot;&gt;whobar&lt;/a&gt; and once that happens you can expect a shiny tutorial and/or plugin for symfony.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OpenID</title>
   <link href="http://davedash.com/2006/10/03/openid/"/>
   <updated>2006-10-03T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/10/03/openid</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt; is a wonderful concept.  If I visit a web site, Acme Widgets, I only need to supply a &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; that belongs to me in order to log in.  The web site at that &lt;acronym title=&quot;URL&quot;&gt;URL&lt;/acronym&gt; will provide a place where I can authorize Acme Widgets to log me in with this id.&lt;/p&gt;

&lt;p&gt;The benefit of this type of identity system is now you don't need to create new username's and passwords for each site you'd like to use.  We initially began &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; with just &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt; for that reason.  Now we can target places like &lt;a href=&quot;http://livejournal.com/&quot;&gt;livejournal&lt;/a&gt; and make it exceptionally easy for their users to register with our site.&lt;/p&gt;

&lt;p&gt;As of this writing, and even more robust system is being developed, &lt;a href=&quot;http://whobar.org/&quot;&gt;whobar&lt;/a&gt;.  &lt;a href=&quot;http://whobar.org/&quot;&gt;Whobar&lt;/a&gt; supports multiple identification sites, not just &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt;.  Rather than walking through explaining how to do a sign-in system that integrates &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt;, I'll direct people to &lt;a href=&quot;http://whobar.org/&quot;&gt;whobar&lt;/a&gt;.  It is in the plans for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; to integrate &lt;a href=&quot;http://whobar.org/&quot;&gt;whobar&lt;/a&gt; and once that happens you can expect a shiny tutorial and/or plugin for symfony.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coming soon to reviewsby.us</title>
   <link href="http://davedash.com/2006/10/02/coming-soon-to-reviewsbyus/"/>
   <updated>2006-10-02T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/10/02/coming-soon-to-reviewsbyus</id>
   <content type="html">&lt;p&gt;In August I took a break from &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; only to be plagued by spam.  In September, I relinquished portions of the project planning to my wife.  We haven't released anything publicly, yet, but there's a lot in development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I updated the development framework to &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; 1.0 alpha and took care of a whole slew of bugs.&lt;/li&gt;
&lt;li&gt;Katie and I came up with a &lt;a href=&quot;http://flickr.com/photos/davedash/251518309/&quot;&gt;wireframe&lt;/a&gt; that details some of the upcoming changes.&lt;/li&gt;
&lt;li&gt;I upgraded the user logic to take advantage of sfGuardUser, a user management plugin for symfony.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm in progress of writing location specific searches.  I'm slow to implement.  It seems that this month is far busier than I'd like, and I can rarely get in a block of enough time to just crank this out.  The problem with geographic-specific searches is mySQL supports those types of queries, but it's not as easy as I'd like.  Zend Search Lucene with some support from PHP, however, may yield some promising results.  As always, I'll share my findings in a forthcoming tutorial.&lt;/p&gt;

&lt;p&gt;Anyway, no visible updates on the actual site, since I didn't want to put alpha software on the live site.  I'm sure by next month symfony will be ready.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coming soon to reviewsby.us</title>
   <link href="http://davedash.com/2006/10/02/coming-soon-to-reviewsbyus/"/>
   <updated>2006-10-02T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/10/02/coming-soon-to-reviewsbyus</id>
   <content type="html">&lt;p&gt;In August I took a break from &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; only to be plagued by spam.  In September, I relinquished portions of the project planning to my wife.  We haven't released anything publicly, yet, but there's a lot in development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I updated the development framework to &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; 1.0 alpha and took care of a whole slew of bugs.&lt;/li&gt;
&lt;li&gt;Katie and I came up with a &lt;a href=&quot;http://flickr.com/photos/davedash/251518309/&quot;&gt;wireframe&lt;/a&gt; that details some of the upcoming changes.&lt;/li&gt;
&lt;li&gt;I upgraded the user logic to take advantage of sfGuardUser, a user management plugin for symfony.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm in progress of writing location specific searches.  I'm slow to implement.  It seems that this month is far busier than I'd like, and I can rarely get in a block of enough time to just crank this out.  The problem with geographic-specific searches is mySQL supports those types of queries, but it's not as easy as I'd like.  Zend Search Lucene with some support from PHP, however, may yield some promising results.  As always, I'll share my findings in a forthcoming tutorial.&lt;/p&gt;

&lt;p&gt;Anyway, no visible updates on the actual site, since I didn't want to put alpha software on the live site.  I'm sure by next month symfony will be ready.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cropping Images using DHTML (Prototype) and symfony</title>
   <link href="http://davedash.com/2006/09/16/cropping-images-using-dhtml-prototype-and-symfony/"/>
   <updated>2006-09-16T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/09/16/cropping-images-using-dhtml-prototype-and-symfony</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://demos.spindrop.us/image_cropper/&quot;&gt;&lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/screenshot.png&quot; style=&quot;float:left;margin-right:1em&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Like many of my tutorials, you don't &lt;em&gt;need&lt;/em&gt; &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, just &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt;.  However, I develop in &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and take advantage of the &lt;acronym title=&quot;Model Viewer Controller&quot;&gt;MVC&lt;/acronym&gt;-support that it offers.&lt;/p&gt;

&lt;p&gt;Years ago when I was working on a photo gallery for &lt;a href=&quot;http://davedash.com/&quot;&gt;davedash.com&lt;/a&gt; I got the art of making tumbnails down fairly well.  It was automated and didn't allow for specifying how the thumbnail should be made.  With dozens of photos (which was a lot back then), when would I find that kind of time.&lt;/p&gt;

&lt;p&gt;Flashback to today, for &lt;a href=&quot;http://workface.com/&quot;&gt;my company&lt;/a&gt;... we want users with avatars... but nothing too large.  Maybe a nice 80x80 picture.  Well the coolest &lt;acronym title=&quot;User Interface&quot;&gt;UI&lt;/acronym&gt; I've seen was Apple's Address Book which let you use this slider mechanism to crop a fixed sized image from a larger image.&lt;/p&gt;

&lt;p&gt;Here's a &lt;a href=&quot;http://demos.spindrop.us/image_cropper/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;Overview&lt;/h3&gt;

&lt;p&gt;The front-end &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; is based on code from &lt;a href=&quot;http://digg.com/&quot;&gt;digg&lt;/a&gt; which is based on the look and feel (as near as I can tell) from Apple.&lt;/p&gt;

&lt;p&gt;The &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; provides a clever visual way of telling the server how to chop the image.  The gist is this, sliding the image around and zooming in and out change a few form values that get passed to another script which uses this data to produce the image.&lt;/p&gt;

&lt;h3&gt;Frontend: What would you like to crop?&lt;/h3&gt;

&lt;p&gt;In this tutorial, we're going to be cropping an 80x80 avatar from an uploaded image.  The front-end requires the correct mix of Javascript, &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt;, &lt;acronym title=&quot;HypterText Markup Languag&quot;&gt;HTML&lt;/acronym&gt; and images.  The Javascript sets up the initial placements of the image and the controls.  The &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; presents some necessary styling.  The images makeup some of the controls.  The &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; glues everything together.&lt;/p&gt;

&lt;h4&gt;&lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt;&lt;/h4&gt;

&lt;p&gt;Let's work on our &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; first.  Since I used &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, I created a &lt;code&gt;crop&lt;/code&gt; action for a &lt;code&gt;userpics&lt;/code&gt; module.  So in our &lt;code&gt;cropSuccess.php&lt;/code&gt; template:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;ava&quot;&amp;gt;
    &amp;lt;?php echo form_tag(&quot;userpics/crop&quot;) ?&amp;gt;
        &amp;lt;div id=&quot;ava_img&quot;&amp;gt;
            &amp;lt;div id=&quot;ava_overlay&quot;&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;div id=&quot;ava_drager&quot;&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;img src=&quot;&amp;lt;?php echo $image ?&amp;gt;&quot; id=&quot;avatar&quot; /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div id=&quot;ava_slider&quot;&amp;gt;&amp;lt;div id=&quot;ava_handle&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_width&quot; name=&quot;width&quot; value=&quot;80&quot; /&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_x&quot; name=&quot;x&quot; value=&quot;100&quot; /&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_y&quot; name=&quot;y&quot; value=&quot;100&quot; /&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_image&quot; name=&quot;file&quot; value=&quot;&amp;lt;?php echo $image ?&amp;gt;&quot; /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;input type=&quot;submit&quot; name=&quot;submit&quot; id=&quot;ava_submit&quot; value=&quot;Crop&quot; style=&quot;width: auto; font-size: 105%; font-weight: bold; margin: 1em 0;&quot; /&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Right now a lot of this doesn't quite make sense.  If you attempt to render it, you will just see only the image.  As we add the corresponding &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; and images it will make some more sense.&lt;/p&gt;

&lt;h4&gt;&lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; and corresponding images&lt;/h4&gt;

&lt;p&gt;We'll go through each style individually and explain what purpose it serves in terms of the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#ava&lt;/code&gt; is our container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava {
    border: 1px solid gray;
     width: 200px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#ava_img&lt;/code&gt; is the area that contains our image.  Our window for editing this image 200x200 pixels.  If we drag out image out of bounds we just want the overflowing image to be clipped.  We also want our position to be relative so any child elements can be positioned absolutely with respect to &lt;code&gt;#ava_img&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_img {
    width: 200px;
    height: 200px;
    overflow: hidden;
    position: relative;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;div style=&quot;float:left;margin: 1em 1em 1em 0&quot;&gt;
    &lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/overlay.png&quot; alt=&quot;overlay&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;#ava_overlay&lt;/code&gt; is a window we use to see what exactly will be our avatar.  If it's in the small 80x80 window in the center of the image, then it's part of the avatar.  If it's in the fuzzy region, then it's getting cropped out.  This overlay of course needs to be positioned absolutely.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_overlay {
    width: 200px;
    height: 200px;
    position: absolute;
    top: 0px;
    left: 0px;
    background: url('/images/overlay.png');
    z-index: 50;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#ava_drager&lt;/code&gt; is probably the least intuitive element (Heck, I'm not even sure if I've even got it right).  In &lt;a href=&quot;http://demos.spindrop.us/image_cropper/&quot;&gt;our demo&lt;/a&gt; you're not actually dragging the image, because you can drag anywhere within the &lt;code&gt;#ava_img&lt;/code&gt; container and move the image around.  You're using dragging an invisible handle.  It's a 400x400 pixel square that can be dragged all over the container and thusly move the image as needed.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_drager {
    width: 400px;
    height: 400px;
    position: absolute;
    z-index: 100;
    color: #fff;
    cursor: move;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#avatar&lt;/code&gt; is our image, and since it will be moving all around the window, it requires absolute positioning.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#avatar {
    position: absolute;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;div style=&quot;float:left;margin: 1em 1em 1em 0&quot;&gt;
    &lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/slider_back.png&quot; alt=&quot;overlay&quot; /&gt;
&lt;/div&gt;


&lt;div style=&quot;float:left;margin-left: 1em 1em 1em 0;&quot;&gt;
    &lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/handle.png&quot; alt=&quot;overlay&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;#ava_slider&lt;/code&gt; and &lt;code&gt;#ava_handle&lt;/code&gt; are our slider components.  They should be self-explanatory.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_slider {
    width: 200px;
    height: 27px;
    background: #eee;
    position: relative;
    border-top: 1px solid gray; 
    background: url('/images/slider_back.png');
}
#ava_handle {
    width: 19px;
    height: 20px;
    background: blue;
    position: absolute;
    background: url('/images/handle.png');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Internet Explorer&lt;/h5&gt;

&lt;p&gt;&lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt; do not work so well in Internet Explorer, but there is a small trick, adding these components into a style sheet that only IE can read will make things work:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_overlay {
  background: none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/ui/cropper/overlay.png', sizingMethod='crop');
}

#ava_handle {
  background: none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/ui/cropper/handle.png', sizingMethod='crop');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;The Javascript&lt;/h4&gt;

&lt;p&gt;The Javascript is actually not as complicated as you'd expect thanks to the wonder of &lt;a href=&quot;http://prototype.conio.net/&quot;&gt;prototype&lt;/a&gt;.  This framework provides so much so easily.  You'll need to include &lt;a href=&quot;http://prototype.conio.net/&quot;&gt;prototype.js&lt;/a&gt; and &lt;a href=&quot;http://boring.youngpup.net/2001/domdrag/project&quot;&gt;dom-drag.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let's take a look.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; charset=&quot;utf-8&quot;&amp;gt;
// &amp;lt;![CDATA[
function setupAva() {
    if ($(&quot;avatar&quot;)) {
        var handle = $(&quot;ava_handle&quot;);
        var avatar = $(&quot;avatar&quot;);
        var drager = $(&quot;ava_drager&quot;);
        var slider = $(&quot;ava_slider&quot;);
        var ava_width = $(&quot;ava_width&quot;);
        var ava_x = $(&quot;ava_x&quot;);
        var ava_y = $(&quot;ava_y&quot;);
        // four numbers are minx, maxx, miny, maxy
        Drag.init(handle, null, 0, 134, 0, 0);
        Drag.init(drager, avatar, -100, 350, -100, 350);
        var start_w = avatar.width;
        var start_h = avatar.height;
        var ratio = (start_h / start_w);
        var new_h;
        var new_w;
        if (ratio &amp;gt; 1) {
            new_w = 80;
            new_h = (80*start_h)/start_w;
        } else {
            new_h = 80;
            new_w = (80*start_w)/start_h;
        }
        // these need to be set after we init
        avatar.style.top = '100px';
        avatar.style.left = '100px';
        avatar.style.width = new_w + 'px';
        avatar.style.height = new_h + 'px';
        avatar.style.margin = '-' + (new_h / 2) + 'px 0 0 -' + (new_w / 2) + 'px';
        handle.style.margin = '3px 0 0 20px';
        avatar.onDrag = function(x, y) {
            ava_x.value = x;
            ava_y.value = y;
        }
        handle.onDrag = function(x, y) {
            var n_width = (new_w + (x * 2));
            var n_height = (new_h + ((x * 2) * ratio));         
            avatar.style.width = n_width + 'px';
            avatar.style.height = n_height+ 'px';
            ava_width.value = n_width;  
            avatar.style.margin = '-' + (n_height / 2) + 'px 0 0 -' + (n_width / 2) + 'px';
        }
    }
}
Event.observe(window,'load',setupAva, false);
// ]]&amp;gt;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If this isn't exactly crystal clear, I can explain.  If you're new to &lt;a href=&quot;http://prototype.conio.net/&quot;&gt;prototype&lt;/a&gt;, &lt;code&gt;$()&lt;/code&gt; is the same as &lt;code&gt;doucment.getElementByID()&lt;/code&gt; (at least for our purposes).&lt;/p&gt;

&lt;p&gt;We need to initialize two draggable elements, one is our slider for zooming and the other is our avatar itself.  We initialize the draggers using &lt;code&gt;Drag.init()&lt;/code&gt;.  We specify what to drag, if another element should be used as a handle and then the range of motion in xy coordinates.  In the second call we use that &lt;code&gt;#dragger&lt;/code&gt; to move around the image in this manner.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Drag.init(handle, null, 0, 134, 0, 0);
Drag.init(drager, avatar, -100, 350, -100, 350);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We want to initialize the the size and placement of the avatar.  We do that using maths.  First we want it in our 80x80 pixel box.  So it should be roughly 80x80.  I've set the math up so that the &lt;em&gt;smallest&lt;/em&gt; side is 80 pixels (there's reasons for doing this the other way around).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    if (ratio &amp;gt; 1) {
        new_w = 80;
        new_h = (80*start_h)/start_w;
    } else {
        new_h = 80;
        new_w = (80*start_w)/start_h;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We then place the avatar element.  We initialize it to be in the center of the screen (&lt;code&gt;top: 100px;left:100px&lt;/code&gt;) and then nudge the image using margins.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    avatar.style.top = '100px';
    avatar.style.left = '100px';
    avatar.style.width = new_w + 'px';
    avatar.style.height = new_h + 'px';
    avatar.style.margin = '-' + (new_h / 2) + 'px 0 0 -' + (new_w / 2) + 'px';
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We also use margins to place the handle.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    handle.style.margin = '3px 0 0 20px';
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#ava_x&lt;/code&gt; and &lt;code&gt;#ava_y&lt;/code&gt; tell us where the center of the avatar is.  So when the avatar is moved we need to set these again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    avatar.onDrag = function(x, y) {
        ava_x.value = x;
        ava_y.value = y;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That was easy.  Slighly more complicated is the zoomer function.  We are basically adjusting the width and the height proportionately based on roughly where the slider is.  Note that we're still using that &lt;code&gt;ratio&lt;/code&gt; variable that we calculated earlier.  We basically take the new x-coordinate of the handle and allow our image to get just slightly larger than the &lt;code&gt;#ava_image&lt;/code&gt; container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    handle.onDrag = function(x, y) {
        var n_width = (new_w + (x * 2));
        var n_height = (new_h + ((x * 2) * ratio));         
        avatar.style.width = n_width + 'px';
        avatar.style.height = n_height+ 'px';
        ava_width.value = n_width;  
        avatar.style.margin = '-' + (n_height / 2) + 'px 0 0 -' + (n_width / 2) + 'px';
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We want to load initialize the slider right away when the page loads: &lt;code&gt;Event.observe(window,'load',setupAva, false);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Not terribly hard or complicated.  Once these elements are all in place you have a working functioning slider.  It returns the x and y coordinates of the center of the image with respect to our 200x200 pixel &lt;code&gt;#ava_image&lt;/code&gt;.  It also tells us the new width of our image.  We feed this information into a new script and out should pop a new image which matches &lt;em&gt;exactly&lt;/em&gt; what we see in our &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt;.&lt;/p&gt;

&lt;!--nextpage--&gt;


&lt;h3&gt;Processing the crop&lt;/h3&gt;

&lt;p&gt;Initially I was frustrated with the data that was being sent.  I knew the center of the image in relation to this 200x200 pixel canvas and its width... but what could I do with that.  Well I could just recreate what I saw in the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt;.  I needed to create a 200x200 pixel image first, place my original avatar resized (and resampled) at the precise coordinates and &lt;em&gt;then&lt;/em&gt; cut out the center most 80x80 pixels to become the final avatar image.&lt;/p&gt;

&lt;p&gt;If you note in our template above for &lt;code&gt;cropSuccess.php&lt;/code&gt; we submit our form back to the &lt;code&gt;crop&lt;/code&gt; action.  Let's look at the action:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function executeCrop()
{
    if ($this-&amp;gt;getRequestParameter('file')&amp;amp;&amp;amp;$this-&amp;gt;getRequestParameter('width')) {          // we are saving our cropped image
        // Load the original avatar into a GD image so we can manipulate it with GD
        $o_filename = $this-&amp;gt;getRequestParameter('file');  // we'll use this to find the file on our system
        $o_filename = sfConfig::get('sf_root_dir').'/web' . $o_filename;
        $o_im = @imagecreatetruecolor(80, 80) or die(&quot;Cannot Initialize new GD image stream&quot;);
        $o_imagetype = exif_imagetype($o_filename); // is this gif/jpeg/png

        // appropriately create the GD image
        switch ($o_imagetype) {
            case 1: // gif
                $o_im = imagecreatefromgif($o_filename);
                break;
            case 2: // jpeg
                $o_im = imagecreatefromjpeg($o_filename);   
                break;
            case 3: // png
                $o_im = imagecreatefrompng($o_filename);
                break;
        }

        // Let's create our canvas
        $im = @imagecreatetruecolor(200, 200) or die(&quot;Cannot Initialize new GD image stream&quot;);
        imagecolortransparent ( $im, 127 ); // set the transparency color to 127
        imagefilledrectangle( $im, 0, 0, 200, 200, 127 ); // fill the canvas with a transparent rectangle

        // let's get the new dimension for our image

        $new_width = $this-&amp;gt;getRequestParameter('width');
        $o_width = imageSX($o_im);
        $o_height = imageSY($o_im);

        $new_height = $o_height/$o_width * $new_width;

        // we place the image at the xy coordinate and then shift it so that the image is now centered at the xy coordinate
        $x = $this-&amp;gt;getRequestParameter('x') - $new_width/2;
        $y = $this-&amp;gt;getRequestParameter('y') - $new_height/2;

        // copy the original image resized and resampled onto the canvas
        imagecopyresampled($im,$o_im,$x,$y,0,0,$new_width,$new_height,$o_width,$o_height); 
        imagedestroy($o_im);

        // $final will be our final image, we will chop $im and take out the 80x80 center
        $final = @imagecreatetruecolor(80, 80) or die(&quot;Cannot Initialize new GD image stream&quot;);
        imagecolortransparent ( $final, 127 ); // maintain transparency

        //copy the center of our original image and store it here
        imagecopyresampled ( $final, $im, 0, 0, 60, 60, 80, 80, 80, 80 );
        imagedestroy($im);

        //save our new user pic
        $p = new Userpic();
        $p-&amp;gt;setUser($this-&amp;gt;getUser()-&amp;gt;getUser());
        $p-&amp;gt;setGD2($final);
        $p-&amp;gt;save();
        imagedestroy($final);
        $this-&amp;gt;userpic = $p;
        return &quot;Finished&quot;;
    }


    $this-&amp;gt;getResponse()-&amp;gt;addJavascript(&quot;dom-drag&quot;);
    $this-&amp;gt;getResponse()-&amp;gt;addJavascript('/sf/js/prototype/prototype');
    $this-&amp;gt;getResponse()-&amp;gt;addJavascript('/sf/js/prototype/effects');
    $this-&amp;gt;image = '/images/userpics/originals/' . $this-&amp;gt;getRequestParameter('file');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's doing exactly what the paragraph above explains when the image dimensions are given.  The code is well commented so it should be easy enough to follow.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.php.net/manual/en/ref.image.php&quot;&gt;GD image functions in PHP&lt;/a&gt; are fairly robust and can help you do a lot of tricks with image data.  Note the code to save the image, we'll cover it in detail soon.&lt;/p&gt;

&lt;h3&gt;The Model&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;$p = new Userpic();
$p-&amp;gt;setUser($this-&amp;gt;getUser()-&amp;gt;getUser());
$p-&amp;gt;setGD2($final);
$p-&amp;gt;save();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First some clarification the second line.  &lt;code&gt;myUser::getUser()&lt;/code&gt; gets the &lt;code&gt;User&lt;/code&gt; object associated with the currently logged in user.  The third line, however, is where the magic happens.  Before we look at it, let's have a quick look at our model:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;userpic:
 _attributes: { phpName: Userpic }
 id:
 user_id:
 image: blob
 thumb: blob
 created_at:
 updated_at:
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We have an &lt;code&gt;image&lt;/code&gt; attribute and a &lt;code&gt;thumb&lt;/code&gt; property to our &lt;code&gt;Userpic&lt;/code&gt; object.  This is where we store &lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt; versions of each icon and their 16x16 thumbnails respectively.  We do this in &lt;code&gt;Userpic::setGD2()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function setGD2($gd2_image)
{
    //convert to PNG
    ob_start();
    imagepng($gd2_image);
    $png = ob_get_clean();
    //save 16x16
    $gd2_tn = @imagecreatetruecolor(16, 16) or die(&quot;Cannot Initialize new GD image stream&quot;);
    imagealphablending( $gd2_tn, true );
    imagecolortransparent ( $gd2_tn, 127 );

    imagecopyresampled ( $gd2_tn, $gd2_image, 0, 0, 0, 0, 16, 16, 80, 80 );
    ob_start();
    imagepng($gd2_tn);
    $tn = ob_get_clean();

    $this-&amp;gt;setImage($png);
    $this-&amp;gt;setThumb($tn);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We capture the output of the full size &lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt;, then we scale it again and capture the output of the thumbnail and set them.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;When it comes to web apps, having a relatively simple &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; for people to resize images can go a long way in terms of adoption rate of avatars and custom user pictures by non technical users.&lt;/p&gt;

&lt;p&gt;Enjoy, and if you found this useful (or better implemented it) let me know.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using Zend Search Lucene in a symfony app</title>
   <link href="http://davedash.com/2006/08/25/using-zend-search-lucene-in-a-symfony-app/"/>
   <updated>2006-08-25T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/08/25/using-zend-search-lucene-in-a-symfony-app</id>
   <content type="html">&lt;p&gt;[tags]zend, search, lucene, zend search lucene, zsl, symfony,php[/tags]&lt;/p&gt;

&lt;p&gt;If you're like me you've probably followed the &lt;a href=&quot;http://symfony-project.com/askeet/21&quot;&gt;Askeet tutorial on Search&lt;/a&gt; in order to create a decent search engine for your web app.  It's fairly straight forward, but they hinted that when &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt; (&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;) is released, that might be the way to go.  Well we are in luck, &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt; is available, so let's just dive right in.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;If you aren't using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; have a look at &lt;a href=&quot;http://devzone.zend.com/node/view/id/91&quot; title=&quot;Roll Your Own Search Engine with Zend_Search_Lucene&quot;&gt;this article&lt;/a&gt; from the &lt;a href=&quot;http://devzone.zend.com/&quot;&gt;Zend Developer Zone&lt;/a&gt;.  It covers just enough to get you started.  If you are using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, just follow along and we'll get you where you need to go.&lt;/p&gt;

&lt;h3&gt;Obtaining Zend Search Lucene&lt;/h3&gt;

&lt;p&gt;First &lt;a href=&quot;http://framework.zend.com/download&quot; title=&quot;Zend Framework Download&quot;&gt;download&lt;/a&gt; the &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; (&lt;acronym title=&quot;Zend Developer Framework&quot;&gt;ZF&lt;/acronym&gt;).  The &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt;  is supposed to be fairly &quot;easy&quot; in terms of installation.  So let's put that to the test.  Open your &lt;a href=&quot;http://framework.zend.com/&quot;&gt;&lt;acronym title=&quot;Zend Developer Framework&quot;&gt;ZF&lt;/acronym&gt;&lt;/a&gt; archive.  Copy &lt;code&gt;Zend.php&lt;/code&gt; and &lt;code&gt;Zend/Search&lt;/code&gt; to your &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; project's library folder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cp Zend.php $SF_PROJECT/lib              
mkdir $SF_PROJECT/lib/Zend
cp -r Zend/Search $SF_PROJECT/lib/Zend
cp Zend/Exception.php $SF_PROJECT/lib/Zend                 
chmod -R a+r $SF_PROJECT/lib/Zend*
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Index Something&lt;/h3&gt;

&lt;p&gt;We'll deviate slightly from &lt;a href=&quot;http://spindrop.us/category/reviewsbyus&quot; title=&quot;ReviewsBy.Us category of Spindrop&quot;&gt;food themed&lt;/a&gt; tutorials and do something generic.  Let's try a user search where we can find a user by their name or email address.  It's fairly simple to accomplish, and hardly requires the use of &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt;, but by using &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt; we can easily extend it to do a full-text search of a user's profile or any other textual data.&lt;/p&gt;

&lt;p&gt;Each &quot;thing&quot; stored in the index is a &quot;document&quot; in &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;, specifically a &lt;code&gt;Zend_Search_Lucene_Document&lt;/code&gt;.  Each document then consists of several &quot;fields&quot; (&lt;code&gt;Zend_Search_Lucene_Field&lt;/code&gt; objects).  In our example, our document will be an individual user and the fields will be relevant attributes of the user (username, first name, last name, email, the text of their profile).&lt;/p&gt;

&lt;p&gt;We're going to write a general re-indexing tool.  Something that will index all users.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;userActions&lt;/code&gt; class let's add the following action:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeReindex()
    {
        require_once 'Zend/Search/Lucene.php';
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
        
        $users = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            $doc = new Zend_Search_Lucene_Document();
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('id', $user-&gt;getId()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $user-&gt;getUsername()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $user-&gt;getEmail()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $user-&gt;getFirstname()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $user-&gt;getLastname()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$user-&gt;getEmail()} {$user-&gt;getFirstname()} {$user-&gt;getLastname()} {$user-&gt;getUsername()}&quot;));
            $index-&gt;addDocument($doc);
        }
        
        $index-&gt;commit();
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The code should be fairly easy to follow.  First of all we're requiring the necessary libraries for Lucene.  The next line we are creating the index:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;app_search_user_index_file&lt;/code&gt; is a symfony configuration that you define in your &lt;code&gt;app.yml&lt;/code&gt;.  It defines which file you want to use for your index.  &lt;code&gt;/tmp/lucene.user.index&lt;/code&gt; works for our purposes.   The second parameter tells Lucene we are creating a new index.&lt;/p&gt;

&lt;p&gt;We then loop through all the users and for each user create a document.  For all the search relevant attributes that a user might have we add a field into the document.  Note the last field:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$user-&gt;getEmail()} {$user-&gt;getFirstname()} {$user-&gt;getLastname()} {$user-&gt;getUsername()}&quot;));
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;By default search is made for the &quot;contents&quot; field.  So in this example we want people to be able to type in someone's name, email, username without having to specify what field we're searching for.&lt;/p&gt;

&lt;h3&gt;Find those users&lt;/h3&gt;

&lt;p&gt;Finding the user's is equally as straight-forward.  We make a new action called &lt;code&gt;search&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeSearch()
    {
        require_once('Zend/Search/Lucene.php');
        $query = $this-&gt;getRequestParameter('q');
    
        $this-&gt;getResponse()-&gt;setTitle('Search for \'' . $query . '\' &amp;laquo; ' . sfConfig::get('app_title'), true);
    
        $hits = array();
    
        if ($query)
        {
            $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
            $hits = $index-&gt;find(strtolower($query));
        }
        $this-&gt;hits = $hits;
    }

The magic happens in our `if` statement:

    if ($query)
    {
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
        $hits = $index-&gt;find(strtolower($query));
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;If we have a query, open the &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;ZSL&lt;/a&gt; index (note that we only have one parameter here).  Run the &lt;code&gt;find&lt;/code&gt; method to find our query and store it to the &lt;code&gt;$hits&lt;/code&gt; array.  Note that our query was cleaned with &lt;code&gt;strtolower&lt;/code&gt;, since &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;ZSL&lt;/a&gt; is case sensitive.&lt;/p&gt;

&lt;p&gt;The template takes care of the rest:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    &lt;?php use_helper('Form');?&gt;
    &lt;?php echo form_tag('@search_users') ?&gt;
    &lt;?php echo input_tag('q'); ?&gt;
    &lt;?php echo submit_tag() ?&gt;
    &lt;/form&gt;
    &lt;?php foreach ($hits as $hit): ?&gt;
      &lt;?php echo $hit-&gt;score ?&gt;
      &lt;?php echo $hit-&gt;firstname ?&gt;
      &lt;?php echo $hit-&gt;lastname ?&gt;
      &lt;?php echo $hit-&gt;email ?&gt;
    &lt;?php endforeach ?&gt;
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Fairly simple... but it could use some cleaning up (enjoy).&lt;/p&gt;

&lt;h3&gt;What about new users?&lt;/h3&gt;

&lt;p&gt;Regularly reindexing might be nice in terms of having an optimized search index, but its lousy if you want to be able to search the network immediately when new people join on.  So why not automatically re-index each user every time they are created or everytime one of their indexed components is summoned?&lt;/p&gt;

&lt;p&gt;This should be fairly simple by adding to the &lt;code&gt;User&lt;/code&gt; class:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    var $reindex = false;
    public function setUsername ( $v )
    {
        parent::setUsername($v);
        $this-&gt;reindex = true;
    }
    public function setFirstname ( $v )
    {
        parent::setFirstname($v);
        $this-&gt;reindex = true;
    }
    public function setLastname ( $v )
    {
        parent::setLastname($v);
        $this-&gt;reindex = true;
    }
    public function setEmail ( $v )
    {
        parent::setEmail($v);
        $this-&gt;reindex = true;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We have an attribute called &lt;code&gt;$reindex&lt;/code&gt;.  When it is false we don't need to worry about indexes.  When something significant changes, like an update to your name or email address, then we set &lt;code&gt;$reindex&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.  Then when we save:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function save ($con = null)
    {
        parent::save($con);
        if ($this-&gt;reindex) {
            require_once 'Zend/Search/Lucene.php';
            $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
            // first find any references to this user and delete them
            $hits = $index-&gt;find('id:'. $this-&gt;getId());
            foreach ($hits AS $hit) {
                $index-&gt;delete($hit-&gt;id);
            }
        
            $doc = $this-&gt;generateZSLDocument();
            $index-&gt;addDocument($doc);
            $index-&gt;commit();
        }
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We're calling a new function called &lt;code&gt;generateZSLDocument&lt;/code&gt;.  It might look familiar:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function generateZSLDocument()
    {
    
        require_once 'Zend/Search/Lucene.php';
        $doc = new Zend_Search_Lucene_Document();
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('id', $this-&gt;getId()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $this-&gt;getUsername()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $this-&gt;getEmail()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$this-&gt;getEmail()} {$this-&gt;getFirstname()} {$this-&gt;getLastname()} {$this-&gt;getUsername()}&quot;));
        return $doc;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Now, whenever a user is updated, so is our index.  Additionally we can modify our reindex action:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeReindex()
    {
        require_once('Zend/Search/Lucene.php');
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
        
        $users = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            
            $index-&gt;addDocument($user-&gt;generateZSLDocument);
        }
        
        $index-&gt;commit();
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;That's a &lt;strong&gt;lot&lt;/strong&gt; easier to deal with.&lt;/p&gt;

&lt;h3&gt;...and beyond&lt;/h3&gt;

&lt;p&gt;Hope this article helps some of you jumpstart your &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; apps.  Really cool, easy to implement search is here.  We no longer have to stick with shoddy solutions like HT://Dig or spend time rolling our own full text search, as the &lt;a href=&quot;http://symfony-project.com/askeet/21&quot;&gt;symfony team diligently showed us we could&lt;/a&gt;.  But there is a lot more ground to cover.  Including optimization techniques and best practices.&lt;/p&gt;

&lt;p&gt;Let me know what you think, and if you use this in any of your apps.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Digg-style AJAX comment editing in PHP/symfony</title>
   <link href="http://davedash.com/2006/08/13/digg-style-ajax-comment-editing-in-phpsymfony/"/>
   <updated>2006-08-13T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/08/13/digg-style-ajax-comment-editing-in-phpsymfony</id>
   <content type="html">&lt;p&gt;&quot;&lt;a href=&quot;http://digg.com/&quot;&gt;Digg&lt;/a&gt;&quot;-style anything can be pretty slick.  The &lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt;-interactions on that site make it very fun to use.  It's styles have been copied everywhere, and are definitely worth copying.  The latest feature that had caught my eye was the ability to edit your comments for a set time after posting them.  Of course, it wasn't just the ability to edit comments, it was &lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt; too and it has a timer.&lt;/p&gt;

&lt;p&gt;This is totally &lt;a href=&quot;http://spindrop.us/2006/07/02/comment-editing-in-reviewsbyus/&quot; title=&quot;Comment Editing in reviewsBy.us&quot;&gt;something I could use on a restaurant review site&lt;/a&gt;.  So I started on this project.  It's pretty straight forward.&lt;!--more--&gt;  For all of your posted comments you check if the owner of them is viewing them within 3 minutes of posting the commen.  3 minutes is usually enough time to notice you made a typo, but if you disagree I'll leave it to you to figure out how to adjust the code.&lt;/p&gt;

&lt;p&gt;For example, I make a comment, realize I spelled something wrong and then I can click on my comment to edit it.  Of course using &lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt; means this all happens without having to reload the web page.  Therefore the edits are seemingly quick.  So let's add it to any web site.&lt;/p&gt;

&lt;h3&gt;In Place Forms&lt;/h3&gt;

&lt;p&gt;First and foremost, the ability to edit a comment means you have a form that you can use to edit and submit your changes.  But rather than deal with creating a boring un&lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt;y form, we'll enlist the help of &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculo.us&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, each comment is rendered using the following &lt;acronym title=&quot;HypterText Markup Language&quot;&gt;HTML&lt;/acronym&gt; and PHP:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;review_block&quot; id=&quot;comment_&amp;lt;?php echo $comment-&amp;gt;getId() ?&amp;gt;&quot;&amp;gt;  
    &amp;lt;p class=&quot;author&quot;&amp;gt;&amp;lt;?php echo link_to_user($comment-&amp;gt;getUser()) ?&amp;gt; - &amp;lt;?php echo $comment-&amp;gt;getCreatedAt('%d %B %Y') ?&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;div class=&quot;review_text&quot; id=&quot;review_text_&amp;lt;?php echo $comment-&amp;gt;getId()?&amp;gt;&quot;&amp;gt;&amp;lt;?php echo $comment-&amp;gt;getHtmlNote() ?&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that this &lt;code&gt;div&lt;/code&gt; and it's child &lt;code&gt;div&lt;/code&gt; have unique ids that we can refer back to (&lt;code&gt;comment_n&lt;/code&gt; and &lt;code&gt;review_text_n&lt;/code&gt; where &lt;code&gt;n&lt;/code&gt; is the id of the comment).  We can use this to interact with the &lt;acronym title=&quot;Document Object Model&quot;&gt;DOM&lt;/acronym&gt; via JavaScript.  What we do is for each comment, we check if it is owned by the current visitor &lt;em&gt;and&lt;/em&gt; if it's within our prescribed 3 minute window.  We can do that with some simple PHP:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php if ($comment-&amp;gt;getUser() &amp;amp;&amp;amp; $comment-&amp;gt;getUserId() == $sf_user-&amp;gt;getId() &amp;amp;&amp;amp; time() &amp;lt; 181 + $comment-&amp;gt;getCreatedAt(null) ): ?&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
    //&amp;lt;![CDATA[
        makeEditable('&amp;lt;?php echo $comment-&amp;gt;getId() ?&amp;gt;', &quot;&amp;lt;?php echo url_for($module . '/save?id=' . $comment-&amp;gt;getId()) ?&amp;gt;&quot;, &quot;&amp;lt;?php echo url_for('restaurantnote/show?id=' . $comment-&amp;gt;getId() . '&amp;amp;mode=raw') ?&amp;gt;&quot;, &amp;lt;?php echo 181-(time() - $comment-&amp;gt;getCreatedAt(null)) ?&amp;gt;);
    //]]&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;?php endif ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see we run the &lt;code&gt;makeEditable()&lt;/code&gt; function for each applicable comment.  As you can guess, &lt;code&gt;makeEditable()&lt;/code&gt; makes a comment editable.  For parameters it takes the comment's id (so it can refer to it in the &lt;acronym title=&quot;Document Object Model&quot;&gt;DOM&lt;/acronym&gt; and other background scripts).  It also takes as an argument the &quot;save&quot; &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; as well as a &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; from which it can load the raw comment.  The last argument is for the timer.&lt;/p&gt;

&lt;p&gt;Here is our function:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var editor;
var pe;
makeEditable = function(id, url, textUrl, time) {
    var div = $(&quot;review_text_&quot; + id);

    pe = new PeriodicalExecuter(function() { updateTime(id); }, 1);

    Element.addClassName($('comment_' + id), 'editable');
    new Insertion.Bottom(div, '&amp;lt;div class=&quot;edit_control&quot; id=&quot;edit_control_'+id+'&quot;&amp;gt;Edit Comment (&amp;lt;span id=&quot;time_'+id+'&quot;&amp;gt;'+time+' seconds&amp;lt;/span&amp;gt;)&amp;lt;/div&amp;gt;');

    editor = new Ajax.InPlaceEditor(div, url, { externalControl: 'edit_control_'+id, rows:6, okText: 'Save', cancelText: 'Cancel', 
    loadTextURL: textUrl, onComplete: function() { makeUneditable(id) } });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It does a couple things.  It runs a &lt;a href=&quot;http://www.sergiopereira.com/articles/prototype.js.html#Reference.PeriodicalExecuter&quot; title=&quot;PeriodicalExecutor&quot;&gt;PeriodicalExecuter&lt;/a&gt; to run the &lt;code&gt;updateTime&lt;/code&gt; function which updates our countdown timer.  It adds a &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; class to our comment &lt;code&gt;div&lt;/code&gt;.  It adds a control button to edit a comment.  Lastly it uses the &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculo.us&lt;/a&gt; &lt;a href=&quot;http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor&quot;&gt;Ajax.InPlaceEditor&lt;/a&gt; to do most of the magic.  The hard part is done.&lt;/p&gt;

&lt;h3&gt;Periodic Execution Timer&lt;/h3&gt;

&lt;p&gt;So the &lt;code&gt;updateTime&lt;/code&gt; function is reasonably simple.  It finds the time written out in the &lt;acronym title=&quot;Document Object Model&quot;&gt;DOM&lt;/acronym&gt; and decrements it by 1 second each second.  Once it hits zero seconds it disables itself and the ability to edit the block.  Let's take a look:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;updateTime = function(id) {
  var div = $(&quot;time_&quot;+id);
  if (div) {
    var time =  parseInt(div.innerHTML) - 1;
    div.innerHTML = time;
  }
  if (time &amp;lt; 1) {
    pe.stop();
    var editLink = $('edit_control_'+id);
    if (Element.visible(editLink)) {
      makeUneditable(id);
      editLink.parentNode.removeChild(editLink);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Call backs&lt;/h3&gt;

&lt;p&gt;We'll need a few call backs for the editor to work properly.  Since many content pieces are converted from something else to &lt;acronym title=&quot;HypterText Markup Language&quot;&gt;HTML&lt;/acronym&gt; and not directly written in &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; we'll need a callback that will load our text.  We'll also need a callback which will save our text (and then display it).&lt;/p&gt;

&lt;h4&gt;Load Text&lt;/h4&gt;

&lt;p&gt;The first call back we can see is referenced in the &lt;code&gt;makeEditable()&lt;/code&gt; function.  In our example it's:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;url_for('restaurantnote/show?id=' . $comment-&amp;gt;getId() . '&amp;amp;mode=raw');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which is a &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; route to the &lt;code&gt;restaurantnote&lt;/code&gt; module and the &lt;code&gt;show&lt;/code&gt; action with an argument &lt;code&gt;mode=raw&lt;/code&gt;.  Let's take a look at this action:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function executeShow ()
{
    $this-&amp;gt;restaurant_note = RestaurantNotePeer::retrieveByPk($this-&amp;gt;getRequestParameter('id'));
    $this-&amp;gt;forward404Unless($this-&amp;gt;restaurant_note instanceof RestaurantNote);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All this does is load the text (in our case the [markdown] formatting) into a template.&lt;/p&gt;

&lt;h4&gt;Save Text&lt;/h4&gt;

&lt;p&gt;The save text url in our example is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;url_for('restaurantnote/save?id=' . $comment-&amp;gt;getId());
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Using the &lt;a href=&quot;http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor&quot;&gt;Ajax.InPlaceEditor&lt;/a&gt; the value of the text-area is saved to the &lt;code&gt;value&lt;/code&gt; POST variable.  We consume it in our action like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function executeSave() 
{
    $note = RestaurantNotePeer::retrieveByPk($this-&amp;gt;getRequestParameter('id'));
    $this-&amp;gt;forward404Unless($note instanceof RestaurantNote);
    if ($note-&amp;gt;getUserId() == $this-&amp;gt;getUser()-&amp;gt;getId()) {
        $note-&amp;gt;setNote($this-&amp;gt;getRequestParameter('value'));
        $note-&amp;gt;save();
    }
    $this-&amp;gt;note = $note;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The note is also sent to a template that renders it, so when the save takes place, the edit form will be replaced with the new text.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;As you can see with some &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculo.us&lt;/a&gt; and &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, it's fairly easy to mimic &quot;Digg-style&quot; in-place comment editing.  You can test out a real example by visiting &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to %^&* yourself over with giant to-do lists</title>
   <link href="http://davedash.com/2006/08/05/how-to-yourself-over-with-giant-to-do-lists/"/>
   <updated>2006-08-05T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/08/05/how-to-yourself-over-with-giant-to-do-lists</id>
   <content type="html">&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;&lt;abbr title=&quot;Question&quot;&gt;Q&lt;/abbr&gt;: How do you eat an elephant?&lt;br/&gt;
&lt;abbr title=&quot;Answer&quot;&gt;A&lt;/abbr&gt;: One bite at a time.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;There's a problem with that adage.  Your body can only eat so much over a given time.  The elephant will probably spoil before you make a significant dent in it.&lt;/p&gt;

&lt;p&gt;This is no different than someone who tries to tackle an impossible to-do list.  Imagine, week after week, eating and eating at this elephant.  You're digesting quite a bit, but there's so much left.  You might just be inclined to give up and quit.  You'll feel like you accomplished nothing (of value), even though you really did quite a lot of work.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;No deadlines&lt;/h3&gt;

&lt;p&gt;The biggest mistake you can have with a to-do list is omitting a &quot;due date&quot; or deadline.  If you're to-do list looks like...:&lt;/p&gt;

&lt;blockquote&gt;&lt;ul&gt;
&lt;li&gt;Write Novel&lt;/li&gt;
&lt;li&gt;Shovel Snow&lt;/li&gt;
&lt;li&gt;Cut up elephant&lt;/li&gt;
&lt;li&gt;Train for triathlon&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;...then there's no concept of &lt;em&gt;when&lt;/em&gt; these items need to get done.  That means all these items are in the forefront at once.. or could be.  A good start or end date will properly prioritize these:&lt;/p&gt;

&lt;blockquote&gt;&lt;ul&gt;
&lt;li&gt;Shovel Snow - 11/1&lt;/li&gt;
&lt;li&gt;Cut up elephant - 11/23&lt;/li&gt;
&lt;li&gt;Write Novel - 12/1-3/1&lt;/li&gt;
&lt;li&gt;Train for triathlon - 4/1&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So now, the novel is going to be written in winter.  You're going to shovel snow on the first day of November.  You'll cut up the elephant just before Thanksgiving.  And you don't need to train for the triathlon until early spring.&lt;/p&gt;

&lt;p&gt;The deadlines serve multiple purposes.  The first is, you know roughly &lt;em&gt;when&lt;/em&gt; to do &lt;em&gt;what&lt;/em&gt;.  You shovel snow next week.  You write the novel in winter.  You train in spring.  The other important purpose is you know when to stop.  A &quot;deadline&quot; means that line item or project or whatever has a &quot;drop dead&quot; date.  That means you don't need to work on it any more.  For example, if you don't finish that novel, you don't need to let it linger.  Of course, if an item is really important and salvageable you can re assign a due date.&lt;/p&gt;

&lt;h3&gt;Too many items at once&lt;/h3&gt;

&lt;p&gt;Another related setup for disaster is having too many items at once.  If you go to a buffet with the intent of eating the proprietor out of house and home, the best bet isn't to shove everything you intend to eat all at once on a plate.  No, of course it'll look comical and you'll never finish.  At least, you won't finish it before a lot of the food gets cold.  Go up multiple times.&lt;/p&gt;

&lt;p&gt;If you're master to-do list is too large, it can be overwhelming.  Even if you get done with what's due now, just looking at items in the future will not only distract you, but take away from any sense of accomplishment you might get.  It might help to just start your day with a small to-do list written on a piece of paper, or maybe in another file.  Just look at that list until you finish.  You don't need to think or plan for the future except at the very beginning of your day, and maybe at the end of the day once you've finished today's items.&lt;/p&gt;

&lt;h3&gt;Too detailed&lt;/h3&gt;

&lt;p&gt;The last disaster area is details.  Too many details is when it takes more time to write down what you're going to do than to just do it.  It's at this point where you're writing things down just to cross them off... and as satisfying as it might be at the time, over the long run you're not accomplishing very much.&lt;/p&gt;

&lt;h3&gt;My somewhat working system&lt;/h3&gt;

&lt;p&gt;Currently my working system takes a lot of this into account.  I have one master todo list which I look at daily.  I only focus on the top elements that are due in the near term, unless I have nothing due soon.  Once I read that, I make a small list of things to do for the day or next block of hours.  Once I'm through those (or once it's a new day) I start over.&lt;/p&gt;

&lt;p&gt;The whole point of a to-do list is to have things that you'll do.  Don't make a large list if it's undoable.  It'll wear you down and prevent you from doing the few things you can do.  Start small and you can go far.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Usability for Nerds: Traversing URLs</title>
   <link href="http://davedash.com/2006/07/30/usability-for-nerds-traversing-urls/"/>
   <updated>2006-07-30T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/30/usability-for-nerds-traversing-urls</id>
   <content type="html">&lt;p&gt;A recent peeve of mine is &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s that you can't manually traverse.  Let me explain.  Let's say you visit &lt;code&gt;http://reviewsby.us/restaurant/cheesecake-factory&lt;/code&gt;.  You should manually be able to remove &lt;code&gt;cheesecake-factory&lt;/code&gt; and see an index page of restaurants at &lt;code&gt;http://reviewsby.us/restaurant/&lt;/code&gt;.  It makes logical sense for that page to be something that would enable you to find more restaurants.&lt;/p&gt;

&lt;p&gt;This is a throwback to static web sites, that consisted of directories and files.  If you accessed a file by its name, you would see the contents of the file (possibly filtered by the server).  If you accessed a directory, you would see an index of files.  In the world of web apps, however, &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s are made up.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;Web apps where these gaps are missing can be especially frustrating when you use your browser's history.  Often I reference the &lt;a href=&quot;http://www.symfony-project.com/api/symfony.html&quot;&gt;symfony api&lt;/a&gt;.  The &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; is &lt;code&gt;http://www.symfony-project.com/api/symfony.html&lt;/code&gt;.  All the other &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s in the &lt;acronym title=&quot;Application Programing Interface&quot;&gt;API&lt;/acronym&gt; listing begin with &lt;code&gt;http://www.symfony-project.com/api/&lt;/code&gt;, so you could assume that &lt;code&gt;http://www.symfony-project.com/api/&lt;/code&gt; is an index page.&lt;/p&gt;

&lt;div style=&quot;&quot;&gt;
&lt;img src=&quot;http://static.flickr.com/60/200898792_9de3c68eb1.jpg&quot; width=&quot;500&quot; height=&quot;101&quot; alt=&quot;URL autocomplete&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;It's not the index page (&lt;code&gt;http://symfony-project.com/api/symfony.html&lt;/code&gt; is).  If you googled for &lt;code&gt;sfConfig&lt;/code&gt; and got to &lt;code&gt;http://symfony-project.com/api/symfony/config/sfConfig.html&lt;/code&gt; and didn't feel like figuring out the navigation structure... or let's say at a later date you're using your browser's &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; auto-complete feature, you will get a 404 Error&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Web applications can try to mirror directory indexes with pretty &lt;acronym title=&quot;URL&quot;&gt;URL&lt;/acronym&gt;s, but often have a few gaps as every &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; (or level of &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;) needs to be designated in the app.  Its a good idea to fill those in as it is another way to navigate a web site.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;Not to pick on the wonderful symfony development team, but I truly do see this a lot on their site.  I'm sure they'll setup a redirect or something to fix this.  Or probably call me out on the fact that many of my sites violate this principle. &lt;a href=&quot;#fnr1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Do you like see-food?  See?  Food.</title>
   <link href="http://davedash.com/2006/07/29/do-you-like-see-food-see-food/"/>
   <updated>2006-07-29T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/29/do-you-like-see-food-see-food</id>
   <content type="html">&lt;div style=&quot;float:right&quot;&gt;
&lt;img src=&quot;http://reviewsby.us/menuitem_image/ed100b3be2d453a6574463bf63f3533d&quot; alt=&quot;Brownie Sundae&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;The nice thing about some restaurants is they pay extra special attention to presentation.  That way if the food isn't to your taste, maybe it is at least pleasing to your eyes.  We know that just a list of each dish and comments about said dish served at a restaurant isn't going to fly.  But if we spruced it up with member-submitted pictures... well... now our eyes have something to see other than text and rating stars.&lt;/p&gt;

&lt;p&gt;Now, who wants a &lt;a href=&quot;http://reviewsby.us/restaurant/adeles-custard-stand/menu/brownie-sundae&quot;&gt;sundae in a boat&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://reviewsby.us/&quot;&gt;Restaurant Reviews By Us&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to remove file extensions from URLs</title>
   <link href="http://davedash.com/2006/07/26/how-to-remove-file-extensions-from-urls/"/>
   <updated>2006-07-26T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/26/how-to-remove-file-extensions-from-urls</id>
   <content type="html">&lt;p&gt;&lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s should be treated as prime real estate.  Their primary purpose is to locate resources on the Internet.  So for a web developer, it makes sense to make things as user-friendly as possible.  One effort is to remove the extensions from files.  I don't mean things like &lt;code&gt;.html&lt;/code&gt; or &lt;code&gt;.pdf&lt;/code&gt;, as those give you an idea that you're reading a page of content or a &lt;acronym title=&quot;Portable Document Format&quot;&gt;PDF&lt;/acronym&gt; document.  I meant things like &lt;code&gt;.php&lt;/code&gt; or &lt;code&gt;.asp&lt;/code&gt; or &lt;code&gt;.pl&lt;/code&gt;, etc.  These are unnecessary items that just clutter the location bar on most browsers.&lt;/p&gt;

&lt;p&gt;There are two ways to do this.  The easy way which just looks at a request, if the requested filename doesn't exist, then it looks for the filename with a &lt;code&gt;.php&lt;/code&gt; (or &lt;code&gt;.asp&lt;/code&gt; or whatever) extension.  In an &lt;code&gt;.htaccess&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ $1.php [L,QSA]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now if you go to: &lt;code&gt;http://domain/about&lt;/code&gt; the server will interpret it as if you went to &lt;code&gt;http://domain/about.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Makes sense, but if we're already breaking the relation between &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; and filename, we may as well break it intelligently.  &lt;!--next page--&gt;Change that &lt;code&gt;.htaccess&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now if you go to: &lt;code&gt;http://domain/about&lt;/code&gt; the server will interpret it as if you went to &lt;code&gt;http://domain/index.php?q=about&lt;/code&gt;.  How is this useful?  Well now &lt;code&gt;index.php&lt;/code&gt; is &lt;em&gt;always&lt;/em&gt; called, so it can do anything common to all pages (which might be nothing) and do something based on the &lt;code&gt;$_GET['q']&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require_once('html_functions.php');
switch ($_GET['q']) {
    case 'about':
        echo myhead('about page');
        break;
    default:
        echo myhead('home page')
        break;
}
include($_GET['q'] . '.php');
echo myfoot();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We're loading a hypothetical library &lt;code&gt;html_functions.php&lt;/code&gt; which contains some simple functions (&lt;code&gt;myhead()&lt;/code&gt; and &lt;code&gt;myfoot()&lt;/code&gt;) that print out a simple header or footer for this site.  The &lt;code&gt;switch&lt;/code&gt; statement dynamically sets the &lt;code&gt;&amp;lt;head/&amp;gt;&lt;/code&gt;.  After the &lt;code&gt;switch&lt;/code&gt; we include a file based on the query string.  In our case it will still pull up &lt;code&gt;about.php&lt;/code&gt;.  Granted, this is not what I use personally, but it's the general idea behind how &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; works.&lt;/p&gt;

&lt;h3&gt;Why?&lt;/h3&gt;

&lt;p&gt;So why go through all this nonsense?  Extensions for the most part don't mean much to an end user.  Sure, &lt;code&gt;jpg&lt;/code&gt;, &lt;code&gt;png&lt;/code&gt; or &lt;code&gt;gif&lt;/code&gt; mean images and &lt;code&gt;html&lt;/code&gt; mean web page and &lt;code&gt;pdf&lt;/code&gt; means the file is a &lt;acronym title=&quot;Portable Document Format&quot;&gt;PDF&lt;/acronym&gt; document.  Dynamic pages, however usually come from &lt;code&gt;cgi&lt;/code&gt;, &lt;code&gt;php&lt;/code&gt;, &lt;code&gt;pl&lt;/code&gt; , &lt;code&gt;asp&lt;/code&gt;  pages or some other 3 or 4 letter extension that the server uses as a hint to determine how to parse, but the output is usually &lt;code&gt;html&lt;/code&gt;.  Servers are smart though.  They don't need hints, and the above code eliminates the need to reveal so explicitly just how a page is delivered.  Take our &lt;a href=&quot;http://reviewsby.us/&quot;&gt;restaurant review site&lt;/a&gt;, for the most part you can't tell that it's done in &lt;code&gt;php&lt;/code&gt;.  In fact all the &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s are &quot;clean&quot; and somewhat logical.  The benefit of having clean simple &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s is if we decide to change from PHP to &lt;acronym title=&quot;Active Server Pages&quot;&gt;ASP&lt;/acronym&gt; for example, we won't need to change our &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Inverting color codes in Textmate</title>
   <link href="http://davedash.com/2006/07/19/inverting-color-codes-in-textmate/"/>
   <updated>2006-07-19T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/19/inverting-color-codes-in-textmate</id>
   <content type="html">&lt;p&gt;I deal a lot with &lt;abbr title=&quot;hexadecimal&quot;&gt;hex&lt;/abbr&gt; color codes in &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt;.  One thing I occasionally need to do is invert color codes.  Normally this is something I could &lt;a href=&quot;http://www.google.com/search?q=inverse+colors+hex&quot;&gt;Google&lt;/a&gt; for, but I wanted a solution that didn't requiring constant reference.&lt;/p&gt;

&lt;p&gt;My favorite text editor, &lt;a href=&quot;http://macromates.com/&quot;&gt;Textmate&lt;/a&gt;, has a powerful automation system.  I can write mini scripts in whatever language suitable and take advantage of the power of Unix shell scripting to execute them.  &lt;a href=&quot;http://www.google.com/search?q=inverse+colors+hex&quot;&gt;From Googling&lt;/a&gt;, I learned enough ruby to learn that this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;printf(&quot;#%06X&quot;, 0xFFFFFF - STDIN.gets.gsub(/^#/,&quot;&quot;).hex )
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Will invert a hex color from standard input.  What it's doing is fairly simple.  It's using &lt;code&gt;printf&lt;/code&gt; to print a formatted string.  &lt;code&gt;%06X&lt;/code&gt; means it should zero-fill the resulting string with up to six zeros, the same way a hex color string is (e.g. we write 0000FF and not FF to mean 'blue').  The rest is simple subtraction.  We take &lt;code&gt;FFFFFF&lt;/code&gt;, the hex code for white, and subtract the input from &lt;code&gt;STDIN&lt;/code&gt; and arrive at the inverse of what we started.&lt;/p&gt;

&lt;!--next page--&gt;


&lt;p&gt;Now to add this to &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; we open &lt;code&gt;Automation|Run Command|Edit Commands...&lt;/code&gt; and create a new command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;echo $TM_SELECTED_TEXT |ruby -e 'printf(&quot;#%06X&quot;, 0xFFFFFF - STDIN.gets.gsub(/^#/,&quot;&quot;).hex )' 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This echo's whatever is selected and pipes it to the &lt;code&gt;ruby&lt;/code&gt; script.  We set the command to input selected text and replace the selected text on output.  Furthermore we can bind it to a keystroke.  I chose &lt;code&gt;Control-Alt-I&lt;/code&gt;, as it is unused on my system.&lt;/p&gt;

&lt;p&gt;Voila, I can highlight any hex code and instantly invert it.&lt;/p&gt;

&lt;p&gt;To keep this on one line, I neglected a few friendly features.  One is interpreting 3-digit &lt;abbr title=&quot;hexadecimal&quot;&gt;hex&lt;/abbr&gt; colors (e.g. #ccc), and the other is knowing whether or not to place the &lt;code&gt;#&lt;/code&gt; in the result.  If you can come up with an elegant solution, please post it below.  Otherwise I hope this helps.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Safari Fixes</title>
   <link href="http://davedash.com/2006/07/11/safari-fixes/"/>
   <updated>2006-07-11T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/11/safari-fixes</id>
   <content type="html">&lt;p&gt;Safari interprets &lt;code&gt;/* */&lt;/code&gt;s differently than FireFox or &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt;.  &lt;acronym title=&quot;FireFox&quot;&gt;FF&lt;/acronym&gt; and &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt; will ignore a unmatched &lt;code&gt;/*&lt;/code&gt; or &lt;code&gt;*/&lt;/code&gt;, whereas Safari will ignore parts of code if there's a lone &lt;code&gt;*/&lt;/code&gt;.  Once I found that out, I was able to get the list items that are used throughout the site to render properly.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Going international... kinda</title>
   <link href="http://davedash.com/2006/07/06/going-international-kinda/"/>
   <updated>2006-07-06T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/06/going-international-kinda</id>
   <content type="html">&lt;p&gt;Some of the first non-Minnesotan restaurants to show up were &lt;a href=&quot;http://reviewsby.us/restaurant/flying-dog&quot;&gt;Flying Dog&lt;/a&gt;, &lt;a href=&quot;http://reviewsby.us/restaurant/bangalore&quot;&gt;Bangalore&lt;/a&gt; and &lt;a href=&quot;http://reviewsby.us/restaurant/konstam&quot;&gt;Konstam&lt;/a&gt;... all of them outside the US.  Wasn't expecting that... but then again, I wasn't really surprised.&lt;/p&gt;

&lt;p&gt;I finally updated our location tables to account for different countries.  Currently it'll only plot what it can Geocode, and relies exclusively on Yahoo! for GeoCoding.&lt;/p&gt;

&lt;p&gt;I've only tested this with a Canadian restaurant.  Hopefully it'll work elsewhere soon.  If anybody plans on adding any non-US, non-Canadian restaurants, let me know if you can figure out how to GeoCode things properly.&lt;/p&gt;

&lt;p&gt;Also, I'm pleased as punch that the map on the homepage shows three states as having recent restaurants.  Rock on!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Going international... kinda</title>
   <link href="http://davedash.com/2006/07/06/going-international-kinda/"/>
   <updated>2006-07-06T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/06/going-international-kinda</id>
   <content type="html">&lt;p&gt;Some of the first non-Minnesotan restaurants to show up were &lt;a href=&quot;http://reviewsby.us/restaurant/flying-dog&quot;&gt;Flying Dog&lt;/a&gt;, &lt;a href=&quot;http://reviewsby.us/restaurant/bangalore&quot;&gt;Bangalore&lt;/a&gt; and &lt;a href=&quot;http://reviewsby.us/restaurant/konstam&quot;&gt;Konstam&lt;/a&gt;... all of them outside the US.  Wasn't expecting that... but then again, I wasn't really surprised.&lt;/p&gt;

&lt;p&gt;I finally updated our location tables to account for different countries.  Currently it'll only plot what it can Geocode, and relies exclusively on Yahoo! for GeoCoding.&lt;/p&gt;

&lt;p&gt;I've only tested this with a Canadian restaurant.  Hopefully it'll work elsewhere soon.  If anybody plans on adding any non-US, non-Canadian restaurants, let me know if you can figure out how to GeoCode things properly.&lt;/p&gt;

&lt;p&gt;Also, I'm pleased as punch that the map on the homepage shows three states as having recent restaurants.  Rock on!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dynamic Linking to Syndication Feeds with symfony</title>
   <link href="http://davedash.com/2006/07/04/dynamic-linking-to-syndication-feeds-with-symfony/"/>
   <updated>2006-07-04T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/04/dynamic-linking-to-syndication-feeds-with-symfony</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.symfony-project.com/content/book/page/syndication.html&quot; title=&quot;How to build a syndication feed&quot;&gt;Adding a statically-linked syndication feed&lt;/a&gt;, a feed that is the same no matter where on the site you are, is a cinch with &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, but what about dynamically linked syndication feeds?  Let's say we're building the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;latest and greatest Web 2.0 app&lt;/a&gt;, there's going to be hundreds of &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feeds, not just the most recent items.  We'll want the latest comments to a post, the favorite things of a website member and it all has to be feed enabled.  Sure, we can slap a link to the &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feed and call it a day, but let's go a step further and stick it in the &lt;code&gt;&amp;lt;head/&amp;gt;&lt;/code&gt; area as well.  That way when someone clicks on the &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; icon in their browser, or adds a web page to &lt;a href=&quot;http://bloglines.com&quot;&gt;Bloglines&lt;/a&gt; those extra feeds can be found.&lt;/p&gt;

&lt;!--break--&gt;


&lt;h2&gt;Expanding your head&lt;/h2&gt;

&lt;p&gt;A typical &lt;code&gt;layout.php&lt;/code&gt; for a symfony app will have a &lt;code&gt;&amp;lt;head/&amp;gt;&lt;/code&gt; section like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;?php echo include_http_metas() ?&amp;gt;
    &amp;lt;?php echo include_metas() ?&amp;gt;
    &amp;lt;?php echo include_title() ?&amp;gt;
    &amp;lt;?php echo auto_discovery_link_tag('rss', 'feed/latest')?&amp;gt;  
    &amp;lt;?php echo auto_discovery_link_tag('rss', '@feed_latest_georss', 
    array('title' =&amp;gt; 'Latest Restaurants\' Locations (GeoRSS)' ))?&amp;gt;     
    &amp;lt;?php echo include_feeds() ?&amp;gt;&amp;lt;!-- this is the custom feed includer --&amp;gt;
    &amp;lt;link rel=&quot;shortcut icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since this is in the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; &lt;code&gt;layout.php&lt;/code&gt;, the latest feed and the latest GeoRSS feed (which we developed in &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot; title=&quot;Easy Yahoo Maps! with symfony&quot;&gt;this article&lt;/a&gt;) will show up on every page.  So for example, if you use &lt;a href=&quot;http://www.mozilla.com/firefox/&quot; title=&quot;Firefox&quot;&gt;FireFox&lt;/a&gt;, you can subscribe to either link when you click on the orange feed icon (&lt;img src=&quot;http://feedicons.com/images/layout/feed-icon-12x12.gif&quot; title=&quot;Feed Available&quot; alt=&quot;feed&quot; /&gt;) in the &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; bar no matter where you are in the web-application.&lt;/p&gt;

&lt;p&gt;To expand this to allow for multiple feeds, we need to include &lt;code&gt;&amp;lt;?php echo include_feeds() ?&amp;gt;&lt;/code&gt; (before or after the &lt;code&gt;auto_discovery_link_tag&lt;/code&gt; calls makes the most sense).&lt;/p&gt;

&lt;h2&gt;Making the Feed Helper&lt;/h2&gt;

&lt;p&gt;Let's created a &lt;code&gt;FeedHelper.php&lt;/code&gt; to put the &lt;code&gt;include_feeds()&lt;/code&gt; function (don't forget to add &lt;code&gt;use_helper('Feed')&lt;/code&gt; to your &lt;code&gt;layout.php&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The function looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function include_feeds()
{
    $type = 'rss';
    $already_seen = array();
    foreach (sfContext::getInstance()-&amp;gt;getRequest()-&amp;gt;getAttributeHolder()-&amp;gt;getAll('helper/asset/auto/feed') as $files)
    {
        if (!is_array($files))
        {
            $files = array($files);
        }
        foreach ($files as $file)
        {
            if (isset($already_seen[$file])) continue;
            $already_seen[$file] = 1;
            echo tag('link', array('rel' =&amp;gt; 'alternate', 'type' =&amp;gt; 'application/'.$type.'+xml', 'title' =&amp;gt; ucfirst($type), 'href' =&amp;gt; url_for($file, true)));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The function is doing what the deprecated &lt;code&gt;include_javascripts&lt;/code&gt; and &lt;code&gt;include_stylesheets&lt;/code&gt; functions did, just with syndication feeds.  Also note, I stuck to just using &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feeds.  This function can no doubt be extended to Atom or other feed types, but for my purposes it was unnecessary&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;Dynamically setting the feeds&lt;/h2&gt;

&lt;p&gt;In the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; site, the menu items are tagged.  There's tags for &lt;a href=&quot;http://reviewsby.us/tag/chicken&quot;&gt;chicken&lt;/a&gt;, &lt;a href=&quot;http://reviewsby.us/tag/indian&quot;&gt;indian&lt;/a&gt; and &lt;a href=&quot;http://reviewsby.us/tag/bread&quot;&gt;bread&lt;/a&gt; for example.  Each of them are to have an associated GeoRSS feed as described in a &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot; title=&quot;Easy Yahoo Maps! with symfony&quot;&gt;previous tutorial&lt;/a&gt;.  I built our tagging system similar to &lt;a href=&quot;http://symfony-project.com/askeet&quot;&gt;Askeet&lt;/a&gt;.  So in our &lt;code&gt;tag&lt;/code&gt; module I created a function in the corresponding &lt;code&gt;actions.class.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function addFeed($feed)
{
    $this-&amp;gt;getRequest()-&amp;gt;setAttribute($feed, $feed, 'helper/asset/auto/feed');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This sets the attribute that &lt;code&gt;include_feeds()&lt;/code&gt; pulls from.  Here &lt;code&gt;$feed&lt;/code&gt; is simply the route to our feed.  So in our &lt;code&gt;executeShow()&lt;/code&gt; I just make a call to &lt;code&gt;$this-&amp;gt;addFeed('@feed_tag_georss?tag=' . $tag)&lt;/code&gt;.  We're done.&lt;/p&gt;

&lt;p&gt;We can now go to any of our tagged pages.  Let's try &lt;a href=&quot;http://reviewsby.us/tag/chicken&quot;&gt;chicken&lt;/a&gt; and see that we can subscribe to a &lt;a href=&quot;http://reviewsby.us/tag/chicken/geo.rss&quot;&gt;GeoRSS feed&lt;/a&gt; of restaurants serving &lt;a href=&quot;http://reviewsby.us/tag/chicken&quot;&gt;dishes tagged as chicken&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Slight problem.  The &lt;code&gt;title&lt;/code&gt; attribute of the generated &lt;code&gt;link&lt;/code&gt; tags are always &lt;code&gt;Rss&lt;/code&gt;.  That can be mildly unusable.&lt;/p&gt;

&lt;h2&gt;Throwing feed titles into the mix&lt;/h2&gt;

&lt;p&gt;Let's change our &lt;code&gt;addFeed()&lt;/code&gt; to allow for a second parameter, a title and have it store both the route and the title in the request attribute:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function addFeed($feed, $title = null)
{
    $feedArray = array('url' =&amp;gt; $feed, 'title' =&amp;gt; $title);
    $this-&amp;gt;getRequest()-&amp;gt;setAttribute($feed, $feedArray, 'helper/asset/auto/feed');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We'll also need to adapt the &lt;code&gt;include_feeds&lt;/code&gt; to appropriately accommodate associative arrays:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function include_feeds()
{
    $type = 'rss';
    $already_seen = array();
    foreach (sfContext::getInstance()-&amp;gt;getRequest()-&amp;gt;getAttributeHolder()-&amp;gt;getAll('helper/asset/auto/feed') as $feeds)
    {
        if (!is_array($feeds) || is_associative($feeds))
        {
            $feeds = array($feeds);
        }

        foreach ($feeds as $feed)
        {
            if (is_array($feed)) {
                $file = $feed['url'];
                $title = empty($feed['title']) ? $type : $feed['title'];
            } else {
                $file = $feed;
                $title = $type;
            }

            if (isset($already_seen[$file])) continue;

            $already_seen[$file] = 1;
            echo tag('link', array('rel' =&amp;gt; 'alternate', 'type' =&amp;gt; 'application/'.$type.'+xml', 'title' =&amp;gt; $title, 'href' =&amp;gt; url_for($file, true)));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note, there's a function &lt;code&gt;is_associative()&lt;/code&gt;.  It's a custom function that we can place in another helper:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function is_associative($array)
{
  if (!is_array($array) || empty($array)) return false;
  $keys = array_keys($array);
  return array_keys($keys) !== $keys;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's a clever way of determining if a function is an associative array or not.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It looks like our GeoRSS feeds are on all our tag pages.  Now we can take our favorite items labeled as &lt;a href=&quot;http://reviewsby.us/tag/indian&quot;&gt;Indian food&lt;/a&gt; and easily add the &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; to a service like &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; and have it &lt;a href=&quot;http://www.bloglines.com/sub/http://reviewsby.us/tag/indian&quot;&gt;keep us up to date on new Indian dishes&lt;/a&gt;.  This was simple, especially when much of the work was taken care of by &lt;a href=&quot;http://symfony-project.com/&quot;&gt;the framework&lt;/a&gt;.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;Most clients support &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; so unless there is a compelling need to use Atom or another format, then keeping it down to one choice is always your best bet. &lt;a href=&quot;#fnr1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Comment editing in reviewsBy.us</title>
   <link href="http://davedash.com/2006/07/02/comment-editing-in-reviewsbyus/"/>
   <updated>2006-07-02T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/02/comment-editing-in-reviewsbyus</id>
   <content type="html">&lt;p&gt;Something that's been heavily requested for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsBy.us&lt;/a&gt; is the ability to either preview or somehow edit comments after they are posted.  I took a cue from &lt;a href=&quot;http://digg.com&quot;&gt;digg&lt;/a&gt; and added some comment editing.&lt;/p&gt;

&lt;p&gt;Since the last time we updated, the &lt;acronym title=&quot;real simple syndication&quot;&gt;RSS&lt;/acronym&gt; feeds work better too thanks to &lt;a href=&quot;http://www.symfony-project.com/trac/ticket/585&quot;&gt;a patch&lt;/a&gt; to the &lt;a href=&quot;http://symfony-project.com&quot;&gt;symfony&lt;/a&gt;.  Also, the basis for a user profile is in place.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Working Backwards as a Way to Prioritize Tasks</title>
   <link href="http://davedash.com/2006/06/29/working-backwards-as-a-way-to-prioritize-tasks/"/>
   <updated>2006-06-29T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/06/29/working-backwards-as-a-way-to-prioritize-tasks</id>
   <content type="html">&lt;p&gt;I handle multiple projects at once, and sometimes I get lost and forget what needs to get done.  The &lt;a href=&quot;ktdd.org&quot; title=&quot;I got married!&quot;&gt;biggest day of my life&lt;/a&gt; was a few weeks ago.  I had a milesstone for a major project due for my 9-5 in this month.  &lt;em&gt;And&lt;/em&gt; I also have this blog, and a &lt;a href=&quot;http://reviewsby.us/&quot; title=&quot;Reviews By Us&quot;&gt;community-run restaurant review&lt;/a&gt; site to take care of.  So figuring out what needs to happen and by when is extremely beneficial.&lt;/p&gt;

&lt;p&gt;Sometimes a strategy that works to complete tasks is working backwards from your goal.  So the pre-requisite here is to have a well-defined goal.  Even though this is a programming blog, let's take my wedding for an example.  The end goal was that the ceremony and reception go off well enough that Katie and I are happy (it did).  That's pretty vague, so let's look at a single aspect of the wedding.  For example, one detail is having a wedding program.  500 wedding programs needed to be printed and delivered to &lt;a href=&quot;http://ktdd.org/temple&quot;&gt;the temple&lt;/a&gt; by Saturday morning (17 June 2006).  This is a more defined goal, or component of the overall goal.&lt;/p&gt;

&lt;p&gt;Let's break it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What we need is to &lt;strong&gt;Deliver the programs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;We'll need to &lt;strong&gt;fold the programs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;but before we can do that we need to &lt;strong&gt;print the programs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;we'll need to first get paper&lt;/li&gt;
&lt;li&gt;and actually we need to know what we're printing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Easy task, didn't need to be broken down much, but it serves our purposes.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;Prioritization - Get the Minimum Done First&lt;/h3&gt;

&lt;p&gt;Working backwards on it's own is good for giving you a list of what to do and even when to do it by.  But it doesn't necessarily tell you what &lt;em&gt;order&lt;/em&gt; to do it by.  In fact, order might not matter, but if you're in a slump and don't feel like doing anything, having a prescribed order can help.&lt;/p&gt;

&lt;p&gt;Now that we know the end goal, let's see if we can extract what the minimum is.  If we follow along with our wedding program example, the minimum would be a sheet of paper with a list of events.  No details, no participants, just events.  That's easy, a cut-and-paste from an email that our officiant sent us and we're done.&lt;/p&gt;

&lt;p&gt;By getting the minimum done, we at least have something.  Sure, we might want something fancier with an elegant design and good descriptions, but even if we botch that up for some reason we can rest easily knowing that we got our bases covered.&lt;/p&gt;

&lt;h3&gt;Finish Building an Onion&lt;/h3&gt;

&lt;p&gt;Finishing out the wedding program is a snap.  We need to make some estimates first.  We can improve on the program as much as we wanted, but we needed to stop by Thursday so we'd have enough time to print and fold them.&lt;/p&gt;

&lt;p&gt;Timeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delivery:   half-hour

&lt;ul&gt;
&lt;li&gt;Folding:    a couple days (it took only a few hours, but we can only print/fold so much at once)

&lt;ul&gt;
&lt;li&gt;Print:  a couple days (see above)

&lt;ul&gt;
&lt;li&gt;get paper:  :15 minutes&lt;/li&gt;
&lt;li&gt;making the program better:  remaining time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So in the remaining time, we came up with small revisions (essentially our onion skin layers) to our minimum.  For example, finding descriptions of each event as a first revision.  Our next revision can be rewriting Sanskrit headings in Devanagari script.  Our final revision can be plopping this all in a nice layout design in Adobe Illustrator.&lt;/p&gt;

&lt;p&gt;All these steps are gravy.  We already had a product that was satisfactory, now we're moving from satisfactory to pretty good to excellent.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;So, no, this isn't just applicable to wedding activities, but it's a great way to prioritize tasks for any project.  When I develop new web sites, like &lt;a href=&quot;http://reviewsby.us/&quot; title=&quot;Reviews By Us&quot;&gt;reviewsby.us&lt;/a&gt;, I first try to figure out what my goal is: to a have a list of the things I like to eat at various restaurants.  All I need to do is build a simple on-line list.  Beyond that, I can add features, and web 2.0 fluff that'll make it more fun to use, but as long as I got the core, that's all I need to have up to have this site be useful to me.  Each additional step adds more value to myself and others.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Breaking IE 6 with links on PNG backgrounds</title>
   <link href="http://davedash.com/2006/06/27/breaking-ie-6-with-links-on-png-backgrounds/"/>
   <updated>2006-06-27T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/06/27/breaking-ie-6-with-links-on-png-backgrounds</id>
   <content type="html">&lt;p&gt;In &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt; there's a whole slew of troubles with &lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt;.  One such trouble is links or anchors will not work in &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt; if you have a &lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt; image that has gone through the Microsoft &lt;code&gt;[AlphaImageLoader]&lt;/code&gt;, which is the only known way to render &lt;acronym title=&quot;Portable Network Graphic&quot;&gt;PNG&lt;/acronym&gt;s in &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt;6.&lt;/p&gt;

&lt;p&gt;The solution, involves running the image filter on a separate element, and then positioning all the links within that element in a higher z-order.  This is explained in better detail in &lt;a href=&quot;http://www.satzansatz.de/cssd/tmp/alphatransparency.html&quot;&gt;Filter Flaws&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;I've been running into too many designers who have been sending me too many designs that require translucent layers, and alpha-transparencies (and they should be able to, even though the trend is more simple these days).  This of course subjects me to pretty much any strange quirk that &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt;6 can dish out with &lt;acronym title=&quot;Portable Network Graphic&quot;&gt;PNG&lt;/acronym&gt;s.&lt;/p&gt;

&lt;p&gt;What's frustrating is that &lt;a href=&quot;http://davedash.com/2001/10/16/png_and_internet_explorer/&quot;&gt;5 years ago&lt;/a&gt; I had &lt;acronym title=&quot;Portable Network Graphic&quot;&gt;PNG&lt;/acronym&gt;s problems.  This is the Internet... very few problems on the Internet last that long.  There's certainly been enough people complaining about this malfunction in &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt; for years.&lt;/p&gt;

&lt;p&gt;The problem with these problems is there is no &quot;right&quot; solution.  Using &lt;code&gt;[AlphaImageLoader]&lt;/code&gt; is a hack.  As we can see with laying links upon backgrounds that use the loader, it's prone to behavior we don't normally expect.  We shouldn't have to raise the links above an invisible layer, in order to click on them.&lt;/p&gt;

&lt;p&gt;Unfortunately as long as the majority of our users continue to use &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt; 6 (or older) we'll be stuck with this problem.  If we look at &lt;a href=&quot;http://www.christopherschmitt.com/2006/04/12/adoption-rate-of-internet-explorer-7/&quot;&gt;adoption rates&lt;/a&gt; it won't be until a year after Internet Explorer 7 is out until it's the top dog.  Even then, if Firefox is still at 10% marketshare, that leaves another 40% with &lt;acronym title=&quot;Internet Explorer&quot;&gt;IE&lt;/acronym&gt;6 (or less) and the less popular browsers.  We're still stuck with this problem for some time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Random Selections using Propel</title>
   <link href="http://davedash.com/2006/06/12/random-selections-using-propel/"/>
   <updated>2006-06-12T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/06/12/random-selections-using-propel</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://propel.phpdb.org/&quot;&gt;Propel&lt;/a&gt; is a handy way to deal with &lt;acronym title=&quot;Object-Relational Mapping&quot;&gt;ORM&lt;/acronym&gt;.   Rather than figuring out the correct &lt;acronym title=&quot;Structured Query Language&quot;&gt;SQL&lt;/acronym&gt; statement to select your elements you just use a -&lt;code&gt;Peer&lt;/code&gt; object to select it.&lt;/p&gt;

&lt;p&gt;The one drawback is there's no way of choosing an object at random.  You can select the first element of a result set, but not a random one without some changes to your -&lt;code&gt;Peer&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The quick and dirty fix that I did is to use custom &lt;acronym title=&quot;Structured Query Language&quot;&gt;SQL&lt;/acronym&gt; to populate a &lt;a href=&quot;http://propel.phpdb.org/&quot;&gt;propel&lt;/a&gt; object.  It's a rather suitable approach for more complicated selects.  So here's how we randomly select things:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$con = Propel::getConnection('propel');
$sql = 'SELECT %s.* FROM %s ORDER BY RAND()';
$sql = sprintf($sql,MyObjectPeer::TABLE_NAME,MyObjectPeer::TABLE_NAME);
$stmt = $con-&amp;gt;createStatement();
$rs = $stmt-&amp;gt;executeQuery($sql, ResultSet::FETCHMODE_NUM);
$objects =  MyObjectPeer::populateObjects($rs);
$object = $objects[0];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you know you're going to only use one object, &lt;code&gt;SELECT %s.* FROM %s ORDER BY RAND() LIMIT 1&lt;/code&gt; will work as well.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How do I... on a Mac?  A software solution guide for OS X</title>
   <link href="http://davedash.com/2006/06/09/how-do-i-on-a-mac-a-software-solution-guide-for-os-x/"/>
   <updated>2006-06-09T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/06/09/how-do-i-on-a-mac-a-software-solution-guide-for-os-x</id>
   <content type="html">&lt;p&gt;I've been doing web development as well as other &quot;computer stuff&quot; with Powerbooks for four years.  I'm currently in a happy place where I've got a nice set of tools with which I can use to accomplish many tasks.&lt;/p&gt;

&lt;p&gt;This guide was inspired by &lt;a href=&quot;http://www.econsultant.com/i-want-firefox-extension/index.html&quot;&gt;this Firefox Extension guide&lt;/a&gt;.  This guide covers how I tackle tasks in &lt;a href=&quot;http://apple.com/macosx/&quot;&gt;OS X&lt;/a&gt; and how I've seen others tackle tasks.  I plan to regularly update this piece (software guides can get stale fast) and will incorporate suggestions left in the comments.&lt;/p&gt;

&lt;p&gt;Hopefully people who are wondering about making the switch from Linux or whatever to &lt;a href=&quot;http://apple.com/macosx/&quot;&gt;OS X&lt;/a&gt; will find this guide as reassuring and helpful.  There's quite a bit more out there than what I have listed, but if I attempted to list every option, I'd never be able to finish this guide.&lt;/p&gt;

&lt;!--more--&gt;


&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#internet&quot;&gt;Internet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;2/&quot;&gt;Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;3&quot;&gt;Multimedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;4&quot;&gt;Productivity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3 id=&quot;internet&quot;&gt;Internet: How do I...&lt;/h3&gt;


&lt;ul&gt;
&lt;li&gt;&lt;h4&gt;Surf the web?&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://www.caminobrowser.org/&quot;&gt;Camino&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;http://www.caminobrowser.org/&quot;&gt;Camino&lt;/a&gt; combines the Mozilla Gecko browser with Apple User Interface.  If &lt;a href=&quot;http://getfirefox.com/&quot;&gt;Firefox&lt;/a&gt;'s lack of &lt;acronym title=&quot;User Interface&quot;&gt;UI&lt;/acronym&gt; integration bothers you (e.g. lack of services menu support), try &lt;a href=&quot;http://www.caminobrowser.org/&quot;&gt;Camino&lt;/a&gt;.  Out of the box it blocks advertisements and popups.  It does lack the extensibility that &lt;a href=&quot;http://getfirefox.com/&quot;&gt;Firefox&lt;/a&gt; has, but otherwise is a worthy browser.  It's fast and it gets the job done.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://getfirefox.com/&quot;&gt;Firefox&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;I used to prefer &lt;a href=&quot;http://getfirefox.com/&quot;&gt;Firefox&lt;/a&gt;, despite its shortcomings.  There are &lt;a href=&quot;http://www.econsultant.com/i-want-firefox-extension/index.html&quot;&gt;so many extensions&lt;/a&gt; available that no other browser comes close.  When I'm developing websites this &lt;strong&gt;must&lt;/strong&gt; be open with &lt;a href=&quot;https://addons.mozilla.org/firefox/1843/&quot;&gt;Firebug&lt;/a&gt; and the &lt;a href=&quot;http://chrispederick.com/work/webdeveloper/&quot;&gt;Web Developer Extension&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://www.apple.com/macosx/features/safari/&quot;&gt;Safari&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;This is a &lt;a href=&quot;http://www.apple.com/&quot;&gt;Apple&lt;/a&gt;'s very own browser based on &lt;a href=&quot;http://www.konqueror.org/features/browser.php&quot;&gt;KHTML&lt;/a&gt; (&lt;a href=&quot;http://www.konqueror.org/&quot;&gt;Konqueror&lt;/a&gt;, etc).  I don't use this myself, but I do use other &lt;a href=&quot;http://webkit.opendarwin.org/&quot;&gt;Apple WebKit&lt;/a&gt; based applications.  It is elegant and integrates well with the rest of the OS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Check my Email&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;Gmail&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;https://mail.google.com/mail&quot;&gt;Gmail&lt;/a&gt; might be a web-app, but it works rather well as an email solution.  It can even integrate into &lt;a href=&quot;http://apple.com/macosx/&quot;&gt;OS X&lt;/a&gt; with the &lt;a href=&quot;http://mail.google.com/mail/help/notifier/&quot;&gt;GMail Notifier&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;Mail.app&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;http://www.apple.com/&quot;&gt;Apple&lt;/a&gt;'s built in Mail.app is a good choice if web-mail isn't an option.  It integrates well with the Address Book and even iChat.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Upload my Files&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.panic.com/transmit/&quot;&gt;Transmit&lt;/a&gt; does &lt;acronym title=&quot;File Transfer Protocol&quot;&gt;FTP&lt;/acronym&gt; and &lt;acronym title=&quot;Secure File Transfer Protocol&quot;&gt;SFTP&lt;/acronym&gt;.  It allows you to edit files &quot;remotely.&quot;  This is a huge boon for all those tiny style sheet changes you might need to update. It is trial ware, but it is a great &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; for something I have to do everyday.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Read blogs&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot; title=&quot;An ASPX page for an Apple product&quot;&gt;NetNewsWire&lt;/a&gt; lets you categorize and browse your feeds.  With the &lt;em&gt;Pro&lt;/em&gt; version, you can specify a lot more details.  There's per-category and per-feed customization for period of downloads.  &lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot; title=&quot;An ASPX page for an Apple product&quot;&gt;NNW&lt;/a&gt; also synchronizes with the &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; web based news reader.  This allows you to read your feeds using &lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot; title=&quot;An ASPX page for an Apple product&quot;&gt;NetNewsWire&lt;/a&gt; go to work, or a friends house, and read them at &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt;.  All my feeds are added to &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; except for some testing ones and some private feeds from &lt;a href=&quot;http://livejournal.com/&quot;&gt;LiveJournal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See &lt;em&gt;&lt;a href=&quot;/2006/06/08/add-to-bloglines-from-netnewswire&quot;&gt;Add to Bloglines from NetNewsWire&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Update my &lt;a href=&quot;http://livejournal.com/&quot;&gt;LiveJournal&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://connectedflow.com/xjournal/&quot;&gt;Xjournal&lt;/a&gt; lets you edit entries, set privacy settings, edit your friends list and do a whole lot of whatever else you want to do on &lt;a href=&quot;http://livejournal.com/&quot;&gt;LiveJournal&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Chat&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://apple.com/ichat&quot;&gt;ichat AV&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;http://apple.com/ichat&quot;&gt;ichat&lt;/a&gt; will probably serve most peoples needs.  It supports &lt;acronym title=&quot;AOL Instant Messenger&quot;&gt;AIM&lt;/acronym&gt; and Jabber (e.g. &lt;abbr title=&quot;Google Talk&quot;&gt;GTalk&lt;/abbr&gt;).  It also does video and audio chat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://adiumx.com/&quot;&gt;Adium&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;Unfortunately, my friends and contacts can't agree on &lt;strike&gt;which &lt;acronym title=&quot;Instant Messaage&quot;&gt;IM&lt;/acronym&gt; service to use&lt;/strike&gt; &lt;abbr title=&quot;Google Talk&quot;&gt;GTalk&lt;/abbr&gt;.  So I use &lt;a href=&quot;http://gaim.sf.net/&quot;&gt;Gaim&lt;/a&gt;-based &lt;a href=&quot;http://adiumx.com/&quot;&gt;Adium&lt;/a&gt;.  &lt;a href=&quot;http://adiumx.com/&quot;&gt;Adium&lt;/a&gt; does pretty much all the major protocols, it's open-source and has a lot of plugins.  What's not to love?  It's even being included in the &lt;a href=&quot;http://trac.adiumx.com/wiki/SummerOfCode&quot;&gt;Summer of Code 2006&lt;/a&gt; so that it can be more integrated with &lt;abbr title=&quot;Google Talk&quot;&gt;GTalk&lt;/abbr&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;[http://skype.net/]&quot;&gt;Skype&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;&lt;div class=&quot;photo screenshot_thumb right&quot;&gt;&lt;a href=&quot;http://www.flickr.com/photos/davedash/85269051/&quot; title=&quot;Photo Sharing&quot;&gt;&lt;img src=&quot;http://static.flickr.com/37/85269051_1194096479_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Skype&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;[http://skype.net/]&quot;&gt;Skype&lt;/a&gt; was a life saver when we had broadband in our Hotel in India this January (2006).  Free voice chat within the &lt;acronym title=&quot;United States&quot;&gt;US&lt;/acronym&gt; and plenty of accessory phones to go along with it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Steal movies and music from starving artists and industry associations&lt;/h4&gt;

&lt;p&gt;Trying to find that new &lt;a href=&quot;http://www.jonathancoulton.com/&quot;&gt;Jonathan Coulton&lt;/a&gt; song?  &lt;a href=&quot;http://acquisitionx.com/&quot;&gt;Acquisition&lt;/a&gt; might be able to help you or maybe even some of it's &lt;a href=&quot;http://acqlite.sourceforge.net/&quot;&gt;free derivatives&lt;/a&gt;.  &lt;a href=&quot;http://acquisitionx.com/&quot;&gt;Acquisition&lt;/a&gt; keeps getting better with each version and allows you to filter out what you aren't looking for.  This is a feature that is lacking from much of the software anywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Collaboratively steal movies and TV shows&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.bittorrent.com/&quot;&gt;BitTorrent&lt;/a&gt; offers a unique method of distribution.  There are a number of &lt;a href=&quot;http://www.bittorrent.com/&quot;&gt;BitTorrent&lt;/a&gt; clients, including &lt;a href=&quot;http://acquisitionx.com/&quot;&gt;Acquisition&lt;/a&gt;.  However, due to the sheer number of plugins (I suggest trying some of the &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; plugins), your best bet might be &lt;a href=&quot;http://azureus.sourceforge.net/&quot;&gt;Azureus&lt;/a&gt;, a cross-platform client.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;!--nextpage--&gt;


&lt;h3 id=&quot;programming&quot;&gt;Programming: How do I...&lt;/h3&gt;


&lt;ul&gt;
&lt;li&gt;&lt;h4&gt;Keep my files under version control&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://subversion.tigris.org/&quot;&gt;Subversion&lt;/a&gt; is my version control software of choice and I prefer using a combination of the command-line client, available via &lt;a href=&quot;http://fink.sf.net/&quot;&gt;Fink&lt;/a&gt; (see below) and &lt;a href=&quot;http://www.lachoseinteractive.net/en/community/subversion/svnx/&quot;&gt;svnX&lt;/a&gt;.  &lt;a href=&quot;http://www.lachoseinteractive.net/en/community/subversion/svnx/&quot;&gt;svnX&lt;/a&gt; makes it easy to see at a glance just exactly what needs to be committed.  It also allows me to browse the repository and find just what I need.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Write code&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;TextMate&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;http://macromates.com/&quot; title=&quot;The Missing Editor for Mac OSX&quot;&gt;TextMate&lt;/a&gt; is an excellent editor for automating editing tasks.  With features like &quot;snippets,&quot; cutting and pasting boilerplate code is a thing of the past.  If it doesn't support the language you want, it's &lt;em&gt;easy&lt;/em&gt; to remedy that and write your own bundle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;Xcode&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;http://www.apple.com/macosx/features/xcode/&quot;&gt;Xcode&lt;/a&gt; is Apple's &lt;acronym title=&quot;Integrated Development Environment&quot;&gt;IDE&lt;/acronym&gt;.  If you're writing Objective-C or Java, &lt;a href=&quot;http://www.apple.com/macosx/features/xcode/&quot;&gt;Xcode&lt;/a&gt; might prove useful.  For writing Cocoa applications, the bulk of your work involves dragging connections and defining relations between elements.  The &quot;legwork&quot; of writing actual code, is assuaged with an editor that features what you'd expect: folding, auto-completion and syntax highlighting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Get my fix of Unix goodness&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;Fink&lt;/h5&gt;

&lt;p&gt;Before I bought my Mac, the first thing I looked for was something like &lt;a href=&quot;http://fink.sf.net/&quot;&gt;Fink&lt;/a&gt;.  &lt;a href=&quot;http://fink.sf.net/&quot;&gt;Fink&lt;/a&gt; uses debian packaging of Unix utilities for the Mac.  It's great.  There's an easy installer that puts the fink environment on your computer and then you've got debian style package management:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For that &lt;code&gt;svn&lt;/code&gt; utility, you'll want &lt;code&gt;sudo apt-get install svn&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://iterm.sf.net/&quot;&gt;iTerm&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;If you use the Unix shell &lt;strong&gt;a lot&lt;/strong&gt;, you'll want &lt;a href=&quot;http://iterm.sf.net/&quot;&gt;iterm&lt;/a&gt; for it's tabbed interface.  It also has a handy feature of bouncing in the dock if something has changed in a background tab.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;!--nextpage--&gt;


&lt;h3 id=&quot;multimedia&quot;&gt;Multimedia: How do I...&lt;/h3&gt;


&lt;p&gt;For the most part, Apple's &lt;a href=&quot;http://apple.com/ilife/&quot;&gt;iLife&lt;/a&gt; can take care of your multimedia needs.  Playing video is the exception (in this guide), since there are so many formats available, &lt;a href=&quot;http://apple.com/quicktime/&quot;&gt;Quicktime&lt;/a&gt; only can support a small subset of these.  For the non-Apple user, this section a useful overview of just what can be done fairly easily with a Mac.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h4&gt;Listen to Music, Burn a &lt;acronym title=&quot;Compact Disc&quot;&gt;CD&lt;/acronym&gt;, rip a &lt;acronym title=&quot;Compact Disc&quot;&gt;CD&lt;/acronym&gt;&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://apple.com/itunes/&quot;&gt;itunes&lt;/a&gt; does a good job of putting all the things that you might need related to your music collection, in one place.  The &lt;a href=&quot;http://apple.com/macosx/&quot;&gt;os x&lt;/a&gt; version is a lot less frustrating than the Windows version, so don't let your past experiences taint your perception of &lt;a href=&quot;http://apple.com/itunes/&quot;&gt;iTunes&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Organize my photos, burn them to a &lt;acronym title=&quot;Digital Versatile Disc&quot;&gt;DVD&lt;/acronym&gt;, make slideshows&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://apple.com/iphoto/&quot;&gt;iphoto&lt;/a&gt; is a giant beast, but it does do quite a bit.  It manages large collections of photos.  You can easily download them off your camera, and then categorize them as necessary.  Want to share them?  Burn it onto a &lt;acronym title=&quot;Digital Versatile Disc&quot;&gt;DVD&lt;/acronym&gt; and give it to your family.  Want to make a slideshow set to music from your &lt;a href=&quot;http://apple.com/itunes/&quot;&gt;iTunes&lt;/a&gt;?  That's not a problem either.&lt;br/&gt;
The entire &lt;a href=&quot;http://apple.com/ilife/&quot;&gt;iLife&lt;/a&gt; suite is fairly well integrated.  My family always wants us to bring our digital camera so we can put together a cheesy slideshow on &lt;acronym title=&quot;Digital Versatile Disc&quot;&gt;DVD&lt;/acronym&gt;.  Good thing it's easy.&lt;/p&gt;

&lt;p&gt;  If you have a lot of photos there are some tools like &lt;a href=&quot;http://homepage.mac.com/bwebster/iphotolibrarymanager.html&quot;&gt;iPhoto Library Manager&lt;/a&gt; that can make your life easier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Make a &lt;acronym title=&quot;Digital Versatile Disc&quot;&gt;DVD&lt;/acronym&gt; of my Videos and Photos&lt;/h4&gt;

&lt;p&gt;Create &lt;acronym title=&quot;Digital Versatile Disc&quot;&gt;DVD&lt;/acronym&gt; menus, order photo slideshows and movies around, add music.  You can do all of this with &lt;a href=&quot;http://apple.com/idvd&quot;&gt;iDVD&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Edit movies&lt;/h4&gt;

&lt;p&gt;If you want to actually edit home movies, &lt;a href=&quot;http://apple.com/imovie&quot;&gt;iMovie&lt;/a&gt; can help.  You can pull in photos to make slide shows, synchronize it with audio tracks, add in video from other sources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Watch video files&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://apple.com/quicktime/&quot;&gt;Quicktime&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;The obvious choice on a Mac is &lt;a href=&quot;http://apple.com/quicktime/&quot;&gt;Quicktime&lt;/a&gt;.  &lt;a href=&quot;http://apple.com/quicktime/&quot;&gt;Quicktime&lt;/a&gt; however lacks some of the codecs that are popularly circulating the internet.  These days you can find much of what you want on &lt;a href=&quot;http://www.youtube.com/&quot;&gt;YouTube&lt;/a&gt; or &lt;a href=&quot;http://video.google.com/&quot;&gt;Google Video&lt;/a&gt; and not have to worry too much of compatability issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;&lt;a href=&quot;http://www.flip4mac.com/&quot;&gt;Flip4Mac&lt;/a&gt;: Make Quicktime work&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;http://www.flip4mac.com/&quot;&gt;Flip4Mac&lt;/a&gt; allows you to play Windows Media Files using Quicktime.  All but a handful of &lt;code&gt;&amp;lt;acronym title=&quot;Windows Media Video&quot;&amp;gt;WMV&amp;lt;/acronym&amp;gt;&lt;/code&gt; files play with this installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;&lt;acronym title=&quot;VideoLan Client&quot;&gt;&lt;a href=&quot;http://videolan.org/vlc/&quot;&gt;VLC&lt;/a&gt;&lt;/acronym&gt;&lt;/h5&gt;

&lt;p&gt;&lt;acronym title=&quot;VideoLan Client&quot;&gt;&lt;a href=&quot;http://videolan.org/vlc/&quot;&gt;VLC&lt;/a&gt;&lt;/acronym&gt; is a cross-platform video client that can play just about anything that &lt;a href=&quot;http://apple.com/quicktime/&quot;&gt;Quicktime&lt;/a&gt; can't with the exception of a handful of certain &lt;code&gt;&amp;lt;acronym title=&quot;Windows Media Video&quot;&amp;gt;WMV&amp;lt;/acronym&amp;gt;&lt;/code&gt; files.  Chances are if you can't play it with either this or &lt;a href=&quot;http://apple.com/quicktime/&quot;&gt;Quicktime&lt;/a&gt;, it can't be played on the Mac.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;!--nextpage--&gt;


&lt;h3 id=&quot;productivity&quot;&gt;Productivity: How do I...&lt;/h3&gt;


&lt;ul&gt;
&lt;li&gt;&lt;h4&gt;Do more from my keyboard/automate things/find things faster&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, &lt;a href=&quot;http://spindrop.us/2006/05/28/quicksilver-textmate-crazy-delicious-development-environment&quot;&gt;as discussed previously&lt;/a&gt;, can speed mundane things up severely:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; is a the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; equivalent to the command line.  You can launch applications or files or perform any number of operations on those files or applications.  With its powerful collection of plugins you can have it do much more, for example you can take a music file and play it in iTunes within the iTunes party shuffle.  Or take an image file and have it submit to &lt;a href=&quot;http://flickr.com/&quot;&gt;flickr&lt;/a&gt; with a few simple keystrokes.  Initially, I couldn't get an idea of the application, other than a lot of people loved it.  Now, I'm barely using it to its potential and I love it.  Using a computer without it is quite a drag.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;If you need to launch things faster or do more things from the keyboard quickly, get this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Keep track of things to do and other lists&lt;/h4&gt;

&lt;p&gt;I didn't at first use &lt;a href=&quot;http://www.omnigroup.com/applications/omnioutliner/&quot;&gt;OmniOutliner&lt;/a&gt; because I didn't understand the point of an outlining software.  It makes outlines, so what?  After reading enough of &lt;a href=&quot;http://43folders.com/&quot;&gt;43Folders&lt;/a&gt;, however, I decided to check it out.  Now it's the &lt;em&gt;key to my productivity&lt;/em&gt;.  I keep task lists for several projects in &lt;a href=&quot;http://www.omnigroup.com/applications/omnioutliner/&quot;&gt;OmniOutliner&lt;/a&gt; and I actually &lt;em&gt;get them done&lt;/em&gt;.  Something about hierarchical groupings and the ability to add any number of columns to a task make this such a useful tool.  I've got todo lists with are checkboxes, descriptions, dates, notes, estimates, etc.  If I'm asked to bid on a project, it takes almost no time for me to drop it into &lt;a href=&quot;http://www.omnigroup.com/applications/omnioutliner/&quot;&gt;OmniOutliner&lt;/a&gt;, split it up into smaller chunks, and throw together an estimate.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;There's a lot of software I'm sure I missed.  But my intent here was to cover a healthy slew of applications that I used myself to get various tasks completed.  I'd like to hear about your favorites and what you think works best on your Mac.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Add to Bloglines from NetNewsWire</title>
   <link href="http://davedash.com/2006/06/08/add-to-bloglines-from-netnewswire/"/>
   <updated>2006-06-08T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/06/08/add-to-bloglines-from-netnewswire</id>
   <content type="html">&lt;p&gt;I use &lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt; in conjunction with &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; and often come across new feeds from within &lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt; that I'd like to add to my &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; (versus adding it directly to &lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; is my sandbox for new feeds until I deem them worthy enough to read in &lt;a href=&quot;http://www.newsgator.com/NGOLProduct.aspx?ProdID=NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt;.  So here's an AppleScript to feed (no pun intended) my &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; addiction:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;Applescript&quot;&gt;
    -- NetNewsWire to Bloglines
    tell application &quot;NetNewsWire&quot;
        set u to (URL of selectedHeadline)
    end tell
    tell application &quot;NetNewsWire&quot;
        activate
        open location &quot;http://bloglines.com/sub?submiturl=Subscribe&amp;url=&quot; &amp; u
    end tell
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;or save &lt;a href=&quot;applescript://com.apple.scripteditor?action=append&amp;amp;script=%2D%2D%20NetNewsWire%20to%20Bloglines%0D%0Dtell%20application%20%22NetNewsWire%22%0D%09set%20u%20to%20%28URL%20of%20selectedHeadline%29%0Dend%20tell%0D%0Dtell%20application%20%22NetNewsWire%22%0D%09activate%0D%09open%20location%20%22http%3A%2F%2Fbloglines%2Ecom%2Fsub%3Fsubmiturl%3DSubscribe%26url%3D%22%20%26%20u%0Dend%20tell%0D&quot;&gt;this apple script&lt;/a&gt; to &lt;code&gt;~/Library/Application\ Support/NetNewsWire/Scripts/&lt;/code&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Spindrop objectives</title>
   <link href="http://davedash.com/2006/06/05/spindrop-objectives/"/>
   <updated>2006-06-05T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/06/05/spindrop-objectives</id>
   <content type="html">&lt;p&gt;I had wanted to write about this later, but &lt;a href=&quot;http://www.problogger.net/archives/2006/06/05/blog-goals-group-writing-project/&quot;&gt;Darren at ProBlogger&lt;/a&gt; started this group writing project on &quot;Blog Goals.&quot;  So I am jumping in quickly.  This blog was &lt;a href=&quot;http://spindrop.us/purpose/&quot;&gt;created for two reasons&lt;/a&gt;, one, to document any technical things that I've learned, code samples, best practices, strategies, etc. as they pertain to web development and open source.  The second is to serve as a site to record updates to any of my projects.&lt;/p&gt;

&lt;p&gt;I'm still in my infancy for this blog, but I've seen a lot of little things that keep me optimistic.&lt;/p&gt;

&lt;p&gt;The objectives I have for &lt;a href=&quot;http://spindrop.us/&quot;&gt;Spindrop&lt;/a&gt; are both internal and external.  Internal goals are things that I can change myself.  For example, the style of the site, linking to other places, posting more content, changing the way ads are presented, etc.&lt;/p&gt;

&lt;p&gt;External goals depend on readers like you.  I can do my best to make this site be relevant to a lot of people, but I can't make people click on my site, comment, or any of that.  I can still make goals for them, and that will subconsciously get me to position myself better.&lt;/p&gt;

&lt;p&gt;They are both related.  If I achieve my internal objectives, I'm better suited to getting external objectives done.  If I get my external objectives done, it encourages my behavior of making the site better.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;What am I willing to do&lt;/h3&gt;

&lt;p&gt;My &quot;9-5&quot; is a fairly demanding job as a lead web developer for a health and wellness website.  On top of that, I'm &lt;a href=&quot;ktdd.org&quot;&gt;getting married this month&lt;/a&gt;, so I'm &lt;strong&gt;very&lt;/strong&gt; pressed for time when it comes to a side project like this.  Luckily I'm very well organized, and do wake up early and spend time writing.&lt;/p&gt;

&lt;h4&gt;Writing&lt;/h4&gt;

&lt;p&gt;In an OmniOutliner file, I keep a detailed list of what I'll be writing as well as other &quot;to-do list&quot; items for this site.  I've already taken into account that I'm not going to be pushing anything useful for the week or two surrounding my wedding.  Hopefully, I can still muster two or three articles this month.  Some of those articles are on software choices, strategies for using &lt;a href=&quot;http://propel.phpdb.org/&quot;&gt;propel&lt;/a&gt; and &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and general work habbits.&lt;/p&gt;

&lt;p&gt;By spending an hour (or more) a day on writing, I'm generally looking over things and making sure I did a halfway decent job.  I prefer not to have typos, spelling errors, etc, I prefer to have a readable article, and I prefer to throw in images, when useful.  Of course, I do this early in the morning, so there are mistakes.  But, I've noticed a lot of heavy hit articles on &lt;a href=&quot;http://del.icio.us/&quot;&gt;del.icio.us&lt;/a&gt; and &lt;a href=&quot;http://digg.com/&quot;&gt;digg&lt;/a&gt; have typos too.&lt;/p&gt;

&lt;h4&gt;Non-content changes&lt;/h4&gt;

&lt;p&gt;I'm willing to tweak ad placement, and practice all the other ad-voodoo that is involved with blogging.    I'm also willing to announce the site at appropriate moments, communicate with people via forums and other blogs.  I'm willing to listen and execute on others advice.  The best advice I've heard is to stick with it.&lt;/p&gt;

&lt;h3&gt;What can I get at the very least?&lt;/h3&gt;

&lt;p&gt;With all my efforts, at the very least, I know I'll have something to show for.  For one, I tend to do things over and over again.  I'm a web developer, and the bulk of my blog is about how to do things I've done before.  So if I have to make &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot;&gt;another map&lt;/a&gt; or &lt;a href=&quot;http://spindrop.us/2006/05/19/migrating-from-drupal-47-to-wordpress&quot;&gt;migrate a blog&lt;/a&gt;, I can see how I did it before.  Now, anything else this blog achieves is gravy.&lt;/p&gt;

&lt;h3&gt;External goals&lt;/h3&gt;

&lt;p&gt;Some of my hopes for the site are out of my control.  I want what a lot of other blogs want: traffic, revenue and community.  Additionally I want traffic to be sent to my other sites, like the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; site so that they can share the success.&lt;/p&gt;

&lt;h4&gt;Traffic&lt;/h4&gt;

&lt;p&gt;The most important goal for me is traffic.  I want this site to get a lot of traffic.  For the last two weeks, I've averaged 112/users a day.  Of course, part of that is due to a spike because of last Friday's article on &lt;a href=&quot;http://spindrop.us/2006/06/02/editing-css-live-in-firefox&quot;&gt;Editing CSS from Firefox&lt;/a&gt; which had an anomalous amount of hits due to getting on &lt;a href=&quot;http://programming.reddit.com/goto?id=73c6&quot;&gt;reddit&lt;/a&gt;, &lt;a href=&quot;http://del.icio.us/popular/&quot;&gt;del.icio.us popular&lt;/a&gt;, &lt;a href=&quot;http://digg.com/&quot;&gt;digg&lt;/a&gt;.&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Traffic is important because I without it I can't expect to have revenue or community.  There's also a sense of validation that you have something useful to say, and sites like &lt;a href=&quot;http://del.icio.us/&quot;&gt;del.icio.us&lt;/a&gt; make you feel that not only was it useful, it's worth hanging on to.&lt;/p&gt;

&lt;h4&gt;Revenue&lt;/h4&gt;

&lt;p&gt;I'd like to earn enough to actually make blogging make a significant change in my lifestyle.  Right now it's a hobby.  A dedicated hobby, but it's still a hobby.  I love writing, I love to see myself improve, I love what I write about.  I'd like to continue to do that full time.  I'm sure with some tweaks to adsense and some other forms of advertising, I can kick that up.  But content, which attracts traffic, will be the biggest driver of them all.  My less-than-&quot;adsense optimized&quot; &lt;a href=&quot;http://reviewsby.us/&quot;&gt;restaurant review site&lt;/a&gt; generates a lot more money (seriously, we're talking in small terms, like the cost of an iced tea), but it has a lot of content and a lot of useful ads that get generated from adsense.&lt;/p&gt;

&lt;p&gt;Last month (May 2006) I made just shy over $2.00 from the web site.  That's not much, but it's better than $0.  I'd like to make $4 in a given month.  On one hand it's a doubling of earnings, but on another, it's not very much money.  I'm not expecting to double the earnings this month, although I might easily do that, but I am expecting to hit that $4 mark eventually.  It's an easily obtainable goal, but for me easy goals are good.  I feel just as good when I hit them, as I would if I got a raise or a bonus at my &quot;9 to 5.&quot;&lt;/p&gt;

&lt;h4&gt;Community&lt;/h4&gt;

&lt;p&gt;I'd like for there to be some level of interaction with me.  I don't  mean community in the &lt;a href=&quot;http://livejournal.com/&quot;&gt;LiveJournal&lt;/a&gt; sense of the word, I just want some interaction with me the poster, and my commenters.  The last week or two has seen a few &quot;real comments,&quot; which is promising.  I hope that trend continues over time.  It's an opportunity to improve myself if I get good feedback, and it's an opportunity to further express or clarify a point.  I also like to help people out.&lt;/p&gt;

&lt;h4&gt;Linking out to my other sites&lt;/h4&gt;

&lt;p&gt;Lastly, I do want this site to assist my other sites.  With a decent amount of traffic, this site can be a great link in to my other sites, and bring them up to speed.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Overall, I'm happy where I am, and I'm happy where I intend to go.  Each week brings in a new useful content which is a nice record of how I do things, and proves some degree of usefulness to others.&lt;/p&gt;

&lt;p&gt;I hope to expand on those strengths.  This body of content might be small now, but it only gets bigger.  My long term goal is to be able to generate the traffic and revenue I need so I can justify dedicating more time to this project.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;Perhaps it's not anomalous, but if you saw my statistics you'd understand why I might think so. &lt;a href=&quot;#fnr1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Welcome Performancing Users</title>
   <link href="http://davedash.com/2006/05/31/welcome-performancing-users/"/>
   <updated>2006-05-31T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/31/welcome-performancing-users</id>
   <content type="html">&lt;div class=&quot;screenshot_thumb&quot; style=&quot;width:240px&quot;&gt;
    &lt;a href=&quot;http://static.flickr.com/45/157125873_34dc285208_o.jpg&quot; title=&quot;Zoom In&quot;&gt;&lt;img src=&quot;http://static.flickr.com/45/157125873_34dc285208_m.jpg&quot; width=&quot;240&quot; height=&quot;96&quot; alt=&quot;Google Analytics Screenshot&quot; /&gt;&lt;/a&gt;
    &lt;p class=&quot;caption&quot;&gt;Referral percentages, the blue is Performancing.com&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;Yesterday I took advantage of the &lt;a href=&quot;http://performancing.com/launchpad&quot;&gt;Performancing launchpad&lt;/a&gt; to &lt;a href=&quot;http://performancing.com/node/2682&quot;&gt;announce reviewsby.us to the blogging world&lt;/a&gt;.  Jumping at the opportunity was smart, I was the second site on the &lt;a href=&quot;http://performancing.com/launchpad&quot;&gt;launchpad&lt;/a&gt; and made it to the &lt;a href=&quot;http://performancing.com/&quot;&gt;homepage&lt;/a&gt;.  As a result, I had probably my largest day of traffic (it's not a whole lot, but it's definitely noticeable in these early stages).&lt;/p&gt;

&lt;p&gt;We also got a few new users and a &lt;a href=&quot;http://reviewsby.us/&quot;&gt;few new restaurants&lt;/a&gt;.  So check it out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Polls</title>
   <link href="http://davedash.com/2006/05/31/polls/"/>
   <updated>2006-05-31T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/31/polls</id>
   <content type="html">&lt;p&gt;I decided to install &lt;a href=&quot;http://blog.jalenack.com/archives/democracy/&quot;&gt;Democracy&lt;/a&gt; for &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;.  This was partially inspired by &lt;a href=&quot;http://www.problogger.net/archives/2006/06/01/blog-polls/&quot;&gt;Darren's post at Problogger&lt;/a&gt; and for my own development efforts for the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;restaurant review site&lt;/a&gt;.  Not sure if it allows you to change your vote, that would be useful for the first poll.  I suppose we'll find out.&lt;/p&gt;

&lt;p&gt;So the first poll is deciding on the next feature to implement for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;.  I've seeded it with some of the top requests I've heard/felt.  But I'd like to hear other peoples ideas.  If you're not familiar with the site, &lt;a href=&quot;http://reviewsby.us/&quot;&gt;take a look&lt;/a&gt; and tell me what you think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For most people the poll is on the left sidebar.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>ReviewsBy.Us bugfixes</title>
   <link href="http://davedash.com/2006/05/30/reviewsbyus-bugfixes/"/>
   <updated>2006-05-30T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/30/reviewsbyus-bugfixes</id>
   <content type="html">&lt;p&gt;A lot of bugs have popped up recently.&lt;/p&gt;

&lt;h3&gt;Logins&lt;/h3&gt;

&lt;p&gt;The logins weren't redirecting people to the correct place.  Unfortunately the login system still needs a lot of work.  I am probably going to rewrite it completely.  It doesn't consistantly remember where you are coming from or where you intend to go after logging in.  I'll be jotting down a clean system to log people in propperly.&lt;/p&gt;

&lt;h3&gt;Tags&lt;/h3&gt;

&lt;p&gt;Tags work a bit better.  They follow the flickr style of tagging, which is each word is a tag, unless surrounded in double quotes.  Previously they weren't producing a lot of empty tags.&lt;/p&gt;

&lt;h3&gt;Latitude and Longitude even more precise.&lt;/h3&gt;

&lt;p&gt;A small error in the database definitions resulted in lat/longitudes of restaurants that were greater than 100 (or rather &lt;code&gt;abs(x) &amp;gt; 100&lt;/code&gt; where &lt;code&gt;x&lt;/code&gt; is latitude or longitude were being truncated to &lt;code&gt;100&lt;/code&gt; or &lt;code&gt;-100&lt;/code&gt;.  This was easily fixed so things should look just right on the maps.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Quicksilver + TextMate = craZy delicious development environment</title>
   <link href="http://davedash.com/2006/05/28/quicksilver-textmate-crazy-delicious-development-environment/"/>
   <updated>2006-05-28T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/28/quicksilver-textmate-crazy-delicious-development-environment</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt;Cmd-T allows you to search for the files in your currently opened TextMate project.  I learned this shortly after writing this post, but forgot to mention it.  Thanks again to Tyson Tune for pointing that out.&lt;/p&gt;

&lt;div class=&quot;article_logo&quot;&gt;
    &lt;img src=&quot;http://spindrop.us/wp-content/uploads/2006/05/qs+tm.png&quot; alt=&quot;Quicksilver + Textmate&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;There's a number of tools for the &lt;a href=&quot;http://www.apple.com/macosx/&quot;&gt;OS X&lt;/a&gt; that help me with my productivity (and unfortunately have no equivalents on other platforms).  &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, a launcher, and &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt;, a text editor work wonders and together work fairly well.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; is a the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; equivalent to the command line.  You can launch applications or files or perform any number of operations on those files or applications.  With its powerful collection of plugins you can have it do much more, for example you can take a music file and play it in iTunes within the iTunes party shuffle.  Or take an image file and have it submit to &lt;a href=&quot;http://flickr.com/&quot;&gt;flickr&lt;/a&gt; with a few simple keystrokes.  Initially, I couldn't get an idea of the application, other than a lot of people loved it.  Now, I'm barely using it to its potential and I love it.  Using a computer without it is quite a drag.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; is similarly feature rich and elegant.  Just using a small number of its features makes it worth its cost.  All my &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; projects are written using &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; as are my articles for this web site.  It's strength for me is its automation.
Together Quicksilver and Textmate make a winning combination.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;Projects in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt;&lt;/h3&gt;

&lt;div class=&quot;screenshot_thumb&quot;&gt;
    &lt;a href=&quot;http://static.flickr.com/49/154779362_e044c75c04_o.png&quot; title=&quot;Larger Photo&quot;&gt;
        &lt;img src=&quot;http://static.flickr.com/49/154779362_e044c75c04_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;reviewsby.us textmate project screenshot&quot; /&gt;
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I like the concept of &quot;projects&quot; in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; (its common to a lot of text editors).  You can drag files or folders into &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; and group them as you see fit.&lt;/p&gt;

&lt;p&gt;Many of my projects are written using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, so I'll try to keep the entire project folder in my &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; project.  Additionally I'll keep the &lt;a href=&quot;http://www.symfony-project.com/trac/browser/trunk/lib&quot;&gt;symfony libraries&lt;/a&gt; if not the entire &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; libraries referenced in the project as well.  Now I have access to all of my files with relative ease.  I generally create a file group in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; of frequently accessed files to bypass the pain going through hierarchies of folders.&lt;/p&gt;

&lt;p&gt;If I have a &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; project open, anytime I open a file that belongs in that project, it will open in that project window.  That means if I use the &lt;code&gt;mate&lt;/code&gt; command line utility, Finder or even &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, it'll still open in the project window.  This is useful.&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;screenshot_thumb_alt&quot;&gt;
&lt;a href=&quot;http://static.flickr.com/51/154781466_ca0f67a703_o.png&quot; title=&quot;Zoom In&quot;&gt;&lt;img src=&quot;http://static.flickr.com/51/154781466_ca0f67a703_m.jpg&quot; width=&quot;240&quot; height=&quot;214&quot; alt=&quot;Quicksilver Catalogue&quot; /&gt;&lt;/a&gt;  
&lt;/div&gt;


&lt;h3&gt;Catalogues in &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;When you open up &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; and type in some letters, it searches some catalogues by default (e.g. Applications, Documents, Desktop, etc) in an attempt to figure out what the &quot;subject&quot; of your action to be.  These catalogues are fully customizable, so it's trivial to add the directories of your project and your libraries into &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;New Workflow&lt;/h3&gt;

&lt;div class=&quot;screenshot_thumb&quot;&gt;
&lt;a href=&quot;http://static.flickr.com/68/154785728_9e7a5a815d_o.png&quot; title=&quot;Zoom In&quot;&gt;&lt;img src=&quot;http://static.flickr.com/68/154785728_9e7a5a815d_m.jpg&quot; width=&quot;240&quot; height=&quot;82&quot; alt=&quot;Quicksilver finding reviewsby.us&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Now that you've setup your project in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; and added the same directories to &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, you'll have a much improved workflow.  If you save your &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; project in your Documents directory, you need only open &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; (I use Command+Space as a shortcut) and type a few letters of the project (for example, I named my project file &lt;code&gt;reviewsby.us&lt;/code&gt; for my restaurant review site).&lt;/p&gt;

&lt;p&gt;When that's open, I can now open any file of that project in anyway I feel necessary.  Let's say I need to open a library file, like &lt;code&gt;sfFeed.class.php&lt;/code&gt;.  I need only type in a few letters and it opens inside my project.&lt;/p&gt;

&lt;p&gt;This process now saves me a ton of time in digging through hierarchies of folders upon folders.  It's many times quicker than Spotlight.  &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Give it a try&lt;/a&gt;, there's thousands of uses for it, this is just one way I use it.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;While debugging errors in a project, if PHP tells you a certain file gave you a certain error, you can highlight the filename in Safari (or any other Cocoa browser), hit Command Escape (or whatever custome keystroke you setup for sending selection to Quicksilver), hit Enter and have it show up in TextMate. &lt;a href=&quot;#fnr1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Google Analytics</title>
   <link href="http://davedash.com/2006/05/26/google-analytics/"/>
   <updated>2006-05-26T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/26/google-analytics</id>
   <content type="html">&lt;div class=&quot;photo right&quot; style=&quot;float: right;&quot;&gt;
&lt;img src=&quot;http://static.flickr.com/51/153678327_1af6ace0c6.jpg&quot; width=&quot;500&quot; height=&quot;223&quot; alt=&quot;Google Analytics Screenshot&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;I've also jumped on the &lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google Analytics&lt;/a&gt; bandwagon and added a lot of the sites in &quot;family.&quot;  If you're familiar with Urchin, it has a similar feel to it.  It almost feels a bit lighter feature-wise.  There's a few issues I will take with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When they list links to your site (e.g. /login) they don't hyperlink it.&lt;/li&gt;
&lt;li&gt;I'd like to drill down to raw data.  I like to make my own correlations.  Heck, I want to drill down to see if I should filter someone from the list.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Other than that, I like the potential that this will offer me.  This combined with &lt;a href=&quot;http://google.com/webmasters/sitemaps/&quot;&gt;Google Sitemaps&lt;/a&gt; will provide powerful analysis of the site.  As it stands, &lt;a href=&quot;http://katiebonn.com/&quot;&gt;Katie&lt;/a&gt; and I should be eating at the &lt;a href=&quot;http://reviewsby.us/restaurant/cheesecake-factory&quot;&gt;Cheesecake Factory&lt;/a&gt; and &lt;a href=&quot;http://reviewsby.us/restaurant/bad-waitress&quot;&gt;non-chain restaurants&lt;/a&gt; as we seem to do well in the ranks and I need to write more &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot;&gt;about maps&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>More usable approach to adding restaurants</title>
   <link href="http://davedash.com/2006/05/25/more-usable-approach-to-adding-restaurants/"/>
   <updated>2006-05-25T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/25/more-usable-approach-to-adding-restaurants</id>
   <content type="html">&lt;p&gt;The &quot;lazy&quot; approach to usability is to just use a web site or application yourself and use &lt;a href=&quot;http://www.useit.com/papers/heuristic/heuristic_list.html&quot;&gt;usability heuristics&lt;/a&gt;.  This is what I do, and it's why I have a huge to-do list of things to fix on the site.  Lately to build out the site content, I've been going to a lot of restaurants not listed on &lt;a href=&quot;http://reviewsby.us/&quot;&gt;the site&lt;/a&gt;.  This means I have a multi-step process after I eat:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a new restaurant.&lt;/li&gt;
&lt;li&gt;Add a location.&lt;/li&gt;
&lt;li&gt;Add a review of the restaurant.&lt;/li&gt;
&lt;li&gt;Rate the restaurant&lt;/li&gt;
&lt;li&gt;Add all the menu items I ate.&lt;/li&gt;
&lt;li&gt;Tag the menu items.&lt;/li&gt;
&lt;li&gt;Write a review of the menu items.&lt;/li&gt;
&lt;li&gt;Rate the menu item.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;For everyday users, I don't expect 1 or 2 and only a few of 3-8, but 1 is a requisite and 2 is nice for maps and just being able to get information quickly.  I did want to streamline this process, so I made a combined form that does 1,2 and 3 all in one place.  That's just six steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a new restaurant.&lt;/li&gt;
&lt;li&gt;Rate the restaurant&lt;/li&gt;
&lt;li&gt;Add all the menu items I ate.&lt;/li&gt;
&lt;li&gt;Tag the menu items.&lt;/li&gt;
&lt;li&gt;Write a review of the menu items.&lt;/li&gt;
&lt;li&gt;Rate the menu item.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;For most users it makes sense, since most restaurants just have one location - or if they are adding a restaurant they are only thinking of a specific location.&lt;/p&gt;

&lt;p&gt;I also took the opportunity to do a check on restaurant names.  If you attempt to enter &lt;a href=&quot;http://reviewsby.us/restaurant/green-mill&quot;&gt;Green Mill&lt;/a&gt; twice, you'll be prompted that a restaurant with that name already exists.  It's not a very smart check, but it should serve it's purpose.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>AJAX star rater for symfony</title>
   <link href="http://davedash.com/2006/05/23/ajax-star-rater-for-symfony/"/>
   <updated>2006-05-23T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/23/ajax-star-rater-for-symfony</id>
   <content type="html">&lt;p&gt;Francois from the &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony project&lt;/a&gt; beat me to the punch.  I was going to post a detailed how-to on adding a star-rater to your web site (similar to the &lt;a href=&quot;http://spindrop.us/2006/05/17/rate-your-favorite-dishes&quot;&gt;one's I created for reviewsby.us&lt;/a&gt;), but for most of you &lt;a href=&quot;http://www.symfony-project.com/snippets/5&quot;&gt;this should do the trick&lt;/a&gt;.  Unless people request it sooner, I'll hold off on publishing the details on my star-rater for a while.  It only offers a few minor differences (&lt;acronym title=&quot;In My Opinion&quot;&gt;IMO&lt;/acronym&gt; advantages) to this snippet.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Migrating from Drupal (4.7) to Wordpress</title>
   <link href="http://davedash.com/2006/05/19/migrating-from-drupal-47-to-wordpress/"/>
   <updated>2006-05-19T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/19/migrating-from-drupal-47-to-wordpress</id>
   <content type="html">&lt;p&gt;I helped &lt;a href=&quot;http://katiebonn.com/&quot;&gt;Katie&lt;/a&gt; setup her new blog this weekend and decided that &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; offers much of what I want out of this blog for a lot less effort than &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt;&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  I decided it might be worth my time to now while this blog is in it's infancy to try converting from &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; to &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;The way I start most of my projects is with a plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#discovery&quot;&gt;Discovery&lt;/a&gt;: Find any articles about converting and get a gist of what to do.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation&quot;&gt;Implementation&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Create a temporary subdomain to install &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#copy_content&quot;&gt;Copy all content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Copy all static pages&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#urls&quot;&gt;Configure &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s to match the existing site&lt;/a&gt; &lt;sup id=&quot;fnr2&quot;&gt;&lt;a href=&quot;#fn2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#adjusting_templates&quot;&gt;adjust templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;split entries correctly (i.e. change &lt;code&gt;&amp;lt;!--break--&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#make_the_switch&quot;&gt;Make the switch when I'm satisfied&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm really confident that this will be easy.  I don't even have to worry about comments or anything, since this blog is pretty new, but I can demonstrate how to take care of the.&lt;/p&gt;

&lt;h3 id=&quot;discovery&quot;&gt;Discovery&lt;/h3&gt;


&lt;p&gt;&lt;a href=&quot;http://vrypan.net/log/archives/2005/03/10/migrating-from-drupal-to-wordpress/&quot;&gt;This post&lt;/a&gt; details a migration path from &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; to &lt;a href=&quot;http://wordpress.org/&quot;&gt;wordpress&lt;/a&gt;.  Some considerations had to be made since I'm using &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; 4.7.&lt;/p&gt;

&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;




&lt;h4 id=&quot;copy_content&quot;&gt;Copy content&lt;/h4&gt;


&lt;p&gt;I followed most of the instructions, with some alterations from &lt;a href=&quot;http://vrypan.net/log/archives/2005/03/10/migrating-from-drupal-to-wordpress/&quot;&gt;vrypan.net&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I installed &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; and in &lt;code&gt;mysql&lt;/code&gt; ran the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;use wordpress;
delete from wp_categories;
delete from wp_posts;     
delete from wp_post2cat;
delete from wp_comments
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I run my &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; site in the same database server, so the data copying was a snap.  If you aren't so fortunate, just copy the relevant drupal tables temporarily your wordpress database.&lt;/p&gt;

&lt;p&gt;First we get the &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; categories into &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;USE wordpress;

INSERT INTO 
    wp_categories (cat_ID, cat_name, category_nicename, category_description, category_parent)
SELECT term_data.tid, name, name, description, parent 
FROM drupal.term_data, drupal.term_hierarchy 
WHERE term_data.tid=term_hierarchy.tid;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again with the posts:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO 
    wp_posts (id, post_date, post_content, post_title, 
    post_excerpt, post_name, post_modified)
SELECT DISTINCT
    n.nid, FROM_UNIXTIME(created), body, n.title, 
    teaser, 
    REPLACE(REPLACE(REPLACE(REPLACE(LOWER(n.title),' ', '_'),'.', '_'),',', '_'),'+', '_'),
    FROM_UNIXTIME(changed) 
FROM drupal.node n, drupal.node_revisions r
WHERE n.vid = r.vid
    AND type='story' OR type='page' ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And the relation between posts and categories:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO wp_post2cat (post_id,category_id) SELECT nid,tid FROM drupal.term_node ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And finally comments:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO 
    wp_comments 
    (comment_post_ID, comment_date, comment_content, comment_parent)
SELECT 
    nid, FROM_UNIXTIME(timestamp), 
    concat('',subject, '&amp;lt;br /&amp;gt;', comment), thread 
FROM drupal.comments ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I ended up moving the one static page I had into &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;'s &quot;pages&quot; section manually.&lt;/p&gt;

&lt;p&gt;Since my pages are written in &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt;, I enabled the &lt;a href=&quot;http://www.michelf.com/projects/php-markdown/&quot;&gt;Markdown for WordPress plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;urls&quot;&gt;&lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s&lt;/h4&gt;


&lt;p&gt;Now for the real test.  I needed to go through each page on my site and see if I could get to it using the same &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s.  Since I had only 14 posts, I did this somewhat manually.  I used &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt;'s built in admin to do this from most popular to least popular.  Most &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s worked fine.  There were a small number that didn't for various reasons, I used custom &lt;code&gt;mod_rewrite&lt;/code&gt; rules to handle them.&lt;/p&gt;

&lt;h4 id=&quot;adjusting_templates&quot;&gt;Adjusting Templates&lt;/h4&gt;


&lt;p&gt;My &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; template was fairly clean and simple.  So I adjusted the &lt;acronym title=&quot;Cascading Style Sheet&quot;&gt;CSS&lt;/acronym&gt; for the default theme in &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; until I got what I liked.  Very minimal changes had to be made to the actual &quot;&lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt;.&quot;&lt;/p&gt;

&lt;h3 id=&quot;make_the_switch&quot;&gt;Make the switch&lt;/h3&gt;


&lt;p&gt;Well, time to make the switch.  In the WordPress administration, I just had to tell it that it's now going to be located at &lt;code&gt;spindrop.us&lt;/code&gt;.  Then I moved my &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; installation to the &lt;a href=&quot;http://spindrop.us/&quot;&gt;spindrop.us&lt;/a&gt; web root.  It was a snap.  Let me know if you have any troubles.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;Taxonomy, legible &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s and trackback support all seemed quite difficult to master in Drupal.  In Wordpress they appeared to be available standard or with a minor change in the administration panels. &lt;a href=&quot;#fnr1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;fn2&quot;&gt;This is a prime reason why &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s should be clean and make sense to the end user, not the programmer of the publishing software. &lt;a href=&quot;#fnr2&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 2 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Comment Anonymously at ReviewsBy.Us</title>
   <link href="http://davedash.com/2006/05/18/comment-anonymously-at-reviewsbyus/"/>
   <updated>2006-05-18T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/18/comment-anonymously-at-reviewsbyus</id>
   <content type="html">&lt;p&gt;I decided to overhaul the comment system on the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; site.  I implemented an &lt;acronym title=&quot;Asynchronous Java and XML&quot;&gt;AJAX&lt;/acronym&gt; form and allow anonymous comments.  Anonymous comments is a slippery slope, but reading &lt;a href=&quot;http://www.alistapart.com/articles/flywheelsandfriction&quot;&gt;about friction&lt;/a&gt; and &lt;a href=&quot;http://www.alistapart.com/articles/identitymatters/&quot;&gt;identity&lt;/a&gt; some things need to be taken into account.&lt;/p&gt;

&lt;p&gt;The site is new.  It's going to take a while before it generates a lot of traffic and therefore members and therefore comments.  But it would be nice to keep collecting information and make that collection as painless as possible.  Of course the potential for spam is high, but when/if that happens there are safeguards that can be put in place.  I imagine, until a decent membership base is established, the rules for who can and cannot post will probably change every now and again.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rate your favorite dishes</title>
   <link href="http://davedash.com/2006/05/17/rate-your-favorite-dishes/"/>
   <updated>2006-05-17T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/17/rate-your-favorite-dishes</id>
   <content type="html">&lt;div class=&quot;photo&quot; style=&quot;float:right&quot;&gt;
&lt;a href=&quot;http://reviewsby.us/restaurant/cheesecake-factory&quot; title=&quot;Screenshot of ratings on Cheesecake Factory&quot;&gt;&lt;img src=&quot;http://static.flickr.com/49/148108735_8af19f1c43_m.jpg&quot; width=&quot;240&quot; height=&quot;132&quot; alt=&quot;Screenshot of reviewsby.us dish ratings&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;
If you've stopped by &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; this week, you'll notice we extended ratings to dishes.  On a restaurant page, blue stars are averages for things you haven't rated yourself and orange stars are your own rating.  At a glance, you can now see what you should order at any given restaurant.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>More maps, better presentation and prices</title>
   <link href="http://davedash.com/2006/05/05/more_maps_better_presentation_and_prices/"/>
   <updated>2006-05-05T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/05/more_maps_better_presentation_and_prices</id>
   <content type="html">&lt;p&gt;I added a lot more features to &lt;a href=&quot;http://reviewsby.us&quot;&gt;the reviewsby.us site&lt;/a&gt;.  One thing I was &lt;a href=&quot;http://reviewsby.us/feedback&quot;&gt;ask&lt;/a&gt;ed about was adding a price field.  I added that in, it could use some work, but it is a start.  I also cleaned up some of the location formatting (see &lt;a href=&quot;http://reviewsby.us/restaurant/tony-romas/location/mall-of-america&quot;&gt;Tony Roma's at the MOA&lt;/a&gt;) so the phone numbers are legible.&lt;/p&gt;

&lt;p&gt;The real exciting thing is maps.  Applying the same principles from the &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot;&gt;Yahoo! Map tutorial&lt;/a&gt;, I added maps to all the tag pages.  Want to know &lt;a href=&quot;http://reviewsby.us/tag/fried&quot;&gt;where to find fried food&lt;/a&gt;?  Just look at the map.  I'm taking a few shortcuts now.  For example most of our restaurants are located in Minneapolis, therefore the maps seem to center in on the Twin Cities area.  This of course may get messy over time when more restaurants get added outside the Minneapolis area.  By then, I'll have some personalization setup to narrow down on restaurants based on where the visitor is located.&lt;/p&gt;

&lt;p&gt;I wrote a few tutorials that we should see later this month based on what I did.  Fairly simple stuff, but still useful in my opinion.&lt;/p&gt;

&lt;p&gt;Enjoy the site, and &lt;a href=&quot;http://reviewsby.us/feedback&quot;&gt;let me know what you want to see&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Easy Yahoo! Maps and  GeoRSS with symfony</title>
   <link href="http://davedash.com/2006/04/26/easy_yahoo_maps_and_georss_with_symfony/"/>
   <updated>2006-04-26T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/04/26/easy_yahoo_maps_and_georss_with_symfony</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;GeoRSS&lt;/a&gt; is an extension of RSS that incorporates geographic data (i.e. latitude/longitude coordinates).  This is useful for plotting any data that might need to be placed on a map.&lt;/p&gt;

&lt;p&gt;While building out the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; map, I decided to use the &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps API&lt;/a&gt; versus the &lt;a href=&quot;http://www.google.com/apis/maps/&quot;&gt;Google Maps API&lt;/a&gt; because I wanted to gain some familiarity with another API.&lt;/p&gt;

&lt;p&gt;It was worth trying &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo!'s API&lt;/a&gt;.  First of all, &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; has addresses for restaurants and Yahoo! provides a simple &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Geocoding REST&lt;/a&gt; service.  This made it easy for me to convert street addresses to latitude and longitude pairs (even though this wasn't required as we'll soon see).&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;  The real selling point of &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo!&lt;/a&gt; was the &lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;GeoRSS&lt;/a&gt; functionality.  I can extend an RSS feed (which &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; generates quite easily) to add latitude or longitude points (or even the street address), direct my &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! map&lt;/a&gt; to the feed and voila, all the locations in that feed are now on the map, and when I click on them, the RSS item is displayed.  That cut down on a lot of development time.&lt;/p&gt;

&lt;!--break--&gt;


&lt;!--more--&gt;


&lt;h3&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;Yahoo's GeoRSS&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The GeoRSS format that Yahoo uses is fairly simple to grasp if you know what an RSS feed looks like.  Here's a typical RSS feed:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;rss version=&quot;2.0&quot;&amp;gt;
    &amp;lt;channel&amp;gt;
        &amp;lt;title&amp;gt;Latest restaurants&amp;lt;/title&amp;gt;
        &amp;lt;link&amp;gt;http://reviewsby.us/&amp;lt;/link&amp;gt;
        &amp;lt;description&amp;gt;A list of the latest restaurants posted to my reviewsby.us&amp;lt;/description&amp;gt;
        &amp;lt;language&amp;gt;en&amp;lt;/language&amp;gt;
        &amp;lt;item&amp;gt;
            &amp;lt;title&amp;gt;Bubba Gump Shrimp Co. Restaurant and Market&amp;lt;/title&amp;gt;
            &amp;lt;description&amp;gt;A *Forest Gump* themed restaurant.  Featuring a large selection of seafood items.&amp;lt;/description&amp;gt;
            &amp;lt;link&amp;gt;http://reviewsby.us/restaurant/bubba-gump-shrimp-co-restaurant-and-market&amp;lt;/link&amp;gt;
            &amp;lt;guid&amp;gt;25&amp;lt;/guid&amp;gt;
            &amp;lt;pubDate&amp;gt;Sun, 23 Apr 2006 08:04:00 -0700&amp;lt;/pubDate&amp;gt;
        &amp;lt;/item&amp;gt;
        &amp;lt;item&amp;gt;
            &amp;lt;title&amp;gt;Famous Dave's Barbeque&amp;lt;/title&amp;gt;
            &amp;lt;link&amp;gt;http://reviewsby.us/restaurant/famous-daves-barbeque&amp;lt;/link&amp;gt;
            &amp;lt;guid&amp;gt;24&amp;lt;/guid&amp;gt;
            &amp;lt;pubDate&amp;gt;Wed, 19 Apr 2006 19:58:08 -0700&amp;lt;/pubDate&amp;gt;
        &amp;lt;/item&amp;gt;
    &amp;lt;/channel&amp;gt;
&amp;lt;/rss&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;item /&amp;gt;&lt;/code&gt; in this example is a restaurant.  To turn this into a GeoRSS feed, we only need to change a few things:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;rss version=&quot;2.0&quot; xmlns:geo=&quot;http://www.w3.org/2003/01/geo/wgs84_pos#&quot;&amp;gt;
    &amp;lt;channel&amp;gt;
        &amp;lt;title&amp;gt;Latest restaurants' locations&amp;lt;/title&amp;gt;
        &amp;lt;link&amp;gt;http://reviewsby.us/&amp;lt;/link&amp;gt;
        &amp;lt;description&amp;gt;A geocoded list of the latest restaurants' locations posted to my reviewsby.us&amp;lt;/description&amp;gt;
        &amp;lt;language&amp;gt;en&amp;lt;/language&amp;gt;
        &amp;lt;item&amp;gt;
            &amp;lt;title&amp;gt;Bubba Gump Shrimp Co. Restaurant and Market (Mall of America (Bloomington, MN))&amp;lt;/title&amp;gt;
            &amp;lt;link&amp;gt;http://reviewsby.us/restaurant/bubba-gump-shrimp-co-restaurant-and-market/location/mall-of-america&amp;lt;/link&amp;gt;
            &amp;lt;guid&amp;gt;18&amp;lt;/guid&amp;gt;
            &amp;lt;pubDate&amp;gt;Sun, 23 Apr 2006 08:08:19 -0700&amp;lt;/pubDate&amp;gt;
            &amp;lt;geo:lat&amp;gt;44.85380173&amp;lt;/geo:lat&amp;gt;
            &amp;lt;geo:long&amp;gt;-93.24040222&amp;lt;/geo:long&amp;gt;
        &amp;lt;/item&amp;gt;
    &amp;lt;/channel&amp;gt;
&amp;lt;/rss&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We just added &lt;code&gt;xmlns:geo=&quot;http://www.w3.org/2003/01/geo/wgs84_pos#&quot;&lt;/code&gt; as an attribute to the &lt;code&gt;&amp;lt;rss/&amp;gt;&lt;/code&gt; tag and added &lt;code&gt;&amp;lt;geo:lat /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;geo:long /&amp;gt;&lt;/code&gt; tags to any given item.  If you'd rather use address, city, state and zip fields instead of latitude and longitude coordinates, &lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;the Yahoo! Georss page&lt;/a&gt; will tell you how.&lt;/p&gt;

&lt;h3&gt;&lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and Geodata&lt;/h3&gt;

&lt;p&gt;I knew full well that &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps&lt;/a&gt; did not require me to have everything in latitude longitude coordinates, but I felt that from an efficiency standpoint, it made more sense for me to convert them once using a &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;geocoder&lt;/a&gt; and then Yahoo! wouldn't have to translate them later.  Also, I'm trying to think ahead when more than just Minneapolis restaurants become a part of the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; site, I now have an easy way of determining distance between a user's home and a given restaurant.  Also, if Yahoo! Maps doesn't work out, I can use these coordinates in other mapping systems.&lt;/p&gt;

&lt;h4&gt;Extending the model&lt;/h4&gt;

&lt;p&gt;Models in &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; lend themselves to being easily extended.  So we can easily take a model for a location:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;table name=&quot;location&quot; phpName=&quot;Location&quot;&amp;gt;
    &amp;lt;column name=&quot;id&quot; type=&quot;integer&quot; primaryKey=&quot;true&quot; autoIncrement=&quot;true&quot;/&amp;gt;
    &amp;lt;column name=&quot;restaurant_id&quot; type=&quot;integer&quot; /&amp;gt;
    &amp;lt;foreign-key foreignTable=&quot;restaurant&quot;&amp;gt;
        &amp;lt;reference local=&quot;restaurant_id&quot; foreign=&quot;id&quot;/&amp;gt;
    &amp;lt;/foreign-key&amp;gt;
    &amp;lt;column name=&quot;stripped_title&quot; type=&quot;varchar&quot; size=&quot;255&quot; /&amp;gt;
    &amp;lt;column name=&quot;name&quot; type=&quot;varchar&quot; size=&quot;255&quot; /&amp;gt;
    &amp;lt;column name=&quot;address&quot; type=&quot;varchar&quot; size=&quot;255&quot; /&amp;gt;
    &amp;lt;column name=&quot;city&quot; type=&quot;varchar&quot; size=&quot;128&quot; /&amp;gt;
    &amp;lt;column name=&quot;state&quot; type=&quot;varchar&quot; size=&quot;16&quot; /&amp;gt;
    &amp;lt;column name=&quot;zip&quot; type=&quot;varchar&quot; size=&quot;9&quot; /&amp;gt;
    &amp;lt;column name=&quot;phone&quot; type=&quot;varchar&quot; size=&quot;16&quot; /&amp;gt;
    &amp;lt;column name=&quot;approved&quot; type=&quot;boolean&quot; /&amp;gt;
    &amp;lt;column name=&quot;updated_at&quot; type=&quot;TIMESTAMP&quot; /&amp;gt;
    &amp;lt;column name=&quot;created_at&quot; type=&quot;TIMESTAMP&quot; /&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and simply add two columns inside the &lt;code&gt;&amp;lt;table/&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;column name=&quot;latitude&quot; type=&quot;float&quot; size=&quot;10&quot; scale=&quot;8&quot;/&amp;gt;
&amp;lt;column name=&quot;longitude&quot; type=&quot;float&quot; size=&quot;10&quot; scale=&quot;8&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Easy!  However, we don't want to have to set the latitude and longitude by hand each time we update a &lt;code&gt;Location&lt;/code&gt;.  So first we write a function that takes an address and converts it to latitude/longitude.  I placed mine in a &lt;code&gt;myTools.class.php&lt;/code&gt; in my &lt;code&gt;lib&lt;/code&gt; folder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class myTools
{
    public static function getLatLng($address, $city = null, $state = null, $zip = null)
    {
        $url = sfConfig::get('app_yahoo_geocode');
        $query['appid'] = sfConfig::get('app_yahoo_app_id');
        $query['street'] = $address;
        $query['city'] = $city;
        $query['state'] = $state;
        $query['zip'] = $zip;
        $query['output'] = 'php'; 
        $url .= '?' . http_build_query($query); 
        $response = @file_get_contents($url);
        if ($response) {
            $response = unserialize($response);
            return $response['ResultSet']['Result'];
        }
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Defined in my &lt;code&gt;app.yml&lt;/code&gt; is &lt;code&gt;app_yahoo_geocode&lt;/code&gt; and my &lt;code&gt;app_yahoo_app_id&lt;/code&gt;.  &lt;code&gt;myTools::getLatLng()&lt;/code&gt; queries the &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Yahoo! REST&lt;/a&gt; service and returns the coordinates that Yahoo! delivers.  Note that the generated query string includes &lt;code&gt;output=php&lt;/code&gt;.  Yahoo! supports serializing output as PHP instead of XML.  This can save a bundle of time over decoding XML.&lt;/p&gt;

&lt;p&gt;So now let's look at our &lt;code&gt;Location.php&lt;/code&gt; and override its inherited save function:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function save($con = null)
{
    // save latitude and longitude
    $locdata = myTools::getLatLng($this-&amp;gt;getAddress(), $this-&amp;gt;getCity(), $this-&amp;gt;getState(), $this-&amp;gt;getZip());
    if ($locdata) 
    {
        $this-&amp;gt;setLatitude($locdata['Latitude']);
        $this-&amp;gt;setLongitude($locdata['Longitude']);
    }
    parent::save($con);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you stop here, you'll at least now know how to add latitude and longitude coordinates to an object automagically.&lt;/p&gt;

&lt;h4&gt;Producing a GeoRSS feed&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; very easily will allow you to &lt;a href=&quot;http://www.symfony-project.com/content/book/page/syndication.html&quot;&gt;generate an RSS feed&lt;/a&gt;.  How do we create a &lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;GeoRSS&lt;/a&gt; feed?  Just extend the &lt;code&gt;sfFeed&lt;/code&gt; class.  Rather than instantiating a feed like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$feed = sfFeed::newInstance('rss201rev2');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$feed = sfFeed::newInstance('geoRSS');  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then create an &lt;a href=&quot;http://spindrop.us/files/symfony/sfGeoRssFeed.class.txt&quot;&gt;sfGeoRssFeed.class.php&lt;/a&gt; and we're done.  We've created a GeoRSS feed fairly easily.  Comb through &lt;a href=&quot;http://spindrop.us/files/symfony/sfGeoRssFeed.class.txt&quot;&gt;sfGeoRssFeed.class.php&lt;/a&gt; and compare it to the &lt;a href=&quot;http://www.symfony-project.com/trac/browser/trunk/lib/symfony/addon/sfFeed/sfRss201rev2Feed.class.php?rev=403&quot;&gt;sfRss201rev2Feed.class.php&lt;/a&gt;, you'll notice it's not that different and that it's fairly easy to extend the sfFeed plugin for &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Adding your feed to a Yahoo! Map.&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/ajax/index.html#ex6&quot;&gt;Adding a GeoRSS feed to Yahoo! maps&lt;/a&gt; is simple.  Before I embedded the RSS feed into my Yahoo! Map I was prepared to write an algorithm to cluster only on the points in my RSS feed, lucky for me (and you), &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps&lt;/a&gt; does this automatically.  One pitfall you might reach during development is that Yahoo! Maps must be able to reach your GeoRSS feed.  My development machine is my personal laptop, so this didn't work so well until I uploaded to a publically accessible staging server.  &lt;a href=&quot;http://reviewsby.us/&quot;&gt;The maps&lt;/a&gt; worked like a charm as you can see.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps&lt;/a&gt; are very powerful, and &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; is up to the task.  I hope you found this tutorial useful.  If you have any trouble, let me know.  I hope your &lt;a href=&quot;http://reviewsby.us/&quot;&gt;next meal&lt;/a&gt; is a good one.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;
            &lt;p&gt;
                Using Yahoo! Maps wasn't a requisite of using the Yahoo! Geocoder.  
                The datasets that were returned could have been used to populate a Google Map just as easily.
                &lt;a href=&quot;#fnr1&quot;  class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;
            &lt;/p&gt;
        &lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 

</feed>

