Towards a dynamic home page

I’ve extended the dynamic approach of my previous home page; you may find it interesting to read why and find out how it’s done.

Towards a dynamic home page

When I migrated this web site to WordPress I wanted to retain its dynamic home page features. My previous home page presented the most viewed posts in the preceding ten days and listed the most recently written posts. I’ve extended that approach in the revised site, and you may find it interesting to read why and find out how it’s done.

The web site has two streams of content. It provides a portfolio of my recent work for prospective clients alongside more personal content: music I’ve written and recorded, images I’ve snapped, bits and pieces I’ve written (the latter content stream is often as reassuring for clients as the former).

On the home page I’ve tried to give equal weight to these two streams, balancing the four portfolio subcategories against the four top level categories for the personal content.

 
What’s it all about?

The purpose of the home page is to draw site visitors to the content most relevant to them. The page doesn’t explain what the web site is about in words; it presents a montage of the website content and tries to do so in way that will attract interest, and maintain interest should there be a repeat visit. The home page is active; it looks a little different each time it’s viewed. This is achieved with the dynamic content in the four central sections, and with the slideshow.

The slideshow at the top of the page comes with the site template, Breeze from Simple Themes. It has eight slides, four for portfolio subcategories and four for personal content. The slides click through to the latest content for that subject, providing the first doorway for home page visitors to access something that interests them.

 
What’s hot

Most WordPress page view plugins only record total page views. I found and installed KometBomb’s View Count plugin, which captures page counts by date. This allows me to present post popularity for the last ten days in the first of the four dynamic sections.

function homeHot(){
  global $wpdb;
  $wpdb->view_count = $table_prefix.'wp_view_count';
  $offset = get_option('gmt_offset') * 60 * 60;
  $start_date = (time() - (10 * 24 * 60 * 60)) + $offset; // 10 = days
  $date_limit = "$wpdb->view_count.day >= '".date("Y-m-d", $start_date)."'";
  $total = $wpdb->get_var($wpdb->prepare("SELECT sum(views) FROM $wpdb->view_count"
    ." WHERE $date_limit AND NOT page = 13"));
  $most_viewed = $wpdb->get_results("SELECT SUM($wpdb->view_count.views) AS views"
    .", $wpdb->posts.ID, $wpdb->posts.post_title, $wpdb->posts.post_excerpt"
    ." FROM $wpdb->posts, $wpdb->view_count"
    ." WHERE $wpdb->posts.ID = $wpdb->view_count.page AND $date_limit AND post_status = 'publish'"
    ." AND post_password = '' AND NOT page = 13"
    ." GROUP BY $wpdb->posts.ID"
    ." ORDER  BY views DESC LIMIT 4");
  $out = '';
  if($most_viewed) {
    $recent = 'of recent visitors ';
    foreach ($most_viewed as $view){
      if ($view->post_title != 'Home'){
        $out .= '<div class="home-hot" onmouseover="this.style.backgroundColor=\'#dedede\'" '
          .'onmouseout="this.style.backgroundColor=\'transparent\'" '
          .'onclick="document.location=\''.get_permalink($view->ID).'\'" title="'
          .$view->post_excerpt.'"><span class="home-hot-percent">'
          .(round($view->views * 100 / $total, 0)).'</span> <b><sup>%</sup></b>'
          .$recent.categoryUserVerb(getPostCategory($view->ID))
          .' <span class="home-hot-title">'.$view->post_title.'</span></div>';
        $recent = '';
      }  
    }
  }
  echo $out;    
}

Points to note:

  • AND NOT page = 13 excludes the home page from the results.
  • Forgive me for the inline Javascript. Yes, I do know better …
  • getPostCategory() does what it says.
  • categoryUserVerb() is a switch statement that returns the descriptive phrase preceding the post title.
 
What’s working and what’s fresh

Dynamic sections two and three share the same logic but the results are formatted slightly differently. The first retrieves the two most recent posts in the portfolio subcategories, the second retrieves their cousins from the four non-portfolio categories. This will be familiar ground for WordPress developers, so I’ll only include code for the first section.

function homePostsWorking(){
  $args = array(
    'numberposts' => 2,
    'orderby' => 'post_date',
    'order' => 'DESC',
    'cat' => '7',
    'post_type' => 'post'
  );
  $posts = get_posts($args);
  $more = '<br />Check out ';
  foreach($posts as $post) {
    $cat = getPostCategory($post->ID);
    categoryIncrement($cat);
    $out .= '<div class="home-hot" onmouseover="this.style.backgroundColor=\'#dedede\'" '
      .'onmouseout="this.style.backgroundColor=\'transparent\'" '
      .'onclick="document.location=\''.get_permalink($post).'\'">'
      .'<img src="'.categoryIcon($cat).'" class="home-icon" />';
    if ($post->post_excerpt)
      $out .= $post->post_excerpt;
    $out .= ' <i>'.$more.categoryName($cat).'</i> ';
    $more = '<br />Find out more in ';
    $out .= '<span class="home-hot-title">'.$post->post_title.'</span>.</div>';
    $out .= '<div style="clear:both;"><!-- --></div>';
  }
  echo $out;  
}

