Running a Development Copy of WordPress Multisite – Update

WordPress Multisite Updates

Back in January I wrote a guide which provided details on how to setup a WordPress Multisite environment for Production and Development that both shared the same database. Since then there have been several updates with the WordPress core and I’ve also come up with several fixes for the sunrise.php file, so I thought I would write a follow up guide. At the time of writing the original guide the only method to make the Development environment fully operational was to modify two core files, which goes against common practice and was especially problematic when it came to updating WordPress. So I would like to focus on the WordPress core updates, incorporated into WordPress 3.9, that allow for the use of a newly introduced filter instead of modifying the core files.

A Better Way of Doing Things

Prior to the WordPress 3.9 release, two core files, ms-settings.php and ms-blogs.php, both needed to be manually edited in order for the Development environment to function properly. Specifically, this fixed the issue where WordPress would check to see if the current request URI matched the site URL saved in the database and since the Development environment differed from the Production environment this test always failed and redirected to the Production environment. Thanks to the introduction of several new methods and most notably the new “pre_get_site_by_path” filter we can now prevent this redirect from happening by making WordPress think it’s being viewed from the correct URI.

To do this we need to add some additional code to our customized sunrise.php drop-in plugin to account for the “pre_get_site_by_path” filter.

/*
* Replacement for /wp-includes/ms-load.php get_site_by_path
**/
function dev_get_site_by_path($_site, $_domain, $_path, $_segments, $_paths) {
	global $wpdb, $path;
	
	// So that there is a possible match in the database, set $_domain to be WP_PRODUCTION_DOMAIN
	$_domain = WP_PRODUCTION_DOMAIN;
	
	// Search for a site matching the domain and first path segment
	$site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s and path = %s", $_domain, $_paths[0] ) );
	$current_path = $_paths[0];
	
	if ($site === null) {
		// Specifically for the main blog - if a site is not found then load the main blog
		$site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s and path = %s", $_domain, '/' ) );
		$current_path = '/';
	}
	
	// Set path to match the first segment
	$path = $current_path;
	
	return $site;
}
add_filter('pre_get_site_by_path', 'dev_get_site_by_path', 1, 5);

The comments within the code explain what exactly is being done, and for further understanding look at the ms-loads.php WordPress core file. The only item not covered in the comments is the WP_PRODUCTION_DOMAIN defined variable, which should be defined in your wp-config.php file. Please read the original guide for full details.

Thankfully, that’s all there is to it! At this point we can undo the changes to the WordPress core files and don’t need to worry about making changes to core files with each new WordPress update.

Also, note that I’ve added some other filters to the sunrise.php file to allow all content (database and wp-content/uploads) to reside in a single location and thus be shared by the Production and Development environments without needing to clone any data. I’ve updated the original guide to reflect those additional filters.

As always I hope this has been helpful, and please leave comments if you have questions or comments.

 

WordPress Editor Custom Buttons and Dialog Windows

When building a custom website on WordPress one goal is to ensure that it’s as easy as possible for the client to be able to create, update, and maintain their own content. Depending on the complexity of the site, or custom features that are added, there are times that the built-in editor doesn’t provide enough functionality. So, being able to add your own buttons or menus is critical. Thankfully, with the release of WordPress 3.9 which incorporated TinyMCE 4.0, customizing the WordPress editor is easier than ever. In this guide I’ll explain how to create custom buttons and dialog windows, packaged as a simple TinyMCE 4.x plugin, in order to customize the WordPress editor.

Getting Started

The first thing we need to do is add the necessary actions and filters to our WordPress plugin or theme to let it know that we’re wanting to customize the editor. The action we’ll be hooking into is “admin_head” and the filters are “mce_external_plugins”, and “mce_buttons”. Let’s go ahead and gets some code added.

function lb_add_tinymce() {
	global $typenow;
	if (empty($typenow)) return;
	    
	add_filter('mce_external_plugins', 'lb_mce_external_plugins_filter');
	add_filter('mce_buttons', 'lb_mce_buttons_filter');
}
add_action('admin_head', 'lb_add_tinymce');

The “admin_head” action hook is checking to see if the current admin screen being viewed represents a post type, whether it be “post”, “page”, or any other custom post type. Next it’s adding the necessary filter hooks needed in order to actually add the TinyMCE plugin and TinyMCE button. Next we’ll need to created the callback functions for each of the filters.

function lb_mce_external_plugins_filter($plugin_array) {
	$plugin_array['my_mce_plugin'] = get_template_directory_uri() . '/js/my-mce-plugin.js';
	    
	return $plugin_array;
}

This callback function is pushing our TinyMCE plugin into the $plugin_array variable so that WordPress is aware of its existence. In this case I’ve named the plugin “my_mce_plugin”, but you’ll want to change this to match the naming of your own plugin. I’m also assuming your plugin is being added to your WordPress theme within a “js” folder, so if this is not the case you’ll need to make necessary edits.

function lb_mce_buttons_filter($buttons) {
	array_push($buttons, 'my_mce_plugin');
	    
	return $buttons;
}

This callback function is pushing the “my_mce_plugin” into the $buttons array so that WordPress knows to add our plugin to the list of buttons in the editor. Note that this specific filter hook will add the button to the top, primary toolbar, row. If you’d like to add your button to the second, advanced toolbar, row you will need to use the “mce_buttons_2” filter instead. There are filters for the third and fourth toolbar rows, however WordPress disables these toolbars by default so in many cases they will not be used.

Creating the TinyMCE Plugin

Now that all of the necessary WordPress action and filter hooks are in place the only thing left to do is actually create the TinyMCE plugin. First, we’ll add the framework for the plugin. Note that this will go into a new JavaScript file.

