Two quick SquareSpace tricks

In the process of migrating modulus to squarespace and starting a separate personal blog, I've come across two useful tricks that I wanted to share in case anyone else ever needs them.

The first is related to migrating from Drupal to SquareSpace. SquareSpace accepts a variety of formats, and they seem pretty ecited about their Moveable Type importer. I lucked out and found a script to export a drupal site to moveable type (scroll down for the python version), but unfortunately it didn't

  1. handle my node revisions correctly
  2. filter out unpublished comments
  3. write the date in a format that SquareSpace could handle
  4. have an obvious way for non-programmers to run it

So, I hacked in fixes to those four issues, and it worked like a charm. I tested it against drupal 5 -- no promises it'll actually work for you, but it was nice to have to work with.


def read_drupal(outfile,db,host,user,passwd):
import re,MySQLdb,time,wikimarkup
linefeed = re.compile('\r')
fout = open(outfile,'w')
db = MySQLdb.Connect(db = db,host = host, user = user, passwd = passwd)
c = db.cursor()
c2 = db.cursor()
cdata = db.cursor()
c.execute("SELECT nid,uid,type,title,status,created,comment from node where type = 'blog' AND status = 1")
stat = ["draft","publish"]
for (nid,uid,type,title,status,created,ncomment) in iter(c.fetchone,None):
#for i in range(0,10):
#(nid,uid,type,title,status,created,ncomment,teaser,body) = c.fetchone()
cdata.execute("SELECT body,teaser,format FROM node_revisions WHERE nid = %i ORDER BY timestamp" % int(nid))
(body, teaser,format) = cdata.fetchone()
# teaser = ''
# body = ''
body = linefeed.sub('',body)
if format==4:
body = wikimarkup.parse(body)
created = time.strftime('%m/%d/%Y %I:%M:%S %p',time.localtime(created))
c2.execute("SELECT name from users where uid = %s", (uid,))
(name,) = c2.fetchone()
fout.write("AUTHOR: %s\nTITLE: %s\nSTATUS: %s\nALLOW COMMENTS: %s\nCONVERT BREAKS: 1\nALLOW PINGS: %s\nDATE: %s\n" % (name,title,stat[status],ncomment,1,created))
c2.execute("SELECT name from term_node n, term_data d where n.nid = %s and n.tid = d.tid" % (nid,))
categories = [cat[0] for cat in iter(c2.fetchone,None)]
fout.write("TAGS:%s\n-----\n" % (','.join(categories)))
fout.write("BODY:\n%s\n-----\nKEYWORDS:\n/node/%s\n-----\n" % (body,nid))
if teaser != '':
fout.write("EXCERPT:\n%s\n-----\n" % (teaser,))
c2.execute("SELECT subject,comment,hostname,timestamp,name,mail,homepage from comments where status = 0 AND nid = %s order by cid" % (nid,))
for (subject,comment,hostname,timestamp,name,mail,homepage) in iter(c2.fetchone,None):
timestamp = time.strftime('%m/%d/%Y %I:%M:%S %p',time.localtime(timestamp))
#timestamp = time.strftime('%Y-%m-%dT%H:%M',time.localtime(timestamp))
fout.write( "COMMENT:\n")
if name != '':
fout.write( "AUTHOR: %s\n" % (name,))
if mail != '':
fout.write( "EMAIL: %s\n" % (mail,))
if hostname != '':
fout.write( "IP: %s\n" % (hostname,))
if homepage != '':
fout.write( "URL: %s\n" % (homepage,))
if timestamp != '':
fout.write( "DATE: %s\n" % (timestamp,))
fout.write( "%s\n" % (subject,));
fout.write(comment + "\n")
fout.write( "-----\n")

if __name__ == "__main__":
read_drupal("filename", "tablename", "server", "user", "password")


