Tag Archives: BuddyPress

Three ways to integrate BuddyPress in three presentations

In the last year or so, I’ve given a number of public presentations about BuddyPress. One of my main goals in giving these presentations to WordPress groups is always to convince WP developers to give BP a try. My angle on this has been “BuddyPress compatibility”: the idea that you can take existing WordPress functionality and, with just a bit of pixie dust and elbow grease, integrate it into BP. While there are countless ways that a WP plugin could integrate with BuddyPress, the big three are: (1) displaying group-specific content in groups, (2) displaying user-specific content on user pages, and (3) registering items in the activity stream.

As of this past Saturday, I’ve now given presentations on all three of these methods:

  1. BuddyPressifying a WordPress Plugin Using the Group Extension API (BuddyCamp Miami 2013)
  2. Herding Cats with the BuddyPress Activity Component (WordCamp Europe 2013)
  3. BuddyPressifying a WordPress Plugin Using BP_Component (WPSessions, June 21, 2014)

If you’re a WP developer who’s looking for an on-ramp into BuddyPress work, I think these presentations are a good place to get started.

Expunge non-public content from a WordPress/BuddyPress installation

It’s a common practice to create local WordPress development environments using a copy of the production database. But this can cause problems with a large production site, as the database can become very large, and it is full of non-public information that you may not want to make available to all members of the development team. This is especially problematic when running a plugin like BuddyPress, which allows users to create a great deal of content with various privacy levels.

To work around this problem for the CUNY Academic Commons, I wrote this plugin: cac-database-cleaner. It will remove all non-public data from a WP database, while still leaving an intact database image that can be used to populate a development environment.

WARNING – This is a dangerous tool, as it deletes large amounts of data. Under no circumstances should you install this plugin on a production site. To use: export your production database; import to a separate database and perform any manual changes necessary for the WordPress site to load locally (such as modification of your local hosts file); activate plugin and navigate to Dashboard > Network Admin > CAC Database Cleaner.

Again, do not use this plugin if you don’t know exactly what you’re doing.

Note that plugin support is ideosyncratic to the CUNY Academic Commons, where we run an old version of BuddyPress Docs, an old fork of BuddyPress Group Documents, a plugin called More Privacy Options, legacy bbPress forums, etc. Feel free to modify the plugin to work with whatever other data you’d like.

Five years of BuddyPress

I started working with BuddyPress by accident. In February 2009, I responded to a tweet from my friend Matt Gold asking for help with a CSS issue on a site he was working on. That site was the still-in-beta CUNY Academic Commons, running on the still-in-beta BuddyPress. Within a few weeks, I was doing paid work for Matt’s project, working with BP (and WP, and web software in general) for the first time. And BuddyPress 1.0 came out just a few weeks after that.

Over the last five years, BuddyPress has taken over my professional life. I began by writing BP plugins. I started to contribute to BP itself through support and patches. I became a member and eventually a lead on the core team. My consultation work involves BuddyPress almost exclusively; this success (in terms of both money and impact) emboldened me to drop out of graduate school. People know me as “the BuddyPress guy”. When you type “boone gorges” into Google, it suggests “boone gorges buddypress”.

I feel very grateful to have stumbled into the project when I did. It aligns with many of my philosophical and political positions: the primacy of people over content, the importance of data ownership and free software, the fight against parasitic software vendors in public institutions. I’ve met some good friends through my association with BP. I’ve leveraged my expertise into a fun and comfortable career.

But the fact remains that it’s all been a fluke. When I realized it’s been five whole years, I couldn’t shake the thought: WTF. How strange to devote such a large part of one’s life to something that was such an accident. [Something something destiny something something forks in the road something.] I got lucky because I happened to stumble into something that was a particularly good fit for me. But I also took many leaps of faith along the way: agreeing to work on the CUNY Academic Commons when I had pretty much no idea what I was doing, submitting my first patches to BP, quitting my job, upping my rates, donating huge amounts of time to the free project instead of doing paid client work. I’m glad I had the guts to make each of these leaps.

Happy birthday to BuddyPress, and happy anniversary to me. Here’s to many more happy accidents!

Any major dude with half a heart surely will be at WordCamp Connecticut on May 10

A few months ago, I had the pleasure of speaking at the WordPress Stamford Meetup, organized by Clint Warren. I musta put a bug in his ear or something, because I got a follow-up email last month letting me know he was organizing the very first WordCamp Connecticut. I’ll be giving a talk about BuddyPress.

The organizers are still looking for speakers, so if you’re a WordPress person in the CT vicinity (Stamford is an easy Metro-North ride from NYC), please consider applying to present! And if you’re just looking to nerd out for a day, add yourself to the mailing list so you’ll know when tickets are available. DO IT

Default Gravatar images and SSL