(function() {
	tinymce.create('tinymce.plugins.my_mce_plugin', {
        init: function(editor, url) {
            editor.addButton('my_mce_plugin', {
            	type: 'menubutton',
            	text: 'My MCE Plugin',
                icon: false,
                menu: [
                	{
	                	text: 'Shortcode 1',
	                	onclick: function() { tinyMCE.activeEditor.execCommand('shortcode1'); }
                	},
                	{
	                	text: 'Shortcode 2',
	                	onclick: function() { tinyMCE.activeEditor.execCommand('shortcode2'); }
                	},
                	{
	                	text: 'Shortcode 3',
	                	onclick: function() { tinyMCE.activeEditor.execCommand('shortcode3'); }
                	}
                ]
            });

            data = {};

            editor.addCommand('shortcode1', function() {
                tinymce.execCommand('mceInsertContent', false, '[shortcode1]');
            });

            editor.addCommand('shortcode2', function() {
                tinymce.execCommand('mceInsertContent', false, '[shortcode2]');
            });

            editor.addCommand('shortcode3', function() {
                tinymce.execCommand('mceInsertContent', false, '[shortcode3]');
            });
        },
        createControl: function(n, cm) {
            return null;
        },
        getInfo: function() {
            return {
                longname : 'My MCE Plugin',
                author : 'laubsterboy',
                authorurl : 'http://laubsterboy.com',
                infourl : 'http://laubsterboy.com',
                version : "1.0"
            };
        }
    });

    tinymce.PluginManager.add('my_mce_plugin', tinymce.plugins.my_mce_plugin);
})();

There are a couple of things to point out in the above code. First, what we’re doing is creating a plugin using the tinymce API and then registering it with the tinymce plugin manager. Next, within the plugin creation code, you can see that we’re adding a button to the editor. This button happens to be a menu (menubutton) with three menu items. Each menu item has an onclick event listener that then executes a tinymce command. The commands are registered just below the menu code. Right now the commands are simply inserting text into the WordPress editor as shortcodes. However, wouldn’t it be nice to allow your client to be able to visually select options for the shortcode and then have the corresponding shortcode text insert into the WordPress editor? This is where the TinyMCE Dialog box (windowManager) comes into play.

To illustrate this we’ll customize the “shortcode3” command to open a dialog box, allow for user input, and then output the shortcode text. To do this we will completely rewrite the shortcode3 command. For now, we’ll create two inputs within the dialog box. One will allow the client to enter a title shortcode attribute and the other will allow them to enter a limit shortcode attribute. Note that these attributes will not do anything, since the shortcode does not exist, but are used to illustrate how you could accomplish this and adapt it to your own project.

editor.addCommand('shortcode3', function() {
    var data = {
        title: 'Default Title',
        limit: 10
    }
    editor.windowManager.open({
        title: 'Insert Shortcode 3',
        data: data,
        body: [
            {
                name: 'title',
                type: 'textbox',
                label: 'Title',
                value: data.title
                onchange: function() { data.title = this.value(); }
            },
            {
                name: 'limit',
                type: 'listbox',
                label: 'Limit',
                values: [
                    {
                        value: 10,
                        text: '10'
                    },
                    {
                        value: 9,
                        text: '9'
                    },
                    {
                        value: 8,
                        text: '8'
                    },
                    {
                        value: 7,
                        text: '7'
                    },
                    {
                        value: 6,
                        text: '6'
                    },
                    {
                        value: 5,
                        text: '5'
                    },
                ],
                onchange: function() { data.title = this.value(); }
            }
        ],
        onSubmit: function(e) {
            var shortcode = '[shortcode3';
            data = tinymce.extend(data, e.data);

            shortcode += ' title="' + data.title + '"';
            shortcode += ' limit="' + data.limit + '"';

            shortcode += ']';

            tinymce.execCommand('mceInsertContent', false, shortcode);
        }
    });
});

Going through the above code, line by line, the first thing we did was create a data object to store the default values that we’d like to use for title and limit. Next we tell the window manager to open a new window with the settings we’ve specified. Within the new window we specify a title, data, body, and onSubmit object properties. The body takes in an array of tinymce input objects. I’ve given an example of two different types, textbox and listbox, but more information about these can be found in the TinyMCE 4.x API documentation and by looking through the TinyMCE source. The onSubmit property takes a function and is called when the OK button is clicked on the dialog box. Lastly, we’re using the same command to actually insert the shortcode text, mceInsertContent.

There are other things that you can do with TinyMCE such as getting the current selected text and modifying it (similar to how you add a link in the WordPress editor) or use AJAX to pull information from WordPress to allow for dynamic user input, but those are things to be covered in a follow-up guide. At this point, you should have a fully functioning custom button in the WordPress editor that can easily be adapted to what ever project you may be working on.

As always, I hope this has been an insightful guide, but let me know if you have any questions or comments.

Update – March 30, 2016

The TinyMCE api documentation has been updated and doesn’t include as detailed of information as it previously did, so it may be useful to look through the TinyMCE source. Specifically, the UI Classes will be helpful if you’re wanting to use different TinyMCE widgets than what was used in this article.

Events Manager Custom Event Attributes and Conditionals

The Events Manager plugin for WordPress can be used to build a fantastic event and calendaring system, with countless features and expandability, but there are times that the built in event attributes just aren’t enough. Thankfully the plugin is very developer friendly and allows users to create custom event attributes directly in the dashboard. However, what if you need to display a custom event attribute if it’s used, and not display it if it’s not used? That’s where event attribute conditional placeholders come in, however if you’re wanting to use a conditional placeholder with custom event attributes you’ll have to create it yourself. That’s what we’ll cover in this article.

Creating Custom Event Attributes

First in order to use a custom conditional placeholder you’ll need to have custom event attributes, so let’s create those first. To do this simply login to your WordPress dashboard and go to Events > Settings > General (tab) > General Options and scroll down until you find the radio input for “Enable event attributes?” and be sure that is set to “Yes”. Next you will need to scroll down to the “Event Attributes” text area and add your own custom event attributes. Note that in order for the custom conditional placeholder to function properly the custom event attribute should have no spaces, such as “#_ATT{this_is_my_custom_event_attribute}”. Also note that the custom event attribute is strictly case sensitive so be sure the exact same name is used throughout. For the sake of this article I will be creating a custom event attribute called test, so it will be added using the code below.

