Projects

CustomInk: a great T-shirt printing experience

After reading Seth’s praise for custom printer CustomInk on more than one occasion, I decided I’d give them a shot.

Now, that’s not the whole truth.  The whole truth is that at first I couldn’t remember the name of the company.  I could, however, remember the name CafePress.  The funny thing is I’ve never used CafePress before – I’ve only ever heard of it.

What was it that made me consider CafePress before gritting my teeth and plundering Seth’s blog for the forgotten name?

Perhaps it is because I’ve heard so many people talk about CafePress, my mother in particular.  My mother is a pastel artist, and she does beautiful work.  She once considered using CafePress to distribute tiny prints of her work.  The samples we received in the mail weren’t any good.  Still, when it came time for me to put brand to cotton, I still went back to the inferior service first.

All I’m saying is that there is marketing at work here in a way that I don’t fully understand. I don’t typically find my mother in tribes to which I am a member.  Go figure.

Long story shorter, I was finally able to recall the name, and let me tell you: I am one satisfied potential customer.  The t-shirt design workshop CustomInk provides through their Web site is top-notch.  The interface is a little 2003; but the usability is unparalleled.  I had only one issue with the tool: it took me a while to figure out how to allow the white to stay in my uploaded art.  (There was a checkbox – I overlooked it.)

The t-shirts are for promoting coreylib – my universal API client (with caching built-in!). Ten t-shirts in two sizes for $251. That’s not bad, considering I’ll be creating ten community billboards the size of Large and Extra Large chests.

Best of all: the CustomInk workshop lets me share my designs with my audience ahead of time. Here’s hoping you guys like it.  Please note: I’ll probably be changing the tag line on the back – the current one is a bit too confrontational. (See, I’m trying to learn from my mistakes.)

customink

WP-CI: a CodeIgniter-based plug-in framework for WordPress

This article is about a new WordPress plug-in that I have just released in ALPHA.  Download and use at your own risk: it is far from finished.

I love WordPress. But writing plug-ins for WordPress sucks. There are a lot of plug-ins out there, all of them using WordPress’ smashing plugin API to create some great functionality. But at the end of the day, the native API leaves me wanting more.

I want to develop WordPress plug-ins using MVC principles. I want for security to be easy to remember and easy to implement. I want to be able to write plug-ins that provide body content on the front-end with minimal modification to my themes. I want to be able to utilize AJAX without implementing esoteric, hack-like features in my plug-in.

And I don’t want to have to refer to the gigantic Codex every time I need to one-off a new project: give me a manageable API.

With these goals in mind, I have written a plug-in for WordPress that creates a new plug-in framework. This framework, based on CodeIgniter, has far exceeded my personal expectations for the project (and it’s still in alpha!). Not only did I manage to satisfy my own requirements, I managed to do so without hacking either of the proven stacks, all while taking advantage of some very cool features of PHP 5 (annotations).

I named the plugin WP-CI.

A bit on how it works

Explaining the nuances of MVC development in CodeIgniter is beyond the scope of this article, and with good reason: they have some great documentation.  But for the uninitiated, know this: CodeIgniter divides application functionality into Controllers and Actions.  Each Controller gets its own PHP file, and each public function on the Controller is exposed to the Web as an Action – discreet units of application functionality, each with a specific purpose (like display a form, save some data, delete a record, or return some JSON).

In WP-CI, each controller file (stored in ./application/controllers) is, in effect, a special WordPress plug-in that I call a WPCI Plug-in.  Within the body of each PHP file you have complete access to the entire WordPress API, as well as complete access to the CodeIgniter framework.

To learn more about the structure of a WP-CI file, download the latest release and have a look at ./application/controllers/hellosparky.php.  A more complicated example exists in the WPCI Plug-in Administrator, which is itself a WPCI Plug-in.

Using annotations

Once again, for the uninitiated, annotations (in PHP) are special comments added at various locations in a PHP file.  This is a concept specific to PHP’s object-oriented programming syntax, and allows developers to add meta-data to class definitions.

WP-CI makes extensive use of this PHP 5 feature, making this framework incompatible with PHP 4 (sorry guys and gals: welcome to the future).

Annotations look like this

/**
 * @user_can(edit_plugins)
 */
class Admin extends WPCI_Controller {
  function index() { /* ... */ }
}
copy code