I have a client who runs a number of WordPress/BuddyPress sites over SSL. He noticed in the last few days that default Gravatar images – the images that Gravatar serves when there is no Gravatar associated with the queried email address – were not being served. The browser showed broken images, and when you attempted to load the associated https://secure.gravatar.com URL in a separate tab, you’d see the message “We cannot complete this request, remote data could not be fetched”.

After a bit of futzing around, I found this recent post by Eric Mann describing a similar issue with the Photon CDN feature in the Jetpack plugin. He managed to figure out that Automattic’s CDN service wasn’t fetching items that were served over HTTPS. (The fact that it ever worked was, apparently, a bug; that “bug” was recently fixed.)

It turns out that the same thing is true for Gravatar’s “Default Image” feature (unsurprising, as I assume it uses the same CDN as Photon). Gravatar lets you specify a local file that will be served if no actual Gravatar is found: <img src="http://www.gravatar.com/avatar/00000000000000000000000000000000?d=http%3A%2F%2Fexample.com%2Fimages%2Favatar.jpg" /> But, as of the last few weeks, if the value of the d= param is served over HTTPS only, Gravatar throws an error.

There are a couple strategies for working around the problem.

  • Use Gravatar’s defaults instead – Gravatar hosts a number of default images that you can use, instead of a local image. This is especially pertinent in the case of BuddyPress. BP’s default behavior is to construct Gravatar requests like this: http://www.gravatar.com/avatar/00000000000000000000000000000000?d=http%3A%2F%2Fexample.com%2Fimages%2Fwp-content%2Fplugins%2Fbuddypress%2Fbp-core%2Fimages%2Fmystery-man.jpg. The thing is that this mystery-man.jpg that ships with BuddyPress is the same image as what you get with ?d=mm. So an easy way around the problem of Gravatar reading from your SSL-protected site is to avoid Gravatar from making any requests to your site at all. In BuddyPress, use the following:
    function bbg_use_gravatar_mm() {
        return 'mm';
    }
    add_filter( 'bp_core_mysteryman_src', 'bbg_use_gravatar_mm' );
    
  • Allow non-SSL access to your default – As suggested in Eric’s post, you can tell your webserver that some of your content can be served over HTTP rather than HTTPS. For example, on one of the sites I’m working on, we force HTTPS for all requests using an .htaccess rule. I can amend it to allow an exception for the custom Gravatar default:
    RewriteCond %{HTTPS} off
    RewriteCond %{REQUEST_URI} !^/wp\-content/themes/yourtheme/images/default\-gravatar.jpg$
    RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R,L]
    

    Then, force BuddyPress to tell Gravatar you want the non-SSL version of the fallback:

    function bbg_custom_default_avatar() {
        return set_url_scheme( get_stylesheet_directory_uri() . '/images/default-gravatar.jpg', 'http' );
    }
    add_filter( 'bp_core_mysteryman_src', 'bbg_custom_default_avatar' );
    

Even if you’re not using BuddyPress or WordPress, the same strategy applies: if you’re serving your whole site over HTTPS, tell Gravatar to use either one of its own images or one of your non-SSL-available images as its default.

WP-CLI tools for BuddyPress

I think we can all agree that WP-CLI is the cat’s pajamas. Starting today, I’ll be maintaining a small (but growing) library of BuddyPress commands for WP-CLI: wp-cli-buddypress. Available commands are currently quite meager (and quite biased toward my needs as a developer rather than a site admin), but I’ll be building more, and would be delighted to receive pull requests.

Commit messages, tickets, and inline docs

As a Trained Academic™, I try to think about software documentation in terms of audience and purpose. Who is going to read technical docs? What will they be trying to learn by reading them? I use these questions as a lens for writing different kinds of documentation.

I like to think of commit messages as the canonical technical narrative of the project. These messages matter most for two groups of people: those following the ongoing development of the project, and those intrepid future souls who are combing through logs to track the lineage of given bit of code. The needs of the two groups are similar, which suggests certain content and formatting for commit messages. Both groups are scanning sizable streams of changesets, and thus need a quick way to disregard those that are irrelevant to them. That’s where the one-line summary is helpful, for git log and GUIs like this. Beyond the summary, they usually need a bit more context, a bird’s-eye summary of the problem the changeset is intended to fix. They need pointers to full discussions (ie, linked tickets). And because in projects like WordPress the changelog is also an attribution log, they need reference to the person responsible for the fix (“props”). It’s these kinds of needs I’m thinking about when I structure commit messages like this. (Side note: I’m a big fan of this post arguing for a similar commit message format.)

Tickets are bug notices or feature requests as they appear in the project’s issue tracker. The primary audience here is the developers, designers, and users who are currently involved in the ongoing development of the software. It’s a workspace, and it’s messy. Tickets may contain extended discussions, with numerous digressions and dead-end patches. It’s for this reason that issue tickets are not a replacement for good commit messages. Commit messages contain justification for changesets, while trackers contain the process that led to that justification.

