Leveraging WordPress Plugins - Building Web Apps with WordPress (2014)

Building Web Apps with WordPress (2014)

Chapter 3. Leveraging WordPress Plugins

Plugins are awesome! If you didn’t know, now you know! Plugins can help you deploy a full-blown web application with little to no knowledge of actual code. Whether you are using a free plugin, premium plugin, or building your own, plugins can extend WordPress to give you the functionality your application requires.

As we mentioned earlier, the great advantage of open source software is that members of the community are invested in improving WordPress and often build plugins to achieve a desired feature. The definition of a plugin provided in the Wordpress codex is, “a program, or a set of one or more functions, written in the PHP scripting language, that adds a specific set of features or services to the WordPress weblog, which can be seamlessly integrated with the weblog using access points and methods provided by the WordPress Plugin Application Program Interface (API).” Plugins allow you to turn your site into anything you can think of, from a basic blog to an ecommerce site to a social network.

There are a couple of plugins that come standard with any new WordPress install: Hello Dolly and Akismet. If you didn’t know, the Hello Dolly plugin adds a random lyric from the song “Hello Dolly” by Louis Armstrong to the top of your dashboard on each page load. It’s not useful, but is a good way to see how to structure your own plugins. The Akismet plugin integrates with Akismet.com to auotmatically filter out spam comments from your blog. While Hello Dolly is useless outside of its educational value, Akismet is downright necessary on any site with commenting turned on. You always have the ability to deactivate these plugins or delete them altogether if you do not see any use for them on your site.

There are over 26,000 plugins available that can be accessed through the official WordPress plugin repository. Not all plugins can be found in the repository, so you can always do a search on the Internet for whatever functionality you are looking for. Many plugin creators have their work available for download through their personal or business sites and many of these are available for a fee. There are also premium plugins, which are plugins that you have to pay to use. Similar to mobile apps, there is sometimes a scaled-down version of the plugin available for free and then a more involved version available for a fee. Most premium plugins also offer developer licenses. This allows developers that may be working on building multiple sites to pay one price for the plugin files and then install them on multiple WordPress installs.

The GPLv2 License

No matter how you purchase or obtain a WordPress plugin, all WordPress plugins must use the GPLv2 code license, which states that if the source code is distributed (made available or sold online, etc.), then you can do anything you want to with the code as long as any derivative work retains the GPLv2 license. Some themes and plugins may use a split license, meaning the HTML, CSS, JavaScript, and images are distributed under a different license than the PHP files. Some themes and plugins do not mention the GPLv2 license or flat out deny it applies. There is a little bit of legal merit to their claims, but the authority figures in the WordPress.org community (namely Matt Mullenweg) state that all themes and plugins must be GPL compatible. Our stance is that if you want to do business in the WordPress community, you should follow their rules.

Overall, plugins are a great way to add enhanced functionality to your website without having to change any of the core WordPress files. If you are looking for a specific feature, you should first do a search to make sure that plugin does not already exist for that functionality. If not, you then have two options: you can choose to download and modify an existing plugin or build a new one from scratch.

Installing WordPress Plugins

To install a WordPress plugin, simply log in to the WordPress admin dashboard of your site, also know as the backend. Click on the Plugins section, as shown in Figure 3-1. You will then have the option to search the WordPress plugin repository or upload one if you have already downloaded a plugin from the repository or another source. Once you have completed your search and found a plugin you are interested in, click to install the plugin. Once the plugin is installed, you will then have the option to activate it. If you do not activate the plugin, it will remain deactivated in the “Plugins → Installed Plugins” page of your site. Also, please keep in mind that many plugins will need to be configured once activated, and you will usually see a message appear in the dashboard telling you to do so.

Add a new plugin.

Figure 3-1. Add a new plugin

If you downloaded a plugin from a source other than the official Wordpress plugin repository, you should have a ZIP file of the plugin files. To upload the plugin to your site, you will need to click on Upload in the Plugins section of the dashboard and then choose that ZIP file from wherever you have it saved on your computer. You will be given the choice to activate the plugin if you wish to do so at that time.

Building Your Own Plugin

The true power of WordPress for app developers is that you can make your own custom plugin to extend WordPress anyway you see fit.

To create a plugin, first create a new folder in wp-content/plugins called my-plugin and make a PHP file in that folder called my-plugin.php. In my-plugin.php, write the following code, and feel free to replace any of the values:

<?php

/*

* Plugin Name: My Plugin

* Plugin URI: http://webdevstudios.com/

* Description: This is my plugin description.

* Author: messenlehner, webdevstudios, strangerstudios

* Version: 1.0.0

* Author URI: http://bwawwp.com

* License: GPLv2 or later

*/

?>

Save your my-plugin.php file. Congratulations, you are a WordPress plugin author! Even though your plugin doesn’t do anything yet, you should be able to see it in /wp-admin/plugins.php and activate it. Go ahead and activate it.

Let’s make your plugin actually do something. Let’s add something to the footer of your WordPress install. Copy and save the following code after the plugin information:

<?php

function my_plugin_wp_footer() {

echo 'I read Building Web Apps with WordPress

and now I am a WordPress Genius!';

}

add_action( 'wp_footer', 'my_plugin_wp_footer' );

?>

If you go out to the frontend of your website (make sure you refresh), you should notice a new message in the footer. Now you are off to the races, and you can customize this basic plugin to do whatever you want. If you are already a PHP developer, start hacking away! If you are new to PHP and WordPress, a good way to kickstart your skills is to download and analyze the code in other plugins to see how they are doing what they do.

We will be going over more code that you can use in any of the plugins that you build throughout the book. This was just a very basic example to get you started.

File Structure for an App Plugin

When building a web app with WordPress, we recommend having one main app plugin to store the core functionality for your app. On the theme side (covered in Chapter 4), you will store the majority of your app’s frontend code in the active theme.

Some plugins only do one or two things, and one .php file is all you need to get things done. Your main app plugin is probably going to be much more complicated, with asset files (CSS, images, and templates), included libraries, class files, and potentially thousands of lines of code you will want to organize into more than one file.

Here is our proposed file structure for an app’s main plugin, using the SchoolPress plugin as an example. Not all of these folders and files may be necessary. We add them to a plugin as needed:

§ /plugins/schoolpress/adminpages/

§ /plugins/schoolpress/classes/

§ /plugins/schoolpress/css/

§ /plugins/schoolpress/css/admin.css

§ /plugins/schoolpress/css/frontend.css

§ /plugins/schoolpress/js/

§ /plugins/schoolpress/images/

§ /plugins/schoolpress/includes/

§ /plugins/schoolpress/includes/lib/

§ /plugins/schoolpress/includes/functions.php

§ /plugins/schoolpress/pages/

§ /plugins/schoolpress/services/

§ /plugins/schoolpress/scheduled/

§ /plugins/schoolpress/schoolpress.php

/adminpages/

