Advertisement
Plugins

How to Create a WordPress Avatar Management Plugin from Scratch: Finishing Touches

by

Avatar Manager for WordPress is a sweet and simple plugin for storing avatars locally and more. Easily.

Enhance your WordPress website by letting your users choose between using Gravatar or a self-hosted avatar image right from their profile screen. Improved workflow, on-demand image generation and custom user permissions under a native interface. Say hello to the Avatar Manager plugin.


A Quick Recap

In the first part of our tutorial, we reviewed:

  • what is a WordPress plugin;
  • how to create a basic WordPress plugin, choose an appropriate license and format for the version number;
  • what are action and filter hooks and how to use them to create our plugin;
  • how to add new settings to existing settings screens;
  • how to make a plugin more flexible by using custom options.

Today, we'll take things further and wrap up our plugin: we'll handle avatar uploads and on-demand image generation, internationalize our plugin and much more.


Step 1. Resizing an Avatar Image

Let's start by writing the following function:

/**
 * Generates a resized copy of the specified avatar image.
 *
 * @uses wp_upload_dir() For retrieving path information on the currently
 * configured uploads directory.
 * @uses wp_basename() For i18n friendly version of basename().
 * @uses wp_get_image_editor() For retrieving a WP_Image_Editor instance and
 * loading a file into it.
 * @uses is_wp_error() For checking whether the passed variable is a WordPress
 * Error.
 * @uses do_action() For calling the functions added to an action hook.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param string $url URL of the avatar image to resize.
 * @param int $size Size of the new avatar image.
 * @return array Array with the URL of the new avatar image.
 */
function avatar_manager_avatar_resize( $url, $size ) {
	// Retrieves path information on the currently configured uploads directory.
	$upload_dir = wp_upload_dir();

	$filename  = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $url );
	$pathinfo  = pathinfo( $filename );
	$dirname   = $pathinfo['dirname'];
	$extension = $pathinfo['extension'];

	// i18n friendly version of basename().
	$basename = wp_basename( $filename, '.' . $extension );

	$suffix    = $size . 'x' . $size;
	$dest_path = $dirname . '/' . $basename . '-' . $suffix . '.' . $extension;
	$avatar    = array();

	if ( file_exists( $dest_path ) ) {
		$avatar['url']  = str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $dest_path );
		$avatar['skip'] = true;
	} else {
		// Retrieves a WP_Image_Editor instance and loads a file into it.
		$image = wp_get_image_editor( $filename );

		if ( ! is_wp_error( $image ) ) {
			// Resizes current image.
			$image->resize( $size, $size, true );

			// Saves current image to file.
			$image->save( $dest_path );

			$avatar['url']  = str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $dest_path );
			$avatar['skip'] = false;
		}
	}

	// Calls the functions added to avatar_manager_avatar_resize action hook.
	do_action( 'avatar_manager_avatar_resize', $url, $size );

	return $avatar;
}

Summary

  • The avatar_manager_avatar_resize() function generates a resized copy of the specified avatar image.
  • The wp_upload_dir() call returns an array containing path information on the currently configured uploads directory.
  • The str_replace() function replaces all occurrences of the search string with the replacement string.
  • The pathinfo() function returns information about a file path.
  • The wp_basename() function is the i18n friendly version of basename() which returns the trailing name component of a path.
  • The file_exists() function checks whether a file or directory exists.
  • The skip flag is set to true if the destination image file already exists, else a new image is generated.
  • The wp_get_image_editor() function returns a WP_Image_Editor instance and loads a file into it. With that we can manipulate the image by calling methods on it.
  • The is_wp_error() function checks whether the passed variable is a WordPress error.
  • Then, we resize and save the image by calling the resize() and save() methods of the $image object.
  • The do_action() executes a hook created by add_action(); this allows themes and plugins to hook to the avatar_manager_avatar_resize action which is triggered after resizing an avatar image.

Step 2. Deleting an Avatar Image

Before taking care of profile updates, we need to define one more function:

/**
 * Deletes an avatar image based on attachment ID.
 *
 * @uses get_post_meta() For retrieving attachment meta fields.
 * @uses wp_upload_dir() For retrieving path information on the currently
 * configured uploads directory.
 * @uses delete_post_meta() For deleting attachment meta fields.
 * @uses get_users() For retrieving an array of users.
 * @uses delete_user_meta() For deleting user meta fields.
 * @uses do_action() For calling the functions added to an action hook.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param int $attachment_id An attachment ID
 */
function avatar_manager_delete_avatar( $attachment_id ) {
	// Retrieves attachment meta field based on attachment ID.
	$is_custom_avatar = get_post_meta( $attachment_id, '_avatar_manager_is_custom_avatar', true );

	if ( ! $is_custom_avatar )
		return;

	// Retrieves path information on the currently configured uploads directory.
	$upload_dir = wp_upload_dir();

	// Retrieves attachment meta field based on attachment ID.
	$custom_avatar = get_post_meta( $attachment_id, '_avatar_manager_custom_avatar', true );

	if ( is_array( $custom_avatar ) ) {
		foreach ( $custom_avatar as $file ) {
			if ( ! $file['skip'] ) {
				$file = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $file['url'] );
				@unlink( $file );
			}
		}
	}

	// Deletes attachment meta fields based on attachment ID.
	delete_post_meta( $attachment_id, '_avatar_manager_custom_avatar' );
	delete_post_meta( $attachment_id, '_avatar_manager_custom_avatar_rating' );
	delete_post_meta( $attachment_id, '_avatar_manager_is_custom_avatar' );

	// An associative array with criteria to match.
	$args = array(
		'meta_key'   => 'avatar_manager_custom_avatar',
		'meta_value' => $attachment_id
	);

	// Retrieves an array of users matching the criteria given in $args.
	$users = get_users( $args );

	foreach ( $users as $user ) {
		// Deletes user meta fields based on user ID.
		delete_user_meta( $user->ID, 'avatar_manager_avatar_type' );
		delete_user_meta( $user->ID, 'avatar_manager_custom_avatar' );
	}

	// Calls the functions added to avatar_manager_delete_avatar action hook.
	do_action( 'avatar_manager_delete_avatar', $attachment_id );
}

add_action( 'delete_attachment', 'avatar_manager_delete_avatar' );

Summary

  • The delete_attachment action hook is called when an attachment is deleted by wp_delete_attachment().
  • The get_post_meta() returns the values of the custom fields with the specified key from the specified post. First, we test if the attachment with the specified ID is an avatar image.
  • The is_array() call finds whether a variable is an array.
  • Then, we use the unlink() function to delete the avatar image including its resized copies, but skipping those with the skip flag set to true.
  • The delete_post_meta() function deletes all custom fields with the specified key from the specified post.
  • The get_users() function retrieves an array of users matching the criteria given in $args.
  • The delete_user_meta() function removes metadata matching criteria from a user.
  • Lastly, we execute the avatar_manager_delete_avatar action hook.

Step 3. Updating a User Profile

When updating a user profile, we need not only to save the options changed by the user but to handle avatar uploads and removals too. Let's do it:

/**
 * Updates user profile based on user ID.
 *
 * @uses avatar_manager_get_options() For retrieving plugin options.
 * @uses sanitize_text_field() For sanitizing a string from user input or from
 * the database.
 * @uses update_user_meta() For updating user meta fields.
 * @uses get_user_meta() For retrieving user meta fields.
 * @uses update_post_meta() For updating attachment meta fields.
 * @uses wp_handle_upload() For handling PHP uploads in WordPress.
 * @uses wp_die() For killing WordPress execution and displaying HTML error
 * message.
 * @uses __() For retrieving the translated string from the translate().
 * @uses avatar_manager_delete_avatar() For deleting an avatar image.
 * @uses wp_insert_attachment() For inserting an attachment into the media
 * library.
 * @uses wp_generate_attachment_metadata() For generating metadata for an
 * attachment.
 * @uses wp_update_attachment_metadata() For updating metadata for an
 * attachment.
 * @uses avatar_manager_avatar_resize() For generating a resized copy of the
 * specified avatar image.
 * @uses avatar_manager_delete_avatar() For deleting an avatar image based on
 * attachment ID.
 * @uses get_edit_user_link() For getting the link to the users edit profile
 * page in the WordPress admin.
 * @uses add_query_arg() For retrieving a modified URL (with) query string.
 * @uses wp_redirect() For redirecting the user to a specified absolute URI.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param int $user_id User to update.
 */
