⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies
Showing posts with label Functions. Show all posts
Showing posts with label Functions. Show all posts

Saturday, January 28, 2012

Limiting The Visibility Of Posts In WordPress Via Usernames

Controlling who is able to view a post is a simple task once the system is established. Limiting access to certain users has several applications, such as enabling a design studio to distribute artwork to its various clients, or enabling a small school to arrange for homework to be posted online using a cheap and easy solution.
lpvuau-splash1
The easiest method to get this system working is to make the recipients of the information “subscribers” (since they need not be able to post) and the distributors of information “authors” (since they should only be able to edit their own posts). This system eliminates several headaches for the website owner by managing who has access to particular posts. The username would be used to identify who is allowed to view certain posts, since it is unique and, for the most part, constant.

The Basics

What Will You Need?

  • WordPress 3.1 or later
  • Members of various roles
  • The ability to modify your theme’s files
  • Basic knowledge of PHP and MySQL

What Is a Username?

In general, a username is a means by which to identify and verify a user. It is not the only way to identify a user, but remembering a username is easier for the person logging in than having to remember a random user ID. A username can be made unique to an individual, unlike a person’s name or email address (family members may share the same name, or even the same email address). This ease of use and uniqueness is why usernames are used on most websites that require people to sign up in order to access the website or certain of its features.
To WordPress, a username is means of identifying a user. Paired with a password, a username enables someone to access their profile and, depending on their permissions within WordPress, to access to the administrative pages of the website. A username can be used for many functions in the operation and management of a website, such as karma, prestige, user roles and expulsion.
A WordPress username is unique and impossible for the average user to change. Thus, the system is a potentially reliable means of identifying individuals. This reliability is important for a system in which a post must be visible to only a few people. The permissions of a post should not alter merely because someone has changed their name or email address.
Screenshot of a WordPress single user page
The user page in a WordPress installation. Note that “Usernames cannot be changed.”

Setting Up The Back End

In order for an author to be able to set permissions for visibility, a method of selecting users must be set up on the post editing page. We could accomplish this by one of several methods, one of the easiest and most efficient of which is to create a meta box (or widget) in the post editing page that allows the author to add custom information, as required by a theme or plugin. This information enables us to tell the theme which members should have viewing rights to particular posts.

A Basic Custom Meta Box


Let’s assume we’re dealing with a website for a music school named “ Fancy Flautists.” We will use the name flautist_access in the code for the back end to distinguish it from other custom functions. Justin’s code is a great starting point for this project, but it needs a little customization for our purpose. Place the following code in your theme’s functions.php, and modify the various labels according to your project.
01/* Fire our meta box setup function on the post editor screen. */
02add_action( 'load-post.php', 'post_meta_boxes_setup' );
03add_action( 'load-post-new.php', 'post_meta_boxes_setup' );
04 
05/* Meta box setup function. */
06function post_meta_boxes_setup() {
07 
08    /* Add meta boxes on the 'add_meta_boxes' hook. */
09    add_action( 'add_meta_boxes', 'add_post_meta_boxes' );
10 
11    /* Save post meta on the 'save_post' hook. */
12    add_action( 'save_post', 'flautist_access_save_meta', 10, 2 );
13}
14 
15/* Create one or more meta boxes to be displayed on the post editor screen. */
16function add_post_meta_boxes() {
17 
18    add_meta_box(
19        'smashing-flautist-access',         // Unique ID
20        esc_html__( 'Post Viewing Permission', 'flautist' ),       // Title
21        'flautist_access_meta_box',        // Callback function
22        'post',                 // Admin page (or post type)
23        'normal',                   // Context
24        'default'                   // Priority
25    );
26}
27 
28/* Display the post meta box. */
29function flautist_access_meta_box( $object, $box ) { ?>
30 
31    <?php wp_nonce_field( basename( __FILE__ ), 'flautist_access_nonce' ); ?>
32 
33    <p>
34        <label for="smashing-flautist-access"><?php _e( "Enter the username of the subscriber that you want to view this content.", 'flautist' ); ?></label>
35        <br />
36        <input class="widefat" type="text" name="smashing-flautist-access" id="smashing-flautist-access" value="<?php echo esc_attr( get_post_meta( $object->ID, 'flautist_access', true ) ); ?>" size="30" />
37    </p>
38<?php }
With Justin’s code, modified for this project, we should have a custom meta box that looks like this:
Screenshot of a basic meta box
A basic meta box positioned below the post editing box.

Adding Ease to the Selection

