Returning an Alias from primary_links’s links['href'] – or – How I became a Drupal Wizard

I’m building a custom template in Drupal for the farm, and needed a way to spit out a custom implementation of primary_links navigation unordered list. I needed something like this:

<ul id="nav">
  <li class="first"><a href="link-goes-here" title="wow, how descriptive"><span>link 1 copy</span></a></li>
  <li><a href="link-goes-here2" title="round 2, FIGHT!"><span>link 2 copy</span></a></li>
</ul>

The spans inside the anchor tag are valid xhtml strict, and necessary for the nested/floating backgrounds. The l() function in drupal does a fine job of spitting out valid code, but I also haven’t found anything to delimit the angle brackets in the span element.
Here’s how I solved the problem.

First, I added a preprocess function in template.php and copied this chunk of code from root/includes/theme.inc:

function theme_links($links, $attributes = array('class' => 'links')) {
 global $language;
 $output = '';

 if (count($links) > 0) {
 $output = '<ul'. drupal_attributes($attributes) .'>';

 $num_links = count($links);
 $i = 1;

 foreach ($links as $key => $link) {
 $class = $key;

 // Add first, last and active classes to the list of links to help out themers.
 if ($i == 1) {
 $class .= ' first';
 }
 if ($i == $num_links) {
 $class .= ' last';
 }
 if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
 && (empty($link['language']) || $link['language']->language == $language->language)) {
 $class .= ' active';
 }
 $output .= '<li'. drupal_attributes(array('class' => $class)) .'>';

 if (isset($link['href'])) {
 // Pass in $link as $options, they share the same keys.
 $output .= l($link['title'], $link['href'], $link);
 }
 else if (!empty($link['title'])) {
 // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
 if (empty($link['html'])) {
 $link['title'] = check_plain($link['title']);
 }
 $span_attributes = '';
 if (isset($link['attributes'])) {
 $span_attributes = drupal_attributes($link['attributes']);
 }
 $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
 }

 $i++;
 $output .= "</li>\n";
 }

 $output .= '</ul>';
 }

 return $output;
}

I then modified the code as follows:

function stoneyacresfarm_links($links, $attributes = array('class' => 'links')) {
 global $language;
 $output = '';

 if (count($links) > 0) {
 $output = '<ul'. drupal_attributes($attributes) .'>';

 $num_links = count($links);
 $i = 1;

 foreach ($links as $key => $link) {
 $class = $key;

 // Add first, last and active classes to the list of links to help out themers.
 if ($i == 1) {
 $class .= ' first';
 }
 if ($i == $num_links) {
 $class .= ' last';
 }
 if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
 && (empty($link['language']) || $link['language']->language == $language->language)) {
 $class .= ' active';
 }
 $output .= '<li'. drupal_attributes(array('class' => $class)) .'>';

 if (isset($link['href'])) {
 // Pass in $link as $options, they share the same keys.
 // here are the modifications
 $temphref = drupal_lookup_path('alias', $link['href'], '');
 $output .= '<a href="' . $temphref . '" title="' . $link['title'] . '"> <span>' . $link['title'] . '</span></a>';
 }
 else if (!empty($link['title'])) {
 // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
 if (empty($link['html'])) {
 $link['title'] = check_plain($link['title']);
 }
 $span_attributes = '';
 if (isset($link['attributes'])) {
 $span_attributes = drupal_attributes($link['attributes']);
 }
 $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
 }

 $i++;
 $output .= "</li>\n";
 }

 $output .= '</ul>';
 }

 return $output;
}

Lines 29 & 30 are the brilliant bits. I scoured api.drupal.org till I found the  drupal_lookup_path function. Follow the link for the arguments that can be passed in. I specified that I wanted an alias, gave it the path ( links['href'] ), and passed in a blank character for language.

This is all rendered out in my page-front.tpl.php and page.tpl.php files in the standard fashion:

<?php if (!empty($primary_links)): ?>
  <?php print theme('links', $primary_links, array('id' => 'nav')); ?>