The annotation featured above, @user_can(edit_plugins), is a cue to our framework to require any user accessing this plug-in to have the built-in WordPress capability of editing plug-ins.  (This requirement implies a user be an administrator.  You can read more about user capabilities in the WordPress Codex.)

There are other annotations too, like @menu and @submenu which serve to create administrative menus and menu items from your plug-in actions.

For more examples of annotations in action, have a look at the WPCI Plug-in Administrator, ./application/controllers/admin.php.

Early adopters beware

The current release is ALPHA and so you should expect the core API to change with the next release, and possibly even break your application in complicated ways. You have been warned.

This software is not warranted in any way.

So, have you tried it out yet?

Crowdsourcing with crowdSPRING

While designing the Web site for Winchester’s Bright Cowork, my friend and colleague @coreyweb used a service called crowdSPRING to crowdsource the logo. As a spectator to the process, it seemed like an efficient and relatively inexpensive way to get some design work done. (You can see the final product here.)

For those of you unfamiliar with the concept:

Crowdsourcing is a neologism for the act of taking tasks traditionally performed by an employee or contractor, and outsourcing it to an undefined, generally large group of people or community in the form of an open call. For example, the public may be invited to develop a new technology, carry out a design task (also known as community-based design[1] and distributed participatory design), refine or carry out the steps of an algorithm (see Human-based computation), or help capture, systematize or analyze large amounts of data (see also citizen science).

Commissioning work through crowdSPRING (almost) couldn’t be easier: just describe the project, and set a competitive price. The price you set is what will be paid to the winning designer. crowdSPRING then charges buyers 15% on top of the stated price – for a $300 job, this fee is $45. All money is paid ahead of the launch of the project, and is held in escrow until the project’s close – by default, one week. “Creatives” submit designs, and at the end of the project, the buyer selects one to be the project winner.

I was encouraged by the results Corey got from his crowdSPRING experience, so I decided to create my own project there (a new logo for my coreylib project).  So far, so good: I am very excited about some of the responses I have received.

But despite what I would call success, it should come as no surprise that the design community is very vocal in their disapproval of crowdsourcing and services like crowdSPRING. There is a status quo being challenged here. Crowdsourcing introduces competition on a grand scale into what has traditionally been a very selective process.  While this does yield a net benefit to the buyers, the work generated does exhibit a few negative consequences of the community process: namely, degraded quality and consistently, and a drastic reduction in the value proposition for the designers.

Business analysts and the service brokers themselves are claiming that crowdsourcing is the future of design, given the reduction in cost and the buyer’s ability to be extremely selective and critical of the end result.  Wrapped too in this new capacity to criticize is a departure from a time of designers being treated like doctors: that is, rather than being given carte blanche, they are instead often forced to assimilate the sometimes-arbitrary opinions of their customers (”I think it would look better if it were more balanced: centered vertically.”).  While this assimilation probably does happen quite a bit in the old world of design, I imagine that this play comes at a much higher price than $345.

It is easy to understand how this new marketplace could be perceived as an assault on the authority of designers and their maintenance of common (and important) aesthetics: an assault that, were it authentic, certainly should lead to a very cynical reception.  But I completely disagree with the idea that crowdsourcing is the future and will replace the traditional marketplace for design. The community-driven process of a service like crowdSPRING could never surmount the true authority and expertise that comes with a trained hand and mind, and the dedicated consultation.

What crowdsourcing does achieve is the creation of a niche market: one at which someone like myself, with limited time and a tight budget, can easily and efficiently vet a large number of ideas.  But these services are far from perfect.  Of the two I know about – 99designs and crowdSPRING – I chose crowdSPRING simply because I knew of a local success story.  In hindsight I am relieved I chose crowdSPRING over 99designs, because the manner in which 99designs manages the purchasing process is completely unethical. Unlike buying designs from crowdSPRING, 99designs does not appear to require the buyer to award a project. This leaves designers highly vulnerable to idea theft, and nefariously forces many into providing free consultations. Frankly, for a thing like community-based design to be fair and productive, both the designers and the buyers must have some skin in the game.