Place the .php files for any dashboard page you add through your plugin in the /adminpages/ directory. For example, here is how you would add a dashboard page and load it out of your /adminpages/ directory:

<?php

// add a SchoolPress menu with reports page

function sp_admin_menu() {

add_menu_page(

'SchoolPress',

'SchoolPress',

'manage_options',

'sp_reports',

'sp_reports_page'

);

}

add_action( 'admin_menu', 'sp_admin_menu' );

// function to load admin page

function sp_reports_page() {

require_once dirname( __FILE__ ) . "/adminpages/reports.php";

}

?>

/classes/

Place any PHP class definitions in the /classes/ directory. In general, each file in this directory should include just one class definition. The class files should have names like class.ClassName.php, where ClassName is the name given to the class.

/css/

Place any CSS files used specifically for your plugin in the /css/ directory. Split your CSS into admin.css and frontend.css files depending on whether the CSS affects the WordPress dashboard or something on the frontend.

Any CSS libraries needed, for example, to support an included JavaScript library, can also be placed in this folder.

Here is some code to enqueue the admin.css and frontend.css styles from the plugin’s CSS folder:

---

<?php

function sp_load_styles() {

if ( is_admin() ) {

wp_enqueue_style(

'schoolpress-plugin-admin',

plugins_url( 'css/admin.css', __FILE__ ),

array(),

SCHOOLPRESS_VERSION,

'screen'

);

} else {

wp_enqueue_style(

'schoolpress-plugin-frontend',

plugins_url( 'css/frontend.css', __FILE__ ),

array(),

SCHOOLPRESS_VERSION,

'screen'

);

}

}

add_action( 'init', 'sp_load_styles' );

?>

---

Any CSS that affects components of the WordPress dashboard should go into the admin.css file. Any CSS that affects the frontend of the site should go into frontend.css, but be careful when adding CSS rules to the frontend.css file. When adding frontend styles to your plugin files, ask yourself first if the CSS rules you are writing should go into the app’s theme instead, since the majority of your frontend-style code should be handed by your theme.

The kind of CSS that would go into the plugin’s CSS file are generally layout styles that would be appropriate no matter what theme was loaded. Imagine that your site had no theme or CSS loaded at all. What would be the bare minimum CSS needed to have the HTML generated by your plugin make sense? Expect the theme to build on and override that.

For example, your plugin’s frontend.css should never include styles for coloring. However, a style saying an avatar is 64 px wide and floated left could be appropriate.

/js/

Place any JavaScript files needed by your plugin in this folder. Again, you can split things into an admin.js and frontend.js file depending on where the JS is needed.

Any third-party JavaScript libraries used may also be placed in this folder. Generally, they should be added to a subfolder of the /js/ directory.

Here is some code to load admin.js and frontend.js files from your plugin’s /js/ directory:

<?php

function sp_load_scripts() {

if ( is_admin() ) {

wp_enqueue_script(

'schoolpress-plugin-admin',

plugins_url( 'js/admin.js', __FILE__ ),

array( 'jquery' ),

SCHOOLPRESS_VERSION

);

} else {

wp_enqueue_script(

'schoolpress-plugin-frontend',

plugins_url( 'js/frontend.js', __FILE__ ),

array( 'jquery' ),

SCHOOLPRESS_VERSION

);

}

}

add_action( 'init', 'sp_load_scripts' );

?>

Just like with stylesheets, it can be difficult to determine if some bit of JavaScript should be included in the plugin’s JavaScript file or the theme’s JavaScript file. In general, JS files that support the theme (e.g., slider effects and menu effects) should go in the theme, and JS files that support the plugin (e.g., AJAX code) should go in the plugin. In practice, however, you will find your plugin using JS defined in your theme and vice versa.

/images/

Place any images needed by your plugin in the /images/ directory.

/includes/

The /includes/ directory is a kind of catchall for any .php files your plugin needs. The only .php file in your plugin’s root folder should be the main plugin file schoolpress.php. All other .php files should go in one of the other folders; and if none are more appropriate, you either need to make another folder or place it in the /includes/ folder.

It is standard procedure to add a functions.php or helpers.php file to include any helper PHP code used by your plugin. This file should include any small scripts that don’t have a central role in the logic or functionality of your plugin but are needed to support it. Examples include functions to trim text, generate random strings, or other framework-like functions that aren’t already available through a core WordPress function.

/includes/lib/

Place any third-party libraries that you need for your app into the /includes/lib/ directory.

/pages/

Place any .php code related to frontend pages added by your plugin in the /pages/ directory. Frontend pages are typically added through shortcodes that you would embed into a standard WordPress page to show the content you want.

The following code snippet illustrates how to create a shortcode that can be placed on a WordPress page to generate a page from your plugin. The preheader here is a chunk of code to run before the wp_head() function loads, and thus before any HTML headers or code are sent to the browser. The shortcode function further down outputs HTML to the actual page at the place of the shortcode.

Place this code in /plugins/{your plugin folder}/pages/stub.php, then include it (typically using the require_once() function) from your main plugin file. Then add the shortcode [sp_stub] to a page of your WordPress site.

<?php

// preheader

function sp_stub_preheader() {

if ( !is_admin() ) {

global $post, $current_user;

if ( !empty( $post->post_content ) && strpos

( $post->post_content, "[sp_stub]" ) !== false ) {

/*

Put your preheader code here.

*/

}

}

}

add_action( 'wp', 'sp_stub_preheader', 1 );

// shortcode [sp_stub]

function sp_stub_shortcode() {

ob_start();

?>

Place your HTML/etc code here.

<?php

$temp_content = ob_get_contents();

ob_end_clean();

return $temp_content;

}

add_shortcode( 'sp_stub', 'sp_stub_shortcode' );

?>

For the preheader code, we first check that the page is being loaded from outside the admin using !is_admin(); otherwise this code might run when editing the post in the dashboard. Then we look for the string [sp_stub] in the content of the $post global. This function is hooked to thewp hook, which runs after WordPress sets up the $post global for the current page, but before any headers or HTML is output.

The preheader code can be used to check permissions, process form submissions, or prep any code needed for the page. In an MVC model, this would be your model and/or controller code. Because this code is run before any headers are output, you can still safely redirect users to another page. For example, you can wp_redirect() them to the login or signup page if they don’t have access to view the page.

In the shortcode function, we use ob_start(), ob_get_contents(), and ob_end_clean(), which are PHP functions used to buffer output to a variable. Using this code means that the code between the preceding ?> and <?php tags is placed into the $temp_content variable instead of output at the time of processing (which would have it echoed out above the <html> tag of your site). This isn’t necessary; you could just define a $temp_content function and use PHP to add to that string. Using output buffering allows us to code in a more template-like way, mixing HTML and PHP, which is easier to read.

/services/

Place any .php code for AJAX calls in the /services/ directory.

/scheduled/

Place any .php code that is related to cron jobs or code that is meant to be run at scheduled intervals here.

