WordPress as a Service

At two recent WordPress conferences, WPCampus 2016 and WordCamp Boston 2016, I gave a talk along with Andrew Bauer on using WordPress as a Service (WPaaS) in a higher education setting.

In this talk, we covered the organizational and technical benefits of running a multi-network, multi-site WordPress as the primary CMS at Boston University. We highlighted specific strategies, including organizational policies, development workflows and team structure that allow us to serve over a million users per month. We’re here to help dispel the notion that a centralized approach to WordPress can’t successfully be implemented in higher ed.



WordPress failing to insert post into the database

I was recently trying to insert imported data into WordPress. When trying to insert it into WordPress using wp_insert_post, I was receiving the following error:

Could not insert post into the database

It turns out that I was trying to insert data in a non-utf8 encoding, whereas WordPress uses UTF8 internally. All I had to do was run the following PHP function to convert it over to UTF8:

$post_content = iconv('ISO-8859-1','UTF-8', $post_content)

mb_strrchr in php 5.1 for compatibility

I needed php’s mb_strrchr function, which is only in php 5.2+. However, my workplace is stuck in php 5.1 and cannot upgrade to php 5.2.

I decided to write a forwards-compatible mb_strrchr() that worked in php 5.1 at least. Please test thoroughly before using. It seemed to work great for the cases I needed it for:

// php 5.1 compat
if( !function_exists('mb_strrchr') ) {
	function mb_strrchr( $haystack, $needle, $part = false, $encoding = '' ) {
		$pos = mb_strrpos( $haystack, $needle, $encoding );
		if( $pos === false ) return false;
		if( $part === true ) {
			return mb_strcut( $haystack, 0, $pos, $encoding );
		} else {
			return mb_strcut( $haystack, $pos, mb_strlen($haystack), $encoding );

PHP misreporting existence of directory or files

Recently I was working with a WordPress site where I was deleting a network. When the network was deleted, the root site was also deleted (or vice versa). And when the root site deleted, it deleted its own media directory and the parent network’s media directory as well. This worked out OK in most cases (because the root site and root network are one-in-the-same in a multisite network). However in some of my workplace’s custom code, we hooked into both network and site deletion. We tried to delete some extra folders attached with the network and site. Some folders were shared, so once they were deleted, they should’ve shown up as missing. However the folders were showing up as existing on second try and when deleting were giving out errors. So…

PHP has something called stat cache to make file operations faster. It caches information anytime you make a call to functions like “stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), and fileperms().” Coincidentally, PHP was misreporting the existence of files and folders when they were clearly deleted and glob() would be confirming that fact. In the end, we had to add calls to clearstatcache() to avoid this trap.

I wish there was a way to disable the stat caching for a session, because calling this function every time gets to be a pain.

Things learned from Wordcamp 2012

I attended most of the developer track presentations at Boston’s Wordcamp 2012. I learned about some interesting tools and techniques that I’d like to document for myself and others.

Talk Optimizing for Speed by Ben Metcalfe

  1. YouGetSignal’s Reverse IP Lookup – Shows you how many other hosts live on the same IP Address. This can be helpful for anyone using shared hosting or maybe VPS.
  2. Debug Bar – Get debugging information from each page WordPress page load.
  3. Google XML Sitemaps – Delete this plugin if you have it. (I had it.)
  4. YSlow – Get load times and optimization tips of a page request.

WordPress as a Web Framework by Sam Hotchkiss

  1. MVC frameworks for WordPress
    1. WP MVC – Provides a singleton object and eliminates the metadata bottleneck by providing tables indexed by post IDs.
    2. Tina MVC
  2. _s Theme – Use Automattic’s Blank Theme as a good starting point.

Automating frontend workflow by Aaron Jorbin (blog post, slides)

  1. Autojump – Jump to frequently used directories
  2. Commander.js (nodejs) – Script working with CLI
  3. watch (nodejs) – Watch files/dirs
  4. mockjax – Fake your ajax calls (good for protyping or testing un-related, yet dependent functionality)
  5. Travis CI – continuous integration service (wordpress plugin tests)
  6. Glue – generate css sprites

Microdata for SEO by Dave Ross

  1. Add itemprop, itemscope, etc to any identifiable schema
  2. Google Rich Snippets Tool – Test your microdata
  3. Examples: SiteNavigation element for navigation, Blog element for blog posts, etc
  4. Some quick tidbits: Bing rates sites with microdata higher than sites without, and Google uses the microdata in search results

Enterprise WordPress by Jake Goldman (1up)

  1. Sites to show clients: showcase, WordPress VIP
  2. Maintaining a beautiful WordPress admin

Shortcodes by Jon Bishop

  1. Use oembed rather than plugins to support embedding media from sites like youtube, facebook, etc

Codex by Erick Hitler

  1. Use santize_* functions to save to db: santize_text_field(), sanitize_title()
  2. Use esc_* functions to show data to user (esc_url_raw() is the exception, it is the opposite of esc_url())

Javascript hooks by Luke Gedeon (1up)

  1. Javascript custom events are coming, they will provide functionality similar to WordPress’ action and filter hooks (list of hooks)

SMF2 Gallery2 Integration Problem

I have a SMF2 forum site, where the gallery is implemented through Gallery2 using Oldiesmann’s SMF + G2 Integration Project.

For 2 years, I’ve had the SMF and Gallery work properly together with linked member groups (a mod option), such that member groups auto-synchronized with ones in Gallery. At some point, either with a Gallery or SMF upgrade, users reported that the Gallery part of the site threw cryptic security warnings for non-admins. I explored the issue and figured users were missing in two required groups “Everybody” and “Registered Users”. I had reported it to Oldiesmann, but he claimed he had no control over those required groups and Gallery was self-managing them.

After about a year, I delved into the code and found that in order to sync the groups that a user had in SMF with the groups that a user had in Gallery, the integration code was removing all groups and adding the shared groups. This of course meant that the user was being removed from the required groups: “Everybody” and “Registered Users”. Users with privileged groups did not see the problem.

Here’s the fix that I’ve also submitted to Oldiesmann. But his forum complains when I added the code, so I had to create this post here. It says it does not allow external links (haha).

My fix will ensure that we do not remove the user from required groups and also add the users back into required groups (if necessary). Please note that the cleanup code is necessary because anyone who has ever visited the buggy gallery will have the groups removed, so there’s a lot of cleanup to do. The code provided below should auto-correct this issue.

commit 97075cd4e20d2807011b38cd293ccc38c728db9a
Author: Inderpreet Singh <inderpreet99gmail>
Date:   Sat Jul 28 10:33:40 2012 -0500

    PJ fix for SMF Gallery2 Integration:
    Avoid g2 required groups: Everybody and Registered Users groups from getting removed
    Add user to the required groups (cleanup our mess)

diff --git a/Sources/Gallery.php b/Sources/Gallery.php
index 74652fe..c289678 100755
--- a/Sources/Gallery.php
+++ b/Sources/Gallery.php
@@ -1348,10 +1348,32 @@ function groupCheck()
+		// Avoid g2 required groups: Everybody and Registered Users groups from getting removed!
+		$groupstoignore = array('Everybody', 'Registered Users');
+		$groupstoadd = $groupstoignore;
+		foreach($galgroups as $gid => $gname)
+		{
+			if(in_array($gname, $groupstoignore))
+			{
+				unset($galgroups[$gid]);
+				$groupstoadd = array_diff($groupstoadd, array($gname));
+			}
+		}
+		// Add user to the required groups (cleanup our mess)
+		foreach($groupstoadd as $gname) {
+			list($ret, $group) = GalleryCoreApi::fetchGroupByGroupName($gname);
+			if($ret)
+			{
+				fatal_error($ret->getAsText(), 'gallery');
+			}
+			GalleryCoreApi::addUserToGroup($context['user']['g2_uid'], $group->getId());
+		}
 		// array_diff will give us an array of all the values in $galgroups that aren't in $galsmfgroups
 		// $galgroups uses the group IDs as the keys, and the group names as the values. We only want the group IDs...
 		$groupstoremove = array_diff(array_keys($galgroups), $galsmfgroups);
 		// Remove them from any group(s) they no longer belong to
 		if(count($groupstoremove) > 0)

No php53-pear for RHEL 5.6, download it the traditional way!

I had to update to the latest vBulletin 4, but for that I needed php5.3. Luckily, RHEL has put out php53-* packages (I installed php 5.3.3-1.el5_6.1 btw). I downloaded them all but they were missing php53-pear* package. I’m not sure if this is just the case for x86_64.

So the main problem was getting PEAR packages to install. I tried to get the newer way of downloading pear packages, pyrus (pear2). However, the command php pyrus.phar install ___ resulted in messages like these:
The sqlite3 extension is required.
You must compile PHP with sqlite3 enabled, or install the necessary extension for your distribution.

(Not sure which extension of php53 would resolve this)

The sqlite3 extension is required.
You must compile PHP with sqlite3 enabled, or install the necessary extension for your distribution.

(This one is resolved by downloading php53-xml, thankfully available)

Anyhow, I didn’t want to go on a hunt to try to get these php53 special packages by following the instructions listed in the manual. However, the url listed in the manual http://pear.php.net/go-pear.phar makes php-fcgi put up a blank/empty page. I had a feeling it’s these .phar (weird php compressed archives) files that they’re using. Luckily in the comments, I noticed somebody said to get http://pear.php.net/go-pear. Now that my friends worked like a charm. All I had to do was download that to the server, open it up in the browser and follow the steps as it shows.

The rest comes easy. If you got questions, fire away.

Profile Visitors 4.1 Mod for SMF 2.0

Profile Visitors mod description:

This modifications logs the visitors for all members’ profile with the time of their last visit. Additionally it gives you the ability to see the total visit count for the profile.

Version 4.1

At last, version 4.1, which is an SMF 2.0 only package is released. This version does not log 20, but all visitors of a user. Also it keeps the total visit count for each visitor and total visit count for each profile.

It, as usual, has own/any profile view option and improved remove visitor permissions. Also it includes “Avoid logging permission”, which allows users to select if they want to be logged or not. User can set it from “Look and Layout” area of their profile.

And some bad news… There won’t be any updates for the SMF 1.x version, but I’ll continue supporting that too.

-Blue Dream

This mod is one of the most requested features on my social networking site, Punjabi Janta. So when I went to look for the mod (http://custom.simplemachines.org/mods/index.php?mod=1150), I noticed that the mod coder, [SiNaN], had ended development for a lot of his mods. A couple of users reuploaded the mod to the forum on user’s requests, but moderators were quick to remove it claiming that mods can’t be distributed without owner’s permission.

Anyhow, I had requested a copy from MasterD, and he was helpful in sharing it with me. Here’s an archived copy of it, you won’t find it on Simple Machine’s Forum.


PS: The Profile.template.php edit may fail. Just replace the leading spaces with tabs on the following lines:

      <div id="detailedinfo">
         <div class="windowbg2">   

Edit June 4, 2011: Here’s the edited version to work with SMF 2.0 with the above fix: ProfileVisitors4.1-IPS

Fix Pear/Mail due to CentOS/RHEL repos using old pear

I couldn’t install the Mail PEAR package because the pear version shipped with the current CentOS/RHEL is 1.4.9, whereas the required version is 1.5.6 or above. The following is the error that you may see.

# pear install Mail
WARNING: channel "pear.php.net" has updated its protocols, use "channel-update pear.php.net" to update
Did not download optional dependencies: pear/Net_SMTP, use --alldeps to download automatically
pear/Mail requires PEAR Installer (version >= 1.5.6), installed version is 1.4.9
pear/Mail can optionally use package "pear/Net_SMTP" (version >= 1.4.1)
No valid packages found
install failed

There are three problems above:

  1. WARNING: channel “pear.php.net” has updated its protocols, use “channel-update pear.php.net” to update
  2. Did not download optional dependencies: pear/Net_SMTP, use –alldeps to download automatically
  3. Main problem: pear/Mail requires PEAR Installer (version >= 1.5.6), installed version is 1.4.9

To fix problem #1 (update channel pear.php.net):

# pear channel-update pear.php.net
Retrieving channel.xml from remote server
Update of Channel "pear.php.net" succeeded

To fix problem #2 (Net_SMTP is not installed), run:

# pear install --alldeps Mail 
pear/Mail requires PEAR Installer (version >= 1.5.6), installed version is 1.4.9
downloading Net_SMTP-1.4.4.tgz ...
Starting to download Net_SMTP-1.4.4.tgz (12,264 bytes)
.....done: 12,264 bytes
downloading Net_Socket-1.0.10.tgz ...
Starting to download Net_Socket-1.0.10.tgz (5,429 bytes)
...done: 5,429 bytes
downloading Auth_SASL-1.0.4.tgz ...
Starting to download Auth_SASL-1.0.4.tgz (5,795 bytes)
...done: 5,795 bytes
install ok: channel://pear.php.net/Auth_SASL-1.0.4
install ok: channel://pear.php.net/Net_Socket-1.0.10
install ok: channel://pear.php.net/Net_SMTP-1.4.4

To fix problem #3 (old pear version), first upgrade pear:

# pear upgrade pear
pear/PEAR dependency package "pear/Structures_Graph" downloaded version 1.0.4 is not the recommended version 1.0.3, but may be compatible, use --force to install
pear/PEAR dependency package "pear/Console_Getopt" downloaded version 1.3.0 is not the recommended version 1.2.3, but may be compatible, use --force to install
pear/Archive_Tar requires PEAR Installer (version >= 1.5.4), installed version is 1.4.9
pear/Console_Getopt requires PEAR Installer (version >= 1.9.1), installed version is 1.4.9
downloading Structures_Graph-1.0.4.tgz ...
Starting to download Structures_Graph-1.0.4.tgz (30,318 bytes)
.........done: 30,318 bytes
downloading XML_Util-1.2.1.tgz ...
Starting to download XML_Util-1.2.1.tgz (17,729 bytes)
...done: 17,729 bytes
upgrade ok: channel://pear.php.net/XML_Util-1.2.1
upgrade ok: channel://pear.php.net/Structures_Graph-1.0.4

But this still doesn’t update base pear, so run:

# pear upgrade --force pear
warning: pear/PEAR dependency package "pear/Console_Getopt" downloaded version 1.3.0 is not the recommended version 1.2.3
warning: pear/Archive_Tar requires PEAR Installer (version >= 1.5.4), installed version is 1.4.9
warning: pear/Console_Getopt requires PEAR Installer (version >= 1.9.1), installed version is 1.4.9
downloading PEAR-1.9.1.tgz ...
Starting to download PEAR-1.9.1.tgz (293,587 bytes)
.............................................................done: 293,587 bytes
downloading Archive_Tar-1.3.7.tgz ...
Starting to download Archive_Tar-1.3.7.tgz (17,610 bytes)
...done: 17,610 bytes
downloading Console_Getopt-1.3.0.tgz ...
Starting to download Console_Getopt-1.3.0.tgz (4,408 bytes)
...done: 4,408 bytes
upgrade ok: channel://pear.php.net/Console_Getopt-1.3.0
upgrade ok: channel://pear.php.net/Archive_Tar-1.3.7
upgrade ok: channel://pear.php.net/PEAR-1.9.1
PEAR: Optional feature webinstaller available (PEAR's web-based installer)
PEAR: Optional feature gtkinstaller available (PEAR's PHP-GTK-based installer)
PEAR: Optional feature gtk2installer available (PEAR's PHP-GTK2-based installer)
To install use "pear install pear/PEAR#featurename"

Now we can install pear’s Mail:

# pear install --alldeps Mail
downloading Mail-1.2.0.tgz ...
Starting to download Mail-1.2.0.tgz (23,214 bytes)
........done: 23,214 bytes
install ok: channel://pear.php.net/Mail-1.2.0

You might also need Mail_mime:

# pear install Mail_mime
downloading Mail_Mime-1.8.1.tgz ...
Starting to download Mail_Mime-1.8.1.tgz (31,530 bytes)
.........done: 31,530 bytes
install ok: channel://pear.php.net/Mail_Mime-1.8.1

Upgrading WPMU 2.x to WordPress 3.0: Multisite Problems

When upgrading WordPress MU installation to a WordPress 3 multisite installation, I faced a lot of “Internal Server Error” problems, such as:

Premature end of script headers: *.php
an error occurred while processing this directive

A lot of people suggested disabling plugins before, or even removing them all from the /wp-content/plugins/ folder. But one crucial detail was missing from most of these suggestions. WPMU keeps a special set plugins for itself in the folder /wp-content/mu-plugins/. It is very important to disable these, because chances are these are the most “incompatible” plugins that you have (because they are not updated like the regular WordPress plugins).