Members Plugin

I needed to figure out how to control who could add the different types of content to the site. The information in the WordPress codex on Roles and Capabilities isn’t too complete at the moment. It took a few readings to understand the system, but there was still not much clue on how to write code to customize it.

After trying a couple of different plugins, I finally found that the Members plugin by Justin Tadlock does what I want. This plugin can be used to manage roles and then assign them capabilities. It can also do other things like limit content from certain users, but I only want to control the types of content which the different roles can post.

My plan is to have a blogger role who will only post normal WordPress posts. These will appear on the blog page. Another role will take on the role of content manager. This role will be given control over the sermons and newsletters, but not the blog posts. The primary intention of all this is to make the admin interface less complex for the end users and only show them the things they can edit.

The blogger role is currently fulfilled by the default author role. By adding the ‘capability_type’ key to the custom post types, the default author can no longer see them.

Using the plugin, it was a simple task to add the role of content manager. I then gave it capabilities to edit and publish sermons and newsletters. The necessary capabilities are edit_sermons, edit_others_sermons, edit_published_sermons, publish_sermons, delete_sermons, delete_others_sermons for sermons. Replace ‘sermons’ with ‘newsletters’ and all all the capabilities for control over newsletters.

It is also necessary to grant manage_categories so category and taxonomy terms can be added and managed. To allow the role to assign terms in custom taxonomies, the key-value pair 'capabilities' => array('assign_terms' => 'edit_sermons') is needed when registering the custom taxonomy. Once again, replace ‘sermon’ with whatever capability_type you want.

To allow media to be managed, the edit_posts and *_posts capabilities also had to be added. * is a wildcard for all the other functions. This actually allows the content manager control over the blogger’s posts, but the problem is mitigated somewhat by hiding the posts menu from the interface.

While the author role currently fulfills the blogger role, it would be better to separate them. The default author role will then be given the combined capabilities of church_cm and blogger.

Advertisements

Add from Server plugin

Uploads through the admin interface go into the uploads folder with the structure year/month. This is usually fine, but what if I want to upload lots of past content? If I upload a lot of past content now, it’ll all end up in the 2011/03 folder, regardless of when the content was produced. This will be messy. The better solution is to upload each to its own year/month folder, a much neater solution.

However, manually placing the files into the desired folder structure does not cause them to show up in the admin interface. It turns out that WordPress doesn’t check the folders to see if anything is inside. A database entry is needed. This can be accomplished with the Add from Server plugin, which scans the directories and adds the required database entry.

This allows the files on the server to be added to the database and hence show up in the media library. The file will attach itself to the post which is currently being written, unless the checkbox “Do not add to current post gallery” is selected.

Custom Post Type Archives Part 4

Phew, custom post type archives are causing lots of problems. Three separate posts about them already, and the plugin was the one which broke WordPress 3.1. Well the updated version doesn’t. However, this led to a thought. Since it’s supposed to be built in, could I do it without using the plugin? After all, I was just after some very basic functionality. The answer turns out to be yes, well, almost.

Enabling the Feature

There is an attribute has_archive in the register_post_type() function which activates the archives feature, but it defaults to false. I added the code in to set this to true. This information was from Mark McWilliams, the same post also gave some hints on how the permalinks would look like.

Disabling the Plugin

The next step was to disable the plugin and see what stops working. Looks like the sidebar has some problems, which also prevents the admin bar from loading properly. The month listing from the custom widget wasn’t appearing at all.

Fixing the Widget

The widget depends on the custom get_post_type_archives() function, explained in Part 2, to do its magic.

Using a debug echo statement and commenting out suspect lines, I found the problem in one of the custom functions provided by the plugin. It returns the permalink of the post type. This can actually be generalized as

get_bloginfo('url') . '/' . $post_type . '/';

Now I get output with a link to the archives by date, but it wasn’t showing the correct number of months. Why??

Filter getarchives_where

Even the plugin just let the built in WordPress function wp_get_archives() do the hard work, so why did it work with the plugin activated? A closer look at the function itself, and the way it was used by the plugin, revealed that wp_get_archives() was insufficient to get archives by post type. It applies a filter to get the correct SQL Where clause. This was used by the plugin.

Since I only want that function, I copied the entire thing over to my own functions.php file.

function pta_wp_get_archives_filter($where, $options) {
	if(!isset($options['post_type'])) return $where; // OK - this is regular wp_get_archives call - don't do anything
	
	global $wpdb; // get the DB engine
	
	$post_type = $wpdb->escape($options['post_type']); // escape the passed value to be SQL safe
	if($post_type == 'all') $post_type = ''; // if we want to have archives for all post types
	else $post_type = "post_type = '$post_type' AND"; // otherwise just for specific one
	
	$where = str_replace('post_type = \'post\' AND', $post_type, $where);
	
	return $where;
}
add_filter('getarchives_where', 'pta_wp_get_archives_filter', 10, 2);