#_ATT{test}

Once you’ve finished adding your custom event attributes be sure to save changes.

Creating Custom Conditional Placeholders

Next, we’ll add the necessary code to process our custom conditional placeholders. To do this we’ll implement the use of a filter hook that is called by the Events Manager plugin. Note that the code for the conditional placeholders will be added to your theme’s functions.php file. Thankfully the code for creating custom conditional placeholder is fairly simple, so I’ll show the code that I’ve used for the “test” custom event attribute and then explain how it works, and what you need to change.

function em_event_output_condition_filter($replacement, $condition, $match, $EM_Event){
    // Checks for has_test conditional
    if(is_object($EM_Event) && preg_match('/^has_(test)$/', $condition, $matches)){
        if(array_key_exists($matches[1], $EM_Event->event_attributes) && !empty($EM_Event->event_attributes[$matches[1]]) ){
            $replacement = preg_replace("/\{\/?$condition\}/", '', $match);
        }else{
            $replacement = '';
        }
    }
    
    return $replacement;
}
add_filter('em_event_output_condition', 'em_event_output_condition_filter', 1, 4);

It’s as simple as that! Once the above code has been added to the functions.php file the “#_ATT{test}” custom event attribute and “{has_test}” custom conditional placeholder are ready to be used.

Explaining the above code, we’re making use of the “em_event_output_condition” filter that is called by the Events Manager plugin each time it encounters a conditional placeholder that doesn’t match one of the built-in conditional placeholders.