This box can be used as is, and the author would simply input the members who they want to allow to view a post. This would work well if each author had very few usernames to remember; but if the author has long list of usernames to choose from, then a list of members would have to be displayed, and there would have to be a system that allows the authors to choose members from the list. Add the following code to the area just below the original box, just after the closing paragraph tag, to display a list of users with their names, along with radio buttons to grant one of the users access to the current post.
01<table class="smashing-flautist-access">
02<tr align="left">
03<th>Username</th>
04<th>    </th>
05<th>Visiblity</th>
06<th>    </th>
07<th>Name</th>
08</tr>
09<?php
10global $post;
11    $users = get_users('role=subscriber');
12    foreach ($users as $user) {
13            $user_info = get_userdata( $user->ID );
14            if(get_post_meta( $object->ID, 'flautist_access', true ) == $user->user_login) $ifchecked = 'checked="checked" ';
15            echo "<tr>";
16            echo "<td>$user->user_login</td><td>    </td>";
17            echo "<td align=\"center\"><input type=\"radio\" name=\"smashing-flautist-access\" id=\"smashing-flautist-access\" value=\"$user->user_login\" " . $ifchecked ."/></td><td>    </td>";
18            echo "<td>$user_info->last_name, $user_info->first_name</td><td>    </td>";
19            echo "</tr>";
20            unset($ifchecked);
21 
22    } ?></table>
If everything goes well, you should end up with a box underneath the post editor that looks similar to the image below. The form containing the radio buttons gets a list of users that are listed as subscribers and makes the selection of the student with viewing permissions easy, all without the post’s author having to remember any usernames.
Screenshot of a meta box with user information
A meta box that contains a method to select the particular name and information of each user.

Saving the List

Now that we have generated a list that makes it easy for the authors to pick which members they want to be able to view particular posts, we have to create a system to add the list to WordPress’ MySQL database so that we can retrieve it later. We also need a way to tell WordPress to update this list of usernames in case the author decides later to add or remove someone from a particular post’s list of authorized viewers. The code provided by Justin does excellent work; place his code below in your theme’s functions.php, just after the function that sets up the custom meta box.
01/* Save post meta on the 'save_post' hook. */
02add_action( 'save_post', 'flautist_access_save_meta', 10, 2 );
03 
04/* Save the meta box's post metadata. */
05function flautist_access_save_meta( $post_id, $post ) {
06 
07    /* Make all $wpdb references within this function refer to this variable */
08    global $wpdb;
09 
10    /* Verify the nonce before proceeding. */
11    if ( !isset( $_POST['flautist_access_nonce'] ) || !wp_verify_nonce( $_POST['flautist_access_nonce'], basename( __FILE__ ) ) )
12        return $post_id;
13 
14    /* Get the post type object. */
15    $post_type = get_post_type_object( $post->post_type );
16 
17    /* Check if the current user has permission to edit the post. */
18    if ( !current_user_can( $post_type->cap->edit_post, $post_id ) )
19        return $post_id;
20 
21    /* Get the posted data and sanitize it for use as an HTML class. */
22    $new_meta_value = ( isset( $_POST['smashing-flautist-access'] ) ? sanitize_html_class( $_POST['smashing-flautist-access'] ) : '' );
23 
24    /* Get the meta key. */
25    $meta_key = 'flautist_access';
26 
27    /* Get the meta value of the custom field key. */
28    $meta_value = get_post_meta( $post_id, $meta_key, true );
29 
30    /* If a new meta value was added and there was no previous value, add it. */
31    if ( $new_meta_value && '' == $meta_value )
32        {
33        add_post_meta( $post_id, $meta_key, $new_meta_value, true );
34        $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET post_status = 'private' WHERE ID = ".$post_id." AND post_type ='post'"));
35        }
36    /* If the new meta value does not match the old value, update it. */
37    elseif ( $new_meta_value && $new_meta_value != $meta_value )
38        {
39        update_post_meta( $post_id, $meta_key, $new_meta_value );
40        $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET post_status = 'private' WHERE ID = ".$post_id." AND post_type ='post'"));
41        }
42    /* If there is no new meta value but an old value exists, delete it. */
43    elseif ( '' == $new_meta_value && $meta_value )
44        {
45        delete_post_meta( $post_id, $meta_key, $meta_value );
46        $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET post_status = 'public' WHERE ID = ".$post_id." AND post_type ='post'"));
47        }
48}
The three MySQL queries are in place to prevent unauthorized users from viewing protected posts and to hide the posts from the RSS feeds. The first query runs only when new data populates the previously empty custom field, while the second query runs only when the data in the custom field has changed. The third query runs only if the custom field is emptied, and it sets the post’s visibility back to “Public.” All three are protected from SQL injection attacks by using $wpdb->prepare() to validate the data entered into the username form field.
If you don’t like that WordPress precedes the post’s title with the word “Private,” then add the following code to your theme’s functions.php file. This custom function is called when your theme would display a post’s title; it finds any instance of the words “Protected” or “Private” at the beginning of the title and removes them. In the core of WordPress’ programming, the function get_the_title() adds those words if a post’s visibility is restricted and the person viewing is not an administrator. What the following code does is send a message to the action that get_the_title() hooks into, telling it to remove the terms “Protected: ” and “Private: ” from the title. So, you can set a post’s title to begin with either term, and the title will not be altered; this code only affects WordPress’ ability to add to your title.
1function title_trim($title) {
2    $title = attribute_escape($title);
3    $needles = array(__('Protected: '),__('Private: '));
4    $title = str_replace($needles,'',$title);
5    return $title;
6}
7add_filter('protected_title_format','title_trim');
8add_filter('private_title_format','title_trim');
To allow users at the subscriber level to see private posts, you have to give them that capability. As it happens, some of the code we’ll be using later frees us from having to worry about users at the subscriber level seeing the posts of others.
1$subRole = get_role( 'subscriber' );
2$subRole->add_cap( 'read_private_posts' );
You can also grant users at the subscriber level permission to view private pages, in case you want a dedicated page of information that subscribers should know.
1$subRole->add_cap( 'read_private_pages' );

