WordPress

Posts filed under WordPress

Home Page and Front Page and Templates, Oh My!

Filed in Web DevelopmentTags: Template Hierarchy, Themes, WordPress

Since the introduction of the front-page.php template file in WordPress 3.0 over three years ago, there remains a great deal of confusion about the proper implementation of custom/static site front pages in WordPress. This post will address that confusion, and explain the proper implementation of the custom/static site front page feature in WordPress.

Nomenclature

The first area of confusion involves nomenclature. In most contexts, "home page" refers to the site front page; however, in WordPress, the "home" page is not the site front page, but rather the Blog Posts Index page. WordPress refers to the site front page as the Front Page.  This nomenclature applies in several areas. For example:

Query conditionals:

  • is_front_page() - returns true when the stie front page is being displayed, whether the site front page is set to display the blog posts index or a static page
  • is_home() - returns true when the blog posts index is being displayed, whether the blog posts index is displayed on the site front page or on a static page

Template file names:

  • front-page.php - used to display the site front page, whether the site front page is set to display the blog posts index or a static page
  • home.php - used to display the blog posts index, whether the blog posts index is displayed on the site front page or on a static page
  • index.php - used as the default fallback template file for all contexts

Unfortunately, this nomenclature isn't consistent throughout WordPress core. For example, the body_class() template tag adds the following classes:

  • Front Page: home
  • Blog Posts Index: blog