/schoolpress.php

This is the main plugin file. For small plugins, this may be the only file needed. For large plugins, the main plugin file will only contain include statements, constant definitions, and some comments about which other files contain the code you might be looking for.

Add-Ons to Existing Plugins

Any plugin or piece of code that runs on WordPress and is distributed[7] is supposed to be open source and licensed under the GPL. You could take any plugin in the repository, change the name, and release it as a totally new plugin. Doing this could get you into a bar fight, so we suggest that you don’t “fork” plugins like this unless you are also planning to improve on and maintain the new plugin.

What if you found a plugin that does 95% of what you need, but it needs a couple lines of code to get to 100%? Consider making an add-on for the plugin.

Most well-developed plugins will have their own hooks and filters, which can allow other developers to create an add-on plugin. Just as you would build a plugin to use hooks and filters in WordPress, you can build a plugin to use hooks and filters in other plugins. In some cases, you may need to hack the original plugin to do what you want, which is totally cool, but maybe you can suggest adding some hooks or filters where you need them to the original plugin author.

Use Cases and Examples

So what should we build with the free and premium plugins we just mentioned? Let’s add a community around WordPress: SchoolPress.

Each teacher will be the administrator of her own group and can easily add students to it. Students can engage in the group activity, or the “Class Wall” as we will call it. With BuddyPress, students can add one another as friends, follow their friends or teachers, and private message their teachers if they have questions.

With BadgeOS and the BadgeOS Community add-on, we can allow teachers to create fun reward badges for their students to earn as they complete various homework assignments and projects that they can share with their friends on the social networks that they are already on.

We can use Gravity Forms to make a really easy way for students to submit their homework.

The WordPress Loop

The great and powerful WordPress Loop is what makes WordPress display its posts. Depending on what theme template file is being called on when navigating your website, WordPress queries the database and retrieves the posts that need to be returned to the end user and then loops through them.

Most correctly built WordPress themes usually have the following files that contain the WordPress loop:

§ index.php

§ archive.php

§ category.php

§ tag.php

§ single.php

§ page.php

If you open up any of these files, will contain code that may look something like this:

<?php

if ( have_posts() ) {

while ( have_posts() ) {

the_post();

// show each post title, excerpt/content , featured image and more

the_title( '<h2>', '</h2>' );

the_content();

}

} else {

// show a message like sorry no posts!

}

?>

The have_posts() function checks to see if there are any posts that need to be looped, and if so, the while loop is initiated. The the_post() function called first in each iteration of the loop sets up the post with all of its global variables so post-specific data can be displayed to the end user.

WordPress Global Variables

Global variables are variables that can be defined and then used anywhere after in the rest of your code. WordPress has a few built-in global variables that can really help you save a lot of time and resources when writing code.

If you wanted to see a full list of every global variable available to you, you can run the following code:

<?php

echo '<pre>';

print_r( $GLOBALS );

echo '</pre>';

?>

To access a global variable in any custom code you are writing, use code like this:

<?php

global $global_variable_name;

?>

Some global variables are only made available to you depending on where you are in WordPress. Below is a short list of some of the more popular global variables:

§ $post—An object that contains all of the post data from the wp_posts table for the current post that you are on within the WordPress loop.

§ $authordata—An object with all of the author data of the current post that you are on within the WordPress loop.

$wpdb

The $wpdb class is used to interact with the database directly. Once globalized, you can use $wpdb in custom functionality to select, update, insert, and delete database records. If you are new to WordPress and aren’t familiar with all of the functions to push and pull from the database, $wpdbis going to be your best friend.

Queries using $wpdb are also useful when you need to manage custom tables required by your app or perform a complicated query (perhaps joining many tables) faster than the core WordPress functions would run on their own. Please don’t assume that the built-in WordPress functions for querying the database are slow. Unless you know exactly what you are doing, you’ll want to use the built-in functions for getting posts, users, and metadata. The WordPress core is smart about optimizing queries and caching the results from these calls, which will work well across all of the plugins you are running. However, in certain situations, you can shave a bit of time by rolling your own query. A few examples like this are covered in Chapter 16.

Using custom DB tables

In SchoolPress, we store the relationship of student submissions to assignments in a custom table. This keeps the core WordPress tables a bit cleaner[8] and allows us to easily query for things like “select all of Isaac’s assignments.”

To add our table to the database, we need to write up the SQL for the CREATE TABLE command and query it against the WordPress database. You can use either the $wpdb->query() method or the dbDelta() function in the WordPress core.

There are a few things we need to do to keep track of our custom tables. We want to store a db_version for our app plugin so we know what version of the database schema we are working with in case it updates between versions. We can also check the version so we only run the setup SQL once for each version. Another common practice is to store your custom table name as a property of $wpdb to make querying it a bit easier later.

Example 3-1 shows a little bit of our database setup function for the SchoolPress app:

Example 3-1. Database setup for SchoolPress

<?php

// setup the database for the SchoolPress app

function sp_setupDB() {

global $wpdb;

// shortcuts for SchoolPress DB tables

$wpdb->schoolpress_assignment_submissions = $wpdb->prefix .

'schoolpress_assignment_submissions';

$db_version = get_option( 'sp_db_version', 0 );

// create tables on new installs

if ( empty( $db_version ) ) {

global $wpdb;

$sqlQuery = "

CREATE TABLE '" . $wpdb->schoolpress_assignment_submissions . "' (

`assignment_id` bigint(11) unsigned NOT NULL,

`submission_id` bigint(11) unsigned NOT NULL,

UNIQUE KEY `assignment_submission` (`assignment_id`,`submission_id`),

UNIQUE KEY `submission_assignment` (`submission_id`,`assignment_id`)

)

";

require_once ABSPATH . 'wp-admin/includes/upgrade.php';

dbDelta( $sqlQuery );

$db_version = '1.0';

update_option( 'sp_db_version', '1.0' );

}

}

add_action( 'init', 'sp_dbSetup', 0 );

?>

The sp_dbSetup() function is run early in init (priority 0) so the table shortcuts are available to any other code you have running. You can’t always assume a wp_ prefix, so the $wpdb->prefix property is used to get the database prefix for the WordPress install.

A DB version for the SchoolPress app is stored in the WordPress options table. We get the value out of options, and if it is empty, we run code to create our custom tables. The CREATE TABLE SQL statement here is pretty standard. You should always try to run these commands directly on the MySQL database before pasting them into your plugin code to make sure they work.

We use the dbDelta() function to create the database table. This function will create a new table if it doesn’t exist. Or if a table with the same name already exists, it will figure out the correct ALTER TABLE query to get the old table to match the new schema.

To use dbDelta(), you must be sure to include the wp-admin/includes/upgrade.php file since that file is only loaded when needed. Then pass dbDelta() the SQL for a CREATE TABLE query. Your SQL must be in a specific format a little more strict than the general MySQL format.