Here is a description of the passed attributes:

  • $replacement (string) will almost always be empty
  • $condition (string) will be the custom conditional placeholder name (such as, “has_test”)
  • $match (string) will contain the text from the beginning of the custom conditional placeholder to the end (such as, “{has_test} #_ATT{test} {/has_test}”)
  • $EM_Event (event object) will be the event object of the current event being viewed

Essentially what the above code does is check for the existence of our custom conditional placeholder, then checks to see if the custom event attribute exists and if it has a non-empty value. If those conditions are met, then the content within the custom conditional placeholder, inside $match, is returned. If those conditions are not met then an empty string is returned.

Due to the simplicity of this filter, in order to modify the above code to work with your custom conditional placeholders simply change the word “test” on lines 2 and 3 to be the name of your custom event attribute, such as “my_custom_event_attribute”.

There you have it! You’ve now created a custom event attribute and a corresponding custom conditional placeholder. You may be wondering, “what if I need to create multiple custom conditional placeholders?”, or “where do I use the custom conditional placeholder?”, and I’ll cover both of those questions in the final section.

Wrapping Everything Up

In the case that you’re needing to create multiple custom conditional placeholders you simply duplicate a portion of the filter code above.

    // Checks for has_test conditional
    if(is_object($EM_Event) && preg_match('/^has_(test)$/', $condition, $matches)){
        if(array_key_exists($matches[1], $EM_Event->event_attributes) && !empty($EM_Event->event_attributes[$matches[1]]) ){
            $replacement = preg_replace("/\{\/?$condition\}/", '', $match);
        }else{
            $replacement = '';
        }
    }

This is the code that actually does the processing, and duplicating it (and placing it above or below the existing code) and then replacing the custom event attribute with your additional custom event attribute is all you need to do. The reason that this works is because the Events Manager will call this filter *each* time it encounters a conditional placeholder that doesn’t match the built-in conditional placeholders. Note that your duplicated code MUST come before the line noted below.

return $replacement;

Lastly, in order for the custom event attribute and custom conditional placeholder to have any effect we need to add it to the front-end. The most common, and simple, way to do this is to login to your WordPress dashboard and then go to Events > Settings > Formatting (tab) > Events and scroll down to the  “Default single event format” text area. This is where you can add your custom event attribute and custom conditional placeholder code where ever you’d like it to display on the single event page.

{has_test} This is an example of how to use the custom event attribute and display its value: #_ATT{test}, using a custom conditional placeholder.{/has_test}

What About no_attribute Conditionals

Update – September 26, 2014: As a follow up to  Koen’s question in the comments I’ve also added a snippet below that will allow you to use a conditional placeholder in the case that a custom attribute is not used.

    // Checks for no_test conditional
    if (is_object($EM_Event) && preg_match('/^no_(test)$/', $condition, $matches)) {
        if ( !array_key_exists($matches[1], $EM_Event->event_attributes) || empty($EM_Event->event_attributes[$matches[1]]) ) {
            $replacement = preg_replace("/\{\/?$condition\}/", '', $match);
        } else {
            $replacement = '';
        }
    }

This can simply be used in addition to your other conditions (has_attribute or no_attribute) within the “em_event_output_condition_filter” filter function.

Nesting Conditionals

Update – October 23, 2014: If you plan to nest conditionals within other conditionals (whether they’re custom or built-in) you will first need to enable this functionality. By default the Events Manager plugin will only look for the first level of conditionals, and unfortunately there is no option for enabling conditional recurrence within the Events Manager Settings within the dashboard. Instead you will need to add some additional code to your functions.php theme file. Once the below code has been added you will need to go to the Events Manager Settings and click save (no changes necessary) to fire the “em_options_save” action. I used this action since this is technically updating an Events Manager option and it prevents the update_option method from being called each page load.

function lb_em_options_save() {
	update_option('dbem_conditional_recursions', 2);
}
add_action('em_options_save', 'lb_em_options_save');

 

The Events Manager plugin checks for the dbem_conditional_recursions option upon instantiation. If this option is set it will use its value, and if not then it will use the default value of one. So, the above code sets the option value to two thus overriding the default.  This will allow for one conditional to be nested within another, however you will need to adjust the above code if you plan nest conditionals at an even deeper level. Keep in mind high levels of recursion can negatively impact performance.

In Conclusion

Overall, the process of adding a custom event attribute and custom conditional placeholder is relatively simple and provides nearly infinite customizability to the Events Manager plugin. I hope this has been an easy to follow and informative guide on how to create custom event attributes. Be sure to post comments and let me know if you’ve ran into any problems or have any questions.

Fancybox Position Problem

When it comes to web design and development it’s always nice to be able to use well established libraries and frameworks to speed up development and deployment. JQuery is probably the best example of how a framework can be used to speed up development, and through its success many plugins have been built on top of it to provide custom functionality and features. One such plugin is fancyBox, by fancyApps, which allows for extremely simple implementation of lightbox modals for single images, image galleries, videos, or even entire web pages. Being able to add sleek functionality like this to a website with just a few lines of code is quite impressive… until it all falls apart.

The Problem

Using someone else’s library or framework is always subject to adhering to the confines and constraints needed for their code to work properly, but when things don’t work properly it can sometimes be difficult to determine what is causing the problem. I recently ran into a couple problems with the fancyBox plugin (while using it for an image gallery) that, though it didn’t break the functionality, completely broke the user experience. The first problem was that each time an image was viewed in the fancyBox modal and then closed the page would immediately be scrolled to the very top. The second problem was that the open and closing effect for opening the fancyBox modal (set to elastic) was zooming in and out of the center of the window rather than the location of the thumbnail that was clicked. As mentioned before, neither of these problems caused the plugin to fail entirely, but visitors would certainly be thrown off by the odd behavior.

The Solution

Initially I searched Google to see if I was alone or if others were experiencing the same problem. Though not much was found, it did appear that others were having similar problems, however none of them provided concrete ways to resolve the problems. One such solution to the “scrolling to the top” problem was to add a helper object to the settings for fancyBox initialization, which disabled locking scrolling. The resulting code is listed below.

jQuery(document).ready(function() {
	jQuery(".fancybox-zoom-no-border").fancybox({
		padding: 0,

		openEffect : 'elastic',
		openSpeed  : 150,

		closeEffect : 'elastic',
		closeSpeed  : 150,

		closeClick : true,
		
		prevEffect : 'elastic',
		nextEffect : 'elastic',

		closeBtn  : true,
		arrows    : true,
		nextClick : true,

		helpers : {
			thumbs : {
				width  : 150,
				height : 150
			},
			overlay: {
				locked: false
			}
		}
	});
});

By adding the overlay, locked: false, helper it prevented fancyBox from adding the overlay (and setting the body overflow value to hidden), and thus fixed the problem. However, this shouldn’t have ever been a problem in the first place, and it also allowed users to scroll while viewing a modal, so I decided this was unacceptable and set the locked value back to true.

Some quick debugging with the Chrome inspector revealed that both problems were being caused by CSS set in my sites main and reset files. Knowing that the problems were caused by CSS I began methodically removing blocks of CSS (in the Chrome inspector) until the problem went away. This allowed me to narrow down the offending code that was causing the problem and eventually fix the problem, properly.

Eventually what I found was that the first problem, where closing a modal scrolled to the top of the page, was caused by my CSS reset file. The reset had a line that was setting “html” and “body” to a height of 100%, and since this was unnecessary for my project I simply removed the line and the scrolling problem went away. I didn’t inspect further the exact reason this was a problem, but I believe it was related to the overlay being hidden/removed and the browser temporarily thinking the page was only as tall as the window height (since overflow hidden was set on the html), and thus it scrolled to the top.

With the first problem resolved I moved on to the next, repeating the same debugging steps as the first time, only this time the offending line was in my main stylesheet. I eventually found that the fancyBox thumbnails were nested inside divs that were displaying block, anchors that were displaying inline, and then images (the thumbnails) that were displaying block. The problem was the anchors displaying as inline and the thumbnail children displaying block. This was causing the anchor elements to receive a default width and height of zero and was apparently causing problems with jQuery getting their position. Setting the anchors to display as block fixed the problem.

Overall, these weren’t big problems and weren’t very difficult to debug, but seeing the limited posts about similar problems in Google I thought I would share my experience in hopes that I could save a little time for others who are running into the same problems.

WordPress Custom Post Type Pagination

Looking around the internet it’s easy to find several examples of how you can create WordPress custom post type pagination links using multiple different methods. The problem is many of them require overwriting or modifying the $wp_query global variable, which can break functionality with plugins, themes, or theme widgets. It’s almost always better practice to create an entirely new WP_Query instance and store it in your own custom variable.

Another added complication is that many methods also assume that the custom post type has the archive enabled, but what if it’s not? There are situations where you may want to create an entirely custom post archive using a page template and assigning that page template to a page. This allows the archive to be viewed using any path you desire (by setting it in the post editor) and more importantly it allows the archive page to be added to a WordPress menu using the normal page selector. However, page numbers are appended as sub nodes in the url, such as /news/page/2. Depending on your site map WordPress may not properly resolve the url and report that the page cannot be found. In most cases this is due to the custom archive page conflicting with the custom post type rewrite slug value. An example of this problem would be the custom archive page url being http://laubsterboy.com/news and the custom post type rewrite slug being ‘news’. In this example when the archive page 2 is viewed WordPress assumes you’re trying to view a news custom post type with a url / title of /news/page/2. Obviously this is not what you’re wanting. To fix this either the archive page url needs to be changed (possibly to something like /all-news), or the custom post type rewrite slug needs to be changed (possibly to something like ‘news-item’).

With the problem outlined lets look at some code.

Here is an example of creating a custom WP_Query object to display the custom post type archive.

<?php
  $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; 
  $news = new WP_Query(array('post_type' => 'news', 'posts_per_page' => get_option('posts_per_page'), 'paged' => $paged));  

  while ($news->have_posts()) : $news->the_post(); 
?>

  <!-- Post Template Here-->

<?php endwhile;

twentyfourteen_paging_nav($news->max_num_pages);

The above code assumes that you’re using, or modifying, the WordPress Twenty Fourteen Theme. Next, we’ll need to modify the inc/template-tags.php file to allow the twentyfourteen_paging_nav function to accept the max_num_pages parameter.

if ( ! function_exists( 'cla_base_paging_nav' ) ) :
/**
* Display navigation to next/previous set of posts when applicable.
*/
function twentyfourteen_paging_nav($max_num_pages = 0) {
	// Don't print empty markup if there's only one page.
	
	if ( $GLOBALS['wp_query']->max_num_pages < 2 && $max_num_pages < 2 ) {
		return;
	}

	$paged        = get_query_var( 'paged' ) ? intval( get_query_var( 'paged' ) ) : 1;
	$pagenum_link = html_entity_decode( get_pagenum_link() );
	$query_args   = array();
	$url_parts    = explode( '?', $pagenum_link );

	if ( isset( $url_parts[1] ) ) {
		wp_parse_str( $url_parts[1], $query_args );
	}

	$pagenum_link = remove_query_arg( array_keys( $query_args ), $pagenum_link );
	$pagenum_link = trailingslashit( $pagenum_link ) . '%_%';

	$format  = $GLOBALS['wp_rewrite']->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : '';
	$format .= $GLOBALS['wp_rewrite']->using_permalinks() ? user_trailingslashit( 'page/%#%', 'paged' ) : '?paged=%#%';

	// Set up paginated links.
	$links = paginate_links( array(
		'base'     => $pagenum_link,
		'format'   => $format,
		'total'    => $max_num_pages !== 0 ? $max_num_pages : $GLOBALS['wp_query']->max_num_pages,
		'current'  => $paged,
		'mid_size' => 1,
		'add_args' => array_map( 'urlencode', $query_args ),
		'prev_text' => '&larr; Previous',
		'next_text' => 'Next &rarr;',
	) );

	if ( $links ) :

	?>
	<nav class="navigation paging-navigation" role="navigation">
		<h1 class="screen-reader-text">Posts navigation</h1>
		<div class="pagination loop-pagination">
			<?php echo $links; ?>
		</div><!-- .pagination -->
	</nav><!-- .navigation -->
	<?php
	endif;
}
endif;

The above code simply looks for the max_num_pages parameter and if it’s set then it will override the wp_query max_num_pages value.

At this point you should have a functioning custom post type archive page, with the custom post type has_archive set to false, that uses the WordPress reading settings to set how many posts to show per page.

I hope this has been helpful and let me know if you have any questions or suggestions for improvement.

Hangry Shark – An HTML5 Canvas Experiment

A couple months ago there seemed to be a handful of blogs posting about new HTML5 canvas games that they were creating, most spawning off of Flappy Birds’ popularity. The premise being that they wanted to challenge themselves to recreate a simple game using the modern web client stack (HTML5, CSS3, and JavaScript) instead of a specific mobile platform, such as iOS or Android. Having developed arcade style Flash game in the past, and being a web developer, I thought it was finally time that I put together my first HTML5 canvas game. As with many of my personal projects I refused to use any SDKs or libraries, but instead develop the entire project from the ground up.

After many hours of work, spread across a couple weeks, and completely rewriting the codebase a couple times I ended up with a simple game called Hangry Shark. The gameplay focuses on a shark looking for a meal that, with nothing else around except clownfish, must avoid touching the jelly fish. Being purely a personal project I decided to only develop and test the game on webkit powered web browsers. Initially the game was developed using DOM elements for all game sprites and user interface elements, which has its benefits, but due to significant performance limitations the codebase was rewritten to allow all game sprites to be drawn via HTML5 canvas. Doing so allowed the game to run smoothly on desktop devices at high resolutions and mobile devices, including mobile devices several years old such as the iPhone 4s. Using techniques such as only one update loop for all game logic and drawing, as well as requestAnimationFrame for the timing of the loop which also ensures the graphics adapter is used for drawing rather than the CPU, helped to increase performance.

Ultimately it was  a fun project, I learned a lot, and I hope to use it as a starting point for future projects. You can check out the game below, or click on the link to open it in a new window. Keep in mind, the game is technically a web-app that supports click and touch events, and uses iOS web-app constructs so it can be added to the home screen to behave as if it were a native app. Give it a try and let me know what you think!

Hangry Shark link

WordPress Richtext Editor for Excerpts

Occasionally when creating a WordPress theme the design relies on using the excerpt meta box, but forcing the end user to manually type html tags defeats the entire purpose of using WordPress (or any CMS for that matter) and adds a layer of unnecessary complexity to editing posts, pages, or any other post type. To make things simpler you can replace the Excerpt meta box with a rich-text editor, just like the main content editor. This can be done by installing an additional plugin, however with just a couple extra lines of code it can also be done through your theme.

function lb_editor_remove_meta_box() {
	global $post_type;

	// Check to see if the global $post_type variable exists
	// and then check to see if the current post_type supports
	// excerpts. If so, remove the default excerpt meta box
	// provided by the WordPress core. If you would like to only
	// change the excerpt meta box for certain post types replace
	// $post_type with the post_type identifier.
	if (isset($post_type) && post_type_supports($post_type, 'excerpt')) remove_meta_box('postexcerpt', $post_type, 'normal');
}
add_action('admin_menu', 'lb_editor_remove_meta_box');

function lb_editor_add_custom_meta_box() {
	global $post_type;

	// Again, check to see if the global $post_type variable
	// exists and then if the current post_type supports excerpts.
	// If so, add the new custom excerpt meta box. If you would
	// like to only change the excerpt meta box for certain post
	// types replace $post_type with the post_type identifier.
	if (isset($post_type) && post_type_supports($post_type, 'excerpt')) add_meta_box('postexcerpt', __('Excerpt'), 'lb_editor_custom_post_excerpt_meta_box', $post_type, 'normal', 'high');
}
add_action( 'add_meta_boxes', 'lb_editor_add_custom_meta_box' );

function lb_editor_custom_post_excerpt_meta_box( $post ) {
	// Adjust the settings for the new wp_editor. For all
	// available settings view the wp_editor reference
	// http://codex.wordpress.org/Function_Reference/wp_editor
	$settings = array( 'textarea_rows' => '12', 'quicktags' => false, 'tinymce' => true);

	// Create the new meta box editor and decode the current
	// post_excerpt value so the TinyMCE editor can display
	// the content as it is styled.
	wp_editor(html_entity_decode(stripcslashes($post->post_excerpt)), 'excerpt', $settings);

	// The meta box description - adjust as necessary
	echo '<p><em>Excerpts are optional, hand-crafted, summaries of your content.</em></p>';
}

Thats it! The above code can be placed in the functions.php file of your theme or placed in its own plugin file. Note that typically with custom meta boxes additional code must be added to save the meta box value when the post/page is published or updated, however since the meta box is replacing the excerpt WordPress automatically handles this process.

Running a Development Copy of WordPress Multisite – Part 2

I recently made a detailed post explaining how I created a production and development environment that share a database using WordPress in a Multisite configuration.

One thing that I felt was missing from the production environment was the ability to quickly jump from production to development while making changes to site content. With the new WordPress adminbar it’s already easy to jump back and forth between the post/page and the dashboard, but I wanted a way to view the current site or current page in the development environment. So, I created a plugin that adds a menu to the WordPress adminbar that lists links to the development environment for the “Current Site”, “Current Page” (if a page is being viewed), and links to the homepage for each site within the WordPress Multisite network.

Since I will not likely ever enter this into the WordPress plugin repository I will simply post the code here. All you need to do is copy the following code into a text editor and save it as a php file and upload it into your production site’s plugin directory. Note that this will only work if you have created a production and development environment in accordance with my guide.

<?php
/*
Plugin Name: Development Menu
Plugin URI: http://www.laubsterboy.com
Description: Adds WordPress Admin Bar menu items to link back to the development version of a site
Version: 0.0.1
Author: John Russell
Author URI: http://www.laubsterboy.com
License: GPLv2 or later
*/

/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

class DevelopmentMenu {

	function __construct() {
		if (defined('WP_DEVELOPMENT_DOMAIN')) add_action('admin_bar_menu', array($this, "development_links"));
	}

	function add_root_menu($name, $id, $href = FALSE) {
		global $wp_admin_bar;
		if ( !is_super_admin() || !is_admin_bar_showing() )
		    return;

		$wp_admin_bar->add_menu( array(
		    'id'   => $id,
		    'meta' => array(),
		    'title' => $name,
		    'href' => $href ) );
	}

	function add_sub_menu($name, $link, $root_menu, $id, $meta = FALSE) {
		global $wp_admin_bar;
		if (!is_super_admin() || !is_admin_bar_showing()) return;

		$wp_admin_bar->add_menu( array(
		  'parent' => $root_menu,
		  'id' => $id,
		  'title' => $name,
		  'href' => $link,
		  'meta' => $meta
		) );
	}

	function get_development_url($url) {
		// Takes in a URL with the production host and returns the same URL with the development host
		$return_value;
		$url_components = parse_url($url);

		$return_value = $url_components['scheme'] . '://' . WP_DEVELOPMENT_DOMAIN . $url_components['path'];

		return $return_value;	
	}

	function development_links() {
		$blog_details = get_blog_details();
		if (is_admin()) $screen = get_current_screen();

		$this->add_root_menu("Development Sites", "devrt");
		$this->add_sub_menu("Current Site", WP_DEVELOPMENT_DOMAIN . $blog_details->path, "devrt", "devcurrsite" );
		if (isset($screen) && $screen->base == 'post') {
			$this->add_sub_menu("Current Page", $this->get_development_url(get_permalink()), "devrt", "devcurrpage" );
		} elseif (!is_admin()) {
			if (is_main_site() || $_SERVER['HTTP_HOST'] == WP_PRODUCTION_DOMAIN) {
				// Current page link to development for main site or sites with no CNAME (using IP address)
				$this->add_sub_menu("Current Page", $this->get_development_url(stripos($_SERVER['SERVER_PROTOCOL'],'https') === true ? 'https://' : 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), "devrt", "devcurrpage" );
			} else {
				$this->add_sub_menu("Current Page", $this->get_development_url(stripos($_SERVER['SERVER_PROTOCOL'],'https') === true ? 'https://' : 'http://' . $_SERVER['HTTP_HOST'] . rtrim($blog_details->path, '/') . $_SERVER['REQUEST_URI']), "devrt", "devcurrpage" );
			}
		}
		// For Multisite
		if (is_multisite()) {
		    global $wpdb, $path;
		    $blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
		    foreach($blog_ids as $blog_id) {
		        $blog_details = get_blog_details($blog_id);
		        $this->add_sub_menu($blog_details->blogname, WP_DEVELOPMENT_DOMAIN . $blog_details->path, 'devrt', 'devblog'.$blog_id);
		    }
		}
	}
}

add_action("init", "DevelopmentMenuInit");
function DevelopmentMenuInit() {
    global $DevelopmentMenu;
    $DevelopmentMenu = new DevelopmentMenu();
}

 

Running a Development Copy of WordPress Multisite

The Problems with Multisite

I had recently been tasked with setting up production and development environments for WordPress Multisite with the intention of following traditional development methods. Having a dedicated development environment allows for testing themes, plugins, and updates without affecting the production environment. While researching best practices for creating production and development environments for WordPress I found frustratingly little examples or documentation covering WordPress in a Multisite configuration. Much of what I found was also dated to the point of being obsolete.

I came across a page in the WordPress Codex titled Running a Development Copy of WordPress (reading this article before proceeding will clarify what follows) which seemed to document the exact configuration that was needed. It made no mention of Multisite, however its merits were worth following. Proceeding with testing the concepts covered in the article I found that the “dev” site did indeed work, however only the main site would load properly, thus being an unusable solution for Multisite.

To clarify, the Multisite test environment that I was using was a sub-directory install using an IP address as the domain. So, at the time I thought moving the “dev” WordPress install from a sub-directory on the web server (being the production server, called WP-1 from now on) over to the web root of a clone of WP-1 (being the development server, called WP-2 from now on) would fix the problem, assuming the word “dev” in the url was causing the problem. Ultimately I planned to be using the WordPress MU Domain Mapping plugin, which requires WordPress to be in the web root so this step would eventually be required anyway.

Once WP-2 was setup (pointing to the same database used by WP-1) I quickly realized that regardless of which site was being viewed it always redirected back to WP-01, which is obviously not what is wanted.

The solution

After many hours of troubleshooting and testing I was finally able to get WP-2 to directly mirror WP-1, whether WP-1 used an A record or IP address as the primary domain. In short, here is what you need to do to setup a production and development environment that share the same database using WordPress Multisite.

Before proceeding, note that none of what is mentioned below is intended to be applied to already existing production/development environments and if attempted please backup your WordPress files and database right now!

What You’ll Need

  1. WP-1: A web server with WordPress 3.8 installed in the web root as a sub-directory Multisite setup, using either an IP address or an A record.
  2. WP-2: A clone of WP-1.
  3. DB: A MySQL server with the WordPress database and separate users for WP-1 and WP-2. The WP-1 user account should have full access to the WordPress database while the WP-2 user account should only have SELECT privileges so that WP-2 cannot “break” the production environment.

Next, be sure to update the wp-config.php file (in the WordPress root) on WP-2 to be sure it’s using the WP-2 MySQL user account. Also, note that the wp-config.php file on WP-2 should be identical to that on WP-1 with the exception of the MySQL user and the items listed below.

While editing the wp-config.php on WP-2, I recommend adding the following lines of code to make troubleshooting plugins and themes easier.

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);

