P'unk Avenue Window

Archive for ‘Uncategorized’

Tonight: toast the Spirit Mars rover!

January 27th, 2010 by Tom No Comments




After exceeding its 90-day mission by SEVEN. FREAKING. YEARS, the Spirit Mars rover is permanently stuck. So sad!

Yes, they plan to do some stationary science work with it when the winter is over, but let’s face it… this is the end.

Philadelphians, please join me tonight at 7pm at National Mechanics (*) to pour out a drop for our fallen robot homie!

To be followed, no doubt, by watching the State of the Union address. Which might even touch on space policy for once.

Let’s give Spirit a Viking funeral! (**)

(*) 3rd between Chestnut and Market if anybody doesn’t already know.
(**) Failure to get this joke is punishable by deportment to the Phobos reeducation camp.

svncampfire: svn commit notices in campfire, take 2

December 18th, 2009 by Tom No Comments

screenshot of svncampfire source

Using svn for version control? Have lots of repositories you’re interested in? Maybe some of them aren’t under your control, so you can’t set up postcommit hooks?

Check out svncampfire (literally… haha, see what I did there). svncampfire pastes svn commit notices into your Campfire chatroom.

We’ve just released version 1.1, which supports the new official 37signals API for campfire. It’s a great API, much more fun than screenscraping; I chose the JSON flavor because it’s so darn easy. Version 1.1 also adds friendly labels for commits from each distinct repository so it’s easier to see what is going on.

Right now svncampfire is monitoring over half a dozen repositories for us, all thanks to the magic of svn log --xml.

Dear Gmail: let us map filters to “From” addresses

December 2nd, 2009 by Tom No Comments

Dear Gmail,

labels.
multiple “From:” addresses.
Now, please let me select a default “From:” address when replying to messages with a particular label. Then I can manage my various secret identities business roles with a much lower rate of accidental goofage.

Yours in the wearing of many hats,

Tom “tommybgoode P’unk Avenue Boutell.Com salsadelphia.com” Boutell

UX Show & Tell

November 9th, 2009 by Geoff 2 Comments

Please join us at P’unk Ave for a UX Show & Tell workshop hosted by Chris Avore.

From the UX Show & Tell website:

UX Show and Tell is a casual workshop that’s all about the work, where you get feedback on your UX, IA and IxD deliverables from practitioners instead of stakeholders.

Because let’s face it: even the most involved practitioners of the UX community have few places to share work with other helpful colleagues in a face to face environment.

Even if you don’t have something to show, help your fellow IA’s, designers, and researchers by sharing solutions with those who are showing work.

We’ll supply the tomato pie if you bring the deliverables!

UX Show & Tell at P’unk Ave
Wednesday, November 18th @ 6:30pm
1168 E. Passyunk Ave [map]

A better strip_tags: pkHtml::simplify

September 21st, 2009 by Tom No Comments

Many web sites allow users to edit content via a rich text editor. And we all know what happens if the user pastes a Word document in there: the styles of the page wind up hopelessly munged. PHP coders can use strip_tags() to limit the HTML tags that get pasted in, but that doesn’t clean up the CSS style attributes that can be pasted in. So your page is still a mess. Often it is so broken that the user can’t figure out how to edit again. This is the point where they pick up the phone and plead with you for help.

strip_tags also won’t help if the HTML is invalid. Unclosed tags can create nightmares elsewhere in the page when users are allowed to submit snippets of HTML that leave a bold tag or a header tag unclosed.

A common workaround is to use the FCK (aka CK) rich text editor’s “paste as plaintext” mode, which thwarts attempts to paste rich text from another program. That works, after a fashion, but users are forced to do all of their styling all over again. Bold, italic and bulleted lists must be recreated from scratch.

And none of the workarounds are sufficient if you can’t at least trust the intentions of your users. This is not a problem when your client logs in to update his site, but it’s a big issue when his customers are creating personal profiles and the like.

HTML Tidy can tackle this sort of filtering for you, but it is often not included in your build of PHP as it is not standard equipment (although some Linux distributions do include it in the box). This is especially important on client sites where you’ve been asked to live with the setup that is available.

HTML Purifier does much the same thing in pure PHP, but since it parses the document directly in PHP it is a bit slow and heavy. Which makes you long for the raw performance of strip_tags.

Enter pkHtml::simplify():

pkHtml::simplify($richTextHTML,
  "<h3><h4><h5><h6><blockquote><p><a><ul><ol><nl><li><b><i>" .
  "<strong><em><strike><code><hr><br><div>" .
  "<table><thead><caption><tbody><tr><th><td>");

