Everything In Between

The brutally honest, first-person account of Meitar Moscovitz's life.

Archive for the ‘PHP’ Category

Quick and Dirty: Clone Custom Field, Template Linked Files on Movable Type

one comment

Movable Type is a pretty frustrating platform to work with because every so often (or, “way too often,” depending on who you ask) a function of the system simply doesn’t do what you’d expect it to do. Such is the case with the “Clone Blog” functionality. Although it dutifully copies most of a website from one “blog” object to another, a few things are missing.

Most notably, custom fields and templates’ linked files are not copied. This is a deal-breaker for any large installation that uses the built-in MT “Clone” feature.

To get around this limitation, I wrote a stupid, quick ‘n’ dirty PHP script to finish the cloning process, called finishclone.php. It takes only 1 argument: the “new ID” you are cloning to. If all goes well, you’ll see output like this:

[root@dev www]$ php finishclone.php 28
Cloning process complete.
[root@dev www]$ 

In this example, 28 is the newly created blog’s ID. The blog you want to clone from is set as a constant within the script. I’ll leave modifying the script to support more flexible command line arguments as an exercise for the reader.

<?php
/**
 * Ease the final steps in cloning a Movable Type blog.
 *
 * Description:   This script should be run after Movable Type's "Clone Blog"
 *                function has completed and before the cloned blog is used.
 *
 * Author:        "Meitar Moscovitz" <meitar@maymay.net>
 */

// Set constants.
define('MT_ORIG_BLOG', 0); // the ID of the blog you are cloning from
define('MYSQL_HOST', 'localhost');
define('MYSQL_USER', 'movabletype');
define('MYSQL_PASS', 'PASSWORD_HERE');
define('MYSQL_DB', 'movabletype');

// Get command line arguments.
if (2 > $_SERVER['argc']) { die('Tell me the ID of the blog to clone into.'); }

$blog_id = (int) $argv[1];

// Connect to db
if ( !mysql_pconnect( MYSQL_HOST, MYSQL_USER, MYSQL_PASS ) ) {
	die( 'Connection to the database has failed: ' . mysql_error( ) );
}
mysql_select_db( MYSQL_DB );

// Clone custom fields.
$result = mysql_query('SELECT * FROM mt_field WHERE field_blog_id='.MT_ORIG_BLOG.';');
while ($row = mysql_fetch_object($result)) {
	mysql_query(
		sprintf("INSERT INTO mt_field ("
			."field_basename,"
			."field_blog_id,"
			."field_default,"
			."field_description,"
			."field_name,"
			."field_obj_type,"
			."field_options,"
			."field_required,"
			."field_tag,"
			."field_type) "
			."VALUES('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');",
			mysql_real_escape_string($row->field_basename),
			mysql_real_escape_string($blog_id),
			mysql_real_escape_string($row->field_default),
			mysql_real_escape_string($row->field_description),
			mysql_real_escape_string($row->field_name),
			mysql_real_escape_string($row->field_obj_type),
			mysql_real_escape_string($row->field_options),
			mysql_real_escape_string($row->field_required),
			mysql_real_escape_string($row->field_tag),
			mysql_real_escape_string($row->field_type)
		)
	) OR print mysql_error() . "\n";
}

// Link template files to filesystem.
$arr = array();
$result = mysql_query('SELECT template_name,template_linked_file FROM mt_template WHERE template_blog_id='.MT_ORIG_BLOG.';');
while ($row = mysql_fetch_object($result)) {
	$arr[$row->template_name] = $row->template_linked_file;
}
foreach ($arr as $k => $v) {
	mysql_query("UPDATE mt_template SET template_linked_file='$v' WHERE template_blog_id=$blog_id AND template_name='$k';");
}

print "Cloning process complete.\n";

Written by Meitar

June 8th, 2010 at 8:19 pm

Posted in Crosspost,PHP,Programming

Tagged with

Add a post limit and output format to the WordPress Category Posts plugin v2.0

leave a comment

Tonight I wrote a quick (and idiotic) patch to the very simple WordPress Category Post plugin v2.0. This backwards-compatible patch features:

  • parameter-based post limit to define how many posts the plugin function will print
  • parameter-based format option to output the posts in real <li> elements

The wp-category-posts.php patch file is available for download here. To apply the patch, run the following commands at your shell promp:

cd path/to/wordpress/installation/wp-content/plugins/wordpress-category-posts
patch -p0 < path/to/downloaded/wp-category-posts.patch

I’m hoping this will get integrated as the next version of the plugin, perhaps version 2.1.

Written by Meitar

September 19th, 2008 at 10:24 am

Arbitrarily exclude posts from displaying in WordPress