function avatar_manager_edit_user_profile_update( $user_id ) {
	// Retrieves plugin options.
	$options = avatar_manager_get_options();

	// Sanitizes the string from user input.
	$avatar_type = isset( $_POST['avatar_manager_avatar_type'] ) ? sanitize_text_field( $_POST['avatar_manager_avatar_type'] ) : 'gravatar';

	// Updates user meta field based on user ID.
	update_user_meta( $user_id, 'avatar_manager_avatar_type', $avatar_type );

	// Retrieves user meta field based on user ID.
	$custom_avatar = get_user_meta( $user_id, 'avatar_manager_custom_avatar', true );

	if ( ! empty( $custom_avatar ) ) {
		// Sanitizes the string from user input.
		$custom_avatar_rating = isset( $_POST['avatar_manager_custom_avatar_rating'] ) ? sanitize_text_field( $_POST['avatar_manager_custom_avatar_rating'] ) : 'G';

		// Updates attachment meta field based on attachment ID.
		update_post_meta( $custom_avatar, '_avatar_manager_custom_avatar_rating', $custom_avatar_rating );
	}

	...
}

add_action( 'edit_user_profile_update', 'avatar_manager_edit_user_profile_update' );
add_action( 'personal_options_update', 'avatar_manager_edit_user_profile_update' );

Summary

Handling Avatar Uploads

To handle avatar uploads, write the following code:

if ( isset( $_POST['avatar-manager-upload-avatar'] ) && $_POST['avatar-manager-upload-avatar'] ) {
	if ( ! function_exists( 'wp_handle_upload' ) )
		require_once( ABSPATH . 'wp-admin/includes/file.php' );

	// An associative array with allowed MIME types.
	$mimes = array(
		'bmp'  => 'image/bmp',
		'gif'  => 'image/gif',
		'jpe'  => 'image/jpeg',
		'jpeg' => 'image/jpeg',
		'jpg'  => 'image/jpeg',
		'png'  => 'image/png',
		'tif'  => 'image/tiff',
		'tiff' => 'image/tiff'
	);

	// An associative array to override default variables.
	$overrides = array(
		'mimes'     => $mimes,
		'test_form' => false
	);

	// Handles PHP uploads in WordPress.
	$avatar = wp_handle_upload( $_FILES['avatar_manager_import'], $overrides );

	if ( isset( $avatar['error'] ) )
		// Kills WordPress execution and displays HTML error message.
		wp_die( $avatar['error'],  __( 'Image Upload Error', 'avatar-manager' ) );

	if ( ! empty( $custom_avatar ) )
		// Deletes users old avatar image.
		avatar_manager_delete_avatar( $custom_avatar );

	// An associative array about the attachment.
	$attachment = array(
		'guid'           => $avatar['url'],
		'post_content'   => $avatar['url'],
		'post_mime_type' => $avatar['type'],
		'post_title'     => basename( $avatar['file'] )
	);

	// Inserts the attachment into the media library.
	$attachment_id = wp_insert_attachment( $attachment, $avatar['file'] );

	// Generates metadata for the attachment.
	$attachment_metadata = wp_generate_attachment_metadata( $attachment_id, $avatar['file'] );

	// Updates metadata for the attachment.
	wp_update_attachment_metadata( $attachment_id, $attachment_metadata );

	$custom_avatar = array();

	// Generates a resized copy of the avatar image.
	$custom_avatar[ $options['default_size'] ] = avatar_manager_avatar_resize( $avatar['url'], $options['default_size'] );

	// Updates attachment meta fields based on attachment ID.
	update_post_meta( $attachment_id, '_avatar_manager_custom_avatar', $custom_avatar );
	update_post_meta( $attachment_id, '_avatar_manager_custom_avatar_rating', 'G' );
	update_post_meta( $attachment_id, '_avatar_manager_is_custom_avatar', true );

	// Updates user meta fields based on user ID.
	update_user_meta( $user_id, 'avatar_manager_avatar_type', 'custom' );
	update_user_meta( $user_id, 'avatar_manager_custom_avatar', $attachment_id );
}