Now the archive links work and the correct number of links are displayed, depending on what the post type is!

Archive Templates

This approach led to my custom post types being displayed incorrectly as they now use the archive.php template file. It only displays the excerpts for archives, so the shortcodes and meta data no longer show up. As each post will be very short in length, I want the full content and meta data to show up, just like it does on the main page listings.

The solution to this was simply to copy archive.php to the child theme, rename it archive-{post-type}.php, and change the function call to the loop so that it calls my previously written custom loops.

Page Templates

{post-type}-template.php were previously used for both the display of the full listing as well as the archive pages. With WordPress 3.1, archives are handled by the archive template pages. Thus, the if-else conditions which handled query_posts() can be removed. The page templates will be used just for display of all the custom posts.

Conclusion

With all these changes, I can now use one less plugin and use more of the functionality available in WordPress Core.

Custom Post Type Archives Part 5

WordPress 3.1 Reinhardt Released

Looking through the forums this morning, I suddenly realised that there were lots of references to WordPress 3.1. I quickly checked the main page and discovered that it had been released while I was asleep. What would it mean for the site?

Having setup my site to use SVN, I just had to switch the working copy over to the latest version to update. After the update was complete, I visited the site to see an Internal Server Error. A quick bit of Google searching suggested that plugins should have been disabled before updating. Oops. I renamed the plugins folder and the site was up again.

One of the new features in WordPress 3.1 is Custom Post Type Archives, so maybe that clashes with the plugin I’m using. It was also the only one with an update. With the plugin disabled, the audio player didn’t work, and neither did the custom archives widget.

There didn’t seem to be any useful information on how to enable dated archives for custom post types, so I decided to try updating and activating the plugin.

Still one more problem. The archive pages were showing the wrong sidebar. A bit of investigation showed that the plugin provided function is_post_type_archive() had been renamed to pta_is_post_type_archive() because WordPress had used the old name. Since I was using that function in sidebar.php to check for the page type, I had to update its name. Everything seems to work now!

Shortcode for the Year

I wanted to get the current year using a shortcode. In addition to that, I wanted it to be usable in a text widget. This will allow me to put it in the footer with a text widget, which is much less troublesome than modifying the entire footer file to use a page.

The code for creating the shortcode is very simple and borrowed from the Thematic theme.

function shortcode_year() {
    return '<span class="the-year">' . date( 'Y' ) . '</span>';
}
add_shortcode('the-year', 'shortcode_year');

This tutorial on DigWP explains how to get shortcodes to work in widgets. It’s a very simple solution which involves filtering the widget text.

add_filter('widget_text', 'do_shortcode');

That’s it, putting [the-year] in a text widget will now result in the current year being displayed in the browser.

Custom Post Type Archives Part 3

Part 2 explained how to create a widget to show the chronological links for the sermon post type. This post will explain how I generalized the widget to show the chronological links for a custom post type specified in the the widgets administration panel.

It uses the same technique as described in the Custom Taxonomy widget post.

Widget function

Add the line $post_type = $instance['posttype']; after the line initializing the $title variable.

Change the hardcoded value of ‘sermon’ in the custom get_post_type_archives call to $post_type

Update function

Add the line $instance['posttype'] = strip_tags($new_instance['posttype']);

Form function

Add the line $post_type = strip_tags($instance['posttype']);

Copy the line for the title text field and paste it right below the original line. Modify all references to the title text field and set it to ‘posttype’. The value which should be echoed out is $post_type

Using the Widget

There will now be an extra field in the widget administration. Type in the name of the custom post type here.

Custom Post Type Archives Part 4

Adding a new Custom Post Type

As before, use the register_post_type() function to register a new custom post type. This will be named ‘newsletter’ and will be for the weekly messengers. The name ‘newsletter’ was chosen as it is the most generic description of this type of content. Using the specific name would make it more troublesome should I need to reuse this code in the future.

The key thing I wanted to do here is to let it use the built in categories. This is done by adding 'taxonomies' => array('category') to the array of arguments passed into the registration function. Additional taxonomies can be added to the array if necessary. This tip was found at deluxeblogtips

The rest of the steps are a repeat of what was done for the sermon post type. A custom loop file was created, the main feature in this loop is the code to get the memory verse from the custom fields and display it in a blockquote.

Similar to the sermon post type, all the newsletter entries will be displayed with a custom page template. This modifies the Query so that it only gets the newsletter post type. The same trick of checking for the existence of the pagename array key is used to check if the template is being used for the chronological archives or the normal display page.

A custom template file is also needed for single posts. It’s the same as the sermon one except that it calls a different loop.

I also had to enable post type archives in the Custom Post Type Archives plugin for my new custom post type.