Within the wp-config.php file of both WP-1 and WP-2 add the following lines of code. These variables will allow the WP-1 or WP-2 domains to change and only need to change the domain names in one place. Note that the IP addresses are only examples and should be changed to match your own server environments, and the WP_PRODUCTION_DOMAIN should match DOMAIN_CURRENT_SITE.

define('SUNRISE', 'on');
define('WP_PRODUCTION_DOMAIN', '192.168.1.10');
define('WP_DEVELOPMENT_DOMAIN', '192.168.1.20');

Once that is done, install the WordPress MU Domain Mapping plugin on WP-1 and then copy the sunrise.php file (in the wp-content directory) and domain-mapping plugin files to WP-2.

Next, you’ll need to make some edits to the sunrise.php file on WP-2. This will remove the error stating that escape is deprecated, if WP_DEBUG is set to true.

// Change line 11 from
$dm_domain = $wpdb->escape( $_SERVER['HTTP_HOST'] );

// To
$dm_domain = $wpdb->_escape( $_SERVER['HTTP_HOST'] );

Also, add the following code to the sunrise.php file, just before the closing php tag.

// Filters the domain that is displayed/output into HTML
add_filter('pre_option_home', 'dev_pre_url_filter', 1 );
add_filter('pre_option_siteurl', 'dev_pre_url_filter', 1 );
add_filter('the_content', 'dev_content_filter', 100);
add_filter('content_url', 'dev_content_url_filter', 100, 2);
add_filter('post_thumbnail_html', 'dev_content_filter', 100);
add_filter('wp_get_attachment_link', 'dev_content_filter', 100);
add_filter('wp_get_attachment_url', 'dev_content_filter', 100);
add_filter('upload_dir', 'dev_upload_dir_filter', 10);

