I am developing a game, and I am planning that, I will have partners, so they can run a game like mine.
For this, I developed a plugin, with some classes, and functions, to handle the game requests.
Because, this plugin is totally unusable for all the wordpress users, I want to send to my partners this plugin in an email in zipped format, but I want them to make able to upgrade plugin, if they need.
So, is it possible, to store the plugin on my own server, and force plugin to check updates from my site, not from the wp plugins directory?
I am developing a game, and I am planning that, I will have partners, so they can run a game like mine.
For this, I developed a plugin, with some classes, and functions, to handle the game requests.
Because, this plugin is totally unusable for all the wordpress users, I want to send to my partners this plugin in an email in zipped format, but I want them to make able to upgrade plugin, if they need.
So, is it possible, to store the plugin on my own server, and force plugin to check updates from my site, not from the wp plugins directory?
All Credit to the following post goes to Abid Omar. The full tutorial can be found on Tuts+: A Guide to the WordPress HTTP API: Automatic Plugin Updates
View Plugin On Github
The gist is to create a new class which will post an external file ( API ) and return plugin to download if it's out of date. The below assumes you're creating a plugin using the following code ( not in functions.php
or mu-plugins
directory ).
wp_autoupdate.php
will be an external file ( in your plugin ) holding the Auto Update class defined belowupdate.php
is an external "remote" file used to check for update, creates the API.The below will be our initial setup.
add_action( 'init', 'wptuts_activate_au' );
function wptuts_activate_au()
{
require_once ('wp_autoupdate.php'); // File which contains the Class below
$wptuts_plugin_current_version = '1.0';
$wptuts_plugin_remote_path = 'http://localhost/update.php';
$wptuts_plugin_slug = plugin_basename(__FILE__);
new wp_auto_update( $wptuts_plugin_current_version, $wptuts_plugin_remote_path, $wptuts_plugin_slug );
}
Autoupdate Class - wp_autoupdate.php
class wp_auto_update
{
/**
* The plugin current version
* @var string
*/
public $current_version;
/**
* The plugin remote update path
* @var string
*/
public $update_path;
/**
* Plugin Slug (plugin_directory/plugin_file.php)
* @var string
*/
public $plugin_slug;
/**
* Plugin name (plugin_file)
* @var string
*/
public $slug;
/**
* Initialize a new instance of the WordPress Auto-Update class
* @param string $current_version
* @param string $update_path
* @param string $plugin_slug
*/
function __construct( $current_version, $update_path, $plugin_slug )
{
// Set the class public variables
$this->current_version = $current_version;
$this->update_path = $update_path;
$this->plugin_slug = $plugin_slug;
list ($t1, $t2) = explode('/', $plugin_slug);
$this->slug = str_replace( '.php', '', $t2 );
// define the alternative API for updating checking
add_filter( 'pre_set_site_transient_update_plugins', array( &$this, 'check_update' ) );
// Define the alternative response for information checking
add_filter('plugins_api', array(&$this, 'check_info'), 10, 3);
}
/**
* Add our self-hosted autoupdate plugin to the filter transient
*
* @param $transient
* @return object $ transient
*/
public function check_update( $transient )
{
if( empty( $transient->checked ) ) {
return $transient;
}
// Get the remote version
$remote_version = $this->getRemote_version();
// If a newer version is available, add the update
if ( version_compare( $this->current_version, $remote_version, '<' ) ) {
$obj = new stdClass();
$obj->slug = $this->slug;
$obj->new_version = $remote_version;
$obj->url = $this->update_path;
$obj->package = $this->update_path;
$transient->response[$this->plugin_slug] = $obj;
}
var_dump( $transient );
return $transient;
}
/**
* Add our self-hosted description to the filter
*
* @param boolean $false
* @param array $action
* @param object $arg
* @return bool|object
*/
public function check_info( $false, $action, $arg )
{
if( $arg->slug === $this->slug ) {
$information = $this->getRemote_information();
return $information;
}
return false;
}
/**
* Return the remote version
* @return string $remote_version
*/
public function getRemote_version()
{
$request = wp_remote_post( $this->update_path, array( 'body' => array( 'action' => 'version' ) ) );
if( ! is_wp_error( $request ) || wp_remote_retrieve_response_code( $request ) === 200 ) {
return $request['body'];
}
return false;
}
/**
* Get information about the remote version
* @return bool|object
*/
public function getRemote_information()
{
$request = wp_remote_post( $this->update_path, array( 'body' => array( 'action' => 'info' ) ) );
if( ! is_wp_error( $request ) || wp_remote_retrieve_response_code( $request ) === 200) {
return unserialize( $request['body'] );
}
return false;
}
/**
* Return the status of the plugin licensing
* @return boolean $remote_license
*/
public function getRemote_license()
{
$request = wp_remote_post( $this->update_path, array( 'body' => array( 'action' => 'license' ) ) );
if( ! is_wp_error( $request ) || wp_remote_retrieve_response_code( $request ) === 200 ) {
return $request['body'];
}
return false;
}
}
Remote ( External ) Update API - update.php
if( isset( $_POST['action'] ) ) {
switch( $_POST['action'] ) {
case 'version':
echo '1.1';
break;
case 'info':
$obj = new stdClass();
$obj->slug = 'plugin.php';
$obj->plugin_name = 'plugin.php';
$obj->new_version = '1.1';
$obj->requires = '3.0';
$obj->tested = '3.3.1';
$obj->downloaded = 12540;
$obj->last_updated = '2012-01-12';
$obj->sections = array(
'description' => 'The new version of the Auto-Update plugin',
'another_section' => 'This is another section',
'changelog' => 'Some new features'
);
$obj->download_link = 'http://localhost/update.php';
echo serialize( $obj );
case 'license':
echo 'false';
break;
}
} else {
header( 'Cache-Control: public' );
header( 'Content-Description: File Transfer' );
header( 'Content-Type: application/zip' );
readfile( 'update.zip' );
}
update.php
will keep the most up-to-date information on your plugin. Using the init
function you would pass the currently installed version so that it can post it to update.php
and check against the current it has. There's some room for improvement on the code to make it more streamlined but I used this to do exactly ( sorta ) what you're trying to do so it's a good jumping point.
Yes, it is possible to host your own plugin and have the plugin check for updates from your server. You need to put some custom code in your plugin to tell it to check with your server for updates. There are some frameworks to help you do that, try these:
Plugin Update Checker
Github Plugin Updater
I have returned to this "problem" several times over the years, and a few of the projects on GitHub shared in the answers here are fantastic, although some of them appear dead now. There have also been some WooCommerce extensions that have attempted to solve this problem while also providing a billing solution, but they were kinda janky and difficult to maintain in my experience.
However one other fantastic solution is Andy Fragen's Git Updater. Instead of having to host your plugin ZIP files on your (unreliable) self-hosted server and/or hack your plugins and themes to add more files and dependencies etc, you simply add a simple one-line "header" inside your style.css
file for WordPress themes or inside your plugin.php
file for plugins. Literally, that's all that's required.
Here's an example:
https://github.com/littlebizzy/hovercraft/blob/master/style.css
GitHub Theme URI: littlebizzy/hovercraft
Andy's approach is one of the cleanest I've seen, and saved me tons of time and headache trying to find a simple solution for updating my WordPress extensions.
It works out-of-the-box for public repos, and has support for API keys for private repos. So you could literally just have like a PayPal or Patreon subscription where customers pay for access to whichever code they want and then you provide them the API key to those private repos on GitHub or GitLab, etc. You don't need to manage any ZIP files or billing whatsover.
And the implementation only takes around 30 seconds for most projects.
I built an other solution to self-host plugins directly from Github and deliver updates. More details you can found at this post: https://eduardovillao.me/how-to-self-host-wordpress-plugins-on-github-and-deliver-updates/
In summary you will need just:
Good lock with your projects!