Summary

  • The function_exists() returns true if the given function has been defined, then the require_once() statement checks if the specified file has already been included, and if so, doesn't include it again.
  • The wp_handle_upload() function handles PHP uploads in WordPress, sanitizing file names, checking extensions for mime type, and moving the file to the appropriate directory within the uploads directory.
  • Before adding the new avatar image, we call the avatar_manager_delete_avatar() function to delete the old avatar, if any is set.
  • The wp_insert_attachment() function inserts an attachment into the media library.
  • The wp_generate_attachment_metadata() function generates metadata for an image attachment; it also creates a thumbnail and other intermediate sizes of the image attachment based on the sizes defined on the Settings Media Screen.
  • The wp_update_attachment_metadata() function updates metadata for an attachment.
  • Next, we call the avatar_manager_avatar_resize() function to generate a copy of the avatar image at default size.
  • Lastly, we update the metadata for the attachment and for the user currently being edited.

Removing an Avatar Image

Now, it's time to make the plugin to actually delete an avatar image when requested:

if ( isset( $_GET['avatar_manager_action'] ) && $_GET['avatar_manager_action'] ) {
	global $wp_http_referer;

	$action = $_GET['avatar_manager_action'];

	switch ( $action ) {
		case 'remove-avatar':
			// Deletes avatar image based on attachment ID.
			avatar_manager_delete_avatar( $_GET['avatar_manager_custom_avatar'] );

			break;
	}

	// Gets the link to the users edit profile page in the WordPress admin.
	$edit_user_link = get_edit_user_link( $user_id );

	// Retrieves a modified URL (with) query string.
	$redirect = add_query_arg( 'updated', true, $edit_user_link );

	if ( $wp_http_referer )
		// Retrieves a modified URL (with) query string.
		$redirect = add_query_arg( 'wp_http_referer', urlencode( $wp_http_referer ), $redirect );

	// Redirects the user to a specified absolute URI.
	wp_redirect( $redirect );

	exit;
}

Summary

  • If the value of the requested action is remove-avatar we call the avatar_manager_delete_avatar() to delete the specified avatar image.
  • The get_edit_user_link() function gets the link to the users edit profile page in the WordPress admin.
  • The urlencode() function encodes a string to be used in a query part of a URL.
  • At the end of the function, we call the wp_redirect() function to redirect the user back to the updated user profile.
  • The exit call terminates the execution of the script; it's a language construct and it can be called without parentheses if no status is passed.

Step 4. Retrieving a Custom Avatar Image

Next, we're going to write a helper function for retrieving a custom avatar image:

/**
 * Returns user custom avatar based on user ID.
 *
 * @uses get_option() For getting values for a named option.
 * @uses avatar_manager_get_options() For retrieving plugin options.
 * @uses get_userdata() For retrieving user data by user ID.
 * @uses is_ssl() For checking if SSL is being used.
 * @uses add_query_arg() For retrieving a modified URL (with) query string.
 * @uses esc_attr() For escaping HTML attributes.
 * @uses get_user_meta() For retrieving user meta fields.
 * @uses get_post_meta() For retrieving attachment meta fields.
 * @uses wp_get_attachment_image_src() For retrieving an array with the image
 * attributes "url", "width" and "height", of an image attachment file.
 * @uses avatar_manager_avatar_resize() For generating a resized copy of the
 * specified avatar image.
 * @uses update_post_meta() For updating attachment meta fields.
 * @uses apply_filters() For calling the functions added to a filter hook.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param int $user_id User to update.
 * @param int $size Size of the avatar image
 * @param string $default URL to a default image to use if no avatar is
 * available.
 * @param string $alt Alternative text to use in image tag. Defaults to blank.
 * @return string <img> tag for the user's avatar.
 */