8 comments

When hacking away at WordPress sites, often times you’ll find yourself in a situation where you need to filter out certain posts from displaying on some pages, such as the home page. There are a lot of ways to do this, but few are perfect. Recently, I had the need to do this and went searching for pre-existing solutions.

I came across Vaibhav’s post on the topic and noted that his solution uses the query_posts() function to alter WordPress’s query object before The Loop has run. While this is a great solution if your exclusion criteria is simple enough to be supported directly by the WordPress query object, other times the query_posts() function doesn’t provide you with the hook you need.

In these cases, you can run the original query, note any modifications you need to make, and then create a new, modified query and display the results you get from running that one instead. For instance, you might need to do this if you need to exclude posts based on category and, say, the beginning of their title, or their category and a certain piece of content in the post itself, or all three, or any other combination you can think of.

Another advantage of this technique over simpler ones is that this method maintains the same behavior you’d expect to see in every other way. Most notably, this means that if you’ve told WordPress to display the 10 most recent posts on the home page (in the WordPress settings), you’ll still see ten posts on that page even after you exclude some of them.

To do something like excluding posts if they are in the “Uncategorized” category (traditionally the category with an ID of 1 in WordPress) and their title begins with “Some title”, you can do this:

// original query runs in The (real) Loop first
while ( have_posts() ) : the_post();
    // detect pots matching our exclusion criteria
    if (in_category(1) && (0 === strpos(the_title('', '', false), 'Some title')) ) {
        $wp_query->post_count++; // increment the post counter
        continue;
    }
    endif;
endwhile;
// now make a new query and show the posts for real, with the adjusted post count and filtering
$my_new_query = new WP_Query($query_string.'&showposts='.$wp_query->post_count);
// do another The Loop (and display the results this time)
while ( $my_new_query->have_posts() ) : $my_new_query->the_post();
    // detect and exclude these same posts
    if (in_category(1) && (0 === strpos(the_title('', '', false), 'Some title')) ) { continue; }

// ...the rest of the WordPress template goes here...

This is neat because it gives you the capability to define arbitrarily complex exclusion patterns and directly modify your new query object however you like before you execute it. Once you know this works, you’ll probably want to extract the filtering code into a function. Using the above example, your new code might look like this:

// define criteria for filtering
function matches_filtering_criteria () {
    if (in_category(1) && (0 === strpos(the_title('', '', false), 'Some title')) ) {
        return true;
    } else {
        return false;
    }
}
// original query runs in The (real) Loop first
while ( have_posts() ) : the_post();
    // detect pots matching our exclusion criteria
    if (matches_filtering_criteria()) {
        $wp_query->post_count++; // increment the post counter
        continue;
    }
    endif;
endwhile;
// now make a new query and show the posts for real, with the adjusted post count and filtering
$my_new_query = new WP_Query($query_string.'&showposts='.$wp_query->post_count);
// do another The Loop (and display the results this time)
while ( $my_new_query->have_posts() ) : $my_new_query->the_post();
    //
    // detect and exclude these same posts
    if (matches_filtering_criteria()) { continue; }

// ...the rest of the WordPress template goes here...

For more information on these functions, see:

Written by Meitar

June 6th, 2008 at 1:11 pm

WordPress Collapsible Archive Widget 2.1 BETA with Collapsible Month Lists

leave a comment

I went searching for a WordPress widget that enabled me to place a collapsible archive control much like the ones available in Blogger blogs “archives” widget a while back. With a little googling, I found a plugin that did almost exactly what I wanted in Ady Romantika‘s Collapsible Archive Widget.

After a few days of playing with it, I had a working copy that reproduced the original behavior or, optionally, exposed each month’s individual posts inside each month’s own collapsible list.

The patch I submitted to Ady is still awaiting integration with the main plugin, so in the meantime you can download the plugin here.

The full description follows:

Collapsible Archive Widget 2.1 BETA is an update to the Collapsible Archive Widget for WordPress that includes a new option in the widget’s settings page that allows you to display links to the individual posts inside each month. With this setting activated, the year is expanded by default to show a list of months. Each month is another list, collapsed by default, that expands to reveal the individual post links.

Update: A new version of the plugin that incorporates the options I’ve added can now be downloaded from the WordPress plugin directory.

Written by Meitar

October 23rd, 2007 at 1:35 pm

Tom Whitbread on Building a MySQL Database Abstraction Class in PHP

one comment

Congratulations to my good friend Tom Whitbread on his recent publication in php|architect, Building a MySQL Database Abstraction Class.

