Category Archives: Plugins

Plugins used and their purpose

Public Cannot View Scheduled Posts

A user going by the name of drober started a thread on the Announcements Ticker forum complaining that non admin users were unable to view the full text of announcements by using the “Read All” link. On his site, they would be prompted to login, but non admin accounts still could not see the content.

This is because the plugin sees future posts as active announcements. This is by design so the announcement can automatically when expire when it reaches the scheduled date. However, WordPress does not allow non admin users to view future posts. This never showed up in testing because I always used a logged in admin account.

The fix was to hook into the one of the save_post actions and change the post status from future to publish if an announcement is saved. I also had to take out the condition that the plugin only selects future posts for display.

For the full discussion, see the forum support thread.

Custom Post Type Permalinks

After creating the custom post type in my plugin, I tried visiting domain.com/, but got a 404 error page instead.

I came across a blog post by Kovshenin which explains how to get permalinks working with custom post types. The key thing is to specify the slug with the ‘rewrite’ parameter when registering the CPT. After that, remember to reset the permalinks by visiting the permalinks settings page in Settings -> Permalinks. Quite a lot of 404 grief can be solved by doing so.

It makes sense to automate this in the plugin. To do so, I added flush_rewrite_rules() to the activation and deactivation hooks. This would ensure that the permalinks are reset only when the CPT becomes active or inactive.

Do not let the function run on every page load. It is very resource intensive and will slow your site down.

register_activation_hook()

I’m creating custom post type in a plugin for the first time. So far, it’s been done directly in the child theme. Not the best place as functionality should be separate from display.

The first thing was to copy, edit and paste one of the register_post_type() calls I had written before. Right away something was strange. The CPT showed up on my development site, but not on a blank install. After much commenting out of various lines, I found that the problem lay with the ‘capability_type’ argument.

My development site is multisite, and it looks like network admins have much more capabilities than normal site admins, or perhaps it was the role management plugin magically granting it rights. I looked into the code for the role management plugin (Members by Justin Tadlock) and discovered that it added some extra capabilities upon plugin activation. Sounds like just what I needed.


register_activation_hook( __FILE__, 'mbpc_feba_cpt_install' );

// Start by giving the administrator role access to the CPT
function prefix_cpt_install() {
	/* Get the administrator role. */
	$role =& get_role( 'administrator' );

	/* If the administrator role exists, add required capabilities for the plugin. */
	if ( !empty( $role ) ) {
		/* Role management capabilities. */
		$role->add_cap( 'publish_cptname' );
		$role->add_cap( 'create_cptname' );
		$role->add_cap( 'delete_cptname' );
		$role->add_cap( 'edit_cptname' );
	}

}

I’ve edited some of the names used. When you’re writing your own, remember to prefix your functions so they’ll be unique, and of course replace ‘cptname’ with a meaningful name.

Unfortunately, the code above did not work for me. It looked like the activation function was not being called. Putting echo statements inside didn’t do anything, and even putting die() inside did nothing.

When there’s a problem, start with the Codex. The page for register_activation_hook() had line near the bottom referencing a support forum thread. Turns out that ‘echo’ really doesn’t work, and the way to check if the activation hook is being called is to use exit().

Another user pointed out that __FILE__ doesn’t work when the plugin directory is only symlinked. That’s exactly what I’m doing! The solution is to pass it the path to the symlink.

register_activation_hook( WP_PLUGIN_DIR . '/plugin-name/plugin-name.php', 'prefix_cpt_install' );

And now the CPT shows up in the admin menus! wp_user_roles in the options table also has the new capabilities added by the activation function.

SlideDeck Bumps

With new events coming up, it was time to replace the Slidedeck on the home page. This turned out to be more difficult than expected.

Firstly, the Insert SlideDeck button was hidden behind the Upgrade banner. When I moved that admin widget away from the sidebar to the main post column, the ‘Insert’ button became visible, but it didn’t work. Terribly irritating.

I remembered that all it did was to insert a shortcode into HTML view, but when I tried [Slidedeck], that didn’t work either. After firing up my development VM and checking the test page I had created, I realized that the shortcode is case sensitive.

For my own reference, and because all the information online was on how to use the ‘Insert’ button, the shortcode I’m using is below.

[SlideDeck id='xxxx' width='100%' height='250px'] Replace xxxx with the id number of the SlideDeck, which can be seen when editing it.

Updating Slidedeck Plugin

Updating the Slidedeck plugin to version 1.4.2 broke my site. I got PHP warnings about “file get content cannot be empty”, or something like that.

Much to my irritation, there’s also no way to downgrade back to version 1.4.1, although that wouldn’t have solved anything.

The problem is that the ‘skins’ folder containing my custom slidedeck skin was overwritten, so it could no longer find the active skin. Replacing this folder from the backup on my local development copy solved the problem.

Plugin Bugfix

It has come to my attention that my plugin was generating a PHP warning “Warning: Missing argument 2 for flh_wpcomgms_search_map_code()”

Although it doesn’t hurt anything, the warning is ugly.

Reading the documentation of the wp_insert_post_data filter more closely revealed the problem.

You must pass 2 for the $accepted_args variable in add_filter() if you want to access $postarr.

So I did that, and the warning went away.

However, when updating the plugin, I ran into a problem. The correct version was made available, but the text on the download button still referred to the old version number. I emailed plugins support, and discovered that I had forgotten to change the version number in the PHP file itself. I had only changed the ‘Stable Tag’ field in the readme.txt file.

So remember, when updating a plugin, it’s necessary to change both the ‘Stable Tag’ in the readme.txt file as well as the version number in the PHP file header.

First Plugin!