Inline docs, primarily in the form of function/method/class docblocks, are intended first and foremost for developers who are currently trying to figure out how the software works. As a rule, these people don’t care about the history of the project, or the reasoning that led to a given piece of code. The documentation should answer questions like: where is this function used in the codebase? if I put x into the function, what will I get out of it? etc. While it’s often good to have pointers to the project history in certain cases – such as a link to a ticket that explains why an unintuitive bit of code works in the way it does – it probably does more harm than good to litter inline documentation with details about the project’s intellectual history. That’s what blame tools are for.

It goes without saying that there are gray areas. Commit messages, tickets, and inline docs all have the same “text” as their subject: the codebase. And the intended audiences for the three kinds of documentation are frequently overlapping. Still, I think it’s helpful to keep the distinctions in mind. When you write documentation, you’re writing the story of the project as it’ll be understand by future coder-historians. You want to make sure that the story makes sense.

A less finicky BuddyPress search

BuddyPress search, out of the box, is not very good. Say you’re looking for a group called “History of Wars in America”. The search term Wars in America will return the group, but America Wars will not. (Technical reason: search terms get lumped as a single string into a MySQL LIKE clause.)

I have some ideas about how to improve this behavior in BuddyPress itself, including stealing some of the goodies that recently went into WordPress. But for now, here’s a simple drop-in filter that fixes the word-wise problem.

(if the formatting is messed up, view the original at https://gist.github.com/boonebgorges/8301715)

Something very similar would work for members searches, though the query variables passed along to the filters probably have a slightly different syntax. (I made these changes for City Tech OpenLab, whose members queries are custom anyway.)

Again, this filter is not perfect – it doesn’t try to do any caching, it doesn’t look for literal strings in quotes, etc – but you might find it useful until some real fixes are in place in BP.

WordPress/BuddyPress registration and the Office 365 email filter

Just tore through the following problem on a client site (independently discovered by Martha Burtis here). WordPress/BuddyPress sites that allow for self-registration send out emails with activation links of the form: http://example.com/activate/?key=12345 (for BuddyPress) and http://example.com/wp-activate.php?key=12345 (for WordPress multisite). This format trips up the link filter that Microsoft’s Office 365 email service uses. After some experimentation, I figured out that the problem is the word ‘key’ in a URL parameter – once this term is removed from the URL, it passes right through the filter.

So, you can fix the problem by changing the URL parameter in the activation emails. That means (a) changing the text of the email, and (b) changing the server-side logic to expect something other than ‘key’. Here’s how to do it in BuddyPress:

function bbg_activation_email_content( $message ) {
	return str_replace( '?key=', '?activationk=', $message );
}
add_filter( 'bp_core_activation_signup_user_notification_message', 'bbg_activation_email_content' );

function bbg_screen_activation() {
	global $bp;

if ( !bp_is_current_component( 'activate' ) )
		return false;

// Check if an activation key has been passed
	if ( isset( $_GET['activationk'] ) ) {

// Activate the signup
		$user = apply_filters( 'bp_core_activate_account', bp_core_activate_signup( $_GET['activationk'] ) );

// If there were errors, add a message and redirect
		if ( !empty( $user->errors ) ) {
			bp_core_add_message( $user->get_error_message(), 'error' );
			bp_core_redirect( trailingslashit( bp_get_root_domain() . '/' . $bp->pages->activate->slug ) );
		}

// Check for an uploaded avatar and move that to the correct user folder
		if ( is_multisite() )
			$hashed_key = wp_hash( $_GET['activationk'] );
		else
			$hashed_key = wp_hash( $user );

// Check if the avatar folder exists. If it does, move rename it, move
		// it and delete the signup avatar dir
		if ( file_exists( bp_core_avatar_upload_path() . '/avatars/signups/' . $hashed_key ) )
			@rename( bp_core_avatar_upload_path() . '/avatars/signups/' . $hashed_key, bp_core_avatar_upload_path() . '/avatars/' . $user );

bp_core_add_message( __( 'Your account is now active!', 'buddypress' ) );

$bp->activation_complete = true;
	}

bp_core_load_template( apply_filters( 'bp_core_template_activate', array( 'activate', 'registration/activate' ) ) );
}
remove_action( 'bp_screens', 'bp_core_screen_activation' );
add_action( 'bp_screens', 'bbg_screen_activation' );

You’d have to do something in the same spirit when not using BuddyPress. For the email, filter ‘wpmu_signup_user_notification_email’. Catching the request and overriding ‘key’ will be trickier. I haven’t experimented with it, but maybe you can hook to ‘activate_header’, detect the presence of $_GET['activationk'], and then redirect to the ‘key=’ URL that wp-activate.php expects.

Hopefully this is enough to help if you’re having the problem.