From the WordPress Codex on Creating Tables with Plugins:

1. You must put each field on its own line in your SQL statement.

2. You must have two spaces between the words PRIMARY KEY and the definition of your primary key.

3. You must use the keyword KEY rather than its synonym INDEX, and you must include at least one KEY.

4. You must not use any apostrophes or backticks around field names.

Running queries

Using dbDelta() is preferred when creating tables because it will automatically update older versions of your tables, but you can also run the CREATE TABLE query using $wpdb->query($sqlQuery);.

You can run any valid SQL statement using the $wpdb->query() method. The query() method sets a lot of properties on the $wpdb object that are useful for debugging or just keeping track of your queries:

§ $wpdb->result will contain the raw result from your SQL query.

§ $wpdb->num_queries is incremented each time a query is run.

§ $wpdb->last_query will contain the last SQL query run.

§ $wpdb->last_error will contain a string with the last SQL error generated if there was one.

§ $wpdb->insert_id will contain the ID created from the last successful INSERT query.

§ $wpdb->rows_affected is set to the number of affected rows.

§ $wpdb->num_rows is set to the number of rows in a result for a SELECT query.

§ $wpdb->last_result will contain an array of row objects generated through the mysql_fetch_object() PHP function.

The return value of the $wpdb->query() method is based on the top of query run and if the query was successful or not:

§ False is returned if the query failed. You can test for this using code like if($wpdb->query($query) === false) { wp_die(“it failed!”); }.

§ The raw MySQL result is returned on CREATE, ALTER, TRUNCATE, and DROP queries.

§ The number of rows affected is returned for INSERT, UPDATE, DELETE, and REPLACE queries.

§ The number of rows returned is returned for SELECT queries.

Escaping in DB queries

It should be noted that values passed into the query() method are not escaped automatically. Therefore, you will always need to escape untrusted input when using the query() method directly.

There are two main ways of escaping values used in your SQL queries: you can wrap your variables in the esc_sql() function (see Example 3-2) or you can use the $wpdb->prepare() method to build your query.

Example 3-2. Using the esc_sql() function

global $wpdb;

$user_query = $_REQUEST[‘uq’];

$sqlQuery = “SELECT user_login FROM $wpdb->users WHERE

user_login LIKE ‘%” . esc_sql($user_query) . “%’ OR

user_email LIKE ‘%” . esc_sql($user_query) . “%’ OR

display_name LIKE ‘%” . esc_sql($user_query) . “%’

”;

$user_logins = $wpdb->get_col($sqlQuery);

if(!empty($user_logins))

{

echo “<ul>”;

foreach($user_logins as $user_login)

{

echo “<li>$user_login</li>”;

}

echo “</ul>”;

}

Alternatively, you could create the query using the prepare() method, which functions similarly to the sprintf() and printf() functions in PHP. This method of the $wpdb class located in wp-includes/wp-db.php accepts two or more parameters:

§ $query—A string of your custom SQL statement with placeholders for each dynamic value.

§ $args—One or more additional parameters to be used to replace the placeholders in your SQL statement.

The following directives can be used in the SQL statement string:

§ %d (integer)

§ %f (float)

§ %s (string)

§ %% (literal percentage sign–-no argument needed)

The directives %d, %f, and %s should be left unquoted in the SQL statement, and each placeholder used needs to have a corresponding argument passed in for it. Literals (%) as part of the query must be properly written as %%:

$sqlQuery = $wpdb->prepare(“SELECT user_login FROM $wpdb->users WHERE

user_login LIKE %%%s%% OR

user_email LIKE %%%s%% OR

display_name LIKE %%%s%%”, $user_query, $user_query, $user_query);

$user_logins = $wpdb->get_col($sqlQuery);

NOTE

If you use $wpdb->prepare() without including the $args parameter, you will get a PHP warning message: “Missing argument 2 for wpdb::prepare()“. If your SQL doesn’t use any placeholder values, you don’t need to use prepare().

Holy percent sign, Batman! The % is used in SQL as a wildcard in SELECT statements when using the LIKE keyword. So if you searched for user_login LIKE %coleman%, it would return users with user logins like “jcoleman” and “jasoncoleman” and “coleman1982.” To keep theseliteral % signs in place with the prepare() method, we need to double them up to %%, which is translated into just one % in the final query.

The other % in there is used with %s, which is the placeholder where our $user_query parameter is going to be swapped in after being escaped.

You may have noticed we used the $wpdb->get_col() method in the previous code segment. WordPress offers many useful methods on the $wpdb object to SELECTs, INSERTs, and other common queries in MySQL.

SELECT queries with $wpdb

The WordPress $wpdb object has a few useful methods for selecting arrays, objects, rows, columns, or even single values out of the MySQL database using SQL queries.

$wpdb→get_results($query, $output_type) will run your query and return the last_results array, including all of the rows from your SQL query in the output type specified. By default, the result will be a “numerically indexed array of row objects.” Here’s the full list of output types from the WordPress Codex:

OBJECT

Result will be output as a numerically indexed array of row objects.

OBJECT_K

Result will be output as an associative array of row objects, using the first column’s values as keys (duplicates will be discarded).

ARRAY_A

Result will be output as an numerically indexed array of associative arrays, using column names as keys.

ARRAY_N

Result will be output as a numerically indexed array of numerically indexed arrays.

The following code helps show how to use the array returned by $wpdb->get_results() when using the OBJECT output type:

<?php

global $wpdb;

$sqlQuery = "SELECT * FROM $wpdb->posts

WHERE post_type = 'assignment'

AND post_status = 'publish' LIMIT 10";

$assignments = $wpdb->get_results( $sqlQuery );

// rows are stored in an array, use foreach to loop through them

foreach ( $assignments as $assignment ) {

// each item is an object with property names equal to the SQL column names?>

<h3><?php echo $assignment->post_title;?></h3>

<?php echo apply_filters( "the_content", $assignment->post_content );?>

<?php

}

?>

$wpdb→get_col($query, $collumn_offset = 0) will return an array of the values in the first column of the MySQL results. The $collumn_offset parameter can be used to grab other columns from the results (0 is the first, 1 is the second, and so on).

This function is most commonly used to grab IDs from a database table to be used in another function call or DB query:

<?php

global $wpdb;

$sqlQuery = "SELECT ID FROM $wpdb->posts

WHERE post_type = 'assignment'

AND post_status = 'publish'

LIMIT 10";

// getting IDs

$assignment_ids = $wpdb->get_col( $sqlQuery );

// result is an array, loop through them

foreach ( $assignment_ids as $assignment_id ) {

// we have the id, we can use get_post to get more data

$assignment = get_post( $assignment_id );

?>

<h3><?php echo $assignment->post_title;?></h3>

<?php echo apply_filters( "the_content", $assignment->post_content );?>

<?php

}

?>