Setting Up The Front End

Now that we have a way to add members to the list of people who can view a particular post, we have to modify our theme to use this data, and to actually control the visibility of each post based on this list. First, we need a way to get the username of the person who can view a post. Secondly, we would compare the username of the member with viewing permissions to the user who is currently logged in. Finally, we would make the theme display either the post in the loop or an error message (or perhaps nothing at all).
Place this code just after The Loop starts. It goes in single.php, category.php and index.php if you will be displaying posts on the home page.
1<?php
2/* Get the post's acceptable viewer. */
3        $flautist_access = get_post_meta($post->ID, 'flautist_access', true );
4/* Get the post's current viewer, if he or she is logged in. */
5        if(is_user_logged_in()) {$current_flautist = $current_user->user_login;}
6/* See if the acceptable viewer and the current viewer are the same */
7        if($flautist_access == $current_flautist || current_user_can('author') || current_user_can('editor') || current_user_can('administrator'))
8            {echo ''; ?>
Place this code just before the loop ends. Here is where you can show an error message telling the user that they may not view this post. Or you could leave this code as is to make it appear as though the current visitor is not missing anything.
1<?php } else { echo ''; } ?>
This is what a hidden post looks like to the public or to a user who is not logged in. They would see what appears to be an error message and are redirected away from the post.
What the public sees when trying to view a protected post
If a person is not logged in and tries to view a restricted post, they would get an error message.
What an unauthorized user sees when trying to view a protected post
If a user is logged in but not allowed to view a restricted post, they would see either nothing or an error message specific to members.
What an authorized user sees when trying to view a protected post
If a member is logged in and authorized to view a protected post, then they would see the post itself.

Conclusion

Being able to control who can view individual posts is a useful feature with a wide variety of applications. Third-party software can natively do this, but WordPress is widely supported and documented, which means that any security holes that might allow unauthorized users to view restricted posts would be shut in a future update. Plus, it allows you to run an actual blog next to posts with limited visibility. This system could be used by administrators to distribute personalized content, by professionals to send files to clients, or by bloggers to restrict the visibility of certain posts to certain members.
Enabling an author to control who can view their posts can help them tailor the blog’s content to the needs or tastes of certain users. Ultimately, you will have to factor in the purpose and content of your website when deciding whether to use this method. It’s not for everyone, but it suit the needs of owners of small websites who want to deliver certain content to certain people.

Resources

Monday, November 21, 2011

WordPress Multisite: Practical Functions And Methods

Multisite is a powerful new feature that arrived with the release of WordPress 3.0. It allows website managers to host multiple independent websites with a single installation of WordPress. Although each “website” in a network is independent, there are many ways to share settings, code and content throughout the entire network.
WordPress Multisite
Since the beginning of the year, I’ve been developing themes and plugins for a WordPress Multisite-powered content network. During that time I’ve learned many powerful tips and tricks unique to Multisite. This guide will introduce you to a few Multisite-specific functions, along with real-world programming examples that you can begin using today. Hopefully, it will open your eyes to a few of the new possibilities available in Multisite.