function avatar_manager_get_custom_avatar( $user_id, $size = '', $default = '', $alt = false ) {
	// Returns if showing avatars is not enabled.
	if ( ! get_option( 'show_avatars' ) )
		return false;

	// Retrieves plugin options.
	$options = avatar_manager_get_options();

	if ( empty( $size ) || ! is_numeric( $size ) ) {
		$size = $options['avatar-manager-default-size'];
	} else {
		$size = absint( $size );

		if ( $size < 1 )
			$size = 1;
		elseif ( $size > 512 )
			$size = 512;
	}

	// Retrieves user data by user ID.
	$user = get_userdata( $user_id );

	// Returns if no user data was retrieved.
	if ( empty( $user ) )
		return false;

	$email = $user->user_email;

	if ( empty( $default ) ) {
		// Retrieves values for the named option.
		$avatar_default = get_option( 'avatar_default' );

		if ( empty( $avatar_default ) )
			$default = 'mystery';
		else
			$default = $avatar_default;
	}

	$email_hash = md5( strtolower( trim( $email ) ) );

	if ( is_ssl() )
		$host = 'https://secure.gravatar.com';
	else
		$host = sprintf( 'http://%d.gravatar.com', ( hexdec( $email_hash[0] ) % 2 ) );

	if ( $default == 'mystery' )
		$default = $host . '/avatar/ad516503a11cd5ca435acc9bb6523536?s=' . $size;
	elseif ( $default == 'gravatar_default' )
		$default = '';
	elseif ( strpos( $default, 'http://' ) === 0 )
		// Retrieves a modified URL (with) query string.
		$default = add_query_arg( 's', $size, $default );

	if ( $alt === false )
		$alt = '';
	else
		// Escapes HTML attributes.
		$alt = esc_attr( $alt );

	// Retrieves values for the named option.
	$avatar_rating = get_option( 'avatar_rating' );

	// Retrieves user meta field based on user ID.
	$custom_avatar = get_user_meta( $user_id, 'avatar_manager_custom_avatar', true );

	// Returns if no attachment ID was retrieved.
	if ( empty( $custom_avatar ) )
		return false;

	// Retrieves attachment meta field based on attachment ID.
	$custom_avatar_rating = get_post_meta( $custom_avatar, '_avatar_manager_custom_avatar_rating', true );

	$ratings['G']  = 1;
	$ratings['PG'] = 2;
	$ratings['R']  = 3;
	$ratings['X']  = 4;

	if ( $ratings[ $custom_avatar_rating ] <= $ratings[ $avatar_rating ] ) {
		// Retrieves attachment meta field based on attachment ID.
		$avatar = get_post_meta( $custom_avatar, '_avatar_manager_custom_avatar', true );

		if ( empty( $avatar[ $size ] ) ) {
			// Retrieves an array with the image attributes "url", "width"
			// and "height", of the image attachment file.
			$url = wp_get_attachment_image_src( $custom_avatar, 'full' );

			// Generates a resized copy of the avatar image.
			$avatar[ $size ] = avatar_manager_avatar_resize( $url[0], $size );

			// Updates attachment meta field based on attachment ID.
			update_post_meta( $custom_avatar, '_avatar_manager_custom_avatar', $avatar );
		}

		$src    = $avatar[ $size ]['url'];
		$avatar = '<img alt="' . $alt . '" class="avatar avatar-' . $size . ' photo avatar-default" height="' . $size . '" src="' . $src . '" width="' . $size . '">';
	} else {
		$src  = $host . '/avatar/';
		$src .= $email_hash;
		$src .= '?s=' . $size;
		$src .= '&d=' . urlencode( $default );
		$src .= '&forcedefault=1';

		$avatar = '<img alt="' . $alt . '" class="avatar avatar-' . $size . ' photo avatar-default" height="' . $size . '" src="' . $src . '" width="' . $size . '">';
	}

	// Calls the functions added to avatar_manager_get_custom_avatar
	// filter hook.
	return apply_filters( 'avatar_manager_get_custom_avatar', $avatar, $user_id, $size, $default, $alt );
}

Summary

  • The avatar_manager_get_custom_avatar function returns a custom avatar image based on user ID or false if showing avatars is not enabled. The function retrieves plugin options, sanitizes the $size parameter and escapes HTML attributes from $alt variable.
  • Then, it retrieves a default image to use instead of the avatar image if the avatar rating doesn't match. A resized copy of the avatar image is generated on-demand if the requested size doesn't match an existing image file.
  • The get_userdata() function returns a WP_User object with the information pertaining to the user whose ID is passed to it.
  • The md5() function returns the MD5 hash for the provided string.
  • The strtolower() function returns the provided string but with all alphabetic characters converted to lowercase.
  • The is_ssl() call checks if SSL is being used.
  • The sprintf() function returns a formatted string.
  • The hexdec() function returns the decimal equivalent of the specified hexadecimal number.
  • The call strpos() finds the numeric position of the first occurrence of needle in the haystack string.
  • The wp_get_attachment_image_src() function returns an array with the image attributes url, width and height, of an image attachment file.
  • Lastly, we use the apply_filters() function to call the functions added to the avatar_manager_get_custom_avatar filter hook.