Note that we’re putting that global $wpdb; line in most of our examples here to reinforce the point that you need to make sure that $wpdb is in scope before calling one of its methods. In practice, this line is usually at the top of the function or file you are working within.

$wpdb→get_row($query, $output_type, $row_offset) is used to get just one row from a result. Instead of getting an array of results, you will just get the first object (or array if the $output_type is specified) from the result set.

You can use the $row_offset parameter to grab a different row from the results (0 is the first, 1 is the second, and so on).

Insert, replace, and update

$wpdb→insert($table, $data, $format) can be used to insert data into the database. Rather than building your own INSERT query, you simply pass the table name and an associative array containing the row data and WordPress will build the query and escape it for you. The keys of your $data array must map to column names in the table. The values in the array are the values to insert into the table row:

<?php

// processing new submissions for assignments

global $wpdb, $current_user;

// create submission

$assignment_id = intval( $_REQUEST['assignment_id'] );

$submission_id = wp_insert_post(

array(

'post_type' => 'submission',

'post_author' => $current_user->ID,

'post_title' => sanitize_title( $_REQUEST['title'] ),

'post_content' => sanitize_text_field( $_POST['submission'] )

)

);

// connect the submission to the assignment

$wpdb->insert(

$wpdb->schoolpress_assignment_submissions,

array( "assignment_id"=>$assignment_id, "submission_id"=>$submission_id ),

array( '%d', '%d' )

);

/*

This insert call will generate a SQL query like:

INSERT INTO

'wp_schoolpress_assignment_submissions'

('assignment_id','submission_id' VALUES (101,10)

*/

?>

In the previous code, we use wp_insert_post() to create the submission then use $wpdb->insert() to insert a row into our custom table connecting assignments with submissions.

We pass an array of formats to the third parameter to tell the method to format the data as integers when constructing the SQL query. The available formats are %s for strings, %d for integers, and %f for floats. If no format is specified, all data will be formatted as a string. In most cases, MySQL will properly cast your string into the format needed to store it in the actual table.

To relate two posts like this, we could also simply put the assignment_id into the post_parent column of the wp_posts table. This is adequate to create a parent/child relationship. However, if you want to do a many-to-many relationship (e.g., if you can post the same submission to multiple assignments), you need a separate table or some other way to connect a post to many other posts.

$wpdb→replace($table, $data, $format) is similar to the $wpdb->insert() method. The $wpdb->replace() method will literally generate the same exact SQL query as $wpdb->insert() but uses the MySQL REPLACE command instead of INSERT, which will override any row with the same keys as the $data passed in.

$wpdb→update($table, $data, $where, $format = null, $where_format = null ) can be used to update rows in a database table. Rather than building your own UPDATE query, you simply pass the table and an associative array containing the updated columns and new data along with an associative array $where containing the fields to check against in the WHERE clause and WordPress will build the query and escape the UPDATE query for you.

The $where and $where_format parameters work the same as the $data and $format arrays, respectively.

The WHERE clause generated by the update() method will check that the columns are equal to the values passed and those checks are combined together by AND conditions.

The update() method is particularly useful in that you can update any number of fields in an table row using the same function. Here is some code that could be used to update orders in an ecommerce plugin:

<?php

global $wpdb;

// just update the status

$wpdb->update(

'ecommerce_orders', //table name

array( 'status' => 'paid' ), //data fields

array( 'id' => $order_id ) //where fields

);

// update more data about the order

$wpdb->update(

'ecommerce_orders', //table name

array( 'status' => 'pending', //data fields

'subtotal' => '100.00',

'tax' => '6.00',

'total' => '106.00'

),

array( 'id' => $order_id ) //where fields

);

?>

§ $wp_query—An object of the WP_Query class that can show you all of the post content returned by WordPress for any given page that you are on. We will talk more about the WP_Query class and its methods in the next chapter.

§ $current_user—An object of all of the data associated with the currently logged-in user. Not only does this object return all of the data for the current user from the wp_users table, but it will also tell you the roles and capabilities of the current user:

<?php

//welcome the logged-in user

global $current_user;

if ( !empty( $current_user->ID ) ) {

echo 'Howdy, ' . $current_user->display_name;

}

?>

When writing your own code to run on WordPress, you can define and use your own global variables if it makes sense. Global variables can save you the hassle of rewriting code and recalling functions because once they are defined, you can use them over and over again.

Action Hooks

WordPress developers hook for a living! Hooks are great and they make adding functionality into WordPress plugins and themes simple and easy. Any place an action hook, or technically a do_action() function, exists in code running on WordPress, you can insert your own code by calling the add_action() function and passing in the action hook name and your custom function with the code you want to run:

§ do_action( $tag, $arg );

§ $tag—The name of the action hook being executed.

§ $arg—One or more additional arguments that will get passed through to the function called from the add_action() function referencing this do_action() function. Say what? Keep reading…

You can create your own hooks in a theme or plugin by adding your own do_action() functions. However, most of the time you will be using established hooks in the WordPress core or other plugins. For example, let’s say we wanted to check if a user was logged in when WordPress first loads up but before any output is displayed to the browser. We can use the init hook:

<?php

add_action( 'init', 'my_user_check' );

function my_user_check() {

if ( is_user_logged_in() ) {

// do something because a user is logged in

}

}

?>

So what just happened? In the core of WordPress, there is an action hook, do_action(init), and we are calling a function called “my_user_check” from the add_action() function. At whatever point in time the code is being executed, when it gets to the init action hook, it will then run our custom my_user_check function to do whatever we want before continuing on.

Check out WordPress’s reference page for a list of the most used WordPress hooks.

Filters

Filters are kind of like action hooks in the sense that you can tap into them wherever they exist in WordPress. However, instead of inserting your own code where the hook or do_action() exists, you are filtering the returned value of existing functions that are using the apply_filters()function in WordPress core, plugins, and/or themes. In other words, by utilizing filters, you can hijack content before it is inserted into the database or before it is displayed to the browser as HTML:

§ apply_filters( $tag, $value, $var );

§ $tag—The name of the filter hook.

§ $value—The value that the filter can be applied on.

§ $var—Any additional variables, such as a string or an array, passed into the filter function.

If you search the core WordPress files for apply_filters you will find that the apply_filters() function is called all over the place, and like action hooks, the apply_filters() function can also be added to and called from any theme or plugin. Anywhere in code running on your WordPress site that you see the apply_filters() function being called, you can filter the value being returned by that function. For our example, we are going to filter the title of all posts before they are displayed to the browser. We can hook into any existing filters using theadd_filter() function:

§ add_filter( $tag, $function, $priority, $accepted_args );

§ $tag—The name of the filter hook you want to filter. This should match the $tag parameter of the apply_filters() function call you want to filter the results for.

§ $function—The name of the custom function used to actually filter the results.

§ $priority—This number sets the priority in which your add_filter will run compared to other places in the code that might be referencing the same filter hook tag. By default, this value is 10.