(Long term, it would possibly be wise for WordPress core to deprecate the "home" nomenclature in favor of "blog" nomenclature - i.e. an is_blog() conditional and a blog.php template file - but that's a discussion for another day.)

The key point here is: in WordPress parlance: home means blog posts index, and not site front page.

Creating a Static Front Page

WordPress core has a specific method for implementing a static front page. Themes need to account for the core implementation, rather than implement a non-core method for displaying a static front page and displaying the blog posts index on a separate static page.

The core method involves creating two static pages, and configuring the front page options under Settings -> Reading.

  • The "Front page displays" option correlates to get_option( 'show_on_front' ), which returns either 'page' if "Front page displays" is set to "A static page", or 'posts' if "Front page displays" is set to "Your latest posts".
  • The "Front page" option is available if "Front page displays" is set to "A static page", and correlates to get_option( 'page_on_front' ), which returns the ID of the page assigned to "Front page"
  • The "Posts page" option is available if "Front page displays" is set to "A static page", and correlates to get_option( 'page_for_posts' ), which returns the ID of the page assigned to "Posts page"

When a user sets these options, it is reasonable and expected for a Theme to respect/incorporate those option settings in the Theme's template files.

Template Hierarchy

When developing a WordPress Theme, understanding the Template Hierarchy is critical to ensuring proper use of Theme template files for the site front page and blog posts index contexts. For the site front page, there are basically three rules:

  1. If the front-page.php template file exists, use it
  2. If the front-page.php template file does not exist, and the front page is set to display the blog posts index, use the Home Template hierarchy
  3. If the front-page.php template file does not exist, and the front page is set to display a static page, use the Page Template hierarchy

To understand how this works, we need to dig a bit into the Template Hierarchy, which is defined in core via wp-includes/template-loader.php. The meat of the code is here:

if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
	$template = false;
	if     ( is_404()            && $template = get_404_template()            ) :
	elseif ( is_search()         && $template = get_search_template()         ) :
	elseif ( is_tax()            && $template = get_taxonomy_template()       ) :
	elseif ( is_front_page()     && $template = get_front_page_template()     ) :
	elseif ( is_home()           && $template = get_home_template()           ) :
	elseif ( is_attachment()     && $template = get_attachment_template()     ) :
		remove_filter('the_content', 'prepend_attachment');
	elseif ( is_single()         && $template = get_single_template()         ) :
	elseif ( is_page()           && $template = get_page_template()           ) :
	elseif ( is_category()       && $template = get_category_template()       ) :
	elseif ( is_tag()            && $template = get_tag_template()            ) :
	elseif ( is_author()         && $template = get_author_template()         ) :
	elseif ( is_date()           && $template = get_date_template()           ) :
	elseif ( is_archive()        && $template = get_archive_template()        ) :
	elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
	elseif ( is_paged()          && $template = get_paged_template()          ) :
	else :
		$template = get_index_template();
	endif;
	if ( $template = apply_filters( 'template_include', $template ) )
		include( $template );
	return;
endif;

 

Basically, this code steps through each of the query context conditionals in a specific order, and defines the template to use for the first one that returns true.

For our purposes, the contexts we are concerned with are Front Page, Home, and Page, which are evaluated in the following order (with conditionals in-between omitted):

elseif ( is_front_page()     && $template = get_front_page_template()     ) :
elseif ( is_home()           && $template = get_home_template()           ) :
elseif ( is_page()           && $template = get_page_template()           ) :

 

Translated, this says:

  • If a valid Front Page template file exists, use it
  • Otherwise, if this is the blog posts template and a valid blog posts index template file exists, use it
  • Otherwise, if this is a static page and a valid page template file exists, use it
  • Otherwise, use the default template file

To understand further what core is doing here, we need to look at get_front_page_template(), get_home_template(), and get_page_template(), which are defined in wp-includes/template.php:

get_front_page_template() looks for

  • front-page.php

get_home_template() looks for:

  • home.php

get_page_template() looks for

  • _wp_page_template meta-key value (user-assigned custom page template)
  • page-{slug}.php
  • page-{id}.php
  • page.php

Which Template File?

So understanding the hierarchy, it should be easy to determine which template file to create for each given context.

Blog Posts Index

Use home.php.

Simple, right? But what about a custom page template, such as template-blog.php? Can't a Theme also use that? Not to put too fine a point on it, but: no. That would be _doing_it_wrong(). Using a custom page template for the blog posts index, either:

  • Won't work, because when implemented properly, a static page as custom page template simply won't ever use a custom page template
  • Would require the Theme to force the user to use a method of assigning the blog posts index that does not conform to the WordPress core method
  • Would cause pagination issues when rendering the blog posts index using the custom page template, because WordPress recognizes the main query for the custom page template to be the static page post object, rather than the blog posts index

So, while technically you can use a custom page template to display the blog posts index, in doing so you'll be forcing your Theme users to use a non-standard way of assigning the static page used to display the blog posts index, and will cause yourself headaches in getting the custom page template to function properly - all of which is easily avoided simply by using home.php instead.

Site Front Page

Use front-page.php.

But there are some caveats - namely, because front-page.php takes precedence on the site front page regardless of what the user has configured the front page to display, the Theme needs to account for the front page displaying either a static page or the blog posts index. There are a few methods to do so.

Conditional output in front-page.php

The first method uses a conditional statement inside front-page.php, that evaluates get_option( 'show_on_front' ), and either includes get_home_template(), or custom front-page markup:

if ( 'posts' == get_option( 'show_on_front' ) ) {
    include( get_home_template() );
} else {
    // Custom content markup goes here
}

 

This method is useful if the Theme already includes a front-page.php template file, and you need to implement an easy way to account for the blog posts index-as-front-page use case.

Conditional output in front-page.php, with a custom page template

The second method is a variation of the first, that includes get_page_template() instead of hard-coded custom front-page markup inside of front-page.php:

if ( 'posts' == get_option( 'show_on_front' ) ) {
    include( get_home_template() );
} else {
    include( get_page_template() );
}

 

This method is useful if the Theme includes a featured-content (or perhaps a portfolio-style) custom page template that isn't necessarily intended to be used as the site front page. When using this method, the user will need to apply the custom page template to the page assigned to display the site front page. This is an extra step for the user, but also gives the user the flexibility to use the featured-content custom page template on any static page, rather than only as the site front page.

Note also: if using this method, you can also safely omit front-page.php entirely, as it is technically unnecessary.

Filter front_page_template

The third method uses front-page.php solely as custom static-page-as-front-page content, and filters front_page_template when the front page is set to display the blog posts index, causing the Template Hierarchy to ignore front-page.php and fall back to get_home_template(), via callback in functions.php:

function themeslug_filter_front_page_template( $template ) {
    return is_home() ? '' : $template;
}
add_filter( 'front_page_template', 'themeslug_filter_front_page_template' );

 

To see how this method works, we need to take another look at get_front_page_template():

function get_front_page_template() {
	$templates = array('front-page.php');

	return get_query_template( 'front_page', $templates );
}

 

The $templates array is passed through get_query_template():

function get_query_template( $type, $templates = array() ) {
	$type = preg_replace( '|[^a-z0-9-]+|', '', $type );

	if ( empty( $templates ) )
		$templates = array("{$type}.php");

	return apply_filters( "{$type}_template", locate_template( $templates ) );
}

 

And here you can see the filter being applied:

return apply_filters( "{$type}_template", locate_template( $templates ) );

 

For the front page context, $type = "front-page", resulting in the filter front_page_template.

By returning an empty string for front_page_template, the Template Hierarchy will evaluate this line as false:

elseif ( is_front_page()     && $template = get_front_page_template()     ) :

 

Causing it to move on to the blog posts index line:

elseif ( is_home()           && $template = get_home_template()           ) :

 

This is probably the simplest method to implement. (H/T Justin Tadlock)

EDIT:

There is a bug in WordPress, that causes the front_page_template filter name to be truncated as frontpage_template. So, use this instead:

function themeslug_filter_front_page_template( $template ) {
    return is_home() ? '' : $template;
}
add_filter( 'frontpage_template', 'themeslug_filter_front_page_template' );

 

Questions? Use cases unaccounted for? Let me know in the comments.

A Guide to Reviewing Themes for the WordPress Theme Repository

Filed in Web DevelopmentTags: Theme Review, Themes, tutorials, WordPress

Thanks in large part to Justin Tadlock's recent blog post, in which he solicited more people to get involved with the WordPress Theme Review Team (WPTRT), we have seen a fantastic, huge influx of new volunteers. Many of these new volunteers have had questions about how to perform a Theme review. Hopefully this guide will help answer those questions.

Getting Started

Test Environment Setup

First things first: review How to Join WPTRT.

WordPress Setup

Ensure that a few WordPress settings are modified in order to facilitate the Theme Unit Tests:

  • Settings -> General: set the Site Title to something fairly long, and set the Tagline to something even longer. These settings will facilitate testing how the Theme handles these values.
  • Settings -> Reading: set "Blog pages show at most" to 5. This setting will ensure that index/archive pagination is triggered.
  • Settings -> Discussion: enable Threaded Comments, at least 3 levels deep. This setting will facilitate testing of Theme comment list styling.
  • Settings -> Discussion: enable Break comments into pages, and set 5 comments per page. This setting will facilitate testing of Theme paginating link markup/styling.
  • Settings -> Media: ensure that no values are set for max width or height of Embedded media. This setting will facilitate testing of the Theme $content_width setting/implementation.
  • Settings -> Permalinks: ensure that a non-default permalink setting is selected, e.g. "Month and name". This setting will facilitate stepping through the Theme Unit Tests.

Next, create at least two Custom Menus:

  • Long Menu: all included Pages
  • Short Menu: a menu of 2-3 Pages

Now, you're ready to be assigned a ticket! Read on for the detailed review process.

Policy Requirements

The first area to review involves some policy requirements, rather than code quality. I verify these before I ever download or install the Theme, by reviewing the appropriate template files using the "SVN" link in the Trac ticket.

License

  • Review the header tags in style.css. Ensure that the License: and LicenseURI: header tags exist, that the indicated license is GPL-compatible, and that the indicated URI is a valid, full-text license.
  • Review the Theme files for bundled fonts or icon sets; if included, verify that the licenses for these bundled resources are GPL-compatible, and explicitly declared by the Theme, either in style.css, readme.txt, or somewhere similarly appropriate.
  • Review other template files, such as footer.php, to ensure that there are no inline comments regarding use restriction, such as prohibiting changing the footer, or requiring a credit link, etc.

Credit Links

  • Review the header tags in style.css. Ensure that Theme URI and Author URI, if used, are valid and appropriate.
  • Review the indicated URLs. Ensure that the sites are appropriate, that they do not sell or promote non-GPL WordPress Themes, and that any license or terms and conditions information does not include non-GPL-compatible content.
  • Review footer.php, sidebar.php, etc. and ensure that the Theme does not include any other inappropriate hard-coded links (sponsor links, SEO links, spam links, etc.). Ensure that all public-facing copyright notices indicate the site name, rather than the Theme name or developer.
  • Review functions.php and ensure that the Theme does not hook in any other inappropriate links or copyright notices.

Theme Name

  • Verify that the Theme name meets naming convention requirements.

Screenshot

  • Verify that screenshot.png is sized appropriately.

Theme Check

Next, install the Theme, and before activating, run Theme Check. Note any Warning or Required notices in the Trac ticket. (Note: for any Theme uploaded since late March 2011, Theme  Check should return no such notices, as they should all be caught by the Theme uploader script. If you notice any, please email the Theme-Reviewers mail list, and indicate what notices you observed.)

You are welcome to note Recommended or Info notices in the Trac ticket, but I generally don't. These notices are more for your reference, as you continue to review the Theme.

For Recommended notices regarding Theme functionality (Post Formats, Post Thumbnails, Navigation Menus, etc.), verify that the Theme does not use a custom implementation. If the Theme does use a custom implementation, note in the Trac ticket that the Theme is required to support the core implementation of the functionality.

Step through each Info notice, and review the Theme template files to determine if the notice is applicable. For example: verify appropriate use of include() vs. get_template_part(); verify appropriateness of hard-coded links; etc.

Code Quality

Now, the review enters the bulk of the requirements in the Theme Review guidelines.

header.php

Review header.php, and verify:

  • All appropriate document head guidelines (valid doctype declaration, properly formed tags (html, head, meta, etc.)
  • Feed links are not hard-coded into the document head
  • No CSS files other than style.css are hard-coded into the document head. (These files must either be enqueued and hooked in appropriately, or added using IE conditional tags.)
  • No scripts or script file links are hard-coded into the document head. (These must be enqueued and hooked in appropriately.)
  • No unnecessary meta tags (WordPress generator tag, SEO meta tags, copyright meta tags, etc.) are hard-coded into the document head.
  • The wp_head() template tag is placed immediately before the closing HTML head tag.

footer.php

Review footer.php, and verify:

  • No scripts or script file links are hard-coded into the footer. (These must be enqueued and hooked in appropriately.)
  • The wp_footer() template tag is placed immediately before the closing HTML body tag.

functions.php

Review functions.php, and verify:

  • All custom Theme functions, constants, classes, global variables, and options are prefixed with theme-slug (or an appropriate variant).
  • Theme Settings are implemented properly:
    • Theme options are added to the database via a single options array, rather than separately
    • Settings page added via add_theme_page() rather than add_menu_page()
    • Settings page capability is edit_theme_options, rather than admin, manage_options, edit_themes, etc.
    • All untrusted data are sanitized/validated upon input into the database, and escaped upon output
    • If the Theme is not using the Settings API:
      • Nonce-checking is performed explicitly
      • $_POST, $_GET, and $_REQUEST data are sanitized/validated upon input into the database, and escaped upon output
  • No core WordPress functions introduced prior to one previous, major WordPress release are wrapped in function_exists() conditionals.

style.css

Verify that all tags listed in the Tags: header tag are appropriate, and that all claimed functionality is included in the Theme.

Theme Template Files

Note which hierarchical template files are used (e.g. single.php, page.php, archive.php, date.php, author.php, category.php, tag.php, taxonomy.php, 404.php, home.php, front-page.php, and search.php), so that you will know which of these to verify when performing the Theme Unit Tests, later.

Review Theme template file names, and ensure that any hierarchical template file name variants (e.g. page-foo.php, category-foo.php, etc.) are included in the Theme documentation, with an explanation for their use.

File Includes

Review Theme template files, make note of any uses of include() or require() and ensure that such use is valid. Verify that:

  • All headers are included via get_header()
  • All footers are included via get_footer()
  • All comments templates are included via comments_template()
  • All sidebars are included using get_sidebar()
  • All template part files are included via get_template_part()
  • Any search form markup is included via get_search_form()
  • Any login form markup is included via wp_login_form()

Theme Unit Tests

Now the review moves on primarily to the requirements in the Theme Unit Tests.

Theme Activation Tests

  • Activate the Theme, and open the site in your browser. Verfiy that Debogger returns no PHP errors, warnings, or notices for index.php.
  • Via Dashboard -> Tools -> Deprecated Calls, verify that Log Deprecated Notices does not return any deprecated function calls or other notices.
    • Note: check the Deprecated Calls log one final time when you complete the review, after you switch back to your default Theme. Sometimes, some notices get thrown when the Theme is deactivated.
  • If the Theme includes a Custom Theme Settings:
    • Load the Settings page, and verify that Debogger returns no PHP errors, warnings, or notices.
    • Save current/default settings, and again verify that Debogger returns no PHP errors, warnings, or notices.
    • Test each Theme Option in turn, and verify that each option can be updated, that the setting is applied properly in the Theme, and that Debogger returns no PHP errors, warnings, or notices.
    • If the Theme Settings page includes a "Reset Defaults" button, reset defaults, and verify that default settings are applied, and that Debogger returns no PHP errors, warnings, or notices.
  • If the Theme supports Custom Image Headers, select a custom header, and verify that it is applied/displayed properly
  • If the Theme supports Custom Backgrounds, select a custom background image or color, and verify that it is applied/displayed properly
  • If the Theme supports Custom Nav Menus:
    • Verify that the default/fallback output renders properly
    • Apply the Short Menu to each theme_location, and verify that the menu renders properly
    • Apply the Long Menu to each theme_location, and verify that the menu renders properly, without breaking the site/menu layout
    • If the Theme has a limitation on number of list items in any theme_location, ensure that such limitation is documented in readme.txt
  • If the Theme adds Custom Widgets, verify that all custom Widgets function as intended, generate no PHP errors, are secure, etc.
  • If the Theme supports Custom Editor Style, verify that the Post editor content renders reasonably similar to the site output
  • If the Theme requires any custom settings or setup, ensure that setup instructions are documented in readme.txt

General Tests

Run the W3C HTML/CSS validator on the Readability Test post (to ensure minimal WordPress-caused validation errors), and note any egregious validation errors, such as:

  • Malformed or improperly closed tags, that cause the page layout to break
  • An exceptional number of HTML or CSS errors

View the site index page, and verify:

  • The screenshot, as seen in the Trac ticket, is a "reasonable facsimile" of the default view of the site
  • The Site Title and Tagline, if displayed, do not break the page layout

Template Page Tests

  • For all included template-hierarchy index pages (index.php, home.php, archive.php, date.php, author.php, category.php, tag.php), verify requirements, as specified.
  • If the Theme includes either a front-page.php or a home.php template file, go to Dashboard -> Settings -> Reading, and set the Front Page to display a Static Page (use any existing Page), and set the Blog Posts index to another Static Page (e.g. "Lorem Ipsum"). Verify requirements, as specified.
  • Trigger the Theme's 404.php page by navigating to a non-existent Page slug (e.g. www.domain.tld/sit-amet). Verify requirements, as specified.
  • Trigger the Theme's search.php page by performing a search (e.g. "lorem ipsum"). Verify requirements, as specified.

Blog Posts Index Page Tests

Return to the Blog Posts index page. Step through the Blog Posts Index Page tests.

Individual Unit Tests

Step through each Blog Post and Static Page, verifying all requirements, as specified. Each unit test should be fairly self-explanatory.

Finishing the Review

At this point, the review is thorough and complete. Summarize your comments in the Trac ticket and close the ticket with the appropriate resolution (or propose a suggested resolution, if you do not have appropriate rights to close the ticket).

Incorporating the Settings API in WordPress Themes

Filed in Web DevelopmentTags: Settings API, Themes, tutorials, WordPress

The inter-tubes are awash in tutorials for how to add Theme Options to WordPress Themes - so why write another? Primarily, because most such tutorials are several years old, don't implement current best-practices, and were written without any awareness of the WordPress Settings API.

While others such as Otto and Ozh have done yeomen's work in explaining how to implement the Settings API, I have not yet come across anything that really put everything together, and explained the process and implementation from beginning to end, in a way that even the less-experienced Theme developers (like me) could easily understand.

This tutorial will attempt to fill that gap, by providing examples of current (as of the pending release of WordPress 3.1) best-practice implementation, not merely of the Settings API, but of Theme Options implementation as a whole, including:

  • Registering options in the database as a single options array
  • Initializing default options
  • Creating a single Theme Settings page (with tabs)
  • Defining settings page sections and fields
  • Validating and white-listing user-input form data
  • Adding Settings Page contextual help
  • Enqueueing custom CSS for the Settings page
  • Implementing settings in the Theme template files
  • Enqueueing front-end CSS

Throughout this tutorial, I will be using code from my Oenology Theme for implementation examples. For full code, see the latest development version.

Assumptions: Best Practices

The following will be the working list of best practices that will be incorporated:

  • Theme Settings defined as an options array in a single database entry
  • Theme settings added to a single Theme Settings Page
  • Theme Settings page is added to the "Appearance" menu
  • Theme Settings page added using the "edit_theme_options" user capability
  • Theme Settings registered, updated, and validated using the WordPress Settings API
  • Theme Settings modify the template using action/filter hooks wherever possible

Getting Started

The first thing we need to do, even before touching any code, is to determine:

  • What options to include in the Theme
  • How to organize those options on the Theme Settings Page

In my case, I am adding only a handful of simple options:

  • Header Navigation Menu Position: currently, Oenology displays the Header Navigation Menu above the site title and description. I am adding a setting optionally to display the Header Navigation Menu below the site title and description.
  • Header Navigation Menu Depth: currently, Oenology is designed so that only the top-level Pages display in the Header Navigation Menu, and Child Pages display in a left-column sub-menu. I am adding a setting optionally to display Pages to a depth of one (top-level Pages only), two, or three, with hover drop-down menus.
  • Footer Credit Link: currently, Oenology does not display any form of footer credit link. I am adding a setting optionally to display a footer credit link.
  • Varietals: the default style of Oenology is intentionally minimal. It is intended to be clean, simple, and cross-browser. I am adding a setting optionally to select from among Theme "skins" (which, in keeping with the oenology metaphor, will be called "varietals"), which will apply different color/style schemes to the Theme.

Given that I'm only adding four Theme options, I could very easily put all four together on one Settings page. However, I may want to add additional options in the future - and also, I want to provide a proof-of-concept for creating complex Theme Settings pages in a way that supports the Settings API. So, the Theme Settings page will have two tabs: "General", and "Varietals". Further, the "General" tab will have two sections: "Header Options" and "Footer Options".

So, that's our basis. Let's get started!

Beginning Git Cheat Sheet

Filed in Web DevelopmentTags: Git, Oenology, Themes, WordPress

Since Theme developers don't (at least, not yet) have SVN-commit access for the WordPress Theme Repository, I decided to host Oenology on GitHub. Git is an entirely different beast from Subversion, so it took me a bit to figure out exactly how to use it.

So, for the most part, this post is my own personal cheat sheet, though perhaps someone else new to Git might also find it helpful.

Initial Setup

Generate SSH Keypair:

ssh-keygen -t rsa -C "name@domain.com"

Setup local system's global Git username and password:

git config --global user.name "User Name"
git config --global user.email name@domain.com

Setting Up a Local Repository

One major difference between SVN and Git that I quickly discovered is that SVN defaults to setting up subdirectories for the working copy (/trunk) and previously tagged versions (/tags), but Git does not. So, whereas with SVN, my local repository checkout (i.e. the directory tracked by SVN) was simply a directory called /oenology, with Git, it made more sense for me to create my own subdirectories: /master for the working copy and /tags for tagged versions, and then do my Git checkout in /oenology/master.

Change path to working directory:

cd ~/<path>/oenology/master

Initialize local directory as a Git repository:

git init

Checkout the remote repository:

git remote add origin git@github.com:<username>/<repository>.git

Working With Git Repositories

Add (stage) all files in the local directory to the repository:

git add .

Commit (snapshot) all file changes in the local directory:

git commit -m "Commit Message"

Push all snapshots to the remote repository:

git push origin master

Working With Tags

Tag the current snapshot:

git tag -a "Version Number"

Push all tags to the remote repository:

git push origin --tags

Delete a tag:

git tag -d "v12345"
git push origin :refs/tags/v12345

Working With Branches

List Branches:

git branch

Create a Branch:

git branch {branch}

Switch to a Branch:

git checkout {branch}

Push a Branch:

git push origin {branch}

Merge a Branch:

git merge {branch}

Delete a Branch:

git delete -d {branch} // only deletes the branch if all changes are merged
git delete -D {branch} // deletes branch, regardless of merge state

For More Information

For more detailed help, refer to GitHub Help, and to the Git Reference.

Using WordPress Post Formats as a Taxonomy

Filed in Web DevelopmentTags: Post Formats, Themes, WordPress

One of the great new features in the soon-to-be-released WordPress 3.1 is Post Formats. This feature enables posts to be classified as one of the following "types": standard (default), aside, audio, chat, gallery, link, quotestatus, or video. As described in the WordPress Codex:

A Post Format is a piece of meta information that can be used by a theme to customize its presentation of a post. The Post Formats feature provides a standardized list of formats that are available to all themes that support the feature... In short, with a theme that supports Post Formats, a blogger can change how each post looks by choosing a Post Format from a radio-button list.

Clearly, the most obvious use of Post Formats will be to customize the layout or style of a post based on the selected Post Format type. While this use will be incredibly beneficial, it does not fully describe the power or functionality of the Post Formats feature.

To give just one example, the Post Formats feature includes its own taxonomy (similar to Categories or Tags). Having their own taxonomy means that posts can be displayed by Post Format type.  Compare:

Category taxonomy:
mysite.com/category/foo-bar
Tag taxonomy:
mysite.com/tag/foo-bar
Post Format taxonomy:
mysite.com/type/foo-bar

Consider the potential of this taxonomy. Want to add podcasting to your site? Assign all of your podcast posts the audio Post Format type, and your podcasting section becomes mysite.com/type/audio. Want to add a photo gallery section to your site? Assign your gallery posts to the gallery Post Format type. Photoblogging? Use the image Post Format type. Videoblogging? Use the video Post Format type.

Implementing Post Format Taxonomy in Themes

Making use of the Post Format taxonomy in Themes is incredibly easy, thanks to a few functions:

  • get_post_format() - return the Post Format slug, e.g. 'aside'.
  • get_post_format_string( $post-format ) - return the Post Format string, e.g. 'Aside'.
  • get_post_format_link( $post-format ) - return the URL to the Post Format archive page, e.g. mysite.com/type/aside

Breadcrumb Navigation

For example, I have added the Post Format taxonomy to the breadcrumb navigation in my Oenology Theme:

Post Format Breadcrumb Navigation

Post Format Breadcrumb Navigation

This breadcrumb is accomplished simply by using:

get_post_format_string( get_post_format() )

(Note: the output is a string, to which I appended a 's' to the end, because I think "Post Format: Images" is more intuitive than "Post Format: Image" in this context.)

Taxonomy Archive Links

As another example, I have added links to the Post Format taxonomy archive:

Post Format Link

Post Format Link

This link is accomplished using the following code:

'<a href="' . get_post_format_link( get_post_format() ) . '">' . get_post_format_string( get_post_format() ) . '</a>'

Taxonomy Sidebar Widget

How about adding a Widget that outputs a list of Post Format types, to complement the Category and Tag Widgets? Here's a rough proof-of-concept:

Post Formats Sidebar Widget

Post Formats Sidebar Widget

The code for this is a bit more convoluted, but here are the important bits:

<ul class="leftcolcatlist">
<?php
$postformatrssimg = "/images/rss.png";
$postformatrssurl = get_template_directory_uri() . $postformatrssimg;
$postformatterms = get_terms( 'post_format' );
foreach( $postformatterms as $term ) {
$termslug = substr( $term->slug, 12 );
$termname = $term->name;
$termlink = get_post_format_link( $termslug );
$termcount = $term->count;
$postformatlist = '<li><a title="Subscribe to the '. $termname .' news feed" href="' . home_url() . '/type/' . $termslug .'/feed/"><img src="'.$postformatrssurl.'" alt="feed" /></a><a href="'. $termlink .'">' . $termname . '</a> (' . $termcount . ')</li>';
echo $postformatlist;
}
?>
</ul>

What else? Well,  what about adding Post Format taxonomy to a menu?

Just remember: with Post Formats, think outside the box of Post layout and style, and consider what else you can do with this great new feature!

I've added a proof-of-concept example of a Post Format sidebar Widget, complete with taxonomy archive link, feed link, and count

Only Download WordPress Themes From Trusted Sources

Filed in Web DevelopmentTags: GPL, Malware, Themes, WordPress

Earlier this week, I released version 1.0 of my Oenology Theme. As I tend to do occasionally, after the release I decided to browse the Google search results for "Oenology WordPress", just to keep track of any mentions of the Theme.

I was somewhat surprised to discover, among the first-page search results, a site that was re-distributing a modified version of the Theme. Now, there is nothing inherently wrong with re-distribution of the Theme in either its original form or modified; I released it under the GPL, which explicitly permits modification and redistribution. However, these Theme modifications turned out to be insidiously malicious. As Otto explains, Themes distributed by the site in question had been hacked to (among other things) include a well-hidden PHP shell that would allow the hacker a backdoor to access the site on which the Theme is installed.

It is generally-accepted knowledge within the WordPress developer community that such malicious sites have over-run the search engines, and dominate the search results for WordPress Theme-related search queries. Thus, following the above discovery, I decided to audit those search results, to get an idea of just how bad the current situation is. I performed a Google search for "Free WordPress Themes". The results are sobering.

For each of the first 30 results (first three pages), I evaluated the domain name against the WordPress Domain Name Trademark Policy, determined whether the Themes were original, or taken from elsewhere, and whether the Themes had SEO/Spam links, encoding, and/or other forms of malware.

A note on the original source of the Themes: while some sites simply modified existing Themes, and some developed "original" Themes (derivatives of Kubrick, or Artiseer-generated, etc.), a great many of the audited sites distributed many of the same Themes. There apparently exists a rather incestuous relationship among these Theme-distribution sites - and especially among the sites distributing the worst-offending Themes.

SEO/Spam Links, Encoding, and Malware

Of those top-30 search results for "Free WordPress Themes":

  • 2/30 were links to the Official WordPress Theme repository
  • 1/30 was a "Top 100 Themes" blog post from a legitimate source
  • 1/30 was an affiliate-link aggregator to legitimate Premium Themes

Of the remaining 26 results:

  • 8% (2/26) were (at least somewhat) legitimate WordPress Theme sites
  • 35% (9/26) violated the WordPress Trademark
  • 88% (23/26)had Themes with Spam/SEO links
  • 76% (19/25) had Themes with base64 or other encoding (note: 1/26 not verified)
  • 12% (3/26) had Themes with other malware (note: 17/26 sites not verified)

Of the sites that added base64 or other encoding, many of them (at least 5/19) included encoded "kill codes" that prevented the Theme from loading if the footer code was modified. All of them included SEO/Spam footer links.

Of the sites that added other malware, one added the well-known "Verify Widgets" worm to the functions.php file, and another added a sort of backdoor that allows for remote management of spam links, and phones home data back to the hacker.

WordPress Trademark-Violating Sites

I noticed what appeared to be a correlation between WordPress trademark-violating domains and the presence of SEO/Spam links, encoding, and malware. That appearance seems to be valid, though I make no claim on the statistical significance of the correlation. One of the 9 WordPress trademark-violating sites was the aforementioned affiliate-link aggregator; of the remaining 8 WordPress trademark-violating sites:

  • 100% (8/8)had Themes with Spam/SEO links
  • 100% (8/8) had Themes with base64 or other encoding
  • 13% (1/8) had Themes with other malware (note: 4/8 sites not verified)

Just to determine if my sample size was too small, I did a quick audit of the next two pages' worth of results (thus including the top 50 results). I found an additional 4 sites (for a total of 13 out of 48 sites (27%) that violated the WordPress trademark).

Including those 4 sites with the previous results:

  • 100% (12/12) had Themes with Spam/SEO links
  • 100% (12/12) had Themes with base64 or other encoding
  • 17% (2/12) had Themes with other malware (note: 4/9 sites not verified)

Are you beginning to notice a trend?

Restrictive Licensing

But mere inclusion of malware isn't the extent of the problem. Themes are also encumbered either with restrictive licensing, or else mal-applied licensing, in order to prevent the user from removing the crap that the sites added to the Themes.

Of the 27 above-referenced, all 27 incorporated some form of license encumbering. Most sites simply distributed the Themes with some sort of "Terms of Use" statement that (among other things) prohibited modifying the footer. The next most-popular licensing strategy was to distribute the Themes under some variation of a Creative Commons Attribution license, with the claim that the "footer links" were the license-conforming attribution. Quite a few Themes even attempted to claim that they were licensed under GPL, and that the GPL allowed the restriction against removal of footer credit links.

Of the 2 legitimate Theme sites, only one - ThemeLab, run by well-known WordPress community member Leland Fiegel -  distributed unencumbered, GPL-licensed Themes. The other distributed Themes encumbered with a mal-applied CC-Attribution license.

Mal-Applied Creative Commons Attribution Clause

Of course, the requirement to keep a public-facing link to an SEO/Spam link has nothing to do with the Attribution clause of the CC-Attribution license [emphasis added]:

If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.

The license requires that attribution of the original author, the original author's copyright, and a link to the same, must be retained within the work. It does not require that such attribution be public-facing, nor does it require that the author may specify any link other than one directly related to attribution to the author and his copyright of the original work. In fact (as noted above), the attribution clause specifically excludes URIs unrelated to the copyright notice or licensing information of the original work.

Mal-Applied GPL Attribution

Similarly, the requirement to keep public-facing attribution links is not only part of the GPL (in any version), but such a use restriction directly contradicts the wording of the license itself. Some confusion may arise regarding the requirement to retain appropriate copyright and licensing information in conveyed works (whether original or derivative), but under no circumstance does the GPL require that such information be portrayed in a public-facing manner. (The accepted standard for PHP code is to add this copyright and licensing information as PHP comments in the header of one or more PHP files.) And in fact, the GPL even states that if further restrictions are imposed upon a GPL-licensed work, that such restrictions may be ignored.

But Wait, There's More!

Theme Quality

As if SEO/Spam links, encoding, malware, and encumbered licensing weren't enough, I cannot fail to discuss the quality of the Themes distributed by the audited sites.

Again, with the lone exception being the one legitimate Theme site that distributes malware-free, GPL-licensed Themes, the remainder of the sites distributed Themes of - there's no better way to say it - utterly horrible quality. The Themes are, generally speaking, woefully obsolete. Trudging through this morass renewed my appreciation for the current effort to improve the quality standard of Themes submitted to the official WordPress Theme Repository.

Conclusion

If you are looking for free WordPress Themes, avoid the search engines; stick to known, trusted sources. Otherwise, you will end up with a poor-quality, license-encumbered Theme full of SEO/spam links, encoded content, and other malicious malware. If your primary objective is to find a free Theme, I strongly suggest that your search start - if not end - with the Official WordPress Theme Repository. (Although, I fully endorse ThemeLab as well.)

As a corollary: sites whose domain names violate the WordPress trademark policy cannot be trusted. It is safe to assume that a site that violates the trademark policy is not directly involved with the WordPress community. Such a site is either intentionally flaunting the trademark policy - likely in order to gain SEO from the domain name, and likely to be doing so for nefarious purposes - or else is not closely enough involved with the WordPress project or community to have the necessary experience and expertise.

Theme Review Weekly Stats: 21 Nov 10

Filed in Web DevelopmentTags: Theme Review, Themes, WordPress

Each week, I publish various statistics to measure the Theme Review process for Themes submitted for inclusion in the Official WordPress.org Theme Repository (any inaccuracies are due to manual counting).

Here's how we've done (improved/disimproved/maintained) over the week ending 20 Nov 2010:

Compared with last week:

  • The # of tickets closed decreased. (76 vs 124)
  • The review queue increased. (56 vs 52)
  • The oldest open ticket age (days) maintained. (14 vs 14)
  • The average time to close tickets (days) increased. (5.9 vs 3.8)
  • The average age of open tickets (days) decreased. (4.0 vs 5.7)
  • The % of tickets closed as approved increased. (27% vs 21%)
  • The # of reviewers decreased. (4 vs 6)

This Week (above/below/at goal):

Closed Tickets:

  • # Tickets Opened: 74
  • # Tickets Closed: 76
    • # Approved: 14
    • # Not-Approved: 38
    • # Newer-Version-Uploaded: 24
  • % Closed Tickets Approved: 27%
  • Average Days to Close: 5.9

Open Tickets:

  • # Tickets Open: 56
  • Oldest: 14 days
  • Average Age: 4.0 days

This Week's Top Reviewers (# Tickets Closed):

  1. chipbennett: 34
  2. chris@thematic: 20
  3. cais: 8
  4. beckyTechy: 1

Historical trend charts have been updated.

Theme Review Weekly Stats: 11 Nov 10

Filed in Web DevelopmentTags: Theme Review, Themes, WordPress

Each week, I publish various statistics to measure the Theme Review process for Themes submitted for inclusion in the Official WordPress.org Theme Repository (any inaccuracies are due to manual counting).

Here's how we've done (improved/disimproved/maintained) over the week ending 06 Nov 2010:

Compared with last week:

  • The # of tickets closed increased. (124 vs 100)
  • The review queue decreased. (52 vs 72)
  • The oldest open ticket age (days) increased. (14 vs 11)
  • The average time to close tickets (days) increased. (5.7 vs 2.6)
  • The average age of open tickets (days) decreased. (3.8 vs 4.7)
  • The % of tickets closed as approved decreased. (21% vs 24%)
  • The # of reviewers maintained. (6 vs 6)

This Week (above/below/at goal):

Closed Tickets:

  • # Tickets Opened: 100
  • # Tickets Closed: 124
    • # Approved: 20
    • # Not-Approved: 74
    • # Newer-Version-Uploaded: 30
  • % Closed Tickets Approved: 21%
  • Average Days to Close: 5.7

Open Tickets:

  • # Tickets Open: 52
  • Oldest: 14 days
  • Average Age: 3.8 days

This Week's Top Reviewers (# Tickets Closed):

  1. chipbennett: 55
  2. cais: 30
  3. chris@thematic: 13
  4. Fingli: 10
  5. pross: 1
  6. beckyTechy: 1

Historical trend charts have been updated.

Interviewed by WPTavern

Filed in Web DevelopmentTags: Theme Review, Themes, WordPress

I was interviewed by Jeff Chandler for Episode 106 of the WordPress Weekly podcast. In the interview, we discussed the WordPress Theme Review Team, the Theme Review Guidelines for Themes submitted for inclusion in the WordPress Theme Repository. Have a listen (click to play inline or download):

WordPress Weekly Episode 106: The Team Review Team

Theme Review Weekly Stats: 07 Nov 10

Filed in Web DevelopmentTags: Theme Review, Themes, WordPress

Each week, I publish various statistics to measure the Theme Review process for Themes submitted for inclusion in the Official WordPress.org Theme Repository (any inaccuracies are due to manual counting).

Here's how we've done (improved/disimproved/maintained) over the week ending 06 Nov 2010:

Compared with last week:

  • The # of tickets closed decreased. (90 vs 92)
  • The review queue increased. (72 vs 44)
  • The oldest open ticket age (days) decreased. (11 vs 12)
  • The average time to close tickets (days) increased. (2.6 vs 1.5)
  • The average age of open tickets (days) increased. (4.7 vs 3.0)
  • The % of tickets closed as approved decreased. (24% vs 25%)
  • The # of reviewers maintained. (6 vs 6)

This Week (above/below/at goal):

Closed Tickets:

  • # Tickets Opened: 131
  • # Tickets Closed: 90
    • # Approved: 18
    • # Not-Approved: 57
    • # Newer-Version-Uploaded: 15
  • % Closed Tickets Approved: 24%
  • Average Days to Close: 2.6

Open Tickets:

  • # Tickets Open: 72
  • Oldest: 11 days
  • Average Age: 4.7 days

This Week's Top Reviewers (# Tickets Closed):

  1. chipbennett: 41
  2. cais: 28
  3. chris@thematic: 8
  4. pross: 4
  5. tinkerpriest: 2
  6. greenshady: 1

Historical trend charts have been updated.