Step 5. Retrieving an Avatar Image

Basically, the next function is the main function of our plugin:

/**
 * Returns the avatar for a user who provided a user ID or email address.
 *
 * @uses get_option() For getting values for a named option.
 * @uses avatar_manager_get_options() For retrieving plugin options.
 * @uses get_userdata() For retrieving user data by user ID.
 * @uses avatar_manager_get_custom_avatar() For retrieving user custom avatar
 * based on user ID.
 * @uses apply_filters() For calling the functions added to a filter hook.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param int|string|object $id_or_email A user ID, email address, or comment
 * object.
 * @param int $size Size of the avatar image
 * @param string $default URL to a default image to use if no avatar is
 * available.
 * @param string $alt Alternative text to use in image tag. Defaults to blank.
 * @return string <img> tag for the user's avatar.
 */
function avatar_manager_get_avatar( $avatar = '', $id_or_email, $size = '', $default = '', $alt = false ) {
	// Returns if showing avatars is not enabled.
	if ( ! get_option( 'show_avatars' ) )
		return false;

	// Retrieves plugin options.
	$options = avatar_manager_get_options();

	if ( empty( $size ) || ! is_numeric( $size ) ) {
		$size = $options['avatar-manager-default-size'];
	} else {
		$size = absint( $size );

		if ( $size < 1 )
			$size = 1;
		elseif ( $size > 512 )
			$size = 512;
	}

	$email = '';

	if ( is_numeric( $id_or_email ) ) {
		$id = (int) $id_or_email;

		// Retrieves user data by user ID.
		$user = get_userdata( $id );

		if ( $user )
			$email = $user->user_email;
	} elseif ( is_object( $id_or_email ) ) {
		if ( ! empty( $id_or_email->user_id ) ) {
			$id = (int) $id_or_email->user_id;

			// Retrieves user data by user ID.
			$user = get_userdata( $id );

			if ( $user )
				$email = $user->user_email;
		} elseif ( ! empty( $id_or_email->comment_author_email ) ) {
			$email = $id_or_email->comment_author_email;
		}
	} else {
		$email = $id_or_email;

		if ( $id = email_exists( $email ) )
			// Retrieves user data by user ID.
			$user = get_userdata( $id );
	}

	if ( isset( $user ) )
		$avatar_type = $user->avatar_manager_avatar_type;
	else
		return $avatar;

	if ( $avatar_type == 'custom' )
		// Retrieves user custom avatar based on user ID.
		$avatar = avatar_manager_get_custom_avatar( $user->ID, $size, $default, $alt );

	// Calls the functions added to avatar_manager_get_avatar filter hook.
	return apply_filters( 'avatar_manager_get_avatar', $avatar, $id_or_email, $size, $default, $alt );
}

add_filter( 'get_avatar', 'avatar_manager_get_avatar', 10, 5 );

Summary

  • The avatar_manager_get_avatar() function returns the avatar for a user who provided a user ID or email address, or false if showing avatars is not enabled.
  • We use the get_avatar filter to change the output of the get_avatar() function. Our function retrieves plugin options, sanitizes the $size parameter and finds the ID of the specified user.
  • Then, it returns the result of the avatar_manager_get_custom_avatar() function call, or the unmodified output of the get_avatar() if the user doesn't use a custom avatar.
  • The is_object() call finds whether a variable is an object.
  • The email_exists() function checks whether or not a given email address has already been registered to a username, and returns that users ID, or false if none exists.

To test the result, go to the Users -> Your Profile Screen.

The Avatar Manager plugin options under the User Your Profile Screen
The Avatar Manager plugin options under the User Your Profile Screen

Browse for an image an upload it. Now, you should be able to choose between using Gravatar or the custom avatar image you just uploaded.