§ $accepted_args—You can set the number of parameters that your custom function that handles the filtering can except. The default is 1, which is the $value parameter of the apply_filters function.

OK, so how would real code for this look? Let’s start by adding a filter to alter the title of any post returned to the browser. We know of a filter hook for the_title that looks like this:

apply_filters( 'the_title', $title, $id );

$title is the title of the post and $id is the ID of the post:

<?php

add_filter( 'the_title', 'my_filtered_title', 10, 2 );

function my_filtered_title( $value, $id ) {

$value = '[' . $value . ']';

return $value;

}

?>

The preceding code should wrap any post titles in brackets. If your post title was “hello world,” it would now read “[hello world].” Note that we didn’t use the $id in our custom function. If we wanted to, we could have only applied the brackets to specific post IDs.

NOTE

While add_action() is meant to be used with do_action() hooks and add_filter() is meant to be used with apply_filters() hooks, the functions work the same way and are interchangeable. For readability, it is still a good idea to use the proper function depending on whether you intend to return a filtered result or just perform some code at a specific time.

Free Plugins

Let’s talk about some useful free plugins that can help extend your web application. There are plugins that exist for almost every purpose. In the event that you can’t find the exact functionality you are looking for in an existing plugin, you could always modify an existing plugin (open source right) or create an entirely new one if you are up for the challenge.

All in One SEO Pack

This is a great plugin to use if you are concerned about SEO (search engine optimization). This plugin was created by Semper Fi Web Design and once installed, it automatically optimizes your site for search engines. It also adds custom meta fields to each page and post that then allow you to add in custom titles as well as descriptions and keywords. There are pro or premium versions of the plugin that extend the functionality to allow for customization of search engine settings for each individual post or page as well as the option to set sitewide defaults in WordPress.

BadgeOS

This plugin can transform any website into a platform for rewarding members achievements based on their activities. It allows the site admin to create different achievement types and award the members sharable badges once they complete all the requirements to earn that particular achievement or achievements. Badges are Mozilla OBI compatible and sharable via Credly.com.

Custom Post Type UI

This is a very powerful plugin for building a web application. Custom Post Type UI allows you to create your own custom post types and taxonomies without touching any lines of code. We will be going over what custom post types and taxonomies are and how to register them in the next chapter, but you can use this plugin to get around writing your own code.

Posts 2 Posts

This is another very powerful plugin for building a web application. This plugin allows you to create many-to-many relationships between posts, pages, and custom post types as well as many-to-many relationships between posts and users.

For an example, you could use P2P to make connections between custom post types for schools, teachers, and subjects. A school could have multiple teachers, and each teacher could be tied to one or more subjects.

P2P provides intuitive settings, feature-rich widgets, and an easy-to-use meta box attached to any post add/edit page for making new connections.

Most of the time, custom plugin developers should avoid creating additional database tables unless it absolutely makes sense. If we wanted to connect posts to other posts, we could store an array of post IDs in a custom field of another post, but this can become inefficient in a large scale application. P2P creates its own database tables for storing the relationships between posts more efficiently.

Table 3-1. DB schema for wp_p2p table

Column

Type

Collation

Null

Default

Extra

p2p_id

bigint(20)

No

None

AUTO_INCREMENT

p2p_from

bigint(20)

No

None

p2p_to

bigint(20)

No

None

p2p_type

varchar(44)

utf8_general_ci

No

Table 3-2. DB schema for wp_p2pmeta table

Column

Type

Collation

Null

Default

Extra

meta_id

bigint(20)

No

None

AUTO_INCREMENT

p2p_id

bigint(20)

No

0

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

For more information on this plugin, make sure to check out the wiki on GitHub.

Members

Members extends the control that you have over user roles and capabilities in your site. It enables you to edit as well as create and delete user roles and capabilities. This plugin also allows you to set permissions for different user roles to determine which roles have the ability to add, edit, and/or delete various pieces of content. This is another must-have plugin for building an extensible web application because you can completely customize each user’s experience by defining and managing the roles and capabilities he will have access to.

W3 Total Cache

Caching your content is a great idea for optimizing the performance of your website. You can save a lot of processing time by displaying cached pages to the end user instead of querying the database every time someone requests data. W3 Total Cache has a lot of built-in features for managing what content gets cached and when the cache should be cleared.

Premium Plugins

Although there are a lot of great free plugins out there, there are also some premium plugins that are definitely worth the money. These plugins are usually available for purchase for one-time use, and some also offer developer licences that allow you to purchase the plugin for installation on multiple WordPress sites.

Gravity Forms

This plugin is an absolute must because it enables you to easily create custom contact forms for your site. It is extremely easy to create a form using the visual form editor, which allows you to drag and drop the fields you need into the form and reposition them as needed. Standard fields are included as well as the option to create your own custom fields. The forms are very flexible and can be set up as multiple page forms with progress bars. Conditional fields allow you to show or hide fields based on the user’s selections in previous fields. Another great feature of this plugin is the ability for the forms, once completed, to be forwarded anywhere as chosen by the site admin in the form settings. All in all, this plugin is extremely useful and flexible for anyone needing to create a form on their site and easy to use for someone without coding knowledge.

Backup Buddy

The Backup Buddy plugin provides you with the opportunity to back up your entire WordPress install for safekeeping, restoring, or moving your site. Backups can be scheduled on a recurring basis, and the file can then be downloaded to your computer, emailed to you, or sent off to the storage location of your choice, such as Dropbox or an FTP server. This plugin also features a restore option that will easily restore your themes, widgets, and plugins. The plugin also allows you to easily move your site to a new server or domain right from the WordPress dashboard, which comes in handy if you work on a dev server and then move the sites over to a production environment upon launch.

WP All Import

This plugin comes in handy if you are looking to import data into WordPress from another source that is in either an XML or CSV file, which are two formats not routinely accepted by WordPress. There is also a pro or premium version of the plugin available for purchase that extends the functionality to allow you to import data into custom post types as well as custom fields. The pro version also allows you to import images from a URL and have them saved in the media library. Another helpful feature is the ability to set up recurring imports that will periodically check a file for changes or updates and then modify the corresponding post as needed.

Community Plugins

You can build a full-blown social network with WordPress and a few free plugins. Social networks are great to bring a niche community together. If you have an active social network, you will have lots of organic content being indexed by search engines. If you think you get a lot of comments and interaction on your existing WordPress website, try turning it into a social network to really get the conversions flowing.

BuddyPress

BuddyPress is social networking in a box. You can start up a social network with most of the same features as Facebook in a matter of minutes.

You can download BuddyPress from the plugin repository like you would any other plugin, or you can get it from the BuddyPress website. This plugin has come a long way since version 1.0 was released in April of 2009. It was originally built by Andy Peatling and only worked on WordPress MU (Multi User) at the time. Automattic saw the potential of a plugin that turns WordPress into a social networking application and started funding the project.

