How to make your plugin have universal settings on Multisite WordPress?

While updating my Check Amazon Links plugin to work with WordPress Multisite, I struggled with how I could make my plugin settings universal across all network installs.  More specifically, I wanted it to be an option that could be turned on and off.  I tried to find a way to implement a hook or filter for when the plugin options were being updated via the settings page, but I had so much trouble with that, that I finally decided to use some Javascript.  Yay for javascript!  Sometimes it’s the easier option.

First, I added the option to my settings page, but only on multisite installs.  Here’s what it looks like:

Multisite Option on my WordPress Plugin (Check Amazon Links)

Next, I wrote the Javascript (jQuery) code to respond to submit events:


jQuery(document).ready(function () {
    var option_page_name = jQuery('input[name=option_page]').val();
    if (typeof(option_page_name) != "undefined") {
        if(option_page_name === 'amazon_link_plugin_options') {
            jQuery('form').submit(function(e) {
                var update_all = jQuery('select[name="azlc_multisite_same[toggle]"]').val();
                if(update_all === '1') {
                    jQuery.post(ajaxurl, 'action=azlc_ms', function(){});
                }

            });
        }
    }

});

As you see, my code checks the page name to insure that it only sends the AJAX request if it’s MY plugin’s page.   I add that javascript code to a javascript file I already enqueue.

Here’s the PHP WordPress Backend code for handling the Ajax request:


if(is_multisite()) {
	add_action('wp_ajax_azlc_ms', 'azlc_ms');
}

function azlc_ms() {
	add_site_option( 'azlc_update_from', get_current_blog_id() );
}

 

And here’s the PHP WordPress code the updates the options:



if(is_multisite()) {
	add_action('plugins_loaded', 'azlc_multisite');
}


function azlc_multisite() {
	global $wpdb;

	// check for flag 
        // I call this a flag because its existence means that this code needs to run.
        // You could write this to make it more human readable
	$blog_id = get_site_option('azlc_update_from');
	if($blog_id) {
		// switch to the blog to copy the actions from
		switch_to_blog($blog_id);
		$options = get_option('azlc_plugin_options');
		// copy settings to all sites
		AmazonLinkCheckerCore::copy_settings_from($blog_id, 'azlc_plugin_options', $options );
		// remove flag
		delete_site_option('azlc_update_from');
		// switch back
		restore_current_blog();
	}
}

 

And here’s the code that actually does the copying:


	public static function copy_settings_from($source_id, $option_name, $option_value) {
				global $wpdb;
				$sql = "SELECT blog_id FROM $wpdb->blogs";
				$blog_ids = $wpdb->get_col($sql);
				foreach($blog_ids as $blog_id) {
					// update option on other blogs
					if($blog_id!=$source_id) {
						switch_to_blog($blog_id);
						update_option($option_name, $option_value);
					}
				}
				restore_current_blog();
	}

 

Related Tutorial

Don’t miss this related article: How to Enable WordPress Multisite for your Plugin

Your feedback is important

I write this blog with sincere hope that I can help other WordPress developers.  Please let me know if this helped you, or if I made a bad error.  Thanks!

Sanitizing & Validating User Input for Amazon Product Advertising API

Summary:  Remove apostrophes to prevent Amazon from returning “No Results”

As a security-conscious web programmer, I always sanitize user input. It’s something you have to do to prevent attacks like SQL Injection and Cross-Site Scripting.  Basically it means that you never trust user input to be safe, and you always filter or sanitize it to remove potentially dangerous input.   PHP has lots of filters for sanitization, so that part is easy.  For Amazon item search, my PHP code filters the user input like this:


if(isset($_POST['title']) && !empty($_POST['title'])) {
     $search_values['Title']= filter_var($_POST['title'], FILTER_SANITIZE_STRING);
}

But there’s a second step.   In addition to sanitizing data, you also want to validate it to make sure that it’s in the expected format.  But, in order to validate correctly, you have to understand the rules about what’s expected. Sometimes the rules are obvious, but other times you’re working with a bit of a black box.

Third party APIs are like black boxes.  You can’t read the code.   You only have the documentation to rely upon.   Giving the wrong input will get you errors or no results, and you might not know what you’re doing wrong.  So, you need to experiment and see if you can figure out what’s allowed and what’s not.

This post is specific to the Amazon Product Advertising API, which is an Amazon service that allows you to look up product information, like availability, pricing, keyword search, etc.

I use the Amazon Product Advertising API for several different projects, and for one of them, my application uses the ItemSearch operation. Specifically, I often search the Books category by Title.

Amazon’s API behaves differently than the search function on their site.  On their site, if you put in an apostrophe, the site just ignores it.  When using the API, you get no results.

So, the solution is to always strip the user input of all apostrophes. Or, if you’re a fellow programmer, you might call them single quotes. If you leave the apostrophe in, you’ll get no results. If you remove it, you hopefully get lots of results!

In my program, I am sending the user input via an HTML Form using Ajax.  My browser actually encodes the user input before sending it to the server.  It encodes the apostrophe character as its html entity which is '.

So, in my back end PHP code, I remove the apostrophe with this:


str_replace("'", "", $my_search_string);

Problem solved!   In actuality, I have combined that function with my code for sanitization, and the whole thing looks like:

 


if(isset($_POST['title']) && !empty($_POST['title'])) {
     $search_values['Title'] = str_replace("'", "", filter_var($_POST['title'], FILTER_SANITIZE_STRING));
}