Database abstraction is not just useful to promote database agnosticism—it can be used to improve database interaction all around! In this article, Tom Whitbread shows you how to tame the MySQL API by creating a class which will handle errors, allow query execution, transport results, and strip or add slashes to your input data.

Great work, Tom, and thanks for sharing your work with the community.

Written by Meitar

April 27th, 2005 at 4:04 pm

Posted in General,PHP

Using Good URL Design to Create a Breadcrumb Navigation System In PHP

6 comments

One fundamental aspect of any good navigation system is a mechanism to let users know where they are in relation to the rest of the content. On the Web, one of the most popular and thoroughly studied ways to reflect this information is in a so-called breadcrumb navigation trail or bar. This navigation bar lists the categories and subcategories (or sections and subsections) of the page which the user is browsing.

(Quick-minded readers will be quick to point out that the term breadcrumb navigation trail, which originates from the Hansel and Gretel story, is not entirely accurate. In the story, Hansel and Gretel left a trail of breadcrumbs as they wandered through the forest so they could retrace their steps back home. On a web site, the breadcrumb navigation trail reflects the current page’s position in the site hierarchy, not the path the user has taken through the site.)

Defining the Requirements of Breadcrumb Navigation

When I started looking for ways to easily implement a breadcrumb navigation trail on my web site, I immediately saw the potential complexity of the project. The hierarchical navigation system needed to:

  • Accurately reflect a page’s position in the hierarchical information architecture of the site and not the physical location of the page in my web server. (This is important for me because I’m constantly moving things around.)
  • Present a nicely formatted label for each level of depth.
  • Automatically adjust itself for new pages, so I would never have to deal with it when adding new sections to my site.
  • Not require any “heavy” database or large application to use and scale.

Additionally, the module should:

  • Produce semantic, valid HTML marked up as a list.
  • Link all elements in the list to their appropriate page level.
  • Not link the last level, which should display the current location of the user in the site. (We don’t need a link back to the same page we’re already on.)

Searching for Inspiration

The first thing I, like any good programmer, went to do was search for existing code. Unfortunately, there is an appallingly small amount of information on implementing breadcrumb navigation out there. Sure, they’ll tell you what it is and what it’s good for, even convince you why you should have it, but as for actually implementing it goes…sorry, you’re on your own.

Every tutorial I found on the matter was decidely sub-par or completely failed to meet my requirements, listed above. Most sites stated the need for databases to create truly scalable and customizeable breadcrumb trails. Of course, nothing can compete with a fully integrated RDBMS solution, but with intelligent information architecture you can come pretty darn close.

Using the URL to Our Advantage

Thankfully, there is already a system of hierarchical navigation readily available to us. In fact, there are two!

  1. Directory trees on your web server. The folders in which your files are stored are probably already sorted somewhat logically and categorically.
  2. The URL of the requested page. The address bar in your browser drills down through these categories to get to the final page.

Both of these systems provide a basis from which we can draw upon to create a simple, self-organizing breadcrumb navigation system on a web site of almost any size. However, there are a few problems with the first method. (If you want to implement that method anyway, here’s a tutorial based around using a directory tree.)

  • It doesn’t scale. If you’ve got a huge web site, you’re not going to keep all that data on static files on your web server. It would also be a full-time job to categorize and sort that content on said server! Oi, vey!
  • It’s dependant on your organization system. If you’re anything like me, you don’t necessarily have folders within folders within folders just to store files, and if you do, you probably change them around pretty often to suit your needs. Unfortunately, basing breadcrumb navigation on your view of your information as opposed to your visitor’s view of the same information is a recipe for disaster in all but the smallest web sites.

Thus, the only remaining solution was to rope the URL of the page into service to serve as the basis for a breadcrumb navigation system. Even so, there is one huge obstacle to overcome with this method. The URL itself must not be attached to a physical filesystem on the web server.

Otherwise, we’re just basically using the filesystem hierarchy, which we’ve already seen is not adequete for our needs. So we need a way to abstract, or detach, the URL from the filesystem itself. Enter mod_rewrite, stage left.

Abstracting the URL from the Filesystem

Abstracting the URL from the filesystem is the critical step to enabling truly scalable, self-organizing breadcrumb navigation using such a simple foundation. This is essential because the URL of your page is the thing that will always be live; if the URL changes, so has your page! This change will then be reflected automatically in the breadcrumb navigation trail.

There are a number of other benefits to abstracting the URL of a page away from the physical filesystem it represents. I’ve even talked about some of them before when I blogged about playing with Apache and when I suggested tips for improving search engine rankings of CMS-generated content.