function dev_pre_url_filter() {
	global $wpdb, $path, $switched;
	$url;
	$switched_path;
	$blog_id = get_current_blog_id();

	if (!$switched) {
		$url = is_ssl() ? 'https://' : 'http://';
		$url .= WP_DEVELOPMENT_DOMAIN;
		if (!is_main_site()) $url .= rtrim($path, '/');
		return $url;
	} else {
		$switched_path = $wpdb->get_var( "SELECT path FROM {$wpdb->blogs} WHERE blog_id = {$blog_id} ORDER BY CHAR_LENGTH(path) DESC LIMIT 1" );
		$url = is_ssl() ? 'https://' : 'http://';
		$url .= WP_DEVELOPMENT_DOMAIN;
		$url .= rtrim($switched_path, '/');
		return $url;
	}
}
function dev_content_filter($post_content) {
	global $wpdb;
	
	$blog_details = get_blog_details();
	$original_url = $wpdb->get_var( "SELECT domain FROM {$wpdb->dmtable} WHERE blog_id = {$blog_details->blog_id} ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" );
	$dev_url = WP_DEVELOPMENT_DOMAIN . $blog_details->path;
	
	if($original_url !== null) {
		$post_content = str_replace($original_url . '/', $original_url, $post_content);
		$post_content = str_replace($original_url, $dev_url, $post_content);	
	}
	// Change all url's to point to staging (images, anchors, anything within the post content)
	$post_content = str_replace(WP_PRODUCTION_DOMAIN, WP_DEVELOPMENT_DOMAIN, $post_content);
	// Change urls for "uploads" to point to production so images are visible
	$post_content = str_replace(WP_DEVELOPMENT_DOMAIN . $blog_details->path . 'wp-content/uploads', WP_PRODUCTION_DOMAIN . $blog_details->path . 'wp-content/uploads', $post_content);
	return $post_content;
}
/*
* Filters the content_url function - specifically looking for content_url('upload') calls where path has uploads in the string
*
* Added so MU-Plugins could use content_url on DEV and PROD
*/
function dev_content_url_filter($url, $path) {
	if (!empty($path) && strpos($path, 'uploads') !== false) {
		return str_replace(WP_DEVELOPMENT_DOMAIN, WP_PRODUCTION_DOMAIN, $url);
	}
	
	return $url;
}
function dev_upload_dir_filter($param) {
	$param['url'] = str_replace(WP_DEVELOPMENT_DOMAIN, WP_PRODUCTION_DOMAIN, $param['url']);
	$param['baseurl'] = str_replace(WP_DEVELOPMENT_DOMAIN, WP_PRODUCTION_DOMAIN, $param['baseurl']);
	
	return $param;
}

