PluginProbe
Polylang / 2.0.3
Polylang v2.0.3
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / install / plugin-updater.php

plugin-updater.php in Polylang 2.0.3, at install/plugin-updater.php

384 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // uncomment this line for testing
4 //set_site_transient( 'update_plugins', null );
5
6 // Exit if accessed directly
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit;
9 }
10
11 /**
12 * Allows plugins to use their own update API.
13 * Modified version with 'polylang' text domain and comments for translators
14 *
15 * @author Pippin Williamson
16 * @version 1.6.3
17 */
18 class PLL_Plugin_Updater {
19 private $api_url = '';
20 private $api_data = array();
21 private $name = '';
22 private $slug = '';
23 private $version = '';
24
25 /**
26 * Class constructor.
27 *
28 * @uses plugin_basename()
29 * @uses hook()
30 *
31 * @param string $_api_url The URL pointing to the custom API endpoint.
32 * @param string $_plugin_file Path to the plugin file.
33 * @param array $_api_data Optional data to send with API calls.
34 */
35 public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
36
37 global $edd_plugin_data;
38
39 $this->api_url = trailingslashit( $_api_url );
40 $this->api_data = $_api_data;
41 $this->name = plugin_basename( $_plugin_file );
42 $this->slug = basename( $_plugin_file, '.php' );
43 $this->version = $_api_data['version'];
44
45 $edd_plugin_data[ $this->slug ] = $this->api_data;
46
47 // Set up hooks.
48 $this->init();
49
50 }
51
52 /**
53 * Set up WordPress filters to hook into WP's update process.
54 *
55 * @uses add_filter()
56 *
57 * @return void
58 */
59 public function init() {
60
61 add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
62 add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
63 remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10, 2 );
64 add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
65 add_action( 'admin_init', array( $this, 'show_changelog' ) );
66
67 }
68
69 /**
70 * Check for Updates at the defined API endpoint and modify the update array.
71 *
72 * This function dives into the update API just when WordPress creates its update array,
73 * then adds a custom API call and injects the custom plugin data retrieved from the API.
74 * It is reassembled from parts of the native WordPress plugin update code.
75 * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
76 *
77 * @uses api_request()
78 *
79 * @param array $_transient_data Update array build by WordPress.
80 * @return array Modified update array with custom plugin data.
81 */
82 public function check_update( $_transient_data ) {
83
84 global $pagenow;
85
86 if ( ! is_object( $_transient_data ) ) {
87 $_transient_data = new stdClass;
88 }
89
90 if ( 'plugins.php' == $pagenow && is_multisite() ) {
91 return $_transient_data;
92 }
93
94 if ( empty( $_transient_data->response ) || empty( $_transient_data->response[ $this->name ] ) ) {
95
96 $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
97
98 if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
99
100 if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
101
102 $_transient_data->response[ $this->name ] = $version_info;
103
104 }
105
106 $_transient_data->last_checked = time();
107 $_transient_data->checked[ $this->name ] = $this->version;
108
109 }
110
111 }
112
113 return $_transient_data;
114 }
115
116 /**
117 * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
118 *
119 * @param string $file
120 * @param array $plugin
121 */
122 public function show_update_notification( $file, $plugin ) {
123
124 if ( ! current_user_can( 'update_plugins' ) ) {
125 return;
126 }
127
128 if ( ! is_multisite() ) {
129 return;
130 }
131
132 if ( $this->name != $file ) {
133 return;
134 }
135
136 // Remove our filter on the site transient
137 remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
138
139 $update_cache = get_site_transient( 'update_plugins' );
140
141 $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
142
143 if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
144
145 $cache_key = md5( 'edd_plugin_' . sanitize_key( $this->name ) . '_version_info' );
146 $version_info = get_transient( $cache_key );
147
148 if ( false === $version_info ) {
149
150 $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
151
152 set_transient( $cache_key, $version_info, 3600 );
153 }
154
155 if ( ! is_object( $version_info ) ) {
156 return;
157 }
158
159 if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
160
161 $update_cache->response[ $this->name ] = $version_info;
162
163 }
164
165 $update_cache->last_checked = time();
166 $update_cache->checked[ $this->name ] = $this->version;
167
168 set_site_transient( 'update_plugins', $update_cache );
169
170 } else {
171
172 $version_info = $update_cache->response[ $this->name ];
173
174 }
175
176 // Restore our filter
177 add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
178
179 if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
180
181 // build a plugin list row, with update notification
182 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
183 echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
184
185 $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
186
187 if ( empty( $version_info->download_link ) ) {
188 printf(
189 /* translators: %1$s plugin name, %3$s plugin version, %2$s and %4$s are html tags */
190 esc_html__( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'polylang' ),
191 esc_html( $version_info->name ),
192 '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
193 esc_html( $version_info->new_version ),
194 '</a>'
195 );
196 } else {
197 printf(
198 /* translators: %1$s plugin name, %3$s plugin version, %2$s, %4$s, %5$s and %6$s are html tags */
199 esc_html__( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'polylang' ),
200 esc_html( $version_info->name ),
201 '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
202 esc_html( $version_info->new_version ),
203 '</a>',
204 '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
205 '</a>'
206 );
207 }
208
209 do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
210
211 echo '</div></td></tr>';
212 }
213 }
214
215
216 /**
217 * Updates information on the "View version x.x details" page with custom data.
218 *
219 * @uses api_request()
220 *
221 * @param mixed $_data
222 * @param string $_action
223 * @param object $_args
224 * @return object $_data
225 */
226 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
227
228
229 if ( $_action != 'plugin_information' ) {
230
231 return $_data;
232
233 }
234
235 if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
236
237 return $_data;
238
239 }
240
241 $to_send = array(
242 'slug' => $this->slug,
243 'is_ssl' => is_ssl(),
244 'fields' => array(
245 'banners' => false, // These will be supported soon hopefully
246 'reviews' => false
247 )
248 );
249
250 $api_response = $this->api_request( 'plugin_information', $to_send );
251
252 if ( false !== $api_response ) {
253 $_data = $api_response;
254 }
255
256 return $_data;
257 }
258
259
260 /**
261 * Disable SSL verification in order to prevent download update failures
262 *
263 * @param array $args
264 * @param string $url
265 * @return object $array
266 */
267 public function http_request_args( $args, $url ) {
268 // If it is an https request and we are performing a package download, disable ssl verification
269 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
270 $args['sslverify'] = false;
271 }
272 return $args;
273 }
274
275 /**
276 * Calls the API and, if successfull, returns the object delivered by the API.
277 *
278 * @uses get_bloginfo()
279 * @uses wp_remote_post()
280 * @uses is_wp_error()
281 *
282 * @param string $_action The requested action.
283 * @param array $_data Parameters for the API action.
284 * @return false|object
285 */
286 private function api_request( $_action, $_data ) {
287
288 global $wp_version;
289
290 $data = array_merge( $this->api_data, $_data );
291
292 if ( $data['slug'] != $this->slug ) {
293 return;
294 }
295
296 if ( $this->api_url == home_url() ) {
297 return false; // Don't allow a plugin to ping itself
298 }
299
300 $api_params = array(
301 'edd_action' => 'get_version',
302 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
303 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
304 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
305 'slug' => $data['slug'],
306 'author' => $data['author'],
307 'url' => home_url()
308 );
309
310 $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
311
312 if ( ! is_wp_error( $request ) ) {
313 $request = json_decode( wp_remote_retrieve_body( $request ) );
314 }
315
316 if ( $request && isset( $request->sections ) ) {
317 $request->sections = maybe_unserialize( $request->sections );
318 } else {
319 $request = false;
320 }
321
322 return $request;
323 }
324
325 public function show_changelog() {
326
327 global $edd_plugin_data;
328
329 if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
330 return;
331 }
332
333 if ( empty( $_REQUEST['plugin'] ) ) {
334 return;
335 }
336
337 if ( empty( $_REQUEST['slug'] ) ) {
338 return;
339 }
340
341 if ( ! current_user_can( 'update_plugins' ) ) {
342 wp_die( __( 'You do not have permission to install plugin updates', 'polylang' ), __( 'Error', 'polylang' ), array( 'response' => 403 ) );
343 }
344
345 $data = $edd_plugin_data[ $_REQUEST['slug'] ];
346 $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_version_info' );
347 $version_info = get_transient( $cache_key );
348
349 if ( false === $version_info ) {
350
351 $api_params = array(
352 'edd_action' => 'get_version',
353 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
354 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
355 'slug' => $_REQUEST['slug'],
356 'author' => $data['author'],
357 'url' => home_url()
358 );
359
360 $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
361
362 if ( ! is_wp_error( $request ) ) {
363 $version_info = json_decode( wp_remote_retrieve_body( $request ) );
364 }
365
366 if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
367 $version_info->sections = maybe_unserialize( $version_info->sections );
368 } else {
369 $version_info = false;
370 }
371
372 set_transient( $cache_key, $version_info, 3600 );
373
374 }
375
376 if ( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
377 echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
378 }
379
380 exit;
381 }
382
383 }
384