The basic prerequisite for a good URL schema, however, is solid information architecture. This means having a well-defined and clear structure to your site so information is easily findable and properly labelled, categorized, and placed within the system you use. Only after you create a sound URL design schema free of any unsightly cruft which succinctly encodes the hierarchy of your site will you be able to leverage them to their fullest potential. (Phew, that was a mouthful!)

So, since this is not a post about URI design (see these resources for articles on that), let’s assume you’ve done all that and are ready to implement your schema. The tools you’ll need are:

  • The Apache web server.
  • Its mod_rewrite module.
  • Access to the server configuration file or the ability to use .htaccess files.
  • Later on, you’ll also need PHP, for the coding thing.

To make things simple, I’ll use my site as an example.

For my own site, I have several sections (“About,” “Services,” “News & Weblog,” etc.) and several top level pages that stand on their own (“Home,” “Contact”). Additionally, each section has an index page to ensure that no hierarchical level of my site is without its corresponding landing page.

This was going to be represented with straight-forward URLs that looked like this:

  • The “Home” page would have this URL: http://www.maymay.net/maymaymedia/
  • So-called “top-level” pages would look like this:
    • For the “Contact” page: http://www.maymay.net/maymaymedia/contact/
    • For the “About Us” page: http://www.maymay.net/maymaymedia/about/
    • For the “Services” page: http://www.maymay.net/maymaymedia/services/
  • Second-level pages would have URLs such as these:
    • For each service:
      • http://www.maymay.net/maymaymedia/services/web-programming/
      • http://www.maymay.net/maymaymedia/services/web-accessibility/
      • http://www.maymay.net/maymaymedia/services/web-site-optimization/
    • For each “About” page, for instance:
      • http://www.maymay.net/maymaymedia/about/accessibility/
      • http://www.maymay.net/maymaymedia/about/philosophy/
      • http://www.maymay.net/maymaymedia/about/technology/

My files are stored pretty simply on my web server. Some pages, like the “Contact” page are stored in the maymaymedia directory but not in their own sub-directory. Other pages, like the “Services” and “About” pages, are stored in the services and about directories, respectively

This created filenames like maymaymedia/contact.php and maymaymedia/about/index.php. That .php stuff had to go!

Writing Your .htaccess File

Using the magic of mod_rewrite, a few relatively simple lines added to your .htaccess file is all it takes to turn these filenames into these beautiful cruft-free URLs.

Creating the sections was easy enough. Some of the work was already done by keeping the pages in their own directory (such as “Services,” for me) so I didn’t even have to do anything. Of course, to be safe, I could future-proof my site to abstract these directory trees using additional lines in my .htaccess file, but for the sake of simplicty we’ll leave it as is.

Here are a few relevant snippets from my .htaccess file:

RewriteEngine On
RewriteBase /maymaymedia/
[…]
RewriteRule ^about/technology/?$ /maymaymedia/about/tech.php
RewriteRule ^about/philosophy/?$ /maymaymedia/about/philosophy.php
RewriteRule ^about/accessibility/?$ /maymaymedia/about/access.php
[…]
RewriteRule ^services?/web-site-optimization/?$ /maymaymedia/services/wso.php
RewriteRule ^services?/web-programming/?$ /maymaymedia/services/webprogramming.php
RewriteRule ^services?/web-accessibility/?$ /maymaymedia/services/accessconsult.php
[…]
RewriteRule ^contact/?$ /maymaymedia/contact.php

These merely map simple URL‘s onto the appropriate files for the specific page on my server, creating the nice breadcrumb-like URI structure we can now use to automate the process of generating our hierarchical navigation bar.

There are two main points to keep in mind when creating such URL schemes.

  • Use a standardized syntax.

    Don’t mix and match symbols. Choose one syntax and stick with it. I used a hyphen (-) to simulate spaces, since spaces are not allowed in URIs. You could also use the underscore (_) if you wanted, but hyphens don’t require users to press the shift key.

  • Make the URL descriptive.

    Since we’re going to be using these URLs as our breadcrumb navigation labels, they need to be as descriptive as possible. Keep them short enough to easily type but long enough to provide context. For instance, I could have used wso instead of web-site-optimization but how many people know that that’s what WSO stands for?

    Besides, including the whole phrase boosts our search engine rankings by embedding our keywords into the URL itself! A nice bonus which also enhances usability.

    If you’re really worried about optimizations (for instance, because you’ll have to write all your links like href="/services/web-site-optimization/") then you can use a redirection script and point your links at something like href="/r/wso/" which would then automatically redirect to the expanded URL.

Coding the PHP Breadcrumb Navigation Bar

Now we’re ready to leverage the inherent advantages of good URL design to create our scalable PHP breadcrumb navigation script. I use a special PHP script called navbar.php to dynamically generate all of my navigation, so that’s where the code will go. navbar.php can then be SSI‘ed or include()‘ed on our page template so it will be present in all the pages we create from here on out.