Since urls are stored in the WordPress database using absolute paths, if a site is viewed on WP-2 all hyperlinks will point to WP-1, so navigation would require manually typing in the url of a page wanting to be viewed. The above code essentially intercepts the WordPress option for siteurl and home, and post/page content, and changes it to match the domain of WP-2 which changes dynamically generated hyperlinks, such as the page title and menus, to point to the WP-2 server. In addition to changing link urls there are several attachment and upload filters to do the same thing, thus allowing us to upload images to the Production environment in the WordPress dashboard and the Development environment will point to the same location so attachments don’t need to be cloned. This allows for natural navigation of WP-2 in a web browser where all hyperlinks (and requests for external CSS and JavaScript files) will point to WP-2.

Now that most of the heavy lifting is done there are just a few things left to fix, mostly because WordPress is still redirecting all requests from WP-2 over to WP-1.

WARNING: The following code requires making some changes to the WordPress Core and the WordPress MU Domain Mapping files on WP-2, which may cause problems with plugins or themes, but most importantly these changes would need to be made each time WordPress is updated.

Comment out the following lines of code in the domain_mapping.php file inside the domain mapping plugin directory. This will prevent the domain mapping plugin from redirecting requests from WP-2 to WP-1.

// Line 708
header( "Location: {$url}{$_SERVER[ 'REQUEST_URI' ]}", true, $redirect );
// Line 709
exit;

