What’s so Neue about Helvetica?

fonts
So, I was just reading Rani Molla’s post on GigaOM called What’s all the fuss about Apple and Helvetica Neue? and I felt compelled (as I so often do, about so many things) to comment on the issue here.

Contrary to how the GigaOM article seems to frame it, the controversy — the, if you will, fontroversy (I regret it already) — when Apple demoed iOS 7 at WWDC last month was not that they were switching to Helvetica Neue as the iOS 7 system font. It’s that they were switching to Helvetica Neue Ultra Light, a particularly delicate weight of the general Helvetica Neue font family. (I’ve read some things that suggest they’re reversing course on that decision based on developer feedback, but the GigaOM post doesn’t even touch that.)

The fact is, Helvetica Neue has been the iOS system font ever since the introduction of the iPhone 4. When the iPhone was first introduced, it used plain old Helvetica as the system font. But with the introduction of the Retina Display, Apple switched to the slightly more refined Helvetica Neue.

So the concern with iOS 7 is not Helvetica Neue itself — that’s been working out just fine. It’s this extra thin weight of the font, which becomes difficult to read at smaller sizes.

Personally I like Helvetica Neue Ultra Light. I think it continues the trend towards refinement Apple began with the switch to Helvetica Neue itself, and is demonstrated effectively in Cabel Sasser’s animated GIF featured in the GigaOM article. The version using Helvetica Neue Regular feels heavier and clunkier to me. That said, I do understand and appreciate the legibility concerns with Ultra Light at very small sizes.

I’m not sure how this will work itself out. I doubt Apple will switch to a different typeface, though they may increase the weight of the font in the final version of iOS 7. But part of the reason Apple went with Helvetica in the first place is that it’s neutral (at least in principle). It gets out of the way and isn’t distracting. It doesn’t convey any particular personality. It’s a “blank canvas” of a font, which makes it a perfect fit for iOS devices, where the device itself disappears and becomes the app you’re using. Developers don’t have to use the system font in their apps, but a lot of them do, and by keeping the system font as neutral as possible, Apple avoids predisposing apps to a certain personality or style.

This is exactly the opposite of the opinions expressed in the closing of the GigaOM article, and is I think the opposite of Apple’s intentions with the iOS experience. Using a custom font that “reinforces a more distinctive brand voice” would be the equivalent of sticking a big Apple logo on the front of the iPhone. Apple’s branding goes on the back (where it can be an effective marketing tool). It’s never a part of the user experience.

Maintaining session between SSL and non-SSL pages in CakePHP

It’s funny, in a way, that cms34 has been around for nearly five years now, and it’s only just become a real issue that we were not maintaining sessions between SSL and non-SSL pages. This is somewhat understandable: practically speaking, the only time it really matters to carry over the session between the two is when a user is logged in. (This might not be the case with all web applications, but for us, at least, there’s rarely enough happening in the session when a user is not logged in for it to matter.)

As it happens, not that many cms34 sites use SSL; not that many cms34 sites use the user login capability on the front end. And very few use both. But we’ve had a couple of new sites come online lately that do use both, and it’s become a bit of an issue.

The issue was exacerbated by the fact that I recently modified the Users controller to require SSL on the login page, if the site has an SSL certificate. Consequently there were issues with trying to access login-restricted, but non-SSL pages… redirect loops and other such fun.

What’s the problem?

The problem is simple: for obvious security reasons, sessions and cookies cannot be shared directly between two different domains. It’s possible (although less secure) to share them between both SSL and non-SSL on the same domain, and it’s also relatively easy to set them up to work between different subdomains. But if your SSL pages use a different domain name than the non-SSL pages, even if they’re on the same server, there’s no way to get them to automatically use the same session.

The solution (though still not ideal, as it can introduce the risk of session hijacking), as you’ll find in lots of places, is to pass the session ID as a query string variable. Then you can use that to restore the same session ID, even if it’s on another domain — as long as it’s on the same physical server.

Some improvements

There are two key improvements I made to the basic “pass the session ID in the query string” scenario.

First, when the session is created I am writing the user’s IP address (using $_SERVER['REMOTE_ADDR']) as a session variable. Then, when I am attempting to restore the session with the ID passed as a query string variable, I read the session file on the server first, and make sure the IP address in the file matches still matches the user’s current IP address. Only then do I restore the session.

Second, and this is an aesthetic issue almost as much as a security one, once the session has been re-established, and before any response has been sent, I strip the session ID out of the requested URL and redirect to that new URL. It’s all invisible to the user, and the session ID never actually appears in the browser’s address bar.