If that looks a lot like the arguments to strip_tags(), you’re right. That’s because pkHtml::simplify() uses strip_tags() as a starting point. But pkHtml::simplify() follows up strip_tags() with a DOMDocument-based filter that removes attributes too, except for the attributes that actually make sense to permit for certain tags. That is, if you are permitting the tag at all, you need those attributes for it to be useful.

Currently these tags are:

A tag -> href and name attributes
img tag -> src attribute

If you’re coding for Symfony, you don’t have to call pkHtml::simplify() directly. Instead, you can use the sfValidatorHtml validator, also found in pkToolkit, which allows the above list of tags by default because they are well-suited to user-entered content. For the convenience of Symfony developers we also package Dominic Schierlinck’s sfWidgetFormRichTextarea widget in pkToolkit. It’s meant to be compatible with both MCE and FCK, although we always use FCK.

pkHtml::simplify is short and sweet because it uses the most straightforward tool for each job. For removing disallowed tags, nothing beats the raw speed of strip_tags. But strip_tags doesn’t know anything about attributes, so for that purpose we use DOMDocument. Many PHP developers aren’t yet familiar with this class, which is standard equipment in PHP 5.

DOMDocument has three great features from our perspective: it can clean up HTML automatically, ensuring that tags are closed. And it lets you manipulate attributes painlessly. And since it is built into the core of PHP, it is much faster than parsing HTML “from scratch” in PHP.

DOMDocument does want to give you back a complete HTML document with nifty things like a document type and html, head and body elements that we don’t need when we’re manipulating snippets of user-entered HTML, not truly complete pages. So pkHtml::simplify optionally undoes that for you, leaving you with an HTML container element that’s ready to snick into place in the middle of your page body.

So: how good is the performance of pkHtml::simplify? Very good indeed. On one project we need to separately clean hundreds of untrusted potential HTML containers in a single XML document, in real time, before presenting portions of that information to the user. pkHtml::simplify has no trouble keeping up in this scenario.

Our Apostrophe CMS takes advantage of pkHtml::simplify() to allow rich text editing without the constant “oops I screwed up my site” issues that come up without a robust server-side filter.

pkToolkitPlugin is available from the Symfony repository and is released under the open-source MIT license. Keep in mind that the pkHtml class is useful to all PHP developers, not just Symfony developers. Just download the tarball and copy it from the lib folder.

I recommend picking up the latest version of the code from svn. You can also use the tarball versions, but you may need to remove a few Symfony logging calls to use it in a non-Symfony context.

How to add ON DELETE CASCADE to an existing MySQL table without dropping any existing data

May 27th, 2009 by Tom No Comments

You created a MySQL table with a foreign key reference… for instance, a user_id field that refers to a separate table of users.

Then you realized you should have specified “ON DELETE CASCADE” when creating the table. Now, when you try to delete a user, you can’t because objects in the other table are still referencing that user object. Ouch.

What to do, what to do!

Do this (substitute your table and column names, of course):

SHOW CREATE TABLE pk_context_cms_access;

Note the name of the foreign key constraint, which is usually automatically generated, and looks like this:

CONSTRAINT `pk_context_cms_access_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `sf_guard_user` (`id`)...

Now drop the foreign key constraint. Relax! You’re not dropping the column involved, so you’re not losing any data. You’re just giving up the integrity check that makes sure foreign IDs refer to stuff that really exists and prevents you from deleting records unless you have set ON DELETE CASCADE… which is probably why you are reading this. (This will temporarily relax referential integrity requirements. In English, that means you should probably temporarily disable the site while blasting through these steps, if it serves billyuns of people every micro-moment.)

alter table pk_context_cms_access drop foreign key pk_context_cms_access_ibfk_1;

Now add the foreign key constraint back again, this time with ON DELETE CASCADE:

alter table pk_context_cms_access add foreign key(user_id) references sf_guard_user(id) on delete cascade;

Boom. Check “show create table pk_context_cms_access” again to see that it worked:

CONSTRAINT `pk_context_cms_access_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `sf_guard_user` (`id`) ON DELETE CASCADE...

Now buy me a beer.

How to open Google Calendar (or any URL) on your Mac at scheduled times of day

May 14th, 2009 by Tom No Comments

We make heavy use of Google Calendar here at work. Unfortunately I rarely remember to look at it unless an alert has specifically been set up to get my attention.

This morning I set up my Mac to open Google Calendar in my web browser every day at 10:15am.

How do you do that? Turns out it’s not hard.

You can open a URL at the terminal command line—

