├── README.md ├── plugin.php ├── update.php └── wp_autoupdate.php /README.md: -------------------------------------------------------------------------------- 1 | # Self-Hosted-WordPress-Plugin-repository 2 | 3 | Create your own self-hosted WordPress Plugin repository for pushing automatic updates. 4 | 5 | For integration with Composer, please use [wp-autoupdate](https://github.com/wpplex/wp-autoupdate) 6 | 7 | ## Quick Start 8 | 9 | 1) Place the `wp_autoupdate.php` file somewhere in your plugin directory and require it. 10 | ```php 11 | require_once( 'wp_autoupdate.php' ); 12 | ``` 13 | 2) Hook the [init](https://codex.wordpress.org/Plugin_API/Action_Reference/init) function to initiatilize the update function when your plugin loads. Best put in your main `plugin.php` file: 14 | ```php 15 | function snb_activate_au() 16 | { 17 | // set auto-update params 18 | $plugin_current_version = ' e.g. "0.6"'; 19 | $plugin_remote_path = ' e.g. http://update.example.com'; 20 | $plugin_slug = plugin_basename(__FILE__); 21 | $license_user = ''; 22 | $license_key = ''; 23 | 24 | // only perform Auto-Update call if a license_user and license_key is given 25 | if ( $license_user && $license_key && $plugin_remote_path ) 26 | { 27 | new wp_autoupdate ($plugin_current_version, $plugin_remote_path, $plugin_slug, $license_user, $license_key); 28 | } 29 | } 30 | 31 | add_action('init', 'snb_activate_au'); 32 | ``` 33 | 34 | The `license_user` and `license_key` fields are optional. You can use these to implement an auto-update functionility for specified customers only. It's left to the developer to implement this if needed. 35 | 36 | Note that it's possible to store certain settings as a Wordpress [option](https://codex.wordpress.org/Options_API) like the `plugin_remote_path` version. 37 | If you do so, you can use `get_option()` to get fields like `plugin_remote_path`, `license_user`, `license_key` directly from your plugin. This increases maintainability. 38 | 39 | 3) Create your server back-end to handle the update requests. You are fee to implement this any way you want, with any framework you want. 40 | The idea is that when Wordpress loads your plugin, it will check the given remote path to see if an update is availabe through the returned transient. For a basic implementation see the example below. 41 | 42 | Note however this example does not provide any protection or security, it serves as a demonstration purpose only. 43 | 44 | ```php 45 | if (isset($_POST['action'])) { 46 | switch ($_POST['action']) { 47 | case 'version': 48 | echo '1.1'; 49 | break; 50 | case 'info': 51 | $obj = new stdClass(); 52 | $obj->slug = 'plugin.php'; 53 | $obj->plugin_name = 'plugin.php'; 54 | $obj->new_version = '1.1'; 55 | $obj->requires = '3.0'; 56 | $obj->tested = '3.3.1'; 57 | $obj->downloaded = 12540; 58 | $obj->last_updated = '2012-01-12'; 59 | $obj->sections = array( 60 | 'description' => 'The new version of the Auto-Update plugin', 61 | 'another_section' => 'This is another section', 62 | 'changelog' => 'Some new features' 63 | ); 64 | $obj->download_link = 'http://localhost/repository/update.zip'; 65 | echo serialize($obj); 66 | case 'license': 67 | echo 'false'; 68 | break; 69 | } 70 | } else { 71 | header('Cache-Control: public'); 72 | header('Content-Description: File Transfer'); 73 | header('Content-Type: application/zip'); 74 | readfile('update.zip'); 75 | } 76 | ``` 77 | 78 | 4) Make sure the `download_link` points to a `*.zip` file that holds the new version of your plugin. This `*.zip` file must have the same name as your WordPress plugin does. Also the `*.zip` file must NOT contain the plugin files directly, but must have a subfolder with the same name as your plugin to make WordPress play nicely with it. 79 | e.g.: 80 | ```php 81 | my-plugin.zip 82 | │ 83 | └ my-plugin 84 | │ 85 | ├ my-plugin.php 86 | ├ README.txt 87 | ├ uninstall.php 88 | ├ index.php 89 | ├ .. 90 | └ etc. 91 | ``` 92 | 93 | # More information 94 | 95 | You could find detailed explanation and example of usage [here](http://code.tutsplus.com/tutorials/a-guide-to-the-wordpress-http-api-automatic-plugin-updates--wp-25181) 96 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | slug = 'plugin.php'; 14 | $obj->name = 'Plugin'; 15 | $obj->plugin_name = 'plugin.php'; 16 | $obj->new_version = '1.1'; 17 | // the url for the plugin homepage 18 | $obj->url = 'http://www.example.com/plugins/my-plugin'; 19 | //the download location for the plugin zip file (can be any internet host) 20 | $obj->package = 'http://mybucket.s3.amazonaws.com/plugin/plugin.zip'; 21 | 22 | switch ( $_POST['action'] ) { 23 | 24 | case 'version': 25 | //echo serialize( $obj ); 26 | echo json_encode( $obj ); 27 | break; 28 | case 'info': 29 | $obj->requires = '4.0'; 30 | $obj->tested = '4.0'; 31 | $obj->downloaded = 12540; 32 | $obj->last_updated = '2012-10-17'; 33 | $obj->sections = array( 34 | 'description' => 'The new version of the Auto-Update plugin', 35 | 'another_section' => 'This is another section', 36 | 'changelog' => 'Some new features' 37 | ); 38 | $obj->download_link = $obj->package; 39 | //echo serialize($obj); 40 | echo json_encode( $obj ); 41 | case 'license': 42 | //echo serialize( $obj ); 43 | echo json_encode( $obj ); 44 | break; 45 | } 46 | 47 | ?> 48 | -------------------------------------------------------------------------------- /wp_autoupdate.php: -------------------------------------------------------------------------------- 1 | current_version = $current_version; 51 | $this->update_path = $update_path; 52 | 53 | // Set the License 54 | $this->license_user = $license_user; 55 | $this->license_key = $license_key; 56 | 57 | // Set the Plugin Slug 58 | $this->plugin_slug = $plugin_slug; 59 | list ($t1, $t2) = explode( '/', $plugin_slug ); 60 | $this->slug = str_replace( '.php', '', $t2 ); 61 | 62 | // define the alternative API for updating checking 63 | add_filter( 'pre_set_site_transient_update_plugins', array( &$this, 'check_update' ) ); 64 | 65 | // Define the alternative response for information checking 66 | add_filter( 'plugins_api', array( &$this, 'check_info' ), 10, 3 ); 67 | } 68 | 69 | /** 70 | * Add our self-hosted autoupdate plugin to the filter transient 71 | * 72 | * @param $transient 73 | * @return object $ transient 74 | */ 75 | public function check_update( $transient ) 76 | { 77 | if ( empty( $transient->checked ) ) { 78 | return $transient; 79 | } 80 | 81 | // Get the remote version 82 | $remote_version = $this->getRemote('version'); 83 | 84 | // If a newer version is available, add the update 85 | if ( version_compare( $this->current_version, $remote_version->new_version, '<' ) ) { 86 | $obj = new stdClass(); 87 | $obj->slug = $this->slug; 88 | $obj->new_version = $remote_version->new_version; 89 | $obj->url = $remote_version->url; 90 | $obj->plugin = $this->plugin_slug; 91 | $obj->package = $remote_version->package; 92 | $obj->tested = $remote_version->tested; 93 | $transient->response[$this->plugin_slug] = $obj; 94 | } 95 | return $transient; 96 | } 97 | 98 | /** 99 | * Add our self-hosted description to the filter 100 | * 101 | * @param boolean $false 102 | * @param array $action 103 | * @param object $arg 104 | * @return bool|object 105 | */ 106 | public function check_info($obj, $action, $arg) 107 | { 108 | if (($action=='query_plugins' || $action=='plugin_information') && 109 | isset($arg->slug) && $arg->slug === $this->slug) { 110 | return $this->getRemote('info'); 111 | } 112 | 113 | return $obj; 114 | } 115 | 116 | /** 117 | * Return the remote version 118 | * 119 | * @return string $remote_version 120 | */ 121 | public function getRemote($action = '') 122 | { 123 | $params = array( 124 | 'body' => array( 125 | 'action' => $action, 126 | 'license_user' => $this->license_user, 127 | 'license_key' => $this->license_key, 128 | ), 129 | ); 130 | 131 | // Make the POST request 132 | $request = wp_remote_post($this->update_path, $params ); 133 | 134 | // Check if response is valid 135 | if ( !is_wp_error( $request ) || wp_remote_retrieve_response_code( $request ) === 200 ) { 136 | //return @unserialize( $request['body'] ); 137 | return @json_decode($request['body']); 138 | } 139 | 140 | return false; 141 | } 142 | } 143 | --------------------------------------------------------------------------------