PluginProbe
Friends / 2.7.8
Friends v2.7.8
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.8, at includes/class-plugin-installer.php

523 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 * Fix wrong slug through Github-added version to ZIP.
418 *
419 * @param bool $response Installation response.
420 * @param array $hook_extra Extra arguments passed to hooked filters.
421 * @param array $result Installation result data.
422 *
423 * @return bool $response Installation response.
424 */
425 public static function after_install( $response, $hook_extra, $result ) {
426 if ( empty( $result['destination_name'] ) ) {
427 return $response;
428 }
429
430 // Is it a versioned destination name?
431 if ( ! preg_match( '#-[0-9]+(?:\.[0-9]+)+$#', $result['destination_name'], $m ) ) {
432 return $response;
433 }
434
435 // This contains the leading slash.
436 $dashed_version = $m[0];
437
438 // Strip off the version to get the real slug.
439 $slug = substr( $result['destination_name'], 0, - strlen( $dashed_version ) );
440
441 $our_plugins = self::get_friends_plugins();
442 if ( ! isset( $our_plugins[ $slug ] ) ) {
443 return $response;
444 }
445
446 // Is this really the plugin ZIP?
447 if ( ! in_array( $slug . '.php', $result['source_files'] ) ) {
448 return $response;
449 }
450
451 $install_directory = str_replace( $result['destination_name'], $slug, $result['destination'] );
452
453 global $wp_filesystem;
454 $wp_filesystem->move( $result['destination'], $install_directory );
455 $result['destination'] = $install_directory;
456
457 return $result;
458 }
459 /**
460 * A method to get the main plugin file.
461 *
462 * @param string $plugin_slug The plugin slug.
463 *
464 * @return $plugin_file
465 *
466 * @since 1.0
467 */
468 public static function get_plugin_file( $plugin_slug ) {
469 require_once ABSPATH . '/wp-admin/includes/plugin.php';
470 $plugins = get_plugins();
471
472 foreach ( $plugins as $plugin_file => $plugin_info ) {
473 $slug = dirname( plugin_basename( $plugin_file ) );
474 if ( $slug ) {
475 if ( $slug === $plugin_slug ) {
476 return $plugin_file;
477 }
478 }
479 }
480 return null;
481 }
482
483 /**
484 * A helper to check file extension
485 *
486 * @param string $filename The filename.
487 *
488 * @return boolean
489 *
490 * @since 1.0
491 */
492 public static function check_file_extension( $filename ) {
493 if ( substr( strrchr( $filename, '.' ), 1 ) === 'php' ) {
494 return true;
495 } else {
496 // ./wp-content/plugins
497 return false;
498 }
499 }
500
501 /**
502 * Enqueue admin scripts and scripts localization
503 *
504 * @since 1.0
505 */
506 public static function enqueue_scripts() {
507 wp_enqueue_script( 'friends-plugin-installer', plugins_url( 'plugin-installer.js', FRIENDS_PLUGIN_FILE ), array( 'jquery' ), FRIENDS_VERSION, true );
508 wp_localize_script(
509 'friends-plugin-installer',
510 'friends_plugin_installer_localize',
511 array(
512 'ajax_url' => admin_url( 'admin-ajax.php' ),
513 'admin_nonce' => wp_create_nonce( 'friends_installer_nonce' ),
514 'installing' => __( 'Installing' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
515 'install_btn' => __( 'Install Now', 'friends' ),
516 'activate_btn' => __( 'Activate' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
517 'deactivate_btn' => __( 'Deactivate' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
518 'installed_btn' => _x( 'Active', 'plugin', 'friends' ),
519 )
520 );
521 }
522 }
523