The breadcrumb navigation script needs to do a few things. The code flow looks like this:

  • Grab the requested URI and break it into its path components.
  • Initialize (or remove) the first (“top”) element.
  • Count the remaining elements.
  • Check for trailing slashes on the last element, and remove it if necessary (such as if its empty or it’s just a query string).
  • Loop through all the elements, placing them within <li></li> HTML elements as well as <a></a> if we are not at the last level.

Stepping Through the Code

First, we start our HTML by providing a header (which we can later set to display:none; in our CSS) and starting the list element. Due to the specific nature of my site structure (the site starts in a sub-directory of my main site), I also chose to always print the main “Maymay Media Home” link. You don’t have to do this.

<h3>Hierarchical Navigation</h3>
<ul id="hier-nav">
<li id="hier-homeLnk"><a href="/maymaymedia/" title="Maymay Media Home">Maymay Media</a></li>

Next, we start the PHP magic, grab the URI the browser requested and break it on the slashes with explode().

<?php
$url_parts = explode('/', $_SERVER['REQUEST_URI']);

Now the $url_parts variable contains an array, each element of which is a segment of the path from the requested URI. Since a URI request always starts with a leading /, the first element of our $url_parts variable is an empty string. That’s somewhat useless for us, so we’ll get rid of it.

array_shift($url_parts); // first item always empty in URLs

In my particular case, the second element will always correspond to the maymaymedia sub-directory I have my site in. Thus, the second element of this array is also useless to me, since I chose to always print this “top level” link before we even started the conditional code. Thus, I get rid of the second element, too. (You may not want to do this in your script!)

array_shift($url_parts); // second item unneeded since always contains "maymaymedia" due to my site structure

Now we need to count how many elements are left in our array which will tell us how many list items we need to make and where to find the end of our array so we can identify the last element. Remember, we’re going to need to check if the last element is empty or not because we don’t know if the visitor entered a trailing slash in the address bar.

If the last element is empty like the first one then we don’t need it. We also don’t need it if all it contains is a query string, so in either case we can pop it off the end of our array and decrement the variable we use to store the size of our array.

$num_parts = count($url_parts);
// remove last element if empty or if a query string
if (empty($url_parts[$num_parts-1]) || $url_parts[$num_parts-1] == '?'.$_SERVER['QUERY_STRING'])
{
    array_pop($url_parts);
    $num_parts--; // decrement to keep track
}

Now we have all the data we need to construct our breadcrumb navigation list. The first step is to start the second list item and insert a separator (I chose a “>” character), and then to format our URL strings into a more human-readable format. Basically, that just means replacing all of the “-” characters with spaces, and then capitalizing the first character of all the resulting words.