Comment out the following lines of code in the ms-settings.php file inside the wp-includes directory. This will prevent WordPress from redirecting requests from WP-2 to WP-1.

// Line 98
header( 'Location: http://' . $current_site->domain . $current_site->path );
// Line 99
exit;

Add the following lines of code to the ms-blogs.php file inside the wp-includes directory. The code will be placed within the scope of the get_blog_details function.

// After these lines of code on line 104 and 105
function get_blog_details( $fields = null, $get_all = true ) {
	global $wpdb;

// Add this one line of code
	if (isset($fields['domain'])) $fields['domain'] = WP_PRODUCTION_DOMAIN;

Instead of modifying the WordPress core files please read my follow up guide, which explains how to do this using a new filter brought in with WordPress 3.9.

Congratulations! At this point, provided nothing went wrong during the changes listed above, you should have a production environment using a database that is shared by the development environment, thus alleviating the need for migrating database tables as development stages progress. The idea is that new sites can be created in the production environment (with no CNAME pointing to it so it is not visible to the public) and then built in the production environment. Once the new site is ready for a soft launch the theme files can be copied to the production environment and a domain can be mapped to the site, thus making it visible to the public. The same concept can be applied to already existing sites that need updates to the theme, where changes can be made in the development environment (thus thankfully not visible to the public when you create a typo in the functions.php file and completely break the site) and once finished migrated to the production environment.

There are unfortunately a few drawbacks, such as not being able to make content changes in only the development environment (without duplicating it to a non public url), not being able to make changes to plugins that require scheme changes, and so on. However, themes can be completely developed and edited in the development environment and then migrated to the production environment by simply moving the theme files, completely avoiding database migrations and search/replace processes altogether. This type of setup isn’t for everyone, obviously, but for those who have large amount of users/editors/administrators who can make changes to posts, pages, calendars, and terms/categories, who are outside of the web development team, being able to avoid direct interaction with the database and being able to develop using “live” data is priceless!

Lastly, when it comes to testing plugin updates or WordPress updates my current thought is to clone WP-1 and the DB to another virtual server, and once vetted the updates will be installed on WP-1 and copied over to WP-2, so the drawbacks above are not much of a concern in my situation.

When it comes to WordPress there are certainly other ways of accomplishing the same thing and I’d be very excited to hear feedback from others, but for now this is a solution that will meet my needs and I hope will help others who are in the same situation I was.

Update – January 8, 2014

I’ve extended this post in a Part 2 where I shared a plugin that I created to add a “development sites” menu to the adminbar to allow easy switching between viewing the production environment and development environment.

Update – August 8, 2014

I’ve written a follow up guide, now that WordPress 3.9 has been released, explaining how to get the same functionality out of the Development environment without needing to modify WordPress core files.

Using JavaScript to Determine the Number of Days Between the Beginning of the Year and a Given Date

I recently came across the need to determine how many days fell between the beginning of the year and a given date. The most convenient way to do this is to add a new method to the Date object. Let’s get started!

Date.prototype.getDOY = function() {
    var januaryFirst = new Date(this.getFullYear(),0,1);
    return Math.ceil((this - januaryFirst) / 86400000);
}

Looking at the above code, line one allows us to add a new method to the Date object by adding a new function to its prototype. This will allow the getDOY method to be invoked on any Date object, such as myDateObject.getDOY(). The getDOY function name stands for “get day of year” and will return the day of year between 1 and 365. The way this works is by first creating a new Date object with the current year, January for the month, and 1 for the day. Next, the Date object (referenced by the keyword this) is equal to the amount of milliseconds since January 1, 1970 to present time, and subtracting the amount of milliseconds from January 1, 1970 to January 1 of the current year will leave you with the amount of milliseconds from the beginning of the year to the current date and time. The only thing left to be done is to convert the milliseconds to days, which can be done by dividing the number by 86,400,000 (1000 milliseconds  x  60 seconds  x  60 minutes  x  24 hours).

There you have it!