So how could using crowdSPRING be easier?  Well, for one thing, it would be nice to be able to quickly view Creatives’ previous submissions.  It is possible to get to previous submissions from a Creative’s profile page (a feature built-into crowdSPRING), but once on the project page, the user’s submission is hidden amongst all the other submissions made.  Some more guidance given to Creatives on the process of submissions would be very helpful: rather than having to score and review seven small variations on a single concept, it would be far more efficient to have all seven submitted as a single submission.  It took me a while to discover that I could score and comment from the gallery page – making this feature more prominent, and adding more controls for navigating between zoomed pieces would dramatically increase the frequency with which buyers provide quality feedback.

So if you’re on the market for a new logo or t-shirt design, I recommend giving crowdSPRING a try.  Make sure to read and follow the tips given to buyers at the onset of the project, namely the one that advises buyers to send private messages to creatives inviting them to work on the new projects.  Using this push approach to getting traffic to your project definitely increases the frequency with which you receive submissions. That and providing as much feedback as possible – most of the crowdSPRING designers thrive on constructive criticism.

If you’re looking for a place to start, I recommend these Creatives:

Have you had success with crowdSPRING or crowdsourcing? Please share your experiences in the comments.

tr.im is dead?

It came to me via a Tweet.  That Kenneth, always in the know.

So, tr.im is dead.  Which is unfortunate, because I wrote my wee-little WordPress Tweet plugin on the back of tr.im (for URL shortening automation).

As luck would have it, “market leader” bit.ly has an API, too.  Too bad you have to authenticate to use it – the tr.im implementation was way less strict.

And why is it that these URL shorteners can’t monetize?  Just show an ad on the jump! Duh. They should have had an easier time of it than Twitter.

Human Fondue

“American Freedom bears a major caveat: all choices ripple unintended consequences across our human fondue.”

We would all do well to recall

the lessons of our past:

human institutions reflect

human nature. But

in all things

mankind has aspired

to do better,

to be more. More than

just a species

just a creation:

to deserve being.

Thus

while some of us will cling to

the now, will preserve the present

incomplete though it may be,

others must pursue the future,

embrace the unknown,

challenge prejudice,

and create the next new world:

a reflection not only of what we are

but in deed, what we can be.

This poem is part of my 100words project. To follow the project, follow me on Twitter, or search Twitter for the #101words hashtag.

101 words every grown-up should know

I’m starting another project! (I think I have five or six now.) This project will be part of my efforts to become a better reader and writer.

Last year my wife purchased this book, 101 Words Every Grown-Up Should Know.  (I’d link to it on Amazon, but it appears to have fallen out of print.)  Ever since she purchased it, it’s sharp blue cover has been luring me to delve into the topic: imagine, a standard for “every educated vocabulary.”

So, for the next 101 days, every morning I’ll use one of the 101 words in a sentence.  You can follow my efforts on Twitter with the #101words hash tag.

July 25, 2009: “Some days it is difficult to imagine a world free of chronic confusion, illusion, and hunger. But we must try.”

July 24, 2009: “American Freedom bears a major caveat: all choices ripple unintended consequences across our human fondue.”

July 23, 2009: “The water at my house is on the verge of being caustic.  Some showers I feel like my hair may actually fall out.”

July 22, 2009: “I once told my wife she was a catalyst in my life: she took it to mean she was temporary. Lost in translation.”

July 21, 2009: “We should reserve castigation for the worst bad behavior, lest habituation render verbal reprimand useless.”

July 20, 2009: “I wonder if I can conjole my wife into going with me to the drive in. She’s not a big fan.”

July 19, 2009: “Let’s be brusque: Gannt charts are about as useful to the creative process as MBOs.”

Read the rest of this entry »

Critical thinking

I grew up in Virginia during the eighties and nineties.  My childhood occurred in the Reagan years – a period during which many conservatives grew to consider the United States a nation of conservative people.  Much to my surprise, Virginia hasn’t always been so conservative.  But I’m getting a little ahead of myself.

Growing up, the handful of people who did talk to me about politics were conservative.  So without really earning it, I adopted what I will call a “series of conservative opinions.”  In my twenties, I grew to recognize these opinions as being rather more like libertarian ideas than conservative ones, and far right of neoconservative ideals.

As I approach thirty and fatherhood, it is becoming more important that I understand these philosophies and their politics to a much greater depth.  Unfortunately, independent critical analysis doesn’t exist – everyone’s opinion is biased by nature, from some much more than others’.  So I have decided to make a different approach.