for ($i=0; $i<$num_parts; $i++)
{
    echo '<li> &gt; ';
    $p = ucwords(str_replace('-', ' ', $url_parts[$i])); // format 'things-like-this' to 'Things Like This'

(Of course, advanced CSS coders will note that inserting a physical character in the HTML markup is unnecessary since we can use a CSS rule such as #hier-nav li:before { content: ">"; } to create this presentational separator instead. Unfortunately, Internet Explorer does not support this level of CSS yet, so we must resign ourselves to use a physical character in the markup for now.)

Then we make a quick check to ensure we’re not at the end of the trail. If indeed we’re not, then we need to turn our text into a link and point it to go to the appropriate level. Otherwise, since we’re at the end of the trail, we just print the text and end our list item.

Creating the link looks complicated, but it’s really not. Our array stores all the path components for us, so all we need to do is slice off the end of it so all that remains is the first elements we’ve already looped through.

Once we’ve done that, we merely reassemble our URL by reversing the formatting process we used before. Namely, we implode() our array to turn it into a single string connected with the slashes, replace all spaces with dashes (-), and then turn the whole thing lowercase. We can do all of it on one line.

Also note that due to my particular site structure I’ve hard-coded the links to point to the root directory of this site. You’ll probably want to remove that on your own script.

    // only link if not last time through loop
    if ($i != ($num_parts-1) )
    {
        echo "<a href=\"/maymaymedia/".strtolower(str_replace(' ', '-', implode('/', array_slice($url_parts, 0, $i+1))))."/\" title=\"Go up to $p\">$p</a></li>";
    }
    else // last time through loop, so don't do the link
    {
        echo "$p</li>";
    }
}
?>
</ul>

That’s it! After closing our list element, we’re done, and we now have scalable breadcrumb navigation based on an intelligent URI schema on every single page. Was it good for you too?

Additional URI Design Resources

(Some segments shamelessly swiped for my own easy reference from Pixelcharmer.)

A list of resources that argue for and suggest best practices in URI construction.

By the way, here’s the difference between the two: What is a URL? What is a URI?

Written by Meitar

March 21st, 2005 at 6:38 am

14 Hour Coding Session

4 comments

Oh my god, fourteen hours playing with XHTML, CSS, and PHP to get Maymay Media to what it is now. (And it’s still not done, though I’m constantly looking for feedback!)

Much, if not most of that time was spent writing and editting new content, and beating down CSS bugs in Internet Explorer. Those of you who know my work habits are likely to be surprised by the fact that I could spend 14 hours dealing mostly with client-side coding issues.

Normally I can’t spend more than a few hours—at the most—dealing with CSS problems because browser bugs tick me off too much. (Ahem, MicroSucks.) Nevertheless, for some miraculous reason, I kept swatting bug after bug after bug this session and the enthusiasm just fueled more coding. Needless to say, I was very very productive last night.

I also didn’t sleep a wink, however, so now I’m dead tired. Here’s a short recap before my memories fall down behind the waterfall in my mind.

Last Night’s Call-a-thon and Digital Departure

  • Last night I was Mr. Popular. I got literally countless phone calls (I can’t remember them all) from folks who wanted to chat it up. I spoke to a few Meetup group members, Danica, my parents, and others. Christine called and wanted to hang out today but she canceled this afternoon. (I didn’t mind, I’m exhausted.)

  • Danica came over from about 8:30 PM to 10:30 PM so that she could give me back the apartment keys and transfer her digital posessions (computer files) off of my computers and onto her own. She also gave me a Moldy Peaches album to import into my iTunes Library. (Cool.)

    So that’s pretty much it. We said goodbye, she seemed sad, and I asked her to call me so I knew she’d gotten home okay. She did, and we said goodbye again. And that’s that; we are finally going our separate ways and have no more connection to each other whatsoever.

    I’m not even thinking about the future, but I do admit that remaining friends is something I’d probably like—if it were not emotionally draining. We’ll see what happens, but the path for right now is very clear: I’m trekking it solo yet again!

The Geeky Parts

  • Back to the tech realm, I’ve implemented my dynamic relevancy message API on this blog. What does that mean for you? Probably nothing, but it does mean you’ll likely see relevancy messages change and grow in abundance over time. As always, use a modern browser to see the full effect.

  • Still in the tech realm, I squashed a number of annoying IE CSS bugs on this blog with the somewhat-magical application of position:relative;. This means visitors using IE will finally get a near-perfect browsing experience, with no annoying vanishing backgrounds anymore.

  • Also, Google SiteSearch has been implemented, so you’ll be seeing that on the top banner for the time being. It’s nothing special, just a way to make it look like you can do more than you can here. It might also make me some more money via clickthroughs, but that’s unlikely. (Oh, which reminds me, those ads on the right side of the window, yeah, if you click on those I get money. Don’t be shy.)

  • A not-so-new but still interesting point of note is that I’ve started using MyBlogLog.com to keep track of which hyperlinks people are clicking on. This has been somewhat informative because it means I get to see which hyperlinks induce clicks and which don’t. Anchor text is important, and this is a useful tool to learn how you can manipulate the effect of a link.

Flickr Photos from the Past!

Finally, I’ve been wanting to get more value out of my digital camera. I haven’t been taking pictures, but backing up and transfering a bunch of Danica’s things last night inspired me to dig through some of my old photo collections to see what could turn up. Well, here it is:

  • Here’s me almost a year ago, during last year’s Passover at my mother’s:

    Me sitting at the table during Passover, 2004

  • Me at San Francisco’s Exploratorium with Danica during the Summer of 2004, on my trip to meet her family. Look at the face I’m making, I had so much fun there.

    Me sitting on the big chair at the San Francisco Exploratorium.

  • Yeah, I was gonna eat that piece of toast. Obviously, my dinner plans were foiled.

    I look at the camera sadly, holding burnt toast in my hands.

  • This is what happens to my face if I don’t shave.

    Me smiling, sunlight clearly showing my unshaven face.

Written by Meitar

March 19th, 2005 at 7:13 pm

Targetting Relevance in Long Web Pages with the CSS :target Pseudo-Class

5 comments

The Importance of Context and Relevance

Context and relevance are two of the most important aspects of effective communication. If the context of a message is not understood then the commuication will likely be disregarded, or worse—misinterpreted. If the message isn’t relevant to the topic at hand then it will be useless to everybody involved.

In order to ensure that a message is successfully received and has the desired effect, one must first ensure that the context for that message is properly set up and that the message itself is relevant to the subject matter.

An excellent example of a scenario where a loss of context and/or relevance can easily cause communication problems is receiving email messages. Surely everyone has recieved an email that they simply don’t know what to do with or why they’ve gotten it. (By the way, for some unbelievably insightful tips on composing effective email messages, read this article [via 43 Folders].)

The same thing can—and does—happen on the web. For example, let’s say you’re reading a page which has a link in its text to another page. You decide to follow the link but once you’ve arrived at the new page you find yourself staring at an enormous amount of other text that doesn’t have anything to do with why you followed the link in the first place. Suddenly you’re confused, you feel lost, and you quickly push the “Back” button of your browser.

What went wrong here? Context was lost; the destination page had no relevance to the source page. If it did, you couldn’t find it because it wasn’t clearly identified. This is a confusing and frustrating situation, and one that I’m willing to bet happens way too often on the web.

On my own blog, I frequently link to entries I’ve previously written. Sometimes these entries can be quite long, and I’m not always referencing the entire entry in my link. But by pointing the link to a specific “intra-page” anchor by using a fragment identifier and clearly marking the relevance of this information in both the link itself (with a descriptive title) and in the landing page (with what I’ve come to call a relevancy message), context is preserved and confusion is eliminated.

To see what I’m talking about in practice, check out my experiment pages. It’s a demo of the problem and relevancy messages in action.

CSS2 The Rescue

There’s only one problem left. How does one insert text into the landing page which will be viewable when someone follows a specially-crafted link to it, but not when they arrive at the page via any other link? Though it is possible to program this into your pages using PHP or another web scripting language, CSS level 2 offers a much simpler and maintainable solution: the :target and :before pseudo-classes.

When used in combination, the :target and :before pseudo-classes can be used to generate text on a page at a specific point in the document when your page is linked to in a certain way. By using an additional CSS2 function, attr(), we can also generate customized text for each landing spot within the document. Let’s take a look at each component separately.

The HTML You’ll Need

Let’s say you’re writing about apples. In your text, you link to a previous article you wrote about oranges. Your link in the article on apples might look like this:

<a href="oranges.html" title="Oranges have a thick, white pith beneath their skin." >oranges</a>

And your text in the article on oranges might look like this:

<p>[…] The skin of an orange is thick, porous, and varies in color from deep to light orange. There is a bitter-tasting white pith underneath the outter-most layer of the fruit's skin. […]</p>

In order to link directly to the paragraph mentioning the orange’s pith you need to insert a named anchor just after the opening paragraph tag where that content was written. You also need to append this name to the URI of the link, as a fragment identifier. The resulting HTML for the link might look like this:

<a href="oranges.html#skin" title="Oranges have a thick, white pith beneath their skin." >oranges</a>

The resulting HTML for the paragraph about oranges might look like this:

<p><a id="skin"></a>The skin of an orange is thick, porous, and varies in color from deep to light orange. There is a bitter-tasting pith underneath the outter-most layer of the fruit's skin.</p>

The CSS You’ll Need

Once the HTML is in place, you need to create a style rule to generate the text you’ll want to display before the paragraph. Your style sheet might look like this:

a:target::before { content: "Here's what I referenced with my link on the last page: "; font-size: larger; font-weight: bold; }

You can even take it one step further. By adding a title attribute with some helpful text in your named anchor, you can actually display that text (specific to the reference you’re making) instead of the generic “Here’s what I referenced…” text. The CSS for that would look like this, and it would display whatever you write as the title for each named anchor you use:

a:target::before { content: attr(title); }

Limitations

The major drawback to this technique is that you need to change the code of the destination document for it to work. Specifically, you need to insert named anchors (though you can also target any element by giving it an unique id), and you need to insert a style rule. (However, see below for another experiment that will let site owners allow people to get around this limitation.)

Another major drawback is that it requires a fully CSS2-compliant browser. That means Internet Explorer 5 and 6 (both Macintosh and Windows versions) will not show the special content on the destination page. They will, however, move focus to the point in the document at which the anchor resides.

Looking to the Future

I have been playing with this technique recently on my blog (can you find the links I’ve used this on? ;). The obvious benefit is creating a far more seamless transition from one page to another and maintaining the same context across web pages. This is invaluable for me and my readers in particular, since I tend to enjoy writing and end up writing far longer entries than I probably should. (Even though I use headings to divide my entries into logical chunks, it’s still good to have another trick up my sleeve to help usability!)

However, this technique is ripe for expansion. For instance, I have created another experiment to dynamically generate the content of the CSS rule based upon a variable in a GET querystring. (It uses three lines of PHP, and that link itself is a proof-of-concept.) This will allow people to write links that point to my documents and insert their own special relevance-describing blurbs in front of the fragment they’ve linked to. (Yes, I realize this may pose XSS security problems, I’m still just playing with the concept.)

Exploring all of this is a lot of fun. The crux of this technique relies upon accessible and clever hypertext copywriting to enhance usability when linking to web pages and referencing information across pages or, one day, sites. As is the case with most things Web, simple is better.

I’m very eager to hear about comments on this technique. Are there dangers I haven’t seen yet? Can you think of another way to use it that I haven’t mentioned here? Are you already using it on your own site? All feedback welcome. :)