Some notes:

  • The $more variable allows us to vary the text for the two entries.
  • See my comments above about getPostCategory() and using inline Javascript.
  • categoryIncrement() is important: you’ll find out more about it below.
  • Predictably categoryIcon() is a switch statement that retrieves a different icon for each category.
  • categoryName() is another switch statement that allows us to use qualified labels for some categories: “source code” instead of “code”, for example.
 
What else

The final dynamic section is intended to bring balance to what’s gone before. The What’s working and What’s fresh sections will have presented content from two, three or four of our eight categories/subcategories.

For each of the posts in the previous two sections we’ve invoked the categoryIncrement() function, which increments a count for that category within the $cat_refs array. Starting this final section, then, we know which categories have been referenced in the preceding two sections and which haven’t. We use that information to serve up some eye catching psuedo buttons with content from unrepresented categories. Images, for example, usually get posted once a month at most. Music, sadly, even less frequently. The “What else” section ensures categories with less current content get represented on the home page anyway.

Don’t miss the notes following the code.

function homePostsElse(){
  global $cat_refs;
  $out = '';
  $total = 0;
  $cat_sort = shuffle_assoc($cat_refs);
  foreach($cat_sort as $key=>$val) {
    if ($val == 0 && $total < 4){
      $args = array(
        'numberposts' => 1,
        'orderby' => 'post_date',
        'order' => 'DESC',
        'cat' => $key,
        'post_type' => 'post'
      );    
      $posts = get_posts($args);
      foreach($posts as $post) {
        $cat = getPostCategory($post->ID);
        $total++;
        $out .= '<div class="home-else-button';
        $out .= ($total == 2 || $total == 4)
          ? ' hebright'
          : ' hebleft';
        $out .= '" onmouseover="showExcerpt('.$total.', true)" '
          .'onmouseout="showExcerpt('.$total.', false)" '
          .'onclick="document.location=\''.get_permalink($post).'\'"'
          .' style="background-image:url(\''.postImage($post->ID).'\');">';
        $out .= '<div class="hebfrost heb-excerpt" id="heb-excerpt_'.$total.'">';
        if ($post->post_excerpt)
          $out .= postAbbreviate($post->post_excerpt, 80);
        else  
          $out .= 'Click to read more ...';
        $out .= '</div><div class="hebfrost" id="heb-post_'.$total.'">';  
        $out .= '<div class="home-else-verb">'.strtoupper(categoryVerb($cat))
          .'<span class="home-else-adjective"> '.strtoupper(categoryShortName($cat))
          .'</span></div>';
        $out .= postAbbreviate($post->post_title, 24);
        $out .= '</div></div>';  
      }  
    }
  }
  echo $out.'<script type="text/javascript">
  function showExcerpt(id, val){
    oPost = document.getElementById("heb-post_" + id);
    oExcerpt = document.getElementById("heb-excerpt_" + id);
    oPost.style.display = (val)
      ? "none"
      : "block";
    oExcerpt.style.display = (val)
      ? "block"
      : "none";
  }
</script>';
}

The promised notes:

  • We shuffle the $cat_refs array so this section has a chance of being ordered differently each time someone visits.
  • See my notes above about getPostCategory() and using inline Javascript.
  • postImage(} returns the post’s featured image if there is one, otherwise a static default image for that category via a switch statement.
  • To keep the content within bounds the postAbbreviate() function trims it to the nearest white space preceding the limit.
  • categoryVerb() and categoryShortName() are additional switch statements that return category-dependent text elements for each item.
  • The javascript function simply toggles visibility of the div containing the initial content and the div containing the post excerpt. Using jQuery would make the code simpler.
 
That’s it?

Pretty much. There is CSS, of course.

.heb-excerpt{display:none;text-align:left;width:204px!important;height:64px!important;padding:8px;}
.hebfrost{color:#ffffff;position:absolute;width:220px;height:80px;
  background:rgb(0,0,0) transparent;background:rgba(0,0,0,0.3);
  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000,endColorstr=#99000000);
  -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";
  border-radius:8px;-webkit-border-radius:8px;-moz-border-radius:8px;}
.hebleft{float:left;clear:both;}
.hebright{float:right;}
.home-else-button{width:220px;height:80px;text-align:center;margin-top:12px;
  border-radius:8px;-webkit-border-radius:8px;-moz-border-radius:8px;
  border:1px solid #ababab;background-color:#ffffff;cursor:pointer;}
.home-else-verb{font-weight:bold;font-size:1.2em;
  font-family:DroidSans,Arial,Helvetica Neue,Helvetica,sans-serif;margin-top:12px;}
.home-else-adjective{font-weight:normal;color:#cdcdcd;text-transform:capitalize;}
.home-hot{clear:left;cursor:pointer;padding:8px 0px;}
.home-hot-percent{font-size:2.4em;font-weight:bold;}
.home-hot-sign{font-size:0.5em;font-weight:normal;}
.home-hot-title{font-weight:bold;color:#2b7fbe;}
.home-icon{float:left;width:64px;height:64px;margin:0px 16px 16px 0px;}

I’ve not converted these functions into plugins: they live happily in a functions.php file. Feel free to use the code in any way you like.

What do you think?

Comments are aggressively moderated. Your best chance is reasoned disagreement.

*