Why Use Multisite?

Multisite is a great option for freelancers, businesses and organizations that manage multiple WordPress websites. Whether you’re a freelancer who wants to provide hosting and maintenance to clients, a college organization looking to centralize the management of your websites, or a large news publisher trying to isolate silos for different departments, Multisite is the answer.
Managing multiple websites with a single installation of WordPress enables you to easily upgrade the core, plugins and themes for every website in a network. You can share functionality across multiple websites with network plugins, as well as standardize design elements across multiple websites using a parent theme.

Overview of Benefits

  • Users are able to easily access and manage multiple websites with a single user account and profile.
  • Users can access a particular website or every website using the same account.
  • Information from one website can be completely isolated from others.
  • Information from one website can be easily shared with others.
  • Updates and upgrades can be rolled out across multiple websites in less time, reducing overhead and maintenance costs.
  • Customizations to WordPress can be efficiently distributed in a centralized, cascading method using network-wide plugins.
I won’t explain how to install and configure Multisite. If you need help, plenty of great articles are available in the WordPress Codex.

Working With Multisite Functions

Multisite-enabled WordPress installations contain additional functions and features that theme developers can use to improve the experience of a website. If you find yourself developing themes and plugins for WordPress Multisite, consider the following tips to customize and improve the connectivity of the network.

Displaying Information About a Network

You might find yourself in a situation where you would like to display the number of websites or users in your network. Providing a link to the network’s primary website would also be nice, so that visitors can learn more about your organization.
Multisite stores global options in the wp_sitemeta database table, such as the network’s name (site_name), the administrator’s email address (admin_email) and the primary website’s URL (siteurl). To access these options, you can use the get_site_option() function.
In this example, I’ve used the get_site_option() function along with get_blog_count() and get_user_count() to display a sentence with details about a network.
1if( is_multisite() ): ?>
2
3   The "%3C?php%20echo%20esc_url%28%20get_site_option%28%20%27siteurl%27%20%29%20%29;%20?%3E">echo esc_html( get_site_option( 'site_name' ) ); ?> network currently powers echo get_blog_count(); ?> websites and echo get_user_count(); ?> users.
4
5endif; ?>
This small snippet of code will display the following HTML:
1The <a href="http://www.smashingmagazine.com">Smashing Magazine networka> currently powers <strong>52strong> websites and <strong>262strong> users.
Many useful Multisite functions can be found in the /wp-includes/ms-functions.php file. I highly suggest browsing the Trac project yourself. It’s a great way to find new functions and to become familiar with WordPress coding standards.

Build a Network Navigation Menu