Written by Meitar

March 11th, 2005 at 3:24 pm

Getting PHP Variables into Apache Logs

4 comments

Gleaning information about the various things that are going on inside of a compuer or on a web site can be a somewhat difficult process. System logs are probably the most useful source of this information, but sometimes (especially for web sites) they don’t necessarily record all the interesting data. One example of a situation where this may be true is when you want your Apache logs to record your web site’s user session IDs.

Since PHP variables are known only to the current PHP process and not to Apache, you can’t just plug them into your Apache system log without first informing Apache about the variable you want to record. This can be done in two nearly identical ways, though the implementation of each is somewhat different. Luckily for the PHP progammer, it makes very little difference which way you choose.

Apache Notes

One way of getting Apache to recognize PHP session variables, or any variable for that matter, is to leave a note in Apache’s notes table via the PHP function apache_note(). An Apache note can be thought of as a reminder from one Apache process to another that a certain variable exists, and what its value is.

Thus, doing apache_note('userID', $_SESSION['userid']) will make the PHP session variable userid available to Apache as userID via an Apache note. Calling apache_note() from another page with only the first arguement ('userID') will retrieve the note from Apache’s notes table.

Apache Subprocess Environment

Another way to make PHP variables accessible to Apache is by putting them into an Apache procesess’ runtime environment. Note that simply using the PHP function putenv() will not work because environment variables set with putenv() expire at the end of the current request. Instead, another Apache-specific function must be used: apache_setenv().

