Skip to main content

Ivan Teoh

Something personal yet public

Although I am very tired now, I need to complete this small task before I go to bed. I need to take some pictures of my current stay in Beijing for showing my parents that I am safe and staying in a comfortable place. Sorry mum. I did not call earlier. Below pictures will give you an idea what apartment I am stay now. I am sharing this apartment with another two co workers. Mum, I am safe. Happy mother day.

Most of the basic theme template file are explained. We do felt error 404 page, 404.php, is quite important. There is always a chance that user might click on the page that does not exist. If it does happen, proper error 404 page as following will handle it.

59-404.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
?>
<?php get_header(); ?>

<h2><?php _e('Error 404 - Not Found'); ?></h2>
<?php get_search_form(); ?>

<?php get_footer(); ?>

There is a new template tag for search form, get_search_form();, in WordPress version 2.7. However, the template tag will look for search form, searchform.php, first. If the file does exists, it will use that file. If not, it will generate the HTML. We decided to use this new template tag. Hence, we will replace the code as following in sidebar file, sidebar.php.

58-sidebar-before.php (Source)

<li>
    <label for="s"><?php _e('Search'); ?></label>
    <form id="searchform" method="get" action="<?php bloginfo('home'); ?>">
        <input type="text" name="s" id="s" size="15" /><br />
        <input type="submit" value="<?php _e('Search'); ?>" />
    </form>
</li>

Now, new search code in sidebar file, sidebar.php, will look as following.

58-sidebar-after.php (Source)

<li>
    <?php get_search_form(); ?>
</li>

After using the new search template tag in sidebar file, sidebar.php, we move the old search code to a search form file called searchform.php. There is a example of search form file as following.

58-searchform.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
?>
<form id="searchform" method="get" action="<?php bloginfo('home'); ?>">
    <label for="s"><?php _e('Search'); ?></label><br />
    <input id="s" type="text" name="s" value="<?php the_search_query(); ?>" />
    <input id="searchsubmit" type="submit" value="<?php _e('Search'); ?>" />
</form>

There is an enhancement for comment display in WordPress 2.7. New template tags are introduced. This WordPress theme will be using those enchancement too. In the header file, header.php, code as following is added right before the call to wp_head(); for supporting new JavaScrip functionality with comment threading in WordPress 2.7.

57-header.php (Source)

<?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' );
  // enables nested comments in WP 2.7 ?>

Nows, let's concerntrate on comments file, comments.php, itself. Due to this theme support at least WordPress version 2.7, it won't include any legacy code that support WordPress version less then 2.7. I used Migrating Plugins and Themes to 2.7/Enhanced Comment Display as reference. Great reference. We won't repeat on what they already said. An example of comments file, comments.php that supporting WordPress 2.7 comments enhancement as following.

57-comments.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
?>
<!-- Comments for WP 2.7 and more -->
<?php
if (!empty($_SERVER['SCRIPT_FILENAME']) && 'comments.php' ==
  basename($_SERVER['SCRIPT_FILENAME']))
    die (__('Please do not load this page directly. Thanks!'));
