As you probably already know, WordPress has a mechanism that detects that the plugins, themes and the kernel itself WordPress have updates. Notify the user when they are available, to receive information about these updates and allows you to automatically install them. However, this only applies to plugins and templates so placed in repositories of WordPress.
Of course, there are ways to extend this mechanism for plugins and themes published on other servers. In the network there are many scripts for the ability to upgrade from GitHub and even from your own server. There are scripts to update with Market Envato (ThemeForest and CodeCanyon). However, all the scripts for the access to the Envato API, which are used today is obsolete because from June 2016 old Envato API will be closed. You must now use the new Envato API. I had to write the script yourself.
Script
The plan
All the upgrading data are provided by the server WordPress, i.e. When your blog requests this data from wordpress.org server, it gets all the necessary information. Envato Server also allows you to receive all the necessary information about updating the plugin that is sold on CodeCanyon (plugins on Envato Market), but with another data structure (other than from WordPress). Therefore our task is to:
- Obtain information of the availability of a new version of the plugin and notify WordPress about the existence of it
- Obtain information about the plugin for future viewing by the user of the plugin (blog administrator)
- Provide access to the file of the new version of the plugin on the server Envato to downloads and updates
All three point plan we can implement using three WordPress filters, namely pre_set_site_transient_update_plugins, plugins_api and upgrader_package_options.
Input Data
The script is implemented as a class (the PLO) and takes two startup parameter necessary for work. In principle, you can modify the class so that incoming data is defined in the class, but the class will lose its versatility.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public function __construct( $id, $data ) { if ( ! empty( $id ) ) { $this->itemId = $id; $this->personalToken = ( isset( $data['token'] ) ) ? $data['token'] : null; $this->currentVersion = ( isset( $data['version'] ) ) ? $data['version'] : null; $this->slug = ( isset( $data['slug'] ) ) ? $data['slug'] : null; $this->pluginSlug = ( isset( $data['pluginSlug'] ) ) ? $data['pluginSlug'] : null; $this->name = ( isset( $data['name'] ) ) ? $data['name'] : null; $this->homepage = ( isset( $data['homepage'] ) ) ? $data['homepage'] : ''; } $this->enabled = self::is_enabled(); if ( $this->enabled ) { add_filter( 'pre_set_site_transient_update_plugins', array( &$this, 'checkUpdate' ) ); add_filter( 'plugins_api', array( &$this, 'checkInfo' ), 10, 3 ); add_filter( 'upgrader_package_options', array( &$this, 'setUpdatePackage' ) ); } } |
To ensure the achievement of the objective, we need the following data:
- $id — ID of the plugin on the Envato Market
- $data[‘token’] — Personal Token of buyer
- $data[‘version’] — plugin current version
- $data[‘slug’] — slug of plugin (i.e.: sam-pro-lite)
- $data[‘pluginSlug’] — full slug of plugin (plugin folder + name of main plugin file, i.e.: sam-pro-lite/sam-pro-lite.php)
- $data[‘name’] — name of plugin
- $data[‘homepage’] – plugin homepage URL, not required
The buyer of the plugin can generate a Personal Token on the website of the Envato API. The minimum set of permissions for the token should be:
The constructor of plugin accepts these data as two parameters: a string $id and array $data. After verifying input data constructor assigns methods of class checkUpdate, checkInfo and setUpdatePackage as handlers of filters.
- pre_set_site_transient_update_plugins — checkUpdate
- plugins_api — checkInfo
- upgrader_package_options — getUpdatePackage
Getting Data from Envato API
For getting data from Envato API requires only ID of plugin (Envato Market) and Personal Token of the buyer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public function request( $data = 'info' ) { $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $this->personalToken, ), 'timeout' => 30, ); switch ( $data ) { case 'info': $url = '//api.envato.com/v2/market/catalog/item?id=' . $this->itemId; break; case 'link': $url = '//api.envato.com/v2/market/buyer/download?item_id=' . $this->itemId . '&shorten_url=true'; break; default: $url = '//api.envato.com/v2/market/catalog/item?id=' . $this->itemId; } $response = wp_remote_get( esc_url_raw( $url ), $args ); $response_code = wp_remote_retrieve_response_code( $response ); $response_message = wp_remote_retrieve_response_message( $response ); if ( 200 !== $response_code && ! empty( $response_message ) ) { return new WP_Error( $response_code, $response_message ); } elseif ( 200 !== $response_code ) { return new WP_Error( $response_code, __( 'An unknown API error occurred.', SAM_PRO_DOMAIN ) ); } else { $out = json_decode( wp_remote_retrieve_body( $response ), true ); if ( null === $out ) { return new WP_Error( 'api_error', __( 'An unknown API error occurred.', SAM_PRO_DOMAIN ) ); } return $out; } } |
Version Request
This script uses filter pre_set_site_transient_update_plugins for getting available version of plugin from Envato Market. And, if version of plugin on the Envato is greater than current version, reports WordPress on the need to update the plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public function checkUpdate( $transient ) { if ( empty( $transient->checked ) ) { return $transient; } $pluginInfo = self::request(); if ( isset( $pluginInfo['wordpress_plugin_metadata'] ) ) { $info = $pluginInfo['wordpress_plugin_metadata']; if ( version_compare( $this->currentVersion, $info['version'], '<' ) ) { $plugin = new stdClass(); $plugin->slug = $this->slug; $plugin->new_version = $info['version']; $plugin->url = ''; // We cannot specify the path to the plugin archive file for "package" field in advance. // Sets value as plugin slug to identify the plugin for future use. $plugin->package = $this->pluginSlug; $plugin->name = $info['plugin_name']; $plugin->plugin = $this->pluginSlug; $transient->response[ $this->pluginSlug ] = $plugin; } } return $transient; } |
The result of filter handling:
Plugin Info Request
When user (blog admin) clicks “View Details” this class requests Envato API to getting detail plugin information.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public function checkInfo( $result, $action, $args ) { if ( $args->slug === $this->slug ) { $pluginInfo = self::request(); if ( isset( $pluginInfo['wordpress_plugin_metadata'] ) ) { $info = $pluginInfo['wordpress_plugin_metadata']; $versions = self::getAttribute( $pluginInfo['attributes'], 'compatible-software' ); // Start: Separating description to sections // You deside how to share a description of the sections $sections = explode( '<h2 id="item-description__changelog">Changelog</h2>', $pluginInfo['description'] ); $description = ( isset( $sections[0] ) ) ? $sections[0] : ''; $changelog = ( isset( $sections[1] ) ) ? $sections[1] : ''; // End $plugin = new stdClass(); $plugin->name = $info['plugin_name']; $plugin->author = $info['author']; $plugin->slug = $this->slug; $plugin->version = $info['version']; $plugin->requires = $versions['required']; $plugin->tested = $versions['tested']; $plugin->rating = ( (int) $pluginInfo['rating']['count'] < 3 ) ? 100.0 : 20 * (float) $pluginInfo['rating']['rating']; $plugin->num_ratings = (int) $pluginInfo['rating']['count']; $plugin->active_installs = (int) $pluginInfo['number_of_sales']; $plugin->last_updated = $pluginInfo['updated_at']; $plugin->added = $pluginInfo['published_at']; $plugin->homepage = $this->homepage; $plugin->sections = array( 'description' => $description, 'changelog' => $changelog ); $plugin->download_link = $pluginInfo['url']; $plugin->banners = array( 'high' => $pluginInfo['previews']['landscape_preview']['landscape_url'] ); return $plugin; } else { return false; } } else { return false; } } |
The result of filter handling:
Upgrading Plugin
Identifying your plugin using “package” field of options array, previously set to Plugin Slug value. Getting a link to the plugin archive file from Envato and assign it to “package” field.
1 2 3 4 5 6 7 8 9 10 11 12 |
public function setUpdatePackage( $options ) { $package = $options['package']; if ( $package === $this->pluginSlug ) { $response = self::request( 'link' ); $options['package'] = ( is_wp_error( $response ) || empty( $response ) || ! empty( $response['error'] ) ) ? '' : $response['wordpress_plugin']; } return $options; } |
The result of filter handling:
Class Usage
Include this class into your plugin using “init” action.
1 |
add_action( 'init', array( &$this, 'updatingService' ) ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public function updatingService() { $settings = self::getSettings(); include_once ('tools/simplelib-plugin-upgrader.php'); $emToken = self::decryptData($settings['emToken']); $this->samProUpgrader = new SimpleLibPluginUpgrader('12721925', array( 'token' => $emToken, 'version' => SAM_PRO_VERSION, 'slug' => 'sam-pro-lite', 'pluginSlug' => basename( SAM_PRO_PATH ) . '/' . basename( SAM_PRO_MAIN_FILE ), 'name' => 'SAM Pro Lite', 'homepage' => '//uncle-sam.info/sam-pro-lite/sam-pro-lite-info/features/' )); } |
Full Script Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
<?php /** * Class SimpleLibPluginUpgrader. * Author: minimus * Date: 21.03.2016 * Time: 12:21 */ if ( ! class_exists( 'SimpleLibPluginUpgrader' ) ) { class SimpleLibPluginUpgrader { private $itemId = null; private $personalToken = null; private $currentVersion = null; private $slug = null; private $pluginSlug = null; private $name = null; private $homepage = ''; public $enabled = false; public function __construct( $id, $data ) { if ( ! empty( $id ) ) { $this->itemId = $id; $this->personalToken = ( isset( $data['token'] ) ) ? $data['token'] : null; $this->currentVersion = ( isset( $data['version'] ) ) ? $data['version'] : null; $this->slug = ( isset( $data['slug'] ) ) ? $data['slug'] : null; $this->pluginSlug = ( isset( $data['pluginSlug'] ) ) ? $data['pluginSlug'] : null; $this->name = ( isset( $data['name'] ) ) ? $data['name'] : null; $this->homepage = ( isset( $data['homepage'] ) ) ? $data['homepage'] : ''; } $this->enabled = self::is_enabled(); if ( $this->enabled ) { add_filter( 'pre_set_site_transient_update_plugins', array( &$this, 'checkUpdate' ) ); add_filter( 'plugins_api', array( &$this, 'checkInfo' ), 10, 3 ); add_filter( 'upgrader_package_options', array( &$this, 'setUpdatePackage' ) ); } } private function is_enabled() { return ( ! is_null( $this->itemId ) && ! is_null( $this->personalToken ) && ! is_null( $this->currentVersion ) && ! is_null( $this->slug ) && ! is_null( $this->pluginSlug ) && ! is_null( $this->name ) ); } private function getAttribute( $data, $name ) { $out = ''; foreach ( $data as $key => $val ) { if ( $val['name'] === $name ) { switch ( $name ) { case 'compatible-software': $out = array( 'required' => str_replace( 'WordPress ', '', $val['value'][ count( $val['value'] ) - 1 ] ), 'tested' => str_replace( 'WordPress ', '', $val['value'][0] ) ); break; default: $out = false; } } } return $out; } public function request( $data = 'info' ) { $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $this->personalToken, ), 'timeout' => 30, ); switch ( $data ) { case 'info': $url = '//api.envato.com/v2/market/catalog/item?id=' . $this->itemId; break; case 'link': $url = '//api.envato.com/v2/market/buyer/download?item_id=' . $this->itemId . '&shorten_url=true'; break; default: $url = '//api.envato.com/v2/market/catalog/item?id=' . $this->itemId; } $response = wp_remote_get( esc_url_raw( $url ), $args ); $response_code = wp_remote_retrieve_response_code( $response ); $response_message = wp_remote_retrieve_response_message( $response ); if ( 200 !== $response_code && ! empty( $response_message ) ) { return new WP_Error( $response_code, $response_message ); } elseif ( 200 !== $response_code ) { return new WP_Error( $response_code, __( 'An unknown API error occurred.', SAM_PRO_DOMAIN ) ); } else { $out = json_decode( wp_remote_retrieve_body( $response ), true ); if ( null === $out ) { return new WP_Error( 'api_error', __( 'An unknown API error occurred.', SAM_PRO_DOMAIN ) ); } return $out; } } public function checkUpdate( $transient ) { if ( empty( $transient->checked ) ) { return $transient; } $pluginInfo = self::request(); if ( isset( $pluginInfo['wordpress_plugin_metadata'] ) ) { $info = $pluginInfo['wordpress_plugin_metadata']; if ( version_compare( $this->currentVersion, $info['version'], '<' ) ) { $plugin = new stdClass(); $plugin->slug = $this->slug; $plugin->new_version = $info['version']; $plugin->url = ''; $plugin->package = $this->pluginSlug; $plugin->name = $info['plugin_name']; $plugin->plugin = $this->pluginSlug; $transient->response[ $this->pluginSlug ] = $plugin; } } return $transient; } public function checkInfo( $result, $action, $args ) { if ( $args->slug === $this->slug ) { $pluginInfo = self::request(); if ( isset( $pluginInfo['wordpress_plugin_metadata'] ) ) { $info = $pluginInfo['wordpress_plugin_metadata']; $versions = self::getAttribute( $pluginInfo['attributes'], 'compatible-software' ); $sections = explode( '<h2 id="item-description__changelog">Changelog</h2>', $pluginInfo['description'] ); $description = ( isset( $sections[0] ) ) ? $sections[0] : ''; $changelog = ( isset( $sections[1] ) ) ? $sections[1] : ''; $plugin = new stdClass(); $plugin->name = $info['plugin_name']; $plugin->author = $info['author']; $plugin->slug = $this->slug; $plugin->version = $info['version']; $plugin->requires = $versions['required']; $plugin->tested = $versions['tested']; $plugin->rating = ( (int) $pluginInfo['rating']['count'] < 3 ) ? 100.0 : 20 * (float) $pluginInfo['rating']['rating']; $plugin->num_ratings = (int) $pluginInfo['rating']['count']; $plugin->active_installs = (int) $pluginInfo['number_of_sales']; $plugin->last_updated = $pluginInfo['updated_at']; $plugin->added = $pluginInfo['published_at']; $plugin->homepage = $this->homepage; $plugin->sections = array( 'description' => $description, 'changelog' => $changelog ); $plugin->download_link = $pluginInfo['url']; $plugin->banners = array( 'high' => $pluginInfo['previews']['landscape_preview']['landscape_url'] ); return $plugin; } else { return false; } } else { return false; } } public function setUpdatePackage( $options ) { $package = $options['package']; if ( $package === $this->pluginSlug ) { $response = self::request( 'link' ); $options['package'] = ( is_wp_error( $response ) || empty( $response ) || ! empty( $response['error'] ) ) ? '' : $response['wordpress_plugin']; } return $options; } } } |
Just include this file in your project and use it.
© 2016, minimus. All rights reserved.
Leave A Comment