Using the same example as with the Apache note, the code would look like this: apache_setenv('userID', $_SESSION['userid']). In order to retrieve the value from another page, you need to use the corresponding function apache_getenv(): $userid = apache_getenv('userID').

Moving the Variable into the Log File

Now that Apache has been made aware of the existence of the variable, the value must be recorded in Apache’s logs. This is the easy part. All you need do is add it to the LogFormat line in your config file. So, using the userID example from before, if your LogFormat directive looks like this,

LogFormat "%h %l %u %t "%r" %>s %b"

and you used an apache_note() You simply need to edit it so that it looks like this:

LogFormat "%h %l %u %t "%r" %>s %b %{userID}n"

Note the small n after the placement of the %{userID} variable in the log format. This signifies that the variable will come from an Apache note. If you used an Apache environment variable (apache_setenv()), then that small n should be changed to an e to signify that the variable comes from the environment.

Analyzing your web site traffic using this technique can often be just as effective as logging information to a database but there is much less overhead involved here than with most other options.

Written by Meitar

February 1st, 2005 at 11:40 pm

Posted in PHP

Generating Random Letters in PHP

3 comments

Generating random numbers in PHP isn’t much a problem thanks to functions like rand() or mt_rand() and so utilizing them is a no brainer, even for programming newbies. Generating random letters, however, is sometimes a little less intuitive.

The tricky thing about it is that computers are basically glorified switches. All they do is answer the question “Is this a 0 or a 1?” for any given datum. So while this makes them pretty good at handling numeric data, doing anything interesting with something else, such as natural languages, requires a bit of creativity.

The concept of what a “letter” is or is not needs to be explicitly defined in some manner that computers (glorified switches) can understand. This is accomplished by mapping a numeric value to a specific character. This map is then known as a character encoding.

I’m not going to get into the whole thing here. For the curious, a pertinent Google search turned up this tutorial which is a good place to start. Additionally this informative article by Joel Spolsky is an entertaining and educational read.

However, back to PHP and generating random letters, we can sidestep the whole issue of encodings as long as the pool of possible letters we wish to generate is somewhat limited. Thanks to a feature of PHP that allows us to pick out a character in a string using array syntax, we can get a random letter with a random number and a string containing the possibilities we want.

function randLetter()
{
    $int = rand(0,51);
    $a_z = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $rand_letter = $a_z[$int];
    return $rand_letter;
}

While this method may carry a little more overhead than a purely numeric approach, it’s certainly easier to digest.

Written by Meitar

December 19th, 2004 at 10:02 pm

Posted in PHP,Programming