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

<channel>
	<title>Chicken Scratches</title>
	<atom:link href="http://www.chickenwingsoftware.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.chickenwingsoftware.com</link>
	<description>Developing ideas on developing.</description>
	<pubDate>Tue, 07 Oct 2008 20:53:07 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6</generator>
	<language>en</language>
				<item>
		<title>Making a Facebook app (with Django) - part 3: Python &#038; FBML</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/django-facebook-3</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/django-facebook-3#comments</comments>
		<pubDate>Mon, 29 Sep 2008 21:17:57 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[facebook]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/?p=61</guid>
		<description><![CDATA[    
      Welcome to the third part in my series of posts about creating a
      Facebook application. I am using Django as my web development
      framework, and this post will focus on some of the backend
  [...]]]></description>
			<content:encoded><![CDATA[    <p>
      Welcome to the third part in my series of posts about creating a
      Facebook application. I am using Django as my web development
      framework, and this post will focus on some of the backend
      techniques I have worked out to make this work easier. This is
      not a tutorial, but a set of tools that I have developed. This
      is a long post, with a lot of source code; I hope you find at
      least some of it useful.
    </p>
    <p>
      Keep in mind as you read this that the Facebook platform is
      still very new, and likely to change. In fact, if you're a FB
      user, you are probably aware they recently completed a major
      transition to a new profile design. This included many changes
      behind the scenes for developers, some of which are still
      playing out. I recommend keeping up with
      the <a href="http://forum.developers.facebook.com/">Facebook
      Platform Developer Forum</a> and
      the <a href="http://developers.facebook.com/news.php?tab=blog">Facebook
      Developer Blog</a>.
    </p>
    <p>
      Also, I will assume you have already read
      the <a href="http://wiki.developers.facebook.com/index.php/API">API
      Documentation</a> and the documentation
      for <a href="http://wiki.developers.facebook.com/index.php/Python">PyFacebook</a>,
      and that you know how to create a web app
      using <a href="http://www.djangoproject.com">Django</a>. If not,
      you will want to start there.
    </p>
<span id="more-61"></span>
    <p>
      PyFacebook is very useful and includes some documentation on
      getting up and running with Django. You do still need an
      understanding of how Django works and how URLs are mapped.
    </p>
    <h4>
      My goal
    </h4>
    <p>
      My goal with these code snippets and techniques is to make
      developing a Facebook app as close as possible to developing a
      regular web app. The application I am using to develop and test
      these features is <a href="http://limericks.four32one.com">The
      Limerick Book</a>. Compare that page
      to <a href="http://apps.facebook.com/limericks">The Limerick
      Book Facebook App</a>. In fact, they are the same application,
      sharing the same code. I also have developed
      a <a href="http://apps.facebook.com/playscopa">multiplayer card
      game</a> based on the traditional Italian
      game <a href="http://apps.facebook.com/playscopa">Scopa</a>. This
      is a Facebook-only application, but I wanted to be able to test
      it outside of Facebook.
    </p>
    <p>
      Ideally, we would be able to write code and templates that can
      work equally well both inside Facebook and outside.  This is
      important even if you want users to only see your application
      within Facebook, because it makes testing infinitely easier. You
      want to be able to test on your local machine before publishing
      content, and you want to be able to test things out free from
      the limitations and frequent bugginess of the Facebook platform.
    </p>
    <h4>
      First steps
    </h4>
    <p>
      Here are some simple helper functions to make life as a Facebook
      Python developer easier.
    </p>
    <p>
      I am making a conscious decision here not to package all these
      helper functions into a downloadable library. My point here
      to <i>explain</i> code, rather than just hand it out. Many of
      these things are specific to my needs, and may not fit exactly
      what you want, so some tweaking may be required. I have most of
      these functions in a file called <i>fbUtil.py</i> that I import
      from most of my Django view code (except for the template tags,
      which need to be in a specific place, per Django). Feel free to
      do the same, or to copy and paste the code for your own use, but
      I recommend reading through the code, as your needs may not be
      the same as mine.
    <p>
      <b>inFb</b> is a simple function that takes a Request object and
      returns a boolean telling whether the request is taking place
      within the context of a Facebook canvas page.
    </p>
    <pre>
<span class="keyword">def</span> <span class="function-name">inFb</span>(request):
    <span class="keyword">return</span> (request.facebook <span class="keyword">and</span>
            (request.facebook.check_session(request) <span class="keyword">or</span>
             request.facebook.in_canvas))
</pre>
    <p>
      Once we have this, we can create some more useful functions.
    </p>
    <pre class="codeSnippet">
facebookUrl = settings.FACEBOOK_URL
<span class="keyword">def</span> <span class="function-name">fbReverse</span>(view, args=<span class="py-pseudo-keyword">None</span>, kwargs=<span class="py-pseudo-keyword">None</span>):
    '''<span class="string">
    Much like django.core.urlresolvers.reverse, except works
    in Facebook. Returns an absolute URL to a Facebook canvas
    page.
    </span>'''
    ret = reverse(view, args=args, kwargs=kwargs)
    <span class="keyword">return</span> facebookUrl + ret[1:] <span class="comment"># Remove leading slash
</span>
<span class="keyword">def</span> <span class="function-name">makeReverse</span>(request, view, args = <span class="py-pseudo-keyword">None</span>):
    '''<span class="string">
    Returns the URL of a the specified view, either in or out of Facebook.
    </span>'''
    <span class="keyword">if</span> inFb(request):
        <span class="keyword">return</span> fbReverse(view, args)
    <span class="keyword">return</span> reverse(view, args=args)

<span class="keyword">def</span> <span class="function-name">makeRedirect</span>(request, view, args = <span class="py-pseudo-keyword">None</span>, extra = ''):
    '''<span class="string">
    Returns a Response object for a HTTP redirect, either in or out of
    Facebook.
    </span>'''
    <span class="keyword">if</span> inFb(request):
        <span class="keyword">return</span> request.facebook.redirect(fbReverse(view, args) + extra)
    <span class="keyword">return</span> HttpResponseRedirect(reverse(view, args=args) + extra)
</pre>
    <p>
      These are designed to simplify URL calculations. See the
      comments in the code for explanations of what they do.
    </p>
    <pre class="codeSnippet">
<span class="keyword">def</span> <span class="function-name">makeResponse</span>(request, template, context, common=<span class="py-pseudo-keyword">False</span>):
    context['<span class="string">pageName</span>'] = template
    <span class="keyword">if</span> (<span class="keyword">not</span> common) <span class="keyword">and</span> inFb(request):
        tmpl = '<span class="string">fb/%s.fbml</span>' % template
    <span class="keyword">else:</span>
        tmpl = template + '<span class="string">.tmpl</span>'
    <span class="keyword">return</span> render_to_response(tmpl,
                              context,
                              context_instance=RequestContext(request))
</pre>      
    <p>
      This one is a little different, and may need updating depending
      on how your code is organized. I keep my templates in the
      directory 'mySite/myApp/templates' and give them names like
      'myTemplate.tmpl'. I put my Facebook-specific templates in the
      subdirectory called 'fb' and give them names like
      'myTemplate.fbml'. This function allows me to create two
      templates for a view: one for inside Facebook and one for
      outside. The function will detect which one to use and render it
      into a Response object. <b>request</b> is the Request object
      that is passed to the view function. <b>template</b> is a string
      holding the base-name of the template file, for example
      'myTemplate'. <b>context</b> is a dictionary with the template
      context variables. And if <b>common</b> is <b>True</b>, the
      function will use the main non-Facebook version of the template
      no matter what (because ideally we would be able to share even
      these as much as possible).
    </p>
    <p>
      One extra little bit that the function does is add an extra
      template variable called <b>pageName</b> with the base-name of
      the template. I find this useful for code re-use within
      templates, though it's not actually a Facebook-related feature.
    </p>
    <h4>
      Authentication
    </h4>
    <p>
      The next thing I wanted to do was to tie in Facebook's user
      information with Django's authentication mechanism. Depending on
      your application, you may or may not want to do this. If you
      want to remember information about user-contributed content, it
      is useful. The way I did it was to create a <i>UserProfile</i>
      model, tied in to the User model by a ForeignKey one-to-one
      relationship, and with a <b>facebookId</b> field (among other,
      app-specific fields). Then I created a Django authentication
      backend to allow authenticating by facebook ID. Here is my code
      for the authentication back end. If a user does not exist with
      the given facebook ID, a new one is created. (That, of course,
      may not be what you want, so you may have to modify that code.)
    </p>
    <p>
      This code assumes that Python's logging facilities have been set
      up, for error reporting.
    </p>
    <pre class="codeSnippet">
<span class="keyword">class</span> <span class="type">FacebookBackend</span>:
    '''<span class="string">
    Authenticate against Facebook.
    </span>'''

    <span class="keyword">def</span> <span class="function-name">authenticate</span>(<span class="py-pseudo-keyword">self</span>, facebookId=<span class="py-pseudo-keyword">None</span>):
        <span class="keyword">if</span> <span class="keyword">not</span> facebookId: <span class="keyword">return</span> <span class="py-pseudo-keyword">None</span>
        <span class="keyword">try:</span>
            profile = UserProfile.objects.get(facebookId=facebookId)
            UpdateFbUserDetails(profile.user, facebookId)
            <span class="keyword">return</span> profile.user
        <span class="keyword">except</span> UserProfile.DoesNotExist:
            <span class="comment"># No user. Create one.
</span>            <span class="keyword">pass</span>
        username = '<span class="string">fb_%s</span>' % facebookId
        <span class="keyword">try:</span>
            user = User.objects.get(username=username)
            <span class="comment"># This shouldn't really happen. Log an error.
</span>            logging.error('<span class="string">Strange: user %s already exists.</span>' % username)
        <span class="keyword">except</span> User.DoesNotExist:
            user = User.objects.create_user('<span class="string">fb_%s</span>' % facebookId, '')
        <span class="keyword">if</span> <span class="keyword">not</span> UpdateFbUserDetails(user, facebookId):
            <span class="keyword">return</span> <span class="py-pseudo-keyword">None</span>
        user.save()
        profile, created = UserProfile.objects.get_or_create(user=user)
        profile.facebookId = facebookId
        profile.save()
        <span class="keyword">return</span> user

    <span class="keyword">def</span> <span class="function-name">get_user</span>(<span class="py-pseudo-keyword">self</span>, user_id):
        <span class="keyword">try:</span>
            <span class="keyword">return</span> User.objects.get(pk=user_id)
        <span class="keyword">except</span> User.DoesNotExist:
            <span class="keyword">return</span> <span class="py-pseudo-keyword">None</span>

<span class="keyword">def</span> <span class="function-name">UpdateFbUserDetails</span>(user, fbId):
    """
<span class="string">    Fill in a user's first and last name, from Facebook.
    "</span>""
    <span class="keyword">if</span> (<span class="keyword">not</span> user.first_name) <span class="keyword">or</span> (<span class="keyword">not</span> user.last_name) :
        <span class="keyword">try:</span>
            fb = get_facebook_client()
            userDetails = fb.users.getInfo(fbId, ['<span class="string">last_name</span>', '<span class="string">first_name</span>'])
            user.first_name = userDetails[0]['<span class="string">first_name</span>'][:30]
            user.last_name = userDetails[0]['<span class="string">last_name</span>'][:30]
            user.save()
            <span class="keyword">return</span> <span class="py-pseudo-keyword">True</span>
        <span class="keyword">except</span> Exception, ex:
            logging.error('<span class="string">Error updating user: %s</span>' % ex)
            <span class="keyword">return</span> <span class="py-pseudo-keyword">False</span>
    <span class="keyword">return</span> <span class="py-pseudo-keyword">True</span>
</pre>
    <p>
      Now here is a function decorator you can use on your view
      functions. It will perform Facebook authentication if
      possible. It can also be used to require a login - either via
      Facebook or through a login page. With a parameter
      of <b>True</b>, it is equivalent to Django's
      built-in <b>login_required</b> decorator or to
      PyFacebook's <b>facebook.require_login</b> decorator, depending
      on whether the view is accessed inside or outside of Facebook.
    </p>
    <pre class="codeSnippet">
<span class="keyword">def</span> <span class="function-name">facebookView</span>(requireLogin=<span class="py-pseudo-keyword">False</span>):
    <span class="keyword">def</span> <span class="function-name">decorator</span>(func):
        <span class="keyword">def</span> <span class="function-name">wrapper</span>(request, *listArgs, **kwArgs):
            facebookLogin(request)
            fb = request.facebook
            <span class="keyword">if</span> requireLogin <span class="keyword">and</span> (<span class="keyword">not</span> request.user.is_authenticated()):
                <span class="keyword">if</span> inFb(request):
                    <span class="keyword">return</span> fb.redirect(fb.get_login_url(next=request.path))
                <span class="keyword">else:</span>
                    <span class="keyword">return</span> HttpResponseRedirect(settings.LOGIN_URL + '<span class="string">?next=%s</span>' % request.path)
            <span class="keyword">else:</span>
                <span class="keyword">return</span> func(request, *listArgs, **kwArgs)
        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        wrapper.__dict__ = func.__dict__
        wrapper.__module__ = func.__module__
        <span class="keyword">return</span> wrapper
    <span class="keyword">return</span> decorator

<span class="keyword">def</span> <span class="function-name">facebookLogin</span>(request):
    '''<span class="string">
    Attempt to login the user based on their Facebook credentials.
    Does nothing outside of Facebook.
    </span>'''
    facebook = get_facebook_client()
    <span class="keyword">if</span> (<span class="keyword">not</span> request.user.is_authenticated()) <span class="keyword">or</span> UserProfile.Get(request.user).facebookId != facebook.uid:
        <span class="keyword">if</span> request.facebook <span class="keyword">and</span> request.facebook.check_session(request):
            user = authenticate(facebookId=facebook.uid)
            login(request, user)
</pre>
    <p>
      And here is a very simple example of how to use it:
    </p>
    <pre class="codeSnippet">
@facebookView(<span class="py-pseudo-keyword">True</span>) <span class="comment"># Require login, in and out of Facebook
</span><span class="keyword">def</span> <span class="function-name">myView</span>(request):
    <span class="comment"># Put important view processing here.
</span>    <span class="keyword">return</span> makeResponse(request, '<span class="string">myTemplate</span>', {'<span class="string">templateVar</span>':'<span class="string">important data</span>'})
</pre>
    <h4>
      Templates
    </h4>
    <p>
      We are getting closer to the holy grail of being able to write
      one set of code that can run both in and out of Facebook. It
      would also be useful to be able to share templates, so I have
      worked out several mechanisms to facilitate this.
    </p>
    <h5>
      The Context Processor
    </h5>
    <p>
      Django has the useful concept of a "Context Processor," which
      allows pre-processing of a RequestContext object before the
      rendering of any template. I take advantage of this quite a
      bit. I already discussed the <b>cacheBreaker</b> variable in
      my <a href="http://www.chickenwingsoftware.com/scratches/python/django-facebook-2">post
      about Facebook and JavaScript</a>. Here are a couple more
      variables I've found useful:
    </p>
    <ul>
      <li>
	<b>fb</b> - The <i>facebook</i> object, for accessing the
	Facebook API, or <b>None</b> outside of Facebook.
      </li>
      <li>
	<b>profile</b> - The <i>UserProfile</i> object, if the user is
	logged in, or <b>None</b> otherwise.
      </li>
      <li>
	<b>baseTemplate</b> - The template to <i>extend</i> - either
	"base.tmpl" outside of Facebook or "fb/base.fbml" inside of
	Facebook.
      </li>
      <li>
	<b>loginRequired</b> - A useful string for including in
	hyperlinks. Within Facebook, it contains
	'loginrequired=true'. Outside of Facebook, it contains
	something like 'onclick="return checkLogin()"', which is some
	custom JavaScript to require the user to login before
	following the link. Of course, if the link already has an
	"onclick" event, this cannot be used.
      </li>
    </ul>
    <p>
      I'll leave it to you to write your custom template processor, as
      they can be very site-specific, but the above should get you
      started. Here
      is <a href="http://docs.djangoproject.com/en/dev/ref/templates/api/?from=olddocs#id1">Django's
      context processor documentation</a>.
    </p>
    <h5>
      Template tags
    </h5>
    <p>
      To allow more sharing, I've defined a couple of useful template
      tags. These must be defined in a file in a "templatetags"
      directory under your application directory, as described in
      the <a href="http://docs.djangoproject.com/en/dev//howto/custom-template-tags/#howto-custom-template-tags">Django
      custom tag and filter documentation</a>.
    </p>
    <p>
      The first is <b>fbUrl</b>. It is the equivalent of the built-in
      Django <b>url</b> tag, except that when used inside Facebook it
      produces an absolute link to the requested page within the
      context of the Facebook canvas. In fact, the code is copied
      directly from the Django implementation
      of <b>url</b>. <b>fbUrl</b> relies on the <b>fb</b> context
      variable, and the <b>fbReverse</b> function, as described above.
    </p>
    <pre class="codeSnippet">
<span class="keyword">from</span> django.template <span class="keyword">import</span> Node
<span class="keyword">class</span> <span class="type">FBURLNode</span>(Node):
    <span class="keyword">def</span> <span class="function-name">__init__</span>(<span class="py-pseudo-keyword">self</span>, view_name, args, kwargs):
        <span class="py-pseudo-keyword">self</span>.view_name = view_name
        <span class="py-pseudo-keyword">self</span>.args = args
        <span class="py-pseudo-keyword">self</span>.kwargs = kwargs

    <span class="keyword">def</span> <span class="function-name">render</span>(<span class="py-pseudo-keyword">self</span>, context):
        fb = template.Variable('<span class="string">fb</span>').resolve(context)
        <span class="keyword">if</span> fb:
            reverseFunc = fbReverse
        <span class="keyword">else:</span>
            reverseFunc = django.core.urlresolvers.reverse

        <span class="keyword">from</span> django.core.urlresolvers <span class="keyword">import</span> reverse, NoReverseMatch
        args = [arg.resolve(context) <span class="keyword">for</span> arg <span class="keyword">in</span> <span class="py-pseudo-keyword">self</span>.args]
        kwargs = <span class="py-builtins">dict</span>([(smart_str(k,'<span class="string">ascii</span>'), v.resolve(context))
                       <span class="keyword">for</span> k, v <span class="keyword">in</span> <span class="py-pseudo-keyword">self</span>.kwargs.items()])
        <span class="keyword">try:</span>
            <span class="keyword">return</span> reverseFunc(<span class="py-pseudo-keyword">self</span>.view_name, args=args, kwargs=kwargs)
        <span class="keyword">except</span> NoReverseMatch:
            <span class="keyword">try:</span>
                project_name = settings.SETTINGS_MODULE.split('<span class="string">.</span>')[0]
                <span class="keyword">return</span> reverseFunc(project_name + '<span class="string">.</span>' + <span class="py-pseudo-keyword">self</span>.view_name,
                                   args=args, kwargs=kwargs)
            <span class="keyword">except</span> NoReverseMatch:
                <span class="keyword">return</span> ''

<span class="keyword">def</span> <span class="function-name">fbUrl</span>(parser, token):
    """<span class="string">
    Just like Django's url tag, except also works inside Facebook.
    "</span>""
    bits = token.contents.split('<span class="string"> </span>', 2)
    <span class="keyword">if</span> <span class="py-builtins">len</span>(bits) &lt; 2:
        <span class="keyword">raise</span> TemplateSyntaxError("<span class="string">'%s' takes at least one argument</span>"
                                  "<span class="string"> (path to a view)</span>" % bits[0])
    args = []
    kwargs = {}
    <span class="keyword">if</span> <span class="py-builtins">len</span>(bits) &gt; 2:
        <span class="keyword">for</span> arg <span class="keyword">in</span> bits[2].split('<span class="string">,</span>'):
            <span class="keyword">if</span> '<span class="string">=</span>' <span class="keyword">in</span> arg:
                k, v = arg.split('<span class="string">=</span>', 1)
                k = k.strip()
                kwargs[k] = parser.compile_filter(v)
            <span class="keyword">else:</span>
                args.append(parser.compile_filter(arg))
    <span class="keyword">return</span> FBURLNode(bits[1], args, kwargs)
fbUrl = register.tag(fbUrl)
                        
</pre>
    <p>
      Next is <b>fbName</b>. This is to provide the same functionality
      as Facebook's <i>fb:name</i> FBML tag, except also useable
      outside of Facebook.
    </p>
    <p>
      The basic use of it looks like <i>{% fbName user %}</i>, where
      "user" is a template variable containing the user whose name to
      display. Then you can add options like <i>linked=false</i>,
      or <i>useyou=false</i>, as described in the Facebook
      documentation.
    </p>
    <p>
      Some differences from the FBML version are:
    </p>
    <ul>
      <li>
	<i>shownetwork</i>, <i>ifcantsee</i>, and <i>subjectid</i> are
	ignored outside of Facebook.
      </li>
      <li>
	<i>linked</i> behaves slightly differently. Outside of
	Facebook, or if its value is set to <i>internal</i>, the
	user's name will be linked to an app-specific profile
	page. The view for this page is specified in the
	variable <b>userProfileView</b>. The view is expected to take
	one parameter: the user ID.
      </li>
    </ul>
    <p>
      Here is the code:
    </p>
    <pre class="codeSnippet">
userProfileView = '<span class="string">userProfile</span>'
<span class="keyword">class</span> <span class="type">FbNameNode</span>(Node):
    <span class="keyword">def</span> <span class="function-name">__init__</span>(<span class="py-pseudo-keyword">self</span>, user, args, kwArgs):
        <span class="py-pseudo-keyword">self</span>.user = template.Variable(user)
        <span class="py-pseudo-keyword">self</span>.args = args
        <span class="py-pseudo-keyword">self</span>.kwArgs = kwArgs

    <span class="keyword">def</span> <span class="function-name">getBoolArg</span>(<span class="py-pseudo-keyword">self</span>, name, default=<span class="py-pseudo-keyword">False</span>):
        val = <span class="py-pseudo-keyword">self</span>.kwArgs.get(name, default)
        <span class="keyword">if</span> <span class="py-builtins">type</span>(val) <span class="keyword">is</span> <span class="keyword">not</span> bool:
            <span class="keyword">return</span> (val.lower() == '<span class="string">true</span>')
        <span class="keyword">return</span> val

    <span class="keyword">def</span> <span class="function-name">render</span>(<span class="py-pseudo-keyword">self</span>, context):
        user = <span class="py-pseudo-keyword">self</span>.user.resolve(context)
        fb = template.Variable('<span class="string">fb</span>').resolve(context)
        loggedInUser = template.Variable('<span class="string">user</span>').resolve(context)
        request = template.Variable('<span class="string">request</span>').resolve(context)
        <span class="keyword">if</span> fb:
            fbUserId = UserFbId(user)
            <span class="keyword">if</span> fbUserId:
                <span class="comment"># In Facebook
</span>                internalLink = <span class="py-pseudo-keyword">False</span>
                ret = '<span class="string">&lt;fb:name uid="%s" </span>' % fbUserId
                <span class="keyword">for</span> item, val <span class="keyword">in</span> <span class="py-pseudo-keyword">self</span>.kwArgs.items():
                    <span class="keyword">if</span> item == '<span class="string">linked</span>' <span class="keyword">and</span> val == '<span class="string">internal</span>':
                        internalLink = <span class="py-pseudo-keyword">True</span>
                        ret += '<span class="string">linked="false" </span>'
                    <span class="keyword">else:</span>
                        <span class="keyword">if</span> <span class="py-builtins">type</span>(val) <span class="keyword">is</span> bool:
                            <span class="keyword">if</span> val:
                                val = '<span class="string">true</span>'
                            <span class="keyword">else:</span>
                                val = '<span class="string">false</span>'
                        ret += '<span class="string">%s="%s" </span>' % (item, val)
                ret += '<span class="string">/&gt;</span>'
                <span class="keyword">if</span> internalLink:
                    ret = '<span class="string">&lt;a href="%s"&gt;%s&lt;/a&gt;</span>' % (fbReverse(userProfileView, [user.id]), ret)
                <span class="keyword">return</span> mark_safe(ret)
        <span class="comment"># Not in Facebook
</span>        <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">useyou</span>', <span class="py-pseudo-keyword">True</span>) <span class="keyword">and</span> user == loggedInUser:
            <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">capitalize</span>'):
                ret = '<span class="string">You</span>'
            <span class="keyword">else:</span>
                ret = '<span class="string">you</span>'
            <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">possessive</span>'):
                ret += '<span class="string">r</span>'
            <span class="keyword">elif</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">reflexive</span>'):
                ret += '<span class="string">rself</span>'
            <span class="comment"># ES: How to handle subjectid?
</span>        <span class="keyword">else:</span>
            ret = UserDisplayName(user)
            <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">firstnameonly</span>'):
                ret = user.first_name <span class="keyword">or</span> user.username
            <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">lastnameonly</span>'):
                ret = user.last_name <span class="keyword">or</span> user.username
            <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">possessive</span>'):
                ret += "<span class="string">'s</span>"
        <span class="keyword">if</span> <span class="py-pseudo-keyword">self</span>.getBoolArg('<span class="string">linked</span>', <span class="py-pseudo-keyword">True</span>) <span class="keyword">or</span> <span class="py-pseudo-keyword">self</span>.kwArgs.get('<span class="string">linked</span>', <span class="py-pseudo-keyword">None</span>) == '<span class="string">internal</span>':
            ret = '<span class="string">&lt;a href="%s"&gt;%s&lt;/a&gt;</span>' % \
                (makeReverse(request, userProfileView, args=[user.id]),
                 ret)
        <span class="keyword">return</span> mark_safe(ret)
            

@register.tag
<span class="keyword">def</span> <span class="function-name">fbName</span>(parser, token):
    '''<span class="string">
    Returns the name for the given user, based on the parameters.

    Acts much like the fb:name FBML tag, except can work in or out of
    Facebook.
    </span>'''
    <span class="keyword">try:</span>
        bits = token.split_contents()[1:]
    <span class="keyword">except</span> ValueError:
        <span class="keyword">raise</span> template.TemplateSyntaxError, "<span class="string">%r tag requires at least 1 argument: the user (%s)</span>" %\
            (token.contents.split()[0], token.split_contents())
    args = []
    kwArgs = {}
    <span class="keyword">for</span> b <span class="keyword">in</span> bits:
        <span class="keyword">if</span> '<span class="string">=</span>' <span class="keyword">in</span> b:
            name,val = b.split('<span class="string">=</span>', 1)
            kwArgs[name.strip()] = val.strip()
        <span class="keyword">else:</span>
            args.append(b.strip())
    <span class="keyword">if</span> <span class="py-builtins">len</span>(args) &lt; 1:
        <span class="keyword">raise</span> template.TemplateSyntaxError, "<span class="string">%r tag requires at least one argument: the user</span>" %\
            token.contents.split()[0]

    <span class="keyword">return</span> FbNameNode(args[0], args[1:], kwArgs)

<span class="keyword">def</span> <span class="function-name">UserFbId</span>(user):
    <span class="keyword">try:</span>
        <span class="keyword">return</span> UserProfile.objects.get(user=user).facebookId
    <span class="keyword">except</span> UserProfile.DoesNotExist:
        <span class="keyword">return</span> <span class="py-pseudo-keyword">None</span>
</pre>
    <h4>
      Moving on
    </h4>
    <p>
      I think that should be enough to work with for now. Next time,
      I'll discuss publishing stories to news feeds and all that
      social good stuff. Until then, please feel free to post any
      comments, questions, or improvements below.
    </p>
]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/django-facebook-3/feed</wfw:commentRss>
		</item>
			<item>
		<title>Making a Facebook app (with Django) - part 2: JavaScript and FBJS</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/django-facebook-2</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/django-facebook-2#comments</comments>
		<pubDate>Sun, 17 Aug 2008 14:45:10 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[facebook]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/?p=54</guid>
		<description><![CDATA[    
      Welcome to the second part in my series of posts about creating
      a Facebook application. I am using Django as my web development
      framework, but this post doesn't have much to do with Django,
  [...]]]></description>
			<content:encoded><![CDATA[    <p>
      Welcome to the second part in my series of posts about creating
      a Facebook application. I am using Django as my web development
      framework, but this post doesn't have much to do with Django,
      since it deals with the front end. In particular, it talks about
      how to write JavaScript that can work both in and out of
      Facebook.
    </p>
    <p>
      As I mentioned last time, Facebook lets developers use a subset
      of JavaScript, which they call FBJS. The FBJS is transformed on
      the fly into JavaScript as the page is loaded. All variables and
      functions you define or reference are prepended with a string
      like "a123456789_", including calls
      to <span class="code">document.getElementById</span>
      and <span class="code">setTimer</span> and the like. This is
      done in order to restrict what you can do with DOM elements, to
      avoid cross-site-scripting attacks and unwanted user-hostile
      behavior. <a href="http://wiki.developers.facebook.com/index.php/FBJS">FBJS is fairly well documented</a>, so if you plan to do
      some Facebook JavaScript development, you should start there.
    </p>
    <p>
      The biggest restriction that FBJS imposes is that you can no
      longer access the attributes of DOM elements directly, but must
      go through an abstraction API consisting of a series of setters
      and getters. For example, instead of saying something like
      <span class="code">imageEl.src = myImageUrl</span>, you instead need
      to call imageEl.setSrc(myImageUrl).
    </p>
<span id="more-54"></span>
    <p>
      This is not too big of an adjustment, if you are accustomed to
      writing raw JavaScript. It actually has some advantages, because
      FBJS abstracts away some of browser-dependent aspects into a
      standard API.
    </p>
    <p>
      The challenge, however, becomes apparent when you try debugging
      your JavaScript. With Internet Explorer and its horrendously
      useless error messages and debugging facilities, it's pretty
      much hopeless, since the line numbers don't correspond to line
      numbers in your source code. However, even with Firefox and the
      wonderful Firebug debugger, all the name mangling and rewriting
      makes tracking down problems more difficult. Ideally you would
      want to debug as much as possible <em>outside</em> of Facebook,
      before then testing on the real site.
    </p>
    <p>
      To make this easier, I developed some useful code to simulate as
      much as possible the FBJS environment when running locally or
      outside of Facebook. The idea was to re-create all the accessor
      functions on all DOM objects. You can <a href="http://www.chickenwingsoftware.com/static/fbHelper.js">download the file
      here</a>. You're free to use it, but please don't link directly to
      the file on my site - copy it to your site and use it there. And
      read the description below first.
    </p>
    <p>
      I considered several different approaches before settling on the
      final version. I considered trade-offs of performance and
      possible side-effects. Rejected approach number one was to
      modify Object.prototype to contain the new getters and
      setters. This would have been the fastest, but can cause
      problems with code like the following:
    </p>
    <pre>
<span class="keyword">for</span> (var param <span class="type">in</span> <span class="variable-name">obj</span>) {
  <span class="comment">// Do something with obj[param]
</span>}

</pre>
    <p>
      This would end up enumerating all of the added functions as well
      as the properties of the object in question. This is why it is
      generally considered a bad idea to modify Object.prototype.
    </p>
    <p>
      The second rejected approach was to create a wrapper class with
      all the accessor functions, that performs the requisite getting
      and setting on a member variable containing the actual DOM
      object. This one could have worked well, but it would have been
      a lot of code to implement, and would have made debugging with
      Firebug more difficult because of the added layer of
      indirection.
    </p>
    <p>
      The approach I settled on was to simply provide wrapper
      functions for document.getElementById and
      document.createElement, and for each element that is "getted" or
      created, to copy in the required member functions. This has a
      perfomance penalty during the getting or creation of elements,
      but the calls themselves are speedy. There is likely to be a lot
      more manipulation than getting, I would think.
    </p>
    <h3>
      How to use this code
    </h3>
    <p>
      To use this code, just reference this file from your web page
      before any of your own JavaScript, but only when you are outside
      of Facebook. Make sure to call fbGetEl and fbCreateEl instead of
      document.getElementById and document.createElement. In the
      version of your page that is intended to be run inside of
      Facebook, include this code:
    </p>
        <pre>
<span class="type">function</span> <span class="function-name">fbGetEl</span>(elId) {
  <span class="keyword">return</span> document.getElementById(elId);
}
<span class="type">function</span> <span class="function-name">fbCreateEl</span>(elType) {
  <span class="keyword">return</span> document.createElement(elType);
}
</pre>
    <p>
      Alternatively, if you are feeling a little tricky, you can add
      in some code like this to the end of fbHelper.js
    </p>
        <pre>
document.getElementById = fbGetEl;
document.createElement = fbGetEl;
</pre>
    <p>
      Then you can continue to use document.getElementById and
      document.createElement. (Note that I have not tested this
      approach.)
    </p>
    <p>
      I hope this comes in handy for some people.
    </p>
    <h3>
      More FBJS tips
    </h3>
    <h4>
      Beware of the cache!
    </h4>
    <p>
       If you make a change to your .js file and
      don't notice any difference in your app's behavior, chances are
      you are getting bitten by the cache bug. To get around this,
      Facebook recommends adding "cache-breaker" code to the URL. For
      example, instead of linking to "myFile.js", link to
      "myFile.js?v=2". The extra text from the question mark on is
      usually ignored by web servers for static files. In my Django
      context processor, I have code like the following:
    </p>
    <pre>
<span class="keyword">from</span> svnVersion <span class="keyword">import</span> svnVersion

<span class="comment"># Add this function to your TEMPLATE_CONTEXT_PROCESSORS
# list in settings.py
</span><span class="keyword">def</span> <span class="function-name">fbContextProcessor</span>(request):
    <span class="keyword">return</span> { <span class="comment"># Add any other template vars you want here.
</span>        '<span class="string">cacheBreaker</span>':'<span class="string">?v=%s</span>' % svnVersion }

</pre>
    <p>
      Then in my template, whenever a have a link, I do something like:
    </p>
    <pre>
&lt;a href="http://example.com/myFile.js{{ cacheBreaker }}"&gt;Click here!&lt;/a&gt;
    </pre>
      Then every time I update my code, I run a shell script that does
      something like:
    </p>
    <pre>
      #!/bin/sh
      echo "svnVersion = '`svnversion -n .`'" > svnVersion.py
    </pre>

    <p>
      That way, every time I update my code, I make sure to break the
      Facebook cache. Alternatively, you could call svnversion
      straight from Python.
    </p>
    <h4>
       console.log is your friend! 
    </h4>
    <p>
     This is true in general when
      JavaScript programming, but especially so with Facebook. Take
      the time to put in some extra logging messages while you're
      developing, and testing and debugging will be much easier. And
      read up on Firebug as much as possible.
    </p>
    <h4>
      Check out the <a href="http://wiki.developers.facebook.com/index.php/FBJS/Animation">Animation library</a>.
    </h4>
    <p>
      It's pretty cool, and it works outside of Facebook, too.
    </p>
    <h4>
      Beware the limitations.
    </h4>
    <p>
      Facebook only allows up to five external
      script files, so keep that in mind. You may unfortunately have
      to combine two or more .js files into one to work around this
      restriction. Also, FBJS doesn't run on profile pages until the
      user has interacted with the app.
    </p>]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/django-facebook-2/feed</wfw:commentRss>
		</item>
			<item>
		<title>Making a Facebook app (with Django) - part 1: The Perils</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/django-facebook-1</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/django-facebook-1#comments</comments>
		<pubDate>Fri, 18 Jul 2008 19:03:11 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[django]]></category>

		<category><![CDATA[facebook]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/?p=19</guid>
		<description><![CDATA[Facebook made a splash a year or two ago when they opened up their API. Now developers could write applications that integrate with the site. Instantly, users -- many of whom had fled to Facebook from a spam-filled MySpace -- were inundated with Requests to battle ninjas and News Items bearing obscene pictures. To Facebook's [...]]]></description>
			<content:encoded><![CDATA[Facebook made a splash a year or two ago when they opened up their API. Now developers could write applications that integrate with the site. Instantly, users -- many of whom had fled to Facebook from a spam-filled MySpace -- were inundated with Requests to battle ninjas and News Items bearing obscene pictures. To Facebook's credit, they did clamp down and put some restrictions on what apps can do. A few entertaining or useful applications have risen to the top, and the potential of the API is ready to be exploited. It's not an easy task for a developer, however.

In the next few Chicken Scratches posts, I'll talk about my experience developing two Facebook apps from scratch: <a href="http://apps.facebook.com/limericks">The Limerick Book, </a>a site that works both in and outside of Facebook and allows users to share and rate Limericks, and <strong>Play Scopa</strong>, a traditional Italian card game that users can play against each other in realtime (this one is not yet launched to the public).

This first post discusses some of the difficulties I have run into. In the next couple posts, I'll discuss how I dealt with them. First, to set the scene:
<h4>My setup</h4>
I am using the <a href="http://www.djangoproject.com">Django web framework</a> for my backend development, the latest SVN version running with Python version 2.4 on a shared host at <a href="http://www.dreamhost.com">Dreamhost</a>. In fact, it's the same server I use for this site and weblog. To connect Django to Facebook, I am using the nice <a href="http://wiki.developers.facebook.com/index.php/PythonPyFacebookTutorial">PyFacebook</a> library, which is pretty mature, though I had to modify the code to support some of the latest features of the Facebook API.

And now, on to the perils.
<span id="more-19"></span>
<h4>Peril 1: Birds of a Feather</h4>
The first peril I ran into is just what I described in this post's introduction: there are a lot of really crappy Facebook apps out there, and there is a very real danger of the product I worked so hard on being associated with all kinds of toy apps, spammy apps, buggy apps, or just poorly designed apps. This is similar to what it was like programming in JavaScript in its early days: nobody thought anyone could do "serious" programming in it. Luckily, Google changed all that with their Maps. The Facebook platform is still struggling with this stigma. Unfortunately, it is a fairly well deserved stigma, as you can see by browsing the Developers Forum archives.  There are a lot of amateur or inexperienced developers who think, "hey, that looks cool. I wanna write an app." It's tough to find anyone on the forum who can answer a legitimate question, and hard to find the answers through all the noise.
<h4>Peril 2: Layers of indirection</h4>
Debugging can be very difficult. The major added difficulty in debugging a Facebook app is simply that the app has to run on Facebook. In typical web development, you can usually edit and test right on your local machine, or at least on your local network. I have come up with some tricks, which I will document in future posts, to enable as much local testing as possible, but much of your time will be spent debugging interactions between users, between your app and users, and between your app and the Facebook platform. This requires a tedious cycle of edit, upload, test; edit, upload, test; edituploadtest edituploadtestedituploadtest, and so on.

Developing client-side scripting is especially difficult. You need to write in a limited variant of JavaScript, called FBJS, which is translated dynamically to JavaScript, but with all kinds of name-mangling and pre-processing, so line numbers don't match up and all variable names are prepended with a whole bunch of cruft. If you think Internet Explorer's error messages are cryptic <em>now</em>, you ain't seen nothing yet!
<h4>Peril 3: You're at their mercy</h4>
The Facebook API is still pretty new, and Facebook itself is still a young company. There are inevitably going to be growing pains, and in fact, as I write this, Facebook's backend servers are experiencing some major network issues, causing many apps to not function properly. It can certainly be frustrating not knowing whether something is failing due to a local bug or a problem with the API.

In addition, APIs, policies, and presentation are constantly changing. Can I post a profile message today? Can I send an email? To which users? Better check the rules, because they've probably changed. It is of course Facebook's playground, so it is their right to change the rules as much as they want, and we as developers just have to be ready to deal with that. It's a fact of life, and it's another unique challenge of developing a Facebook app.
<h4>Peril 4: It's different</h4>
Anybody who has developed web software for any length of time has built up a library of reusable "stuff:" page templates, JavaScript libraries, development patterns. Unfortunately, many of these things are useless when developing for Facebook. The markup language, FBML, is almost, but not quite, HTML. The front-end scripting language, FBJS, is almost, but not quite, JavaScript, and so on. It requires re-learning or re-implementing a lot of the standard tips and tricks.
<h4>But I'm doing it anyway</h4>
Well, what can I say? Developing for Facebook is fun. It's very visual, and it's rewarding to share what you've created with your online "friends." Stay tuned for more posts on my development experience, including some of the more technical issues and how I dealt with them, using Python and Django.]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/django-facebook-1/feed</wfw:commentRss>
		</item>
			<item>
		<title>Auto-closing Django template tags in Emacs</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/auto-closing-django-tags</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/auto-closing-django-tags#comments</comments>
		<pubDate>Mon, 07 Jul 2008 15:11:16 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[django]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[python]]></category>

		<category><![CDATA[xemacs]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/?p=18</guid>
		<description><![CDATA[    
      I have written two previous articles about how I edit Django template files in Emacs and XEmacs.
      Here is
      How
      I edit Django templates. And here
     [...]]]></description>
			<content:encoded><![CDATA[    <p>
      I have written two previous articles about how I edit Django template files in Emacs and XEmacs.
      Here is
      <a href="http://www.chickenwingsoftware.com/scratches/python/how-i-edit-django-templates">How
      I edit Django templates</a>. And here
      is <a href="http://www.chickenwingsoftware.com/scratches/python/more-on-editing-django-templates-in-xemacs">More
	on editing Django templates in XEmacs</a>. Here today is another
      little tip that can be used in conjunction with those two other
      posts or independently.
    </p>
    <p>
      Django templates involve a lot of punctuation. Between the angle
      brackets and slashes of HTML and the curly braces and percent
      signs of the Django template language, it's enough to make your
      pinky fingers hurt just thinking about it. Therefore any little
      trick to reduce some of this typing burden can be
      helpful. Presented here is some Emacs Lisp code to provide
      auto-closing of Django template tags. So even if you still have
      to type things like <b>curly-brace percent-sign space ifequal blah
      blah2 percent-sign close-curly-brace</b>, you won't have to type the
      <b>{% endifequal %}</b>. (Of course, if you're using
      the <em>abbrev</em> tips I gave previously, you won't even need
      to type the opening tag very often, but sometimes you still do.)
    </p>
<span id="more-18"></span>
    <h4>The code</h4>
    <p>
      Ok, here it is. You should be able to copy and paste this into
      your .emacs file. Then anywhere in your template file, simply call
      the new function <b>django-close-tag</b> and it will find the
      last open tag and close it. In the sample code below, I have it
      bound to <em>C-c]</em>; that is, hold down the control key and
      press C, then let go of the control key and press the right
      square bracket. Of course, you can change the code to bind the
      function to any key combination you want.
    </p>
    <pre>
(<span class="keyword">defvar</span> <span class="variable-name">django-closable-tags</span>
  '("<span class="string">for</span>" "<span class="string">block</span>" "<span class="string">comment</span>" "<span class="string">filter</span>" "<span class="string">ifchanged</span>" "<span class="string">ifequal</span>"
    "<span class="string">ifnotequal</span>" "<span class="string">spaceless</span>" "<span class="string">if</span>"))

(<span class="keyword">defvar</span> <span class="variable-name">django-tag-re</span>
  (concat "<span class="string">{%\\s *\\(end\\)?\\(</span>"
          (mapconcat 'identity django-closable-tags "<span class="string">\\|</span>")
          "<span class="string">\\)[^%]*%}</span>"))

(<span class="keyword">defun</span> <span class="function-name">django-find-open-tag</span> ()
  (<span class="keyword">if</span> (search-backward-regexp django-tag-re nil t)
      (<span class="keyword">if</span> (match-string 1) <span class="comment">; If it's an end tag
</span>          (<span class="keyword">if</span> (not (string= (match-string 2) (django-find-open-tag)))
              (error "<span class="string">Unmatched Django tag</span>")
            (django-find-open-tag))
        (match-string 2)) <span class="comment">; Otherwise, return the match
</span>    nil))

(<span class="keyword">defun</span> <span class="function-name">django-close-tag</span> ()
  (interactive)
  (<span class="keyword">let</span> ((open-tag (<span class="keyword">save-excursion</span> (django-find-open-tag))))
    (<span class="keyword">if</span> open-tag
        (insert "<span class="string">{% end</span>" open-tag "<span class="string"> %}</span>")
      (error "<span class="string">Nothing to close</span>"))))

    
(<span class="keyword">define-key</span> <span class="variable-name">html-mode-map</span> "<span class="doc-string">\C-c]</span>" 'django-close-tag)


    </pre>            

    <h4>How it works</h4>
    <p>
      The
      variable <span class="function-name">django-closable-tags</span> is
      a list of all the Django tags that require closing. As more tags
      get added, this list can be expanded.
    </p>
    <p>
      The meat of the work is done
      in <span class="function-name">django-find-open-tag</span>. The
      first thing it does is search backwards through the buffer for
      the last Django tag. If the last tag is an end-tag, the function
      calls itself recursively to skip over that block. This continues
      until it finds an unclosed tag, which it then returns as a string.
    </p>
    <p>
      The function you actually call
      is <span class="function-name">django-close-tag</span>. This
      calls <span class="function-name">django-find-open-tag</span> to
      find a tag to close, then inserts the appropriate end-tag text.
    </p>
    <p>
      Hopefully that is pretty straightforward. The only tricky part
      for a Lisp newbie could be the use of recursion. For a situation
      like this, recursion is the perfect approach. In a sense, the
      function calls build a list of closed tags on the call-stack,
      which then unravels one at a time until an unmatched tag is
      found.
    </p>
    <p>
      Other than that, there is some pretty
      straightforward <a href="http://www.gnu.org/software/emacs/manual/html_node/emacs/Regexps.html">regular
      expression matching</a>. These are extremely powerful, and you
      should read up on them if you haven't already.
    </p>
]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/auto-closing-django-tags/feed</wfw:commentRss>
		</item>
			<item>
		<title>What&#8217;s Wrong with Flying: How the internet killed travel</title>
		<link>http://www.chickenwingsoftware.com/scratches/programming/whats-wrong-with-flying</link>
		<comments>http://www.chickenwingsoftware.com/scratches/programming/whats-wrong-with-flying#comments</comments>
		<pubDate>Sun, 22 Jun 2008 17:21:04 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[non-software]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[airlines]]></category>

		<category><![CDATA[expedia]]></category>

		<category><![CDATA[flying]]></category>

		<category><![CDATA[travel]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/?p=17</guid>
		<description><![CDATA[Air travel is in a downward spiral. Not literally of course, but clearly and consistently, and everybody knows it. The airlines know it, the airline employees know it, travel agents found out a while ago, and consumers sure as Hell know it. There are a lot of reasons for it, but this post talks about just one: Expedia.com.

Airlines claim the degradation in quality comes from increased competition. They say they are forced to cut corners and reduce features in order to stay competitive. Does this make sense?]]></description>
			<content:encoded><![CDATA[<p>
  Air travel is in a downward spiral. Not literally of course, but
  clearly and consistently, and everybody knows it. The airlines know
  it, the airline employees know it, travel agents found out a while
  ago, and consumers sure as Hell know it. There are a lot of reasons
  for it, but this post talks about just one: Expedia.com.
</p>
<p>
  Airlines claim the degradation in quality comes from increased
competition. They say they are forced to cut corners and reduce
features in order to stay competitive. Does this make sense?
</p>
<span id="more-17"></span>
<p>
  <h4>What about other industries?</h4>
  Well, if it did make sense, we might expect to see a similar pattern
  in other industries. There is certainly vigorous competition in the
  auto industry. Car companies struggle to meet their profit
  margins. And yet, over the last twenty years, cars have gotten more
  reliable, full of more features, and more fuel-efficient, when you
  compare similar models. Competition has hurt the auto workers and
  the bottom lines of the auto companies, but it has made the product
  itself <em>better</em> for the consumer.
</p>
<p>
  Why has this not happened in the airline industry? Cars and flights
  are clearly two very different products, but what is the difference
  that accounts for this discrepancy? The main factor, I believe, is
  the way people make their purchases. When buying a car, a potential
  purchaser reads all the specs, he goes to the dealership and
  test-drives the car and kicks the tires. When the same person buys
  an airline ticket, he types the itinerary into Expedia.com (or
  Travelocity, or Orbitz) and gets a list of flights, ordered by
  price. He picks the cheapest ticket and steels himself for a
  miserable experience.
</p>
<p>
  "Well," contend the airlines, "people only care about the price of a
  flight, so we sacrifice the customer experience to meet the demand
  for lower prices." They now even resort to secret fees for checked
  bags, in-flight meals, even headphones to watch the movie. This is a
  sneaky trick to manipulate the search engines. Those extra fees do
  not show up in the results page. I challenge the assertion that
  consumers only care about price to the exclusion of other factors. I
  think the real problem is that price is the only piece of
  information they are given.
</p>
<h4>The proof of the pudding</h4>
<p>
  Try it. Go to expedia.com. Fill out the query form. Notice there is
  no field for the number of checked bags, there is no field for
  whether you will be having a meal, there is no field for whether you
  will want to watch the movie. These are important factors that you
  would want to take into account, but you are not given the
  opportunity.
</p>
<p>
  Now submit your search. You're presented with a list of flights,
  with the price in big black numbers and the flight schedule. That's
  it. No information about leg room, customer satisfaction, on-time
  performance, in-flight food and entertainment options, cancellation
  policies, or baggage handling. If you had this information, you
  would take it into account, but since you don't, you have no choice
  but to base your decision on price alone.
</p>
<p>
  When I go to Amazon.com to buy a fifteen dollar book, I can read
  four pages of user reviews telling me about the book. When buying a
  600 dollar cross-country flight, I don't see a single user
  review. There is nowhere for me to enter a review if I want to. The
  technology is here. It's everywhere. Everywhere except the airline
  search engines. Why?
  <h4>Why?</h4>
<p>
  Maybe the airlines are scared to let the consumers have a
  voice. Maybe they don't want user reviews, because the reviews would
  be overwhelmingly negative. If so, they are wrong. They're not wrong
  about the tone of the reviews; they would certainly be mostly
  negative, at least at first. They are wrong about the effect the
  reviews would have. In the long run, providing a way for consumers
  to compare flights and airlines based on more than just price will
  make flying a more pleasant experience for all. It will allow
  airlines to compete based on quality, not just price. And it's smart
  business.
</p>
<p>
  Expedia, Travelocity, and Orbitz are not going to change. They are
  the Big Three of flight searches. They are cleaning up while the
  airlines are suffering. There is <em>not enough
  competition</em>. The only way we will see an improvement is if
  somebody else steps in and offers us an alternative way to shop for
  flights. Google, are you listening? Amazon? Apple? Give us more
  information. Please.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/programming/whats-wrong-with-flying/feed</wfw:commentRss>
		</item>
			<item>
		<title>Python templates - Django and Cheetah</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/python-templates-django-and-cheetah</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/python-templates-django-and-cheetah#comments</comments>
		<pubDate>Wed, 28 May 2008 19:14:40 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[cheetah]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/?p=16</guid>
		<description><![CDATA[My thoughts on two popular Python-based templating engines.]]></description>
			<content:encoded><![CDATA[    <p>
      When writing web applications, sooner or later (usually sooner),
      everybody is going to need a template
      language. String-interpolation just doesn't cut it. We
      need a way to write something that is <em>almost all</em> text
      (or HTML, or XML, or whatever), but with some dynamic pieces
      thrown in.
    </p>
    <p>
      Since this need is so universal, and the basic requirements are
      so easy to describe, many different groups of people have taken
      it upon themselves to create Yet Another Template Language. As
      developers, we can join the fray and roll our own, or we can
      wade through the myriad options available to us to find the one
      that meets our needs or philosophy. Those who use PHP or ASP
      pretty much have the choice made for them, since the languages
      themselves are glorified template processors. Python
      programmers have a lot more options.
    </p>
    <p>
      Here I'm just going to focus on the two Python templating
      languages I have used in real applications: <a href="http://www.cheetahtemplate.org/">Cheetah</a> and the
      <a href="http://www.djangoproject.com">Django</a> templating engine. (Django, of course, is more than
      just templates, but the template subsystem can be used
      independently.) I use and enjoy both of these, but there are
      significant differences that are worth comparing and
      contrasting, when deciding which to use for your particular
      needs. There are other comparisons out there, including <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=146606">one by
      the Benevolent Dictator for Life</a> himself (though that's a bit
      out of date and inaccurate). When choosing which to use, you
      should read as many opinions as you can, then make the decision
      yourself. Presented here are just my personal thoughts.
    </p>
    <span id="more-16"></span>
    <h4>
      Syntax
    </h4>
    <p>
      Both of these languages are plain-text based, rather than XML
      based or any other format. This means it's easy to use them for
      creating formats that are not XML or HTML, like text or even
      code. It also means it's easy to forget to close a tag or to
      create non-compliant pages, so always be sure to test with
      HTML-Tidy or something like that.
    </p>
    <p>
      The two languages are pretty similar in their syntax, and in my
      humble opinion, they're both pretty ugly.
    </p>
    <p>
      Django uses <span class="code">{{</span> and <span class="code">}}</span> to surround variables to be
      interpolated. These variables can be passed through a "filter" -
      a specially designed Python function - using a vertical bar
      |. One additional parameter can be passed to the filter using a
      colon :. So, for example, to include a floating-point variable,
      expressed with two digits after the decimal point, you would
      write something like: <span class="code">{{ myVar|floatformat:2
      }}</span>. There is a lot of punctuation there, but you do get
      used to it.
    </p>
    <p>
      Cheetah's variable interpretation looks more like how it's done
      in shell-scripting languages, with a dollar
      sign <span class="code">$</span> and optional
      brackets <span class="code">{</span>
      and <span class="code">}</span>, so it looks
      like <span class="code">$myVar</span>
      or <span class="code">${myVar}</span>.
    </p>
    <p>
      They both also allow some logic and flow control. Django has
      "tags" surrounded by {% and %}. Cheetah has "directives" that
      start with # and end with either another # or a newline. Cheetah
      also allows embedding pure Python using
      ASP-like <span class="code">&lt;%</span>
      and <span class="code">%&gt;</span>.
    </p>
    <p>
      Aesthetically, I prefer the dollar signs of Cheetah for
      variables, but prefer Django's approach to the logic
      sections. Either way, you'd better do some finger exercises to
      limber up before typing.
    </p>
    <h4>
      Philosophy
    </h4>
    <p>
      The major philosophical difference between Cheetah and Django
      templating is the amount of programming power  each
      system makes available in the templates themselves. In some
      circles, there is a priority placed on separation of content
      from code, often going so far as to delegate the tasks to
      different people. According to this philosophy, designers should
      work on the presentation, writing HTML and so on, and
      programmers should work on the logic and back-end behavioral
      aspects. In the real world, this is an impossible division,
      because the two are often so closely intertwined, and the very
      existence of template languages is an admission of this fact. A
      template is, by definition, a mixture of logic and content. The
      difference is in the extent of this mixture.
    </p>
    <p>
      Cheetah makes the whole Python interpreter available to the
      template writer. Django provides a simple set of "tags" and
      "filters," allowing limited functionality. These can be extended
      through "custom tags" and "custom filters." According to the
      Django documentation, this limitation is intentional, and is
      meant to limit the mixing of logic and content.
    </p>
    <p>
      In practice, the mixing happens either way. In Cheetah, it is
      easy to end up with a whole lot of Python code in the middle of
      your HTML file, which some would consider inappropriate. In
      Django, it is easy to wind up constructing pieces of HTML in
      your code files and passing them to the template processor as
      parameters. On one side, the Python infects the HTML; on the
      other side, the HTML infects the Python.
    </p>
    <p>
      Personally, I'm a one-man development team, so I have no problem
      with mixing logic and content, either philosophically or
      pragmatically. I find the Cheetah approach leads to less total
      code required, plus I'm a big believer in the "we're all adults
      here" approach to programming, so I prefer the power that
      Cheetah offers. However, I can understand if people want to
      limit what logic is available in templates, if they are meant to
      be edited by designers, marketers, or pointy-haired bosses. In
      that case, Django might make more sense.
    </p>
    <h4>
      What I use
    </h4>
    <p>
      As I said above, I use both. I use the Django templating system
      when I am working within the framework of Django. Django does
      allow you to use any templating system you like, but then you
      lose the power of the built-in generic views and context
      processors. I use Cheetah when I don't need all the power of the
      Django framework (the database access layer, URL dispatcher,
      authentication, etc.).
    </p>
    <p>
      I have a slight preference for Cheetah, but they're both very
      useful. I hope this post provides some help for those choosing
      between the two.
    </p>
]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/python-templates-django-and-cheetah/feed</wfw:commentRss>
		</item>
			<item>
		<title>This month&#8217;s Limerick contest: Basketball</title>
		<link>http://www.chickenwingsoftware.com/scratches/non-software/this-months-limerick-contest-basketball</link>
		<comments>http://www.chickenwingsoftware.com/scratches/non-software/this-months-limerick-contest-basketball#comments</comments>
		<pubDate>Mon, 12 May 2008 21:28:18 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[non-software]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/?p=15</guid>
		<description><![CDATA[
This month's contest over at limericks.chickenwingsoftware.com is Basketball: In this, the NBA playoff season, young man's fancy turns lightly to thoughts of hoops. Plus, the Celtics wear green!


A typical entry:


There once was a girl named Christine
Who had gotten the gambling gene
She said with some sadness,
"I lost at March Madness
Before it reached the sweet sixteen."



Sure, it's [...]]]></description>
			<content:encoded><![CDATA[<p>
This month's contest over at <a href="http://limericks.chickenwingsoftware.com">limericks.chickenwingsoftware.com</a> is Basketball: In this, the NBA playoff season, young man's fancy turns lightly to thoughts of hoops. Plus, the Celtics wear green!
</p>
<p>
A typical entry:
</p>

<pre>There once was a girl named Christine
Who had gotten the gambling gene
She said with some sadness,
"I lost at March Madness
Before it reached the sweet sixteen."
</pre>

<p>
Sure, it's no masterpiece, but it's fun. That's sort of the spirit of these contests. <a href="http://limericks.chickenwingsoftware.com">Head on over</a> to read more, rate them, or write your own.
</p>]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/non-software/this-months-limerick-contest-basketball/feed</wfw:commentRss>
		</item>
			<item>
		<title>CSS: A one act play</title>
		<link>http://www.chickenwingsoftware.com/scratches/uncategorized/css-a-one-act-play</link>
		<comments>http://www.chickenwingsoftware.com/scratches/uncategorized/css-a-one-act-play#comments</comments>
		<pubDate>Wed, 16 Apr 2008 12:43:09 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/uncategorized/css-a-one-act-play</guid>
		<description><![CDATA[<p><strong>Student</strong>: I just learned CSS and I'm very excited about it! It seems like a nice clean and logical way to do layout. Look, I can just say <strong>color:red</strong> and my text turns red.</p>
<p><strong>Guru</strong>: Oh yes, it's <em>soo</em> much better than using tables. Tables are bad, bad, bad! Never use tables!! You'll ruin us all!!!</p>
<p><strong>Student</strong>: Um, ok. No tables. But say, I have a question.</p>]]></description>
			<content:encoded><![CDATA[<p><strong>Student</strong>: I just learned CSS and I'm very excited about it! It seems like a nice clean and logical way to do layout. Look, I can just say <strong>color:red</strong> and my text turns red.</p>
<p><strong>Guru</strong>: Oh yes, it's <em>soo</em> much better than using tables. Tables are bad, bad, bad! Never use tables!! You'll ruin us all!!!</p>
<p><strong>Student</strong>: Um, ok. No tables. But say, I have a question. I'm trying to make my links look like buttons, but when I set a width on them nothing happens. What's <em>that</em> all about?</p>
<p><strong>Guru</strong>: Duh! Don't you know, you can't set a width on an inline element? You have to set <strong>display:block</strong> .</p>
<p><strong>Student</strong>: Ah, I see. So now I can set the width, but it just puts each link on a separate line by itself. What if I want three or four on the same line? They're not very wide after all. Could I maybe just use a ta-</p>
<p><strong>Guru</strong>: <em>Nooooo!</em> No tables, I said! Look, it's very easy and logical. Just set a fixed width and set <strong>float:left</strong> on all the items you want on the same line. Why would you not think of that?</p>
<p><strong>Student</strong>: Ah, of course, <strong>float:left</strong> . I guess that should have been obvious.</p>
<p><strong>Guru</strong>: Very good.</p>
<p><strong>Student</strong>: But wait.</p>
<p><strong>Guru</strong>: Now what is it?</p>
<p><strong>Student</strong>: Now my buttons are all squished up with the text that comes after them, even if I put a &lt;br&gt; tag after them. Why is it ignoring my &lt;br&gt; tag?</p>
<p><strong>Guru</strong>: Don't you know? You should never use the &lt;br&gt; tag! That's mixing layout with content. It doesn't work after floated elements, anyway. You have to make an empty &lt;div&gt;, put it after all the buttons, and set <strong>clear:both</strong> on it.</p>
<p><strong>Student</strong>: Ah, of course. Why didn't I think of that? We wouldn't want to have extra markup who's sole purpose is for layout, would we?</p>
<p><strong>Guru</strong>: Of course not. Empty divs and wrapper divs don't count. At least they're not tables.</p>
<p><strong>Student</strong>: Ok, now how do I center them?</p>
<p><strong>Guru</strong>: What?</p>
<p><strong>Student</strong>: I want to center my table of buttons. How do I do that?</p>
<p><strong>Guru</strong>: Well, first of all, it's not a "table" of buttons. Don't ever let me hear you use that word again. It's a "list" of buttons. Turn them into a list. Then it's very easy to center them. Set <strong>list-style-type:none</strong>. Then set <strong>display:table</strong>, but only in Firefox. You need to use conditional HTML comments for IE. Then set <strong>margin-left:auto</strong>. That's how we center things, of course. Oh yeah, and you have to enclose the whole thing in another wrapper &lt;div&gt;. Then set <strong>text-align:center</strong> on the wrapper. See, easy as pie. Aren't you glad we're not in the bad old days, when we used to have to write hacky HTML just to make the layout work?</p>
<p><strong>Student</strong> : Maybe I'll go into marketing. (<em>exit stage left</em>)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/uncategorized/css-a-one-act-play/feed</wfw:commentRss>
		</item>
			<item>
		<title>Let the Limericks Flow</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/let-the-limericks-flow</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/let-the-limericks-flow#comments</comments>
		<pubDate>Wed, 09 Apr 2008 19:58:39 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[django]]></category>

		<category><![CDATA[non-software]]></category>

		<category><![CDATA[python]]></category>

		<category><![CDATA[contest]]></category>

		<category><![CDATA[limericks]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/?p=11</guid>
		<description><![CDATA[We've created an online contest
To see who can rhyme the bestest
So come to our site
And prove you can write
The limerick that rates the highest.


Sorry, that's what happens when you try to write a limerick after four hours of reading technical specification documents. If you think you can do better, come to our limerick contest website [...]]]></description>
			<content:encoded><![CDATA[<pre>We've created an online contest
To see who can rhyme the bestest
So come to our site
And prove you can write
The limerick that rates the highest.
</pre>

<p>Sorry, that's what happens when you try to write a limerick after four hours of reading technical specification documents. If you think you can do better, come to our <a href="http://limericks.chickenwingsoftware.com">limerick contest website</a> and make an entry, read and rate other entries, and waste some time.</p>
<p>
For the developers, the contest site is hosted on Django with a MySQL backend.
</p>
<p>
There will be a new category of limerick writing contest every month. This month's theme is <strong>cheese</strong>. Enjoy!
</p>
<ul>
	<li><a href="http://limericks.chickenwingsoftware.com">Go to the limerick contest</a>.</li>
</ul>]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/let-the-limericks-flow/feed</wfw:commentRss>
		</item>
			<item>
		<title>More on editing Django templates in XEmacs</title>
		<link>http://www.chickenwingsoftware.com/scratches/python/more-on-editing-django-templates-in-xemacs</link>
		<comments>http://www.chickenwingsoftware.com/scratches/python/more-on-editing-django-templates-in-xemacs#comments</comments>
		<pubDate>Thu, 27 Mar 2008 13:58:32 +0000</pubDate>
		<dc:creator>Eddie Sullivan</dc:creator>
		
		<category><![CDATA[django]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[python]]></category>

		<category><![CDATA[xemacs]]></category>

		<guid isPermaLink="false">http://www.chickenwingsoftware.com/scratches/python/more-on-editing-django-templates-in-xemacs</guid>
		<description><![CDATA[This post addresses the age old question, "If (X)Emacs is so good, why can't it do the typing for me?" Well, the answer, of course, is it can. You just have to write a few lines of Lisp first. Or you can copy and paste the Lisp code off of sites like this one.]]></description>
			<content:encoded><![CDATA[    <p>
      Previously, I wrote about<a href="http://www.chickenwingsoftware.com/scratches/python/how-i-edit-django-templates"> how I set up syntax-highlighting for
      Django template files using MMM (multiple major modes) in
      XEmacs</a>. This entry builds on the previous one, so if you haven't
      read that one, I suggest doing so now.
    </p>
    <p>
      This post addresses the age old question, "If (X)Emacs is so
      good, why can't it do the typing for me?" Well, the answer, of
      course, is it can. You just have to write a few lines of Lisp
      first. Or you can copy and paste the Lisp code off of sites like
      this one. ;)
    </p>
    <h4>Introducing dynamic abbreviations</h4>
    <p>
      Pressing M-/, (that is, holding down the <b>Alt</b> key and
      pressing the forward slash key), runs the command
      <b>dabbrev-expand</b>, which tries to finish whatever word you
      are in the middle of typing. For example, if as I'm typing this
      entry, I type the letter <b>d</b>, then press <b>M-/</b>,
      "dabbrev-expand" shows up, because that was the last word I
      typed that started with <b>d</b>. If I then type <b>M-/</b>
      again, "dabbrev-expand" changes to "down". If I keep pressing
      it, the word cycles through different guesses for what I'm going
      for. If you've ever used VisualStudio, you may see some
      similarities to the Intellisense feature. I find I almost never
      type a whole word anymore, I've become so dependent on dynamic
      completion.
    </p>
    <h4>Templates and more abbreviations</h4>
    <p>
      <b>dabbrev-expand</b> is useful to avoid having to type long
      words over and over, but there are also longer patterns that
      seem to need typing frequently. Thats where the <b>tempo</b>
      package comes in handy, especially when combined with the
      <b>abbrev</b> library. Here is some <a href="http://www.emacswiki.org/cgi-bin/emacs-en/TempoMode">documentation for
      tempo</a>. Here is some <a href="http://www.cs.cmu.edu/cgi-bin/info2www?(emacs)Abbrevs">documentation for abbrev</a>.
    </p>
    <p>
      Essentially, <b>tempo</b> allows you to specify "templates," or
      blocks of standard text that can be parameterized as they are
      filled in: the same concept as Django templates, but meant for
      interactive use. From now on, I will say "tempo template" or
      "Django template" to avoid confusing the two types.
    </p>
    <p>
      <b>abbrev</b> lets you define your own pre-set abbreviations,
      which can be filled in automatically as you type or upon
      request. This can be combined with <b>tempo</b> to do some
      pretty powerful stuff very easily.
    </p>
    <p>
      I have a few Django-specific tempo templates and abbrevs set up
      in my XEmacs initialization file. For example, as soon as I type
      "{% block" followed by a space, the entire framework of a Django
      template block is filled in for me. I've also added a special
      menu to the menu bar for tempo templates I use frequently.
    </p>
    <p>
      HTML and templates can be very repetitive, so I've found this
      saves me a <em>lot</em> of typing.
    </p>
    <h4>My initialization file</h4>
    <p>
      Here is the subset of my ~/.xemacs/init.el file. This includes
      the <b>mmm-mode</b> stuff I discussed last time, as well as the
      tempo templates and abbrevs. I haven't put a tempo template for
      every possible Django template tag, just the ones I use most
      frequently. I'll leave further extension as an exercise for the
      reader.
    </p>
    <p>
      Also, some people don't like the expansion to happen
      automatically as they are typing. To turn this off, comment out
      the line that says "(setq abbrev-mode t)" by putting a semicolon
      (<span class="comment">;</span>) in front of it. Then you can
      manually expand tempo templates by pressing <b>C-\</b>
      (control-backslash).
    </p>
    <p>
      Once again, this is known to work in XEmacs version 21.4 on
      Windows XP. It will most likely work in other versions of
      XEmacs, and possibly in GNU Emacs.
    </p>
    <pre>
<span class="comment">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CSS-Mode
</span>(<span class="keyword">autoload</span> 'css-mode "<span class="doc-string">css-mode</span>" "<span class="doc-string">Mode for editing CSS files</span>" t)
(add-to-list 'auto-mode-alist '("<span class="string">\\.css\\'</span>" . css-mode))
(setq cssm-indent-function #'cssm-c-style-indenter)
(setq cssm-indent-level '2)

<span class="comment">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use hm--html-mode for files that end in .tmpl (Django templates)
</span>(add-to-list 'auto-mode-alist '("<span class="string">\\.tmpl\\'</span>" . hm--html-mode))

<span class="comment">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Multiple Major Modes.
</span>(<span class="keyword">require</span> '<span class="reference">mmm-vars</span>)
(<span class="keyword">require</span> '<span class="reference">mmm-mode</span>)
(<span class="keyword">require</span> '<span class="reference">mmm-sample</span>)
(setq mmm-global-mode 'maybe)

<span class="comment">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Custom MMM classes for Django templates
</span>(mmm-add-classes
 '((my-django-expr
    <span class="reference">:submode</span> python-mode
    <span class="reference">:face</span> mmm-declaration-submode-face
    <span class="reference">:front</span> "<span class="string">{%</span>"
    <span class="reference">:back</span> "<span class="string">%}</span>"
    <span class="reference">:include-front</span> t
    <span class="reference">:include-back</span> t)))

(mmm-add-classes
 '((my-django-var
    <span class="reference">:submode</span> python
    <span class="reference">:face</span> mmm-output-submode-face
    <span class="reference">:front</span> "<span class="string">{{</span>"
    <span class="reference">:back</span> "<span class="string">}}</span>"
    <span class="reference">:include-front</span> t
    <span class="reference">:include-back</span> t)))

(mmm-add-mode-ext-class nil "<span class="doc-string">\\.tmpl\\'</span>" 'embedded-css)
(mmm-add-mode-ext-class nil "<span class="doc-string">\\.tmpl\\'</span>" 'my-django-var)
(mmm-add-mode-ext-class nil "<span class="doc-string">\\.tmpl\\'</span>" 'my-django-expr)
(mmm-add-mode-ext-class nil "<span class="doc-string">\\.tmpl\\'</span>" 'html-js)

<span class="comment">;; Use different colors for different sub-modes.
</span>(setq mmm-submode-decoration-level 2)
<span class="comment">;; Make the code submode a little more readable.
</span>(set-face-background 'mmm-code-submode-face "<span class="doc-string">#EEEEFF</span>")


<span class="comment">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tempo templates and abbrevs
</span>
<span class="comment">;; Control-backslash to expand a tempo template manually.
</span>(global-set-key "<span class="doc-string">\C-\\</span>" 'tempo-complete-tag)

(<span class="keyword">require</span> '<span class="reference">adapt</span>) <span class="comment">; Must be done before hm--html for some reason.
</span>(<span class="keyword">require</span> '<span class="reference">hm--html-mode</span>) <span class="comment">; Needed for hm--html-mode-abbrev-table
</span>(<span class="keyword">require</span> '<span class="reference">tempo</span>) <span class="comment">; Tempo templates.
</span>

<span class="comment">;; Expands &lt;span into an HTML span with a class defined.
</span>(tempo-define-template "<span class="doc-string">span</span>"
                       '("<span class="string">&lt;span class=\"</span>" (p "<span class="string">Class? </span>") "<span class="string">\"&gt;</span>" r "<span class="string">&lt;/span&gt;</span>" &gt;)
                       "<span class="doc-string">&lt;span </span>"
                       "<span class="doc-string">Insert a template for a span with a class</span>")
(<span class="keyword">define-abbrev</span> <span class="function-name">hm--html-mode-abbrev-table</span>
  "<span class="doc-string">&lt;span</span>" "" 'tempo-template-span)

<span class="comment">;; Expands &lt;script into an HTML javascript block.
</span>(tempo-define-template "<span class="doc-string">script</span>"
                       '(&gt; "<span class="string">&lt;script type=\"text/javascript\"&gt;</span>" r "<span class="string">&lt;/script&gt;</span>" &gt; %)
                       "<span class="doc-string">&lt;script </span>"
                       "")
(<span class="keyword">define-abbrev</span> <span class="function-name">hm--html-mode-abbrev-table</span>
  "<span class="doc-string">&lt;script</span>" "" 'tempo-template-script)

<span class="comment">;; Expands a Django template block.
</span>(tempo-define-template "<span class="doc-string">block</span>"
                       '("<span class="string">{% block </span>" (p "<span class="string">Block name? </span>") "<span class="string"> %}</span>" p "<span class="string">{% endblock %}</span>")
                       "<span class="doc-string">{% block </span>"
                       "")
(<span class="keyword">define-abbrev</span> <span class="function-name">hm--html-mode-abbrev-table</span>
  "<span class="doc-string">{% block</span>" "" 'tempo-template-block)

<span class="comment">;; Expands a Django template if tag.
</span>(tempo-define-template "<span class="doc-string">django-if</span>"
                       '("<span class="string">{% if </span>" (p "<span class="string">Conditional? </span>") "<span class="string"> %}</span>" p "<span class="string">{% endif %}</span>")
                       "<span class="doc-string">{% if </span>"
                       "")
(<span class="keyword">define-abbrev</span> <span class="function-name">hm--html-mode-abbrev-table</span>
  "<span class="doc-string">{% if</span>" "" 'tempo-template-django-if)

<span class="comment">;; Expands a Django template for tag.
</span>(tempo-define-template "<span class="doc-string">django-for</span>"
                       '("<span class="string">{% for </span>"
                         (p "<span class="string">Variable? </span>") "<span class="string"> in </span>"
                         (p "<span class="string">List? </span>") "<span class="string"> %}</span>" p
                         "<span class="string">{% endfor %}</span>")
                       "<span class="doc-string">{% for </span>"
                       "")
(<span class="keyword">define-abbrev</span> <span class="function-name">hm--html-mode-abbrev-table</span>
  "<span class="doc-string">{% for</span>" "" 'tempo-template-django-for)

<span class="comment">;; Create a menu for inserting these templates.
</span>(<span class="keyword">defun</span> <span class="function-name">add-templates-menu</span> ()
  (interactive)
  (add-submenu nil '("<span class="string">Tem%_plates</span>"
                     ["<span class="string">{%% %_block %%}</span>" tempo-template-block]
                     ["<span class="string">{%% %_if %%}</span>" tempo-template-django-if]
                     ["<span class="string">{%% %_for %%}</span>" tempo-template-django-for]
                     ["<span class="string">&lt;%_span&gt;</span>" tempo-template-span]
                     ["<span class="string">&lt;s%_cript&gt;</span>" tempo-template-script])))

<span class="comment">;; When entering hm--html-mode, turn on abbrevs and add the template menu.
</span>(add-hook 'hm--html-mode-hook
          (<span class="keyword">lambda</span> ()
            (setq abbrev-mode t)
            (add-templates-menu)))
            
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.chickenwingsoftware.com/scratches/python/more-on-editing-django-templates-in-xemacs/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