if ( post_password_required() ) : ?>
    <p><?php _e('This post is password protected. Enter the password to view
      comments.'); ?></p>
<?php return; endif; // END: if ( post_password_required() ) ?>

<h3>
    <?php comments_number(__('No Comments'), __('1 Comment'),
      __('% Comments')); ?>
</h3>

<?php if ( have_comments() ) : // Showing all the comments ?>
    <ul>
        <?php wp_list_comments(); ?>
    </ul>
    <?php previous_comments_link(); ?>
    <?php next_comments_link(); ?>
<?php else : // this is displayed if there are no comments so far ?>
    <p><?php _e('No comments yet.'); ?></p>
<?php endif; // END: if ( $comments ) ?>

<?php if ( comments_open() ) : ?>
<div id="respond">
    <h3><?php comment_form_title( 'Leave a Reply', 'Leave a Reply to %s' ); ?>
    </h3>
    <div id="cancel-comment-reply">
        <small><?php cancel_comment_reply_link() ?></small>
    </div>

    <?php if ( get_option('comment_registration') && !$user_ID ) :
        // If registration required ?>
        <p><?php printf(__('You must be <a href="%s">logged in</a>
          to post a comment.'),
          get_option('siteurl')."/wp-login.php?redirect_to="
          .urlencode(get_permalink()));?></p>
    <?php else : // If registration do not required ?>

    <form action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php"
      method="post" id="commentform">

        <?php if ( $user_ID ) : // Log in user info ?>

            <p><?php printf(__('Logged in as %s.'),
            '<a href="'.get_option('siteurl').'/wp-admin/profile.php">'
            .$user_identity.'</a>'); ?>
            <a href="<?php echo wp_logout_url(get_permalink()); ?>"
              title="<?php _e('Log out of this account'); ?>">
              <?php _e('Log out &raquo;'); ?></a></p>

        <?php else : // Public user ?>

            <p><input type="text" name="author" id="author" value="
              <?php echo $comment_author; ?>" size="22" tabindex="1" />
            <label for="author"><small><?php _e('Name'); ?>
              <?php if ($req) _e('(required)'); ?></small></label></p>

            <p><input type="text" name="email" id="email"
              value="<?php echo $comment_author_email; ?>" size="22"
              tabindex="2" />
            <label for="email"><small>
              <?php _e('Mail (will not be published)');?>
              <?php if ($req) _e('(required)'); ?></small></label></p>

            <p><input type="text" name="url" id="url" value="
              <?php echo $comment_author_url; ?>" size="22" tabindex="3" />
            <label for="url"><small><?php _e('Website'); ?></small></label></p>

        <?php endif; ?>

        <p><small><strong>XHTML:</strong>
          <?php printf(__('You can use these tags: %s'), allowed_tags()); ?>
        </small></p>
        <p><textarea name="comment" id="comment" cols="100%" rows="10"
          tabindex="4"></textarea></p>
        <p><input name="submit" type="submit" id="submit" tabindex="5"
          value="<?php echo attribute_escape(__('Submit Comment')); ?>" /></p>
        <?php comment_id_fields(); ?>
        <?php do_action('comment_form', $post->ID); ?>

    </form>
</div>
    <?php endif; // END: if ( get_option('comment_registration') && !$user_ID )
    // If registration required and not logged in ?>

<?php else : // Comments are closed ?>
    <p><?php _e('Sorry, the comment form is closed at this time.'); ?></p>
<?php endif; // END: if ( comments_open() )?>

Currently there is no plan on developing popup comments template. Therefore, there will be no popup comments file, comments-popup.php, in this theme. However, if you want to publish your theme publicly, you might want to consider for adding it.

The WordPress theme functions file, functions.php, basically acts like a plugin, adding more features to WordPress theme. Currently I added the code as following to this file for supporting widget in dynamic sidebar.

56-functions.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
if ( function_exists('register_sidebar') )
    register_sidebar(array(
        'before_widget' => '<li id="%1$s" class="widget %2$s">',
        'after_widget' => '</li>',
        'before_title' => '',
        'after_title' => '',
    ));
?>

In the main index template file, index.php, I added sidebar include tag, get_sidebar();. This tag will include the file sidebar.php. Now, I will talk about what information will be included in this file. Your sidebar needs to support dynamic widget sidebar as well as old style sidebar. Hence, <?php if ( !function_exists('dynamic_sidebar') || !dynamic_sidebar() ) : ?> is needed. Next, it will be the search form. I include text input and submit input in the form. I also like to display a list of WordPress pages as links, a monthly archive links list, a list of categories as links and a list of bookmarks as links. Since all the links title does not have any html tag, I will remove both the default before text, <h2>, and default after text, </h2>, by adding empty string for title_after and title_before arguments in bookmark template tag. For meta tag list, I added wp_register(); for displaying either the "Register" link to users that are not logged in or the "Site Admin" link if a user is logged in. Adding some WordPress build-in feeds for allowing your friends to subscribe your blog, which is what I doing too. Lastly, wp_meta(); is added for allowing other WordPress plugin to insert content into this file. An example of this file is stated as following.

55-sidebar.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
?>
<!-- begin sidebar -->
<ul>
    <?php   /* Widgetized sidebar, if you have the plugin installed. */
        if ( !function_exists('dynamic_sidebar') || !dynamic_sidebar() ) : ?>

    <li>
        <label for="s"><?php _e('Search'); ?></label>
        <form id="searchform" method="get" action="<?php bloginfo('home'); ?>">
            <input type="text" name="s" id="s" size="15" /><br />
            <input type="submit" value="<?php _e('Search'); ?>" />
        </form>
    </li>

    <?php wp_list_pages('title_li=' . __('Pages')); ?>

    <li><?php _e('Archives'); ?>
        <ul>
            <?php wp_get_archives('type=monthly'); ?>
        </ul>
    </li>

    <?php wp_list_categories('title_li=' . __('Categories')); ?>

    <?php wp_list_bookmarks('title_after=&title_before='); ?>

    <li><?php _e('Meta'); ?>
        <ul>
            <?php wp_register(); ?>
            <li><?php wp_loginout(); ?></li>
            <li><a title="<?php _e('Syndicate this blog using RSS'); ?>"
              href="<?php bloginfo('rss2_url'); ?>"><?php _e('Blog <abbr
              title="Really Simple Syndication">RSS</abbr> Feed'); ?></a></li>
            <li><a title="<?php _e('The latest comments to all posts in RSS');
              ?>" href="<?php bloginfo('comments_rss2_url'); ?>">
              <?php _e('Comments <abbr title="Really Simple Syndication">RSS
              </abbr> Feed'); ?></a></li>
            <?php wp_meta(); ?>
        </ul>
    </li>
    <?php endif; ?>
</ul>
<!-- end sidebar -->

Normally, main index template file, index.php, consists of get_header();, posts (the loop) and get_footer();. If footer.php file exists, the code inside this file will appear in get_footer(); section. In footer.php, you might want to show WordPress support by having the "powered by" link in this file. I also include the WordPress version, so that the reader will know what version of WordPress I am using for this web site. Lastly, don't forget about wp_footer();. It has the same purpose as wp_head();. It will hook up with any functions that linked to wp_footer, especially from other WordPress plugin.

54-footer.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
?>
<!-- begin footer -->

<p><?php bloginfo('name'); ?> <?php _e('is proudly powered by'); ?>
  <a title="<?php _e('WordPress, state-of-the-art semantic personal publishing
  platform.'); ?>" href="http://wordpress.org/">WordPress <?php
    bloginfo('version'); ?></a>.</p>

<?php wp_footer(); ?>
</body>
</html>

Normally, header.php will start with html doctype declaration, and xhtml namespace. By default, WordPress theme is using XHTML 1.0 Transitional. WordPress function, language_attributes();, will display the language attributes for the html tag. If you want your site supporting XFN (XHTML Friends Network), you can add profile="http://gmpg.org/xfn/11" for the header tag.

Inside header tag, you will have title, of course, content type and description meta tags, links to style sheet, blog RSS feed, comments RSS feeds and pingback. Action hook template tag, wp_head();, should be added too in order to support other WordPress plugin that you decided to install. Other WordPress plugin might add JavaScript in your header tag by calling wp_head();.

Here come to body tag, you might want to display title and desciption on every page in your web site. The header.php is the right place to put all this information. Following is the example header.php code that you can reference.

53-header.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
<head profile="http://gmpg.org/xfn/11">
    <title><?php wp_title('&laquo;', true, 'right'); ?>
      <?php bloginfo('name'); ?></title>

    <meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>;
      charset=<?php bloginfo('charset'); ?>" />
    <meta name="description" content="<?php bloginfo('name'); _e(' - ');
        if ( is_single() ) {
            single_post_title('', true);
        } else {
            bloginfo('description');
        } ?>" />

    <link rel="stylesheet" type="text/css" href="<?php
      bloginfo('stylesheet_url'); ?>"  media="screen" />
    <link rel="alternate" type="application/rss+xml" title="<?php
      bloginfo('name'); ?> Blog RSS Feed" href="<?php
      bloginfo('rss2_url'); ?>" />
    <link rel="pingback" href="<?php bloginfo('pingback_url'); ?>" />

    <?php wp_head(); ?>
</head>
<body>
    <h1><a href="<?php bloginfo('url'); ?>/"><?php bloginfo('name'); ?></a></h1>
    <p><?php bloginfo('description'); ?></p>
<!-- end header -->

If you see the example above, there is a condition in meta tag for description. It is showing different description on single post compare to the rest. In single post, it will describe the post title instead of the site description.

By default, WordPress has build-in feeds for blog web site. I choose RSS 2.0 feed. There are others you can choose from, but this is the common one.

Let's talk about index.php template file. This file is a must for WordPress theme. If any file is missing, WordPress will look for this main template file. Currently, I will use these WordPress themes below as references for learning WordPress theme.

The minimum index.php would be:

52-index-before.php (Source)

<?php
get_header();
if (have_posts()) :
   while (have_posts()) :
      the_post();
      the_content();
   endwhile;
endif;
get_sidebar();
get_footer();
?>

But you might want to add something more in your main template file. In the posts loop, title of the post is added. It is good to mention the post is written by who and when. Categories and tags can be added in the posts loop too. Link pages, comments pop up link, comments template, next and previous posts link are also added in this main template file. Basically, this (as below) will be the default index.php for this theme.

52-index-after.php (Source)

<?php
/**
 * @package WordPress
 * @subpackage Your_Theme
 */
get_header(); ?>
<?php if (have_posts()) : ?>
    <?php while (have_posts()) : ?>
        <?php the_post(); ?>
        <div <?php post_class(); ?> id="post-<?php the_ID(); ?>">
        <h2><a href="<?php the_permalink() ?>" rel="bookmark"
          title="Permanent Link to <?php the_title_attribute(); ?>">
          <?php the_title(); ?></a></h2>
        <?php the_content(__('Read more...')); ?>
        <p><?php wp_link_pages(); ?></p>
        <p><?php _e('Posted by'); ?> <?php the_author(); ?> @
          <?php the_time('F jS, Y'); ?> <?php _e('in'); ?>
          <?php the_category(','); ?> <?php the_tags(__('with '), ', '); ?></p>
        <p><?php comments_popup_link(__('No Comments &#187;'),
          __('1 Comment &#187;'), __('% Comments &#187;')); ?>
          <?php edit_post_link(__('Edit This'), '&#8212;'); ?></p>
        </div>
        <?php comments_template(); ?>
    <?php endwhile; ?>
    <p><?php next_posts_link(__('&laquo; Older Posts')); ?></p>
    <p><?php previous_posts_link(__('Newer Posts &raquo;')); ?></p>
<?php else : ?>
<h2><?php _e('Not Found'); ?></h2>
<p><?php _e('Sorry, but you are looking for something that isn&#39;t here.'); ?>
</p>
<?php endif; ?>
<?php get_sidebar(); ?>
<?php get_footer(); ?>

There are different between __( ) and _e( ). __( ) is used when the message is passed as an argument to another php function. Whereas _e( ) is used for writing the message directly to the page. More detail can be found in Translating WordPress.

I decided to develop WordPress theme from scratch in order to learn how the WordPress theme works. This WordPress theme development series is for WordPress version 2.7. Some of template tags that I use only available in WordPress version 2.7. So you might want to modified it to suite other WordPress version. First of all, WordPress theme must contains at least two files, which are style.css and index.php. This style.css is the theme main style sheet. It must consists your theme information such as below at the beginning of the file. Cascading Style Sheets (CSS) information will be added at later state. More info on index.php will be explained next.

51-style.css (Source)

/*
Theme Name: Theme-Name
Theme URI: The-Theme-Web-Site
Description: The-Theme-Description
Version: The-Theme-Version
Author: Your-Name
Author URI: Your-Web-Site

    General information if any.
    License statement if any.

*/

Reference