The second is a bit of javascript for creating a music player on blog enclosures. I'm posting about a minute of music, about every day, to my personal blog, and I'd wanted an easy way for people to be able to play it. So, I found a nice, creative-commons licensed music player called dewplayer, and wrote some jQuery code to find MP3 enclosured and add a player for them to the entry.


$(function () {
mp3s = $(".enclosureWrapper a[href$=mp3]");
mp3s.wrap("<div class='player-wrapper'></div>");
mp3s.each(function() {
elem = $(this);
wrapper = elem.parent();
song = elem.attr("href");
playerHolder = $("<div class='player'></div>");
swf: '/storage/resources/dewplayer.swf',
flashvars: {
mp3: song,
wmode: "transparent",
showtime: 1
params: {
wmode: "transparent"
height: 20,
width: 200


Because SquareSpace uses YUI internally, eventually I'll rewrite that code to just use YUI and swfObject. But, for now, it works and isn't too heavy, and has the advantage of only taking about 15 minutes to throw together.

Starting back up

For a while I was blogging pretty well on I had a blog running on Drupal hosted with Site5 and I was enjoying it a lot. Then I got very busy, and my blog faltered. But, even faltered, my content was still there. But then something much more annoying happened: site5 performed an upgrade and knocked my sites out. Being pretty busy, it was hard to find the time to fix a suddenly broken drupal install or any of the other applications that had suddenly fallen offline. Getting back online has left me with some decisions to make about blogging platforms, and I'd like to chronicle those here.

For me, the first question was, do I host my own blog again? I'm a full time web developer, so finding hosting and getting the app installed isn't really an issue. And, I'd certainly use an open source engine (zine is quite impressive, if slightly lacking in usability), so I'd like to pick one I might be able to contribute to. But, there's a flip-side to being a web developer: for me at least, keeping a server up to date in my free time isn't a ton of fun. In fact, it's a chore. I don't mind doing it for clients, or for interesting pet projects that require some admin attention; but, updating wordpress or drupal regularly (and wondering if everything's going to randomly break) is just a motivator to avoid looking at the blog at all. So there's a very strong temptation to just use a hosted service.

The temptation is so strong, in fact, that it looks like I've given into it. Certainly no technology choice is forever, but my goal this year is to simplify the details so that I actually do the work I care about. And picking a hosted blog means one less detail to keep track of. So, now I'm doing a heads-up comparison of a few blog hosts:

I also tried to get a sense of what I'm looking for in a blog, which ended up being a pretty healthy list, and which you can check out in the mindmap above if you're really interested. But the highlights were that I needed to be able to be pretty sure on the security front, be able to use custom CSS (and javascript), be able to import and export data from the system, be able to set arbitrary URL redirects (to preserve content from my old blog), offer feeds filtered by tag (so I can submit entries to various planets), and use a custom domain.

I eliminated Vox right away because there was no obvious way to use a custom domain. This is too bad though, because I really like their media manager; if I ever set up a more personal blog, I'd definitely consider using them. TypePad I had used before when I used to write for Prevention Works, and I had honestly grown to hate their admin UI; it's cluttered and I can almost never find what I'm looking for. I know it works for lots of people, but we don't get along. Blogger lost out for similar reasons -- I don't like the editor UI, and I want blogging to be something easy and peasant so that I actually do it.

This left Squarespace and WordPress. Deciding between these two is actually pretty difficult. WordPress is an actively developed open source project, and benefits from that development. It has a killer community, lots of nice looking themes, and a usable admin UI. Squarespace is smaller, but has nearly all the features I want, and a really nice theme editor that makes simple personalization dead-easy. It also has a redirect system, and makes it possible in inject JavaScript onto the page (meaning I can have fancy features like code-highlighting via JavaScript), which is a big plus.

So, this ends up as a long post saying that I'm blogging again, I'm not hosting myself anymore but instead I'm using Squarespace. Who knows what I'll be using next year, but for now, I'm really enjoying this service. Hopefully I'll have my entries exported from my drupal DB backup soon, too, and then this blog can be whole again.