<?php endif; ?>

I hope this helps!

Logging, Logging & Mushrooms

I went out to the woods today, beyond the pasture, to haul back some oak with Tony & Big Ed, the midnight ploughboy. The plan is to let them incubate a bit, build a rack for them to stand in, and come spring, drill them out for shiitake spores.

We clung to the rickety wagon as Ed’s duct-taped gloves loosely clutched the steering wheel of an old, but still bright green tractor. Tony has probably done this a million times, but it was entirely new to me. The floorboards of the agrarian chariot were rotting and the metal carriage was loosely held together by ancient bolts – the flex would prove crucial on our return, wagon brimming with several thousand pounds of wood. Ed hopped off to fell an enormous oak, probably a good 80 plus footer. We went on to collect from a different section of woods before heading back.

There was a slight gap in the forest where the tree had been, as well as the other trees it leveled with it. It was all planned of course – Ed is a master. He knows the trees like an addict knows veins. The man is over six-foot-six and weighs a good 240 pounds. He is lean but colossal and muscular, as is Tony. Standing beside them, my six-foot-two frame seems diminutive in comparison. They toss logs like toothpicks.

So we loaded wood. Not 5, or 10 logs. Seventy is a low estimate. Five feet in length, 3 to 8 inches in diameter, we stacked them into the wagon steadily over the course of an hour and a half, whilst making a separate pile for firewood and branches that will quickly be consumed, back into the rich forest floor from which they came.

When we returned, we clung to the outside of the wagon, gas can clacking against the rails. A chain connecting the two side walls kept them from breaking from the immense weight of wood. The wheels bulged, almost flat, and the back end dragged through the deep snow troughs along the tractor path.

It was fun. Hard work, but fun – and a great way to stay warm and fit. The mushrooms will be included in the CSA baskets for the season, and sold to restaurants and farmers markets. I can’t wait.

Observations on an Early Saturday Morning

It’s a sweltering 7° Fahrenheit as we pull into Park Falls, Wisconsin for the Price County Democrats’ February meeting. The air smells of wood furnaces, and swarms of snowmobiles wait patiently to peel across the busy highway that defines the downtown strip. A sign at the bank informs: ATMs Now Here.

Well, it’s about Tyme.

5 Reasons I Rarely Buy Salted Butter

