PluginProbe ʕ •ᴥ•ʔ
One Click Demo Import / 3.0.1
One Click Demo Import v3.0.1
3.4.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.4.0 2.0.0 2.0.1 2.0.2 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 3.0.0 3.0.1 3.0.2 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.3.0 3.4.0
one-click-demo-import / inc / PluginInstaller.php
one-click-demo-import / inc Last commit date
CreateDemoContent 5 years ago CustomizerImporter.php 5 years ago CustomizerOption.php 5 years ago Downloader.php 5 years ago Helpers.php 5 years ago ImportActions.php 5 years ago Importer.php 5 years ago Logger.php 5 years ago OneClickDemoImport.php 5 years ago PluginInstaller.php 5 years ago PluginInstallerSkin.php 5 years ago PluginInstallerSkinSilent.php 5 years ago ReduxImporter.php 5 years ago ViewHelpers.php 5 years ago WPCLICommands.php 5 years ago WXRImporter.php 5 years ago WidgetImporter.php 5 years ago
PluginInstaller.php
594 lines
1 <?php
2 /**
3 * Plugin Installer class - responsible for installing other plugins.
4 *
5 * @package ocdi
6 */
7
8 namespace OCDI;
9
10 class PluginInstaller {
11
12 /**
13 * Holds all registered plugins.
14 *
15 * @var array
16 */
17 private $plugins;
18
19 /**
20 * Initialize everything needed for the plugin installer class to function properly.
21 */
22 public function init() {
23 $this->set_plugins();
24
25 add_action( 'ocdi/plugin_intaller_before_plugin_activation', array( $this, 'before_plugin_activation' ) );
26 add_action( 'ocdi/plugin_intaller_after_plugin_activation', array( $this, 'after_plugin_activation' ) );
27
28 add_action( 'wp_ajax_ocdi_install_plugin', array( $this, 'install_plugin_callback' ) );
29 }
30
31 /**
32 * Prevent the auto redirects for our recommended plugins.
33 * This code is run before plugin is activated.
34 *
35 * @param string $slug The plugin slug.
36 */
37 public function before_plugin_activation( $slug ) {
38 // Disable the WPForms redirect after plugin activation.
39 if ( $slug === 'wpforms-lite' ) {
40 update_option( 'wpforms_activation_redirect', true );
41 }
42
43 // Disable the AIOSEO redirect after plugin activation.
44 if ( $slug === 'all-in-one-seo-pack' ) {
45 update_option( 'aioseo_activation_redirect', true );
46 }
47
48 // Disable the WP Mail SMTP redirect after plugin activation.
49 if ( $slug === 'wp-mail-smtp' ) {
50 update_option( 'wp_mail_smtp_activation_prevent_redirect', true );
51 }
52 }
53
54 /**
55 * Prevent the auto redirects for our recommended plugins.
56 * This code is run after plugin is activated.
57 *
58 * @param string $slug The plugin slug.
59 */
60 public function after_plugin_activation( $slug ) {
61 // Disable the RafflePress redirect after plugin activation.
62 if ( $slug === 'rafflepress' ) {
63 delete_transient('_rafflepress_welcome_screen_activation_redirect');
64 }
65
66 // Disable the MonsterInsights redirect after plugin activation.
67 if ( $slug === 'google-analytics-for-wordpress' ) {
68 delete_transient('_monsterinsights_activation_redirect');
69 }
70
71 // Disable the SeedProd redirect after the plugin activation.
72 if ( $slug === 'coming-soon' ) {
73 delete_transient( '_seedprod_welcome_screen_activation_redirect' );
74 }
75 }
76
77 /**
78 * Get all partner plugins data.
79 *
80 * @return array[]
81 */
82 public function get_partner_plugins() {
83 return array(
84 array(
85 'name' => esc_html__( 'WPForms', 'one-click-demo-import' ),
86 'description' => esc_html__( 'Join 4,000,000+ professionals who build smarter forms and surveys with WPForms.', 'one-click-demo-import' ),
87 'slug' => 'wpforms-lite',
88 'required' => false,
89 'preselected' => true,
90 ),
91 array(
92 'name' => esc_html__( 'All in One SEO', 'one-click-demo-import' ),
93 'description' => esc_html__( 'Use All in One SEO Pack to optimize your WordPress site for SEO.', 'one-click-demo-import' ),
94 'slug' => 'all-in-one-seo-pack',
95 'required' => false,
96 'preselected' => true,
97 ),
98 array(
99 'name' => esc_html__( 'MonsterInsights', 'one-click-demo-import' ),
100 'description' => esc_html__( 'The #1 Google Analytics Plugin for WordPress that’s easy and powerful.', 'one-click-demo-import' ),
101 'slug' => 'google-analytics-for-wordpress',
102 'required' => false,
103 'preselected' => true,
104 ),
105 array(
106 'name' => esc_html__( 'Custom Landing Pages by SeedProd', 'one-click-demo-import' ),
107 'description' => esc_html__( 'Work on your site in private while visitors see a "Coming Soon" or "Maintenance Mode" page.', 'one-click-demo-import' ),
108 'slug' => 'coming-soon',
109 'required' => false,
110 ),
111 array(
112 'name' => esc_html__( 'Smash Balloon Social Photo Feed', 'one-click-demo-import' ),
113 'description' => esc_html__( 'Display beautifully clean, customizable, and responsive Instagram feeds.', 'one-click-demo-import' ),
114 'slug' => 'instagram-feed',
115 'required' => false,
116 ),
117 array(
118 'name' => esc_html__( 'WP Mail SMTP', 'one-click-demo-import' ),
119 'description' => esc_html__( 'Make email delivery easy for WordPress. Connect with SMTP, Gmail, Outlook, Mailgun, and more.', 'one-click-demo-import' ),
120 'slug' => 'wp-mail-smtp',
121 'required' => false,
122 ),
123 );
124 }
125
126 /**
127 * Set all registered plugins.
128 * With our recommended plugins being set as defaults.
129 */
130 public function set_plugins() {
131 $all_plugins = array_merge( $this->get_partner_plugins(), Helpers::apply_filters( 'ocdi/register_plugins', array() ) );
132
133 $this->plugins = $this->filter_plugins( $all_plugins );
134 }
135
136 /**
137 * Get all theme registered plugins.
138 * With our 3 top recommended plugins being set as defaults.
139 */
140 public function get_theme_plugins() {
141 $default_plugins = $this->get_top_partner_plugins();
142
143 $theme_plugins = array_merge( $default_plugins, Helpers::apply_filters( 'ocdi/register_plugins', array() ) );
144
145 return $this->filter_plugins( $theme_plugins );
146 }
147
148 /**
149 * Get the top 3 partner plugins if they are not already activated.
150 *
151 * @return array
152 */
153 private function get_top_partner_plugins() {
154 $installed_plugins = $this->get_plugins();
155 $contact_form = [
156 'wpforms-lite/wpforms.php',
157 'wpforms/wpforms.php',
158 'formidable/formidable.php',
159 'formidable/formidable-pro.php',
160 'gravityforms/gravityforms.php',
161 'ninja-forms/ninja-forms.php',
162 ];
163 $seo = [
164 'all-in-one-seo-pack/all_in_one_seo_pack.php',
165 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
166 'wordpress-seo/wp-seo.php',
167 'wordpress-seo-premium/wp-seo-premium.php',
168 'seo-by-rank-math/rank-math.php',
169 'seo-by-rank-math-pro/rank-math-pro.php',
170 ];
171 $google_analytics = [
172 'google-analytics-for-wordpress/googleanalytics.php',
173 'exactmetrics-premium/exactmetrics-premium.php',
174 'google-analytics-dashboard-for-wp/gadwp.php',
175 ];
176
177 $plugins = array_slice( $this->get_partner_plugins(), 0, 3 );
178
179 $plugins = array_map( function ( $plugin ) {
180 unset( $plugin['preselected'] );
181
182 return $plugin;
183 } , $plugins );
184
185 return array_filter(
186 $plugins,
187 function ( $plugin ) use ( $installed_plugins, $contact_form, $seo, $google_analytics ) {
188 if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
189 return false;
190 }
191
192 if ( 'wpforms-lite' === $plugin['slug'] ) {
193 foreach ( $installed_plugins as $basename => $plugin_info ) {
194 if ( in_array( $basename, $contact_form, true ) ) {
195 return false;
196 }
197 }
198 } elseif ( 'all-in-one-seo-pack' === $plugin['slug'] ) {
199 foreach ( $installed_plugins as $basename => $plugin_info ) {
200 if ( in_array( $basename, $seo, true ) ) {
201 return false;
202 }
203 }
204 } elseif ( 'google-analytics-for-wordpress' === $plugin['slug'] ) {
205 foreach ( $installed_plugins as $basename => $plugin_info ) {
206 if ( in_array( $basename, $google_analytics, true ) ) {
207 return false;
208 }
209 }
210 }
211
212 return true;
213 }
214 );
215 }
216
217 /**
218 * AJAX callback for installing a plugin.
219 * Has to contain the `slug` POST parameter.
220 */
221 public function install_plugin_callback() {
222 check_ajax_referer( 'ocdi-ajax-verification', 'security' );
223
224 // Check if user has the WP capability to install plugins.
225 if ( ! current_user_can( 'install_plugins' ) ) {
226 wp_send_json_error( esc_html__( 'Could not install the plugin. You don\'t have permission to install plugins.', 'one-click-demo-import' ) );
227 }
228
229 $slug = ! empty( $_POST['slug'] ) ? sanitize_key( wp_unslash( $_POST['slug'] ) ) : '';
230
231 if ( empty( $slug ) ) {
232 wp_send_json_error( esc_html__( 'Could not install the plugin. Plugin slug is missing.', 'one-click-demo-import' ) );
233 }
234
235 // Check if the plugin is already installed and activated.
236 if ( $this->is_plugin_active( $slug ) ) {
237 wp_send_json_success( esc_html__( 'Plugin is already installed and activated!', 'one-click-demo-import' ) );
238 }
239
240 // Activate the plugin if the plugin is already installed.
241 if ( $this->is_plugin_installed( $slug ) ) {
242 $activated = $this->activate_plugin( $this->get_plugin_basename_from_slug( $slug ), $slug );
243
244 if ( ! is_wp_error( $activated ) ) {
245 wp_send_json_success( esc_html__( 'Plugin was already installed! We activated it for you.', 'one-click-demo-import' ) );
246 } else {
247 wp_send_json_error( $activated->get_error_message() );
248 }
249 }
250
251 // Check for file system permissions.
252 if ( ! $this->filesystem_permissions_allowed() ) {
253 wp_send_json_error( esc_html__( 'Could not install the plugin. Don\'t have file permission.', 'one-click-demo-import' ) );
254 }
255
256 // Do not allow WordPress to search/download translations, as this will break JS output.
257 remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
258
259 // Prep variables for Plugin_Installer_Skin class.
260 $extra = array();
261 $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
262 $source = $this->get_download_url( $slug );
263 $api = empty( $this->get_plugin_data( $slug )['source'] ) ? $this->get_plugins_api( $slug ) : null;
264 $api = ( false !== $api ) ? $api : null;
265
266 if ( ! empty( $api ) && is_wp_error( $api ) ) {
267 wp_send_json_error( $api->get_error_message() );
268 }
269
270 if ( ! class_exists( '\Plugin_Upgrader', false ) ) {
271 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
272 }
273
274 $skin_args = array(
275 'type' => 'web',
276 'plugin' => '',
277 'api' => $api,
278 'extra' => $extra,
279 );
280
281 $upgrader = new \Plugin_Upgrader( new PluginInstallerSkin( $skin_args ) );
282
283 $upgrader->install( $source );
284
285 // Flush the cache and return the newly installed plugin basename.
286 wp_cache_flush();
287
288 if ( $upgrader->plugin_info() ) {
289 $activated = $this->activate_plugin( $upgrader->plugin_info(), $slug );
290
291 if ( ! is_wp_error( $activated ) ) {
292 wp_send_json_success(
293 esc_html__( 'Plugin installed and activated succesfully.', 'one-click-demo-import' )
294 );
295 } else {
296 wp_send_json_success( $activated->get_error_message() );
297 }
298 }
299
300 wp_send_json_error( esc_html__( 'Could not install the plugin. WP Plugin installer could not retrieve plugin information.', 'one-click-demo-import' ) );
301 }
302
303 /**
304 * Direct plugin install, without AJAX responses.
305 *
306 * @param string $slug The registered plugin slug to install.
307 *
308 * @return bool
309 */
310 public function install_plugin( $slug ) {
311 if ( empty( $slug ) ) {
312 return false;
313 }
314
315 // Check if user has the WP capability to install plugins.
316 if ( ! current_user_can( 'install_plugins' ) ) {
317 return false;
318 }
319
320 // Check if the plugin is already installed and activated.
321 if ( $this->is_plugin_active( $slug ) ) {
322 return true;
323 }
324
325 // Activate the plugin if the plugin is already installed.
326 if ( $this->is_plugin_installed( $slug ) ) {
327 $activated = $this->activate_plugin( $this->get_plugin_basename_from_slug( $slug ), $slug );
328
329 return ! is_wp_error( $activated );
330 }
331
332 // Check for file system permissions.
333 if ( ! $this->filesystem_permissions_allowed() ) {
334 return false;
335 }
336
337 // Do not allow WordPress to search/download translations, as this will break JS output.
338 remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
339
340 // Prep variables for Plugin_Installer_Skin class.
341 $extra = array();
342 $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
343 $source = $this->get_download_url( $slug );
344 $api = empty( $this->get_plugin_data( $slug )['source'] ) ? $this->get_plugins_api( $slug ) : null;
345 $api = ( false !== $api ) ? $api : null;
346
347 if ( ! empty( $api ) && is_wp_error( $api ) ) {
348 return false;
349 }
350
351 if ( ! class_exists( '\Plugin_Upgrader', false ) ) {
352 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
353 }
354
355 $skin_args = array(
356 'type' => 'web',
357 'plugin' => '',
358 'api' => $api,
359 'extra' => $extra,
360 );
361
362 $upgrader = new \Plugin_Upgrader( new PluginInstallerSkinSilent( $skin_args ) );
363
364 $upgrader->install( $source );
365
366 // Flush the cache and return the newly installed plugin basename.
367 wp_cache_flush();
368
369 if ( $upgrader->plugin_info() ) {
370 $activated = $this->activate_plugin( $upgrader->plugin_info(), $slug );
371
372 if ( ! is_wp_error( $activated ) ) {
373 return true;
374 }
375 }
376
377 return false;
378 }
379
380 /**
381 * Activate the plugin with the before and after hooks.
382 *
383 * @param string $plugin_filename The plugin's basename(example: wpforms/wpforms.php).
384 * @param string $slug The plugin's slug.
385 *
386 * @return null|WP_Error Null on success, WP_Error on invalid file.
387 */
388 private function activate_plugin( $plugin_filename, $slug ) {
389 Helpers::do_action( 'ocdi/plugin_intaller_before_plugin_activation', $slug );
390
391 $activated = activate_plugin( $plugin_filename );
392
393 Helpers::do_action( 'ocdi/plugin_intaller_after_plugin_activation', $slug );
394
395 return $activated;
396 }
397
398 /**
399 * Helper function to check for the filesystem permissions.
400 *
401 * @return bool
402 */
403 private function filesystem_permissions_allowed() {
404 $ocdi = OneClickDemoImport::get_instance();
405 $url = esc_url_raw( $ocdi->get_plugin_settings_url() );
406 $creds = request_filesystem_credentials( $url, '', false, false, null );
407
408 // Check for file system permissions.
409 if ( false === $creds || ! WP_Filesystem( $creds ) ) {
410 return false;
411 }
412
413 return true;
414 }
415
416 /**
417 * Get the data of a registered plugin via the slug.
418 *
419 * @param string $slug The plugin slug.
420 *
421 * @return array
422 */
423 public function get_plugin_data( $slug ) {
424 $data = [];
425
426 foreach ( $this->plugins as $plugin ) {
427 if ( $plugin['slug'] === $slug ) {
428 $data = $plugin;
429 break;
430 }
431 }
432
433 return $data;
434 }
435
436 /**
437 * Get the download URL for a plugin.
438 *
439 * @param string $slug Plugin slug.
440 *
441 * @return string Plugin download URL.
442 */
443 public function get_download_url( $slug ) {
444 $plugin_data = $this->get_plugin_data( $slug );
445
446 if ( ! empty( $plugin_data['source'] ) ) {
447 return $plugin_data['source'];
448 }
449
450 return $this->get_wp_repo_download_url( $slug );
451 }
452
453 /**
454 * Get the download URL from the WP.org.
455 *
456 * @param string $slug Plugin slug.
457 *
458 * @return string Plugin download URL from WP.org.
459 */
460 protected function get_wp_repo_download_url( $slug ) {
461 $source = '';
462 $api = $this->get_plugins_api( $slug );
463
464 if ( false !== $api && isset( $api->download_link ) ) {
465 $source = $api->download_link;
466 }
467
468 return $source;
469 }
470
471 /**
472 * Try to grab information from WordPress API.
473 *
474 * @param string $slug Plugin slug.
475 *
476 * @return object Plugins_api response object on success, WP_Error on failure.
477 */
478 protected function get_plugins_api( $slug ) {
479 static $api = array(); // Cache received responses.
480
481 if ( ! isset( $api[ $slug ] ) ) {
482 if ( ! function_exists( 'plugins_api' ) ) {
483 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
484 }
485
486 $api[ $slug ] = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
487 }
488
489 return $api[ $slug ];
490 }
491
492 /**
493 * Wrapper around the core WP get_plugins function, making sure it's actually available.
494 *
495 * @param string $plugin_folder Optional. Relative path to single plugin folder.
496 *
497 * @return array Array of installed plugins with plugin information.
498 */
499 public function get_plugins( $plugin_folder = '' ) {
500 if ( ! function_exists( 'get_plugins' ) ) {
501 require_once ABSPATH . 'wp-admin/includes/plugin.php';
502 }
503
504 return get_plugins( $plugin_folder );
505 }
506
507 /**
508 * Helper function to extract the plugin file path from the
509 * plugin slug, if the plugin is installed.
510 *
511 * @param string $slug Plugin slug (typically folder name) as provided by the developer.
512 *
513 * @return string|bool Either plugin file path for plugin if installed, or false.
514 */
515 protected function get_plugin_basename_from_slug( $slug ) {
516 $keys = array_keys( $this->get_plugins() );
517
518 foreach ( $keys as $key ) {
519 if ( preg_match( '/^' . $slug . '\//', $key ) ) {
520 return $key;
521 }
522 }
523
524 return false;
525 }
526
527 /**
528 * Check if a plugin is installed. Does not take must-use plugins into account.
529 *
530 * @param string $slug Plugin slug.
531 *
532 * @return bool True if installed, false otherwise.
533 */
534 public function is_plugin_installed( $slug ) {
535 return ( ! empty( $this->get_plugin_basename_from_slug( $slug ) ) );
536 }
537
538 /**
539 * Check if a plugin is active.
540 *
541 * @param string $slug Plugin slug.
542 *
543 * @return bool True if active, false otherwise.
544 */
545 public function is_plugin_active( $slug ) {
546 $plugin_path = $this->get_plugin_basename_from_slug( $slug );
547
548 if ( empty( $plugin_path ) ) {
549 return false;
550 }
551
552 return is_plugin_active( $plugin_path );
553 }
554
555 /**
556 * Get the list of plugins (with their data) of all non-active and non-installed registered plugins.
557 *
558 * @return array
559 */
560 public function get_missing_plugins() {
561 $missing = [];
562
563 foreach ( $this->plugins as $plugin_data ) {
564 if ( ! $this->is_plugin_active( $plugin_data['slug'] ) ) {
565 $missing[] = $plugin_data;
566 }
567 }
568
569 return $missing;
570 }
571
572 /**
573 * Return only plugins with required attributes:
574 * - name
575 * - slug
576 *
577 * @param array $plugins The array of plugin's data.
578 *
579 * @return array
580 */
581 private function filter_plugins( $plugins ) {
582 return array_filter(
583 $plugins,
584 function ( $plugin ) {
585 if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
586 return false;
587 }
588
589 return true;
590 }
591 );
592 }
593 }
594