Since version 1.7, BuddyPress has been theme agnostic, meaning you can turn it on and it will work with any theme—well, most themes—if coded properly. Prior to version 1.7 (see Figure 3-2), you needed to use a BuddyPress theme in order to properly use the plugin. This was good for people wanting to build a social network from scratch because they could use the default theme that comes with BuddyPress, purchase a nice premium BuddyPress child theme, or plan to build their own BuddyPress child theme. It was kind of limiting to people that already had a WordPress website because they couldn’t just turn on BuddyPress and have it work with their existing theme. In most cases, people with existing websites that wanted to turn on BuddyPress needed to do some customization, which is OK for someone who knows CSS, PHP, and how WordPress works. But noncoders would have to hire someone to turn their existing theme (which they may have already paid for) into a BuddyPress child or compatible theme. With newer version of BuddyPress, it just works!

Welcome to BuddyPress

Figure 3-2. Welcome to BuddyPress

People with existing websites can now turn on BuddyPress and any of its features and it should work in their existing theme. It is also very easy to override any of the existing styles to tailor the BuddyPress features more to your website. Special thanks to the more recent core contributors, John Jacoby, Boone Gorges, Paul Gibbs, and todo: real name RAY for making BuddyPress what it is today, a theme-independent plugin that turns WordPress into a social network.

Database tables

Unlike a lot of WordPress plugins, BuddyPress creates its own database tables in MySQL. If the original BuddyPress developers were to rewrite the plugin from scratch today, they would probably store activities and notifications as custom posts instead of using custom tables. However, custom post types weren’t implemented when the original version of BuddyPress was released and it would take a lot of effort to change that architecture now. The custom tables that store groups and friend relationships between users are much easier to understand and faster to query against that if these kinds of things were stored as some combination of posts, user meta, and taxonomies.

For smaller distributed plugins, it makes sense to avoid custom tables whenever possible because it means there is less overhead for users of the plugin to worry about. However, for plugins specific to your app or plugins that include as much functionality as BuddyPress, custom tables can help to speed up or better organize your data. We’ve included the schema for each BuddyPress table here (Table 3-3 through Table 3-18) as an example of how you might go about structuring custom tables for your own apps and also to help you understand how BuddyPress data is stored in case you would like to query for that information directly.

Table 3-3. DB schema for wp_bp_activity table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

user_id

bigint(20)

No

None

component

varchar(75)

utf8_general_ci

No

None

type

varchar(75)

utf8_general_ci

No

None

action

text

utf8_general_ci

No

None

content

longtext

utf8_general_ci

No

None

primary_link

varchar(255)

utf8_general_ci

No

None

item_id

bigint(20)

No

None

secondary_item_id

bigint(20)

Yes

NULL

date_recorded

datetime

No

None

hide_sitewide

tinyint(1)

Yes

0

mptt_left

int(11)

No

0

mptt_right

int(11)

No

0

is_spam

tinyint(1)

No

0

Table 3-4. DB schema for wp_bp_activity_meta table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

activity_id

bigint(20)

No

None

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

Table 3-5. DB schema for wp_bp_friends table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

initiator_user_id

bigint(20)

No

None

friend_user_id

bigint(20)

No

None

is_confirmed

tinyint(1)

Yes

0

is_limited

tinyint(1)

Yes

0

date_created

datetime

No

None

Table 3-6. DB schema for wp_bp_groups table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

creator_id

bigint(20)

No

None

name

varchar(100)

utf8_general_ci

No

None

slug

varchar(200)

utf8_general_ci

No

None

description

longtext

utf8_general_ci

No

None

status

varchar(100)

utf8_general_ci

No

Public

enable_forum

tinyint(1)

No

1

date_created

datetime

No

None

Table 3-7. DB schema for wp_bp_groups_groupmeta table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

group_id

bigint(20)

No

None

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

Table 3-8. DB schema for wp_bp_groups_members table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

group_id

bigint(20)

No

None

user_id

bigint(20)

No

None

inviter_id

bigint(20)

No

None

is_admin

tinyint(1)

No

0

is_mod

tinyint(1)

No

0

user_title

varchar(100)

utf8_general_ci

No

None

date_modified

datetime

No

None

comments

longtext

utf8_general_ci

No

None

is_confirmed

tinyint(1)

No

0

is_banned

tinyint(1)

No

0

invite_sent

tinyint(1)

No

0

Table 3-9. DB schema for wp_bp_messages_messages table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

thread_id

bigint(20)

No

None

sender_id

bigint(20)

No

None

subject

varchar(200)

utf8_general_ci

No

None

message

longtext

utf8_general_ci

No

None

date_sent

datetime

No

None

Table 3-10. DB schema for wp_bp_messages_notices table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

subject

varchar(200)

utf8_general_ci

No

None

message

longtext

utf8_general_ci

No

None

date_sent

datetime

No

None

is_active

tinyint(1)

No

0

Table 3-11. DB schema for wp_bp_messages_recipients table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

user_id

bigint(20)

No

None

thread_id

bigint(20)

No

None

unread_count

int(10)

No

0

sender_only

tinyint(1)

No

0

is_deleted

tinyint(1)

No

0

Table 3-12. DB schema for wp_bp_notifications table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

user_id

bigint(20)

No

None

item_id

bigint(20)

No

None

secondary_item_id

bigint(20)

Yes

NULL

component_name

varchar(75)

utf8_general_ci

No

None

component_action

varchar(75)

utf8_general_ci

No

None

date_notified

datetime

No

None

is_new

tinyint(1)

No

0

Table 3-13. DB schema for wp_bp_user_blogs table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

user_id

bigint(20)

No

None

blog_id

bigint(20)

No

None

Table 3-14. DB schema for wp_bp_user_blogs_blogmeta table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

blog_id

bigint(20)

No

None

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

Table 3-15. DB schema for wp_bp_xprofile_data table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

field_id

bigint(20)

No

None

user_id

bigint(20)

No

None

value

longtext

utf8_general_ci

No

None

last_updated

datetime

No

None

Table 3-16. DB schema for wp_bp_xprofile_fields table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

group_id

bigint(20)

No

None

parent_id

bigint(20)

No

None

type

varchar(150)

utf8_general_ci

No

None

name

varchar(150)

utf8_general_ci

No

None

description

longtext

utf8_general_ci

No

None

is_required

tinyint(1)

No

0

is_default_option

tinyint(1)

No

0

field_order

bigint(20)

No

0

option_order

bigint(20)

No

0

order_by

varchar(15)

utf8_general_ci

No

can_delete

tinyint(1)

No

1

Table 3-17. DB schema for wp_bp_xprofile_groups table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

name

varchar(150)

utf8_general_ci

No

None

description

mediumtext

utf8_general_ci

No

None

group_order

bigint(20)

No

0

can_delete

tinyint(1)

No

None

Table 3-18. DB schema for wp_bp_xprofile_meta table

Column

