PluginProbe
Friends / 2.7.4
Friends v2.7.4
4.3.2 4.3.1 4.3.0 4.2.2 4.2.1 4.2.0 4.1.0 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.1 All 88 releases
friends / includes / class-plugin-installer.php

class-plugin-installer.php in Friends 2.7.4, at includes/class-plugin-installer.php

524 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friends Plugin Installer
4 *
5 * This contains the functions to install plugins for the Friends Plugin.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 /**
13 * This is the class to install plugins for the Friends Plugin. Adapted from the github link below.
14 *
15 * @since 1.0
16 *
17 * @package Friends
18 * @author Alex Kirk
19 * @author Darren Cooney
20 * @link https://github.com/dcooney/wordpress-plugin-installer
21 */
22 class Plugin_Installer {
23 /**
24 * Our plugins.
25 *
26 * @var array
27 */
28 private static $plugins = array();
29
30 /**
31 * Register the WordPress hooks
32 */
33 public static function register_hooks() {
34 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
35 add_action( 'wp_ajax_friends_plugin_installer', array( __CLASS__, 'ajax_plugin_installer' ) );
36 add_action( 'wp_ajax_friends_plugin_activation', array( __CLASS__, 'ajax_plugin_activation' ) );
37 add_action( 'wp_ajax_friends_plugin_deactivation', array( __CLASS__, 'ajax_plugin_deactivation' ) );
38 add_filter( 'plugins_api', array( __CLASS__, 'override_plugin_info' ), 20, 3 );
39 add_filter( 'site_transient_update_plugins', array( __CLASS__, 'override_plugin_push_update' ) );
40 add_filter( 'upgrader_post_install', array( __CLASS__, 'after_install' ), 10, 3 );
41 }
42
43 /**
44 * Initialize the display of the plugins.
45 *
46 * @since 1.0
47 */
48 public static function init() {
49 $additional_plugins = array(
50 'activitypub' => __( 'Adds ActivityPub support to your blog. Be followed on Mastodon, follow people on Mastodon with the Friends plugin.', 'friends' ),
51 'enable-mastodon-apps' => true,
52 );
53 $plugins = array_merge( $additional_plugins, self::get_friends_plugins() );
54 ksort( $plugins );
55
56 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
57
58 wp_enqueue_script( 'plugin-install' );
59 add_thickbox();
60 wp_enqueue_script( 'updates' );
61
62 foreach ( array_keys( (array) $plugins ) as $plugin_slug ) {
63
64 $button_classes = 'install button button-primary';
65 $button_text = __( 'Install Now', 'friends' );
66
67 $api = plugins_api(
68 'plugin_information',
69 array(
70 'slug' => sanitize_file_name( $plugin_slug ),
71 'fields' => array(
72 'short_description' => true,
73 'sections' => false,
74 'requires' => false,
75 'downloaded' => true,
76 'last_updated' => false,
77 'added' => false,
78 'tags' => false,
79 'compatibility' => false,
80 'homepage' => false,
81 'donate_link' => false,
82 'icons' => true,
83 'banners' => true,
84 ),
85 )
86 );
87
88 if ( isset( $additional_plugins[ $plugin_slug ] ) ) {
89 $api->more_info = admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_slug . '&TB_iframe=true' );
90 if ( is_string( $additional_plugins[ $plugin_slug ] ) ) {
91 $api->comment = $additional_plugins[ $plugin_slug ];
92 }
93 }
94 if ( ! is_wp_error( $api ) ) {
95 $main_plugin_file = self::get_plugin_file( $plugin_slug );
96 if ( $main_plugin_file && self::check_file_extension( $main_plugin_file ) ) {
97 if ( is_plugin_active( $main_plugin_file ) ) {
98 $button_classes = 'installed button disabled';
99 $button_text = __( 'Active' ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
100 } else {
101 $button_classes = 'activate button button-primary';
102 $button_text = __( 'Activate' ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
103 }
104 }
105 self::render_template( $api, $button_text, $button_classes );
106 }
107 }
108 }
109
110 /**
111 * Gets the available plugins.
112 *
113 * @return object The plugin information.
114 */
115 public static function get_friends_plugins() {
116 $cache_key = 'friends_plugins_info_v1';
117 $offline = apply_filters( 'friends_offline_mode', false );
118
119 $data = get_transient( $cache_key );
120 if ( $offline ) {
121 // We'll want to use the shipped file.
122 $data = false;
123 }
124
125 if ( false === $data || apply_filters( 'friends_deactivate_plugin_cache', false ) ) {
126 $remote = false;
127 if ( ! $offline ) {
128 $remote = wp_remote_get(
129 'https://raw.githubusercontent.com/akirk/friends/main/plugins.json',
130 array(
131 'timeout' => 10,
132 'headers' => array(
133 'Accept' => 'application/json',
134 ),
135 )
136 );
137
138 if (
139 is_wp_error( $remote )
140 || 200 !== wp_remote_retrieve_response_code( $remote )
141 || is_null( json_decode( wp_remote_retrieve_body( $remote ) ) )
142 ) {
143 $remote = false;
144 }
145 }
146
147 if ( false === $remote ) {
148 $remote = array(
149 'status_code' => 200,
150 'headers' => array(
151 'content-type' => 'application/json',
152 ),
153 'body' => file_get_contents( FRIENDS_PLUGIN_DIR . '/plugins.json' ),
154 );
155 }
156
157 $data = array();
158 foreach ( json_decode( wp_remote_retrieve_body( $remote ) ) as $slug => $plugin_data ) {
159 if ( 'activitypub' === $slug ) {
160 continue;
161 }
162 $plugin_data->sections = (array) $plugin_data->sections;
163 $data[ $slug ] = $plugin_data;
164 }
165
166 if ( ! $offline ) {
167 set_transient( $cache_key, $data, 12 * HOUR_IN_SECONDS );
168 }
169 }
170
171 if ( ! $data ) {
172 return array();
173 }
174
175 return $data;
176 }
177
178 /**
179 * Override the plugin info.
180 *
181 * @param false|object|array $res The resource.
182 * @param string $action The action.
183 * @param object $args The arguments.
184 *
185 * @return bool|stdClass ( description_of_the_return_value )
186 */
187 public static function override_plugin_info( $res, $action, $args ) {
188 if ( 'plugin_information' !== $action ) {
189 return $res;
190 }
191
192 $our_plugins = self::get_friends_plugins();
193 if ( ! isset( $our_plugins[ $args->slug ] ) ) {
194 return false;
195 }
196
197 return $our_plugins[ $args->slug ];
198 }
199
200 /**
201 * Override the plugin update function for our plugins.
202 *
203 * @param object|false $transient The transient.
204 *
205 * @return object The modified transient.
206 */
207 public static function override_plugin_push_update( $transient ) {
208 if ( empty( $transient->checked ) ) {
209 return $transient;
210 }
211
212 foreach ( self::get_friends_plugins() as $data ) {
213 if ( $data && isset( $transient->checked[ $data->slug . '/' . $data->slug . '.php' ] ) && version_compare( $transient->checked[ $data->slug . '/' . $data->slug . '.php' ], $data->version, '<' ) && ( ! isset( $data->requires ) || version_compare( $data->requires, get_bloginfo( 'version' ), '<' ) ) ) {
214 $res = new \stdClass();
215 $res->slug = $data->slug;
216 $res->plugin = $data->slug . '/' . $data->slug . '.php';
217 $res->new_version = $data->version;
218 if ( isset( $data->tested ) ) {
219 $res->tested = $data->tested;
220 }
221 $res->package = $data->download_link;
222 $transient->response[ $res->plugin ] = $res;
223 }
224 }
225
226 return $transient;
227 }
228
229 /**
230 * Render display template for each plugin.
231 *
232 * @param object $api Results from plugins_api.
233 * @param string $button_text Text for the button.
234 * @param string $button_classes Classnames for the button.
235 *
236 * @since 1.0
237 */
238 public static function render_template( $api, $button_text, $button_classes ) {
239 $args = array(
240 'api' => $api,
241 'button_classes' => $button_classes,
242 'button_text' => $button_text,
243 'deactivate_button_class' => '',
244 );
245
246 $args['install_url'] = add_query_arg(
247 array(
248 'tab' => 'install-plugin',
249 'plugin' => $api->slug,
250 '_wpnonce' => wp_create_nonce( 'install-plugin_' . $api->slug ),
251 ),
252 admin_url( 'update.php' )
253 );
254 if ( preg_match( '/\b(install|activate)\b/', $button_classes ) ) {
255 $args['deactivate_button_class'] = 'hidden';
256 }
257
258 Friends::template_loader()->get_template_part( 'admin/plugin-info', null, $args );
259 }
260
261 /**
262 * An Ajax method for installing plugin.
263 */
264 public static function ajax_plugin_installer() {
265
266 if ( ! current_user_can( 'install_plugins' ) ) {
267 wp_die( esc_html( __( 'Sorry, you are not allowed to install plugins on this site.', 'friends' ) ) );
268 }
269
270 $nonce = $_POST['nonce']; // phpcs:ignore
271 $plugin = $_POST['plugin']; // phpcs:ignore
272
273 // Check our nonce, if they don't match then bounce!
274 if ( ! wp_verify_nonce( $nonce, 'friends_installer_nonce' ) ) {
275 wp_die( esc_html( __( 'Error - unable to verify nonce, please try again.', 'friends' ) ) );
276 }
277
278 // Include required libs for installation.
279 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
280 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
281 require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
282 require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
283
284 $api = plugins_api(
285 'plugin_information',
286 array(
287 'slug' => $plugin,
288 'fields' => array(
289 'short_description' => false,
290 'sections' => false,
291 'requires' => false,
292 'rating' => false,
293 'ratings' => false,
294 'downloaded' => false,
295 'last_updated' => false,
296 'added' => false,
297 'tags' => false,
298 'compatibility' => false,
299 'homepage' => false,
300 'donate_link' => false,
301 ),
302 )
303 );
304
305 $skin = new \WP_Ajax_Upgrader_Skin();
306 $upgrader = new \Plugin_Upgrader( $skin );
307 $upgrader->install( $api->download_link );
308
309 if ( $api->name ) {
310 $status = 'success';
311 $msg = $api->name . ' successfully installed.';
312 } else {
313 $status = 'failed';
314 $msg = 'There was an error installing ' . $api->name . '.';
315 }
316
317 $json = array(
318 'status' => $status,
319 'msg' => $msg,
320 );
321
322 wp_send_json( $json );
323 }
324
325 /**
326 * Activate plugin via Ajax.
327 */
328 public static function ajax_plugin_activation() {
329 if ( ! current_user_can( 'install_plugins' ) ) {
330 wp_die( esc_html( __( 'Sorry, you are not allowed to activate plugins on this site.', 'friends' ) ) );
331 }
332
333 $nonce = $_POST['nonce']; // phpcs:ignore
334 $plugin = $_POST['plugin']; // phpcs:ignore
335
336 // Check our nonce, if they don't match then bounce!
337 if ( ! wp_verify_nonce( $nonce, 'friends_installer_nonce' ) ) {
338 die( esc_html( __( 'Error - unable to verify nonce, please try again.', 'friends' ) ) );
339 }
340
341 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
342 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
343 require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
344
345 $api = plugins_api(
346 'plugin_information',
347 array(
348 'slug' => $plugin,
349 'fields' => array(
350 'short_description' => false,
351 'sections' => false,
352 'requires' => false,
353 'rating' => false,
354 'ratings' => false,
355 'downloaded' => false,
356 'last_updated' => false,
357 'added' => false,
358 'tags' => false,
359 'compatibility' => false,
360 'homepage' => false,
361 'donate_link' => false,
362 ),
363 )
364 );
365
366 if ( $api->name ) {
367 $main_plugin_file = self::get_plugin_file( $plugin );
368 $status = 'success';
369 if ( $main_plugin_file ) {
370 activate_plugin( $main_plugin_file );
371 $msg = $api->name . ' successfully activated.';
372 }
373 } else {
374 $status = 'failed';
375 $msg = 'There was an error activating ' . $api->name . '.';
376 }
377
378 $json = array(
379 'status' => $status,
380 'msg' => $msg,
381 );
382
383 wp_send_json( $json );
384 }
385
386 /**
387 * Deativate plugin via Ajax.
388 */
389 public static function ajax_plugin_deactivation() {
390 if ( ! current_user_can( 'install_plugins' ) ) {
391 wp_die( esc_html( __( 'Sorry, you are not allowed to activate plugins on this site.', 'friends' ) ) );
392 }
393
394 $nonce = $_POST['nonce']; // phpcs:ignore
395 $plugin = $_POST['plugin']; // phpcs:ignore
396
397 // Check our nonce, if they don't match then bounce!
398 if ( ! wp_verify_nonce( $nonce, 'friends_installer_nonce' ) ) {
399 die( esc_html( __( 'Error - unable to verify nonce, please try again.', 'friends' ) ) );
400 }
401
402 require_once ABSPATH . 'wp-admin/includes/plugin.php';
403
404 deactivate_plugins( $plugin . '/' . $plugin . '.php' );
405
406 $msg = $plugin . ' successfully deactivated.';
407
408 $json = array(
409 'status' => 'success',
410 'msg' => $msg,
411 );
412
413 wp_send_json( $json );
414
415 }
416
417 /**
418 * Fix wrong slug through Github-added version to ZIP.
419 *
420 * @param bool $response Installation response.
421 * @param array $hook_extra Extra arguments passed to hooked filters.
422 * @param array $result Installation result data.
423 *
424 * @return bool $response Installation response.
425 */
426 public static function after_install( $response, $hook_extra, $result ) {
427 if ( empty( $result['destination_name'] ) ) {
428 return $response;
429 }
430
431 // Is it a versioned destination name?
432 if ( ! preg_match( '#-[0-9]+(?:\.[0-9]+)+$#', $result['destination_name'], $m ) ) {
433 return $response;
434 }
435
436 // This contains the leading slash.
437 $dashed_version = $m[0];
438
439 // Strip off the version to get the real slug.
440 $slug = substr( $result['destination_name'], 0, - strlen( $dashed_version ) );
441
442 $our_plugins = self::get_friends_plugins();
443 if ( ! isset( $our_plugins[ $slug ] ) ) {
444 return $response;
445 }
446
447 // Is this really the plugin ZIP?
448 if ( ! in_array( $slug . '.php', $result['source_files'] ) ) {
449 return $response;
450 }
451
452 $install_directory = str_replace( $result['destination_name'], $slug, $result['destination'] );
453
454 global $wp_filesystem;
455 $wp_filesystem->move( $result['destination'], $install_directory );
456 $result['destination'] = $install_directory;
457
458 return $result;
459 }
460 /**
461 * A method to get the main plugin file.
462 *
463 * @param string $plugin_slug The plugin slug.
464 *
465 * @return $plugin_file
466 *
467 * @since 1.0
468 */
469 public static function get_plugin_file( $plugin_slug ) {
470 require_once ABSPATH . '/wp-admin/includes/plugin.php';
471 $plugins = get_plugins();
472
473 foreach ( $plugins as $plugin_file => $plugin_info ) {
474 $slug = dirname( plugin_basename( $plugin_file ) );
475 if ( $slug ) {
476 if ( $slug === $plugin_slug ) {
477 return $plugin_file;
478 }
479 }
480 }
481 return null;
482 }
483
484 /**
485 * A helper to check file extension
486 *
487 * @param string $filename The filename.
488 *
489 * @return boolean
490 *
491 * @since 1.0
492 */
493 public static function check_file_extension( $filename ) {
494 if ( substr( strrchr( $filename, '.' ), 1 ) === 'php' ) {
495 return true;
496 } else {
497 // ./wp-content/plugins
498 return false;
499 }
500 }
501
502 /**
503 * Enqueue admin scripts and scripts localization
504 *
505 * @since 1.0
506 */
507 public static function enqueue_scripts() {
508 wp_enqueue_script( 'friends-plugin-installer', plugins_url( 'plugin-installer.js', FRIENDS_PLUGIN_FILE ), array( 'jquery' ), FRIENDS_VERSION, true );
509 wp_localize_script(
510 'friends-plugin-installer',
511 'friends_plugin_installer_localize',
512 array(
513 'ajax_url' => admin_url( 'admin-ajax.php' ),
514 'admin_nonce' => wp_create_nonce( 'friends_installer_nonce' ),
515 'installing' => __( 'Installing' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
516 'install_btn' => __( 'Install Now', 'friends' ),
517 'activate_btn' => __( 'Activate' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
518 'deactivate_btn' => __( 'Deactivate' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
519 'installed_btn' => _x( 'Active', 'plugin', 'friends' ),
520 )
521 );
522 }
523 }
524