<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <title>Glen Smith (Gravl Related category)</title>
  <link rel="alternate" href="http://blogs.bytecode.com.au:80/categories/Gravl" />
  <subtitle>Glen Smith (Gravl Related category)</subtitle>
  <entry>
    <title>Gravl: Funky Image Preview and Inplace Editing</title>
    <link rel="alternate" href="http://blogs.bytecode.com.au:80/glen/2008/01/29/gravl--funky-image-preview-and-inplace-editing.html" />
    <author>
      <name />
    </author>
    <updated>2008-01-29T11:06:00Z</updated>
    <published>2008-01-29T11:06:00Z</published>
    <summary type="html">&lt;p&gt;
I've been having fun implementing my first cut at Image/File management for Gravl, and along the way I've learnt about &lt;a href="http://www.huddletogether.com/projects/lightbox2/"&gt;LightBox&lt;/a&gt; and the Scriptaculous &lt;a href="http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor"&gt;InPlaceEditor&lt;/a&gt; stuff.
&lt;/p&gt;
&lt;p&gt;
First up, I needed a way of doing an image preview, so I could see what all of those PNGs really meant. Lightbox gives you a great way of "overlaying" the preview image as a popup without the need to refresh the page. Very snazzy. I followed the &lt;a href="http://www.huddletogether.com/projects/lightbox2/"&gt;instructions&lt;/a&gt; and I was up and running in a few minutes.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="images/2008/gravlImagePreview.png" alt="Gravl Image Preview with Lightbox"/&gt;
&lt;/p&gt;
&lt;p&gt;
Everything will make more sense with a little &lt;a href="images/2008/gravlImages.mov"&gt;screencast&lt;/a&gt; (1.5mb)
&lt;/p&gt;
&lt;p&gt;
Next stop was handling image renaming. I was really after a way to edit the actual filename inplace. Enter &lt;a href="http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor"&gt;InPlaceEditor&lt;/a&gt;. This call lets you specify the id of the object you want to edit inplace, along with the url to do the ajax call against (which will get passed the user's updated value in a &lt;code&gt;params.value&lt;/code&gt;). If all is well, you return the changed value and life continues. 
&lt;/p&gt;
&lt;p&gt;
&lt;img style="border: 1px solid lightgray; margin: 1em" src="images/2008/gravlInplaceRename.png" alt="Gravl Inplace Rename with Scriptaculous"/&gt;
&lt;/p&gt;
&lt;p&gt;
Well that was quite a lunch hour. I have a basic image management solution to keep me going so I can now upload the images for this blog entry without needing an SCP client!
&lt;/p&gt;
&lt;p&gt;
Happy Previewing in Grails!
&lt;/p&gt;</summary>
    <dc:date>2008-01-29T11:06:00Z</dc:date>
  </entry>
  <entry>
    <title>Gravl: Autocompleting and Funky Timelines</title>
    <link rel="alternate" href="http://blogs.bytecode.com.au:80/glen/2008/01/23/gravl--autocompleting-and-funky-timelines.html" />
    <author>
      <name />
    </author>
    <updated>2008-01-23T02:24:00Z</updated>
    <published>2008-01-23T02:24:00Z</published>
    <summary type="html">&lt;p&gt;&#xD;
If you haven't had a chance to play with the &lt;a href="http://www.grails.org/RichUI+Plugin"&gt;RichUI plugin&lt;/a&gt;, you've got to take it for a spin - it's just fantastic. I've been meaning to implement autocomplete for Gravl tags on new blog entries, and with the RichUI plugin (which uses &lt;a href="http://developer.yahoo.com/yui/autocomplete"&gt;Yahoo Autocomplete&lt;/a&gt; under the hood) it's a total snack.&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
First you declare the RichUI taglibs in your gsp file (note: I'm using the undocumented &lt;i&gt;delimChar&lt;/i&gt; setting so that I separate multiple tags with a space):&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
&amp;lt;resource:autoComplete skin="default" /&amp;gt;&#xD;
&amp;lt;richui:autoComplete name="tagList" delimChar=" " style="width: 100%"  &#xD;
    action="${createLinkTo(dir: params.blog+ '/blog/tagcomplete')}"/&amp;gt;&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
Then you add yourself a backend action that returns some xml representing the items for the list. I filter the tags by hand because I want to keep everything case insensitive, but your might be able to do the lot in a dynamic finder.&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
def tagcomplete = {&#xD;
    Blog blogObj = Blog.findByBlogid(params.blog)&#xD;
    def queryRegex = "(?i)${params.query}" // case insensitive...&#xD;
    def tags = blogObj.tags.findAll { tag -&gt; tag.name =~ queryRegex }&#xD;
    render(contentType: "text/xml") {&#xD;
        results() {&#xD;
            tags.each {t -&gt;&#xD;
                result() {&#xD;
                    name(t.name)&#xD;
                }&#xD;
            }&#xD;
        }&#xD;
    }&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
And you've got yourself an autocomplete for tags...&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
&lt;img src="images/2008/gravlAutocomplete.png" alt="Gravl autocomplete in action"/&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Not that we've got a nifty tag autocomplete, it's time to take the timeline virtualisation tag for a spin... One the way into the timeline populate the start date for your entries...&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
def timeline = {&#xD;
        Blog blogObj = Blog.findByBlogid(params.blog)&#xD;
        def entries = BlogEntry.findAllByBlogAndStatus(blogObj, "published", [ sort: 'created', order: 'asc'])&#xD;
&#xD;
        return [ blogObj: blogObj, startDate: entries[0]?.created  ]&#xD;
    }&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
Then create the tags in your page to kick things off..&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
&amp;lt;resource:timeline /&amp;gt;&#xD;
&#xD;
&amp;lt;richui:timeline style="height: 500px; border: 1px solid #aaa" startDate="${startDate}" datasource="${createLinkTo(dir: params.blog+ '/timelineData')}" /&amp;gt;&#xD;
        &amp;lt;g:javascript&amp;gt;&#xD;
            initTimeline();&#xD;
        &amp;lt;/g:javascript&amp;gt;&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
Which will callback to render the data on a timeline...&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
&#xD;
def timelineData = {&#xD;
&#xD;
        def baseUri = request.scheme + "://" + request.serverName +&#xD;
                 (request.serverPort != 80 ? ":" +  request.serverPort : "") +&#xD;
                 grailsAttributes.getApplicationUri(request)&#xD;
&#xD;
        Blog blogObj = Blog.findByBlogid(params.blog)&#xD;
        def entries = BlogEntry.findAllByBlogAndStatus(blogObj, "published", [ sort: 'created', order: 'asc'])&#xD;
&#xD;
        println "Timeline rendering for ${entries.size()} entries"&#xD;
&#xD;
        render(contentType: "text/xml") {&#xD;
            SimpleDateFormat sdf = new SimpleDateFormat("MMM dd yyyy HH:mm:ss", Locale.US)&#xD;
&#xD;
            data() {&#xD;
                entries.each { entry -&gt;&#xD;
                    event(start: sdf.format(entry.created),&#xD;
                            title: entry.title,&#xD;
                            link: baseUri + entry.toPermalink(),&#xD;
                            "" )&#xD;
                    }&#xD;
            }&#xD;
        }&#xD;
    }&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
And you have one funky looking timeline... (click on the &lt;a href="http://blogs.bytecode.com.au/glen/timeline"&gt;Timeline&lt;/a&gt; link on the right gutter if you want to play with the live one)&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
&lt;img src="images/2008/gravlTimeline.png" alt="Gravl timeline in action"/&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Well thats enough UI work for one day... Huge props to Andreas for an outstanding plugin!&#xD;
&lt;/p&gt;</summary>
    <dc:date>2008-01-23T02:24:00Z</dc:date>
  </entry>
  <entry>
    <title>Gravl: Fulltext Search Redux with Searchable</title>
    <link rel="alternate" href="http://blogs.bytecode.com.au:80/glen/2008/01/22/gravl--fulltext-search-redux-with-searchable.html" />
    <author>
      <name />
    </author>
    <updated>2008-01-22T08:54:00Z</updated>
    <published>2008-01-22T08:54:00Z</published>
    <summary type="html">&lt;p&gt;&#xD;
Thanks to a fantastic tip from Karl on yesterday's post, I've got the searchable plugin up and running on my Grails-RC3 install! Here's the fix from Karl:&#xD;
&lt;/p&gt;&#xD;
&lt;pre style="border: 1px dotted black; padding: 3px;"&gt;&#xD;
i‘ve got a working Searchable-0.4-SNAPSHOT wo. npe, this is working with the RCs too.&#xD;
In the plugin‘s java directory u need to slightly modify the &#xD;
org/codehaus/groovy/grails/plugins/searchable/compass/SearchableCompassUtils.java file.&#xD;
&#xD;
Here is the “patched” version:&#xD;
&lt;a href="http://rafb.net/p/prsZip92.html"&gt;http://rafb.net/p/prsZip92.html&lt;/a&gt; (nopaste url)&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
So I patched my version, and I'm up and running. Getting Gravl converted over was a snack. Basically, I just had to remove all my custom code! And, of course, add the magic &lt;code&gt;static searchable=true&lt;/code&gt; to my &lt;code&gt;BlogEntry&lt;/code&gt; object.&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
After I patched the plugin, I ran &lt;code&gt;grails install-searchable-config&lt;/code&gt; to get a SearchableConfiguration file in my conf directory. From there I could change the &lt;code&gt;compassConnection&lt;/code&gt; setting to point to the place I like to store my indexes.&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
My search action could then shrink to:&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
def search = {&#xD;
&#xD;
        def query = params.query&#xD;
        def blogid = params.blog&#xD;
        &#xD;
        def results = BlogEntry.search(query, params) &#xD;
&#xD;
        return [ results: results, query: query ]&#xD;
    }&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
The results you get back in ${results.results} are first class domain objects! So it's pretty much just a matter of a little markup...&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
&amp;lt;g:each var=&amp;quot;result&amp;quot; in=&amp;quot;${results.results}&amp;quot;&amp;gt;&#xD;
&#xD;
                &amp;lt;div class='hit'&amp;gt;&#xD;
&#xD;
                    &amp;lt;div class='hitEntry'&amp;gt;&#xD;
                        &amp;lt;div class='hitTitle'&amp;gt;&#xD;
                            &amp;lt;a href='${request.contextPath}/${result.toPermalink()}'&amp;gt;&#xD;
                                ${result.title}&#xD;
                            &amp;lt;/a&amp;gt;&#xD;
                        &amp;lt;/div&amp;gt;&#xD;
                        &amp;lt;div class='hitInfo'&amp;gt;&#xD;
                            &amp;lt;g:niceDate date=&amp;quot;${result.created}&amp;quot;/&amp;gt;&#xD;
                        &amp;lt;/div&amp;gt;&#xD;
                        &amp;lt;p class='hitBody'&amp;gt;&amp;lt;/p&amp;gt;&#xD;
                    &amp;lt;/div&amp;gt;&#xD;
                &amp;lt;/div&amp;gt;&#xD;
&#xD;
            &amp;lt;/g:each&amp;gt;&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
Add a g:paginate into the mix, and you're cooking!&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
One thing I have lost in the conversion is the ability to do hit term highlighting. I know Compass supports it, so it might just be a matter of digging in a little deeper! I'd also like to be able to store some DynamicMetaData with the index (blogEntry.blog.blogid), but haven't managed to get the annotations working just yet...&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Anyways... a huge code saving. Give it a try on the top right. And thanks Karl, you're a champion!&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Happy searching!&#xD;
&lt;/p&gt;</summary>
    <dc:date>2008-01-22T08:54:00Z</dc:date>
  </entry>
  <entry>
    <title>Gravl: Adding Fulltext Search</title>
    <link rel="alternate" href="http://blogs.bytecode.com.au:80/glen/2008/01/21/gravl--adding-fulltext-search.html" />
    <author>
      <name />
    </author>
    <updated>2008-01-21T08:54:00Z</updated>
    <published>2008-01-21T08:54:00Z</published>
    <summary type="html">&lt;p&gt;&#xD;
I've just finished adding fulltext search to Gravl, and, while is still needs some more CSS tweaks, it's working just great. I took the chance to have a good look at the &lt;a href="http://grails.codehaus.org/Searchable+Plugin"&gt;Searchable&lt;/a&gt; plugin and it looks awesome! Sadly, it just kept NPEing at me and complaining that it couldn't find java.lang.Class! Perhaps it's a victim of the RC-flux, no doubt it will resurface as 1.0 settles in.&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Anyways, I ripped the SearchService out of Groovyblogs, and I was up and running in an hour. I must bundle that thing into a LuceneSearch plugin someday... it's really very handy. Feel free to have a search in that box at the top right, "Pebble" gives you a good chance to see the paginate in action.&#xD;
&lt;/p&gt;&#xD;
&lt;p style="border: 1px solid black; marking: 3px"&gt;&#xD;
&lt;img src="images/2008/gravlSearch.png" alt="gravl Search in action... searching for Gravl!"/&gt;&#xD;
&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&#xD;
So that last real piece of the Gravl puzzle is image upload and management. But right now I've got to get cracking on my "Grails Extreme Makeover" talk for &lt;a href="http://www.groovygrails.com"&gt;G2X&lt;/a&gt;.&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Search on!&#xD;
&lt;/p&gt;</summary>
    <dc:date>2008-01-21T08:54:00Z</dc:date>
  </entry>
  <entry>
    <title>Gravl: Referers, Geocoding, Charts</title>
    <link rel="alternate" href="http://blogs.bytecode.com.au:80/glen/2008/01/17/gravl--referers--geocoding--charts.html" />
    <author>
      <name />
    </author>
    <updated>2008-01-16T19:10:00Z</updated>
    <published>2008-01-16T19:10:00Z</published>
    <summary type="html">&lt;p&gt;&#xD;
Continuing my obsession with the power of Grails filters, it was time to add some referer tracking and ipaddress Geocoding to Gravl to see where people are coming from. So I created a new &lt;code&gt;RefererFilters&lt;/code&gt; class in &lt;code&gt;conf&lt;/code&gt; and I was up and running. I already had a little CacheService happening, so I created a new 24 hour cache, scooped up some interesting browser header data, and squirrelled it away for later:&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
 // Scoop up Referer and User-Agent and remote IP&#xD;
def userAgent = request.getHeader("User-Agent")&#xD;
def referer = request.getHeader("Referer")&#xD;
def ip = request.getRemoteAddr()&#xD;
&#xD;
// Grails adds request.forwardURI to keep the orig URI&#xD;
def url = request.forwardURI &#xD;
&#xD;
// put in the cache, not use of applicationContext to lookup service bean&#xD;
CacheService cacheService = applicationContext.getBean('cacheService')&#xD;
cacheService.putToCache("referers", 60*60*24, Calendar.getInstance(),&#xD;
				[ userAgent: userAgent, referer: referer, ip: ip, url: url ])&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
One thing to notice in the above, service injections don't happen in the filter chain, but you are provided with an &lt;code&gt;applicationContext&lt;/code&gt; that you can use to look up your services by name. &#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Anyways, now I've got some stats, it's time to do some crunching... Referers was the first target to add to my stats page... though the css needs some work to fit the counters  in :-)&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
&lt;img src="images/2008/gravlReferers.png" alt="Gravl referers in need of some css"/&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Now what to do with those ip addresses I was gathering? Picking up on the stuff &lt;a href="http://www.simongbrown.com/blog/"&gt;Simon&lt;/a&gt; is doing in the latest Pebble, I thought it might be nice to add some geocoding to that referers list so I can see which country my traffic is coming from. &#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Turns out there's a very simple little Java library available from MaxMind called &lt;a href="http://www.maxmind.com/app/geoip_country"&gt;GeoLite Countries&lt;/a&gt;. I mavenized the source into a jar, then dropped the resources file into my /src/java/resources. I already had the ip addresses I'd gathered from headers into my cache, so to convert them to a country name, it was time to create a &lt;code&gt;CountryLookupService&lt;/code&gt; to wrap all the calls to the GeoLite library:&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
class CountryLookupService {&#xD;
	&#xD;
    LookupService lookupService&#xD;
&#xD;
    private LookupService getLookupService() {&#xD;
        if (lookupService == null) {&#xD;
            // assume you put your data file in /src/java/resources&#xD;
            def url = this.class.getResource("/resources/geoIp/GeoIP.dat")&#xD;
            log.debug ("Looking for GeoIP database at: $url")&#xD;
            lookupService = new LookupService(new File(url.toURI()),LookupService.GEOIP_MEMORY_CACHE);&#xD;
        }&#xD;
        return lookupService&#xD;
    }&#xD;
&#xD;
    def getCountryName(String ipAddress) {&#xD;
        log.debug "Looking up address for ${ipAddress}"&#xD;
        return getLookupService().getCountry(ipAddress).getName()&#xD;
    }&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
And we're off and running. The GeoCoding API requires you to supply a current GeoIP.dat file, and the sample above shows you how to load it from your Grails /src/java/resources folder to keep everything all relative. I'm not sure where the agreed convention is on resource placement in Grails, but there is as good as any for now... After that I just needed to iterate my cache, and we have ourselves a hashmap of countryname to hitcount:&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
&lt;img src="images/2008/gravlGeocoding.png" alt="Gravl Geocoding in action"/&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt; &#xD;
Once you've crunched all that data, it'd be nice to produce some slick charts to make it all digestable. And what quicker way to chart it than with &lt;a href="http://www.jameswilliams.be/blog/entry/index"&gt;James Williams&lt;/a&gt; uber cool &lt;a href="http://www.grails.org/Google+Chart+Plugin"&gt;Google Charts Plugin&lt;/a&gt;. &#xD;
&lt;/p&gt;&#xD;
&lt;p&gt; &#xD;
If you haven't used &lt;a href="http://code.google.com/apis/chart/"&gt;Google Charts&lt;/a&gt; before, the idea is just so good and so simple. Create an &lt;code&gt;img&lt;/code&gt; tag with the &lt;code&gt;src&lt;/code&gt; that points off to &lt;code&gt;http://chart.apis.google.com/chart?&lt;/code&gt; then add url params to tell it your chart size, type, title, etc. and Google will render a png on the fly for you.  &#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
James plugin makes that process very simple and painless. I take my hashmap of countries (which carries countryName -&gt; hitCount) and feed it straight into his taglib:&#xD;
&lt;/p&gt;&#xD;
&lt;pre class="prettyprint"&gt;&#xD;
&amp;lt;g:pieChart type="3d" title='Hits By Country' size="${[700,300]}"&#xD;
   labels="${countries.keySet()}" dataType='simple' data='${countries.values().asList()}' /&amp;gt;&#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&#xD;
And the result?&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
&lt;img src="http://chart.apis.google.com/chart?chs=700x300&amp;chd=s:B9BBBBCCGBCABEBBDBBFBDAABCAAABABAAA&amp;chtt=Hits%20By%20Country&amp;chl=Turkey|United%20States|United%20Arab%20Emirates|Argentina|Czech%20Republic|Greece|Austria|Korea,%20Republic%20of|France|Finland|Australia|Nigeria|Poland|Ukraine|India|Spain|Panama|Indonesia|Canada|Germany|Switzerland|United%20Kingdom|Hungary|South%20Africa|Belgium|N/A|Egypt|Guam|Philippines|China|Romania|Netherlands|Jamaica|Israel|Italy&amp;cht=p3" alt="Hits by Country"/&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
So perhaps a pie chart might not be the best selection... :-)&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Wow.. that was all quite an adventure in Referers, Geocoding, and Charting! I'll be talking about some of these adventures at &lt;a href="http://www.groovygrails.com/gg/2gexperience"&gt;G2X&lt;/a&gt;, so if you're in North America next month, make sure you drop over and say hi!&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&#xD;
Happy Grails Stats crunching!&#xD;
&lt;/p&gt;</summary>
    <dc:date>2008-01-16T19:10:00Z</dc:date>
  </entry>
</feed>

