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.
Introduction
A WordPress plugin is a PHP application that adds a specific set of features or services to WordPress, which can be seamlessly integrated with WordPress using access points and methods provided by the WordPress Plugin API.
This article will guide you through the process of creating your own WordPress plugin from scratch.
Note: This article assumes that you are already familiar with the basic functionality of WordPress, and PHP programming.
Step 1. Setting Up the Workspace
To get started navigate to wp-content/plugins/ under your WordPress install. In order to set up the workspace start by creating the following structure of directories and empty files as exemplified in the image below:
Workspace structure for the Avatar Manager plugin
Make sure to pick a unique name for the plugin directory and for the main PHP file, such as avatar-manager and avatar-manager.php in this example, and put all the plugin's files into that directory.
Silence Is Golden
Before starting to write our plugin, open avatar-manager/index.php and add the following code:
<?php // Silence is golden. ?>
You can see this file in many places of WordPress. It's a simple trick used to prevent directory browsing.
Step 2. Writing a Basic WordPress Plugin
Now, it's time to put some information into our main plugin PHP file.
Standard Plugin Information
The top of the plugin's main PHP file must contain a standard plugin information header. This header lets WordPress recognize that the plugin exists, add it to the plugin management screen so it can be activated, load it, and run its functions; without the header, the plugin will never be activated and will never run.
Open avatar-manager/avatar-manager.php and add the following lines:
<?php /** * @package Avatar_Manager */ /* Plugin Name: Avatar Manager Plugin URI: http://wordpress.org/extend/plugins/avatar-manager/ Description: Avatar Manager for WordPress is a sweet and simple plugin for storing avatars locally and more. Easily. Version: 1.0.0 Author: Cătălin Dogaru Author URI: http://profiles.wordpress.org/cdog/ License: GPLv2 or later */ ?>
The minimum information WordPress needs to recognize our plugin is the Plugin Name
line. The rest of the information (if present) will be used to create the table of plugins on the plugin management screen. The order of the lines is not important.
So that the upgrade mechanism can correctly read the version of our plugin it is recommended to pick a format for the version number and stick to it between the different releases.
The License slug should be a short common identifier for the license the plugin is under and is meant to be a simple way of being explicit about the license of the code.
Versioning
For transparency and insight into our release cycle, and for striving to maintain backward compatibility, Avatar Manager will be maintained under the Semantic Versioning guidelines as much as possible.
Releases will be numbered with the following format:
<major>.<minor>.<patch>
And constructed with the following guidelines:
- Breaking backward compatibility bumps the major (and resets the minor and patch).
- New additions without breaking backward compatibility bumps the minor (and resets the patch).
- Bug fixes and misc changes bumps the patch.
For more information on SemVer, please visit semver.org.
License
It is customary to follow the standard header with information about licensing for the plugin. Most plugins use the same license used by WordPress, which is the GPLv2 license or a license compatible with the GPLv2. To indicate a GPLv2 license, include the following lines in our plugin:
/* Copyright © 2013 Cătălin Dogaru This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
Next, open avatar-manager/LICENSE and paste the plain text version of GPLv2 to it.
Step 3. Programming the Avatar Manager Plugin
After completing the previous step you should be able to locate the Avatar Manager plugin under the Plugins Screen.
The Avatar Manager plugin under Plugins Screen
Now, it's time to make our plugin actually do something. Activate it and add the following lines of code to the main plugin PHP file:
define( 'AVATAR_MANAGER_VERSION', '1.0.0' ); define( 'AVATAR_MANAGER_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'AVATAR_MANAGER_AVATAR_UPLOADS', 0 ); define( 'AVATAR_MANAGER_DEFAULT_SIZE', 96 );
The define()
function defines a named constant at runtime. The plugin_dir_url()
function gets the URL (with trailing slash) for the plugin __FILE__
passed in. The value of __FILE__
is the full path and filename of the current file and it's one of the eight magical constants that PHP provides.
Let's go further and initialize our plugin:
/** * Sets up plugin defaults and makes Avatar Manager available for translation. * * @uses load_theme_textdomain() For translation/localization support. * @uses plugin_basename() For retrieving the basename of the plugin. * * @since Avatar Manager 1.0.0 */ function avatar_manager_init() { // Makes Avatar Manager available for translation. load_plugin_textdomain( 'avatar-manager', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); } add_action( 'init', 'avatar_manager_init' );
The add_action()
call hooks a function on to a specific action. The init
action runs after WordPress has finished loading but before any headers are sent. Also the load_plugin_textdomain()
call should be made during init
, otherwise users can't hook into it. But more about this later, when I'll cover the internationalization of our plugin. The dirname()
function returns the parent directory's path, while plugin_basename()
function gets the basename of the plugin.
Hooks, Actions and Filters
Hooks are provided by WordPress to allow a plugin to hook into the rest of WordPress; that is, to call functions in the plugin at specific times, and thereby set the plugin in motion. There are two types of hooks:
- Actions – Actions are the hooks that the WordPress core launches at specific points during execution, or when specific events occur.
- Filters – Filters are the hooks that WordPress launches to modify text of various types before adding it to the database or sending it to the browser screen.
Step 4. Adding Plugin Options
Next, we're going to add the plugin options. Allowing for customization makes a plugin far more flexible for the user.
/** * Registers sanitization callback and plugin setting fields. * * @uses register_setting() For registering a setting and its sanitization * callback. * @uses add_settings_field() For registering a settings field to a settings * page and section. * @uses __() For retrieving the translated string from the translate(). * * @since Avatar Manager 1.0.0 */ function avatar_manager_admin_init() { // Registers plugin setting and its sanitization callback. register_setting( 'discussion', 'avatar_manager', 'avatar_manager_sanitize_options' ); // Registers Avatar Uploads settings field under the Settings Discussion // Screen. add_settings_field( 'avatar-manager-avatar_uploads', __( 'Avatar Uploads', 'avatar-manager' ), 'avatar_manager_avatar_uploads_settings_field', 'discussion', 'avatars' ); // Registers Default Size settings field under the Settings Discussion // Screen. add_settings_field( 'avatar-manager-default-size', __( 'Default Size', 'avatar-manager' ), 'avatar_manager_default_size_settings_field', 'discussion', 'avatars' ); } add_action( 'admin_init', 'avatar_manager_admin_init' );
The admin_init
action is triggered before any other hook when a user accesses the admin area. The register_setting()
function registers a setting and its sanitization callback. The add_settings_field()
function registers a settings field to a settings page and section. We used them to add our plugin options under the Settings Discussion Screen. The __()
function will be explained later, when I'll cover the internationalization process.
Step 5. Adding the Sanitization Callback
Before writing the sanitization callback, we need to define two more functions, avatar_manager_get_default_options()
and avatar_manager_get_options()
.
/** * Returns plugin default options. * * @since Avatar Manager 1.0.0 * * @return array Plugin default options. */ function avatar_manager_get_default_options() { $options = array( 'avatar_uploads' => AVATAR_MANAGER_AVATAR_UPLOADS, 'default_size' => AVATAR_MANAGER_DEFAULT_SIZE ); return $options; }
The avatar_manager_get_default_options()
function returns plugin default options.
/** * Returns plugin options. * * @uses get_option() For getting values for a named option. * @uses avatar_manager_get_default_options() For retrieving plugin default * options. * * @since Avatar Manager 1.0.0 * * @return array Plugin options. */ function avatar_manager_get_options() { return get_option( 'avatar_manager', avatar_manager_get_default_options() ); }
The avatar_manager_get_options()
function retrieves current plugin options. The get_otpion()
function returns the value of the specified option or the default value if the option isn't in the database.
/** * Sanitizes and validates plugin options. * * @uses avatar_manager_get_default_options() For retrieving plugin default * options. * @uses absint() For converting a value to a non-negative integer. * * @since Avatar Manager 1.0.0 * * @return array Sanitized plugin options. */ function avatar_manager_sanitize_options( $input ) { $options = avatar_manager_get_default_options(); if ( isset( $input['avatar_uploads'] ) && trim( $input['avatar_uploads'] ) ) $options['avatar_uploads'] = trim( $input['avatar_uploads'] ) ? 1 : 0; if ( isset( $input['default_size'] ) && is_numeric( trim( $input['default_size'] ) ) ) { $options['default_size'] = absint( trim( $input['default_size'] ) ); if ( $options['default_size'] < 1 ) $options['default_size'] = 1; elseif ( $options['default_size'] > 512 ) $options['default_size'] = 512; } return $options; }
The avatar_manager_sanitize_options()
function sanitizes and validates plugin options. The isset()
call determines if a variable is set and not NULL
. The trim()
function strips whitespaces from the beginning and end of a string. The is_numeric()
function finds whether a variable is a number or a numeric string. The absint()
function converts a value to a non-negative integer.
Step 6. Adding the Setting Fields
Now, it's time to add the setting fields.
/** * Prints Avatar Uploads settings field. * * @uses avatar_manager_get_options() For retrieving plugin options. * @uses _e() For displaying the translated string from the translate(). * @uses checked() For comparing two given values. * * @since Avatar Manager 1.0.0 */ function avatar_manager_avatar_uploads_settings_field() { // Retrieves plugin options. $options = avatar_manager_get_options(); ?> <fieldset> <legend class="screen-reader-text"> <span> <?php _e( 'Avatar Uploads', 'avatar-manager' ); ?> </span> </legend><!-- .screen-reader-text --> <label> <input <?php checked( $options['avatar_uploads'], 1, true ); ?> name="avatar_manager[avatar_uploads]" type="checkbox" value="1"> <?php _e( 'Anyone can upload', 'avatar-manager' ); ?> </label> </fieldset> <?php }
The avatar_manager_avatar_uploads_settings_field()
callback prints Avatar Uploads settings field. The checked()
function compares two given values and, if the values are the same, adds the checked
attribute to the current checkbox. The _e()
function will be described later, when I'll explain the internationalization process.
/** * Prints Default Size settings field. * * @uses avatar_manager_get_options() For retrieving plugin options. * @uses _e() For displaying the translated string from the translate(). * * @since Avatar Manager 1.0.0 */ function avatar_manager_default_size_settings_field() { // Retrieves plugin options. $options = avatar_manager_get_options(); ?> <fieldset> <legend class="screen-reader-text"> <span> <?php _e( 'Default Size', 'avatar-manager' ); ?> </span> </legend><!-- .screen-reader-text --> <label> <?php _e( 'Default size of the avatar image', 'avatar-manager' ); ?> <input class="small-text" min="1" name="avatar_manager[default_size]" step="1" type="number" value="<?php echo $options['default_size']; ?>"> </label> </fieldset> <?php }
The avatar_manager_default_size_settings_field()
callback prints Default Size settings field.
After adding the setting fields you should be able to locate the plugin options under the Settings Discussion Screen.
The Avatar Manager plugin options under the Settings Discussion Screen
The first option controls whether low privileged users can upload an avatar image or not, while the second option represents the default size for an avatar image.
Step 7. Adding the Avatar Section
To allow users manage their avatar, we need to add a new section to their profile page. Let's go furher and add the Avatar section under the User Your Profile Screen:
/** * Prints Avatar section. * * @uses avatar_manager_get_options() For retrieving plugin options. * @uses get_post_meta() For retrieving attachment meta fields. * @uses remove_filter() For removing a function attached to a specified action * hook. * @uses _e() For displaying the translated string from the translate(). * @uses checked() For comparing two given values. * @uses get_avatar() For retrieving the avatar for a user. * @uses esc_attr() For escaping HTML attributes. * @uses add_query_arg() For retrieving a modified URL (with) query string. * @uses self_admin_url() For retrieving an admin url link with optional path * appended. * @uses current_user_can() For checking whether the current user has a certain * capability. * @uses submit_button() For echoing a submit button, with provided text and * appropriate class. * @uses __() For retrieving the translated string from the translate(). * * @since Avatar Manager 1.0.0 * * @param array $profileuser User to edit. */ function avatar_manager_edit_user_profile( $profileuser ) { // Retrieves plugin options. $options = avatar_manager_get_options(); $avatar_type = isset( $profileuser->avatar_manager_avatar_type ) ? $profileuser->avatar_manager_avatar_type : 'gravatar'; if ( isset( $profileuser->avatar_manager_custom_avatar ) ) { // Retrieves attachment meta fields based on attachment ID. $custom_avatar_rating = get_post_meta( $profileuser->avatar_manager_custom_avatar, '_avatar_manager_custom_avatar_rating', true ); $user_has_custom_avatar = get_post_meta( $profileuser->avatar_manager_custom_avatar, '_avatar_manager_is_custom_avatar', true ); } if ( ! isset( $custom_avatar_rating ) || empty( $custom_avatar_rating ) ) $custom_avatar_rating = 'G'; if ( ! isset( $user_has_custom_avatar ) || empty( $user_has_custom_avatar ) ) $user_has_custom_avatar = false; if ( $user_has_custom_avatar ) // Removes the function attached to the specified action hook. remove_filter( 'get_avatar', 'avatar_manager_get_avatar' ); ?> <h3> <?php _e( 'Avatar', 'avatar-manager' ); ?> </h3> <table class="form-table"> ... </table><!-- .form-table --> <?php } add_action( 'show_user_profile', 'avatar_manager_edit_user_profile' ); add_action( 'edit_user_profile', 'avatar_manager_edit_user_profile' );
The show_user_profile
and edit_user_profile
actions help customization of the user profile page. The $profileuser
parameter is a WP_User
object of the user being edited. The get_post_meta()
function returns the values of the custom fields with the specified key from the specified post. The empty()
function determines whether a variable is empty. The remove_filter()
function removes a function attached to a specified filter hook; it's required to remove our custom function for retrieving an avatar image.
Next, we're going to add an avatar picker, an upload form and a rating chooser for the custom avatar image of each user.
The Avatar Picker
The avatar picker lets a user choose between using Gravatar or a custom avatar image. To add it, write the following code:
<tr> <th> <?php _e( 'Display this avatar', 'avatar-manager' ); ?> </th> <td class="avatar-manager"> <fieldset> <legend class="screen-reader-text"> <span> <?php _e( 'Display this avatar', 'avatar-manager' ); ?> </span><!-- .screen-reader-text --> </legend> <label> <input <?php checked( $avatar_type, 'gravatar', true ); ?> name="avatar_manager_avatar_type" type="radio" value="gravatar"> <?php echo get_avatar( $profileuser->ID, 32, '', false ); ?> <?php _e( 'Gravatar', 'avatar-manager' ); ?> <span class="description"> <?php _e( '<a href="http://codex.wordpress.org/How_to_Use_Gravatars_in_WordPress" target="_blank">More information</a>', 'avatar-manager' ); ?> </span><!-- .description --> </label> <?php if ( $user_has_custom_avatar ) : ?> <br> <label> <input <?php checked( $avatar_type, 'custom', true ); ?> name="avatar_manager_avatar_type" type="radio" value="custom"> <?php echo avatar_manager_get_custom_avatar( $profileuser->ID, 32, '', false ); ?> <?php _e( 'Custom', 'avatar-manager' ); ?> </label> <?php endif; ?> <?php if ( $user_has_custom_avatar && ( current_user_can( 'upload_files' ) || $options['avatar_uploads'] ) ) { $href = esc_attr( add_query_arg( array( 'action' => 'update', 'avatar_manager_action' => 'remove-avatar', 'avatar_manager_custom_avatar' => $profileuser->avatar_manager_custom_avatar, 'user_id' => $profileuser->ID ), self_admin_url( IS_PROFILE_PAGE ? 'profile.php' : 'user-edit.php' ) ) ); ?> <a class="delete" href="<?php echo wp_nonce_url( $href, 'update-user_' . $profileuser->ID ); ?>" onclick="return showNotice.warn();"> <?php _e( 'Delete', 'avatar-manager' ); ?> </a><!-- .delete --> <?php } ?> </fieldset> </td><!-- .avatar-manager --> </tr>
The get_avatar()
function retrieves the avatar for a user who provided a user ID or email address. To retrievie a custom avatar image by a user ID, we used the avatar_manager_get_custom_avatar()
function, which we'll write later. The current_user_can()
function checks whether the current user has a certain capability. Normally, low priviledged users like Subscribers aren't allowed to upload files; this is why we use the $options['avatar_uploads']
variable. The esc_attr()
function escapes HTML attributes. The self_admin_url()
function retrieves an admin URL link with optional path appended. The IS_PROFILE_PAGE
constant tells us if we're editing our profile or another user's profile. The wp_nonce_url()
function retrieves URL with nonce added to URL query. Before deleting an avatar, we ask the user to confirm by calling the showNotice.warn()
method at the onclick
event of the Delete link which displays a warning notice.
The Upload Form
The upload form allows a user to browse and upload a custom avatar image:
<?php if ( current_user_can( 'upload_files' ) || $options['avatar_uploads'] ) : ?> <tr> <th> <?php _e( 'Select Image', 'avatar-manager' ); ?> </th> <td> <fieldset> <legend class="screen-reader-text"> <span> <?php _e( 'Select Image', 'avatar-manager' ); ?> </span> </legend><!-- .screen-reader-text --> <label class="description" for="avatar-manager-upload-avatar"> <?php _e( 'Choose an image from your computer:', 'avatar-manager' ); ?> </label><!-- .description --> <br> <input name="avatar_manager_import" type="file"> <?php submit_button( __( 'Upload', 'avatar-manager' ), 'button', 'avatar-manager-upload-avatar', false ); ?> </fieldset> </td> </tr> <?php endif; ?>
The submit_button()
function echoes a submit button, with provided text and appropriate class.
The Rating Chooser
The rating chooser shows up only when a custom avatar is available. To add it, write the following lines:
<?php if ( $user_has_custom_avatar ) : ?> <tr> <th> <?php _e( 'Avatar Rating', 'avatar-manager' ); ?> </th> <td> <fieldset> <legend class="screen-reader-text"> <span> <?php _e( 'Avatar Rating', 'avatar-manager' ); ?> </span> </legend><!-- .screen-reader-text --> <?php $ratings = array( // Translators: Content suitability rating: // http://bit.ly/89QxZA 'G' => __( 'G — Suitable for all audiences', 'avatar-manager' ), // Translators: Content suitability rating: // http://bit.ly/89QxZA 'PG' => __( 'PG — Possibly offensive, usually for audiences 13 and above', 'avatar-manager' ), // Translators: Content suitability rating: // http://bit.ly/89QxZA 'R' => __( 'R — Intended for adult audiences above 17', 'avatar-manager' ), // Translators: Content suitability rating: // http://bit.ly/89QxZA 'X' => __( 'X — Even more mature than above', 'avatar-manager' ) ); foreach ( $ratings as $key => $rating ) { ?> <label> <input <?php checked( $custom_avatar_rating, $key, true ); ?> name="avatar_manager_custom_avatar_rating" type="radio" value="<?php echo esc_attr( $key ); ?>"> <?php echo $rating; ?> </label> <br> <?php } ?> <span class="description"> <?php _e( 'Choose a rating for your custom avatar.', 'avatar-manager' ); ?> </span><!-- .description --> </fieldset> </td> </tr> <?php endif; ?>
It lets the user choose an appropriate rating for the custom avatar image being used.
Step 8. Adding Scripts and Styles
Now that we've added the Avatar section, it's time to style it. Also, we'll write some JS to change the form encoding; this step is required because we've added a file upload control to it.
The CSS Style
To style our plugin, open avatar-manager/avatar-manager.css and write the following lines:
#profile-page .avatar-manager img { margin: 2px 0; vertical-align: middle; } #profile-page .avatar-manager .delete { color: red; padding: 2px; } #profile-page .avatar-manager .delete:hover { background: red; color: #fff; text-decoration: none; }
This aligns an avatar image vertically with a radiobox and styles the Delete link accordingly for a seamless integration with WordPress' native interface.
The JS Script
Next, open avatar-manager/avatar-manager.js and add the following code:
jQuery( document ).ready( function() { jQuery( '#your-profile' ).attr( 'enctype', 'multipart/form-data' ); } );
The .attr()
function sets the value of one or more attributes for every matched element. The enctype
attribute specifies how the form-data should be encoded when submitting it to the server. We need to change its value to multipart/form-data
to allow file uploads.
Step 9. Enqueuing the Scripts and Styles
The safe and recommended method of adding scripts and styles to a WordPress generated page is by using wp_enqueue_script()
and wp_enqueue_style()
. These functions include the scripts and styles if they haven't already been included, and safely handle dependencies.
/** * Enqueues plugin scripts and styles for Users Your Profile Screen. * * @uses wp_register_style() For registering a CSS style file. * @uses wp_enqueue_style() For enqueuing a CSS style file. * @uses wp_register_script() For registering a JS script file. * @uses wp_enqueue_script() For enqueuing a JS script file. * * @since Avatar Manager 1.0.0 */ function avatar_manager_admin_enqueue_scripts() { global $hook_suffix; if ( $hook_suffix == 'profile.php' || $hook_suffix == 'user-edit.php' ) { // Registers plugin CSS style file. wp_register_style( 'avatar-manager.css', AVATAR_MANAGER_PLUGIN_URL . 'avatar-manager.css', array(), '1.0.0' ); // Enqueues plugin CSS style file. wp_enqueue_style( 'avatar-manager.css' ); // Registers plugin JS script file. wp_register_script( 'avatar-manager.js', AVATAR_MANAGER_PLUGIN_URL . 'avatar-manager.js', array( 'jquery' ), '1.0.0' ); // Enqueues plugin JS script file. wp_enqueue_script( 'avatar-manager.js' ); } } add_action( 'admin_enqueue_scripts', 'avatar_manager_admin_enqueue_scripts' );
The admin_enqueue_scripts
action is the first action hooked into the admin scripts actions. Then, the global variable $hook_suffix
is used to add our scripts and styles only on the needed pages. Before enqueuing a script or style, we need to register it first. The wp_register_style()
function is a safe way to register a CSS style file for later use; the wp_enqueue_style()
function is used to enqueue it. Similarly, the wp_register_script()
and wp_enqueue_script()
functions are used to register and enqueue our plugin JS script file.
After this step you should be able to locate the plugin options under the User Your Profile Screen.
The Avatar Manager plugin options under the User Your Profile Screen
The first option lets you choose between using Gravatar or a self-hosted avatar image. The second field lets you browse and upload a custom avatar image. The Avatar Rating option shows up only when a custom avatar is available. But more about this later, when we'll handle avatar uploads.
What's Next
This completes the first part of our tutorial. I hope you've enjoyed the time we've spent together and found it to be useful. In the next part we're going to finish the Avatar Manager plugin; we'll handle avatar uploads and on-demand image generation, internationalize our plugin and much more. 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.
- Settings API - A reference with examples for adding new settings to existing settings screens.
- 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.
- SemVer - Semantic Versioning (SemVer) specification.
- GPLv2 - GNU General Public License, version 2.
External Links
- Avatar Manager on WordPress Plugin Directory - Official home for the Avatar Manager plugin.
- Avatar Manager on GitHub - Source code of the Avatar Manager plugin.
Comments