If you get your data model right, implementing a tagcloud turns out to be a snack. In Gravl, I’ve setup a many-to-many between BlogEntry and Tag, but I also have a link from the parent Blog directly to all available tags:
Implementing many-to-many simplifies to import process when I import my Pebble data. The import process ends up with something like this:
def tag = Tag.findByBlogAndName(newBlog, importedCat) if (tag == null) { tag = new Tag(name: importedCat) log.debug "Creating new tag for ${importedCat}" // Grails handles the bidirectional link for us newBlog.addToTags(tag).save() } else { log.debug "Found existing tag for ${importedCat}" } newEntry.addToTags(tag).save()
Once you’ve got an easy way to access all tags for a blog, and a count of how many times they occur, you’re all set to following a simple tag cloud algorithm. Sounds like a perfect case for a TagCloudTagLib? Needs some error handling, but here’s a first cut (apologies for the formatting):
class TagCloudTagLib { // Simple Algo from http://www.petefreitag.com/item/396.cfm def tagCloud = {attrs, body -> def blogId = attrs.blogId Blog blog = Blog.findByBlogid(blogId) log.debug "Building tagcloud for: $blog.title" // step 1: determine tags and their frequency def tagToFreq = new TreeMap() blog.tags.each {tag -> def tagCount = tag.entries.size() tagToFreq[tag.name] = tagCount log.debug "Setting size of ${tag.name} to $tagCount" } def allFreq = tagToFreq.collect {tag, freq -> return freq}.sort() def minFreq = allFreq[0] // first entry def maxFreq = allFreq[-1] // last entry log.debug "Max is $maxFreq and min is $minFreq" // step 3: find the spread, use 5 font sizes def distrib = maxFreq - minFreq def catSize = (distrib / 5).intValue() + 1 // add 1 to simulate ceil() // step 4: output your tags tagToFreq.each {tag, freq -> out << "<a href='/$blogId/tags/$tag' class='tagCloudSize" + (freq / catSize).intValue() + "'>" out << tag out << "</a> " log.debug "Tag class of $tag with size $freq is " + (freq / catSize).intValue() } } }
Which make using tagclouds in a gsp as easy as:
<g:tagCloud blogId="glen"/>
Or something more dynamic..
Of course, you’ll want to style those bad boys once you put them in a GSP. But the CSS is pretty straightforward:
.tagCloudSize0 { font-size: xx-small; } .tagCloudSize1 { font-size: small; } .tagCloudSize2 { font-size: medium; } .tagCloudSize3 { font-size: large; } .tagCloudSize4 { font-size: xx-large; }
And the result? Well… I really need to add some more categories for my blogging… Or get a more balanced life:
If you’d like to have a more detailed browse of the source, I’m hosting at google code for now. Check out the source page. It’s all Apache2.
Ok… Tagclouds done… Now on to some calendaring…