Type

Collation

Null

Default

Extra

id

bigint(20)

No

None

AUTO_INCREMENT

object_id

bigint(20)

No

None

object_type

varchar(150)

utf8_general_ci

No

None

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

Components

After activating BuddyPress, head on over to the Components panel in Settings → BuddyPress or /wp-admin/options-general.php?page=bp-components to set up what components you would like to use. See Figure 3-3 for an illustration of the panel.

You will see the following components:

Extended Profiles

Just like any typical social network, BuddyPress has member profiles. A member can join and have complete control of her own profile. Out of the box, all members are listed in a members directory; once you click on a member, you will be taken to her profile page.

Account Settings

Members can update their email address, change their password, and even manage the email notifications they will receive when other members interact with them.

Friend Connections

Members can add each other as friends. When one member requests to be friends with another, the other member will receive a friend request. Think Facebook friends.

Private Messaging

Members can send private messages to each other and view all of their messages in one place, like an inbox for your social network. Members can reply, mark as read, delete, and perform other actions with messages you might expect with any large social network.

Activity Streams

Members can post activity updates to their profiles and groups, leave comments on other members’ or groups’ activity, and favorite any activity post. Sounds kind of like Facebook, right? BuddyPress has an @mention feature that is kind of like when someone mentions you on Twitter. @mentions are automatically linked to the mentioned member’s profile page, and if that member doesn’t have his notifications turned off, he will receive an email about it. Activity also comes standard with RSS feeds.

User Groups

A very powerful component of BuddyPress, groups can be created organically (or not) by network members. Each group is listed on a Groups listing page, and clicking on that group’s avatar brings you to that group’s profile page. The group profile is set up very similar to the member profile page, but with group-specific subpages like group activity, members, admin settings, and invite friends. Groups can be public, private, or hidden and members can be promoted to group admins or group moderators.

Site Tracking

Any new posts and comments on your site will create BuddyPress activity posts. If you are running BuddyPress on a WordPress multisite network, any posts and comments created on any site in your network will also create BuddyPress activity posts.

All of these core BuddyPress components can be extended with BuddyPress plugins. It can be a little confusing if you are new to all of this, but you can install additional plugins specific to BuddyPress or build your own. There are approximately 485 WordPress plugins that extend or integrate with BuddyPress in one way or another.

BuddyPress components

Figure 3-3. BuddyPress components

Pages

Once you have decided which core components you want to use, go to the Pages tab at Settings → BuddyPress → Pages (shown in Figure 3-4). BuddyPress maps the components it is using to new or existing pages. By default, BuddyPress will try to make a new page for each component. If you wanted to call members “Students” instead of “Members,” you could create a regular WordPress page called “Students” and map the members component to this new page. The same goes for other BuddyPress components. You will notice two pages that need to be created for member registration, Register and Activate. You will need to map both of these to pages if you wish to have open registration on your social network.

BuddyPress pages

Figure 3-4. BuddyPress pages

CAUTION

To allow open registration, you will also have to make sure that anyone can register; check the “Anyone can register” checkbox under Settings → General.

Settings

In the Settings panel (Settings → BuddyPress → Settings) or /wp-admin/admin.php?page=bp-settings, you can configure some additional BuddyPress settings:

Toolbar

By default, BuddyPress shows the WordPress admin bar with a Login and Register link for non-logged-in users. If you would like to turn this off, you can do so here.

Account Deletion

You can decide if you want to allow registered users to be able to delete their accounts.

Avatar Uploads

Allow registered members to upload avatars.

Profile Syncing

Enable BuddyPress to WordPress profile syncing.

Group Creation

Allow your registered members to create their own groups. Site admins can still create groups if this setting is turned off.

Blog & Forum Comments

Allow activity stream commenting on blog and forum posts.

Profile fields

Located at Users → Profile Fields or /wp-admin/users.php?page=bp-profile-setup, this BuddyPress feature allows you to create any number of profile field groups and profile fields for your members. You can collect data such as location, date of birth, likes, dislikes, favorite color, and/or whatever you want. This feature is very flexible in allowing you to organize your profile fields into different profile groups, all of which will be made available on any member’s frontend profile page.

When adding any new profile field, you are provided with a slick UI for deciding to make your new field required or not, what type of form element it should be, what the default visibility is, and whether you want your members to be able to decide if they can change the visibility for the field. This form is shown in Figure 3-5.

NOTE

By default, all of the profile fields in the “Base” profile group will show up on the registration page.

BuddyPress profile fields

Figure 3-5. BuddyPress profile fields

BuddyPress plugins

As you can see, BuddyPress is a very intuitive and easy-to-use plugin. We talked briefly about installing additional BuddyPress specific plugins. Below is a quick list of some cool BuddyPress plugins so you can get an idea of how BuddyPress can be extended:

BuddyPress Toolbar

Adds a BuddyPress menu to the existing WordPress admin menu. This is a great plugin for administering your BuddyPress web application.

BuddyPress FollowMe

Allows your members to follow each other. This is kind of like the built-in friending functionality but more like a Twitter or Instagram approach where a member can follow other members that they are interested in. Each member will be able to see in her profile all of the activity of the other members she’s following.

BuddyPress Media

This plugin allows your members to upload photos, music, and videos to their activity posts. It also allows for your members to organize all of their photos into photo albums on their profile page. There is mobile device support that includes automatic audio and video conversion.

BuddyPress Registration Options

This is a great plugin for stopping spam bots from registering on your BuddyPress website! This plugin allows for new member moderation, if moderation is turned on from the admin settings page; any new members will be blocked from interacting with any BuddyPress components (except editing their own profile and uploading their avatar) and will not be listed in any directory until an administrator approves or denies their account. If moderation is turned on, admins can create custom display messages and email alert messages for approved or denied accounts. When an admin approves or denies, custom emails get sent out to new members telling them they were approved or denied.

BuddyMobile

This plugin automatically provides a slick UI for BuddyPress when browsing your site from a mobile device.

BadgeOS Community Add-on

The BadgeOS Community Add-on integrates BadgeOS features into BuddyPress and bbPress. Site members complete achievements and earn badges based on a range of community activity and triggers. This add-on to BadgeOS also includes the ability to display badges and achievements on user profiles and activity feeds.

bbPress

Got forums? bbPress can fulfill all of your forum needs. Unlike BuddyPress, bbPress utilizes custom post types, so it does not create its own tables in the database like it used to in prior versions.

Using bbPress can require a bit of theme work if your theme isn’t already styled to support bbPress, but it is by far the easiest way to add forum functionality to a WordPress site.


[7] In the context of the GPL, distribution means selling your source code or offering it for download on a website like the WordPress.org plugin repository. Code that you personally install for someone does not need to inherit the GPL license.

[8] It would take a synced entry in both the wp_usermeta and wp_postmeta tables to provide the same lookup ability a single wp_schoolpress_assignment_submissions table offers.