I’ve written and contributed my first ever plugin! This was in response to a forum post asking for help in getting WordPress.com’s Google Maps shortcode to work. The user had moved over to a self-hosted WordPress.org and all the previously used shortcodes weren’t working.

My original solution was simply to add in the shortcode and get the shortcode handler to throw out the <iframe> code. The width and height was hardcoded and the ‘View Larger Map’ link wasn’t there, but the maps showed.

The very first draft of the plugin can be found in the pastebin.

The release version of the plugin supports variable widths and heights for the maps. It does this by finding the width and height of the original <iframe> then appending those values to the end of the URL stored in the shortcode, just like how it’s done on WordPress.com.

When the URL is parsed for output, those values are extracted and used to define the dimensions of the <iframe>.

Another important step which had to be taken was to move the execution of the shortcode up, before wpautop is called. If not, linebreaks will not be added correctly, causing the layout in the post to go haywire. Code to do this was adapted from http://www.viper007bond.com/tag/wpautop/

After all this talk, I’m sure you want to know where to find the plugin. It’s been uploaded to the official plugin repository.

I’ve also created a page on this blog which explains its purpose.

If you find it useful, do leave a comment and tell me about it 🙂

Adding Google Analytics

I suddenly had the urge to know how many visitors my site is getting. I had added Google Analytics code manually to the other site, but didn’t have it on the main church site.

Logging in to Google Analytics and getting the code was easy, the problem was how to put it in. The instructions said to put it before the </head> tag, which implied putting the Javascript into a .js file and using wp_enqueue_script() to put it in. I decided to google a bit to see if this is the best way to insert the code.

Of course there are already plugins to do this. I decided to try the one by Joost (http://wordpress.org/extend/plugins/google-analytics-for-wordpress/). The options are pretty comprehensive and it seems simple enough to use. I just had to type in the UA number and that’s it!

Now to wait and see if it starts tracking successfully.

Incorrect Download link for Subsites

Background

A custom content form for my custom newsletter post type wasn’t saving post content, thus my solution was to ignore the post content and generate it on the fly by getting the attached files instead. This worked beautifully in the main site, unfortunately, on the chinese subsite, wp_get_attachment_url returned something different.

Cause of Problem

It turns out that the _wp_attached_file meta value in the postmeta table is storing the full path to the link in the subsite. The corresponding value for posts in the main site only have the year/month/filename. They don’t contain the full domain name too. What this meant is that for the subsite, wp_get_attachment_url will add the domain to the front of the full link, thus generating an invalid link.

Solution

An email to the wp-hackers list had no responses, I’m not sure if it’s because nobody knows anything about it or whether the email got lost. Thus my solution is to filter the function output to strip out the extra baseurl which prepends the domain name. If this is necessary, the post meta is also updated so the next time this is needed, the correct output will be returned and there will no need to strip out the extra domain name again.

add_filter( 'wp_get_attachment_url', 'mbpc_wp_get_attachment_url_filter', 10, 2);

function mbpc_wp_get_attachment_url_filter($url, $postID) {
	$upload_dir = wp_upload_dir();

	//search through the url and see if it contains a 2nd instance of the baseurl
	$pos = strpos( $url, $upload_dir['baseurl'] , 1);

	if ( $pos !== false) {		//2nd instance exists
		$url = substr ($url, $pos);
		//update post meta so it won't have to find the substring on subsequent calls
		update_post_meta( $postID, '_wp_attached_file', substr( $url, strlen( $upload_dir['baseurl'] ) + 1 ));
	}
	return $url;
}

Newsletters for Subsite

Previously, newsletters on the subsite were put up with default Posts because I hadn’t done the necessary work for the subsite as well. However, to allow for a uniform admin interface, it makes more sense for the chinese newsletters to go up as ‘newsletter’ custom post types as well. Using phpmyadmin and SQL, I changed all the Posts in the subsite to newsletters.

Content Manager Rights

The whole idea is to let a layperson upload the weekly newsletters. This function already exists for the main site. However, to make it work for the subsite, I had to setup the Members plugin and roles all over again. Since everything is stored in the wp_options table, I just had to copy and paste the wp_user_roles option to wp_x_user_roles on the subsite, and copy and paste the members_settings option wp_options to the corresponding options table for the subsite. This has the effect of activating and configuring the members plugin with all the role types I had previously setup on the main site correctly configured on the subsite.

Roles and Capabilities for Custom Post Types

WordPress access rights are controlled by Roles and Capabilities. To perform an action, your user role must have that capability. For example, to edit posts, your role must have edit_posts assigned to it.

Custom Taxonomies

Strangely, while managing terms for custom taxonomies requires manage_categories, this does not give the user the right to assign terms to the post. To allow that, add

'capabilities' => array('assign_terms' => 'edit_sermons')

to the arguments array of register_taxonomy(). Replace the word “sermons” with whatever you want to name this capability. Naming it edit_sermons means that the user who can edit sermon custom post types will also be able to assign terms belonging to this taxonomy.

Custom Post Types

Posts and pages have built in capabilities for editing, adding, publishing etc etc. To restrict custom post types, it’s necessary to assign them a new bunch of capabilities. Do this by adding the following code to the array of arguments.

'capability_type' => 'sermon',
'map_meta_cap' => true

The map_meta_cap key is necessary so that edit_sermons, delete_sermons and add_sermons will work as expected.

This is explained in Justin Tadlock’s article, but he had to do it manually then.

Assign Capabilities

Use the Members plugin to create roles and assign capabilities. When assigning capabilities, give edit_{capability_type}, publish_{capability_type}, but add an ‘s’ after that (plural form).