A look at the code

There’s a lot going on in the cms34 code, much of which is highly specific to this application. But in short the keys to making this work happen in two places:

UsersController::login()

I have a login() action in UsersController that handles all of the special functionality that needs to happen when a user logs in. The actual login itself happens “automagically” via AuthComponent, but Auth doesn’t know everything I need to have happen when a user logs in, so after Auth does its work, my login() action takes it from there.

Honestly not a lot needs to happen here to make this work. Just two things: you have to write the user’s IP address to the session as I noted above, and you have to pass the session ID in a query string variable on the redirect that happens when login is successful. My code looks a little something like this (note that I have an array in the session called Misc that I use for… miscellaneous stuff like this):

class UsersController extends AppController {

  var $name = 'Users'
  // Other controller variables go here, of course.

  function login() {

    // All of this should only run if AuthComponent has already logged the user in.
    // Your session variable names may vary.
    if ($this->Session->read('Auth.User')) {

      // Various session prep stuff happens here.

      // Write IP address to session (used to verify user when restoring session).
      $this->Session->write('Misc.remote_addr(',$_SERVER['REMOTE_ADDR']);

      // Some conditionals for special redirects come here but we'll skip that.

      // Redirect user to home page, with session ID in query string.
      // Make up a query string variable that makes sense for you.
      $this->redirect('/?cms34sid=' . session_id());

    }
  }
}

So far, so good. The rest of the excitement happens in…

AppController::beforeFilter()

Ah yes, the magical beforeFilter() method. There’s a whole lot of stuff going on in AppController::beforeFilter() in cms34, most of which is highly specific to our application. But this is where you will need to put your code to retrieve the session ID from the query string and restore the session… this function runs at the beginning of every page load on your site.

I’ve put this logic almost at the beginning of beforeFilter(), because we really do want that session restored as soon as possible.

Here’s a look…

class AppController extends Controller {

  function beforeFilter() {

    // Additional code specific to your app will likely come before and after this.

    // Only run if session ID query string variable is passed and different from the existing ID.
    // Be sure to replace cms34sid with your actual query string variable name.
    if (!empty($this->params['url']['cms34sid']) && $this->params['url']['cms34sid'] != session_id()) {

      // Verify session file exists.
      // I am using CakeSession; your session files may be elsewhere.
      $session_file = TMP.DS.'sessions'.DS.'sess_'.$this->params['url']['cms34sid'];

      if (file_exists($session_file)) {
        $session_contents = file_get_contents($session_file);

        // Find user's IP address in session file data (to verify identity).
        // The CakePHP session file stores a set of serialized arrays; we're reading raw serialized data.
        // If you used a different session variable name than remote_addr, change it AND the 11 to its string length.
        $session_match = 's:11:"remote_addr";s:'.strlen($_SERVER['REMOTE_ADDR']).':"'.$_SERVER['REMOTE_ADDR'] .'";';

        // User's IP address is in session file; so we can continue.
        if (strpos($session_contents,$session_match) !== false) {

          // Set session ID to restore session
          $this->Session->id($this->params['url']['cms34sid']);

          // Redirect to this same URL without session ID in query string
          $current_url = rtrim(preg_replace('/cms34sid=[^&]+[&]?/','',current_url()),'?&');
          $this->redirect($current_url);
        }
      }
    }
  }
}

A few final thoughts

I didn’t really discuss cookies at all here, but suffice to say there’s a cookie containing the session ID that gets written. If you’re only using cookies for the session ID (which is probably a good idea), then you don’t really need to do anything else with them. But if you’re writing certain cookies when a user logs in (like I do), you’ll need to write additional logic to restore them in AppController::beforeFilter(). In my case, the relevant cookies are all duplicates of session data, but are intended for a few edge cases where I need to access that information through JavaScript or in .htaccess files that are protecting login-restricted downloadable files — in other words, places where I can’t use PHP to look at session data.

You may also notice near the end of the code in the AppController::beforeFilter() example above that I am calling a function called current_url(). This is not a built-in part of PHP or CakePHP; it’s a simple little function I have in my config/functions.php file. Here it is:

function current_url() {
  return (!empty($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
}

There is no John Galt

Who is John Galt? The rhetorical question, posed frequently throughout the early sections of Ayn Rand’s epic tome Atlas Shrugged, continues to crop up here and there to this day, usually as a bumper sticker on the back of a BMW: the economic libertarian’s counterpart to the Deadhead dancing bears. It’s a codeword, the inverse of 420. If you know the answer to the question, you’re in the club of laissez-faire capitalists and would-be prime movers.

If you’ve never read Ayn Rand — and if you’re older than 20, you probably shouldn’t bother — you may still wonder just who John Galt is. Quick summary: he’s the bold visionary savior of capitalism, the person who would let the old world die so he and his disciples can shape a new one in the image of the dollar sign. If that still doesn’t answer the question for you, well, take some solace in the fact that the question probably isn’t really worth answering in the first place.

I’ve been thinking about John Galt more lately than I have in about 18 years, since the second and last time I read Atlas Shrugged cover-to-cover. I’ll admit, it can be a page turner for most of its (excessive) length, at least until the portion near then end where John Galt himself takes over the world’s airwaves and launches into a dry, rambling 80-page soliloquy laying bare Ayn Rand’s philosophy. But people don’t read Ayn Rand because her writing is so great. It’s not. They read Ayn Rand because her ideas are radical and liberating to ambitious minds that feel trapped in a society of conformist mediocrity.

In other words, her ideas are just what 15-year-old, Rush-and-D&D-obsessed nerds need to feel better about themselves in a world that rejects them for being different. At least, that’s what I thought her ideas were until I got really obsessed with them in college, moving beyond her novels to her collected non-fiction essays, along with those written by her “egoist” acolytes, including Alan Greenspan.

Yes, that Alan Greenspan.

I was pretty surprised to learn that the (at the time) Fed chairman was an Ayn Rand devotee, and it convinced me (at the time) that some day soon we’d see Ayn Rand’s philosophy rise up and vanquish the mediocrity of our soul-sucking society.

But then I grew up. I realized that her writing fell firmly in the realm of fantasy. And it wasn’t just that the “second-handers” of society that she described did not correspond in any recognizable way to anyone in the real world. It was that the leaders in her world — not just the godlike John Galt but the creators, the captains of industry, like Dagny Taggart and Hank Rearden — didn’t have any real-world counterparts either. People, in most cases, do not rise to power and wealth purely through their noble industriousness and hard work, just as people do not struggle with poverty because they’re lazy. The world Ayn Rand creates has a tantalizingly simple internal logic. Unfortunately, her world is a miserably inadequate model of the complex, messy external reality she believed her “objectivism” so clearly observed.

Still, all of this would be an academic exercise for me to ponder in my parents’ basement were it not for the likes of Alan Greenspan, and so many who have come after him: Ron and Rand (Rand!) Paul, Glenn Beck and Rush Limbaugh and Sean Hannity, the Tea Party and anyone whose ability to follow a thought through to logical conclusion is so broken that they somehow manage to espouse both Ayn Rand’s (aggressively atheist) philosophy and fundamentalist Christianity simultaneously. Check your premises, etc. etc.

What frightens me is that in the two decades or so since I outgrew Ayn Rand myself, and especially since the 2010 midterm elections, we’ve come to a point where we have people who embrace Ayn Rand’s philosophy, however contradictory their overall views may be, in positions of government power in the United States. People who apparently know (and, for that matter, care) so little about the way our government actually functions, yet who believe so fully — so faithfully — in the economic principles described in books like The Fountainhead and, especially, Atlas Shrugged, that they would run the metaphorical ship aground on these shaky premises, believing that allowing the United States to default on its debts, allowing the economy to crumble, would actually be a good thing, and would give them the opportunity to remake our government, our economy, our society, in a way more in line with Ayn Rand’s ideas.

But Ayn Rand wasn’t even a good science fiction writer, much less a good economist, and far less an astute, objective observer of the fragile complexities of human character and American society. If we allow our economy to collapse, if we make it collapse because we think we can start over from scratch with a (non-existent) team of all-star CEOs drawing up the blueprints, we will quickly learn the answer to the question. There is no John Galt.

Update (November 16, 2011): John Galt is getting some more attention lately, as apparently Lululemon loves him.

Quickly now…

Just a quick one, as I don’t have a lot of time to post… but of course, as usual, I had to make time for a detour into the madness of Internet Explorer when I discovered that my CSS line-height positioning was breaking when there was an inline image in the text. Why? Who knows? But the solution involved sticking vspace=”7″ into each offending image tag. Yet another case of hacking a one-off solution into the code to work around an Internet Explorer quirk. Almost as frustrating as IE’s violations of standards is the fact that there’s almost never a one-size-fits-all workaround!