Many networks have consistent dynamic navigation that appears on all websites, making it easy for visitors to browse the network. Using the $wpdb database class, along with the get_site_url(), home_url(), get_current_blog_id(), switch_to_blog() and restore_current_blog() functions, we can create a fully dynamic network menu, including a class (.current-site-item) to highlight the current website.
The SQL query we’ve created in this example has the potential to become very large, possibly causing performance issues. For this reason, we’ll use the Transients API, which enables us to temporarily store a cached version of the results as network website “transients” in the sitemeta table using the set_site_transient() and get_site_transient() functions.
Transients provide a simple and standardized way to store cached data in the database for a set period of time, after which the data expires and is deleted. It’s very similar to storing information with the Options API, except that it has the added value of an expiration time. Transients are also sped up by caching plugins, whereas normal options aren’t. Due to the nature of the expiration process, never assume that a transient is in the database when writing code.
The SQL query will run every two hours, and the actual data will be returned from the transient, making things much more efficient. I’ve included two parameters, $size and $expires, allowing you to control the number of posts returned and the expiration time for the transient.
One of the most powerful elements of this example is the use of switch_to_blog() and restore_current_blog(). These two Multisite functions enable us to temporarily switch to another website (by ID), gather information or content, and then switch back to the original website.
Add the following to your theme’s functions.php file:
01/**
02 * Build a list of all websites in a network
03 */
04function wp_list_sites( $expires = 7200 ) {
05   if( !is_multisite() ) return false;
06
07   // Because the get_blog_list() function is currently flagged as deprecated
08   // due to the potential for high consumption of resources, we'll use
09   // $wpdb to roll out our own SQL query instead. Because the query can be
10   // memory-intensive, we'll store the results using the Transients API
11   if ( false === ( $site_list = get_transient( 'multisite_site_list' ) ) ) {
12      global $wpdb;
13      $site_list = $wpdb->get_results( $wpdb->prepare('SELECT * FROM wp_blogs ORDER BY blog_id') );
14      // Set the Transient cache to expire every two hours
15      set_site_transient( 'multisite_site_list', $site_list, $expires );
16   }
17
18   $current_site_url = get_site_url( get_current_blog_id() );
19
20   $html = '
21
    "network-menu">' . "\n";
22
23   foreach ( $site_list as $site ) {
24      switch_to_blog( $site->blog_id );
25      $class = ( home_url() == $current_site_url ) ? ' class="current-site-item"' : '';
26      $html .= "\t" . '
27

  • "site-' . $site->blog_id . '" '="" .="" $class="">' . get_bloginfo('name') . '




  • 28
    29' . "\n";
    30      restore_current_blog();
    31   }
    32
    33   $html .= '
    34
    35' . "\n\n";
    36
    37   return $html;
    38}
    (Please note: The get_blog_list() function is currently deprecated due to the potential for a high consumption of resources if a network contains more than 1000 websites. Currently, there is no replacement function, which is why I have used a custom $wpdb query in its place. In future, WordPress developers will probably release a better alternative. I suggest checking for a replacement before implementing this example on an actual network.)
    This function first verifies that Multisite is enabled and, if it’s not, returns false. First, we gather a list of IDs of all websites in the network, sorting them in ascending order using our custom $wpdb query. Next, we iterate through each website in the list, using switch_to_blog() to check whether it is the current website, and adding the .current-site-item class if it is. Then, we use the name and link for that website to create a list item for our menu, returning to the original website using restore_current_blog(). When the loop is complete, we return the complete unordered list to be outputted in our theme. It’s that simple.
    To use this in your theme, call the wp_list_sites() function where you want the network menu to be displayed. Because the function first checks for a Multisite-enabled installation, you should verify that the returned value is not false before displaying the corresponding HTML.
    01
    02// Multisite Network Menu
    03$network_menu = wp_list_sites();
    04if( $network_menu ):
    05?>
    06
    "network-menu">
    07   echo $network_menu; ?>
    08
    09
    10
    11endif; ?>

    List Recent Posts Across an Entire Network

    If the websites in your network share similar topics, you may want to create a list of the most recent posts across all websites. Unfortunately, WordPress does not have a built-in function to do this, but with a little help from the $wpdb database class, you can create a custom database query of the latest posts across your network.
    This SQL query also has the potential to become very large. For this reason, we’ll use the Transients API again in a method very similar to what is used in the wp_list_sites() function.
    Start by adding the wp_recent_across_network() function to your theme’s functions.php file.
    01/**
    02 * List recent posts across a Multisite network
    03 *
    04 * @uses get_blog_list(), get_blog_permalink()
    05 *
    06 * @param int $size The number of results to retrieve
    07 * @param int $expires Seconds until the transient cache expires
    08 * @return object Contains the blog_id, post_id, post_date and post_title
    09 */
    10function wp_recent_across_network( $size = 10, $expires = 7200 ) {
    11   if( !is_multisite() ) return false;
    12
    13   // Cache the results with the WordPress Transients API
    14   // Get any existing copy of our transient data
    15   if ( ( $recent_across_network = get_site_transient( 'recent_across_network' ) ) === false ) {
    16
    17      // No transient found, regenerate the data and save a new transient
    18      // Prepare the SQL query with $wpdb
    19      global $wpdb;
    20
    21      $base_prefix = $wpdb->get_blog_prefix(0);
    22      $base_prefix = str_replace( '1_', '' , $base_prefix );
    23
    24      // Because the get_blog_list() function is currently flagged as deprecated
    25      // due to the potential for high consumption of resources, we'll use
    26      // $wpdb to roll out our own SQL query instead. Because the query can be
    27      // memory-intensive, we'll store the results using the Transients API
    28      if ( false === ( $site_list = get_site_transient( 'multisite_site_list' ) ) ) {
    29         global $wpdb;
    30         $site_list = $wpdb->get_results( $wpdb->prepare('SELECT * FROM wp_blogs ORDER BY blog_id') );
    31         set_site_transient( 'multisite_site_list', $site_list, $expires );
    32      }
    33
    34      $limit = absint($size);
    35
    36      // Merge the wp_posts results from all Multisite websites into a single result with MySQL "UNION"
    37      foreach ( $site_list as $site ) {
    38         if( $site == $site_list[0] ) {
    39            $posts_table = $base_prefix . "posts";
    40         } else {
    41            $posts_table = $base_prefix . $site->blog_id . "_posts";
    42         }
    43
    44         $posts_table = esc_sql( $posts_table );
    45         $blogs_table = esc_sql( $base_prefix . 'blogs' );
    46
    47         $query .= "(SELECT $posts_table.ID, $posts_table.post_title, $posts_table.post_date, $blogs_table.blog_id FROM $posts_table, $blogs_table\n";
    48         $query .= "\tWHERE $posts_table.post_type = 'post'\n";
    49         $query .= "\tAND $posts_table.post_status = 'publish'\n";
    50         $query .= "\tAND $blogs_table.blog_id = {$site->blog_id})\n";
    51
    52         if( $site !== end($site_list) )
    53            $query .= "UNION\n";
    54         else
    55            $query .= "ORDER BY post_date DESC LIMIT 0, $limit";
    56      }
    57
    58      // Sanitize and run the query
    59      $query = $wpdb->prepare($query);
    60      $recent_across_network = $wpdb->get_results( $query );
    61
    62      // Set the Transients cache to expire every two hours
    63      set_site_transient( 'recent_across_network', $recent_across_network, 60*60*2 );
    64   }
    65
    66   // Format the HTML output
    67   $html = '
    68
      ';
    69   foreach ( $recent_across_network as $post ) {
    70      $html .= '
    71

  • "%27%20.%20get_blog_permalink%28%20$post-%3Eblog_id,%20$post-%3EID%20%29%20.%20%27">' . $post->post_title . '




  • 72
    73';
    74   }
    75   $html .= '
    76
    77';
    78
    79   return $html;
    80}
    Using this function in your theme is simple. Be certain to check the return value before outputting HTML to avoid conflicts with non-Multisite installations.
    01
    02// Display recent posts across the entire network
    03$recent_network_posts = wp_recent_across_network();
    04if( $recent_network_posts ):
    05?>
    06
    class="recent-accross-network">
    07   echo $recent_network_posts; ?>
    08
    09
    10endif; ?>

    Retrieve a Single Post from Another Website in the Network

    In certain situations, you may find it useful to refer to a single page, post or post type from another website in your network. The get_blog_post() function makes this process simple.
    For example, you may want to display the_content() from an “About” page on the primary website in your network.
    01
    02// Display "About" page content from the network's primary website
    03$about_page = get_blog_post( 1, 317 );
    04if( $about_page ):
    05?>
    06
    class="network-about entry">
    07   echo $about_page->post_content; ?>
    08
    09
    10endif; ?>
    Did you notice that the entire $post object is returned? In this example, we’ve used only the_content(), but far more information is available for other circumstances.

    Set Up Global Variables Across a Network

    Starting any WordPress project in a solid local development environment is always important. You might find it handy to have a global variable that determines whether a website is “live” or “staging.” In Multisite, you can achieve this using a network-activated plugin that contains the following handy function, assuming that your local host contains localhost in the URL:
    1/**
    2 * Define network globals
    3 */
    4function ms_define_globals() {
    5   global $blog_id;
    6   $GLOBALS['staging'] = ( strstr( $_SERVER['SERVER_NAME'], 'localhost' ) ) ? true : false;
    7}
    8add_action( 'init', 'ms_define_globals', 1 );
    When would you use this $staging variable? I use it to display development-related messages, notifications and information to improve my workflow.

    Display the Page Request Information in a Local Environment

    I use the $staging global variable to display the number of queries and page-request speed for every page across a network in my local environment.
    01/**
    02 * Display page request info
    03 *
    04 * @requires $staging
    05 */
    06function wp_page_request_info() {
    07   global $staging;
    08   if ( $staging ): ?>
    09      echo get_num_queries(); ?> queries in seconds.
    10   endif;
    11}
    12add_action( 'wp_footer', 'wp_page_request_info', 1000 );
    This is only one of many ways you can use the ms_define_globals() function. I’ve used it to define, find and replace URLs in the content delivery network, to detect mobile devices and user agents, and to filter local attachment URLs.

    Conclusion

    There is tremendous value in the simplicity of managing multiple websites in a single installation of WordPress. Leveraging WordPress Multisite is quickly becoming a requisite skill among WordPress developers. These techniques should provide a solid foundation for you to build on, so that you can be the next WordPress Multisite theme rock star!

    Other Resources