I intend to read two books

  • The Conscience of a Conservative by Barry Goldwater
  • The Conscience of a Liberal by Paul Krugman

For those of you who know neither the books nor the authors, I suggest you head to Wikipedia and play catch-up.  Suffice it to say, these men represent the faithful few: men truly dedicated to the ideals of their political camps.  So I will read them both, and throughout the experience I will measure my reaction as a means of deciding (for now) where my own politics lie.

In short, I will develop my own opinion.

While everyone else is sleeping, I’m writing PHP code

This is a test of my brand new Twitter plugin for Wordpress.

Why would I want to write yet another Twitter plugin for Wordpress?

Simply put: I wanted it to be simple.  I wanted to be able to edit the Tweet from the Post Editing screen.  And I wanted the link back to be tr.im’d, not bit.ly’d, or tinyurl’d, or any other such nonsense.

Now it’s done.  I’ll post it up on projects just as soon as all the bugs are worked out. (And just as soon as I get around to creating that projects page.)

Calculate the distance between two U.S. zip codes

Every once in a while a Web developer gets the opportunity to use those math skills he honed in college.  (I suppose this is only theoretical, since I didn’t go to college.)

We’re working on a project that calls for restricting search results to those occurring within a fixed distance from a user-supplied zip code.  The premise is simple enough: discover the geographical location of two zip codes and calculate the distance between them.  Of course, there are a few prerequisites:

  • a database of geocoded zip codes
  • a formula for calculating the distance between them

I thought the first resource would be difficult to find, especially considering my budget for this project is exactly $0.  But lo and behold, much to my surprise and delight, the folks over at PopularData.com provide such a resource, and it’s free!  (As well it should be, since it’s probably based on the U.S. Census data collected in 2000 or earlier.)  They claim that the database is mostly complete, except for a few dropouts (most of which, they say, are military stations, and not too useful to our application.)

I had built an application similar to this one in the past, so I knew somewhere someone had written down the formula for calculating the distance.  Remember that it’s not just a simple point to point calculation.  No, that would be too easy.  You see, the Earth is round.  (No, really.)  And the curvature of the Earth increases the distance between two points.  Oh, if only Columbus had been wrong: think of how much gas we’d save!

In fact, the formula for calculating this distance is known as the Haversine formula. You can read more about it, if you wanna; but it basically goes like this (in JavaScript):

var R = 6371; // circumference of the Earth, in kilometers
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
copy code

For our application, we basically want a view of our zip code data that represents the set of zip codes within X miles (or kilometers) of a given zip code origin.  We’ll use that view to filter our other records before returning those to the end-user.  To achieve this, I’ve written three MySQL procedures:

  • function km, which performs the calculation between two sets of latitude and longitude coordinates
  • function miles, which relies on km to do the calculation, and then converts the result to miles
  • procedure inside, which accepts two parameters: a zip code origin, and a maximum distance with units (km for kilometers, and mi for miles)

So, to query those zip codes within a ten mile radius of my home, the SQL looks something like this:

CALL inside('22601', '10mi');
copy code

And the results are a bit like this:

"22601",39.1697,-78.1686,"WINCHESTER","VA",0
"22604",39.1676,-78.1686,"WINCHESTER","VA",0.142347455024719
"22655",39.1634,-78.2462,"STEPHENS CITY","VA",4.17041921615601
"22656",39.2137,-78.0901,"STEPHENSON","VA",5.17398071289062
"22602",39.1501,-78.269,"WINCHESTER","VA",5.53605270385742
"22603",39.264,-78.1989,"WINCHESTER","VA",6.70094060897827
"22638",39.2369,-78.2885,"WINCHESTER","VA",7.90875339508057
"22624",39.2719,-78.0998,"CLEAR BROOK","VA",7.94622468948364
"22622",39.2543,-78.0664,"BRUCETOWN","VA",7.98962259292603
"22611",39.1357,-77.9919,"BERRYVILLE","VA",9.72859001159668
copy code

To increase the usefulness of the result set, we sort the results of the stored procedure by their distance from the origin: least to greatest.

I hope this turns out to be as useful for you as it is for us.  If you’d like to get a head start, feel free to download our source files:

Subscribe to Perseverance Trumps Talent