“Whoa! Did you just say ‘terminal command line?’ I thought you said this wasn’t hard?”

Well, it isn’t! All you really need to know about the terminal application is this:

1. It’s not hard.
2. Go to the Finder, click “Applications” (at lower left), open up “Utilities” in the pane at right, and then double-click “Terminal.” Now you have a terminal window to type commands in.
3. After each terminal command, one generally presses the Enter (return) key. So if I say nothing to the contrary, assume you’re meant to do that after each command.

You can open a URL in your web browser from the terminal command line by typing a command like this. Just copy and paste your Google Calendar’s URL from the address bar:


open 'https://www.google.com/calendar/hosted/punkave.com/render?tab=mc'

(Note that if your URL actually contains the ' character you’ll need to escape it with a \ in front.)

OK, you’ve opened a web page from the command line. Now, how do you schedule that to happen every day?

Luckily the Mac, like any good Unix box, offers a utility called cron which does things for you at scheduled times. To set that up, all you have to do is edit your cron jobs with the crontab command…

But this part is a little bit of a PITA: by default, the crontab command opens your cron jobs file with vi, an old-school Unix text editor. I’m happy with it, but you don’t need to be. So I’m going to first walk you through how to switch things around so that crontab opens things in good old TextEdit.

I’ll also show you how to set up a convenient command you can use in the terminal window to edit any file with TextEdit from the command line.

We’ll start by editing your ~/.profile file. This file contains commands that the terminal window runs when you first start it up.

First, use the touch command to ensure the file exists. TextEdit doesn’t like being asked to edit files that don’t yet exist:


touch ~/.profile

Now try this command (hint: don’t suffer, copy and paste it into terminal):


/Applications/TextEdit.app/Contents/MacOS/TextEdit ~/.profile

Your .profile file is open in TextEdit. This file contains Unix shell commands that should be run every time a new shell window opens for you. At first it will probably be empty.

Add these lines at the end of the file (which might be empty to start):


# Use TextEdit for crontab and similar commands
# that want to use your default editor
export VISUAL=/Applications/TextEdit.app/Contents/MacOS/TextEdit
# Use TextEdit when you type: edit filename
alias edit='/Applications/TextEdit.app/Contents/MacOS/TextEdit'

The lines that begin with # are comments and the command shell will ignore them. I provide them so you can remember why you did this stuff later.

A word of warning: when launched this way, TextEdit tends to open behind the terminal window. You might have to apple-tab around a bit to find it.

Finally, close your terminal window and open a new one. This ensures that the commands in ~/.profile have been run. Alternatively you can use this command to run the commands in ~/.profile in your current terminal window:


source ~/.profile

Now we're ready to edit your cron jobs... in TextEdit, not vi. At the terminal prompt, type:


crontab -e

And paste in this line (but substitute your Google Calendar URL of course, copied and pasted from your browser):


15 10 * * * open 'http://your.url.here/'

Then save your work and quit TextEdit. The crontab command can't continue until you quit TextEdit.

Now the URL you've pasted in will open every day at 10:15am. If Firefox is your default browser, it will open in a new tab. I haven't tried this with Safari as the default browser. Freel free to enlighten me as to how that works out.

What are those numbers and stars about? They represent minutes, hours, days of the month, months, and days of the week. We are only concerned with the first two: we want the job to run at 10:15am. If you want to run the job in the afternoon, be sure to use 24-hour time (for instance, set the second number to 13 for 1pm).

cron can do quite a bit more. If you want to know more about scheduling cron jobs, try this command:


man 5 crontab

Internet Explorer 6 to be ghetto-virtualized into oblivion

April 25th, 2009 by Tom No Comments

Microsoft will allow bass-ackwards XP-only applications, such as IE 6-only web apps, to run in emulation via Virtual PC as part of the regular Windows 7 desktop. That feature will apparently be standard in enterprise-class versions of Windows.

I appreciate that Microsoft has found a way to do what they have been unable to do for years: push a modern web browser onto the desktops of the most stubborn corporate and institutional clients. The people who make the lives of web designers pure hell by insisting on IE 6 compatibility because they once allowed someone to sell them a web application that can never, ever be upgraded to work with anything else. This is good. Frustrating as all hell, but good.

But Microsoft, if you do this the wrong way… where “the wrong way” is allowing admins to enforce a policy like “everybody gets virtualized IE6 all the time and may not have IE8 as their default browser…” then you’re going to hear the screaming, the wailing and the gnashing all over again.

Criticism, journalism: things are tough/awesome all over

April 24th, 2009 by Tom 2 Comments

I enjoyed last night’s Junto discussion on art criticism in Philadelphia but I’m puzzling over some fundamentals. The larger context went mostly unspoken.

All journalism everywhere is in deep sheep dip right now because print newspapers are dying.

At the same time, everybody’s a critic (ahem) thanks to the Interwebs.

Which means we have an overwhelming supply of armchair critique of everything— art, politics, sports, everything— but our supply of profoundly well-informed criticism is perhaps in danger.

But that last is far more an issue for politics (if you can’t afford to send stringers to Pakistan, your information is limited) than for art (there are people who choose to invest 20 hours a week in art, on their own dime).

Also, why is this discussion so centered on Philly? Who cares about locality? Okay, it’s an important color in the palette, but it’s not everything. And other media have already gotten the message that it doesn’t matter so much anymore.

Craft artists have moved en masse to Etsy. They can sell their work, they can get discovered. And criticism and commentary happen everywhere people care enough about the work. Geography is mostly irrelevant, except insofar as it informs the style of the work itself.

And I’m talking about craft artists who produce functional objects. The need to be in the same room with it before you buy it should be greater, not less, than the need to stand in a gallery with a painting. So why must fine art sales, and fine art criticism, be local? And dependent on the expense of galleries and print publication?

Local artist Katie Henry produces both sewn paintings and bags. To my knowledge her sewn paintings have sold only on a local basis, while it is completely impossible for her to keep a handmade bag in stock for more than eight seconds. But I suspect this is mostly because her paintings haven’t been listed on Etsy (hey, the lady’s busy enough as it is).

So why hasn’t fine art really arrived on Etsy yet? It could be as simple as a marketing decision on Etsy’s part. Perhaps a wise one. But that’s just an opportunity for someone else.

Sometimes, blogging is timeless

April 15th, 2009 by Tom No Comments

Sometimes Google doesn’t exactly rush pell-mell to index your blog. And when Google does index your blog, sometimes they don’t do it in a way that really helps your readers.

To my mind, blogs have two kinds of readers:

1. People who read it regularly, via RSS or similar
2. People who find individual articles via a search.

Category #1 isn’t really Google’s department. Fair enough.

Category #2, of course, is what Google Search is all about. Sure, 99% of queries are about “country matters” and every search engine has them down to a tee, but the queries you really care about are always in that 1% that’s obscure and weird and only mentioned in passing on one site ever. And Google’s capacity to actually find that stuff is the reason they took over the search world in the first place.

But for a while there, this really wasn’t working for the P’unk Avenue Window blog.

Searches like these (with the quotes, making them exact):

“Designing while intoxicated”
“svncampfire”

Were turning up other people linking to or talking about our articles, but they were not turning up the articles themselves. Results on our actual blog always turned out to be category pages or the home page… which, more often than not, didn’t feature that content anymore due to the addition of new blog posts.

This poses a problem because people don’t know they are interested in something like svncampfire until they need it.

I did quite a bit of research looking for an explanation for Google’s dislike of our individual article pages. I considered blaming our robots.txt file, the presence of dates in our article page URLs, and of course sunspots.

But in the process I finally got around to learning about Google Sitemaps. And more importantly, the Google Sitemap Generator plugin for WordPress, which is beautiful. There’s really no other word for it.

A sitemap is a simple file that spells out what pages are on your site and what their relative importance is. You can’t use it to increase your overall rank with Google, but you can use it to promote your individual articles at the expense of things that might otherwise seem more important to Google, like your category pages. Where before Google might only rarely bother to crawl back through your calendar archive links, now Google clearly understands that your posts are special and unique snowflakes that do not wither with age and should be taken equally seriously. And unless you’re giving out stock tips or weather reports, this is of the good.

The sitemap plugin will not only generate sitemaps for you, it will automatically update them whenever you post to your blog. Tweak the weightings using the handy control panel and you’re good to go.

There is, however, one catch that meant an extra week of waiting before Google finally caught on: the very first time you write a sitemap, you must submit it to Google manually via the Google Webmaster Tools. The plugin cheerfully informed me that it had already “submitted the sitemap to Google” without mentioning this little fact. So take care to follow through on that detail. You should be using the webmaster tools anyway!

The results are not perfect. Some searches that ought to bring up our individual articles still mysteriously bring up only category pages. And tweets consisting almost entirely of links to our articles (often using the hated URL shorteners, which Google doesn’t seem to translate) still often outrank the articles themselves. But hey, it’s progress. Most of the time we’re on the first page in appropriately narrow searches. It’s a big improvement, and I have a much warmer, fuzzier feeling about the usefulness of blogging on topics of lasting interest.