This is just a quick list, brought up by something I learned when I snagged a pound of salted organic butter from the fridge by accident (rather than the unsalted one) while making dough for tartes tonight (#3):

  1. Better control over salt content. Even with the nutrition label data, it’s hard to know exactly how much salt is in your butter – you could use butter to season your food, but that’s not really its job. Butter is my fat, not my salt.
  2. Salt is a preservative. I don’t have solid evidence for this, but I would assume salted butter is older when purchased than its unsalted counterpart. Even so, I’d rather buy it in smaller quantities and know it’s good rather than have a mummified beurre-pharaoh in my fridge.
  3. When making pie crusts or other similar doughs, salted butter melts faster and is MUCH harder to work with. There’s a reason it’s used on winter roads. This one is not so obvious, but very
  4. It overwhelms the subtleties that great butters can possess. This isn’t always true (see my post on Kiel), but it is the case more often than not. Fresh creamed butter, especially unpasteurized, it absolutely fantastic and unrivaled in complex flavors. I swear I can taste the clover and other pasture feed in some butters.
  5. I love fresh bread with butter – and this is the one case where salted butter might be better – but I generally go with anchovy paste for the salinity, or some other umami-laden saline vehicle.

Sorry salted butter: you’re…toast.

In Search of the Perfect Tom Kha Recipe

Tom Kha (or tom kar)  is sin in a bowl. And if I believed in the afterlife, I would wish that I would spend eternity as a fish swimming through vast oceans of unadulterated sin. Oceans of Tom Kha, that is.

For those unfamiliar with this unctuous soup, tom is Thai for “boiled” and kha means “galangal”. Galangal, or blue ginger, isn’t actually ginger but a close rhizome relative. Unlike it’s sharp, pungent cousin, galangal is sweet, citrusy and earthy. It’s skin is smooth and almost waxy and it’s hard root is moist but almost woody.

Traditionally, that’s the foundation. Adding to the flavor structure of the galanga is lemongrass, kaffir lime leaves, and Thai or Birdseye chilies all boiled in a mix of coconut milk and another liquid, typically water or stock. “Gai” which is Thai for chicken makes the soup into Tom Kha Gai. “Kûng” or shrimp makes it Tom Kha Kûng. You can use chicken or shrimp/fish stock respectively, or stick to water. Veggies and straw or oyster mushrooms are sometimes added. After everything has been briefly boiled, the soup should be removed from the heat and seasoned with lime juice, cilantro, fish sauce, and sugar. Don’t overdo the fish sauce – use salt instead to taste. The inedible lime leaves and slices of galangal remain present as garnishes, as a reminder of where the dish gets its subtle and unique flavors.

That’s the classic framework, anyhow.

The best Tom Kha I’ve ever had is far from classic. EE Sane is my favorite Milwaukee Thai restaurant. Their tom kha is thick, creamy, spicy, pungent, sweet and tart, all in perfect balance. The flavors fiercely fluctuate, competing for attention while its luxuriously thick and spicy coconut broth coats the palate. It’s not traditional. It is not subtle. The broth is orangey-red, laden with the oil of the chiles – exciting compared to the usual opalescent swirl of tropical milk. The lemongrass, galangal and lime leaves are not to be found with the usual suspects: shrimp, broccoli and the occasional baby corn. And I swear, they add cream.

Sometimes, tradition is wrong. I don’t think that’s the case with Tom Kha, but the subtleties of the traditional dish would have a difficult time competing with EE Sane’s implementation; after all, it is the latter against which I compare all other versions of this soup. I will someday post a recipe as close to EE Sane’s as I can achieve. Until then, good eating.

Bugs and Other IETester Issues

I recently started using IETester for visual and functional website development testing. While overall a nice product, there is a bug that might grow tiresome. I personally don’t mind. Commercial use might also come at a cost someday.

The Bug

The IETester software uses tabbed browing, as many modern browsers do. The difference is that each tab can be set to render using a different version of Internet Explorer, from version 5.5 through 8, including the system default. After 5-10 minutes of use (or idle), all but the main tab crash. This is not to say the whole program crashes. It is a multiprocess program, which means it can run instances of programs within it; each tabbed window is like it’s own instance of a browser, all hosted within a singular parent application (Firefox, Opera, Safari, IE 7 & 8, and others all use a similar process to render tabs). So, the good news is the whole program doesn’t take a dive. The bad news is you have to refresh the tabs somewhat regularly. Not such a big deal if you’re testing webpages anyhow.

The Potential Cost

IETester is free for personal and commercial use, for now. The company that makes it also produces DebugBar – a web development tool, which is free for personal use, but costs 59€ for commercial implementation after 60 days.That’s currently about $80, American. I would imagine once IETester is out of alpha/beta releases, a similar license would be implemented.

The Conclusion

Even if IETester didn’t have issues, I’d be hesitant to pony up. There are better free alternatives as mentioned in a recent post, including running a legit copy of Windows XP in VirtualBox, Sun’s Virtual Machine. That’s the path I’ll be taking soon, right after I figure out how to share files between host and guest. More on that in a post to come.

Fix IE 6 Floating Element Double Margin Bug

If you read my last post, you know that I develop for IE6 and why. As is the case, IE6 throws annoying little gems at us, like its non-compliant box model, and the subject of today’s blog: the ever persistent double margin bug.

As the title alludes to, IE6 doubles the right and/or left margin on a floated element unless you declare:

display: inline;

Don’t worry about block-level items losing their width or height: they won’t. This isn’t a comprehensive explanation of the fix; it works for the conditions specified.