Step 6. Removing Unnecessary Filter Hooks

If you go to the Settings Discussion Screen you'll notice that the avatars from the Default Avatar setting are replaced with your custom avatar. To fix this issue, we'll restore the default avatars by removing our custom function when it isn't needed with the help of the avatar_defaults filter hook. To do so, add the following code:

/**
 * Prevents custom avatars from being applied to the Default Avatar setting.
 *
 * @uses remove_filter() For removing a function attached to a specified action
 * hook.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param array $avatar_defaults An associative array with default avatars.
 * @return array An associative array with default avatars.
 */
function avatar_manager_avatar_defaults( $avatar_defaults ) {
	// Removes the avatar_manager_get_avatar function attached to get_avatar
	// action hook.
	remove_filter( 'get_avatar', 'avatar_manager_get_avatar' );

	return $avatar_defaults;
}

add_filter( 'avatar_defaults', 'avatar_manager_avatar_defaults', 10, 1 );

To prevent custom avatars from being applied to the Default Avatar setting, we call the remove_filter() function. It removes a function attached to a specified filter hook. This method can be used to remove default functions attached to a specific filter hook and possibly replace them with a substitute.


Step 7. Displaying Custom Media States

The Media Library Screen allows you to edit, view, and delete images, video, recordings, and files previously uploaded to your blog. To identify an attachment being used as an avatar image, we're going to display a custom media state for it:

/**
 * Displays media states for avatar images.
 *
 * @uses get_post_meta() For retrieving attachment meta fields.
 * @uses __() For retrieving the translated string from the translate().
 * @uses apply_filters() For calling the functions added to a filter hook.
 *
 * @since Avatar Manager 1.0.0
 *
 * @param array $media_states An associative array with media states.
 * @return array An associative array with media states.
 */
function avatar_manager_display_media_states( $media_states ) {
	global $post;

	// Retrieves attachment meta field based on attachment ID.
	$meta_avatar = get_post_meta( $post->ID, '_avatar_manager_is_custom_avatar', true );

	if ( ! empty( $meta_avatar ) )
		$media_states[] = __( 'Avatar Image', 'avatar-manager' );

	// Calls the functions added to avatar_manager_display_media_states filter
	// hook.
	return apply_filters( 'avatar_manager_display_media_states', $media_states );
}

add_filter( 'display_media_states', 'avatar_manager_display_media_states', 10, 1 );

The display_media_states filter is used to display custom media states for attachmets that have been added to the Media Library. We use the $post global variable to grab the ID of the current attachment. If the _avatar_manager_is_custom_avatar custom field isn't empty, the attachment is an avatar image so we add a custom media state for it.

If you don't have any custom avatar image set up, upload one and go to the Media Library Screen.

The Avatar Manager plugin media states under the Media Library Screen
The Avatar Manager plugin media states under the Media Library Screen

Notice that each attachment being used as a custom avatar image does have the Avatar Image string appended next to its filename.


Step 8. Adding the Uninstaller

In order to handle the uninstall process, a plugin should create a file named uninstall.php in the base plugin directory rather than using register_uninstall_hook(). This file will be called, if it exists, during the uninstall process bypassing the uninstall hook. To do so, open avatar-manager/uninstall.php and add the following code:

<?php
/**
 * @package Avatar_Manager
 * @subpackage Uninstaller
 */

// Exits if uninstall is not called from WordPress.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) )
	exit;

if ( ! function_exists( 'avatar_manager_delete_avatar' ) )
	include_once( 'avatar-manager.php' );

// Deletes plugin options.
delete_option( 'avatar_manager' );

// An associative array with criteria to match.
$args = array(
	'meta_key' => 'avatar_manager_custom_avatar'
);

// Retrieves an array of users matching the criteria given in $args.
$users = get_users( $args );

foreach ( $users as $user ) {
	// Deletes avatar image based on attachment ID.
	avatar_manager_delete_avatar( $user->avatar_manager_custom_avatar );
}
?>

When using uninstall.php the plugin should always check for the WP_UNINSTALL_PLUGIN constant, before executing. The WP_UNINSTALL_PLUGIN constant is defined by WordPress at runtime during a plugin uninstall and it will not be present if uninstall.php is requested directly. The defined() checks whether a given named constant exists. The include_once statement includes and evaluates the specified file during the execution of the script; if the code from a file has already been included, it will not be included again. The delete_option() function removes a named option from the options database table.


Step 9. Internationalizing and Translating the Plugin

Once you have the programming for your plugin done, another consideration is internationalization. Internationalization, often abbreviated as i18n, is the process of setting up software so that it can be localized; localization, or l10n, is the process of translating text displayed by the software into different languages. WordPress uses the gettext libraries and tools for i18n.

It is highly recommended that you internationalize your plugin, so that users from different countries can localize it.

Translatable Strings

In order to make a string translatable, you have to just wrap the original string in a __() function call. If your code should echo the string to the browser, use the _e() function instead. As you might have noticed, we've already done that in our plugin.

Text Domains

A text domain is a unique identifier, which makes sure WordPress can distinguish between all loaded translations. Using the basename of your plugin is always a good choice. You can load the plugin's translated strings by calling the load_plugin_textdomain() function, which we've already done in the first part of our tutorial.

PO Files

Now, we need to make a .po file for translators. To do this, we'll use the Poedit translation software. Once you've downloaded it, click File -> New Catalog... to setup a new catalog. A new window should open up. Go to the Project info tab and enter Avatar Manager as the project name.

Poedit's Project info tab under the Settings window
Poedit's Project info tab under the Settings window

On the Paths tab, let’s leave the base path as . which refers to the directory in which the catalog is.

Poedit's Paths tab under the Settings window
Poedit's Paths tab under the Settings window

Next, go to the Keywords tab. Remove all the items there, and add in these keywords: __, _e, _n and _x.

Poedit's Keywords tab under the Settings window
Poedit's Keywords tab under the Settings window

Press OK and save the file as avatar-manager/languages/avatar-manager-default.po. Now, the file is ready for translation.

Poedit's main window
Poedit's main window

Translate all the strings you want and then save the file as avatar-manager/languages/avatar-manager-{locale}.po. The locale is the language code and/or country code you defined in the constant WPLANG in the file wp-config.php.

MO Files

A .mo file is a binary file which contains all the original strings and their translations in a format suitable for fast translation extraction. The conversion is done automatically if you go to Edit -> Preferences -> Editor and check Automatically compile .mo file on save.


Step 10. Releasing and Promoting a Plugin

This section goes through the steps for taking a plugin that you've created and getting it distributed widely.

Submitting to the WordPress Plugin Directory

The fastest, easiest and best way to get your plugin out there is to upload your plugin to the WordPress Plugin Directory. For more details about submitting your plugin see the about page or skip straight to the plugin submission page.

Promoting and Documenting Your Plugin

To submit and promote your plugin to the WordPress Community, first create a page for the plugin on your site with complete and well written instructions. Include a link to this explanation page in the plugin's header, so people can easily check for updates and more information and help.

If you choose to submit your plugin to the WordPress Plugin Directory, also make this information as clear as possible so they can categorize and help others understand the usage of your plugin. You also need to create a readme.txt file in a standard format, and include it with your plugin.


Conclusion

This closes our tutorial; now, we have a fully working plugin and learned some practical tips and tricks about WordPress plugin development. The idea behind this plugin started as a feature requested in WordPress core; I would love to hear your thoughts about it. Does it improve the current workflow? Would you find a similar approach useful but on managing Gravatar images right from your profile screen?

As a bonus, the Avatar Manager plugin is also available on WordPress Plugin Directory and GitHub. Check it out to stay up-to-date with the latest releases. Thanks for reading!


References

  • WordPress Coding Standards - General information about coding standards for WordPress development.
  • Writing a Plugin - Best starting place for learning about how to develop WordPress plugins.
  • Plugin API - Description of how to use action and filter hooks in your WordPress plugin, and core functions that plugins can override.
  • Function Reference - An article with many of the core WordPress functions useful to plugin and theme developers; lists most of the core functions, excluding Template Tags.
  • I18n for WordPress Developers - Internationalization, including a section on how to internationalize your plugin.
  • Plugin Submission and Promotion - Once you have written your plugin, here are some hints on distributing it widely.

External Links

Related Posts