WP Mail SMTP by WPForms - Version 1.3.2

Version Description

  • 2018-06-29 =
  • Make sure that other plugins/themes are not conflicting with our TGMPA library.
Download this release

Release Info

Developer slaFFik
Plugin Icon 128x128 WP Mail SMTP by WPForms
Version 1.3.2
Comparing to
See all releases

Code changes from version 1.3.1 to 1.3.2

Files changed (4) hide show
  1. readme.txt +3 -0
  2. src/Core.php +3 -3
  3. src/TGMPA.php +3129 -3160
  4. wp_mail_smtp.php +2 -2
readme.txt CHANGED
@@ -146,6 +146,9 @@ By all means please contact us to discuss features or options you'd like to see
146
 
147
  == Changelog ==
148
 
 
 
 
149
  = 1.3.1 - 2018-06-29 =
150
  * Fixed: Other SMTP: Clear new Debug messages about failed email delivery on next successful email sending.
151
  * Fixed: Introduce conditional autoloader to workaround Gmail PHP 5.5 requirement and its library compatibility issues vs PHP 5.3+ minimum viable plugin version.
146
 
147
  == Changelog ==
148
 
149
+ = 1.3.2 - 2018-06-29 =
150
+ * Make sure that other plugins/themes are not conflicting with our TGMPA library.
151
+
152
  = 1.3.1 - 2018-06-29 =
153
  * Fixed: Other SMTP: Clear new Debug messages about failed email delivery on next successful email sending.
154
  * Fixed: Introduce conditional autoloader to workaround Gmail PHP 5.5 requirement and its library compatibility issues vs PHP 5.3+ minimum viable plugin version.
src/Core.php CHANGED
@@ -53,10 +53,10 @@ class Core {
53
  add_action( 'plugins_loaded', array( $this, 'init_notifications' ) );
54
 
55
  // Recommendations.
56
- if ( ! class_exists( 'WPMailSMTP\TGM_Plugin_Activation', false ) ) {
57
  require_once __DIR__ . '/TGMPA.php';
58
  }
59
- add_action( 'tgmpa_register', array( $this, 'init_recommendations' ) );
60
 
61
  add_action( 'init', array( $this, 'init' ) );
62
  }
@@ -265,7 +265,7 @@ class Core {
265
  ),
266
  ) );
267
 
268
- tgmpa( (array) $plugins, (array) $config );
269
  }
270
 
271
  /**
53
  add_action( 'plugins_loaded', array( $this, 'init_notifications' ) );
54
 
55
  // Recommendations.
56
+ if ( ! class_exists( '\WPMailSMTP\TGM_Plugin_Activation', false ) ) {
57
  require_once __DIR__ . '/TGMPA.php';
58
  }
59
+ add_action( 'wpms_tgmpa_register', array( $this, 'init_recommendations' ) );
60
 
61
  add_action( 'init', array( $this, 'init' ) );
62
  }
265
  ),
266
  ) );
267
 
268
+ \WPMailSMTP\tgmpa( (array) $plugins, (array) $config );
269
  }
270
 
271
  /**
src/TGMPA.php CHANGED
@@ -35,2122 +35,2123 @@ namespace WPMailSMTP;
35
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36
  */
37
 
38
- if ( ! class_exists( 'TGM_Plugin_Activation' ) ) {
39
-
 
 
 
 
 
 
 
 
 
 
 
 
40
  /**
41
- * Automatic plugin installation and activation library.
42
  *
43
- * Creates a way to automatically install and activate plugins from within themes.
44
- * The plugins can be either bundled, downloaded from the WordPress
45
- * Plugin Repository or downloaded from another external source.
46
- *
47
- * @since 1.0.0
48
  *
49
- * @package TGM-Plugin-Activation
50
- * @author Thomas Griffin
51
- * @author Gary Jones
52
  */
53
- class TGM_Plugin_Activation {
54
- /**
55
- * TGMPA version number.
56
- *
57
- * @since 2.5.0
58
- *
59
- * @const string Version number.
60
- */
61
- const TGMPA_VERSION = '2.6.1';
62
 
63
- /**
64
- * Regular expression to test if a URL is a WP plugin repo URL.
65
- *
66
- * @const string Regex.
67
- *
68
- * @since 2.5.0
69
- */
70
- const WP_REPO_REGEX = '|^http[s]?://wordpress\.org/(?:extend/)?plugins/|';
71
-
72
- /**
73
- * Arbitrary regular expression to test if a string starts with a URL.
74
- *
75
- * @const string Regex.
76
- *
77
- * @since 2.5.0
78
- */
79
- const IS_URL_REGEX = '|^http[s]?://|';
80
 
81
- /**
82
- * Holds a copy of itself, so it can be referenced by the class name.
83
- *
84
- * @since 1.0.0
85
- *
86
- * @var TGM_Plugin_Activation
87
- */
88
- public static $instance;
89
 
90
- /**
91
- * Holds arrays of plugin details.
92
- *
93
- * @since 1.0.0
94
- * @since 2.5.0 the array has the plugin slug as an associative key.
95
- *
96
- * @var array
97
- */
98
- public $plugins = array();
99
 
100
- /**
101
- * Holds arrays of plugin names to use to sort the plugins array.
102
- *
103
- * @since 2.5.0
104
- *
105
- * @var array
106
- */
107
- protected $sort_order = array();
 
108
 
109
- /**
110
- * Whether any plugins have the 'force_activation' setting set to true.
111
- *
112
- * @since 2.5.0
113
- *
114
- * @var bool
115
- */
116
- protected $has_forced_activation = false;
117
 
118
- /**
119
- * Whether any plugins have the 'force_deactivation' setting set to true.
120
- *
121
- * @since 2.5.0
122
- *
123
- * @var bool
124
- */
125
- protected $has_forced_deactivation = false;
126
 
127
- /**
128
- * Name of the unique ID to hash notices.
129
- *
130
- * @since 2.4.0
131
- *
132
- * @var string
133
- */
134
- public $id = 'tgmpa';
135
 
136
- /**
137
- * Name of the query-string argument for the admin page.
138
- *
139
- * @since 1.0.0
140
- *
141
- * @var string
142
- */
143
- protected $menu = 'tgmpa-install-plugins';
144
 
145
- /**
146
- * Parent menu file slug.
147
- *
148
- * @since 2.5.0
149
- *
150
- * @var string
151
- */
152
- public $parent_slug = 'themes.php';
153
 
154
- /**
155
- * Capability needed to view the plugin installation menu item.
156
- *
157
- * @since 2.5.0
158
- *
159
- * @var string
160
- */
161
- public $capability = 'edit_theme_options';
162
 
163
- /**
164
- * Default absolute path to folder containing bundled plugin zip files.
165
- *
166
- * @since 2.0.0
167
- *
168
- * @var string Absolute path prefix to zip file location for bundled plugins. Default is empty string.
169
- */
170
- public $default_path = '';
171
 
172
- /**
173
- * Flag to show admin notices or not.
174
- *
175
- * @since 2.1.0
176
- *
177
- * @var boolean
178
- */
179
- public $has_notices = true;
180
 
181
- /**
182
- * Flag to determine if the user can dismiss the notice nag.
183
- *
184
- * @since 2.4.0
185
- *
186
- * @var boolean
187
- */
188
- public $dismissable = true;
189
 
190
- /**
191
- * Message to be output above nag notice if dismissable is false.
192
- *
193
- * @since 2.4.0
194
- *
195
- * @var string
196
- */
197
- public $dismiss_msg = '';
198
 
199
- /**
200
- * Flag to set automatic activation of plugins. Off by default.
201
- *
202
- * @since 2.2.0
203
- *
204
- * @var boolean
205
- */
206
- public $is_automatic = false;
207
 
208
- /**
209
- * Optional message to display before the plugins table.
210
- *
211
- * @since 2.2.0
212
- *
213
- * @var string Message filtered by wp_kses_post(). Default is empty string.
214
- */
215
- public $message = '';
216
 
217
- /**
218
- * Holds configurable array of strings.
219
- *
220
- * Default values are added in the constructor.
221
- *
222
- * @since 2.0.0
223
- *
224
- * @var array
225
- */
226
- public $strings = array();
227
 
228
- /**
229
- * Holds the version of WordPress.
230
- *
231
- * @since 2.4.0
232
- *
233
- * @var int
234
- */
235
- public $wp_version;
 
 
236
 
237
- /**
238
- * Holds the hook name for the admin page.
239
- *
240
- * @since 2.5.0
241
- *
242
- * @var string
243
- */
244
- public $page_hook;
245
 
246
- /**
247
- * Adds a reference of this object to $instance, populates default strings,
248
- * does the tgmpa_init action hook, and hooks in the interactions to init.
249
- *
250
- * {@internal This method should be `protected`, but as too many TGMPA implementations
251
- * haven't upgraded beyond v2.3.6 yet, this gives backward compatibility issues.
252
- * Reverted back to public for the time being.}}
253
- *
254
- * @since 1.0.0
255
- *
256
- * @see TGM_Plugin_Activation::init()
257
- */
258
- public function __construct() {
259
- // Set the current WordPress version.
260
- $this->wp_version = $GLOBALS['wp_version'];
261
 
262
- // Announce that the class is ready, and pass the object (for advanced use).
263
- do_action_ref_array( 'tgmpa_init', array( $this ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
- /*
266
- * Load our text domain and allow for overloading the fall-back file.
267
- *
268
- * {@internal IMPORTANT! If this code changes, review the regex in the custom TGMPA
269
- * generator on the website.}}
270
- */
271
- add_action( 'init', array( $this, 'load_textdomain' ), 5 );
272
- add_filter( 'load_textdomain_mofile', array( $this, 'overload_textdomain_mofile' ), 10, 2 );
 
 
 
 
 
 
 
 
 
 
 
273
 
274
- // When the rest of WP has loaded, kick-start the rest of the class.
275
- add_action( 'init', array( $this, 'init' ) );
276
- }
 
 
 
 
 
 
 
 
277
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  /**
279
- * Magic method to (not) set protected properties from outside of this class.
280
- *
281
- * {@internal hackedihack... There is a serious bug in v2.3.2 - 2.3.6 where the `menu` property
282
- * is being assigned rather than tested in a conditional, effectively rendering it useless.
283
- * This 'hack' prevents this from happening.}}
284
- *
285
- * @see https://github.com/TGMPA/TGM-Plugin-Activation/blob/2.3.6/tgm-plugin-activation/class-tgm-plugin-activation.php#L1593
286
  *
287
- * @since 2.5.2
288
  *
289
- * @param string $name Name of an inaccessible property.
290
- * @param mixed $value Value to assign to the property.
291
- * @return void Silently fail to set the property when this is tried from outside of this class context.
292
- * (Inside this class context, the __set() method if not used as there is direct access.)
293
  */
294
- public function __set( $name, $value ) {
295
  return;
296
  }
297
 
298
- /**
299
- * Magic method to get the value of a protected property outside of this class context.
300
- *
301
- * @since 2.5.2
302
- *
303
- * @param string $name Name of an inaccessible property.
304
- * @return mixed The property value.
305
- */
306
- public function __get( $name ) {
307
- return $this->{$name};
308
- }
309
-
310
- /**
311
- * Initialise the interactions between this class and WordPress.
312
- *
313
- * Hooks in three new methods for the class: admin_menu, notices and styles.
314
- *
315
- * @since 2.0.0
316
- *
317
- * @see TGM_Plugin_Activation::admin_menu()
318
- * @see TGM_Plugin_Activation::notices()
319
- * @see TGM_Plugin_Activation::styles()
320
- */
321
- public function init() {
322
- /**
323
- * By default TGMPA only loads on the WP back-end and not in an Ajax call. Using this filter
324
- * you can overrule that behaviour.
325
- *
326
- * @since 2.5.0
327
- *
328
- * @param bool $load Whether or not TGMPA should load.
329
- * Defaults to the return of `is_admin() && ! defined( 'DOING_AJAX' )`.
330
- */
331
- if ( true !== apply_filters( 'tgmpa_load', ( is_admin() && ! defined( 'DOING_AJAX' ) ) ) ) {
332
- return;
333
- }
334
-
335
- // Load class strings.
336
- $this->strings = array(
337
- 'page_title' => __( 'Install Required Plugins', 'tgmpa' ),
338
- 'menu_title' => __( 'Install Plugins', 'tgmpa' ),
339
- /* translators: %s: plugin name. */
340
- 'installing' => __( 'Installing Plugin: %s', 'tgmpa' ),
341
- /* translators: %s: plugin name. */
342
- 'updating' => __( 'Updating Plugin: %s', 'tgmpa' ),
343
- 'oops' => __( 'Something went wrong with the plugin API.', 'tgmpa' ),
344
- 'notice_can_install_required' => _n_noop(
345
  /* translators: 1: plugin name(s). */
346
- 'This theme requires the following plugin: %1$s.',
347
- 'This theme requires the following plugins: %1$s.',
348
- 'tgmpa'
349
- ),
350
- 'notice_can_install_recommended' => _n_noop(
351
  /* translators: 1: plugin name(s). */
352
- 'This theme recommends the following plugin: %1$s.',
353
- 'This theme recommends the following plugins: %1$s.',
354
- 'tgmpa'
355
- ),
356
- 'notice_ask_to_update' => _n_noop(
357
  /* translators: 1: plugin name(s). */
358
- 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.',
359
- 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.',
360
- 'tgmpa'
361
- ),
362
- 'notice_ask_to_update_maybe' => _n_noop(
363
  /* translators: 1: plugin name(s). */
364
- 'There is an update available for: %1$s.',
365
- 'There are updates available for the following plugins: %1$s.',
366
- 'tgmpa'
367
- ),
368
- 'notice_can_activate_required' => _n_noop(
369
  /* translators: 1: plugin name(s). */
370
- 'The following required plugin is currently inactive: %1$s.',
371
- 'The following required plugins are currently inactive: %1$s.',
372
- 'tgmpa'
373
- ),
374
- 'notice_can_activate_recommended' => _n_noop(
375
  /* translators: 1: plugin name(s). */
376
- 'The following recommended plugin is currently inactive: %1$s.',
377
- 'The following recommended plugins are currently inactive: %1$s.',
378
- 'tgmpa'
379
- ),
380
- 'install_link' => _n_noop(
381
- 'Begin installing plugin',
382
- 'Begin installing plugins',
383
- 'tgmpa'
384
- ),
385
- 'update_link' => _n_noop(
386
- 'Begin updating plugin',
387
- 'Begin updating plugins',
388
- 'tgmpa'
389
- ),
390
- 'activate_link' => _n_noop(
391
- 'Begin activating plugin',
392
- 'Begin activating plugins',
393
- 'tgmpa'
394
- ),
395
- 'return' => __( 'Return to Required Plugins Installer', 'tgmpa' ),
396
- 'dashboard' => __( 'Return to the Dashboard', 'tgmpa' ),
397
- 'plugin_activated' => __( 'Plugin activated successfully.', 'tgmpa' ),
398
- 'activated_successfully' => __( 'The following plugin was activated successfully:', 'tgmpa' ),
399
- /* translators: 1: plugin name. */
400
- 'plugin_already_active' => __( 'No action taken. Plugin %1$s was already active.', 'tgmpa' ),
401
- /* translators: 1: plugin name. */
402
- 'plugin_needs_higher_version' => __( 'Plugin not activated. A higher version of %s is needed for this theme. Please update the plugin.', 'tgmpa' ),
403
- /* translators: 1: dashboard link. */
404
- 'complete' => __( 'All plugins installed and activated successfully. %1$s', 'tgmpa' ),
405
- 'dismiss' => __( 'Dismiss this notice', 'tgmpa' ),
406
- 'notice_cannot_install_activate' => __( 'There are one or more required or recommended plugins to install, update or activate.', 'tgmpa' ),
407
- 'contact_admin' => __( 'Please contact the administrator of this site for help.', 'tgmpa' ),
408
- );
409
 
410
- do_action( 'tgmpa_register' );
411
 
412
- /* After this point, the plugins should be registered and the configuration set. */
413
 
414
- // Proceed only if we have plugins to handle.
415
- if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
416
- return;
417
- }
418
 
419
- // Set up the menu and notices if we still have outstanding actions.
420
- if ( true !== $this->is_tgmpa_complete() ) {
421
- // Sort the plugins.
422
- array_multisort( $this->sort_order, SORT_ASC, $this->plugins );
423
 
424
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
425
- add_action( 'admin_head', array( $this, 'dismiss' ) );
426
 
427
- // Prevent the normal links from showing underneath a single install/update page.
428
- add_filter( 'install_plugin_complete_actions', array( $this, 'actions' ) );
429
- add_filter( 'update_plugin_complete_actions', array( $this, 'actions' ) );
430
 
431
- if ( $this->has_notices ) {
432
- add_action( 'admin_notices', array( $this, 'notices' ) );
433
- add_action( 'admin_init', array( $this, 'admin_init' ), 1 );
434
- add_action( 'admin_enqueue_scripts', array( $this, 'thickbox' ) );
435
- }
436
  }
 
437
 
438
- // If needed, filter plugin action links.
439
- add_action( 'load-plugins.php', array( $this, 'add_plugin_action_link_filters' ), 1 );
440
 
441
- // Make sure things get reset on switch theme.
442
- add_action( 'switch_theme', array( $this, 'flush_plugins_cache' ) );
443
 
444
- if ( $this->has_notices ) {
445
- add_action( 'switch_theme', array( $this, 'update_dismiss' ) );
446
- }
447
 
448
- // Setup the force activation hook.
449
- if ( true === $this->has_forced_activation ) {
450
- add_action( 'admin_init', array( $this, 'force_activation' ) );
451
- }
452
 
453
- // Setup the force deactivation hook.
454
- if ( true === $this->has_forced_deactivation ) {
455
- add_action( 'switch_theme', array( $this, 'force_deactivation' ) );
456
- }
457
  }
 
458
 
459
- /**
460
- * Load translations.
461
- *
462
- * @since 2.6.0
463
- *
464
- * (@internal Uses `load_theme_textdomain()` rather than `load_plugin_textdomain()` to
465
- * get round the different ways of handling the path and deprecated notices being thrown
466
- * and such. For plugins, the actual file name will be corrected by a filter.}}
467
- *
468
- * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
469
- * generator on the website.}}
470
- */
471
- public function load_textdomain() {
472
- if ( is_textdomain_loaded( 'tgmpa' ) ) {
473
- return;
474
- }
475
 
476
- if ( false !== strpos( __FILE__, WP_PLUGIN_DIR ) || false !== strpos( __FILE__, WPMU_PLUGIN_DIR ) ) {
477
- // Plugin, we'll need to adjust the file name.
478
- add_action( 'load_textdomain_mofile', array( $this, 'correct_plugin_mofile' ), 10, 2 );
479
- load_theme_textdomain( 'tgmpa', dirname( __FILE__ ) . '/languages' );
480
- remove_action( 'load_textdomain_mofile', array( $this, 'correct_plugin_mofile' ), 10 );
481
- } else {
482
- load_theme_textdomain( 'tgmpa', dirname( __FILE__ ) . '/languages' );
483
- }
484
  }
 
485
 
486
- /**
487
- * Correct the .mo file name for (must-use) plugins.
488
- *
489
- * Themese use `/path/{locale}.mo` while plugins use `/path/{text-domain}-{locale}.mo`.
490
- *
491
- * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
492
- * generator on the website.}}
493
- *
494
- * @since 2.6.0
495
- *
496
- * @param string $mofile Full path to the target mofile.
497
- * @param string $domain The domain for which a language file is being loaded.
498
- * @return string $mofile
499
- */
500
- public function correct_plugin_mofile( $mofile, $domain ) {
501
- // Exit early if not our domain (just in case).
502
- if ( 'tgmpa' !== $domain ) {
503
- return $mofile;
504
- }
505
- return preg_replace( '`/([a-z]{2}_[A-Z]{2}.mo)$`', '/tgmpa-$1', $mofile );
506
  }
 
 
507
 
508
- /**
509
- * Potentially overload the fall-back translation file for the current language.
510
- *
511
- * WP, by default since WP 3.7, will load a local translation first and if none
512
- * can be found, will try and find a translation in the /wp-content/languages/ directory.
513
- * As this library is theme/plugin agnostic, translation files for TGMPA can exist both
514
- * in the WP_LANG_DIR /plugins/ subdirectory as well as in the /themes/ subdirectory.
515
- *
516
- * This method makes sure both directories are checked.
517
- *
518
- * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
519
- * generator on the website.}}
520
- *
521
- * @since 2.6.0
522
- *
523
- * @param string $mofile Full path to the target mofile.
524
- * @param string $domain The domain for which a language file is being loaded.
525
- * @return string $mofile
526
- */
527
- public function overload_textdomain_mofile( $mofile, $domain ) {
528
- // Exit early if not our domain, not a WP_LANG_DIR load or if the file exists and is readable.
529
- if ( 'tgmpa' !== $domain || false === strpos( $mofile, WP_LANG_DIR ) || @is_readable( $mofile ) ) {
530
- return $mofile;
531
- }
532
 
533
- // Current fallback file is not valid, let's try the alternative option.
534
- if ( false !== strpos( $mofile, '/themes/' ) ) {
535
- return str_replace( '/themes/', '/plugins/', $mofile );
536
- } elseif ( false !== strpos( $mofile, '/plugins/' ) ) {
537
- return str_replace( '/plugins/', '/themes/', $mofile );
538
- } else {
539
- return $mofile;
540
- }
541
  }
 
542
 
543
- /**
544
- * Hook in plugin action link filters for the WP native plugins page.
545
- *
546
- * - Prevent activation of plugins which don't meet the minimum version requirements.
547
- * - Prevent deactivation of force-activated plugins.
548
- * - Add update notice if update available.
549
- *
550
- * @since 2.5.0
551
- */
552
- public function add_plugin_action_link_filters() {
553
- foreach ( $this->plugins as $slug => $plugin ) {
554
- if ( false === $this->can_plugin_activate( $slug ) ) {
555
- add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_activate' ), 20 );
556
- }
557
 
558
- if ( true === $plugin['force_activation'] ) {
559
- add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_deactivate' ), 20 );
560
- }
561
 
562
- if ( false !== $this->does_plugin_require_update( $slug ) ) {
563
- add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_update' ), 20 );
564
- }
565
  }
566
  }
 
567
 
568
- /**
569
- * Remove the 'Activate' link on the WP native plugins page if the plugin does not meet the
570
- * minimum version requirements.
571
- *
572
- * @since 2.5.0
573
- *
574
- * @param array $actions Action links.
575
- * @return array
576
- */
577
- public function filter_plugin_action_links_activate( $actions ) {
578
- unset( $actions['activate'] );
579
 
580
- return $actions;
581
- }
 
 
 
 
 
 
 
 
 
 
 
582
 
583
- /**
584
- * Remove the 'Deactivate' link on the WP native plugins page if the plugin has been set to force activate.
585
- *
586
- * @since 2.5.0
587
- *
588
- * @param array $actions Action links.
589
- * @return array
590
- */
591
- public function filter_plugin_action_links_deactivate( $actions ) {
592
- unset( $actions['deactivate'] );
593
 
594
- return $actions;
595
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
 
597
- /**
598
- * Add a 'Requires update' link on the WP native plugins page if the plugin does not meet the
599
- * minimum version requirements.
600
- *
601
- * @since 2.5.0
602
- *
603
- * @param array $actions Action links.
604
- * @return array
605
- */
606
- public function filter_plugin_action_links_update( $actions ) {
607
- $actions['update'] = sprintf(
608
- '<a href="%1$s" title="%2$s" class="edit">%3$s</a>',
609
- esc_url( $this->get_tgmpa_status_url( 'update' ) ),
610
- esc_attr__( 'This plugin needs to be updated to be compatible with your theme.', 'tgmpa' ),
611
- esc_html__( 'Update Required', 'tgmpa' )
612
- );
613
 
614
- return $actions;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  }
616
 
617
- /**
618
- * Handles calls to show plugin information via links in the notices.
619
- *
620
- * We get the links in the admin notices to point to the TGMPA page, rather
621
- * than the typical plugin-install.php file, so we can prepare everything
622
- * beforehand.
623
- *
624
- * WP does not make it easy to show the plugin information in the thickbox -
625
- * here we have to require a file that includes a function that does the
626
- * main work of displaying it, enqueue some styles, set up some globals and
627
- * finally call that function before exiting.
628
- *
629
- * Down right easy once you know how...
630
- *
631
- * Returns early if not the TGMPA page.
632
- *
633
- * @since 2.1.0
634
- *
635
- * @global string $tab Used as iframe div class names, helps with styling
636
- * @global string $body_id Used as the iframe body ID, helps with styling
637
- *
638
- * @return null Returns early if not the TGMPA page.
639
- */
640
- public function admin_init() {
641
- if ( ! $this->is_tgmpa_page() ) {
642
- return;
643
- }
644
 
645
- if ( isset( $_REQUEST['tab'] ) && 'plugin-information' === $_REQUEST['tab'] ) {
646
- // Needed for install_plugin_information().
647
- require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
648
 
649
- wp_enqueue_style( 'plugin-install' );
 
 
 
 
650
 
651
- global $tab, $body_id;
652
- $body_id = 'plugin-information';
653
- // @codingStandardsIgnoreStart
654
- $tab = 'plugin-information';
655
- // @codingStandardsIgnoreEnd
656
 
657
- install_plugin_information();
 
 
658
 
659
- exit;
660
- }
 
 
 
 
 
 
 
 
 
 
 
 
661
  }
 
662
 
663
- /**
664
- * Enqueue thickbox scripts/styles for plugin info.
665
- *
666
- * Thickbox is not automatically included on all admin pages, so we must
667
- * manually enqueue it for those pages.
668
- *
669
- * Thickbox is only loaded if the user has not dismissed the admin
670
- * notice or if there are any plugins left to install and activate.
671
- *
672
- * @since 2.1.0
673
- */
674
- public function thickbox() {
675
- if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
676
- add_thickbox();
677
- }
 
 
 
 
678
  }
679
 
680
- /**
681
- * Adds submenu page if there are plugin actions to take.
682
- *
683
- * This method adds the submenu page letting users know that a required
684
- * plugin needs to be installed.
685
- *
686
- * This page disappears once the plugin has been installed and activated.
687
- *
688
- * @since 1.0.0
689
- *
690
- * @see TGM_Plugin_Activation::init()
691
- * @see TGM_Plugin_Activation::install_plugins_page()
692
- *
693
- * @return null Return early if user lacks capability to install a plugin.
694
- */
695
- public function admin_menu() {
696
- // Make sure privileges are correct to see the page.
697
- if ( ! current_user_can( 'install_plugins' ) ) {
698
- return;
699
- }
700
 
701
- $args = apply_filters(
702
- 'tgmpa_admin_menu_args',
703
- array(
704
- 'parent_slug' => $this->parent_slug, // Parent Menu slug.
705
- 'page_title' => $this->strings['page_title'], // Page title.
706
- 'menu_title' => $this->strings['menu_title'], // Menu title.
707
- 'capability' => $this->capability, // Capability.
708
- 'menu_slug' => $this->menu, // Menu slug.
709
- 'function' => array( $this, 'install_plugins_page' ), // Callback.
710
- )
711
- );
712
 
713
- $this->add_admin_menu( $args );
 
 
 
 
 
 
 
 
 
 
 
 
714
  }
715
 
716
- /**
717
- * Add the menu item.
718
- *
719
- * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
720
- * generator on the website.}}
721
- *
722
- * @since 2.5.0
723
- *
724
- * @param array $args Menu item configuration.
725
- */
726
- protected function add_admin_menu( array $args ) {
727
- if ( has_filter( 'tgmpa_admin_menu_use_add_theme_page' ) ) {
728
- _deprecated_function( 'The "tgmpa_admin_menu_use_add_theme_page" filter', '2.5.0', esc_html__( 'Set the parent_slug config variable instead.', 'tgmpa' ) );
729
- }
730
-
731
- if ( 'themes.php' === $this->parent_slug ) {
732
- $this->page_hook = call_user_func( 'add_theme_page', $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
733
- } else {
734
- $this->page_hook = call_user_func( 'add_submenu_page', $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
735
- }
736
  }
 
737
 
738
- /**
739
- * Echoes plugin installation form.
740
- *
741
- * This method is the callback for the admin_menu method function.
742
- * This displays the admin page and form area where the user can select to install and activate the plugin.
743
- * Aborts early if we're processing a plugin installation action.
744
- *
745
- * @since 1.0.0
746
- *
747
- * @return null Aborts early if we're processing a plugin installation action.
748
- */
749
- public function install_plugins_page() {
750
- // Store new instance of plugin table in object.
751
- $plugin_table = new TGMPA_List_Table;
752
 
753
- // Return early if processing a plugin installation action.
754
- if ( ( ( 'tgmpa-bulk-install' === $plugin_table->current_action() || 'tgmpa-bulk-update' === $plugin_table->current_action() ) && $plugin_table->process_bulk_actions() ) || $this->do_plugin_install() ) {
755
- return;
756
- }
757
 
758
- // Force refresh of available plugin information so we'll know about manual updates/deletes.
759
- wp_clean_plugins_cache( false );
760
 
761
- ?>
762
- <div class="tgmpa wrap">
763
- <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
764
- <?php $plugin_table->prepare_items(); ?>
765
 
766
- <?php
767
- if ( ! empty( $this->message ) && is_string( $this->message ) ) {
768
- echo wp_kses_post( $this->message );
769
- }
770
- ?>
771
- <?php $plugin_table->views(); ?>
772
-
773
- <form id="tgmpa-plugins" action="" method="post">
774
- <input type="hidden" name="tgmpa-page" value="<?php echo esc_attr( $this->menu ); ?>" />
775
- <input type="hidden" name="plugin_status" value="<?php echo esc_attr( $plugin_table->view_context ); ?>" />
776
- <?php $plugin_table->display(); ?>
777
- </form>
778
- </div>
779
  <?php
780
- }
781
-
782
- /**
783
- * Installs, updates or activates a plugin depending on the action link clicked by the user.
784
- *
785
- * Checks the $_GET variable to see which actions have been
786
- * passed and responds with the appropriate method.
787
- *
788
- * Uses WP_Filesystem to process and handle the plugin installation
789
- * method.
790
- *
791
- * @since 1.0.0
792
- *
793
- * @uses WP_Filesystem
794
- * @uses WP_Error
795
- * @uses WP_Upgrader
796
- * @uses Plugin_Upgrader
797
- * @uses Plugin_Installer_Skin
798
- * @uses Plugin_Upgrader_Skin
799
- *
800
- * @return boolean True on success, false on failure.
801
- */
802
- protected function do_plugin_install() {
803
- if ( empty( $_GET['plugin'] ) ) {
804
- return false;
805
  }
 
 
 
 
 
 
 
 
 
 
 
806
 
807
- // All plugin information will be stored in an array for processing.
808
- $slug = $this->sanitize_key( urldecode( $_GET['plugin'] ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
 
810
- if ( ! isset( $this->plugins[ $slug ] ) ) {
811
- return false;
812
- }
813
 
814
- // Was an install or upgrade action link clicked?
815
- if ( ( isset( $_GET['tgmpa-install'] ) && 'install-plugin' === $_GET['tgmpa-install'] ) || ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) ) {
 
816
 
817
- $install_type = 'install';
818
- if ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) {
819
- $install_type = 'update';
820
- }
 
 
 
821
 
822
- check_admin_referer( 'tgmpa-' . $install_type, 'tgmpa-nonce' );
823
 
824
- // Pass necessary information via URL if WP_Filesystem is needed.
825
- $url = wp_nonce_url(
826
- add_query_arg(
827
- array(
828
- 'plugin' => urlencode( $slug ),
829
- 'tgmpa-' . $install_type => $install_type . '-plugin',
830
- ),
831
- $this->get_tgmpa_url()
832
  ),
833
- 'tgmpa-' . $install_type,
834
- 'tgmpa-nonce'
835
- );
 
 
836
 
837
- $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
838
 
839
- if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, array() ) ) ) {
840
- return true;
841
- }
842
 
843
- if ( ! WP_Filesystem( $creds ) ) {
844
- request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, array() ); // Setup WP_Filesystem.
845
- return true;
846
- }
847
 
848
- /* If we arrive here, we have the filesystem. */
849
 
850
- // Prep variables for Plugin_Installer_Skin class.
851
- $extra = array();
852
- $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
853
- $source = $this->get_download_url( $slug );
854
- $api = ( 'repo' === $this->plugins[ $slug ]['source_type'] ) ? $this->get_plugins_api( $slug ) : null;
855
- $api = ( false !== $api ) ? $api : null;
856
 
857
- $url = add_query_arg(
858
- array(
859
- 'action' => $install_type . '-plugin',
860
- 'plugin' => urlencode( $slug ),
861
- ),
862
- 'update.php'
863
- );
864
 
865
- if ( ! class_exists( '\Plugin_Upgrader', false ) ) {
866
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
867
- }
868
 
869
- $title = ( 'update' === $install_type ) ? $this->strings['updating'] : $this->strings['installing'];
870
- $skin_args = array(
871
- 'type' => ( 'bundled' !== $this->plugins[ $slug ]['source_type'] ) ? 'web' : 'upload',
872
- 'title' => sprintf( $title, $this->plugins[ $slug ]['name'] ),
873
- 'url' => esc_url_raw( $url ),
874
- 'nonce' => $install_type . '-plugin_' . $slug,
875
- 'plugin' => '',
876
- 'api' => $api,
877
- 'extra' => $extra,
878
- );
879
- unset( $title );
880
 
881
- if ( 'update' === $install_type ) {
882
- $skin_args['plugin'] = $this->plugins[ $slug ]['file_path'];
883
- $skin = new \Plugin_Upgrader_Skin( $skin_args );
884
- } else {
885
- $skin = new \Plugin_Installer_Skin( $skin_args );
886
- }
887
 
888
- // Create a new instance of Plugin_Upgrader.
889
- $upgrader = new \Plugin_Upgrader( $skin );
890
 
891
- // Perform the action and install the plugin from the $source urldecode().
892
- add_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
893
 
894
- if ( 'update' === $install_type ) {
895
- // Inject our info into the update transient.
896
- $to_inject = array( $slug => $this->plugins[ $slug ] );
897
- $to_inject[ $slug ]['source'] = $source;
898
- $this->inject_update_info( $to_inject );
899
 
900
- $upgrader->upgrade( $this->plugins[ $slug ]['file_path'] );
901
- } else {
902
- $upgrader->install( $source );
903
- }
904
 
905
- remove_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1 );
906
 
907
- // Make sure we have the correct file path now the plugin is installed/updated.
908
- $this->populate_file_path( $slug );
909
 
910
- // Only activate plugins if the config option is set to true and the plugin isn't
911
- // already active (upgrade).
912
- if ( $this->is_automatic && ! $this->is_plugin_active( $slug ) ) {
913
- $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method.
914
- if ( false === $this->activate_single_plugin( $plugin_activate, $slug, true ) ) {
915
- return true; // Finish execution of the function early as we encountered an error.
916
- }
917
  }
 
918
 
919
- $this->show_tgmpa_version();
920
 
921
- // Display message based on if all plugins are now active or not.
922
- if ( $this->is_tgmpa_complete() ) {
923
- echo '<p>', sprintf( esc_html( $this->strings['complete'] ), '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>' ), '</p>';
924
- echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
925
- } else {
926
- echo '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
927
- }
928
 
929
- return true;
930
- } elseif ( isset( $this->plugins[ $slug ]['file_path'], $_GET['tgmpa-activate'] ) && 'activate-plugin' === $_GET['tgmpa-activate'] ) {
931
- // Activate action link was clicked.
932
- check_admin_referer( 'tgmpa-activate', 'tgmpa-nonce' );
933
 
934
- if ( false === $this->activate_single_plugin( $this->plugins[ $slug ]['file_path'], $slug ) ) {
935
- return true; // Finish execution of the function early as we encountered an error.
936
- }
937
  }
938
-
939
- return false;
940
  }
941
 
942
- /**
943
- * Inject information into the 'update_plugins' site transient as WP checks that before running an update.
944
- *
945
- * @since 2.5.0
946
- *
947
- * @param array $plugins The plugin information for the plugins which are to be updated.
948
- */
949
- public function inject_update_info( $plugins ) {
950
- $repo_updates = get_site_transient( 'update_plugins' );
951
 
952
- if ( ! is_object( $repo_updates ) ) {
953
- $repo_updates = new stdClass;
954
- }
 
 
 
 
 
 
955
 
956
- foreach ( $plugins as $slug => $plugin ) {
957
- $file_path = $plugin['file_path'];
 
958
 
959
- if ( empty( $repo_updates->response[ $file_path ] ) ) {
960
- $repo_updates->response[ $file_path ] = new stdClass;
961
- }
962
 
963
- // We only really need to set package, but let's do all we can in case WP changes something.
964
- $repo_updates->response[ $file_path ]->slug = $slug;
965
- $repo_updates->response[ $file_path ]->plugin = $file_path;
966
- $repo_updates->response[ $file_path ]->new_version = $plugin['version'];
967
- $repo_updates->response[ $file_path ]->package = $plugin['source'];
968
- if ( empty( $repo_updates->response[ $file_path ]->url ) && ! empty( $plugin['external_url'] ) ) {
969
- $repo_updates->response[ $file_path ]->url = $plugin['external_url'];
970
- }
971
  }
972
 
973
- set_site_transient( 'update_plugins', $repo_updates );
 
 
 
 
 
 
 
974
  }
975
 
976
- /**
977
- * Adjust the plugin directory name if necessary.
978
- *
979
- * The final destination directory of a plugin is based on the subdirectory name found in the
980
- * (un)zipped source. In some cases - most notably GitHub repository plugin downloads -, this
981
- * subdirectory name is not the same as the expected slug and the plugin will not be recognized
982
- * as installed. This is fixed by adjusting the temporary unzipped source subdirectory name to
983
- * the expected plugin slug.
984
- *
985
- * @since 2.5.0
986
- *
987
- * @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
988
- * @param string $remote_source Path to upgrade/zip-file-name.tmp.
989
- * @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
990
- * @return string $source
991
- */
992
- public function maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
993
- if ( ! $this->is_tgmpa_page() || ! is_object( $GLOBALS['wp_filesystem'] ) ) {
994
- return $source;
995
- }
996
 
997
- // Check for single file plugins.
998
- $source_files = array_keys( $GLOBALS['wp_filesystem']->dirlist( $remote_source ) );
999
- if ( 1 === count( $source_files ) && false === $GLOBALS['wp_filesystem']->is_dir( $source ) ) {
1000
- return $source;
1001
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1002
 
1003
- // Multi-file plugin, let's see if the directory is correctly named.
1004
- $desired_slug = '';
 
 
 
1005
 
1006
- // Figure out what the slug is supposed to be.
1007
- if ( false === $upgrader->bulk && ! empty( $upgrader->skin->options['extra']['slug'] ) ) {
1008
- $desired_slug = $upgrader->skin->options['extra']['slug'];
1009
- } else {
1010
- // Bulk installer contains less info, so fall back on the info registered here.
1011
- foreach ( $this->plugins as $slug => $plugin ) {
1012
- if ( ! empty( $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) && $plugin['name'] === $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) {
1013
- $desired_slug = $slug;
1014
- break;
1015
- }
 
 
1016
  }
1017
- unset( $slug, $plugin );
1018
  }
 
 
1019
 
1020
- if ( ! empty( $desired_slug ) ) {
1021
- $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
1022
 
1023
- if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
1024
- $from_path = untrailingslashit( $source );
1025
- $to_path = trailingslashit( $remote_source ) . $desired_slug;
1026
 
1027
- if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
1028
- return trailingslashit( $to_path );
1029
- } else {
1030
- return new WP_Error( 'rename_failed', esc_html__( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
1031
- }
1032
- } elseif ( empty( $subdir_name ) ) {
1033
- return new WP_Error( 'packaged_wrong', esc_html__( 'The remote plugin package consists of more than one file, but the files are not packaged in a folder.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
1034
  }
 
 
1035
  }
1036
-
1037
- return $source;
1038
  }
1039
 
1040
- /**
1041
- * Activate a single plugin and send feedback about the result to the screen.
1042
- *
1043
- * @since 2.5.0
1044
- *
1045
- * @param string $file_path Path within wp-plugins/ to main plugin file.
1046
- * @param string $slug Plugin slug.
1047
- * @param bool $automatic Whether this is an automatic activation after an install. Defaults to false.
1048
- * This determines the styling of the output messages.
1049
- * @return bool False if an error was encountered, true otherwise.
1050
- */
1051
- protected function activate_single_plugin( $file_path, $slug, $automatic = false ) {
1052
- if ( $this->can_plugin_activate( $slug ) ) {
1053
- $activate = activate_plugin( $file_path );
1054
 
1055
- if ( is_wp_error( $activate ) ) {
1056
- echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>',
1057
- '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
 
 
 
 
 
 
 
 
 
 
 
1058
 
1059
- return false; // End it here if there is an error with activation.
1060
- } else {
1061
- if ( ! $automatic ) {
1062
- // Make sure message doesn't display again if bulk activation is performed
1063
- // immediately after a single activation.
1064
- if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
1065
- echo '<div id="message" class="updated"><p>', esc_html( $this->strings['activated_successfully'] ), ' <strong>', esc_html( $this->plugins[ $slug ]['name'] ), '.</strong></p></div>';
1066
- }
1067
- } else {
1068
- // Simpler message layout for use on the plugin install page.
1069
- echo '<p>', esc_html( $this->strings['plugin_activated'] ), '</p>';
1070
- }
1071
- }
1072
- } elseif ( $this->is_plugin_active( $slug ) ) {
1073
- // No simpler message format provided as this message should never be encountered
1074
- // on the plugin install page.
1075
- echo '<div id="message" class="error"><p>',
1076
- sprintf(
1077
- esc_html( $this->strings['plugin_already_active'] ),
1078
- '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
1079
- ),
1080
- '</p></div>';
1081
- } elseif ( $this->does_plugin_require_update( $slug ) ) {
1082
  if ( ! $automatic ) {
1083
  // Make sure message doesn't display again if bulk activation is performed
1084
  // immediately after a single activation.
1085
  if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
1086
- echo '<div id="message" class="error"><p>',
1087
- sprintf(
1088
- esc_html( $this->strings['plugin_needs_higher_version'] ),
1089
- '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
1090
- ),
1091
- '</p></div>';
1092
  }
1093
  } else {
1094
  // Simpler message layout for use on the plugin install page.
1095
- echo '<p>', sprintf( esc_html( $this->strings['plugin_needs_higher_version'] ), esc_html( $this->plugins[ $slug ]['name'] ) ), '</p>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1096
  }
 
 
 
1097
  }
 
1098
 
1099
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1100
  }
1101
 
1102
- /**
1103
- * Echoes required plugin notice.
1104
- *
1105
- * Outputs a message telling users that a specific plugin is required for
1106
- * their theme. If appropriate, it includes a link to the form page where
1107
- * users can install and activate the plugin.
1108
- *
1109
- * Returns early if we're on the Install page.
1110
- *
1111
- * @since 1.0.0
1112
- *
1113
- * @global object $current_screen
1114
- *
1115
- * @return null Returns early if we're on the Install page.
1116
- */
1117
- public function notices() {
1118
- // Remove nag on the install page / Return early if the nag message has been dismissed or user < author.
1119
- if ( ( $this->is_tgmpa_page() || $this->is_core_update_page() ) || get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) || ! current_user_can( apply_filters( 'tgmpa_show_admin_notice_capability', 'publish_posts' ) ) ) {
1120
- return;
1121
- }
1122
 
1123
- // Store for the plugin slugs by message type.
1124
- $message = array();
 
 
 
1125
 
1126
- // Initialize counters used to determine plurality of action link texts.
1127
- $install_link_count = 0;
1128
- $update_link_count = 0;
1129
- $activate_link_count = 0;
1130
- $total_required_action_count = 0;
1131
 
1132
- foreach ( $this->plugins as $slug => $plugin ) {
1133
- if ( $this->is_plugin_active( $slug ) && false === $this->does_plugin_have_update( $slug ) ) {
1134
- continue;
1135
- }
1136
 
1137
- if ( ! $this->is_plugin_installed( $slug ) ) {
1138
- if ( current_user_can( 'install_plugins' ) ) {
1139
- $install_link_count++;
 
 
 
 
 
 
 
 
 
 
1140
 
1141
  if ( true === $plugin['required'] ) {
1142
- $message['notice_can_install_required'][] = $slug;
1143
  } else {
1144
- $message['notice_can_install_recommended'][] = $slug;
1145
  }
1146
  }
1147
  if ( true === $plugin['required'] ) {
1148
  $total_required_action_count++;
1149
  }
1150
- } else {
1151
- if ( ! $this->is_plugin_active( $slug ) && $this->can_plugin_activate( $slug ) ) {
1152
- if ( current_user_can( 'activate_plugins' ) ) {
1153
- $activate_link_count++;
1154
-
1155
- if ( true === $plugin['required'] ) {
1156
- $message['notice_can_activate_required'][] = $slug;
1157
- } else {
1158
- $message['notice_can_activate_recommended'][] = $slug;
1159
- }
1160
- }
1161
- if ( true === $plugin['required'] ) {
1162
- $total_required_action_count++;
1163
- }
1164
- }
1165
 
1166
- if ( $this->does_plugin_require_update( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1167
 
1168
- if ( current_user_can( 'update_plugins' ) ) {
1169
- $update_link_count++;
1170
 
1171
- if ( $this->does_plugin_require_update( $slug ) ) {
1172
- $message['notice_ask_to_update'][] = $slug;
1173
- } elseif ( false !== $this->does_plugin_have_update( $slug ) ) {
1174
- $message['notice_ask_to_update_maybe'][] = $slug;
1175
- }
1176
- }
1177
- if ( true === $plugin['required'] ) {
1178
- $total_required_action_count++;
1179
  }
1180
  }
 
 
 
1181
  }
1182
  }
1183
- unset( $slug, $plugin );
1184
-
1185
- // If we have notices to display, we move forward.
1186
- if ( ! empty( $message ) || $total_required_action_count > 0 ) {
1187
- krsort( $message ); // Sort messages.
1188
- $rendered = '';
1189
 
1190
- // As add_settings_error() wraps the final message in a <p> and as the final message can't be
1191
- // filtered, using <p>'s in our html would render invalid html output.
1192
- $line_template = '<span style="display: block; margin: 0.5em 0.5em 0 0; clear: both;">%s</span>' . "\n";
 
1193
 
1194
- if ( ! current_user_can( 'activate_plugins' ) && ! current_user_can( 'install_plugins' ) && ! current_user_can( 'update_plugins' ) ) {
1195
- $rendered = esc_html( $this->strings['notice_cannot_install_activate'] ) . ' ' . esc_html( $this->strings['contact_admin'] );
1196
- $rendered .= $this->create_user_action_links_for_notice( 0, 0, 0, $line_template );
1197
- } else {
1198
 
1199
- // If dismissable is false and a message is set, output it now.
1200
- if ( ! $this->dismissable && ! empty( $this->dismiss_msg ) ) {
1201
- $rendered .= sprintf( $line_template, wp_kses_post( $this->dismiss_msg ) );
1202
- }
1203
 
1204
- // Render the individual message lines for the notice.
1205
- foreach ( $message as $type => $plugin_group ) {
1206
- $linked_plugins = array();
 
1207
 
1208
- // Get the external info link for a plugin if one is available.
1209
- foreach ( $plugin_group as $plugin_slug ) {
1210
- $linked_plugins[] = $this->get_info_link( $plugin_slug );
1211
- }
1212
- unset( $plugin_slug );
1213
-
1214
- $count = count( $plugin_group );
1215
- $linked_plugins = array_map( array( 'WPMailSMTP\TGMPA_Utils', 'wrap_in_em' ), $linked_plugins );
1216
- $last_plugin = array_pop( $linked_plugins ); // Pop off last name to prep for readability.
1217
- $imploded = empty( $linked_plugins ) ? $last_plugin : ( implode( ', ', $linked_plugins ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
1218
-
1219
- $rendered .= sprintf(
1220
- $line_template,
1221
- sprintf(
1222
- translate_nooped_plural( $this->strings[ $type ], $count, 'tgmpa' ),
1223
- $imploded,
1224
- $count
1225
- )
1226
- );
1227
 
 
 
 
1228
  }
1229
- unset( $type, $plugin_group, $linked_plugins, $count, $last_plugin, $imploded );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1230
 
1231
- $rendered .= $this->create_user_action_links_for_notice( $install_link_count, $update_link_count, $activate_link_count, $line_template );
1232
  }
 
1233
 
1234
- // Register the nag messages and prepare them to be processed.
1235
- add_settings_error( 'tgmpa', 'tgmpa', $rendered, $this->get_admin_notice_class() );
1236
  }
1237
 
1238
- // Admin options pages already output settings_errors, so this is to avoid duplication.
1239
- if ( 'options-general' !== $GLOBALS['current_screen']->parent_base ) {
1240
- $this->display_settings_errors();
1241
- }
1242
  }
1243
 
1244
- /**
1245
- * Generate the user action links for the admin notice.
1246
- *
1247
- * @since 2.6.0
1248
- *
1249
- * @param int $install_count Number of plugins to install.
1250
- * @param int $update_count Number of plugins to update.
1251
- * @param int $activate_count Number of plugins to activate.
1252
- * @param int $line_template Template for the HTML tag to output a line.
1253
- * @return string Action links.
1254
- */
1255
- protected function create_user_action_links_for_notice( $install_count, $update_count, $activate_count, $line_template ) {
1256
- // Setup action links.
1257
- $action_links = array(
1258
- 'install' => '',
1259
- 'update' => '',
1260
- 'activate' => '',
1261
- 'dismiss' => $this->dismissable ? '<a href="' . esc_url( wp_nonce_url( add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ), 'tgmpa-dismiss-' . get_current_user_id() ) ) . '" class="dismiss-notice" target="_parent">' . esc_html( $this->strings['dismiss'] ) . '</a>' : '',
1262
- );
 
 
 
 
 
 
1263
 
1264
- $link_template = '<a href="%2$s">%1$s</a>';
1265
 
1266
- if ( current_user_can( 'install_plugins' ) ) {
1267
- if ( $install_count > 0 ) {
1268
- $action_links['install'] = sprintf(
1269
- $link_template,
1270
- translate_nooped_plural( $this->strings['install_link'], $install_count, 'tgmpa' ),
1271
- esc_url( $this->get_tgmpa_status_url( 'install' ) )
1272
- );
1273
- }
1274
- if ( $update_count > 0 ) {
1275
- $action_links['update'] = sprintf(
1276
- $link_template,
1277
- translate_nooped_plural( $this->strings['update_link'], $update_count, 'tgmpa' ),
1278
- esc_url( $this->get_tgmpa_status_url( 'update' ) )
1279
- );
1280
- }
1281
  }
1282
-
1283
- if ( current_user_can( 'activate_plugins' ) && $activate_count > 0 ) {
1284
- $action_links['activate'] = sprintf(
1285
  $link_template,
1286
- translate_nooped_plural( $this->strings['activate_link'], $activate_count, 'tgmpa' ),
1287
- esc_url( $this->get_tgmpa_status_url( 'activate' ) )
1288
  );
1289
  }
 
 
 
 
 
 
 
 
 
1290
 
1291
- $action_links = apply_filters( 'tgmpa_notice_action_links', $action_links );
1292
 
1293
- $action_links = array_filter( (array) $action_links ); // Remove any empty array items.
1294
 
1295
- if ( ! empty( $action_links ) ) {
1296
- $action_links = sprintf( $line_template, implode( ' | ', $action_links ) );
1297
- return apply_filters( 'tgmpa_notice_rendered_action_links', $action_links );
1298
- } else {
1299
- return '';
1300
- }
1301
  }
 
1302
 
1303
- /**
1304
- * Get admin notice class.
1305
- *
1306
- * Work around all the changes to the various admin notice classes between WP 4.4 and 3.7
1307
- * (lowest supported version by TGMPA).
1308
- *
1309
- * @since 2.6.0
1310
- *
1311
- * @return string
1312
- */
1313
- protected function get_admin_notice_class() {
1314
- if ( ! empty( $this->strings['nag_type'] ) ) {
1315
- return sanitize_html_class( strtolower( $this->strings['nag_type'] ) );
 
 
 
 
 
1316
  } else {
1317
- if ( version_compare( $this->wp_version, '4.2', '>=' ) ) {
1318
- return 'notice-warning';
1319
- } elseif ( version_compare( $this->wp_version, '4.1', '>=' ) ) {
1320
- return 'notice';
1321
- } else {
1322
- return 'updated';
1323
- }
1324
  }
1325
  }
 
1326
 
1327
- /**
1328
- * Display settings errors and remove those which have been displayed to avoid duplicate messages showing
1329
- *
1330
- * @since 2.5.0
1331
- */
1332
- protected function display_settings_errors() {
1333
- global $wp_settings_errors;
1334
 
1335
- settings_errors( 'tgmpa' );
1336
 
1337
- foreach ( (array) $wp_settings_errors as $key => $details ) {
1338
- if ( 'tgmpa' === $details['setting'] ) {
1339
- unset( $wp_settings_errors[ $key ] );
1340
- break;
1341
- }
1342
  }
1343
  }
 
1344
 
1345
- /**
1346
- * Register dismissal of admin notices.
1347
- *
1348
- * Acts on the dismiss link in the admin nag messages.
1349
- * If clicked, the admin notice disappears and will no longer be visible to this user.
1350
- *
1351
- * @since 2.1.0
1352
- */
1353
- public function dismiss() {
1354
- if ( isset( $_GET['tgmpa-dismiss'] ) && check_admin_referer( 'tgmpa-dismiss-' . get_current_user_id() ) ) {
1355
- update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, 1 );
1356
- }
1357
  }
 
1358
 
1359
- /**
1360
- * Add individual plugin to our collection of plugins.
1361
- *
1362
- * If the required keys are not set or the plugin has already
1363
- * been registered, the plugin is not added.
1364
- *
1365
- * @since 2.0.0
1366
- *
1367
- * @param array|null $plugin Array of plugin arguments or null if invalid argument.
1368
- * @return null Return early if incorrect argument.
1369
- */
1370
- public function register( $plugin ) {
1371
- if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
1372
- return;
1373
- }
1374
 
1375
- if ( empty( $plugin['slug'] ) || ! is_string( $plugin['slug'] ) || isset( $this->plugins[ $plugin['slug'] ] ) ) {
1376
- return;
1377
- }
1378
 
1379
- $defaults = array(
1380
- 'name' => '', // String
1381
- 'slug' => '', // String
1382
- 'source' => 'repo', // String
1383
- 'required' => false, // Boolean
1384
- 'version' => '', // String
1385
- 'force_activation' => false, // Boolean
1386
- 'force_deactivation' => false, // Boolean
1387
- 'external_url' => '', // String
1388
- 'is_callable' => '', // String|Array.
1389
- );
1390
 
1391
- // Prepare the received data.
1392
- $plugin = wp_parse_args( $plugin, $defaults );
1393
 
1394
- // Standardize the received slug.
1395
- $plugin['slug'] = $this->sanitize_key( $plugin['slug'] );
1396
 
1397
- // Forgive users for using string versions of booleans or floats for version number.
1398
- $plugin['version'] = (string) $plugin['version'];
1399
- $plugin['source'] = empty( $plugin['source'] ) ? 'repo' : $plugin['source'];
1400
- $plugin['required'] = TGMPA_Utils::validate_bool( $plugin['required'] );
1401
- $plugin['force_activation'] = TGMPA_Utils::validate_bool( $plugin['force_activation'] );
1402
- $plugin['force_deactivation'] = TGMPA_Utils::validate_bool( $plugin['force_deactivation'] );
1403
 
1404
- // Enrich the received data.
1405
- $plugin['file_path'] = $this->_get_plugin_basename_from_slug( $plugin['slug'] );
1406
- $plugin['source_type'] = $this->get_plugin_source_type( $plugin['source'] );
1407
 
1408
- // Set the class properties.
1409
- $this->plugins[ $plugin['slug'] ] = $plugin;
1410
- $this->sort_order[ $plugin['slug'] ] = $plugin['name'];
1411
 
1412
- // Should we add the force activation hook ?
1413
- if ( true === $plugin['force_activation'] ) {
1414
- $this->has_forced_activation = true;
1415
- }
1416
 
1417
- // Should we add the force deactivation hook ?
1418
- if ( true === $plugin['force_deactivation'] ) {
1419
- $this->has_forced_deactivation = true;
1420
- }
1421
  }
 
1422
 
1423
- /**
1424
- * Determine what type of source the plugin comes from.
1425
- *
1426
- * @since 2.5.0
1427
- *
1428
- * @param string $source The source of the plugin as provided, either empty (= WP repo), a file path
1429
- * (= bundled) or an external URL.
1430
- * @return string 'repo', 'external', or 'bundled'
1431
- */
1432
- protected function get_plugin_source_type( $source ) {
1433
- if ( 'repo' === $source || preg_match( self::WP_REPO_REGEX, $source ) ) {
1434
- return 'repo';
1435
- } elseif ( preg_match( self::IS_URL_REGEX, $source ) ) {
1436
- return 'external';
1437
- } else {
1438
- return 'bundled';
1439
- }
1440
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1441
 
1442
  /**
1443
- * Sanitizes a string key.
1444
- *
1445
- * Near duplicate of WP Core `sanitize_key()`. The difference is that uppercase characters *are*
1446
- * allowed, so as not to break upgrade paths from non-standard bundled plugins using uppercase
1447
- * characters in the plugin directory path/slug. Silly them.
1448
- *
1449
- * @see https://developer.wordpress.org/reference/hooks/sanitize_key/
1450
  *
1451
  * @since 2.5.0
1452
  *
1453
- * @param string $key String key.
1454
- * @return string Sanitized key
1455
  */
1456
- public function sanitize_key( $key ) {
1457
- $raw_key = $key;
1458
- $key = preg_replace( '`[^A-Za-z0-9_-]`', '', $key );
1459
-
1460
- /**
1461
- * Filter a sanitized key string.
1462
- *
1463
- * @since 2.5.0
1464
- *
1465
- * @param string $key Sanitized key.
1466
- * @param string $raw_key The key prior to sanitization.
1467
- */
1468
- return apply_filters( 'tgmpa_sanitize_key', $key, $raw_key );
1469
- }
1470
 
1471
- /**
1472
- * Amend default configuration settings.
1473
- *
1474
- * @since 2.0.0
1475
- *
1476
- * @param array $config Array of config options to pass as class properties.
1477
- */
1478
- public function config( $config ) {
1479
- $keys = array(
1480
- 'id',
1481
- 'default_path',
1482
- 'has_notices',
1483
- 'dismissable',
1484
- 'dismiss_msg',
1485
- 'menu',
1486
- 'parent_slug',
1487
- 'capability',
1488
- 'is_automatic',
1489
- 'message',
1490
- 'strings',
1491
- );
1492
 
1493
- foreach ( $keys as $key ) {
1494
- if ( isset( $config[ $key ] ) ) {
1495
- if ( is_array( $config[ $key ] ) ) {
1496
- $this->$key = array_merge( $this->$key, $config[ $key ] );
1497
- } else {
1498
- $this->$key = $config[ $key ];
1499
- }
1500
  }
1501
  }
1502
  }
 
1503
 
1504
- /**
1505
- * Amend action link after plugin installation.
1506
- *
1507
- * @since 2.0.0
1508
- *
1509
- * @param array $install_actions Existing array of actions.
1510
- * @return false|array Amended array of actions.
1511
- */
1512
- public function actions( $install_actions ) {
1513
- // Remove action links on the TGMPA install page.
1514
- if ( $this->is_tgmpa_page() ) {
1515
- return false;
1516
- }
1517
-
1518
- return $install_actions;
1519
- }
1520
-
1521
- /**
1522
- * Flushes the plugins cache on theme switch to prevent stale entries
1523
- * from remaining in the plugin table.
1524
- *
1525
- * @since 2.4.0
1526
- *
1527
- * @param bool $clear_update_cache Optional. Whether to clear the Plugin updates cache.
1528
- * Parameter added in v2.5.0.
1529
- */
1530
- public function flush_plugins_cache( $clear_update_cache = true ) {
1531
- wp_clean_plugins_cache( $clear_update_cache );
1532
  }
1533
 
1534
- /**
1535
- * Set file_path key for each installed plugin.
1536
- *
1537
- * @since 2.1.0
1538
- *
1539
- * @param string $plugin_slug Optional. If set, only (re-)populates the file path for that specific plugin.
1540
- * Parameter added in v2.5.0.
1541
- */
1542
- public function populate_file_path( $plugin_slug = '' ) {
1543
- if ( ! empty( $plugin_slug ) && is_string( $plugin_slug ) && isset( $this->plugins[ $plugin_slug ] ) ) {
1544
- $this->plugins[ $plugin_slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $plugin_slug );
1545
- } else {
1546
- // Add file_path key for all plugins.
1547
- foreach ( $this->plugins as $slug => $values ) {
1548
- $this->plugins[ $slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $slug );
1549
- }
1550
- }
1551
- }
1552
 
1553
- /**
1554
- * Helper function to extract the file path of the plugin file from the
1555
- * plugin slug, if the plugin is installed.
1556
- *
1557
- * @since 2.0.0
1558
- *
1559
- * @param string $slug Plugin slug (typically folder name) as provided by the developer.
1560
- * @return string Either file path for plugin if installed, or just the plugin slug.
1561
- */
1562
- protected function _get_plugin_basename_from_slug( $slug ) {
1563
- $keys = array_keys( $this->get_plugins() );
 
1564
 
1565
- foreach ( $keys as $key ) {
1566
- if ( preg_match( '|^' . $slug . '/|', $key ) ) {
1567
- return $key;
1568
- }
 
 
 
 
 
 
 
 
 
 
 
1569
  }
1570
-
1571
- return $slug;
1572
  }
 
1573
 
1574
- /**
1575
- * Retrieve plugin data, given the plugin name.
1576
- *
1577
- * Loops through the registered plugins looking for $name. If it finds it,
1578
- * it returns the $data from that plugin. Otherwise, returns false.
1579
- *
1580
- * @since 2.1.0
1581
- *
1582
- * @param string $name Name of the plugin, as it was registered.
1583
- * @param string $data Optional. Array key of plugin data to return. Default is slug.
1584
- * @return string|boolean Plugin slug if found, false otherwise.
1585
- */
1586
- public function _get_plugin_data_from_name( $name, $data = 'slug' ) {
1587
- foreach ( $this->plugins as $values ) {
1588
- if ( $name === $values['name'] && isset( $values[ $data ] ) ) {
1589
- return $values[ $data ];
1590
- }
1591
- }
1592
 
1593
- return false;
 
 
 
1594
  }
1595
 
1596
- /**
1597
- * Retrieve the download URL for a package.
1598
- *
1599
- * @since 2.5.0
1600
- *
1601
- * @param string $slug Plugin slug.
1602
- * @return string Plugin download URL or path to local file or empty string if undetermined.
1603
- */
1604
- public function get_download_url( $slug ) {
1605
- $dl_source = '';
1606
 
1607
- switch ( $this->plugins[ $slug ]['source_type'] ) {
1608
- case 'repo':
1609
- return $this->get_wp_repo_download_url( $slug );
1610
- case 'external':
1611
- return $this->plugins[ $slug ]['source'];
1612
- case 'bundled':
1613
- return $this->default_path . $this->plugins[ $slug ]['source'];
 
 
 
 
 
 
 
 
 
1614
  }
1615
-
1616
- return $dl_source; // Should never happen.
1617
  }
1618
 
1619
- /**
1620
- * Retrieve the download URL for a WP repo package.
1621
- *
1622
- * @since 2.5.0
1623
- *
1624
- * @param string $slug Plugin slug.
1625
- * @return string Plugin download URL.
1626
- */
1627
- protected function get_wp_repo_download_url( $slug ) {
1628
- $source = '';
1629
- $api = $this->get_plugins_api( $slug );
1630
 
1631
- if ( false !== $api && isset( $api->download_link ) ) {
1632
- $source = $api->download_link;
1633
- }
 
 
 
 
 
 
 
1634
 
1635
- return $source;
 
 
 
 
 
 
1636
  }
1637
 
1638
- /**
1639
- * Try to grab information from WordPress API.
1640
- *
1641
- * @since 2.5.0
1642
- *
1643
- * @param string $slug Plugin slug.
1644
- * @return object Plugins_api response object on success, WP_Error on failure.
1645
- */
1646
- protected function get_plugins_api( $slug ) {
1647
- static $api = array(); // Cache received responses.
1648
 
1649
- if ( ! isset( $api[ $slug ] ) ) {
1650
- if ( ! function_exists( 'plugins_api' ) ) {
1651
- require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1652
- }
 
 
 
 
 
 
 
1653
 
1654
- $response = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
 
 
1655
 
1656
- $api[ $slug ] = false;
 
1657
 
1658
- if ( is_wp_error( $response ) ) {
1659
- wp_die( esc_html( $this->strings['oops'] ) );
1660
- } else {
1661
- $api[ $slug ] = $response;
1662
- }
1663
- }
 
 
 
 
1664
 
1665
- return $api[ $slug ];
1666
- }
 
 
1667
 
1668
- /**
1669
- * Retrieve a link to a plugin information page.
1670
- *
1671
- * @since 2.5.0
1672
- *
1673
- * @param string $slug Plugin slug.
1674
- * @return string Fully formed html link to a plugin information page if available
1675
- * or the plugin name if not.
1676
- */
1677
- public function get_info_link( $slug ) {
1678
- if ( ! empty( $this->plugins[ $slug ]['external_url'] ) && preg_match( self::IS_URL_REGEX, $this->plugins[ $slug ]['external_url'] ) ) {
1679
- $link = sprintf(
1680
- '<a href="%1$s" target="_blank">%2$s</a>',
1681
- esc_url( $this->plugins[ $slug ]['external_url'] ),
1682
- esc_html( $this->plugins[ $slug ]['name'] )
1683
- );
1684
- } elseif ( 'repo' === $this->plugins[ $slug ]['source_type'] ) {
1685
- $url = add_query_arg(
1686
- array(
1687
- 'tab' => 'plugin-information',
1688
- 'plugin' => urlencode( $slug ),
1689
- 'TB_iframe' => 'true',
1690
- 'width' => '640',
1691
- 'height' => '500',
1692
  ),
1693
- self_admin_url( 'plugin-install.php' )
1694
- );
1695
 
1696
- $link = sprintf(
1697
- '<a href="%1$s" class="thickbox">%2$s</a>',
1698
- esc_url( $url ),
1699
- esc_html( $this->plugins[ $slug ]['name'] )
1700
- );
1701
  } else {
1702
- $link = esc_html( $this->plugins[ $slug ]['name'] ); // No hyperlink.
1703
  }
1704
-
1705
- return $link;
1706
  }
1707
 
1708
- /**
1709
- * Determine if we're on the TGMPA Install page.
1710
- *
1711
- * @since 2.1.0
1712
- *
1713
- * @return boolean True when on the TGMPA page, false otherwise.
1714
- */
1715
- protected function is_tgmpa_page() {
1716
- return isset( $_GET['page'] ) && $this->menu === $_GET['page'];
1717
- }
1718
 
1719
- /**
1720
- * Determine if we're on a WP Core installation/upgrade page.
1721
- *
1722
- * @since 2.6.0
1723
- *
1724
- * @return boolean True when on a WP Core installation/upgrade page, false otherwise.
1725
- */
1726
- protected function is_core_update_page() {
1727
- // Current screen is not always available, most notably on the customizer screen.
1728
- if ( ! function_exists( 'get_current_screen' ) ) {
1729
- return false;
1730
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1731
 
1732
- $screen = get_current_screen();
 
 
 
 
 
 
 
1733
 
1734
- if ( 'update-core' === $screen->base ) {
1735
- // Core update screen.
1736
- return true;
1737
- } elseif ( 'plugins' === $screen->base && ! empty( $_POST['action'] ) ) { // WPCS: CSRF ok.
1738
- // Plugins bulk update screen.
1739
- return true;
1740
- } elseif ( 'update' === $screen->base && ! empty( $_POST['action'] ) ) { // WPCS: CSRF ok.
1741
- // Individual updates (ajax call).
1742
- return true;
1743
- }
 
 
 
1744
 
 
 
 
 
 
 
 
 
 
 
1745
  return false;
1746
  }
1747
 
1748
- /**
1749
- * Retrieve the URL to the TGMPA Install page.
1750
- *
1751
- * I.e. depending on the config settings passed something along the lines of:
1752
- * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins
1753
- *
1754
- * @since 2.5.0
1755
- *
1756
- * @return string Properly encoded URL (not escaped).
1757
- */
1758
- public function get_tgmpa_url() {
1759
- static $url;
1760
-
1761
- if ( ! isset( $url ) ) {
1762
- $parent = $this->parent_slug;
1763
- if ( false === strpos( $parent, '.php' ) ) {
1764
- $parent = 'admin.php';
1765
- }
1766
- $url = add_query_arg(
1767
- array(
1768
- 'page' => urlencode( $this->menu ),
1769
- ),
1770
- self_admin_url( $parent )
1771
- );
1772
- }
1773
 
1774
- return $url;
 
 
 
 
 
 
 
 
1775
  }
1776
 
1777
- /**
1778
- * Retrieve the URL to the TGMPA Install page for a specific plugin status (view).
1779
- *
1780
- * I.e. depending on the config settings passed something along the lines of:
1781
- * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins&plugin_status=install
1782
- *
1783
- * @since 2.5.0
1784
- *
1785
- * @param string $status Plugin status - either 'install', 'update' or 'activate'.
1786
- * @return string Properly encoded URL (not escaped).
1787
- */
1788
- public function get_tgmpa_status_url( $status ) {
1789
- return add_query_arg(
 
 
 
 
 
 
 
 
 
1790
  array(
1791
- 'plugin_status' => urlencode( $status ),
1792
  ),
1793
- $this->get_tgmpa_url()
1794
  );
1795
  }
1796
 
1797
- /**
1798
- * Determine whether there are open actions for plugins registered with TGMPA.
1799
- *
1800
- * @since 2.5.0
1801
- *
1802
- * @return bool True if complete, i.e. no outstanding actions. False otherwise.
1803
- */
1804
- public function is_tgmpa_complete() {
1805
- $complete = true;
1806
- foreach ( $this->plugins as $slug => $plugin ) {
1807
- if ( ! $this->is_plugin_active( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1808
- $complete = false;
1809
- break;
1810
- }
1811
- }
1812
-
1813
- return $complete;
1814
- }
1815
 
1816
- /**
1817
- * Check if a plugin is installed. Does not take must-use plugins into account.
1818
- *
1819
- * @since 2.5.0
1820
- *
1821
- * @param string $slug Plugin slug.
1822
- * @return bool True if installed, false otherwise.
1823
- */
1824
- public function is_plugin_installed( $slug ) {
1825
- $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
 
 
 
 
 
 
 
 
 
1826
 
1827
- return ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ] ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
1828
  }
1829
 
1830
- /**
1831
- * Check if a plugin is active.
1832
- *
1833
- * @since 2.5.0
1834
- *
1835
- * @param string $slug Plugin slug.
1836
- * @return bool True if active, false otherwise.
1837
- */
1838
- public function is_plugin_active( $slug ) {
1839
- return ( ( ! empty( $this->plugins[ $slug ]['is_callable'] ) && is_callable( $this->plugins[ $slug ]['is_callable'] ) ) || is_plugin_active( $this->plugins[ $slug ]['file_path'] ) );
1840
- }
1841
 
1842
- /**
1843
- * Check if a plugin can be updated, i.e. if we have information on the minimum WP version required
1844
- * available, check whether the current install meets them.
1845
- *
1846
- * @since 2.5.0
1847
- *
1848
- * @param string $slug Plugin slug.
1849
- * @return bool True if OK to update, false otherwise.
1850
- */
1851
- public function can_plugin_update( $slug ) {
1852
- // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1853
- if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1854
- return true;
1855
- }
1856
 
1857
- $api = $this->get_plugins_api( $slug );
 
1858
 
1859
- if ( false !== $api && isset( $api->requires ) ) {
1860
- return version_compare( $this->wp_version, $api->requires, '>=' );
1861
- }
 
 
 
 
 
 
 
 
1862
 
1863
- // No usable info received from the plugins API, presume we can update.
 
 
 
 
 
 
 
 
 
 
 
1864
  return true;
1865
  }
1866
 
1867
- /**
1868
- * Check to see if the plugin is 'updatetable', i.e. installed, with an update available
1869
- * and no WP version requirements blocking it.
1870
- *
1871
- * @since 2.6.0
1872
- *
1873
- * @param string $slug Plugin slug.
1874
- * @return bool True if OK to proceed with update, false otherwise.
1875
- */
1876
- public function is_plugin_updatetable( $slug ) {
1877
- if ( ! $this->is_plugin_installed( $slug ) ) {
1878
- return false;
1879
- } else {
1880
- return ( false !== $this->does_plugin_have_update( $slug ) && $this->can_plugin_update( $slug ) );
1881
- }
1882
- }
1883
 
1884
- /**
1885
- * Check if a plugin can be activated, i.e. is not currently active and meets the minimum
1886
- * plugin version requirements set in TGMPA (if any).
1887
- *
1888
- * @since 2.5.0
1889
- *
1890
- * @param string $slug Plugin slug.
1891
- * @return bool True if OK to activate, false otherwise.
1892
- */
1893
- public function can_plugin_activate( $slug ) {
1894
- return ( ! $this->is_plugin_active( $slug ) && ! $this->does_plugin_require_update( $slug ) );
1895
  }
1896
 
1897
- /**
1898
- * Retrieve the version number of an installed plugin.
1899
- *
1900
- * @since 2.5.0
1901
- *
1902
- * @param string $slug Plugin slug.
1903
- * @return string Version number as string or an empty string if the plugin is not installed
1904
- * or version unknown (plugins which don't comply with the plugin header standard).
1905
- */
1906
- public function get_installed_version( $slug ) {
1907
- $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1908
-
1909
- if ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'] ) ) {
1910
- return $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'];
1911
- }
1912
 
1913
- return '';
 
 
 
 
 
 
 
 
 
 
 
 
 
1914
  }
 
1915
 
1916
- /**
1917
- * Check whether a plugin complies with the minimum version requirements.
1918
- *
1919
- * @since 2.5.0
1920
- *
1921
- * @param string $slug Plugin slug.
1922
- * @return bool True when a plugin needs to be updated, otherwise false.
1923
- */
1924
- public function does_plugin_require_update( $slug ) {
1925
- $installed_version = $this->get_installed_version( $slug );
1926
- $minimum_version = $this->plugins[ $slug ]['version'];
 
1927
 
1928
- return version_compare( $minimum_version, $installed_version, '>' );
 
 
 
 
 
 
 
 
 
 
 
 
 
1929
  }
1930
 
1931
- /**
1932
- * Check whether there is an update available for a plugin.
1933
- *
1934
- * @since 2.5.0
1935
- *
1936
- * @param string $slug Plugin slug.
1937
- * @return false|string Version number string of the available update or false if no update available.
1938
- */
1939
- public function does_plugin_have_update( $slug ) {
1940
- // Presume bundled and external plugins will point to a package which meets the minimum required version.
1941
- if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1942
- if ( $this->does_plugin_require_update( $slug ) ) {
1943
- return $this->plugins[ $slug ]['version'];
1944
- }
1945
 
1946
- return false;
1947
- }
 
 
 
 
 
 
 
 
 
1948
 
1949
- $repo_updates = get_site_transient( 'update_plugins' );
 
1950
 
1951
- if ( isset( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version ) ) {
1952
- return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version;
 
 
 
 
 
 
 
 
 
 
 
1953
  }
1954
 
1955
  return false;
1956
  }
1957
 
1958
- /**
1959
- * Retrieve potential upgrade notice for a plugin.
1960
- *
1961
- * @since 2.5.0
1962
- *
1963
- * @param string $slug Plugin slug.
1964
- * @return string The upgrade notice or an empty string if no message was available or provided.
1965
- */
1966
- public function get_upgrade_notice( $slug ) {
1967
- // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1968
- if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1969
- return '';
1970
- }
1971
 
1972
- $repo_updates = get_site_transient( 'update_plugins' );
 
 
1973
 
1974
- if ( ! empty( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice ) ) {
1975
- return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice;
1976
- }
1977
 
 
 
 
 
 
 
 
 
 
 
 
1978
  return '';
1979
  }
1980
 
1981
- /**
1982
- * Wrapper around the core WP get_plugins function, making sure it's actually available.
1983
- *
1984
- * @since 2.5.0
1985
- *
1986
- * @param string $plugin_folder Optional. Relative path to single plugin folder.
1987
- * @return array Array of installed plugins with plugin information.
1988
- */
1989
- public function get_plugins( $plugin_folder = '' ) {
1990
- if ( ! function_exists( 'get_plugins' ) ) {
1991
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
1992
- }
1993
 
1994
- return get_plugins( $plugin_folder );
 
1995
  }
1996
 
1997
- /**
1998
- * Delete dismissable nag option when theme is switched.
1999
- *
2000
- * This ensures that the user(s) is/are again reminded via nag of required
2001
- * and/or recommended plugins if they re-activate the theme.
2002
- *
2003
- * @since 2.1.1
2004
- */
2005
- public function update_dismiss() {
2006
- delete_metadata( 'user', null, 'tgmpa_dismissed_notice_' . $this->id, null, true );
2007
- }
2008
 
2009
- /**
2010
- * Forces plugin activation if the parameter 'force_activation' is
2011
- * set to true.
2012
- *
2013
- * This allows theme authors to specify certain plugins that must be
2014
- * active at all times while using the current theme.
2015
- *
2016
- * Please take special care when using this parameter as it has the
2017
- * potential to be harmful if not used correctly. Setting this parameter
2018
- * to true will not allow the specified plugin to be deactivated unless
2019
- * the user switches themes.
2020
- *
2021
- * @since 2.2.0
2022
- */
2023
- public function force_activation() {
2024
- foreach ( $this->plugins as $slug => $plugin ) {
2025
- if ( true === $plugin['force_activation'] ) {
2026
- if ( ! $this->is_plugin_installed( $slug ) ) {
2027
- // Oops, plugin isn't there so iterate to next condition.
2028
- continue;
2029
- } elseif ( $this->can_plugin_activate( $slug ) ) {
2030
- // There we go, activate the plugin.
2031
- activate_plugin( $plugin['file_path'] );
2032
- }
2033
- }
2034
- }
2035
  }
2036
 
2037
- /**
2038
- * Forces plugin deactivation if the parameter 'force_deactivation'
2039
- * is set to true and adds the plugin to the 'recently active' plugins list.
2040
- *
2041
- * This allows theme authors to specify certain plugins that must be
2042
- * deactivated upon switching from the current theme to another.
2043
- *
2044
- * Please take special care when using this parameter as it has the
2045
- * potential to be harmful if not used correctly.
2046
- *
2047
- * @since 2.2.0
2048
- */
2049
- public function force_deactivation() {
2050
- $deactivated = array();
2051
 
2052
- foreach ( $this->plugins as $slug => $plugin ) {
2053
- /*
2054
- * Only proceed forward if the parameter is set to true and plugin is active
2055
- * as a 'normal' (not must-use) plugin.
2056
- */
2057
- if ( true === $plugin['force_deactivation'] && is_plugin_active( $plugin['file_path'] ) ) {
2058
- deactivate_plugins( $plugin['file_path'] );
2059
- $deactivated[ $plugin['file_path'] ] = time();
2060
- }
2061
- }
 
2062
 
2063
- if ( ! empty( $deactivated ) ) {
2064
- update_option( 'recently_activated', $deactivated + (array) get_option( 'recently_activated' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2065
  }
2066
  }
 
2067
 
2068
- /**
2069
- * Echo the current TGMPA version number to the page.
2070
- *
2071
- * @since 2.5.0
2072
- */
2073
- public function show_tgmpa_version() {
2074
- echo '<p style="float: right; padding: 0em 1.5em 0.5em 0;"><strong><small>',
2075
- esc_html(
2076
- sprintf(
2077
- /* translators: %s: version number */
2078
- __( 'TGMPA v%s', 'tgmpa' ),
2079
- self::TGMPA_VERSION
2080
- )
2081
- ),
2082
- '</small></strong></p>';
2083
- }
2084
 
2085
- /**
2086
- * Returns the singleton instance of the class.
2087
- *
2088
- * @since 2.4.0
2089
- *
2090
- * @return \TGM_Plugin_Activation The TGM_Plugin_Activation object.
2091
- */
2092
- public static function get_instance() {
2093
- if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
2094
- self::$instance = new self();
2095
  }
2096
-
2097
- return self::$instance;
2098
  }
2099
- }
2100
 
2101
- if ( ! function_exists( 'load_tgm_plugin_activation' ) ) {
2102
- /**
2103
- * Ensure only one instance of the class is ever invoked.
2104
- *
2105
- * @since 2.5.0
2106
- */
2107
- function load_tgm_plugin_activation() {
2108
- $GLOBALS['wp_mail_smtp_tgmpa'] = TGM_Plugin_Activation::get_instance();
2109
  }
2110
  }
2111
 
2112
- if ( did_action( 'plugins_loaded' ) ) {
2113
- load_tgm_plugin_activation();
2114
- } else {
2115
- add_action( 'plugins_loaded', 'WPMailSMTP\load_tgm_plugin_activation' );
 
 
 
 
 
 
 
 
 
 
 
2116
  }
2117
- }
2118
 
2119
- if ( ! function_exists( 'tgmpa' ) ) {
2120
  /**
2121
- * Helper function to register a collection of required plugins.
2122
  *
2123
- * @since 2.0.0
2124
- * @api
2125
  *
2126
- * @param array $plugins An array of plugin arrays.
2127
- * @param array $config Optional. An array of configuration values.
2128
  */
2129
- function tgmpa( $plugins, $config = array() ) {
2130
- $instance = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
2131
-
2132
- foreach ( $plugins as $plugin ) {
2133
- call_user_func( array( $instance, 'register' ), $plugin );
2134
  }
2135
 
2136
- if ( ! empty( $config ) && is_array( $config ) ) {
2137
- // Send out notices for deprecated arguments passed.
2138
- if ( isset( $config['notices'] ) ) {
2139
- _deprecated_argument( __FUNCTION__, '2.2.0', 'The `notices` config parameter was renamed to `has_notices` in TGMPA 2.2.0. Please adjust your configuration.' );
2140
- if ( ! isset( $config['has_notices'] ) ) {
2141
- $config['has_notices'] = $config['notices'];
2142
- }
2143
- }
2144
 
2145
- if ( isset( $config['parent_menu_slug'] ) ) {
2146
- _deprecated_argument( __FUNCTION__, '2.4.0', 'The `parent_menu_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.' );
2147
- }
2148
- if ( isset( $config['parent_url_slug'] ) ) {
2149
- _deprecated_argument( __FUNCTION__, '2.4.0', 'The `parent_url_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2150
  }
 
2151
 
2152
- call_user_func( array( $instance, 'config' ), $config );
 
 
 
 
2153
  }
 
 
2154
  }
2155
  }
2156
 
@@ -2160,982 +2161,972 @@ if ( ! function_exists( 'tgmpa' ) ) {
2160
  *
2161
  * @since 2.2.0
2162
  */
2163
- if ( ! class_exists( 'WP_List_Table' ) ) {
2164
  require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
2165
  }
2166
 
2167
- if ( ! class_exists( 'TGMPA_List_Table' ) ) {
2168
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2169
  /**
2170
- * List table class for handling plugins.
2171
  *
2172
- * Extends the WP_List_Table class to provide a future-compatible
2173
- * way of listing out all required/recommended plugins.
2174
- *
2175
- * Gives users an interface similar to the Plugin Administration
2176
- * area with similar (albeit stripped down) capabilities.
2177
  *
2178
- * This class also allows for the bulk install of plugins.
 
 
 
 
 
2179
  *
2180
- * @since 2.2.0
2181
  *
2182
- * @package TGM-Plugin-Activation
2183
- * @author Thomas Griffin
2184
- * @author Gary Jones
2185
  */
2186
- class TGMPA_List_Table extends \WP_List_Table {
2187
- /**
2188
- * TGMPA instance.
2189
- *
2190
- * @since 2.5.0
2191
- *
2192
- * @var object
2193
- */
2194
- protected $tgmpa;
2195
 
2196
- /**
2197
- * The currently chosen view.
2198
- *
2199
- * @since 2.5.0
2200
- *
2201
- * @var string One of: 'all', 'install', 'update', 'activate'
2202
- */
2203
- public $view_context = 'all';
 
 
 
 
 
2204
 
2205
- /**
2206
- * The plugin counts for the various views.
2207
- *
2208
- * @since 2.5.0
2209
- *
2210
- * @var array
2211
- */
2212
- protected $view_totals = array(
2213
- 'all' => 0,
2214
- 'install' => 0,
2215
- 'update' => 0,
2216
- 'activate' => 0,
 
 
2217
  );
2218
 
2219
- /**
2220
- * References parent constructor and sets defaults for class.
2221
- *
2222
- * @since 2.2.0
2223
- */
2224
- public function __construct() {
2225
- $this->tgmpa = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
2226
-
2227
- parent::__construct(
2228
- array(
2229
- 'singular' => 'plugin',
2230
- 'plural' => 'plugins',
2231
- 'ajax' => false,
2232
- )
2233
- );
2234
-
2235
- if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'install', 'update', 'activate' ), true ) ) {
2236
- $this->view_context = sanitize_key( $_REQUEST['plugin_status'] );
2237
- }
2238
-
2239
- add_filter( 'tgmpa_table_data_items', array( $this, 'sort_table_items' ) );
2240
- }
2241
-
2242
- /**
2243
- * Get a list of CSS classes for the <table> tag.
2244
- *
2245
- * Overruled to prevent the 'plural' argument from being added.
2246
- *
2247
- * @since 2.5.0
2248
- *
2249
- * @return array CSS classnames.
2250
- */
2251
- public function get_table_classes() {
2252
- return array( 'widefat', 'fixed' );
2253
- }
2254
-
2255
- /**
2256
- * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table.
2257
- *
2258
- * @since 2.2.0
2259
- *
2260
- * @return array $table_data Information for use in table.
2261
- */
2262
- protected function _gather_plugin_data() {
2263
- // Load thickbox for plugin links.
2264
- $this->tgmpa->admin_init();
2265
- $this->tgmpa->thickbox();
2266
-
2267
- // Categorize the plugins which have open actions.
2268
- $plugins = $this->categorize_plugins_to_views();
2269
-
2270
- // Set the counts for the view links.
2271
- $this->set_view_totals( $plugins );
2272
-
2273
- // Prep variables for use and grab list of all installed plugins.
2274
- $table_data = array();
2275
- $i = 0;
2276
-
2277
- // Redirect to the 'all' view if no plugins were found for the selected view context.
2278
- if ( empty( $plugins[ $this->view_context ] ) ) {
2279
- $this->view_context = 'all';
2280
- }
2281
-
2282
- foreach ( $plugins[ $this->view_context ] as $slug => $plugin ) {
2283
- $table_data[ $i ]['sanitized_plugin'] = $plugin['name'];
2284
- $table_data[ $i ]['slug'] = $slug;
2285
- $table_data[ $i ]['plugin'] = '<strong>' . $this->tgmpa->get_info_link( $slug ) . '</strong>';
2286
- $table_data[ $i ]['source'] = $this->get_plugin_source_type_text( $plugin['source_type'] );
2287
- $table_data[ $i ]['type'] = $this->get_plugin_advise_type_text( $plugin['required'] );
2288
- $table_data[ $i ]['status'] = $this->get_plugin_status_text( $slug );
2289
- $table_data[ $i ]['installed_version'] = $this->tgmpa->get_installed_version( $slug );
2290
- $table_data[ $i ]['minimum_version'] = $plugin['version'];
2291
- $table_data[ $i ]['available_version'] = $this->tgmpa->does_plugin_have_update( $slug );
2292
-
2293
- // Prep the upgrade notice info.
2294
- $upgrade_notice = $this->tgmpa->get_upgrade_notice( $slug );
2295
- if ( ! empty( $upgrade_notice ) ) {
2296
- $table_data[ $i ]['upgrade_notice'] = $upgrade_notice;
2297
-
2298
- add_action( "tgmpa_after_plugin_row_{$slug}", array( $this, 'wp_plugin_update_row' ), 10, 2 );
2299
- }
2300
-
2301
- $table_data[ $i ] = apply_filters( 'tgmpa_table_data_item', $table_data[ $i ], $plugin );
2302
-
2303
- $i++;
2304
- }
2305
-
2306
- return $table_data;
2307
- }
2308
-
2309
- /**
2310
- * Categorize the plugins which have open actions into views for the TGMPA page.
2311
- *
2312
- * @since 2.5.0
2313
- */
2314
- protected function categorize_plugins_to_views() {
2315
- $plugins = array(
2316
- 'all' => array(), // Meaning: all plugins which still have open actions.
2317
- 'install' => array(),
2318
- 'update' => array(),
2319
- 'activate' => array(),
2320
- );
2321
-
2322
- foreach ( $this->tgmpa->plugins as $slug => $plugin ) {
2323
- if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2324
- // No need to display plugins if they are installed, up-to-date and active.
2325
- continue;
2326
- } else {
2327
- $plugins['all'][ $slug ] = $plugin;
2328
-
2329
- if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2330
- $plugins['install'][ $slug ] = $plugin;
2331
- } else {
2332
- if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2333
- $plugins['update'][ $slug ] = $plugin;
2334
- }
2335
-
2336
- if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2337
- $plugins['activate'][ $slug ] = $plugin;
2338
- }
2339
- }
2340
- }
2341
- }
2342
-
2343
- return $plugins;
2344
- }
2345
-
2346
- /**
2347
- * Set the counts for the view links.
2348
- *
2349
- * @since 2.5.0
2350
- *
2351
- * @param array $plugins Plugins order by view.
2352
- */
2353
- protected function set_view_totals( $plugins ) {
2354
- foreach ( $plugins as $type => $list ) {
2355
- $this->view_totals[ $type ] = count( $list );
2356
- }
2357
  }
2358
 
2359
- /**
2360
- * Get the plugin required/recommended text string.
2361
- *
2362
- * @since 2.5.0
2363
- *
2364
- * @param string $required Plugin required setting.
2365
- * @return string
2366
- */
2367
- protected function get_plugin_advise_type_text( $required ) {
2368
- if ( true === $required ) {
2369
- return __( 'Required', 'tgmpa' );
2370
- }
2371
-
2372
- return __( 'Recommended', 'tgmpa' );
2373
- }
2374
 
2375
- /**
2376
- * Get the plugin source type text string.
2377
- *
2378
- * @since 2.5.0
2379
- *
2380
- * @param string $type Plugin type.
2381
- * @return string
2382
- */
2383
- protected function get_plugin_source_type_text( $type ) {
2384
- $string = '';
 
 
2385
 
2386
- switch ( $type ) {
2387
- case 'repo':
2388
- $string = __( 'WordPress Repository', 'tgmpa' );
2389
- break;
2390
- case 'external':
2391
- $string = __( 'External Source', 'tgmpa' );
2392
- break;
2393
- case 'bundled':
2394
- $string = __( 'Pre-Packaged', 'tgmpa' );
2395
- break;
2396
- }
2397
 
2398
- return $string;
2399
- }
2400
 
2401
- /**
2402
- * Determine the plugin status message.
2403
- *
2404
- * @since 2.5.0
2405
- *
2406
- * @param string $slug Plugin slug.
2407
- * @return string
2408
- */
2409
- protected function get_plugin_status_text( $slug ) {
2410
- if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2411
- return __( 'Not Installed', 'tgmpa' );
2412
- }
2413
 
2414
- if ( ! $this->tgmpa->is_plugin_active( $slug ) ) {
2415
- $install_status = __( 'Installed But Not Activated', 'tgmpa' );
2416
- } else {
2417
- $install_status = __( 'Active', 'tgmpa' );
2418
- }
2419
 
2420
- $update_status = '';
 
 
 
2421
 
2422
- if ( $this->tgmpa->does_plugin_require_update( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2423
- $update_status = __( 'Required Update not Available', 'tgmpa' );
 
 
 
 
 
 
 
 
2424
 
2425
- } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) {
2426
- $update_status = __( 'Requires Update', 'tgmpa' );
 
 
2427
 
2428
- } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2429
- $update_status = __( 'Update recommended', 'tgmpa' );
2430
  }
2431
 
2432
- if ( '' === $update_status ) {
2433
- return $install_status;
2434
- }
2435
 
2436
- return sprintf(
2437
- /* translators: 1: install status, 2: update status */
2438
- _x( '%1$s, %2$s', 'Install/Update Status', 'tgmpa' ),
2439
- $install_status,
2440
- $update_status
2441
- );
2442
  }
2443
 
2444
- /**
2445
- * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type.
2446
- *
2447
- * @since 2.5.0
2448
- *
2449
- * @param array $items Prepared table items.
2450
- * @return array Sorted table items.
2451
- */
2452
- public function sort_table_items( $items ) {
2453
- $type = array();
2454
- $name = array();
2455
 
2456
- foreach ( $items as $i => $plugin ) {
2457
- $type[ $i ] = $plugin['type']; // Required / recommended.
2458
- $name[ $i ] = $plugin['sanitized_plugin'];
2459
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2460
 
2461
- array_multisort( $type, SORT_DESC, $name, SORT_ASC, $items );
 
 
 
 
 
2462
 
2463
- return $items;
 
 
 
 
2464
  }
2465
 
2466
- /**
2467
- * Get an associative array ( id => link ) of the views available on this table.
2468
- *
2469
- * @since 2.5.0
2470
- *
2471
- * @return array
2472
- */
2473
- public function get_views() {
2474
- $status_links = array();
2475
 
2476
- foreach ( $this->view_totals as $type => $count ) {
2477
- if ( $count < 1 ) {
2478
- continue;
2479
- }
 
 
 
 
 
 
 
 
2480
 
2481
- switch ( $type ) {
2482
- case 'all':
2483
- /* translators: 1: number of plugins. */
2484
- $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins', 'tgmpa' );
2485
- break;
2486
- case 'install':
2487
- /* translators: 1: number of plugins. */
2488
- $text = _n( 'To Install <span class="count">(%s)</span>', 'To Install <span class="count">(%s)</span>', $count, 'tgmpa' );
2489
- break;
2490
- case 'update':
2491
- /* translators: 1: number of plugins. */
2492
- $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count, 'tgmpa' );
2493
- break;
2494
- case 'activate':
2495
- /* translators: 1: number of plugins. */
2496
- $text = _n( 'To Activate <span class="count">(%s)</span>', 'To Activate <span class="count">(%s)</span>', $count, 'tgmpa' );
2497
- break;
2498
- default:
2499
- $text = '';
2500
- break;
2501
- }
2502
 
2503
- if ( ! empty( $text ) ) {
 
2504
 
2505
- $status_links[ $type ] = sprintf(
2506
- '<a href="%s"%s>%s</a>',
2507
- esc_url( $this->tgmpa->get_tgmpa_status_url( $type ) ),
2508
- ( $type === $this->view_context ) ? ' class="current"' : '',
2509
- sprintf( $text, number_format_i18n( $count ) )
2510
- );
2511
- }
2512
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2513
 
2514
- return $status_links;
 
 
 
 
 
 
 
 
 
 
2515
  }
2516
 
2517
- /**
2518
- * Create default columns to display important plugin information
2519
- * like type, action and status.
2520
- *
2521
- * @since 2.2.0
2522
- *
2523
- * @param array $item Array of item data.
2524
- * @param string $column_name The name of the column.
2525
- * @return string
2526
- */
2527
- public function column_default( $item, $column_name ) {
2528
- return $item[ $column_name ];
2529
  }
2530
 
2531
- /**
2532
- * Required for bulk installing.
2533
- *
2534
- * Adds a checkbox for each plugin.
2535
- *
2536
- * @since 2.2.0
2537
- *
2538
- * @param array $item Array of item data.
2539
- * @return string The input checkbox with all necessary info.
2540
- */
2541
- public function column_cb( $item ) {
2542
- return sprintf(
2543
- '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />',
2544
- esc_attr( $this->_args['singular'] ),
2545
- esc_attr( $item['slug'] ),
2546
- esc_attr( $item['sanitized_plugin'] )
2547
- );
2548
  }
2549
 
2550
- /**
2551
- * Create default title column along with the action links.
2552
- *
2553
- * @since 2.2.0
2554
- *
2555
- * @param array $item Array of item data.
2556
- * @return string The plugin name and action links.
2557
- */
2558
- public function column_plugin( $item ) {
2559
- return sprintf(
2560
- '%1$s %2$s',
2561
- $item['plugin'],
2562
- $this->row_actions( $this->get_row_actions( $item ), true )
2563
- );
2564
  }
2565
 
2566
- /**
2567
- * Create version information column.
2568
- *
2569
- * @since 2.5.0
2570
- *
2571
- * @param array $item Array of item data.
2572
- * @return string HTML-formatted version information.
2573
- */
2574
- public function column_version( $item ) {
2575
- $output = array();
2576
 
2577
- if ( $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2578
- $installed = ! empty( $item['installed_version'] ) ? $item['installed_version'] : _x( 'unknown', 'as in: "version nr unknown"', 'tgmpa' );
 
 
 
 
 
 
 
 
 
2579
 
2580
- $color = '';
2581
- if ( ! empty( $item['minimum_version'] ) && $this->tgmpa->does_plugin_require_update( $item['slug'] ) ) {
2582
- $color = ' color: #ff0000; font-weight: bold;';
2583
- }
2584
 
2585
- $output[] = sprintf(
2586
- '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Installed version:', 'tgmpa' ) . '</p>',
2587
- $color,
2588
- $installed
2589
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
2590
  }
2591
 
2592
- if ( ! empty( $item['minimum_version'] ) ) {
2593
- $output[] = sprintf(
2594
- '<p><span style="min-width: 32px; text-align: right; float: right;">%1$s</span>' . __( 'Minimum required version:', 'tgmpa' ) . '</p>',
2595
- $item['minimum_version']
2596
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2597
  }
2598
 
2599
- if ( ! empty( $item['available_version'] ) ) {
2600
- $color = '';
2601
- if ( ! empty( $item['minimum_version'] ) && version_compare( $item['available_version'], $item['minimum_version'], '>=' ) ) {
2602
- $color = ' color: #71C671; font-weight: bold;';
2603
- }
2604
 
2605
- $output[] = sprintf(
2606
- '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Available version:', 'tgmpa' ) . '</p>',
2607
- $color,
2608
- $item['available_version']
 
2609
  );
2610
  }
 
2611
 
2612
- if ( empty( $output ) ) {
2613
- return '&nbsp;'; // Let's not break the table layout.
2614
- } else {
2615
- return implode( "\n", $output );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2616
  }
2617
- }
2618
 
2619
- /**
2620
- * Sets default message within the plugins table if no plugins
2621
- * are left for interaction.
2622
- *
2623
- * Hides the menu item to prevent the user from clicking and
2624
- * getting a permissions error.
2625
- *
2626
- * @since 2.2.0
2627
- */
2628
- public function no_items() {
2629
- echo esc_html__( 'No plugins to install, update or activate.', 'tgmpa' ) . ' <a href="' . esc_url( self_admin_url() ) . '"> ' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>';
2630
- echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
2631
  }
2632
 
2633
- /**
2634
- * Output all the column information within the table.
2635
- *
2636
- * @since 2.2.0
2637
- *
2638
- * @return array $columns The column names.
2639
- */
2640
- public function get_columns() {
2641
- $columns = array(
2642
- 'cb' => '<input type="checkbox" />',
2643
- 'plugin' => __( 'Plugin', 'tgmpa' ),
2644
- 'source' => __( 'Source', 'tgmpa' ),
2645
- 'type' => __( 'Type', 'tgmpa' ),
2646
  );
 
2647
 
2648
- if ( 'all' === $this->view_context || 'update' === $this->view_context ) {
2649
- $columns['version'] = __( 'Version', 'tgmpa' );
2650
- $columns['status'] = __( 'Status', 'tgmpa' );
 
2651
  }
2652
 
2653
- return apply_filters( 'tgmpa_table_columns', $columns );
 
 
 
 
2654
  }
2655
 
2656
- /**
2657
- * Get name of default primary column
2658
- *
2659
- * @since 2.5.0 / WP 4.3+ compatibility
2660
- * @access protected
2661
- *
2662
- * @return string
2663
- */
2664
- protected function get_default_primary_column_name() {
2665
- return 'plugin';
2666
  }
 
2667
 
2668
- /**
2669
- * Get the name of the primary column.
2670
- *
2671
- * @since 2.5.0 / WP 4.3+ compatibility
2672
- * @access protected
2673
- *
2674
- * @return string The name of the primary column.
2675
- */
2676
- protected function get_primary_column_name() {
2677
- if ( method_exists( 'WP_List_Table', 'get_primary_column_name' ) ) {
2678
- return parent::get_primary_column_name();
2679
- } else {
2680
- return $this->get_default_primary_column_name();
2681
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2682
  }
2683
 
2684
- /**
2685
- * Get the actions which are relevant for a specific plugin row.
2686
- *
2687
- * @since 2.5.0
2688
- *
2689
- * @param array $item Array of item data.
2690
- * @return array Array with relevant action links.
2691
- */
2692
- protected function get_row_actions( $item ) {
2693
- $actions = array();
2694
- $action_links = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2695
 
2696
- // Display the 'Install' action link if the plugin is not yet available.
2697
- if ( ! $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2698
  /* translators: %2$s: plugin name in screen reader markup */
2699
- $actions['install'] = __( 'Install %2$s', 'tgmpa' );
2700
- } else {
2701
- // Display the 'Update' action link if an update is available and WP complies with plugin minimum.
2702
- if ( false !== $this->tgmpa->does_plugin_have_update( $item['slug'] ) && $this->tgmpa->can_plugin_update( $item['slug'] ) ) {
2703
- /* translators: %2$s: plugin name in screen reader markup */
2704
- $actions['update'] = __( 'Update %2$s', 'tgmpa' );
2705
- }
2706
 
2707
- // Display the 'Activate' action link, but only if the plugin meets the minimum version.
2708
- if ( $this->tgmpa->can_plugin_activate( $item['slug'] ) ) {
2709
- /* translators: %2$s: plugin name in screen reader markup */
2710
- $actions['activate'] = __( 'Activate %2$s', 'tgmpa' );
2711
- }
2712
  }
 
2713
 
2714
- // Create the actual links.
2715
- foreach ( $actions as $action => $text ) {
2716
- $nonce_url = wp_nonce_url(
2717
- add_query_arg(
2718
- array(
2719
- 'plugin' => urlencode( $item['slug'] ),
2720
- 'tgmpa-' . $action => $action . '-plugin',
2721
- ),
2722
- $this->tgmpa->get_tgmpa_url()
2723
  ),
2724
- 'tgmpa-' . $action,
2725
- 'tgmpa-nonce'
2726
- );
2727
-
2728
- $action_links[ $action ] = sprintf(
2729
- '<a href="%1$s">' . esc_html( $text ) . '</a>', // $text contains the second placeholder.
2730
- esc_url( $nonce_url ),
2731
- '<span class="screen-reader-text">' . esc_html( $item['sanitized_plugin'] ) . '</span>'
2732
- );
2733
- }
2734
 
2735
- $prefix = ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) ? 'network_admin_' : '';
2736
- return apply_filters( "tgmpa_{$prefix}plugin_action_links", array_filter( $action_links ), $item['slug'], $item, $this->view_context );
 
 
 
2737
  }
2738
 
2739
- /**
2740
- * Generates content for a single row of the table.
2741
- *
2742
- * @since 2.5.0
2743
- *
2744
- * @param object $item The current item.
2745
- */
2746
- public function single_row( $item ) {
2747
- parent::single_row( $item );
2748
 
2749
- /**
2750
- * Fires after each specific row in the TGMPA Plugins list table.
2751
- *
2752
- * The dynamic portion of the hook name, `$item['slug']`, refers to the slug
2753
- * for the plugin.
2754
- *
2755
- * @since 2.5.0
2756
- */
2757
- do_action( "tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context );
2758
- }
2759
 
2760
  /**
2761
- * Show the upgrade notice below a plugin row if there is one.
2762
- *
2763
- * @since 2.5.0
2764
  *
2765
- * @see /wp-admin/includes/update.php
 
2766
  *
2767
- * @param string $slug Plugin slug.
2768
- * @param array $item The information available in this table row.
2769
- * @return null Return early if upgrade notice is empty.
2770
  */
2771
- public function wp_plugin_update_row( $slug, $item ) {
2772
- if ( empty( $item['upgrade_notice'] ) ) {
2773
- return;
2774
- }
2775
 
2776
- echo '
2777
- <tr class="plugin-update-tr">
2778
- <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange">
2779
- <div class="update-message">',
2780
- esc_html__( 'Upgrade message from the plugin author:', 'tgmpa' ),
2781
- ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong>
2782
- </div>
2783
- </td>
2784
- </tr>';
 
 
 
 
 
2785
  }
2786
 
2787
- /**
2788
- * Extra controls to be displayed between bulk actions and pagination.
2789
- *
2790
- * @since 2.5.0
2791
- *
2792
- * @param string $which 'top' or 'bottom' table navigation.
2793
- */
2794
- public function extra_tablenav( $which ) {
2795
- if ( 'bottom' === $which ) {
2796
- $this->tgmpa->show_tgmpa_version();
2797
- }
 
 
 
 
 
 
 
 
 
 
2798
  }
 
2799
 
2800
- /**
2801
- * Defines the bulk actions for handling registered plugins.
2802
- *
2803
- * @since 2.2.0
2804
- *
2805
- * @return array $actions The bulk actions for the plugin install table.
2806
- */
2807
- public function get_bulk_actions() {
2808
 
2809
- $actions = array();
2810
 
2811
- if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) {
2812
- if ( current_user_can( 'install_plugins' ) ) {
2813
- $actions['tgmpa-bulk-install'] = __( 'Install', 'tgmpa' );
2814
- }
2815
  }
 
2816
 
2817
- if ( 'install' !== $this->view_context ) {
2818
- if ( current_user_can( 'update_plugins' ) ) {
2819
- $actions['tgmpa-bulk-update'] = __( 'Update', 'tgmpa' );
2820
- }
2821
- if ( current_user_can( 'activate_plugins' ) ) {
2822
- $actions['tgmpa-bulk-activate'] = __( 'Activate', 'tgmpa' );
2823
- }
2824
  }
2825
-
2826
- return $actions;
2827
  }
2828
 
2829
- /**
2830
- * Processes bulk installation and activation actions.
2831
- *
2832
- * The bulk installation process looks for the $_POST information and passes that
2833
- * through if a user has to use WP_Filesystem to enter their credentials.
2834
- *
2835
- * @since 2.2.0
2836
- */
2837
- public function process_bulk_actions() {
2838
- // Bulk installation process.
2839
- if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) {
2840
-
2841
- check_admin_referer( 'bulk-' . $this->_args['plural'] );
2842
 
2843
- $install_type = 'install';
2844
- if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2845
- $install_type = 'update';
2846
- }
 
 
 
 
 
 
 
2847
 
2848
- $plugins_to_install = array();
2849
 
2850
- // Did user actually select any plugins to install/update ?
2851
- if ( empty( $_POST['plugin'] ) ) {
2852
- if ( 'install' === $install_type ) {
2853
- $message = __( 'No plugins were selected to be installed. No action taken.', 'tgmpa' );
2854
- } else {
2855
- $message = __( 'No plugins were selected to be updated. No action taken.', 'tgmpa' );
2856
- }
2857
 
2858
- echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2859
 
2860
- return false;
 
 
 
 
 
2861
  }
2862
 
2863
- if ( is_array( $_POST['plugin'] ) ) {
2864
- $plugins_to_install = (array) $_POST['plugin'];
2865
- } elseif ( is_string( $_POST['plugin'] ) ) {
2866
- // Received via Filesystem page - un-flatten array (WP bug #19643).
2867
- $plugins_to_install = explode( ',', $_POST['plugin'] );
2868
- }
2869
 
2870
- // Sanitize the received input.
2871
- $plugins_to_install = array_map( 'urldecode', $plugins_to_install );
2872
- $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install );
2873
 
2874
- // Validate the received input.
2875
- foreach ( $plugins_to_install as $key => $slug ) {
2876
- // Check if the plugin was registered with TGMPA and remove if not.
2877
- if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) {
2878
- unset( $plugins_to_install[ $key ] );
2879
- continue;
2880
- }
2881
 
2882
- // For install: make sure this is a plugin we *can* install and not one already installed.
2883
- if ( 'install' === $install_type && true === $this->tgmpa->is_plugin_installed( $slug ) ) {
2884
- unset( $plugins_to_install[ $key ] );
2885
- }
2886
 
2887
- // For updates: make sure this is a plugin we *can* update (update available and WP version ok).
2888
- if ( 'update' === $install_type && false === $this->tgmpa->is_plugin_updatetable( $slug ) ) {
2889
- unset( $plugins_to_install[ $key ] );
2890
- }
 
 
2891
  }
2892
 
2893
- // No need to proceed further if we have no plugins to handle.
2894
- if ( empty( $plugins_to_install ) ) {
2895
- if ( 'install' === $install_type ) {
2896
- $message = __( 'No plugins are available to be installed at this time.', 'tgmpa' );
2897
- } else {
2898
- $message = __( 'No plugins are available to be updated at this time.', 'tgmpa' );
2899
- }
2900
 
2901
- echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
 
 
 
 
2902
 
2903
- return false;
 
 
 
 
 
2904
  }
2905
 
2906
- // Pass all necessary information if WP_Filesystem is needed.
2907
- $url = wp_nonce_url(
2908
- $this->tgmpa->get_tgmpa_url(),
2909
- 'bulk-' . $this->_args['plural']
2910
- );
2911
 
2912
- // Give validated data back to $_POST which is the only place the filesystem looks for extra fields.
2913
- $_POST['plugin'] = implode( ',', $plugins_to_install ); // Work around for WP bug #19643.
 
 
 
2914
 
2915
- $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
2916
- $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem.
2917
 
2918
- if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) {
2919
- return true; // Stop the normal page form from displaying, credential request form will be shown.
2920
- }
2921
 
2922
- // Now we have some credentials, setup WP_Filesystem.
2923
- if ( ! WP_Filesystem( $creds ) ) {
2924
- // Our credentials were no good, ask the user for them again.
2925
- request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields );
2926
 
2927
- return true;
2928
- }
 
 
2929
 
2930
- /* If we arrive here, we have the filesystem */
 
2931
 
2932
- // Store all information in arrays since we are processing a bulk installation.
2933
- $names = array();
2934
- $sources = array(); // Needed for installs.
2935
- $file_paths = array(); // Needed for upgrades.
2936
- $to_inject = array(); // Information to inject into the update_plugins transient.
2937
 
2938
- // Prepare the data for validated plugins for the install/upgrade.
2939
- foreach ( $plugins_to_install as $slug ) {
2940
- $name = $this->tgmpa->plugins[ $slug ]['name'];
2941
- $source = $this->tgmpa->get_download_url( $slug );
 
2942
 
2943
- if ( ! empty( $name ) && ! empty( $source ) ) {
2944
- $names[] = $name;
 
 
2945
 
2946
- switch ( $install_type ) {
 
2947
 
2948
- case 'install':
2949
- $sources[] = $source;
2950
- break;
2951
 
2952
- case 'update':
2953
- $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2954
- $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ];
2955
- $to_inject[ $slug ]['source'] = $source;
2956
- break;
2957
- }
 
 
 
2958
  }
2959
  }
2960
- unset( $slug, $name, $source );
2961
-
2962
- // Create a new instance of TGMPA_Bulk_Installer.
2963
- $installer = new TGMPA_Bulk_Installer(
2964
- new TGMPA_Bulk_Installer_Skin(
2965
- array(
2966
- 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),
2967
- 'nonce' => 'bulk-' . $this->_args['plural'],
2968
- 'names' => $names,
2969
- 'install_type' => $install_type,
2970
- )
2971
  )
2972
- );
 
2973
 
2974
- // Wrap the install process with the appropriate HTML.
2975
- echo '<div class="tgmpa">',
2976
- '<h2 style="font-size: 23px; font-weight: 400; line-height: 29px; margin: 0; padding: 9px 15px 4px 0;">', esc_html( get_admin_page_title() ), '</h2>
2977
- <div class="update-php" style="width: 100%; height: 98%; min-height: 850px; padding-top: 1px;">';
2978
 
2979
- // Process the bulk installation submissions.
2980
- add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2981
 
2982
- if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2983
- // Inject our info into the update transient.
2984
- $this->tgmpa->inject_update_info( $to_inject );
2985
 
2986
- $installer->bulk_upgrade( $file_paths );
2987
- } else {
2988
- $installer->bulk_install( $sources );
2989
- }
2990
 
2991
- remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1 );
2992
 
2993
- echo '</div></div>';
2994
 
2995
- return true;
2996
- }
2997
 
2998
- // Bulk activation process.
2999
- if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
3000
- check_admin_referer( 'bulk-' . $this->_args['plural'] );
3001
 
3002
- // Did user actually select any plugins to activate ?
3003
- if ( empty( $_POST['plugin'] ) ) {
3004
- echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'tgmpa' ), '</p></div>';
3005
 
3006
- return false;
3007
- }
3008
 
3009
- // Grab plugin data from $_POST.
3010
- $plugins = array();
3011
- if ( isset( $_POST['plugin'] ) ) {
3012
- $plugins = array_map( 'urldecode', (array) $_POST['plugin'] );
3013
- $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins );
3014
- }
3015
 
3016
- $plugins_to_activate = array();
3017
- $plugin_names = array();
3018
 
3019
- // Grab the file paths for the selected & inactive plugins from the registration array.
3020
- foreach ( $plugins as $slug ) {
3021
- if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
3022
- $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path'];
3023
- $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name'];
3024
- }
3025
  }
3026
- unset( $slug );
 
3027
 
3028
- // Return early if there are no plugins to activate.
3029
- if ( empty( $plugins_to_activate ) ) {
3030
- echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'tgmpa' ), '</p></div>';
3031
 
3032
- return false;
3033
- }
3034
 
3035
- // Now we are good to go - let's start activating plugins.
3036
- $activate = activate_plugins( $plugins_to_activate );
3037
 
3038
- if ( is_wp_error( $activate ) ) {
3039
- echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>';
3040
- } else {
3041
- $count = count( $plugin_names ); // Count so we can use _n function.
3042
- $plugin_names = array_map( array( 'TGMPA_Utils', 'wrap_in_strong' ), $plugin_names );
3043
- $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability.
3044
- $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
3045
-
3046
- printf( // WPCS: xss ok.
3047
- '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',
3048
- esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'tgmpa' ) ),
3049
- $imploded
3050
- );
3051
 
3052
- // Update recently activated plugins option.
3053
- $recent = (array) get_option( 'recently_activated' );
3054
- foreach ( $plugins_to_activate as $plugin => $time ) {
3055
- if ( isset( $recent[ $plugin ] ) ) {
3056
- unset( $recent[ $plugin ] );
3057
- }
3058
  }
3059
- update_option( 'recently_activated', $recent );
3060
  }
3061
-
3062
- unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another.
3063
-
3064
- return true;
3065
  }
3066
 
3067
- return false;
3068
- }
3069
-
3070
- /**
3071
- * Prepares all of our information to be outputted into a usable table.
3072
- *
3073
- * @since 2.2.0
3074
- */
3075
- public function prepare_items() {
3076
- $columns = $this->get_columns(); // Get all necessary column information.
3077
- $hidden = array(); // No columns to hide, but we must set as an array.
3078
- $sortable = array(); // No reason to make sortable columns.
3079
- $primary = $this->get_primary_column_name(); // Column which has the row actions.
3080
- $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers.
3081
-
3082
- // Process our bulk activations here.
3083
- if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
3084
- $this->process_bulk_actions();
3085
- }
3086
 
3087
- // Store all of our plugin data into $items array so WP_List_Table can use it.
3088
- $this->items = apply_filters( 'tgmpa_table_data_items', $this->_gather_plugin_data() );
3089
  }
3090
 
3091
- /* *********** DEPRECATED METHODS *********** */
 
3092
 
3093
- /**
3094
- * Retrieve plugin data, given the plugin name.
3095
- *
3096
- * @since 2.2.0
3097
- * @deprecated 2.5.0 use {@see TGM_Plugin_Activation::_get_plugin_data_from_name()} instead.
3098
- * @see TGM_Plugin_Activation::_get_plugin_data_from_name()
3099
- *
3100
- * @param string $name Name of the plugin, as it was registered.
3101
- * @param string $data Optional. Array key of plugin data to return. Default is slug.
3102
- * @return string|boolean Plugin slug if found, false otherwise.
3103
- */
3104
- protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
3105
- _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'TGM_Plugin_Activation::_get_plugin_data_from_name()' );
3106
 
3107
- return $this->tgmpa->_get_plugin_data_from_name( $name, $data );
 
 
3108
  }
3109
- }
3110
- }
3111
 
 
 
 
3112
 
3113
- if ( ! class_exists( 'TGM_Bulk_Installer' ) ) {
3114
 
3115
  /**
3116
- * Hack: Prevent TGMPA v2.4.1- bulk installer class from being loaded if 2.4.1- is loaded after 2.5+.
3117
  *
3118
- * @since 2.5.2
 
 
3119
  *
3120
- * {@internal The TGMPA_Bulk_Installer class was originally called TGM_Bulk_Installer.
3121
- * For more information, see that class.}}
 
3122
  */
3123
- class TGM_Bulk_Installer {
 
 
 
3124
  }
3125
  }
3126
 
3127
- if ( ! class_exists( 'TGM_Bulk_Installer_Skin' ) ) {
 
 
 
 
 
 
 
 
 
3128
 
3129
- /**
3130
- * Hack: Prevent TGMPA v2.4.1- bulk installer skin class from being loaded if 2.4.1- is loaded after 2.5+.
3131
- *
3132
- * @since 2.5.2
3133
- *
3134
- * {@internal The TGMPA_Bulk_Installer_Skin class was originally called TGM_Bulk_Installer_Skin.
3135
- * For more information, see that class.}}
3136
- */
3137
- class TGM_Bulk_Installer_Skin {
3138
- }
3139
  }
3140
 
3141
  /**
@@ -3148,710 +3139,688 @@ if ( ! class_exists( 'TGM_Bulk_Installer_Skin' ) ) {
3148
  *
3149
  * @since 2.2.0
3150
  */
3151
- add_action( 'admin_init', 'WPMailSMTP\tgmpa_load_bulk_installer' );
3152
- if ( ! function_exists( 'tgmpa_load_bulk_installer' ) ) {
3153
- /**
3154
- * Load bulk installer
3155
- */
3156
- function tgmpa_load_bulk_installer() {
3157
- // Silently fail if 2.5+ is loaded *after* an older version.
3158
- if ( ! isset( $GLOBALS['wp_mail_smtp_tgmpa'] ) ) {
3159
- return;
3160
- }
3161
 
3162
- // Get TGMPA class instance.
3163
- $tgmpa_instance = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
 
 
 
 
 
 
3164
 
3165
- if ( isset( $_GET['page'] ) && $tgmpa_instance->menu === $_GET['page'] ) {
3166
- if ( ! class_exists( '\Plugin_Upgrader', false ) ) {
3167
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3168
- }
 
 
 
3169
 
3170
- if ( ! class_exists( 'TGMPA_Bulk_Installer' ) ) {
3171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3172
  /**
3173
- * Installer class to handle bulk plugin installations.
3174
- *
3175
- * Extends WP_Upgrader and customizes to suit the installation of multiple
3176
- * plugins.
3177
  *
3178
  * @since 2.2.0
3179
  *
3180
- * {@internal Since 2.5.0 the class is an extension of Plugin_Upgrader rather than WP_Upgrader.}}
3181
- * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer to TGMPA_Bulk_Installer.
3182
- * This was done to prevent backward compatibility issues with v2.3.6.}}
3183
- *
3184
- * @package TGM-Plugin-Activation
3185
- * @author Thomas Griffin
3186
- * @author Gary Jones
3187
  */
3188
- class TGMPA_Bulk_Installer extends \Plugin_Upgrader {
3189
- /**
3190
- * Holds result of bulk plugin installation.
3191
- *
3192
- * @since 2.2.0
3193
- *
3194
- * @var string
3195
- */
3196
- public $result;
3197
-
3198
- /**
3199
- * Flag to check if bulk installation is occurring or not.
3200
- *
3201
- * @since 2.2.0
3202
- *
3203
- * @var boolean
3204
- */
3205
- public $bulk = false;
3206
-
3207
- /**
3208
- * TGMPA instance
3209
- *
3210
- * @since 2.5.0
3211
- *
3212
- * @var object
3213
- */
3214
- protected $tgmpa;
3215
 
3216
- /**
3217
- * Whether or not the destination directory needs to be cleared ( = on update).
3218
- *
3219
- * @since 2.5.0
3220
- *
3221
- * @var bool
3222
- */
3223
- protected $clear_destination = false;
3224
 
3225
- /**
3226
- * References parent constructor and sets defaults for class.
3227
- *
3228
- * @since 2.2.0
3229
- *
3230
- * @param \Bulk_Upgrader_Skin|null $skin Installer skin.
3231
- */
3232
- public function __construct( $skin = null ) {
3233
- // Get TGMPA class instance.
3234
- $this->tgmpa = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
3235
 
3236
- parent::__construct( $skin );
 
 
 
 
 
 
 
3237
 
3238
- if ( isset( $this->skin->options['install_type'] ) && 'update' === $this->skin->options['install_type'] ) {
3239
- $this->clear_destination = true;
3240
- }
 
 
 
 
 
 
 
3241
 
3242
- if ( $this->tgmpa->is_automatic ) {
3243
- $this->activate_strings();
3244
- }
3245
 
3246
- add_action( 'upgrader_process_complete', array( $this->tgmpa, 'populate_file_path' ) );
 
3247
  }
3248
 
3249
- /**
3250
- * Sets the correct activation strings for the installer skin to use.
3251
- *
3252
- * @since 2.2.0
3253
- */
3254
- public function activate_strings() {
3255
- $this->strings['activation_failed'] = __( 'Plugin activation failed.', 'tgmpa' );
3256
- $this->strings['activation_success'] = __( 'Plugin activated successfully.', 'tgmpa' );
3257
  }
3258
 
3259
- /**
3260
- * Performs the actual installation of each plugin.
3261
- *
3262
- * @since 2.2.0
3263
- *
3264
- * @see WP_Upgrader::run()
3265
- *
3266
- * @param array $options The installation config options.
3267
- * @return null|array Return early if error, array of installation data on success.
3268
- */
3269
- public function run( $options ) {
3270
- $result = parent::run( $options );
3271
 
3272
- // Reset the strings in case we changed one during automatic activation.
3273
- if ( $this->tgmpa->is_automatic ) {
3274
- if ( 'update' === $this->skin->options['install_type'] ) {
3275
- $this->upgrade_strings();
3276
- } else {
3277
- $this->install_strings();
3278
- }
3279
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3280
 
3281
- return $result;
 
 
 
 
 
 
3282
  }
3283
 
3284
- /**
3285
- * Processes the bulk installation of plugins.
3286
- *
3287
- * @since 2.2.0
3288
- *
3289
- * {@internal This is basically a near identical copy of the WP Core
3290
- * Plugin_Upgrader::bulk_upgrade() method, with minor adjustments to deal with
3291
- * new installs instead of upgrades.
3292
- * For ease of future synchronizations, the adjustments are clearly commented, but no other
3293
- * comments are added. Code style has been made to comply.}}
3294
- *
3295
- * @see Plugin_Upgrader::bulk_upgrade()
3296
- * @see https://core.trac.wordpress.org/browser/tags/4.2.1/src/wp-admin/includes/class-wp-upgrader.php#L838
3297
- * (@internal Last synced: Dec 31st 2015 against https://core.trac.wordpress.org/browser/trunk?rev=36134}}
3298
- *
3299
- * @param array $plugins The plugin sources needed for installation.
3300
- * @param array $args Arbitrary passed extra arguments.
3301
- * @return array|false Install confirmation messages on success, false on failure.
3302
- */
3303
- public function bulk_install( $plugins, $args = array() ) {
3304
- // [TGMPA + ] Hook auto-activation in.
3305
- add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3306
 
3307
- $defaults = array(
3308
- 'clear_update_cache' => true,
3309
- );
3310
- $parsed_args = wp_parse_args( $args, $defaults );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3311
 
3312
- $this->init();
3313
- $this->bulk = true;
3314
 
3315
- $this->install_strings(); // [TGMPA + ] adjusted.
3316
 
3317
- /* [TGMPA - ] $current = get_site_transient( 'update_plugins' ); */
3318
 
3319
- /* [TGMPA - ] add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); */
3320
 
3321
- $this->skin->header();
3322
 
3323
- // Connect to the Filesystem first.
3324
- $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
3325
- if ( ! $res ) {
3326
- $this->skin->footer();
3327
- return false;
3328
- }
3329
 
3330
- $this->skin->bulk_header();
3331
-
3332
- /*
3333
- * Only start maintenance mode if:
3334
- * - running Multisite and there are one or more plugins specified, OR
3335
- * - a plugin with an update available is currently active.
3336
- * @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.
3337
- */
3338
- $maintenance = ( is_multisite() && ! empty( $plugins ) );
3339
-
3340
- /*
3341
- [TGMPA - ]
3342
- foreach ( $plugins as $plugin )
3343
- $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) );
3344
- */
3345
- if ( $maintenance ) {
3346
- $this->maintenance_mode( true );
3347
- }
3348
 
3349
- $results = array();
3350
-
3351
- $this->update_count = count( $plugins );
3352
- $this->update_current = 0;
3353
- foreach ( $plugins as $plugin ) {
3354
- $this->update_current++;
3355
-
3356
- /*
3357
- [TGMPA - ]
3358
- $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);
3359
-
3360
- if ( !isset( $current->response[ $plugin ] ) ) {
3361
- $this->skin->set_result('up_to_date');
3362
- $this->skin->before();
3363
- $this->skin->feedback('up_to_date');
3364
- $this->skin->after();
3365
- $results[$plugin] = true;
3366
- continue;
3367
- }
3368
-
3369
- // Get the URL to the zip file.
3370
- $r = $current->response[ $plugin ];
3371
-
3372
- $this->skin->plugin_active = is_plugin_active($plugin);
3373
- */
3374
-
3375
- $result = $this->run(
3376
- array(
3377
- 'package' => $plugin, // [TGMPA + ] adjusted.
3378
- 'destination' => WP_PLUGIN_DIR,
3379
- 'clear_destination' => false, // [TGMPA + ] adjusted.
3380
- 'clear_working' => true,
3381
- 'is_multi' => true,
3382
- 'hook_extra' => array(
3383
- 'plugin' => $plugin,
3384
- ),
3385
- )
3386
- );
3387
-
3388
- $results[ $plugin ] = $this->result;
3389
-
3390
- // Prevent credentials auth screen from displaying multiple times.
3391
- if ( false === $result ) {
3392
- break;
3393
- }
3394
- } //end foreach $plugins
3395
-
3396
- $this->maintenance_mode( false );
3397
-
3398
- /**
3399
- * Fires when the bulk upgrader process is complete.
3400
- *
3401
- * @since WP 3.6.0 / TGMPA 2.5.0
3402
- *
3403
- * @param \Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might
3404
- * be a Theme_Upgrader or Core_Upgrade instance.
3405
- * @param array $data {
3406
- * Array of bulk item update data.
3407
- *
3408
- * @type string $action Type of action. Default 'update'.
3409
- * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
3410
- * @type bool $bulk Whether the update process is a bulk update. Default true.
3411
- * @type array $packages Array of plugin, theme, or core packages to update.
3412
- * }
3413
- */
3414
- do_action( 'upgrader_process_complete', $this, array(
3415
- 'action' => 'install', // [TGMPA + ] adjusted.
3416
- 'type' => 'plugin',
3417
- 'bulk' => true,
3418
- 'plugins' => $plugins,
3419
- ) );
3420
-
3421
- $this->skin->bulk_footer();
3422
 
3423
- $this->skin->footer();
 
 
 
 
 
 
 
3424
 
3425
- // Cleanup our hooks, in case something else does a upgrade on this connection.
3426
- /* [TGMPA - ] remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3427
 
3428
- // [TGMPA + ] Remove our auto-activation hook.
3429
- remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3430
 
3431
- // Force refresh of plugin update information.
3432
- wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
 
 
 
3433
 
3434
- return $results;
3435
- }
3436
 
3437
  /**
3438
- * Handle a bulk upgrade request.
3439
  *
3440
- * @since 2.5.0
3441
  *
3442
- * @see Plugin_Upgrader::bulk_upgrade()
 
 
 
3443
  *
3444
- * @param array $plugins The local WP file_path's of the plugins which should be upgraded.
3445
- * @param array $args Arbitrary passed extra arguments.
3446
- * @return string|bool Install confirmation messages on success, false on failure.
 
 
3447
  */
3448
- public function bulk_upgrade( $plugins, $args = array() ) {
 
 
 
 
 
3449
 
3450
- add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3451
 
3452
- $result = parent::bulk_upgrade( $plugins, $args );
3453
 
3454
- remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
 
3455
 
3456
- return $result;
3457
- }
3458
 
3459
- /**
3460
- * Abuse a filter to auto-activate plugins after installation.
3461
- *
3462
- * Hooked into the 'upgrader_post_install' filter hook.
3463
- *
3464
- * @since 2.5.0
3465
- *
3466
- * @param bool $bool The value we need to give back (true).
3467
- * @return bool
3468
- */
3469
- public function auto_activate( $bool ) {
3470
- // Only process the activation of installed plugins if the automatic flag is set to true.
3471
- if ( $this->tgmpa->is_automatic ) {
3472
- // Flush plugins cache so the headers of the newly installed plugins will be read correctly.
3473
- wp_clean_plugins_cache();
 
 
 
 
 
3474
 
3475
- // Get the installed plugin file.
3476
- $plugin_info = $this->plugin_info();
3477
 
3478
- // Don't try to activate on upgrade of active plugin as WP will do this already.
3479
- if ( ! is_plugin_active( $plugin_info ) ) {
3480
- $activate = activate_plugin( $plugin_info );
3481
 
3482
- // Adjust the success string based on the activation result.
3483
- $this->strings['process_success'] = $this->strings['process_success'] . "<br />\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3484
 
3485
- if ( is_wp_error( $activate ) ) {
3486
- $this->skin->error( $activate );
3487
- $this->strings['process_success'] .= $this->strings['activation_failed'];
3488
- } else {
3489
- $this->strings['process_success'] .= $this->strings['activation_success'];
3490
- }
 
 
 
 
 
 
 
 
 
3491
  }
3492
  }
3493
-
3494
- return $bool;
3495
  }
 
 
3496
  }
3497
  }
 
3498
 
3499
- if ( ! class_exists( 'TGMPA_Bulk_Installer_Skin' ) ) {
3500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3501
  /**
3502
- * Installer skin to set strings for the bulk plugin installations..
3503
- *
3504
- * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple
3505
- * plugins.
3506
  *
3507
  * @since 2.2.0
3508
  *
3509
- * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer_Skin to
3510
- * TGMPA_Bulk_Installer_Skin.
3511
- * This was done to prevent backward compatibility issues with v2.3.6.}}
 
 
 
3512
  *
3513
- * @see https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
3514
  *
3515
- * @package TGM-Plugin-Activation
3516
- * @author Thomas Griffin
3517
- * @author Gary Jones
3518
  */
3519
- class TGMPA_Bulk_Installer_Skin extends \Bulk_Upgrader_Skin {
3520
- /**
3521
- * Holds plugin info for each individual plugin installation.
3522
- *
3523
- * @since 2.2.0
3524
- *
3525
- * @var array
3526
- */
3527
- public $plugin_info = array();
3528
 
3529
- /**
3530
- * Holds names of plugins that are undergoing bulk installations.
3531
- *
3532
- * @since 2.2.0
3533
- *
3534
- * @var array
3535
- */
3536
- public $plugin_names = array();
3537
 
3538
- /**
3539
- * Integer to use for iteration through each plugin installation.
3540
- *
3541
- * @since 2.2.0
3542
- *
3543
- * @var integer
3544
- */
3545
- public $i = 0;
3546
 
3547
- /**
3548
- * TGMPA instance
3549
- *
3550
- * @since 2.5.0
3551
- *
3552
- * @var object
3553
- */
3554
- protected $tgmpa;
 
 
 
 
 
 
 
 
 
 
 
3555
 
3556
- /**
3557
- * Constructor. Parses default args with new ones and extracts them for use.
3558
- *
3559
- * @since 2.2.0
3560
- *
3561
- * @param array $args Arguments to pass for use within the class.
3562
- */
3563
- public function __construct( $args = array() ) {
3564
- // Get TGMPA class instance.
3565
- $this->tgmpa = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
3566
-
3567
- // Parse default and new args.
3568
- $defaults = array(
3569
- 'url' => '',
3570
- 'nonce' => '',
3571
- 'names' => array(),
3572
- 'install_type' => 'install',
3573
- );
3574
- $args = wp_parse_args( $args, $defaults );
3575
 
3576
- // Set plugin names to $this->plugin_names property.
3577
- $this->plugin_names = $args['names'];
 
3578
 
3579
- // Extract the new args.
3580
- parent::__construct( $args );
3581
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3582
 
3583
- /**
3584
- * Sets install skin strings for each individual plugin.
3585
- *
3586
- * Checks to see if the automatic activation flag is set and uses the
3587
- * the proper strings accordingly.
3588
- *
3589
- * @since 2.2.0
3590
- */
3591
- public function add_strings() {
3592
- if ( 'update' === $this->options['install_type'] ) {
3593
- parent::add_strings();
3594
  /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3595
- $this->upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3596
  } else {
3597
- /* translators: 1: plugin name, 2: error message. */
3598
- $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while installing %1$s: <strong>%2$s</strong>.', 'tgmpa' );
3599
  /* translators: 1: plugin name. */
3600
- $this->upgrader->strings['skin_update_failed'] = __( 'The installation of %1$s failed.', 'tgmpa' );
3601
-
3602
- if ( $this->tgmpa->is_automatic ) {
3603
- // Automatic activation strings.
3604
- $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3605
- /* translators: 1: plugin name. */
3606
- $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed and activated successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3607
- $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations and activations have been completed.', 'tgmpa' );
3608
- /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3609
- $this->upgrader->strings['skin_before_update_header'] = __( 'Installing and Activating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3610
- } else {
3611
- // Default installation strings.
3612
- $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3613
- /* translators: 1: plugin name. */
3614
- $this->upgrader->strings['skin_update_successful'] = esc_html__( '%1$s installed successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3615
- $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations have been completed.', 'tgmpa' );
3616
- /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3617
- $this->upgrader->strings['skin_before_update_header'] = __( 'Installing Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3618
- }
3619
  }
3620
  }
 
3621
 
3622
- /**
3623
- * Outputs the header strings and necessary JS before each plugin installation.
3624
- *
3625
- * @since 2.2.0
3626
- *
3627
- * @param string $title Unused in this implementation.
3628
- */
3629
- public function before( $title = '' ) {
3630
- if ( empty( $title ) ) {
3631
- $title = esc_html( $this->plugin_names[ $this->i ] );
3632
- }
3633
- parent::before( $title );
3634
  }
 
 
3635
 
3636
- /**
3637
- * Outputs the footer strings and necessary JS after each plugin installation.
3638
- *
3639
- * Checks for any errors and outputs them if they exist, else output
3640
- * success strings.
3641
- *
3642
- * @since 2.2.0
3643
- *
3644
- * @param string $title Unused in this implementation.
3645
- */
3646
- public function after( $title = '' ) {
3647
- if ( empty( $title ) ) {
3648
- $title = esc_html( $this->plugin_names[ $this->i ] );
3649
- }
3650
- parent::after( $title );
3651
-
3652
- $this->i++;
3653
  }
 
3654
 
3655
- /**
3656
- * Outputs links after bulk plugin installation is complete.
3657
- *
3658
- * @since 2.2.0
3659
- */
3660
- public function bulk_footer() {
3661
- // Serve up the string to say installations (and possibly activations) are complete.
3662
- parent::bulk_footer();
3663
 
3664
- // Flush plugins cache so we can make sure that the installed plugins list is always up to date.
3665
- wp_clean_plugins_cache();
 
 
 
 
 
 
3666
 
3667
- $this->tgmpa->show_tgmpa_version();
 
3668
 
3669
- // Display message based on if all plugins are now active or not.
3670
- $update_actions = array();
3671
 
3672
- if ( $this->tgmpa->is_tgmpa_complete() ) {
3673
- // All plugins are active, so we display the complete string and hide the menu to protect users.
3674
- echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
3675
- $update_actions['dashboard'] = sprintf(
3676
- esc_html( $this->tgmpa->strings['complete'] ),
3677
- '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>'
3678
- );
3679
- } else {
3680
- $update_actions['tgmpa_page'] = '<a href="' . esc_url( $this->tgmpa->get_tgmpa_url() ) . '" target="_parent">' . esc_html( $this->tgmpa->strings['return'] ) . '</a>';
3681
- }
3682
 
3683
- /**
3684
- * Filter the list of action links available following bulk plugin installs/updates.
3685
- *
3686
- * @since 2.5.0
3687
- *
3688
- * @param array $update_actions Array of plugin action links.
3689
- * @param array $plugin_info Array of information for the last-handled plugin.
3690
- */
3691
- $update_actions = apply_filters( 'tgmpa_update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );
3692
-
3693
- if ( ! empty( $update_actions ) ) {
3694
- $this->feedback( implode( ' | ', (array) $update_actions ) );
3695
- }
3696
  }
3697
 
3698
- /* *********** DEPRECATED METHODS *********** */
3699
-
3700
  /**
3701
- * Flush header output buffer.
3702
  *
3703
- * @since 2.2.0
3704
- * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3705
- * @see Bulk_Upgrader_Skin::flush_output()
3706
- */
3707
- public function before_flush_output() {
3708
- _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3709
- $this->flush_output();
3710
- }
3711
-
3712
- /**
3713
- * Flush footer output buffer and iterate $this->i to make sure the
3714
- * installation strings reference the correct plugin.
3715
  *
3716
- * @since 2.2.0
3717
- * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3718
- * @see Bulk_Upgrader_Skin::flush_output()
3719
  */
3720
- public function after_flush_output() {
3721
- _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3722
- $this->flush_output();
3723
- $this->i++;
3724
  }
3725
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3726
  }
3727
  }
3728
  }
3729
  }
3730
 
3731
- if ( ! class_exists( 'TGMPA_Utils' ) ) {
3732
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3733
  /**
3734
- * Generic utilities for TGMPA.
3735
  *
3736
- * All methods are static, poor-dev name-spacing class wrapper.
3737
  *
3738
- * Class was called TGM_Utils in 2.5.0 but renamed TGMPA_Utils in 2.5.1 as this was conflicting with Soliloquy.
 
 
 
 
 
 
 
 
 
3739
  *
3740
  * @since 2.5.0
3741
  *
3742
- * @package TGM-Plugin-Activation
3743
- * @author Juliette Reinders Folmer
 
 
3744
  */
3745
- class TGMPA_Utils {
3746
- /**
3747
- * Whether the PHP filter extension is enabled.
3748
- *
3749
- * @see http://php.net/book.filter
3750
- *
3751
- * @since 2.5.0
3752
- *
3753
- * @static
3754
- *
3755
- * @var bool $has_filters True is the extension is enabled.
3756
- */
3757
- public static $has_filters;
3758
 
3759
- /**
3760
- * Wrap an arbitrary string in <em> tags. Meant to be used in combination with array_map().
3761
- *
3762
- * @since 2.5.0
3763
- *
3764
- * @static
3765
- *
3766
- * @param string $string Text to be wrapped.
3767
- * @return string
3768
- */
3769
- public static function wrap_in_em( $string ) {
3770
- return '<em>' . wp_kses_post( $string ) . '</em>';
3771
- }
3772
 
3773
- /**
3774
- * Wrap an arbitrary string in <strong> tags. Meant to be used in combination with array_map().
3775
- *
3776
- * @since 2.5.0
3777
- *
3778
- * @static
3779
- *
3780
- * @param string $string Text to be wrapped.
3781
- * @return string
3782
- */
3783
- public static function wrap_in_strong( $string ) {
3784
- return '<strong>' . wp_kses_post( $string ) . '</strong>';
 
3785
  }
3786
 
3787
- /**
3788
- * Helper function: Validate a value as boolean
3789
- *
3790
- * @since 2.5.0
3791
- *
3792
- * @static
3793
- *
3794
- * @param mixed $value Arbitrary value.
3795
- * @return bool
3796
- */
3797
- public static function validate_bool( $value ) {
3798
- if ( ! isset( self::$has_filters ) ) {
3799
- self::$has_filters = extension_loaded( 'filter' );
3800
- }
3801
 
3802
- if ( self::$has_filters ) {
3803
- return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3804
  } else {
3805
- return self::emulate_filter_bool( $value );
3806
  }
3807
  }
3808
 
3809
- /**
3810
- * Helper function: Cast a value to bool
3811
- *
3812
- * @since 2.5.0
3813
- *
3814
- * @static
3815
- *
3816
- * @param mixed $value Value to cast.
3817
- * @return bool
3818
- */
3819
- protected static function emulate_filter_bool( $value ) {
3820
- // @codingStandardsIgnoreStart
3821
- static $true = array(
3822
- '1',
3823
- 'true', 'True', 'TRUE',
3824
- 'y', 'Y',
3825
- 'yes', 'Yes', 'YES',
3826
- 'on', 'On', 'ON',
3827
- );
3828
- static $false = array(
3829
- '0',
3830
- 'false', 'False', 'FALSE',
3831
- 'n', 'N',
3832
- 'no', 'No', 'NO',
3833
- 'off', 'Off', 'OFF',
3834
- );
3835
- // @codingStandardsIgnoreEnd
3836
-
3837
- if ( is_bool( $value ) ) {
3838
- return $value;
3839
- } elseif ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) {
3840
- return (bool) $value;
3841
- } elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) {
3842
- return (bool) $value;
3843
- } elseif ( is_string( $value ) ) {
3844
- $value = trim( $value );
3845
- if ( in_array( $value, $true, true ) ) {
3846
- return true;
3847
- } elseif ( in_array( $value, $false, true ) ) {
3848
- return false;
3849
- } else {
3850
- return false;
3851
- }
3852
- }
3853
-
3854
- return false;
3855
- }
3856
- } // End of class TGMPA_Utils
3857
- } // End of class_exists wrapper
35
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36
  */
37
 
38
+ /**
39
+ * Automatic plugin installation and activation library.
40
+ *
41
+ * Creates a way to automatically install and activate plugins from within themes.
42
+ * The plugins can be either bundled, downloaded from the WordPress
43
+ * Plugin Repository or downloaded from another external source.
44
+ *
45
+ * @since 1.0.0
46
+ *
47
+ * @package TGM-Plugin-Activation
48
+ * @author Thomas Griffin
49
+ * @author Gary Jones
50
+ */
51
+ class TGM_Plugin_Activation {
52
  /**
53
+ * TGMPA version number.
54
  *
55
+ * @since 2.5.0
 
 
 
 
56
  *
57
+ * @const string Version number.
 
 
58
  */
59
+ const TGMPA_VERSION = '2.6.1';
 
 
 
 
 
 
 
 
60
 
61
+ /**
62
+ * Regular expression to test if a URL is a WP plugin repo URL.
63
+ *
64
+ * @const string Regex.
65
+ *
66
+ * @since 2.5.0
67
+ */
68
+ const WP_REPO_REGEX = '|^http[s]?://wordpress\.org/(?:extend/)?plugins/|';
 
 
 
 
 
 
 
 
 
69
 
70
+ /**
71
+ * Arbitrary regular expression to test if a string starts with a URL.
72
+ *
73
+ * @const string Regex.
74
+ *
75
+ * @since 2.5.0
76
+ */
77
+ const IS_URL_REGEX = '|^http[s]?://|';
78
 
79
+ /**
80
+ * Holds a copy of itself, so it can be referenced by the class name.
81
+ *
82
+ * @since 1.0.0
83
+ *
84
+ * @var TGM_Plugin_Activation
85
+ */
86
+ public static $instance;
 
87
 
88
+ /**
89
+ * Holds arrays of plugin details.
90
+ *
91
+ * @since 1.0.0
92
+ * @since 2.5.0 the array has the plugin slug as an associative key.
93
+ *
94
+ * @var array
95
+ */
96
+ public $plugins = array();
97
 
98
+ /**
99
+ * Holds arrays of plugin names to use to sort the plugins array.
100
+ *
101
+ * @since 2.5.0
102
+ *
103
+ * @var array
104
+ */
105
+ protected $sort_order = array();
106
 
107
+ /**
108
+ * Whether any plugins have the 'force_activation' setting set to true.
109
+ *
110
+ * @since 2.5.0
111
+ *
112
+ * @var bool
113
+ */
114
+ protected $has_forced_activation = false;
115
 
116
+ /**
117
+ * Whether any plugins have the 'force_deactivation' setting set to true.
118
+ *
119
+ * @since 2.5.0
120
+ *
121
+ * @var bool
122
+ */
123
+ protected $has_forced_deactivation = false;
124
 
125
+ /**
126
+ * Name of the unique ID to hash notices.
127
+ *
128
+ * @since 2.4.0
129
+ *
130
+ * @var string
131
+ */
132
+ public $id = 'tgmpa';
133
 
134
+ /**
135
+ * Name of the query-string argument for the admin page.
136
+ *
137
+ * @since 1.0.0
138
+ *
139
+ * @var string
140
+ */
141
+ protected $menu = 'tgmpa-install-plugins';
142
 
143
+ /**
144
+ * Parent menu file slug.
145
+ *
146
+ * @since 2.5.0
147
+ *
148
+ * @var string
149
+ */
150
+ public $parent_slug = 'themes.php';
151
 
152
+ /**
153
+ * Capability needed to view the plugin installation menu item.
154
+ *
155
+ * @since 2.5.0
156
+ *
157
+ * @var string
158
+ */
159
+ public $capability = 'edit_theme_options';
160
 
161
+ /**
162
+ * Default absolute path to folder containing bundled plugin zip files.
163
+ *
164
+ * @since 2.0.0
165
+ *
166
+ * @var string Absolute path prefix to zip file location for bundled plugins. Default is empty string.
167
+ */
168
+ public $default_path = '';
169
 
170
+ /**
171
+ * Flag to show admin notices or not.
172
+ *
173
+ * @since 2.1.0
174
+ *
175
+ * @var boolean
176
+ */
177
+ public $has_notices = true;
178
 
179
+ /**
180
+ * Flag to determine if the user can dismiss the notice nag.
181
+ *
182
+ * @since 2.4.0
183
+ *
184
+ * @var boolean
185
+ */
186
+ public $dismissable = true;
187
 
188
+ /**
189
+ * Message to be output above nag notice if dismissable is false.
190
+ *
191
+ * @since 2.4.0
192
+ *
193
+ * @var string
194
+ */
195
+ public $dismiss_msg = '';
196
 
197
+ /**
198
+ * Flag to set automatic activation of plugins. Off by default.
199
+ *
200
+ * @since 2.2.0
201
+ *
202
+ * @var boolean
203
+ */
204
+ public $is_automatic = false;
205
 
206
+ /**
207
+ * Optional message to display before the plugins table.
208
+ *
209
+ * @since 2.2.0
210
+ *
211
+ * @var string Message filtered by wp_kses_post(). Default is empty string.
212
+ */
213
+ public $message = '';
 
 
214
 
215
+ /**
216
+ * Holds configurable array of strings.
217
+ *
218
+ * Default values are added in the constructor.
219
+ *
220
+ * @since 2.0.0
221
+ *
222
+ * @var array
223
+ */
224
+ public $strings = array();
225
 
226
+ /**
227
+ * Holds the version of WordPress.
228
+ *
229
+ * @since 2.4.0
230
+ *
231
+ * @var int
232
+ */
233
+ public $wp_version;
234
 
235
+ /**
236
+ * Holds the hook name for the admin page.
237
+ *
238
+ * @since 2.5.0
239
+ *
240
+ * @var string
241
+ */
242
+ public $page_hook;
 
 
 
 
 
 
 
243
 
244
+ /**
245
+ * Adds a reference of this object to $instance, populates default strings,
246
+ * does the tgmpa_init action hook, and hooks in the interactions to init.
247
+ *
248
+ * {@internal This method should be `protected`, but as too many TGMPA implementations
249
+ * haven't upgraded beyond v2.3.6 yet, this gives backward compatibility issues.
250
+ * Reverted back to public for the time being.}}
251
+ *
252
+ * @since 1.0.0
253
+ *
254
+ * @see TGM_Plugin_Activation::init()
255
+ */
256
+ public function __construct() {
257
+ // Set the current WordPress version.
258
+ $this->wp_version = $GLOBALS['wp_version'];
259
+
260
+ // Announce that the class is ready, and pass the object (for advanced use).
261
+ do_action_ref_array( 'wpms_tgmpa_init', array( $this ) );
262
+
263
+ /*
264
+ * Load our text domain and allow for overloading the fall-back file.
265
+ *
266
+ * {@internal IMPORTANT! If this code changes, review the regex in the custom TGMPA
267
+ * generator on the website.}}
268
+ */
269
+ add_action( 'init', array( $this, 'load_textdomain' ), 5 );
270
+ add_filter( 'load_textdomain_mofile', array( $this, 'overload_textdomain_mofile' ), 10, 2 );
271
+
272
+ // When the rest of WP has loaded, kick-start the rest of the class.
273
+ add_action( 'init', array( $this, 'init' ) );
274
+ }
275
 
276
+ /**
277
+ * Magic method to (not) set protected properties from outside of this class.
278
+ *
279
+ * {@internal hackedihack... There is a serious bug in v2.3.2 - 2.3.6 where the `menu` property
280
+ * is being assigned rather than tested in a conditional, effectively rendering it useless.
281
+ * This 'hack' prevents this from happening.}}
282
+ *
283
+ * @see https://github.com/TGMPA/TGM-Plugin-Activation/blob/2.3.6/tgm-plugin-activation/class-tgm-plugin-activation.php#L1593
284
+ *
285
+ * @since 2.5.2
286
+ *
287
+ * @param string $name Name of an inaccessible property.
288
+ * @param mixed $value Value to assign to the property.
289
+ * @return void Silently fail to set the property when this is tried from outside of this class context.
290
+ * (Inside this class context, the __set() method if not used as there is direct access.)
291
+ */
292
+ public function __set( $name, $value ) {
293
+ return;
294
+ }
295
 
296
+ /**
297
+ * Magic method to get the value of a protected property outside of this class context.
298
+ *
299
+ * @since 2.5.2
300
+ *
301
+ * @param string $name Name of an inaccessible property.
302
+ * @return mixed The property value.
303
+ */
304
+ public function __get( $name ) {
305
+ return $this->{$name};
306
+ }
307
 
308
+ /**
309
+ * Initialise the interactions between this class and WordPress.
310
+ *
311
+ * Hooks in three new methods for the class: admin_menu, notices and styles.
312
+ *
313
+ * @since 2.0.0
314
+ *
315
+ * @see TGM_Plugin_Activation::admin_menu()
316
+ * @see TGM_Plugin_Activation::notices()
317
+ * @see TGM_Plugin_Activation::styles()
318
+ */
319
+ public function init() {
320
  /**
321
+ * By default TGMPA only loads on the WP back-end and not in an Ajax call. Using this filter
322
+ * you can overrule that behaviour.
 
 
 
 
 
323
  *
324
+ * @since 2.5.0
325
  *
326
+ * @param bool $load Whether or not TGMPA should load.
327
+ * Defaults to the return of `is_admin() && ! defined( 'DOING_AJAX' )`.
 
 
328
  */
329
+ if ( true !== apply_filters( 'wpms_tgmpa_load', ( is_admin() && ! defined( 'DOING_AJAX' ) ) ) ) {
330
  return;
331
  }
332
 
333
+ // Load class strings.
334
+ $this->strings = array(
335
+ 'page_title' => __( 'Install Required Plugins', 'tgmpa' ),
336
+ 'menu_title' => __( 'Install Plugins', 'tgmpa' ),
337
+ /* translators: %s: plugin name. */
338
+ 'installing' => __( 'Installing Plugin: %s', 'tgmpa' ),
339
+ /* translators: %s: plugin name. */
340
+ 'updating' => __( 'Updating Plugin: %s', 'tgmpa' ),
341
+ 'oops' => __( 'Something went wrong with the plugin API.', 'tgmpa' ),
342
+ 'notice_can_install_required' => _n_noop(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  /* translators: 1: plugin name(s). */
344
+ 'This theme requires the following plugin: %1$s.',
345
+ 'This theme requires the following plugins: %1$s.',
346
+ 'tgmpa'
347
+ ),
348
+ 'notice_can_install_recommended' => _n_noop(
349
  /* translators: 1: plugin name(s). */
350
+ 'This theme recommends the following plugin: %1$s.',
351
+ 'This theme recommends the following plugins: %1$s.',
352
+ 'tgmpa'
353
+ ),
354
+ 'notice_ask_to_update' => _n_noop(
355
  /* translators: 1: plugin name(s). */
356
+ 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.',
357
+ 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.',
358
+ 'tgmpa'
359
+ ),
360
+ 'notice_ask_to_update_maybe' => _n_noop(
361
  /* translators: 1: plugin name(s). */
362
+ 'There is an update available for: %1$s.',
363
+ 'There are updates available for the following plugins: %1$s.',
364
+ 'tgmpa'
365
+ ),
366
+ 'notice_can_activate_required' => _n_noop(
367
  /* translators: 1: plugin name(s). */
368
+ 'The following required plugin is currently inactive: %1$s.',
369
+ 'The following required plugins are currently inactive: %1$s.',
370
+ 'tgmpa'
371
+ ),
372
+ 'notice_can_activate_recommended' => _n_noop(
373
  /* translators: 1: plugin name(s). */
374
+ 'The following recommended plugin is currently inactive: %1$s.',
375
+ 'The following recommended plugins are currently inactive: %1$s.',
376
+ 'tgmpa'
377
+ ),
378
+ 'install_link' => _n_noop(
379
+ 'Begin installing plugin',
380
+ 'Begin installing plugins',
381
+ 'tgmpa'
382
+ ),
383
+ 'update_link' => _n_noop(
384
+ 'Begin updating plugin',
385
+ 'Begin updating plugins',
386
+ 'tgmpa'
387
+ ),
388
+ 'activate_link' => _n_noop(
389
+ 'Begin activating plugin',
390
+ 'Begin activating plugins',
391
+ 'tgmpa'
392
+ ),
393
+ 'return' => __( 'Return to Required Plugins Installer', 'tgmpa' ),
394
+ 'dashboard' => __( 'Return to the Dashboard', 'tgmpa' ),
395
+ 'plugin_activated' => __( 'Plugin activated successfully.', 'tgmpa' ),
396
+ 'activated_successfully' => __( 'The following plugin was activated successfully:', 'tgmpa' ),
397
+ /* translators: 1: plugin name. */
398
+ 'plugin_already_active' => __( 'No action taken. Plugin %1$s was already active.', 'tgmpa' ),
399
+ /* translators: 1: plugin name. */
400
+ 'plugin_needs_higher_version' => __( 'Plugin not activated. A higher version of %s is needed for this theme. Please update the plugin.', 'tgmpa' ),
401
+ /* translators: 1: dashboard link. */
402
+ 'complete' => __( 'All plugins installed and activated successfully. %1$s', 'tgmpa' ),
403
+ 'dismiss' => __( 'Dismiss this notice', 'tgmpa' ),
404
+ 'notice_cannot_install_activate' => __( 'There are one or more required or recommended plugins to install, update or activate.', 'tgmpa' ),
405
+ 'contact_admin' => __( 'Please contact the administrator of this site for help.', 'tgmpa' ),
406
+ );
407
 
408
+ do_action( 'wpms_tgmpa_register' );
409
 
410
+ /* After this point, the plugins should be registered and the configuration set. */
411
 
412
+ // Proceed only if we have plugins to handle.
413
+ if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
414
+ return;
415
+ }
416
 
417
+ // Set up the menu and notices if we still have outstanding actions.
418
+ if ( true !== $this->is_tgmpa_complete() ) {
419
+ // Sort the plugins.
420
+ array_multisort( $this->sort_order, SORT_ASC, $this->plugins );
421
 
422
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
423
+ add_action( 'admin_head', array( $this, 'dismiss' ) );
424
 
425
+ // Prevent the normal links from showing underneath a single install/update page.
426
+ add_filter( 'install_plugin_complete_actions', array( $this, 'actions' ) );
427
+ add_filter( 'update_plugin_complete_actions', array( $this, 'actions' ) );
428
 
429
+ if ( $this->has_notices ) {
430
+ add_action( 'admin_notices', array( $this, 'notices' ) );
431
+ add_action( 'admin_init', array( $this, 'admin_init' ), 1 );
432
+ add_action( 'admin_enqueue_scripts', array( $this, 'thickbox' ) );
 
433
  }
434
+ }
435
 
436
+ // If needed, filter plugin action links.
437
+ add_action( 'load-plugins.php', array( $this, 'add_plugin_action_link_filters' ), 1 );
438
 
439
+ // Make sure things get reset on switch theme.
440
+ add_action( 'switch_theme', array( $this, 'flush_plugins_cache' ) );
441
 
442
+ if ( $this->has_notices ) {
443
+ add_action( 'switch_theme', array( $this, 'update_dismiss' ) );
444
+ }
445
 
446
+ // Setup the force activation hook.
447
+ if ( true === $this->has_forced_activation ) {
448
+ add_action( 'admin_init', array( $this, 'force_activation' ) );
449
+ }
450
 
451
+ // Setup the force deactivation hook.
452
+ if ( true === $this->has_forced_deactivation ) {
453
+ add_action( 'switch_theme', array( $this, 'force_deactivation' ) );
 
454
  }
455
+ }
456
 
457
+ /**
458
+ * Load translations.
459
+ *
460
+ * @since 2.6.0
461
+ *
462
+ * (@internal Uses `load_theme_textdomain()` rather than `load_plugin_textdomain()` to
463
+ * get round the different ways of handling the path and deprecated notices being thrown
464
+ * and such. For plugins, the actual file name will be corrected by a filter.}}
465
+ *
466
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
467
+ * generator on the website.}}
468
+ */
469
+ public function load_textdomain() {
470
+ if ( is_textdomain_loaded( 'tgmpa' ) ) {
471
+ return;
472
+ }
473
 
474
+ if ( false !== strpos( __FILE__, WP_PLUGIN_DIR ) || false !== strpos( __FILE__, WPMU_PLUGIN_DIR ) ) {
475
+ // Plugin, we'll need to adjust the file name.
476
+ add_action( 'load_textdomain_mofile', array( $this, 'correct_plugin_mofile' ), 10, 2 );
477
+ load_theme_textdomain( 'tgmpa', dirname( __FILE__ ) . '/languages' );
478
+ remove_action( 'load_textdomain_mofile', array( $this, 'correct_plugin_mofile' ), 10 );
479
+ } else {
480
+ load_theme_textdomain( 'tgmpa', dirname( __FILE__ ) . '/languages' );
 
481
  }
482
+ }
483
 
484
+ /**
485
+ * Correct the .mo file name for (must-use) plugins.
486
+ *
487
+ * Themese use `/path/{locale}.mo` while plugins use `/path/{text-domain}-{locale}.mo`.
488
+ *
489
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
490
+ * generator on the website.}}
491
+ *
492
+ * @since 2.6.0
493
+ *
494
+ * @param string $mofile Full path to the target mofile.
495
+ * @param string $domain The domain for which a language file is being loaded.
496
+ * @return string $mofile
497
+ */
498
+ public function correct_plugin_mofile( $mofile, $domain ) {
499
+ // Exit early if not our domain (just in case).
500
+ if ( 'tgmpa' !== $domain ) {
501
+ return $mofile;
 
 
502
  }
503
+ return preg_replace( '`/([a-z]{2}_[A-Z]{2}.mo)$`', '/tgmpa-$1', $mofile );
504
+ }
505
 
506
+ /**
507
+ * Potentially overload the fall-back translation file for the current language.
508
+ *
509
+ * WP, by default since WP 3.7, will load a local translation first and if none
510
+ * can be found, will try and find a translation in the /wp-content/languages/ directory.
511
+ * As this library is theme/plugin agnostic, translation files for TGMPA can exist both
512
+ * in the WP_LANG_DIR /plugins/ subdirectory as well as in the /themes/ subdirectory.
513
+ *
514
+ * This method makes sure both directories are checked.
515
+ *
516
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
517
+ * generator on the website.}}
518
+ *
519
+ * @since 2.6.0
520
+ *
521
+ * @param string $mofile Full path to the target mofile.
522
+ * @param string $domain The domain for which a language file is being loaded.
523
+ * @return string $mofile
524
+ */
525
+ public function overload_textdomain_mofile( $mofile, $domain ) {
526
+ // Exit early if not our domain, not a WP_LANG_DIR load or if the file exists and is readable.
527
+ if ( 'tgmpa' !== $domain || false === strpos( $mofile, WP_LANG_DIR ) || @is_readable( $mofile ) ) {
528
+ return $mofile;
529
+ }
530
 
531
+ // Current fallback file is not valid, let's try the alternative option.
532
+ if ( false !== strpos( $mofile, '/themes/' ) ) {
533
+ return str_replace( '/themes/', '/plugins/', $mofile );
534
+ } elseif ( false !== strpos( $mofile, '/plugins/' ) ) {
535
+ return str_replace( '/plugins/', '/themes/', $mofile );
536
+ } else {
537
+ return $mofile;
 
538
  }
539
+ }
540
 
541
+ /**
542
+ * Hook in plugin action link filters for the WP native plugins page.
543
+ *
544
+ * - Prevent activation of plugins which don't meet the minimum version requirements.
545
+ * - Prevent deactivation of force-activated plugins.
546
+ * - Add update notice if update available.
547
+ *
548
+ * @since 2.5.0
549
+ */
550
+ public function add_plugin_action_link_filters() {
551
+ foreach ( $this->plugins as $slug => $plugin ) {
552
+ if ( false === $this->can_plugin_activate( $slug ) ) {
553
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_activate' ), 20 );
554
+ }
555
 
556
+ if ( true === $plugin['force_activation'] ) {
557
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_deactivate' ), 20 );
558
+ }
559
 
560
+ if ( false !== $this->does_plugin_require_update( $slug ) ) {
561
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_update' ), 20 );
 
562
  }
563
  }
564
+ }
565
 
566
+ /**
567
+ * Remove the 'Activate' link on the WP native plugins page if the plugin does not meet the
568
+ * minimum version requirements.
569
+ *
570
+ * @since 2.5.0
571
+ *
572
+ * @param array $actions Action links.
573
+ * @return array
574
+ */
575
+ public function filter_plugin_action_links_activate( $actions ) {
576
+ unset( $actions['activate'] );
577
 
578
+ return $actions;
579
+ }
580
+
581
+ /**
582
+ * Remove the 'Deactivate' link on the WP native plugins page if the plugin has been set to force activate.
583
+ *
584
+ * @since 2.5.0
585
+ *
586
+ * @param array $actions Action links.
587
+ * @return array
588
+ */
589
+ public function filter_plugin_action_links_deactivate( $actions ) {
590
+ unset( $actions['deactivate'] );
591
 
592
+ return $actions;
593
+ }
 
 
 
 
 
 
 
 
594
 
595
+ /**
596
+ * Add a 'Requires update' link on the WP native plugins page if the plugin does not meet the
597
+ * minimum version requirements.
598
+ *
599
+ * @since 2.5.0
600
+ *
601
+ * @param array $actions Action links.
602
+ * @return array
603
+ */
604
+ public function filter_plugin_action_links_update( $actions ) {
605
+ $actions['update'] = sprintf(
606
+ '<a href="%1$s" title="%2$s" class="edit">%3$s</a>',
607
+ esc_url( $this->get_tgmpa_status_url( 'update' ) ),
608
+ esc_attr__( 'This plugin needs to be updated to be compatible with your theme.', 'tgmpa' ),
609
+ esc_html__( 'Update Required', 'tgmpa' )
610
+ );
611
 
612
+ return $actions;
613
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
 
615
+ /**
616
+ * Handles calls to show plugin information via links in the notices.
617
+ *
618
+ * We get the links in the admin notices to point to the TGMPA page, rather
619
+ * than the typical plugin-install.php file, so we can prepare everything
620
+ * beforehand.
621
+ *
622
+ * WP does not make it easy to show the plugin information in the thickbox -
623
+ * here we have to require a file that includes a function that does the
624
+ * main work of displaying it, enqueue some styles, set up some globals and
625
+ * finally call that function before exiting.
626
+ *
627
+ * Down right easy once you know how...
628
+ *
629
+ * Returns early if not the TGMPA page.
630
+ *
631
+ * @since 2.1.0
632
+ *
633
+ * @global string $tab Used as iframe div class names, helps with styling
634
+ * @global string $body_id Used as the iframe body ID, helps with styling
635
+ *
636
+ * @return void Returns early if not the TGMPA page.
637
+ */
638
+ public function admin_init() {
639
+ if ( ! $this->is_tgmpa_page() ) {
640
+ return;
641
  }
642
 
643
+ if ( isset( $_REQUEST['tab'] ) && 'plugin-information' === $_REQUEST['tab'] ) {
644
+ // Needed for install_plugin_information().
645
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
 
647
+ wp_enqueue_style( 'plugin-install' );
 
 
648
 
649
+ global $tab, $body_id;
650
+ $body_id = 'plugin-information';
651
+ // @codingStandardsIgnoreStart
652
+ $tab = 'plugin-information';
653
+ // @codingStandardsIgnoreEnd
654
 
655
+ install_plugin_information();
 
 
 
 
656
 
657
+ exit;
658
+ }
659
+ }
660
 
661
+ /**
662
+ * Enqueue thickbox scripts/styles for plugin info.
663
+ *
664
+ * Thickbox is not automatically included on all admin pages, so we must
665
+ * manually enqueue it for those pages.
666
+ *
667
+ * Thickbox is only loaded if the user has not dismissed the admin
668
+ * notice or if there are any plugins left to install and activate.
669
+ *
670
+ * @since 2.1.0
671
+ */
672
+ public function thickbox() {
673
+ if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
674
+ add_thickbox();
675
  }
676
+ }
677
 
678
+ /**
679
+ * Adds submenu page if there are plugin actions to take.
680
+ *
681
+ * This method adds the submenu page letting users know that a required
682
+ * plugin needs to be installed.
683
+ *
684
+ * This page disappears once the plugin has been installed and activated.
685
+ *
686
+ * @since 1.0.0
687
+ *
688
+ * @see TGM_Plugin_Activation::init()
689
+ * @see TGM_Plugin_Activation::install_plugins_page()
690
+ *
691
+ * @return void Return early if user lacks capability to install a plugin.
692
+ */
693
+ public function admin_menu() {
694
+ // Make sure privileges are correct to see the page.
695
+ if ( ! current_user_can( 'install_plugins' ) ) {
696
+ return;
697
  }
698
 
699
+ $args = apply_filters(
700
+ 'wpms_tgmpa_admin_menu_args',
701
+ array(
702
+ 'parent_slug' => $this->parent_slug, // Parent Menu slug.
703
+ 'page_title' => $this->strings['page_title'], // Page title.
704
+ 'menu_title' => $this->strings['menu_title'], // Menu title.
705
+ 'capability' => $this->capability, // Capability.
706
+ 'menu_slug' => $this->menu, // Menu slug.
707
+ 'function' => array( $this, 'install_plugins_page' ), // Callback.
708
+ )
709
+ );
 
 
 
 
 
 
 
 
 
710
 
711
+ $this->add_admin_menu( $args );
712
+ }
 
 
 
 
 
 
 
 
 
713
 
714
+ /**
715
+ * Add the menu item.
716
+ *
717
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
718
+ * generator on the website.}}
719
+ *
720
+ * @since 2.5.0
721
+ *
722
+ * @param array $args Menu item configuration.
723
+ */
724
+ protected function add_admin_menu( array $args ) {
725
+ if ( has_filter( 'wpms_tgmpa_admin_menu_use_add_theme_page' ) ) {
726
+ _deprecated_function( 'The "tgmpa_admin_menu_use_add_theme_page" filter', '2.5.0', esc_html__( 'Set the parent_slug config variable instead.', 'tgmpa' ) );
727
  }
728
 
729
+ if ( 'themes.php' === $this->parent_slug ) {
730
+ $this->page_hook = call_user_func( 'add_theme_page', $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
731
+ } else {
732
+ $this->page_hook = call_user_func( 'add_submenu_page', $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
  }
734
+ }
735
 
736
+ /**
737
+ * Echoes plugin installation form.
738
+ *
739
+ * This method is the callback for the admin_menu method function.
740
+ * This displays the admin page and form area where the user can select to install and activate the plugin.
741
+ * Aborts early if we're processing a plugin installation action.
742
+ *
743
+ * @since 1.0.0
744
+ *
745
+ * @return null Aborts early if we're processing a plugin installation action.
746
+ */
747
+ public function install_plugins_page() {
748
+ // Store new instance of plugin table in object.
749
+ $plugin_table = new TGMPA_List_Table();
750
 
751
+ // Return early if processing a plugin installation action.
752
+ if ( ( ( 'tgmpa-bulk-install' === $plugin_table->current_action() || 'tgmpa-bulk-update' === $plugin_table->current_action() ) && $plugin_table->process_bulk_actions() ) || $this->do_plugin_install() ) {
753
+ return;
754
+ }
755
 
756
+ // Force refresh of available plugin information so we'll know about manual updates/deletes.
757
+ wp_clean_plugins_cache( false );
758
 
759
+ ?>
760
+ <div class="tgmpa wrap">
761
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
762
+ <?php $plugin_table->prepare_items(); ?>
763
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  <?php
765
+ if ( ! empty( $this->message ) && is_string( $this->message ) ) {
766
+ echo wp_kses_post( $this->message );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  }
768
+ ?>
769
+ <?php $plugin_table->views(); ?>
770
+
771
+ <form id="tgmpa-plugins" action="" method="post">
772
+ <input type="hidden" name="tgmpa-page" value="<?php echo esc_attr( $this->menu ); ?>" />
773
+ <input type="hidden" name="plugin_status" value="<?php echo esc_attr( $plugin_table->view_context ); ?>" />
774
+ <?php $plugin_table->display(); ?>
775
+ </form>
776
+ </div>
777
+ <?php
778
+ }
779
 
780
+ /**
781
+ * Installs, updates or activates a plugin depending on the action link clicked by the user.
782
+ *
783
+ * Checks the $_GET variable to see which actions have been
784
+ * passed and responds with the appropriate method.
785
+ *
786
+ * Uses WP_Filesystem to process and handle the plugin installation
787
+ * method.
788
+ *
789
+ * @since 1.0.0
790
+ *
791
+ * @uses WP_Filesystem
792
+ * @uses WP_Error
793
+ * @uses WP_Upgrader
794
+ * @uses Plugin_Upgrader
795
+ * @uses Plugin_Installer_Skin
796
+ * @uses Plugin_Upgrader_Skin
797
+ *
798
+ * @return boolean True on success, false on failure.
799
+ */
800
+ protected function do_plugin_install() {
801
+ if ( empty( $_GET['plugin'] ) ) {
802
+ return false;
803
+ }
804
 
805
+ // All plugin information will be stored in an array for processing.
806
+ $slug = $this->sanitize_key( urldecode( $_GET['plugin'] ) );
 
807
 
808
+ if ( ! isset( $this->plugins[ $slug ] ) ) {
809
+ return false;
810
+ }
811
 
812
+ // Was an install or upgrade action link clicked?
813
+ if ( ( isset( $_GET['tgmpa-install'] ) && 'install-plugin' === $_GET['tgmpa-install'] ) || ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) ) {
814
+
815
+ $install_type = 'install';
816
+ if ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) {
817
+ $install_type = 'update';
818
+ }
819
 
820
+ check_admin_referer( 'tgmpa-' . $install_type, 'tgmpa-nonce' );
821
 
822
+ // Pass necessary information via URL if WP_Filesystem is needed.
823
+ $url = wp_nonce_url(
824
+ add_query_arg(
825
+ array(
826
+ 'plugin' => urlencode( $slug ),
827
+ 'tgmpa-' . $install_type => $install_type . '-plugin',
 
 
828
  ),
829
+ $this->get_tgmpa_url()
830
+ ),
831
+ 'tgmpa-' . $install_type,
832
+ 'tgmpa-nonce'
833
+ );
834
 
835
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
836
 
837
+ if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, array() ) ) ) {
838
+ return true;
839
+ }
840
 
841
+ if ( ! WP_Filesystem( $creds ) ) {
842
+ request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, array() ); // Setup WP_Filesystem.
843
+ return true;
844
+ }
845
 
846
+ /* If we arrive here, we have the filesystem. */
847
 
848
+ // Prep variables for Plugin_Installer_Skin class.
849
+ $extra = array();
850
+ $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
851
+ $source = $this->get_download_url( $slug );
852
+ $api = ( 'repo' === $this->plugins[ $slug ]['source_type'] ) ? $this->get_plugins_api( $slug ) : null;
853
+ $api = ( false !== $api ) ? $api : null;
854
 
855
+ $url = add_query_arg(
856
+ array(
857
+ 'action' => $install_type . '-plugin',
858
+ 'plugin' => urlencode( $slug ),
859
+ ),
860
+ 'update.php'
861
+ );
862
 
863
+ if ( ! class_exists( '\Plugin_Upgrader', false ) ) {
864
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
865
+ }
866
 
867
+ $title = ( 'update' === $install_type ) ? $this->strings['updating'] : $this->strings['installing'];
868
+ $skin_args = array(
869
+ 'type' => ( 'bundled' !== $this->plugins[ $slug ]['source_type'] ) ? 'web' : 'upload',
870
+ 'title' => sprintf( $title, $this->plugins[ $slug ]['name'] ),
871
+ 'url' => esc_url_raw( $url ),
872
+ 'nonce' => $install_type . '-plugin_' . $slug,
873
+ 'plugin' => '',
874
+ 'api' => $api,
875
+ 'extra' => $extra,
876
+ );
877
+ unset( $title );
878
 
879
+ if ( 'update' === $install_type ) {
880
+ $skin_args['plugin'] = $this->plugins[ $slug ]['file_path'];
881
+ $skin = new \Plugin_Upgrader_Skin( $skin_args );
882
+ } else {
883
+ $skin = new \Plugin_Installer_Skin( $skin_args );
884
+ }
885
 
886
+ // Create a new instance of Plugin_Upgrader.
887
+ $upgrader = new \Plugin_Upgrader( $skin );
888
 
889
+ // Perform the action and install the plugin from the $source urldecode().
890
+ add_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
891
 
892
+ if ( 'update' === $install_type ) {
893
+ // Inject our info into the update transient.
894
+ $to_inject = array( $slug => $this->plugins[ $slug ] );
895
+ $to_inject[ $slug ]['source'] = $source;
896
+ $this->inject_update_info( $to_inject );
897
 
898
+ $upgrader->upgrade( $this->plugins[ $slug ]['file_path'] );
899
+ } else {
900
+ $upgrader->install( $source );
901
+ }
902
 
903
+ remove_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1 );
904
 
905
+ // Make sure we have the correct file path now the plugin is installed/updated.
906
+ $this->populate_file_path( $slug );
907
 
908
+ // Only activate plugins if the config option is set to true and the plugin isn't
909
+ // already active (upgrade).
910
+ if ( $this->is_automatic && ! $this->is_plugin_active( $slug ) ) {
911
+ $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method.
912
+ if ( false === $this->activate_single_plugin( $plugin_activate, $slug, true ) ) {
913
+ return true; // Finish execution of the function early as we encountered an error.
 
914
  }
915
+ }
916
 
917
+ $this->show_tgmpa_version();
918
 
919
+ // Display message based on if all plugins are now active or not.
920
+ if ( $this->is_tgmpa_complete() ) {
921
+ echo '<p>', sprintf( esc_html( $this->strings['complete'] ), '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>' ), '</p>';
922
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
923
+ } else {
924
+ echo '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
925
+ }
926
 
927
+ return true;
928
+ } elseif ( isset( $this->plugins[ $slug ]['file_path'], $_GET['tgmpa-activate'] ) && 'activate-plugin' === $_GET['tgmpa-activate'] ) {
929
+ // Activate action link was clicked.
930
+ check_admin_referer( 'tgmpa-activate', 'tgmpa-nonce' );
931
 
932
+ if ( false === $this->activate_single_plugin( $this->plugins[ $slug ]['file_path'], $slug ) ) {
933
+ return true; // Finish execution of the function early as we encountered an error.
 
934
  }
 
 
935
  }
936
 
937
+ return false;
938
+ }
 
 
 
 
 
 
 
939
 
940
+ /**
941
+ * Inject information into the 'update_plugins' site transient as WP checks that before running an update.
942
+ *
943
+ * @since 2.5.0
944
+ *
945
+ * @param array $plugins The plugin information for the plugins which are to be updated.
946
+ */
947
+ public function inject_update_info( $plugins ) {
948
+ $repo_updates = get_site_transient( 'update_plugins' );
949
 
950
+ if ( ! is_object( $repo_updates ) ) {
951
+ $repo_updates = new \stdClass();
952
+ }
953
 
954
+ foreach ( $plugins as $slug => $plugin ) {
955
+ $file_path = $plugin['file_path'];
 
956
 
957
+ if ( empty( $repo_updates->response[ $file_path ] ) ) {
958
+ $repo_updates->response[ $file_path ] = new \stdClass();
 
 
 
 
 
 
959
  }
960
 
961
+ // We only really need to set package, but let's do all we can in case WP changes something.
962
+ $repo_updates->response[ $file_path ]->slug = $slug;
963
+ $repo_updates->response[ $file_path ]->plugin = $file_path;
964
+ $repo_updates->response[ $file_path ]->new_version = $plugin['version'];
965
+ $repo_updates->response[ $file_path ]->package = $plugin['source'];
966
+ if ( empty( $repo_updates->response[ $file_path ]->url ) && ! empty( $plugin['external_url'] ) ) {
967
+ $repo_updates->response[ $file_path ]->url = $plugin['external_url'];
968
+ }
969
  }
970
 
971
+ set_site_transient( 'update_plugins', $repo_updates );
972
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
973
 
974
+ /**
975
+ * Adjust the plugin directory name if necessary.
976
+ *
977
+ * The final destination directory of a plugin is based on the subdirectory name found in the
978
+ * (un)zipped source. In some cases - most notably GitHub repository plugin downloads -, this
979
+ * subdirectory name is not the same as the expected slug and the plugin will not be recognized
980
+ * as installed. This is fixed by adjusting the temporary unzipped source subdirectory name to
981
+ * the expected plugin slug.
982
+ *
983
+ * @since 2.5.0
984
+ *
985
+ * @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
986
+ * @param string $remote_source Path to upgrade/zip-file-name.tmp.
987
+ * @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
988
+ * @return string $source
989
+ */
990
+ public function maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
991
+ if ( ! $this->is_tgmpa_page() || ! is_object( $GLOBALS['wp_filesystem'] ) ) {
992
+ return $source;
993
+ }
994
 
995
+ // Check for single file plugins.
996
+ $source_files = array_keys( $GLOBALS['wp_filesystem']->dirlist( $remote_source ) );
997
+ if ( 1 === count( $source_files ) && false === $GLOBALS['wp_filesystem']->is_dir( $source ) ) {
998
+ return $source;
999
+ }
1000
 
1001
+ // Multi-file plugin, let's see if the directory is correctly named.
1002
+ $desired_slug = '';
1003
+
1004
+ // Figure out what the slug is supposed to be.
1005
+ if ( false === $upgrader->bulk && ! empty( $upgrader->skin->options['extra']['slug'] ) ) {
1006
+ $desired_slug = $upgrader->skin->options['extra']['slug'];
1007
+ } else {
1008
+ // Bulk installer contains less info, so fall back on the info registered here.
1009
+ foreach ( $this->plugins as $slug => $plugin ) {
1010
+ if ( ! empty( $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) && $plugin['name'] === $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) {
1011
+ $desired_slug = $slug;
1012
+ break;
1013
  }
 
1014
  }
1015
+ unset( $slug, $plugin );
1016
+ }
1017
 
1018
+ if ( ! empty( $desired_slug ) ) {
1019
+ $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
1020
 
1021
+ if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
1022
+ $from_path = untrailingslashit( $source );
1023
+ $to_path = trailingslashit( $remote_source ) . $desired_slug;
1024
 
1025
+ if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
1026
+ return trailingslashit( $to_path );
1027
+ } else {
1028
+ return new \WP_Error( 'rename_failed', esc_html__( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
 
 
 
1029
  }
1030
+ } elseif ( empty( $subdir_name ) ) {
1031
+ return new \WP_Error( 'packaged_wrong', esc_html__( 'The remote plugin package consists of more than one file, but the files are not packaged in a folder.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
1032
  }
 
 
1033
  }
1034
 
1035
+ return $source;
1036
+ }
 
 
 
 
 
 
 
 
 
 
 
 
1037
 
1038
+ /**
1039
+ * Activate a single plugin and send feedback about the result to the screen.
1040
+ *
1041
+ * @since 2.5.0
1042
+ *
1043
+ * @param string $file_path Path within wp-plugins/ to main plugin file.
1044
+ * @param string $slug Plugin slug.
1045
+ * @param bool $automatic Whether this is an automatic activation after an install. Defaults to false.
1046
+ * This determines the styling of the output messages.
1047
+ * @return bool False if an error was encountered, true otherwise.
1048
+ */
1049
+ protected function activate_single_plugin( $file_path, $slug, $automatic = false ) {
1050
+ if ( $this->can_plugin_activate( $slug ) ) {
1051
+ $activate = activate_plugin( $file_path );
1052
 
1053
+ if ( is_wp_error( $activate ) ) {
1054
+ echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>',
1055
+ '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
1056
+
1057
+ return false; // End it here if there is an error with activation.
1058
+ } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1059
  if ( ! $automatic ) {
1060
  // Make sure message doesn't display again if bulk activation is performed
1061
  // immediately after a single activation.
1062
  if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
1063
+ echo '<div id="message" class="updated"><p>', esc_html( $this->strings['activated_successfully'] ), ' <strong>', esc_html( $this->plugins[ $slug ]['name'] ), '.</strong></p></div>';
 
 
 
 
 
1064
  }
1065
  } else {
1066
  // Simpler message layout for use on the plugin install page.
1067
+ echo '<p>', esc_html( $this->strings['plugin_activated'] ), '</p>';
1068
+ }
1069
+ }
1070
+ } elseif ( $this->is_plugin_active( $slug ) ) {
1071
+ // No simpler message format provided as this message should never be encountered
1072
+ // on the plugin install page.
1073
+ echo '<div id="message" class="error"><p>',
1074
+ sprintf(
1075
+ esc_html( $this->strings['plugin_already_active'] ),
1076
+ '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
1077
+ ),
1078
+ '</p></div>';
1079
+ } elseif ( $this->does_plugin_require_update( $slug ) ) {
1080
+ if ( ! $automatic ) {
1081
+ // Make sure message doesn't display again if bulk activation is performed
1082
+ // immediately after a single activation.
1083
+ if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
1084
+ echo '<div id="message" class="error"><p>',
1085
+ sprintf(
1086
+ esc_html( $this->strings['plugin_needs_higher_version'] ),
1087
+ '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
1088
+ ),
1089
+ '</p></div>';
1090
  }
1091
+ } else {
1092
+ // Simpler message layout for use on the plugin install page.
1093
+ echo '<p>', sprintf( esc_html( $this->strings['plugin_needs_higher_version'] ), esc_html( $this->plugins[ $slug ]['name'] ) ), '</p>';
1094
  }
1095
+ }
1096
 
1097
+ return true;
1098
+ }
1099
+
1100
+ /**
1101
+ * Echoes required plugin notice.
1102
+ *
1103
+ * Outputs a message telling users that a specific plugin is required for
1104
+ * their theme. If appropriate, it includes a link to the form page where
1105
+ * users can install and activate the plugin.
1106
+ *
1107
+ * Returns early if we're on the Install page.
1108
+ *
1109
+ * @since 1.0.0
1110
+ *
1111
+ * @global object $current_screen
1112
+ *
1113
+ * @return void Returns early if we're on the Install page.
1114
+ */
1115
+ public function notices() {
1116
+ // Remove nag on the install page / Return early if the nag message has been dismissed or user < author.
1117
+ if ( ( $this->is_tgmpa_page() || $this->is_core_update_page() ) || get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) || ! current_user_can( apply_filters( 'wpms_tgmpa_show_admin_notice_capability', 'publish_posts' ) ) ) {
1118
+ return;
1119
  }
1120
 
1121
+ // Store for the plugin slugs by message type.
1122
+ $message = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1123
 
1124
+ // Initialize counters used to determine plurality of action link texts.
1125
+ $install_link_count = 0;
1126
+ $update_link_count = 0;
1127
+ $activate_link_count = 0;
1128
+ $total_required_action_count = 0;
1129
 
1130
+ foreach ( $this->plugins as $slug => $plugin ) {
1131
+ if ( $this->is_plugin_active( $slug ) && false === $this->does_plugin_have_update( $slug ) ) {
1132
+ continue;
1133
+ }
 
1134
 
1135
+ if ( ! $this->is_plugin_installed( $slug ) ) {
1136
+ if ( current_user_can( 'install_plugins' ) ) {
1137
+ $install_link_count++;
 
1138
 
1139
+ if ( true === $plugin['required'] ) {
1140
+ $message['notice_can_install_required'][] = $slug;
1141
+ } else {
1142
+ $message['notice_can_install_recommended'][] = $slug;
1143
+ }
1144
+ }
1145
+ if ( true === $plugin['required'] ) {
1146
+ $total_required_action_count++;
1147
+ }
1148
+ } else {
1149
+ if ( ! $this->is_plugin_active( $slug ) && $this->can_plugin_activate( $slug ) ) {
1150
+ if ( current_user_can( 'activate_plugins' ) ) {
1151
+ $activate_link_count++;
1152
 
1153
  if ( true === $plugin['required'] ) {
1154
+ $message['notice_can_activate_required'][] = $slug;
1155
  } else {
1156
+ $message['notice_can_activate_recommended'][] = $slug;
1157
  }
1158
  }
1159
  if ( true === $plugin['required'] ) {
1160
  $total_required_action_count++;
1161
  }
1162
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1163
 
1164
+ if ( $this->does_plugin_require_update( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1165
 
1166
+ if ( current_user_can( 'update_plugins' ) ) {
1167
+ $update_link_count++;
1168
 
1169
+ if ( $this->does_plugin_require_update( $slug ) ) {
1170
+ $message['notice_ask_to_update'][] = $slug;
1171
+ } elseif ( false !== $this->does_plugin_have_update( $slug ) ) {
1172
+ $message['notice_ask_to_update_maybe'][] = $slug;
 
 
 
 
1173
  }
1174
  }
1175
+ if ( true === $plugin['required'] ) {
1176
+ $total_required_action_count++;
1177
+ }
1178
  }
1179
  }
1180
+ }
1181
+ unset( $slug, $plugin );
 
 
 
 
1182
 
1183
+ // If we have notices to display, we move forward.
1184
+ if ( ! empty( $message ) || $total_required_action_count > 0 ) {
1185
+ krsort( $message ); // Sort messages.
1186
+ $rendered = '';
1187
 
1188
+ // As add_settings_error() wraps the final message in a <p> and as the final message can't be
1189
+ // filtered, using <p>'s in our html would render invalid html output.
1190
+ $line_template = '<span style="display: block; margin: 0.5em 0.5em 0 0; clear: both;">%s</span>' . "\n";
 
1191
 
1192
+ if ( ! current_user_can( 'activate_plugins' ) && ! current_user_can( 'install_plugins' ) && ! current_user_can( 'update_plugins' ) ) {
1193
+ $rendered = esc_html( $this->strings['notice_cannot_install_activate'] ) . ' ' . esc_html( $this->strings['contact_admin'] );
1194
+ $rendered .= $this->create_user_action_links_for_notice( 0, 0, 0, $line_template );
1195
+ } else {
1196
 
1197
+ // If dismissable is false and a message is set, output it now.
1198
+ if ( ! $this->dismissable && ! empty( $this->dismiss_msg ) ) {
1199
+ $rendered .= sprintf( $line_template, wp_kses_post( $this->dismiss_msg ) );
1200
+ }
1201
 
1202
+ // Render the individual message lines for the notice.
1203
+ foreach ( $message as $type => $plugin_group ) {
1204
+ $linked_plugins = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1205
 
1206
+ // Get the external info link for a plugin if one is available.
1207
+ foreach ( $plugin_group as $plugin_slug ) {
1208
+ $linked_plugins[] = $this->get_info_link( $plugin_slug );
1209
  }
1210
+ unset( $plugin_slug );
1211
+
1212
+ $count = count( $plugin_group );
1213
+ $linked_plugins = array_map( array( '\WPMailSMTP\TGMPA_Utils', 'wrap_in_em' ), $linked_plugins );
1214
+ $last_plugin = array_pop( $linked_plugins ); // Pop off last name to prep for readability.
1215
+ $imploded = empty( $linked_plugins ) ? $last_plugin : ( implode( ', ', $linked_plugins ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
1216
+
1217
+ $rendered .= sprintf(
1218
+ $line_template,
1219
+ sprintf(
1220
+ translate_nooped_plural( $this->strings[ $type ], $count, 'tgmpa' ),
1221
+ $imploded,
1222
+ $count
1223
+ )
1224
+ );
1225
 
 
1226
  }
1227
+ unset( $type, $plugin_group, $linked_plugins, $count, $last_plugin, $imploded );
1228
 
1229
+ $rendered .= $this->create_user_action_links_for_notice( $install_link_count, $update_link_count, $activate_link_count, $line_template );
 
1230
  }
1231
 
1232
+ // Register the nag messages and prepare them to be processed.
1233
+ add_settings_error( 'tgmpa', 'tgmpa', $rendered, $this->get_admin_notice_class() );
 
 
1234
  }
1235
 
1236
+ // Admin options pages already output settings_errors, so this is to avoid duplication.
1237
+ if ( 'options-general' !== $GLOBALS['current_screen']->parent_base ) {
1238
+ $this->display_settings_errors();
1239
+ }
1240
+ }
1241
+
1242
+ /**
1243
+ * Generate the user action links for the admin notice.
1244
+ *
1245
+ * @since 2.6.0
1246
+ *
1247
+ * @param int $install_count Number of plugins to install.
1248
+ * @param int $update_count Number of plugins to update.
1249
+ * @param int $activate_count Number of plugins to activate.
1250
+ * @param int $line_template Template for the HTML tag to output a line.
1251
+ * @return string Action links.
1252
+ */
1253
+ protected function create_user_action_links_for_notice( $install_count, $update_count, $activate_count, $line_template ) {
1254
+ // Setup action links.
1255
+ $action_links = array(
1256
+ 'install' => '',
1257
+ 'update' => '',
1258
+ 'activate' => '',
1259
+ 'dismiss' => $this->dismissable ? '<a href="' . esc_url( wp_nonce_url( add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ), 'tgmpa-dismiss-' . get_current_user_id() ) ) . '" class="dismiss-notice" target="_parent">' . esc_html( $this->strings['dismiss'] ) . '</a>' : '',
1260
+ );
1261
 
1262
+ $link_template = '<a href="%2$s">%1$s</a>';
1263
 
1264
+ if ( current_user_can( 'install_plugins' ) ) {
1265
+ if ( $install_count > 0 ) {
1266
+ $action_links['install'] = sprintf(
1267
+ $link_template,
1268
+ translate_nooped_plural( $this->strings['install_link'], $install_count, 'tgmpa' ),
1269
+ esc_url( $this->get_tgmpa_status_url( 'install' ) )
1270
+ );
 
 
 
 
 
 
 
 
1271
  }
1272
+ if ( $update_count > 0 ) {
1273
+ $action_links['update'] = sprintf(
 
1274
  $link_template,
1275
+ translate_nooped_plural( $this->strings['update_link'], $update_count, 'tgmpa' ),
1276
+ esc_url( $this->get_tgmpa_status_url( 'update' ) )
1277
  );
1278
  }
1279
+ }
1280
+
1281
+ if ( current_user_can( 'activate_plugins' ) && $activate_count > 0 ) {
1282
+ $action_links['activate'] = sprintf(
1283
+ $link_template,
1284
+ translate_nooped_plural( $this->strings['activate_link'], $activate_count, 'tgmpa' ),
1285
+ esc_url( $this->get_tgmpa_status_url( 'activate' ) )
1286
+ );
1287
+ }
1288
 
1289
+ $action_links = apply_filters( 'wpms_tgmpa_notice_action_links', $action_links );
1290
 
1291
+ $action_links = array_filter( (array) $action_links ); // Remove any empty array items.
1292
 
1293
+ if ( ! empty( $action_links ) ) {
1294
+ $action_links = sprintf( $line_template, implode( ' | ', $action_links ) );
1295
+ return apply_filters( 'wpms_tgmpa_notice_rendered_action_links', $action_links );
1296
+ } else {
1297
+ return '';
 
1298
  }
1299
+ }
1300
 
1301
+ /**
1302
+ * Get admin notice class.
1303
+ *
1304
+ * Work around all the changes to the various admin notice classes between WP 4.4 and 3.7
1305
+ * (lowest supported version by TGMPA).
1306
+ *
1307
+ * @since 2.6.0
1308
+ *
1309
+ * @return string
1310
+ */
1311
+ protected function get_admin_notice_class() {
1312
+ if ( ! empty( $this->strings['nag_type'] ) ) {
1313
+ return sanitize_html_class( strtolower( $this->strings['nag_type'] ) );
1314
+ } else {
1315
+ if ( version_compare( $this->wp_version, '4.2', '>=' ) ) {
1316
+ return 'notice-warning';
1317
+ } elseif ( version_compare( $this->wp_version, '4.1', '>=' ) ) {
1318
+ return 'notice';
1319
  } else {
1320
+ return 'updated';
 
 
 
 
 
 
1321
  }
1322
  }
1323
+ }
1324
 
1325
+ /**
1326
+ * Display settings errors and remove those which have been displayed to avoid duplicate messages showing
1327
+ *
1328
+ * @since 2.5.0
1329
+ */
1330
+ protected function display_settings_errors() {
1331
+ global $wp_settings_errors;
1332
 
1333
+ settings_errors( 'tgmpa' );
1334
 
1335
+ foreach ( (array) $wp_settings_errors as $key => $details ) {
1336
+ if ( 'tgmpa' === $details['setting'] ) {
1337
+ unset( $wp_settings_errors[ $key ] );
1338
+ break;
 
1339
  }
1340
  }
1341
+ }
1342
 
1343
+ /**
1344
+ * Register dismissal of admin notices.
1345
+ *
1346
+ * Acts on the dismiss link in the admin nag messages.
1347
+ * If clicked, the admin notice disappears and will no longer be visible to this user.
1348
+ *
1349
+ * @since 2.1.0
1350
+ */
1351
+ public function dismiss() {
1352
+ if ( isset( $_GET['tgmpa-dismiss'] ) && check_admin_referer( 'tgmpa-dismiss-' . get_current_user_id() ) ) {
1353
+ update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, 1 );
 
1354
  }
1355
+ }
1356
 
1357
+ /**
1358
+ * Add individual plugin to our collection of plugins.
1359
+ *
1360
+ * If the required keys are not set or the plugin has already
1361
+ * been registered, the plugin is not added.
1362
+ *
1363
+ * @since 2.0.0
1364
+ *
1365
+ * @param array|null $plugin Array of plugin arguments or null if invalid argument.
1366
+ * @return void Return early if incorrect argument.
1367
+ */
1368
+ public function register( $plugin ) {
1369
+ if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
1370
+ return;
1371
+ }
1372
 
1373
+ if ( empty( $plugin['slug'] ) || ! is_string( $plugin['slug'] ) || isset( $this->plugins[ $plugin['slug'] ] ) ) {
1374
+ return;
1375
+ }
1376
 
1377
+ $defaults = array(
1378
+ 'name' => '', // String.
1379
+ 'slug' => '', // String.
1380
+ 'source' => 'repo', // String.
1381
+ 'required' => false, // Boolean.
1382
+ 'version' => '', // String.
1383
+ 'force_activation' => false, // Boolean.
1384
+ 'force_deactivation' => false, // Boolean.
1385
+ 'external_url' => '', // String.
1386
+ 'is_callable' => '', // String|Array.
1387
+ );
1388
 
1389
+ // Prepare the received data.
1390
+ $plugin = wp_parse_args( $plugin, $defaults );
1391
 
1392
+ // Standardize the received slug.
1393
+ $plugin['slug'] = $this->sanitize_key( $plugin['slug'] );
1394
 
1395
+ // Forgive users for using string versions of booleans or floats for version number.
1396
+ $plugin['version'] = (string) $plugin['version'];
1397
+ $plugin['source'] = empty( $plugin['source'] ) ? 'repo' : $plugin['source'];
1398
+ $plugin['required'] = TGMPA_Utils::validate_bool( $plugin['required'] );
1399
+ $plugin['force_activation'] = TGMPA_Utils::validate_bool( $plugin['force_activation'] );
1400
+ $plugin['force_deactivation'] = TGMPA_Utils::validate_bool( $plugin['force_deactivation'] );
1401
 
1402
+ // Enrich the received data.
1403
+ $plugin['file_path'] = $this->_get_plugin_basename_from_slug( $plugin['slug'] );
1404
+ $plugin['source_type'] = $this->get_plugin_source_type( $plugin['source'] );
1405
 
1406
+ // Set the class properties.
1407
+ $this->plugins[ $plugin['slug'] ] = $plugin;
1408
+ $this->sort_order[ $plugin['slug'] ] = $plugin['name'];
1409
 
1410
+ // Should we add the force activation hook ?
1411
+ if ( true === $plugin['force_activation'] ) {
1412
+ $this->has_forced_activation = true;
1413
+ }
1414
 
1415
+ // Should we add the force deactivation hook ?
1416
+ if ( true === $plugin['force_deactivation'] ) {
1417
+ $this->has_forced_deactivation = true;
 
1418
  }
1419
+ }
1420
 
1421
+ /**
1422
+ * Determine what type of source the plugin comes from.
1423
+ *
1424
+ * @since 2.5.0
1425
+ *
1426
+ * @param string $source The source of the plugin as provided, either empty (= WP repo), a file path
1427
+ * (= bundled) or an external URL.
1428
+ * @return string 'repo', 'external', or 'bundled'
1429
+ */
1430
+ protected function get_plugin_source_type( $source ) {
1431
+ if ( 'repo' === $source || preg_match( self::WP_REPO_REGEX, $source ) ) {
1432
+ return 'repo';
1433
+ } elseif ( preg_match( self::IS_URL_REGEX, $source ) ) {
1434
+ return 'external';
1435
+ } else {
1436
+ return 'bundled';
 
1437
  }
1438
+ }
1439
+
1440
+ /**
1441
+ * Sanitizes a string key.
1442
+ *
1443
+ * Near duplicate of WP Core `sanitize_key()`. The difference is that uppercase characters *are*
1444
+ * allowed, so as not to break upgrade paths from non-standard bundled plugins using uppercase
1445
+ * characters in the plugin directory path/slug. Silly them.
1446
+ *
1447
+ * @see https://developer.wordpress.org/reference/hooks/sanitize_key/
1448
+ *
1449
+ * @since 2.5.0
1450
+ *
1451
+ * @param string $key String key.
1452
+ * @return string Sanitized key
1453
+ */
1454
+ public function sanitize_key( $key ) {
1455
+ $raw_key = $key;
1456
+ $key = preg_replace( '`[^A-Za-z0-9_-]`', '', $key );
1457
 
1458
  /**
1459
+ * Filter a sanitized key string.
 
 
 
 
 
 
1460
  *
1461
  * @since 2.5.0
1462
  *
1463
+ * @param string $key Sanitized key.
1464
+ * @param string $raw_key The key prior to sanitization.
1465
  */
1466
+ return apply_filters( 'wpms_tgmpa_sanitize_key', $key, $raw_key );
1467
+ }
 
 
 
 
 
 
 
 
 
 
 
 
1468
 
1469
+ /**
1470
+ * Amend default configuration settings.
1471
+ *
1472
+ * @since 2.0.0
1473
+ *
1474
+ * @param array $config Array of config options to pass as class properties.
1475
+ */
1476
+ public function config( $config ) {
1477
+ $keys = array(
1478
+ 'id',
1479
+ 'default_path',
1480
+ 'has_notices',
1481
+ 'dismissable',
1482
+ 'dismiss_msg',
1483
+ 'menu',
1484
+ 'parent_slug',
1485
+ 'capability',
1486
+ 'is_automatic',
1487
+ 'message',
1488
+ 'strings',
1489
+ );
1490
 
1491
+ foreach ( $keys as $key ) {
1492
+ if ( isset( $config[ $key ] ) ) {
1493
+ if ( is_array( $config[ $key ] ) ) {
1494
+ $this->$key = array_merge( $this->$key, $config[ $key ] );
1495
+ } else {
1496
+ $this->$key = $config[ $key ];
 
1497
  }
1498
  }
1499
  }
1500
+ }
1501
 
1502
+ /**
1503
+ * Amend action link after plugin installation.
1504
+ *
1505
+ * @since 2.0.0
1506
+ *
1507
+ * @param array $install_actions Existing array of actions.
1508
+ * @return false|array Amended array of actions.
1509
+ */
1510
+ public function actions( $install_actions ) {
1511
+ // Remove action links on the TGMPA install page.
1512
+ if ( $this->is_tgmpa_page() ) {
1513
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1514
  }
1515
 
1516
+ return $install_actions;
1517
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1518
 
1519
+ /**
1520
+ * Flushes the plugins cache on theme switch to prevent stale entries
1521
+ * from remaining in the plugin table.
1522
+ *
1523
+ * @since 2.4.0
1524
+ *
1525
+ * @param bool $clear_update_cache Optional. Whether to clear the Plugin updates cache.
1526
+ * Parameter added in v2.5.0.
1527
+ */
1528
+ public function flush_plugins_cache( $clear_update_cache = true ) {
1529
+ wp_clean_plugins_cache( $clear_update_cache );
1530
+ }
1531
 
1532
+ /**
1533
+ * Set file_path key for each installed plugin.
1534
+ *
1535
+ * @since 2.1.0
1536
+ *
1537
+ * @param string $plugin_slug Optional. If set, only (re-)populates the file path for that specific plugin.
1538
+ * Parameter added in v2.5.0.
1539
+ */
1540
+ public function populate_file_path( $plugin_slug = '' ) {
1541
+ if ( ! empty( $plugin_slug ) && is_string( $plugin_slug ) && isset( $this->plugins[ $plugin_slug ] ) ) {
1542
+ $this->plugins[ $plugin_slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $plugin_slug );
1543
+ } else {
1544
+ // Add file_path key for all plugins.
1545
+ foreach ( $this->plugins as $slug => $values ) {
1546
+ $this->plugins[ $slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $slug );
1547
  }
 
 
1548
  }
1549
+ }
1550
 
1551
+ /**
1552
+ * Helper function to extract the file path of the plugin file from the
1553
+ * plugin slug, if the plugin is installed.
1554
+ *
1555
+ * @since 2.0.0
1556
+ *
1557
+ * @param string $slug Plugin slug (typically folder name) as provided by the developer.
1558
+ * @return string Either file path for plugin if installed, or just the plugin slug.
1559
+ */
1560
+ protected function _get_plugin_basename_from_slug( $slug ) {
1561
+ $keys = array_keys( $this->get_plugins() );
 
 
 
 
 
 
 
1562
 
1563
+ foreach ( $keys as $key ) {
1564
+ if ( preg_match( '|^' . $slug . '/|', $key ) ) {
1565
+ return $key;
1566
+ }
1567
  }
1568
 
1569
+ return $slug;
1570
+ }
 
 
 
 
 
 
 
 
1571
 
1572
+ /**
1573
+ * Retrieve plugin data, given the plugin name.
1574
+ *
1575
+ * Loops through the registered plugins looking for $name. If it finds it,
1576
+ * it returns the $data from that plugin. Otherwise, returns false.
1577
+ *
1578
+ * @since 2.1.0
1579
+ *
1580
+ * @param string $name Name of the plugin, as it was registered.
1581
+ * @param string $data Optional. Array key of plugin data to return. Default is slug.
1582
+ * @return string|boolean Plugin slug if found, false otherwise.
1583
+ */
1584
+ public function _get_plugin_data_from_name( $name, $data = 'slug' ) {
1585
+ foreach ( $this->plugins as $values ) {
1586
+ if ( $name === $values['name'] && isset( $values[ $data ] ) ) {
1587
+ return $values[ $data ];
1588
  }
 
 
1589
  }
1590
 
1591
+ return false;
1592
+ }
 
 
 
 
 
 
 
 
 
1593
 
1594
+ /**
1595
+ * Retrieve the download URL for a package.
1596
+ *
1597
+ * @since 2.5.0
1598
+ *
1599
+ * @param string $slug Plugin slug.
1600
+ * @return string Plugin download URL or path to local file or empty string if undetermined.
1601
+ */
1602
+ public function get_download_url( $slug ) {
1603
+ $dl_source = '';
1604
 
1605
+ switch ( $this->plugins[ $slug ]['source_type'] ) {
1606
+ case 'repo':
1607
+ return $this->get_wp_repo_download_url( $slug );
1608
+ case 'external':
1609
+ return $this->plugins[ $slug ]['source'];
1610
+ case 'bundled':
1611
+ return $this->default_path . $this->plugins[ $slug ]['source'];
1612
  }
1613
 
1614
+ return $dl_source; // Should never happen.
1615
+ }
 
 
 
 
 
 
 
 
1616
 
1617
+ /**
1618
+ * Retrieve the download URL for a WP repo package.
1619
+ *
1620
+ * @since 2.5.0
1621
+ *
1622
+ * @param string $slug Plugin slug.
1623
+ * @return string Plugin download URL.
1624
+ */
1625
+ protected function get_wp_repo_download_url( $slug ) {
1626
+ $source = '';
1627
+ $api = $this->get_plugins_api( $slug );
1628
 
1629
+ if ( false !== $api && isset( $api->download_link ) ) {
1630
+ $source = $api->download_link;
1631
+ }
1632
 
1633
+ return $source;
1634
+ }
1635
 
1636
+ /**
1637
+ * Try to grab information from WordPress API.
1638
+ *
1639
+ * @since 2.5.0
1640
+ *
1641
+ * @param string $slug Plugin slug.
1642
+ * @return object Plugins_api response object on success, WP_Error on failure.
1643
+ */
1644
+ protected function get_plugins_api( $slug ) {
1645
+ static $api = array(); // Cache received responses.
1646
 
1647
+ if ( ! isset( $api[ $slug ] ) ) {
1648
+ if ( ! function_exists( 'plugins_api' ) ) {
1649
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1650
+ }
1651
 
1652
+ $response = plugins_api(
1653
+ 'plugin_information',
1654
+ array(
1655
+ 'slug' => $slug,
1656
+ 'fields' => array(
1657
+ 'sections' => false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1658
  ),
1659
+ )
1660
+ );
1661
 
1662
+ $api[ $slug ] = false;
1663
+
1664
+ if ( is_wp_error( $response ) ) {
1665
+ wp_die( esc_html( $this->strings['oops'] ) );
 
1666
  } else {
1667
+ $api[ $slug ] = $response;
1668
  }
 
 
1669
  }
1670
 
1671
+ return $api[ $slug ];
1672
+ }
 
 
 
 
 
 
 
 
1673
 
1674
+ /**
1675
+ * Retrieve a link to a plugin information page.
1676
+ *
1677
+ * @since 2.5.0
1678
+ *
1679
+ * @param string $slug Plugin slug.
1680
+ * @return string Fully formed html link to a plugin information page if available
1681
+ * or the plugin name if not.
1682
+ */
1683
+ public function get_info_link( $slug ) {
1684
+ if ( ! empty( $this->plugins[ $slug ]['external_url'] ) && preg_match( self::IS_URL_REGEX, $this->plugins[ $slug ]['external_url'] ) ) {
1685
+ $link = sprintf(
1686
+ '<a href="%1$s" target="_blank">%2$s</a>',
1687
+ esc_url( $this->plugins[ $slug ]['external_url'] ),
1688
+ esc_html( $this->plugins[ $slug ]['name'] )
1689
+ );
1690
+ } elseif ( 'repo' === $this->plugins[ $slug ]['source_type'] ) {
1691
+ $url = add_query_arg(
1692
+ array(
1693
+ 'tab' => 'plugin-information',
1694
+ 'plugin' => rawurlencode( $slug ),
1695
+ 'TB_iframe' => 'true',
1696
+ 'width' => '640',
1697
+ 'height' => '500',
1698
+ ),
1699
+ self_admin_url( 'plugin-install.php' )
1700
+ );
1701
 
1702
+ $link = sprintf(
1703
+ '<a href="%1$s" class="thickbox">%2$s</a>',
1704
+ esc_url( $url ),
1705
+ esc_html( $this->plugins[ $slug ]['name'] )
1706
+ );
1707
+ } else {
1708
+ $link = esc_html( $this->plugins[ $slug ]['name'] ); // No hyperlink.
1709
+ }
1710
 
1711
+ return $link;
1712
+ }
1713
+
1714
+ /**
1715
+ * Determine if we're on the TGMPA Install page.
1716
+ *
1717
+ * @since 2.1.0
1718
+ *
1719
+ * @return boolean True when on the TGMPA page, false otherwise.
1720
+ */
1721
+ protected function is_tgmpa_page() {
1722
+ return isset( $_GET['page'] ) && $this->menu === $_GET['page'];
1723
+ }
1724
 
1725
+ /**
1726
+ * Determine if we're on a WP Core installation/upgrade page.
1727
+ *
1728
+ * @since 2.6.0
1729
+ *
1730
+ * @return boolean True when on a WP Core installation/upgrade page, false otherwise.
1731
+ */
1732
+ protected function is_core_update_page() {
1733
+ // Current screen is not always available, most notably on the customizer screen.
1734
+ if ( ! function_exists( 'get_current_screen' ) ) {
1735
  return false;
1736
  }
1737
 
1738
+ $screen = get_current_screen();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1739
 
1740
+ if ( 'update-core' === $screen->base ) {
1741
+ // Core update screen.
1742
+ return true;
1743
+ } elseif ( 'plugins' === $screen->base && ! empty( $_POST['action'] ) ) { // WPCS: CSRF ok.
1744
+ // Plugins bulk update screen.
1745
+ return true;
1746
+ } elseif ( 'update' === $screen->base && ! empty( $_POST['action'] ) ) { // WPCS: CSRF ok.
1747
+ // Individual updates (ajax call).
1748
+ return true;
1749
  }
1750
 
1751
+ return false;
1752
+ }
1753
+
1754
+ /**
1755
+ * Retrieve the URL to the TGMPA Install page.
1756
+ *
1757
+ * I.e. depending on the config settings passed something along the lines of:
1758
+ * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins
1759
+ *
1760
+ * @since 2.5.0
1761
+ *
1762
+ * @return string Properly encoded URL (not escaped).
1763
+ */
1764
+ public function get_tgmpa_url() {
1765
+ static $url;
1766
+
1767
+ if ( ! isset( $url ) ) {
1768
+ $parent = $this->parent_slug;
1769
+ if ( false === strpos( $parent, '.php' ) ) {
1770
+ $parent = 'admin.php';
1771
+ }
1772
+ $url = add_query_arg(
1773
  array(
1774
+ 'page' => rawurlencode( $this->menu ),
1775
  ),
1776
+ self_admin_url( $parent )
1777
  );
1778
  }
1779
 
1780
+ return $url;
1781
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1782
 
1783
+ /**
1784
+ * Retrieve the URL to the TGMPA Install page for a specific plugin status (view).
1785
+ *
1786
+ * I.e. depending on the config settings passed something along the lines of:
1787
+ * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins&plugin_status=install
1788
+ *
1789
+ * @since 2.5.0
1790
+ *
1791
+ * @param string $status Plugin status - either 'install', 'update' or 'activate'.
1792
+ * @return string Properly encoded URL (not escaped).
1793
+ */
1794
+ public function get_tgmpa_status_url( $status ) {
1795
+ return add_query_arg(
1796
+ array(
1797
+ 'plugin_status' => rawurlencode( $status ),
1798
+ ),
1799
+ $this->get_tgmpa_url()
1800
+ );
1801
+ }
1802
 
1803
+ /**
1804
+ * Determine whether there are open actions for plugins registered with TGMPA.
1805
+ *
1806
+ * @since 2.5.0
1807
+ *
1808
+ * @return bool True if complete, i.e. no outstanding actions. False otherwise.
1809
+ */
1810
+ public function is_tgmpa_complete() {
1811
+ $complete = true;
1812
+ foreach ( $this->plugins as $slug => $plugin ) {
1813
+ if ( ! $this->is_plugin_active( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1814
+ $complete = false;
1815
+ break;
1816
+ }
1817
  }
1818
 
1819
+ return $complete;
1820
+ }
 
 
 
 
 
 
 
 
 
1821
 
1822
+ /**
1823
+ * Check if a plugin is installed. Does not take must-use plugins into account.
1824
+ *
1825
+ * @since 2.5.0
1826
+ *
1827
+ * @param string $slug Plugin slug.
1828
+ * @return bool True if installed, false otherwise.
1829
+ */
1830
+ public function is_plugin_installed( $slug ) {
1831
+ $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
 
 
 
 
1832
 
1833
+ return ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ] ) );
1834
+ }
1835
 
1836
+ /**
1837
+ * Check if a plugin is active.
1838
+ *
1839
+ * @since 2.5.0
1840
+ *
1841
+ * @param string $slug Plugin slug.
1842
+ * @return bool True if active, false otherwise.
1843
+ */
1844
+ public function is_plugin_active( $slug ) {
1845
+ return ( ( ! empty( $this->plugins[ $slug ]['is_callable'] ) && is_callable( $this->plugins[ $slug ]['is_callable'] ) ) || is_plugin_active( $this->plugins[ $slug ]['file_path'] ) );
1846
+ }
1847
 
1848
+ /**
1849
+ * Check if a plugin can be updated, i.e. if we have information on the minimum WP version required
1850
+ * available, check whether the current install meets them.
1851
+ *
1852
+ * @since 2.5.0
1853
+ *
1854
+ * @param string $slug Plugin slug.
1855
+ * @return bool True if OK to update, false otherwise.
1856
+ */
1857
+ public function can_plugin_update( $slug ) {
1858
+ // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1859
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1860
  return true;
1861
  }
1862
 
1863
+ $api = $this->get_plugins_api( $slug );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1864
 
1865
+ if ( false !== $api && isset( $api->requires ) ) {
1866
+ return version_compare( $this->wp_version, $api->requires, '>=' );
 
 
 
 
 
 
 
 
 
1867
  }
1868
 
1869
+ // No usable info received from the plugins API, presume we can update.
1870
+ return true;
1871
+ }
 
 
 
 
 
 
 
 
 
 
 
 
1872
 
1873
+ /**
1874
+ * Check to see if the plugin is 'updatetable', i.e. installed, with an update available
1875
+ * and no WP version requirements blocking it.
1876
+ *
1877
+ * @since 2.6.0
1878
+ *
1879
+ * @param string $slug Plugin slug.
1880
+ * @return bool True if OK to proceed with update, false otherwise.
1881
+ */
1882
+ public function is_plugin_updatetable( $slug ) {
1883
+ if ( ! $this->is_plugin_installed( $slug ) ) {
1884
+ return false;
1885
+ } else {
1886
+ return ( false !== $this->does_plugin_have_update( $slug ) && $this->can_plugin_update( $slug ) );
1887
  }
1888
+ }
1889
 
1890
+ /**
1891
+ * Check if a plugin can be activated, i.e. is not currently active and meets the minimum
1892
+ * plugin version requirements set in TGMPA (if any).
1893
+ *
1894
+ * @since 2.5.0
1895
+ *
1896
+ * @param string $slug Plugin slug.
1897
+ * @return bool True if OK to activate, false otherwise.
1898
+ */
1899
+ public function can_plugin_activate( $slug ) {
1900
+ return ( ! $this->is_plugin_active( $slug ) && ! $this->does_plugin_require_update( $slug ) );
1901
+ }
1902
 
1903
+ /**
1904
+ * Retrieve the version number of an installed plugin.
1905
+ *
1906
+ * @since 2.5.0
1907
+ *
1908
+ * @param string $slug Plugin slug.
1909
+ * @return string Version number as string or an empty string if the plugin is not installed
1910
+ * or version unknown (plugins which don't comply with the plugin header standard).
1911
+ */
1912
+ public function get_installed_version( $slug ) {
1913
+ $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1914
+
1915
+ if ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'] ) ) {
1916
+ return $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'];
1917
  }
1918
 
1919
+ return '';
1920
+ }
 
 
 
 
 
 
 
 
 
 
 
 
1921
 
1922
+ /**
1923
+ * Check whether a plugin complies with the minimum version requirements.
1924
+ *
1925
+ * @since 2.5.0
1926
+ *
1927
+ * @param string $slug Plugin slug.
1928
+ * @return bool True when a plugin needs to be updated, otherwise false.
1929
+ */
1930
+ public function does_plugin_require_update( $slug ) {
1931
+ $installed_version = $this->get_installed_version( $slug );
1932
+ $minimum_version = $this->plugins[ $slug ]['version'];
1933
 
1934
+ return version_compare( $minimum_version, $installed_version, '>' );
1935
+ }
1936
 
1937
+ /**
1938
+ * Check whether there is an update available for a plugin.
1939
+ *
1940
+ * @since 2.5.0
1941
+ *
1942
+ * @param string $slug Plugin slug.
1943
+ * @return false|string Version number string of the available update or false if no update available.
1944
+ */
1945
+ public function does_plugin_have_update( $slug ) {
1946
+ // Presume bundled and external plugins will point to a package which meets the minimum required version.
1947
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1948
+ if ( $this->does_plugin_require_update( $slug ) ) {
1949
+ return $this->plugins[ $slug ]['version'];
1950
  }
1951
 
1952
  return false;
1953
  }
1954
 
1955
+ $repo_updates = get_site_transient( 'update_plugins' );
 
 
 
 
 
 
 
 
 
 
 
 
1956
 
1957
+ if ( isset( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version ) ) {
1958
+ return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version;
1959
+ }
1960
 
1961
+ return false;
1962
+ }
 
1963
 
1964
+ /**
1965
+ * Retrieve potential upgrade notice for a plugin.
1966
+ *
1967
+ * @since 2.5.0
1968
+ *
1969
+ * @param string $slug Plugin slug.
1970
+ * @return string The upgrade notice or an empty string if no message was available or provided.
1971
+ */
1972
+ public function get_upgrade_notice( $slug ) {
1973
+ // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1974
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1975
  return '';
1976
  }
1977
 
1978
+ $repo_updates = get_site_transient( 'update_plugins' );
 
 
 
 
 
 
 
 
 
 
 
1979
 
1980
+ if ( ! empty( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice ) ) {
1981
+ return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice;
1982
  }
1983
 
1984
+ return '';
1985
+ }
 
 
 
 
 
 
 
 
 
1986
 
1987
+ /**
1988
+ * Wrapper around the core WP get_plugins function, making sure it's actually available.
1989
+ *
1990
+ * @since 2.5.0
1991
+ *
1992
+ * @param string $plugin_folder Optional. Relative path to single plugin folder.
1993
+ * @return array Array of installed plugins with plugin information.
1994
+ */
1995
+ public function get_plugins( $plugin_folder = '' ) {
1996
+ if ( ! function_exists( 'get_plugins' ) ) {
1997
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1998
  }
1999
 
2000
+ return get_plugins( $plugin_folder );
2001
+ }
 
 
 
 
 
 
 
 
 
 
 
 
2002
 
2003
+ /**
2004
+ * Delete dismissable nag option when theme is switched.
2005
+ *
2006
+ * This ensures that the user(s) is/are again reminded via nag of required
2007
+ * and/or recommended plugins if they re-activate the theme.
2008
+ *
2009
+ * @since 2.1.1
2010
+ */
2011
+ public function update_dismiss() {
2012
+ delete_metadata( 'user', null, 'tgmpa_dismissed_notice_' . $this->id, null, true );
2013
+ }
2014
 
2015
+ /**
2016
+ * Forces plugin activation if the parameter 'force_activation' is
2017
+ * set to true.
2018
+ *
2019
+ * This allows theme authors to specify certain plugins that must be
2020
+ * active at all times while using the current theme.
2021
+ *
2022
+ * Please take special care when using this parameter as it has the
2023
+ * potential to be harmful if not used correctly. Setting this parameter
2024
+ * to true will not allow the specified plugin to be deactivated unless
2025
+ * the user switches themes.
2026
+ *
2027
+ * @since 2.2.0
2028
+ */
2029
+ public function force_activation() {
2030
+ foreach ( $this->plugins as $slug => $plugin ) {
2031
+ if ( true === $plugin['force_activation'] ) {
2032
+ if ( ! $this->is_plugin_installed( $slug ) ) {
2033
+ // Oops, plugin isn't there so iterate to next condition.
2034
+ continue;
2035
+ } elseif ( $this->can_plugin_activate( $slug ) ) {
2036
+ // There we go, activate the plugin.
2037
+ activate_plugin( $plugin['file_path'] );
2038
+ }
2039
  }
2040
  }
2041
+ }
2042
 
2043
+ /**
2044
+ * Forces plugin deactivation if the parameter 'force_deactivation'
2045
+ * is set to true and adds the plugin to the 'recently active' plugins list.
2046
+ *
2047
+ * This allows theme authors to specify certain plugins that must be
2048
+ * deactivated upon switching from the current theme to another.
2049
+ *
2050
+ * Please take special care when using this parameter as it has the
2051
+ * potential to be harmful if not used correctly.
2052
+ *
2053
+ * @since 2.2.0
2054
+ */
2055
+ public function force_deactivation() {
2056
+ $deactivated = array();
 
 
2057
 
2058
+ foreach ( $this->plugins as $slug => $plugin ) {
2059
+ /*
2060
+ * Only proceed forward if the parameter is set to true and plugin is active
2061
+ * as a 'normal' (not must-use) plugin.
2062
+ */
2063
+ if ( true === $plugin['force_deactivation'] && is_plugin_active( $plugin['file_path'] ) ) {
2064
+ deactivate_plugins( $plugin['file_path'] );
2065
+ $deactivated[ $plugin['file_path'] ] = time();
 
 
2066
  }
 
 
2067
  }
 
2068
 
2069
+ if ( ! empty( $deactivated ) ) {
2070
+ update_option( 'recently_activated', $deactivated + (array) get_option( 'recently_activated' ) );
 
 
 
 
 
 
2071
  }
2072
  }
2073
 
2074
+ /**
2075
+ * Echo the current TGMPA version number to the page.
2076
+ *
2077
+ * @since 2.5.0
2078
+ */
2079
+ public function show_tgmpa_version() {
2080
+ echo '<p style="float: right; padding: 0em 1.5em 0.5em 0;"><strong><small>',
2081
+ esc_html(
2082
+ sprintf(
2083
+ /* translators: %s: version number */
2084
+ __( 'TGMPA v%s', 'tgmpa' ),
2085
+ self::TGMPA_VERSION
2086
+ )
2087
+ ),
2088
+ '</small></strong></p>';
2089
  }
 
2090
 
 
2091
  /**
2092
+ * Returns the singleton instance of the class.
2093
  *
2094
+ * @since 2.4.0
 
2095
  *
2096
+ * @return TGM_Plugin_Activation The TGM_Plugin_Activation object.
 
2097
  */
2098
+ public static function get_instance() {
2099
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
2100
+ self::$instance = new self();
 
 
2101
  }
2102
 
2103
+ return self::$instance;
2104
+ }
2105
+ }
 
 
 
 
 
2106
 
2107
+ /**
2108
+ * Ensure only one instance of the class is ever invoked.
2109
+ *
2110
+ * @since 2.5.0
2111
+ */
2112
+ function load_tgm_plugin_activation() {
2113
+ $GLOBALS['wp_mail_smtp_tgmpa'] = TGM_Plugin_Activation::get_instance();
2114
+ }
2115
+
2116
+ if ( did_action( 'plugins_loaded' ) ) {
2117
+ load_tgm_plugin_activation();
2118
+ } else {
2119
+ add_action( 'plugins_loaded', '\WPMailSMTP\load_tgm_plugin_activation' );
2120
+ }
2121
+
2122
+ /**
2123
+ * Helper function to register a collection of required plugins.
2124
+ *
2125
+ * @since 2.0.0
2126
+ * @api
2127
+ *
2128
+ * @param array $plugins An array of plugin arrays.
2129
+ * @param array $config Optional. An array of configuration values.
2130
+ */
2131
+ function tgmpa( $plugins, $config = array() ) {
2132
+ $instance = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
2133
+
2134
+ foreach ( $plugins as $plugin ) {
2135
+ call_user_func( array( $instance, 'register' ), $plugin );
2136
+ }
2137
+
2138
+ if ( ! empty( $config ) && is_array( $config ) ) {
2139
+ // Send out notices for deprecated arguments passed.
2140
+ if ( isset( $config['notices'] ) ) {
2141
+ _deprecated_argument( __FUNCTION__, '2.2.0', 'The `notices` config parameter was renamed to `has_notices` in TGMPA 2.2.0. Please adjust your configuration.' );
2142
+ if ( ! isset( $config['has_notices'] ) ) {
2143
+ $config['has_notices'] = $config['notices'];
2144
  }
2145
+ }
2146
 
2147
+ if ( isset( $config['parent_menu_slug'] ) ) {
2148
+ _deprecated_argument( __FUNCTION__, '2.4.0', 'The `parent_menu_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.' );
2149
+ }
2150
+ if ( isset( $config['parent_url_slug'] ) ) {
2151
+ _deprecated_argument( __FUNCTION__, '2.4.0', 'The `parent_url_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.' );
2152
  }
2153
+
2154
+ call_user_func( array( $instance, 'config' ), $config );
2155
  }
2156
  }
2157
 
2161
  *
2162
  * @since 2.2.0
2163
  */
2164
+ if ( ! class_exists( 'WP_List_Table', false ) ) {
2165
  require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
2166
  }
2167
 
2168
+ /**
2169
+ * List table class for handling plugins.
2170
+ *
2171
+ * Extends the WP_List_Table class to provide a future-compatible
2172
+ * way of listing out all required/recommended plugins.
2173
+ *
2174
+ * Gives users an interface similar to the Plugin Administration
2175
+ * area with similar (albeit stripped down) capabilities.
2176
+ *
2177
+ * This class also allows for the bulk install of plugins.
2178
+ *
2179
+ * @since 2.2.0
2180
+ *
2181
+ * @package TGM-Plugin-Activation
2182
+ * @author Thomas Griffin
2183
+ * @author Gary Jones
2184
+ */
2185
+ class TGMPA_List_Table extends \WP_List_Table {
2186
  /**
2187
+ * TGMPA instance.
2188
  *
2189
+ * @since 2.5.0
 
 
 
 
2190
  *
2191
+ * @var object
2192
+ */
2193
+ protected $tgmpa;
2194
+
2195
+ /**
2196
+ * The currently chosen view.
2197
  *
2198
+ * @since 2.5.0
2199
  *
2200
+ * @var string One of: 'all', 'install', 'update', 'activate'
 
 
2201
  */
2202
+ public $view_context = 'all';
 
 
 
 
 
 
 
 
2203
 
2204
+ /**
2205
+ * The plugin counts for the various views.
2206
+ *
2207
+ * @since 2.5.0
2208
+ *
2209
+ * @var array
2210
+ */
2211
+ protected $view_totals = array(
2212
+ 'all' => 0,
2213
+ 'install' => 0,
2214
+ 'update' => 0,
2215
+ 'activate' => 0,
2216
+ );
2217
 
2218
+ /**
2219
+ * References parent constructor and sets defaults for class.
2220
+ *
2221
+ * @since 2.2.0
2222
+ */
2223
+ public function __construct() {
2224
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
2225
+
2226
+ parent::__construct(
2227
+ array(
2228
+ 'singular' => 'plugin',
2229
+ 'plural' => 'plugins',
2230
+ 'ajax' => false,
2231
+ )
2232
  );
2233
 
2234
+ if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'install', 'update', 'activate' ), true ) ) {
2235
+ $this->view_context = sanitize_key( $_REQUEST['plugin_status'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2236
  }
2237
 
2238
+ add_filter( 'wpms_tgmpa_table_data_items', array( $this, 'sort_table_items' ) );
2239
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
2240
 
2241
+ /**
2242
+ * Get a list of CSS classes for the <table> tag.
2243
+ *
2244
+ * Overruled to prevent the 'plural' argument from being added.
2245
+ *
2246
+ * @since 2.5.0
2247
+ *
2248
+ * @return array CSS classnames.
2249
+ */
2250
+ public function get_table_classes() {
2251
+ return array( 'widefat', 'fixed' );
2252
+ }
2253
 
2254
+ /**
2255
+ * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table.
2256
+ *
2257
+ * @since 2.2.0
2258
+ *
2259
+ * @return array $table_data Information for use in table.
2260
+ */
2261
+ protected function _gather_plugin_data() {
2262
+ // Load thickbox for plugin links.
2263
+ $this->tgmpa->admin_init();
2264
+ $this->tgmpa->thickbox();
2265
 
2266
+ // Categorize the plugins which have open actions.
2267
+ $plugins = $this->categorize_plugins_to_views();
2268
 
2269
+ // Set the counts for the view links.
2270
+ $this->set_view_totals( $plugins );
 
 
 
 
 
 
 
 
 
 
2271
 
2272
+ // Prep variables for use and grab list of all installed plugins.
2273
+ $table_data = array();
2274
+ $i = 0;
 
 
2275
 
2276
+ // Redirect to the 'all' view if no plugins were found for the selected view context.
2277
+ if ( empty( $plugins[ $this->view_context ] ) ) {
2278
+ $this->view_context = 'all';
2279
+ }
2280
 
2281
+ foreach ( $plugins[ $this->view_context ] as $slug => $plugin ) {
2282
+ $table_data[ $i ]['sanitized_plugin'] = $plugin['name'];
2283
+ $table_data[ $i ]['slug'] = $slug;
2284
+ $table_data[ $i ]['plugin'] = '<strong>' . $this->tgmpa->get_info_link( $slug ) . '</strong>';
2285
+ $table_data[ $i ]['source'] = $this->get_plugin_source_type_text( $plugin['source_type'] );
2286
+ $table_data[ $i ]['type'] = $this->get_plugin_advise_type_text( $plugin['required'] );
2287
+ $table_data[ $i ]['status'] = $this->get_plugin_status_text( $slug );
2288
+ $table_data[ $i ]['installed_version'] = $this->tgmpa->get_installed_version( $slug );
2289
+ $table_data[ $i ]['minimum_version'] = $plugin['version'];
2290
+ $table_data[ $i ]['available_version'] = $this->tgmpa->does_plugin_have_update( $slug );
2291
 
2292
+ // Prep the upgrade notice info.
2293
+ $upgrade_notice = $this->tgmpa->get_upgrade_notice( $slug );
2294
+ if ( ! empty( $upgrade_notice ) ) {
2295
+ $table_data[ $i ]['upgrade_notice'] = $upgrade_notice;
2296
 
2297
+ add_action( "tgmpa_after_plugin_row_{$slug}", array( $this, 'wp_plugin_update_row' ), 10, 2 );
 
2298
  }
2299
 
2300
+ $table_data[ $i ] = apply_filters( 'wpms_tgmpa_table_data_item', $table_data[ $i ], $plugin );
 
 
2301
 
2302
+ $i++;
 
 
 
 
 
2303
  }
2304
 
2305
+ return $table_data;
2306
+ }
 
 
 
 
 
 
 
 
 
2307
 
2308
+ /**
2309
+ * Categorize the plugins which have open actions into views for the TGMPA page.
2310
+ *
2311
+ * @since 2.5.0
2312
+ */
2313
+ protected function categorize_plugins_to_views() {
2314
+ $plugins = array(
2315
+ 'all' => array(), // Meaning: all plugins which still have open actions.
2316
+ 'install' => array(),
2317
+ 'update' => array(),
2318
+ 'activate' => array(),
2319
+ );
2320
+
2321
+ foreach ( $this->tgmpa->plugins as $slug => $plugin ) {
2322
+ if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2323
+ // No need to display plugins if they are installed, up-to-date and active.
2324
+ continue;
2325
+ } else {
2326
+ $plugins['all'][ $slug ] = $plugin;
2327
 
2328
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2329
+ $plugins['install'][ $slug ] = $plugin;
2330
+ } else {
2331
+ if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2332
+ $plugins['update'][ $slug ] = $plugin;
2333
+ }
2334
 
2335
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2336
+ $plugins['activate'][ $slug ] = $plugin;
2337
+ }
2338
+ }
2339
+ }
2340
  }
2341
 
2342
+ return $plugins;
2343
+ }
 
 
 
 
 
 
 
2344
 
2345
+ /**
2346
+ * Set the counts for the view links.
2347
+ *
2348
+ * @since 2.5.0
2349
+ *
2350
+ * @param array $plugins Plugins order by view.
2351
+ */
2352
+ protected function set_view_totals( $plugins ) {
2353
+ foreach ( $plugins as $type => $list ) {
2354
+ $this->view_totals[ $type ] = count( $list );
2355
+ }
2356
+ }
2357
 
2358
+ /**
2359
+ * Get the plugin required/recommended text string.
2360
+ *
2361
+ * @since 2.5.0
2362
+ *
2363
+ * @param string $required Plugin required setting.
2364
+ * @return string
2365
+ */
2366
+ protected function get_plugin_advise_type_text( $required ) {
2367
+ if ( true === $required ) {
2368
+ return __( 'Required', 'tgmpa' );
2369
+ }
 
 
 
 
 
 
 
 
 
2370
 
2371
+ return __( 'Recommended', 'tgmpa' );
2372
+ }
2373
 
2374
+ /**
2375
+ * Get the plugin source type text string.
2376
+ *
2377
+ * @since 2.5.0
2378
+ *
2379
+ * @param string $type Plugin type.
2380
+ * @return string
2381
+ */
2382
+ protected function get_plugin_source_type_text( $type ) {
2383
+ $string = '';
2384
+
2385
+ switch ( $type ) {
2386
+ case 'repo':
2387
+ $string = __( 'WordPress Repository', 'tgmpa' );
2388
+ break;
2389
+ case 'external':
2390
+ $string = __( 'External Source', 'tgmpa' );
2391
+ break;
2392
+ case 'bundled':
2393
+ $string = __( 'Pre-Packaged', 'tgmpa' );
2394
+ break;
2395
+ }
2396
+
2397
+ return $string;
2398
+ }
2399
 
2400
+ /**
2401
+ * Determine the plugin status message.
2402
+ *
2403
+ * @since 2.5.0
2404
+ *
2405
+ * @param string $slug Plugin slug.
2406
+ * @return string
2407
+ */
2408
+ protected function get_plugin_status_text( $slug ) {
2409
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2410
+ return __( 'Not Installed', 'tgmpa' );
2411
  }
2412
 
2413
+ if ( ! $this->tgmpa->is_plugin_active( $slug ) ) {
2414
+ $install_status = __( 'Installed But Not Activated', 'tgmpa' );
2415
+ } else {
2416
+ $install_status = __( 'Active', 'tgmpa' );
 
 
 
 
 
 
 
 
2417
  }
2418
 
2419
+ $update_status = '';
2420
+
2421
+ if ( $this->tgmpa->does_plugin_require_update( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2422
+ $update_status = __( 'Required Update not Available', 'tgmpa' );
2423
+
2424
+ } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) {
2425
+ $update_status = __( 'Requires Update', 'tgmpa' );
2426
+
2427
+ } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2428
+ $update_status = __( 'Update recommended', 'tgmpa' );
 
 
 
 
 
 
 
2429
  }
2430
 
2431
+ if ( '' === $update_status ) {
2432
+ return $install_status;
 
 
 
 
 
 
 
 
 
 
 
 
2433
  }
2434
 
2435
+ return sprintf(
2436
+ /* translators: 1: install status, 2: update status */
2437
+ _x( '%1$s, %2$s', 'Install/Update Status', 'tgmpa' ),
2438
+ $install_status,
2439
+ $update_status
2440
+ );
2441
+ }
 
 
 
2442
 
2443
+ /**
2444
+ * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type.
2445
+ *
2446
+ * @since 2.5.0
2447
+ *
2448
+ * @param array $items Prepared table items.
2449
+ * @return array Sorted table items.
2450
+ */
2451
+ public function sort_table_items( $items ) {
2452
+ $type = array();
2453
+ $name = array();
2454
 
2455
+ foreach ( $items as $i => $plugin ) {
2456
+ $type[ $i ] = $plugin['type']; // Required / recommended.
2457
+ $name[ $i ] = $plugin['sanitized_plugin'];
2458
+ }
2459
 
2460
+ array_multisort( $type, SORT_DESC, $name, SORT_ASC, $items );
2461
+
2462
+ return $items;
2463
+ }
2464
+
2465
+ /**
2466
+ * Get an associative array ( id => link ) of the views available on this table.
2467
+ *
2468
+ * @since 2.5.0
2469
+ *
2470
+ * @return array
2471
+ */
2472
+ public function get_views() {
2473
+ $status_links = array();
2474
+
2475
+ foreach ( $this->view_totals as $type => $count ) {
2476
+ if ( $count < 1 ) {
2477
+ continue;
2478
  }
2479
 
2480
+ switch ( $type ) {
2481
+ case 'all':
2482
+ /* translators: 1: number of plugins. */
2483
+ $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins', 'tgmpa' );
2484
+ break;
2485
+ case 'install':
2486
+ /* translators: 1: number of plugins. */
2487
+ $text = _n( 'To Install <span class="count">(%s)</span>', 'To Install <span class="count">(%s)</span>', $count, 'tgmpa' );
2488
+ break;
2489
+ case 'update':
2490
+ /* translators: 1: number of plugins. */
2491
+ $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count, 'tgmpa' );
2492
+ break;
2493
+ case 'activate':
2494
+ /* translators: 1: number of plugins. */
2495
+ $text = _n( 'To Activate <span class="count">(%s)</span>', 'To Activate <span class="count">(%s)</span>', $count, 'tgmpa' );
2496
+ break;
2497
+ default:
2498
+ $text = '';
2499
+ break;
2500
  }
2501
 
2502
+ if ( ! empty( $text ) ) {
 
 
 
 
2503
 
2504
+ $status_links[ $type ] = sprintf(
2505
+ '<a href="%s"%s>%s</a>',
2506
+ esc_url( $this->tgmpa->get_tgmpa_status_url( $type ) ),
2507
+ ( $type === $this->view_context ) ? ' class="current"' : '',
2508
+ sprintf( $text, number_format_i18n( $count ) )
2509
  );
2510
  }
2511
+ }
2512
 
2513
+ return $status_links;
2514
+ }
2515
+
2516
+ /**
2517
+ * Create default columns to display important plugin information
2518
+ * like type, action and status.
2519
+ *
2520
+ * @since 2.2.0
2521
+ *
2522
+ * @param array $item Array of item data.
2523
+ * @param string $column_name The name of the column.
2524
+ * @return string
2525
+ */
2526
+ public function column_default( $item, $column_name ) {
2527
+ return $item[ $column_name ];
2528
+ }
2529
+
2530
+ /**
2531
+ * Required for bulk installing.
2532
+ *
2533
+ * Adds a checkbox for each plugin.
2534
+ *
2535
+ * @since 2.2.0
2536
+ *
2537
+ * @param array $item Array of item data.
2538
+ * @return string The input checkbox with all necessary info.
2539
+ */
2540
+ public function column_cb( $item ) {
2541
+ return sprintf(
2542
+ '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />',
2543
+ esc_attr( $this->_args['singular'] ),
2544
+ esc_attr( $item['slug'] ),
2545
+ esc_attr( $item['sanitized_plugin'] )
2546
+ );
2547
+ }
2548
+
2549
+ /**
2550
+ * Create default title column along with the action links.
2551
+ *
2552
+ * @since 2.2.0
2553
+ *
2554
+ * @param array $item Array of item data.
2555
+ * @return string The plugin name and action links.
2556
+ */
2557
+ public function column_plugin( $item ) {
2558
+ return sprintf(
2559
+ '%1$s %2$s',
2560
+ $item['plugin'],
2561
+ $this->row_actions( $this->get_row_actions( $item ), true )
2562
+ );
2563
+ }
2564
+
2565
+ /**
2566
+ * Create version information column.
2567
+ *
2568
+ * @since 2.5.0
2569
+ *
2570
+ * @param array $item Array of item data.
2571
+ * @return string HTML-formatted version information.
2572
+ */
2573
+ public function column_version( $item ) {
2574
+ $output = array();
2575
+
2576
+ if ( $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2577
+ $installed = ! empty( $item['installed_version'] ) ? $item['installed_version'] : _x( 'unknown', 'as in: "version nr unknown"', 'tgmpa' );
2578
+
2579
+ $color = '';
2580
+ if ( ! empty( $item['minimum_version'] ) && $this->tgmpa->does_plugin_require_update( $item['slug'] ) ) {
2581
+ $color = ' color: #ff0000; font-weight: bold;';
2582
  }
 
2583
 
2584
+ $output[] = sprintf(
2585
+ '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Installed version:', 'tgmpa' ) . '</p>',
2586
+ $color,
2587
+ $installed
2588
+ );
 
 
 
 
 
 
 
2589
  }
2590
 
2591
+ if ( ! empty( $item['minimum_version'] ) ) {
2592
+ $output[] = sprintf(
2593
+ '<p><span style="min-width: 32px; text-align: right; float: right;">%1$s</span>' . __( 'Minimum required version:', 'tgmpa' ) . '</p>',
2594
+ $item['minimum_version']
 
 
 
 
 
 
 
 
 
2595
  );
2596
+ }
2597
 
2598
+ if ( ! empty( $item['available_version'] ) ) {
2599
+ $color = '';
2600
+ if ( ! empty( $item['minimum_version'] ) && version_compare( $item['available_version'], $item['minimum_version'], '>=' ) ) {
2601
+ $color = ' color: #71C671; font-weight: bold;';
2602
  }
2603
 
2604
+ $output[] = sprintf(
2605
+ '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Available version:', 'tgmpa' ) . '</p>',
2606
+ $color,
2607
+ $item['available_version']
2608
+ );
2609
  }
2610
 
2611
+ if ( empty( $output ) ) {
2612
+ return '&nbsp;'; // Let's not break the table layout.
2613
+ } else {
2614
+ return implode( "\n", $output );
 
 
 
 
 
 
2615
  }
2616
+ }
2617
 
2618
+ /**
2619
+ * Sets default message within the plugins table if no plugins
2620
+ * are left for interaction.
2621
+ *
2622
+ * Hides the menu item to prevent the user from clicking and
2623
+ * getting a permissions error.
2624
+ *
2625
+ * @since 2.2.0
2626
+ */
2627
+ public function no_items() {
2628
+ echo esc_html__( 'No plugins to install, update or activate.', 'tgmpa' ) . ' <a href="' . esc_url( self_admin_url() ) . '"> ' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>';
2629
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
2630
+ }
2631
+
2632
+ /**
2633
+ * Output all the column information within the table.
2634
+ *
2635
+ * @since 2.2.0
2636
+ *
2637
+ * @return array $columns The column names.
2638
+ */
2639
+ public function get_columns() {
2640
+ $columns = array(
2641
+ 'cb' => '<input type="checkbox" />',
2642
+ 'plugin' => __( 'Plugin', 'tgmpa' ),
2643
+ 'source' => __( 'Source', 'tgmpa' ),
2644
+ 'type' => __( 'Type', 'tgmpa' ),
2645
+ );
2646
+
2647
+ if ( 'all' === $this->view_context || 'update' === $this->view_context ) {
2648
+ $columns['version'] = __( 'Version', 'tgmpa' );
2649
+ $columns['status'] = __( 'Status', 'tgmpa' );
2650
  }
2651
 
2652
+ return apply_filters( 'wpms_tgmpa_table_columns', $columns );
2653
+ }
2654
+
2655
+ /**
2656
+ * Get name of default primary column
2657
+ *
2658
+ * @since 2.5.0 / WP 4.3+ compatibility
2659
+ * @access protected
2660
+ *
2661
+ * @return string
2662
+ */
2663
+ protected function get_default_primary_column_name() {
2664
+ return 'plugin';
2665
+ }
2666
+
2667
+ /**
2668
+ * Get the name of the primary column.
2669
+ *
2670
+ * @since 2.5.0 / WP 4.3+ compatibility
2671
+ * @access protected
2672
+ *
2673
+ * @return string The name of the primary column.
2674
+ */
2675
+ protected function get_primary_column_name() {
2676
+ if ( method_exists( 'WP_List_Table', 'get_primary_column_name' ) ) {
2677
+ return parent::get_primary_column_name();
2678
+ } else {
2679
+ return $this->get_default_primary_column_name();
2680
+ }
2681
+ }
2682
 
2683
+ /**
2684
+ * Get the actions which are relevant for a specific plugin row.
2685
+ *
2686
+ * @since 2.5.0
2687
+ *
2688
+ * @param array $item Array of item data.
2689
+ * @return array Array with relevant action links.
2690
+ */
2691
+ protected function get_row_actions( $item ) {
2692
+ $actions = array();
2693
+ $action_links = array();
2694
+
2695
+ // Display the 'Install' action link if the plugin is not yet available.
2696
+ if ( ! $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2697
+ /* translators: %2$s: plugin name in screen reader markup */
2698
+ $actions['install'] = __( 'Install %2$s', 'tgmpa' );
2699
+ } else {
2700
+ // Display the 'Update' action link if an update is available and WP complies with plugin minimum.
2701
+ if ( false !== $this->tgmpa->does_plugin_have_update( $item['slug'] ) && $this->tgmpa->can_plugin_update( $item['slug'] ) ) {
2702
  /* translators: %2$s: plugin name in screen reader markup */
2703
+ $actions['update'] = __( 'Update %2$s', 'tgmpa' );
2704
+ }
 
 
 
 
 
2705
 
2706
+ // Display the 'Activate' action link, but only if the plugin meets the minimum version.
2707
+ if ( $this->tgmpa->can_plugin_activate( $item['slug'] ) ) {
2708
+ /* translators: %2$s: plugin name in screen reader markup */
2709
+ $actions['activate'] = __( 'Activate %2$s', 'tgmpa' );
 
2710
  }
2711
+ }
2712
 
2713
+ // Create the actual links.
2714
+ foreach ( $actions as $action => $text ) {
2715
+ $nonce_url = wp_nonce_url(
2716
+ add_query_arg(
2717
+ array(
2718
+ 'plugin' => rawurlencode( $item['slug'] ),
2719
+ 'tgmpa-' . $action => $action . '-plugin',
 
 
2720
  ),
2721
+ $this->tgmpa->get_tgmpa_url()
2722
+ ),
2723
+ 'tgmpa-' . $action,
2724
+ 'tgmpa-nonce'
2725
+ );
 
 
 
 
 
2726
 
2727
+ $action_links[ $action ] = sprintf(
2728
+ '<a href="%1$s">' . esc_html( $text ) . '</a>', // $text contains the second placeholder.
2729
+ esc_url( $nonce_url ),
2730
+ '<span class="screen-reader-text">' . esc_html( $item['sanitized_plugin'] ) . '</span>'
2731
+ );
2732
  }
2733
 
2734
+ $prefix = ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) ? 'network_admin_' : '';
2735
+ return apply_filters( "wpms_tgmpa_{$prefix}plugin_action_links", array_filter( $action_links ), $item['slug'], $item, $this->view_context );
2736
+ }
 
 
 
 
 
 
2737
 
2738
+ /**
2739
+ * Generates content for a single row of the table.
2740
+ *
2741
+ * @since 2.5.0
2742
+ *
2743
+ * @param object $item The current item.
2744
+ */
2745
+ public function single_row( $item ) {
2746
+ parent::single_row( $item );
 
2747
 
2748
  /**
2749
+ * Fires after each specific row in the TGMPA Plugins list table.
 
 
2750
  *
2751
+ * The dynamic portion of the hook name, `$item['slug']`, refers to the slug
2752
+ * for the plugin.
2753
  *
2754
+ * @since 2.5.0
 
 
2755
  */
2756
+ do_action( "wpms_tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context );
2757
+ }
 
 
2758
 
2759
+ /**
2760
+ * Show the upgrade notice below a plugin row if there is one.
2761
+ *
2762
+ * @since 2.5.0
2763
+ *
2764
+ * @see /wp-admin/includes/update.php
2765
+ *
2766
+ * @param string $slug Plugin slug.
2767
+ * @param array $item The information available in this table row.
2768
+ * @return null Return early if upgrade notice is empty.
2769
+ */
2770
+ public function wp_plugin_update_row( $slug, $item ) {
2771
+ if ( empty( $item['upgrade_notice'] ) ) {
2772
+ return;
2773
  }
2774
 
2775
+ echo '
2776
+ <tr class="plugin-update-tr">
2777
+ <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange">
2778
+ <div class="update-message">',
2779
+ esc_html__( 'Upgrade message from the plugin author:', 'tgmpa' ),
2780
+ ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong>
2781
+ </div>
2782
+ </td>
2783
+ </tr>';
2784
+ }
2785
+
2786
+ /**
2787
+ * Extra controls to be displayed between bulk actions and pagination.
2788
+ *
2789
+ * @since 2.5.0
2790
+ *
2791
+ * @param string $which 'top' or 'bottom' table navigation.
2792
+ */
2793
+ public function extra_tablenav( $which ) {
2794
+ if ( 'bottom' === $which ) {
2795
+ $this->tgmpa->show_tgmpa_version();
2796
  }
2797
+ }
2798
 
2799
+ /**
2800
+ * Defines the bulk actions for handling registered plugins.
2801
+ *
2802
+ * @since 2.2.0
2803
+ *
2804
+ * @return array $actions The bulk actions for the plugin install table.
2805
+ */
2806
+ public function get_bulk_actions() {
2807
 
2808
+ $actions = array();
2809
 
2810
+ if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) {
2811
+ if ( current_user_can( 'install_plugins' ) ) {
2812
+ $actions['tgmpa-bulk-install'] = __( 'Install', 'tgmpa' );
 
2813
  }
2814
+ }
2815
 
2816
+ if ( 'install' !== $this->view_context ) {
2817
+ if ( current_user_can( 'update_plugins' ) ) {
2818
+ $actions['tgmpa-bulk-update'] = __( 'Update', 'tgmpa' );
2819
+ }
2820
+ if ( current_user_can( 'activate_plugins' ) ) {
2821
+ $actions['tgmpa-bulk-activate'] = __( 'Activate', 'tgmpa' );
 
2822
  }
 
 
2823
  }
2824
 
2825
+ return $actions;
2826
+ }
 
 
 
 
 
 
 
 
 
 
 
2827
 
2828
+ /**
2829
+ * Processes bulk installation and activation actions.
2830
+ *
2831
+ * The bulk installation process looks for the $_POST information and passes that
2832
+ * through if a user has to use WP_Filesystem to enter their credentials.
2833
+ *
2834
+ * @since 2.2.0
2835
+ */
2836
+ public function process_bulk_actions() {
2837
+ // Bulk installation process.
2838
+ if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) {
2839
 
2840
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
2841
 
2842
+ $install_type = 'install';
2843
+ if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2844
+ $install_type = 'update';
2845
+ }
 
 
 
2846
 
2847
+ $plugins_to_install = array();
2848
 
2849
+ // Did user actually select any plugins to install/update ?
2850
+ if ( empty( $_POST['plugin'] ) ) {
2851
+ if ( 'install' === $install_type ) {
2852
+ $message = __( 'No plugins were selected to be installed. No action taken.', 'tgmpa' );
2853
+ } else {
2854
+ $message = __( 'No plugins were selected to be updated. No action taken.', 'tgmpa' );
2855
  }
2856
 
2857
+ echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
 
 
 
 
 
2858
 
2859
+ return false;
2860
+ }
 
2861
 
2862
+ if ( is_array( $_POST['plugin'] ) ) {
2863
+ $plugins_to_install = (array) $_POST['plugin'];
2864
+ } elseif ( is_string( $_POST['plugin'] ) ) {
2865
+ // Received via Filesystem page - un-flatten array (WP bug #19643).
2866
+ $plugins_to_install = explode( ',', $_POST['plugin'] );
2867
+ }
 
2868
 
2869
+ // Sanitize the received input.
2870
+ $plugins_to_install = array_map( 'urldecode', $plugins_to_install );
2871
+ $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install );
 
2872
 
2873
+ // Validate the received input.
2874
+ foreach ( $plugins_to_install as $key => $slug ) {
2875
+ // Check if the plugin was registered with TGMPA and remove if not.
2876
+ if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) {
2877
+ unset( $plugins_to_install[ $key ] );
2878
+ continue;
2879
  }
2880
 
2881
+ // For install: make sure this is a plugin we *can* install and not one already installed.
2882
+ if ( 'install' === $install_type && true === $this->tgmpa->is_plugin_installed( $slug ) ) {
2883
+ unset( $plugins_to_install[ $key ] );
2884
+ }
 
 
 
2885
 
2886
+ // For updates: make sure this is a plugin we *can* update (update available and WP version ok).
2887
+ if ( 'update' === $install_type && false === $this->tgmpa->is_plugin_updatetable( $slug ) ) {
2888
+ unset( $plugins_to_install[ $key ] );
2889
+ }
2890
+ }
2891
 
2892
+ // No need to proceed further if we have no plugins to handle.
2893
+ if ( empty( $plugins_to_install ) ) {
2894
+ if ( 'install' === $install_type ) {
2895
+ $message = __( 'No plugins are available to be installed at this time.', 'tgmpa' );
2896
+ } else {
2897
+ $message = __( 'No plugins are available to be updated at this time.', 'tgmpa' );
2898
  }
2899
 
2900
+ echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2901
+
2902
+ return false;
2903
+ }
 
2904
 
2905
+ // Pass all necessary information if WP_Filesystem is needed.
2906
+ $url = wp_nonce_url(
2907
+ $this->tgmpa->get_tgmpa_url(),
2908
+ 'bulk-' . $this->_args['plural']
2909
+ );
2910
 
2911
+ // Give validated data back to $_POST which is the only place the filesystem looks for extra fields.
2912
+ $_POST['plugin'] = implode( ',', $plugins_to_install ); // Work around for WP bug #19643.
2913
 
2914
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
2915
+ $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem.
 
2916
 
2917
+ if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) {
2918
+ return true; // Stop the normal page form from displaying, credential request form will be shown.
2919
+ }
 
2920
 
2921
+ // Now we have some credentials, setup WP_Filesystem.
2922
+ if ( ! WP_Filesystem( $creds ) ) {
2923
+ // Our credentials were no good, ask the user for them again.
2924
+ request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields );
2925
 
2926
+ return true;
2927
+ }
2928
 
2929
+ /* If we arrive here, we have the filesystem */
 
 
 
 
2930
 
2931
+ // Store all information in arrays since we are processing a bulk installation.
2932
+ $names = array();
2933
+ $sources = array(); // Needed for installs.
2934
+ $file_paths = array(); // Needed for upgrades.
2935
+ $to_inject = array(); // Information to inject into the update_plugins transient.
2936
 
2937
+ // Prepare the data for validated plugins for the install/upgrade.
2938
+ foreach ( $plugins_to_install as $slug ) {
2939
+ $name = $this->tgmpa->plugins[ $slug ]['name'];
2940
+ $source = $this->tgmpa->get_download_url( $slug );
2941
 
2942
+ if ( ! empty( $name ) && ! empty( $source ) ) {
2943
+ $names[] = $name;
2944
 
2945
+ switch ( $install_type ) {
 
 
2946
 
2947
+ case 'install':
2948
+ $sources[] = $source;
2949
+ break;
2950
+
2951
+ case 'update':
2952
+ $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2953
+ $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ];
2954
+ $to_inject[ $slug ]['source'] = $source;
2955
+ break;
2956
  }
2957
  }
2958
+ }
2959
+ unset( $slug, $name, $source );
2960
+
2961
+ // Create a new instance of TGMPA_Bulk_Installer.
2962
+ $installer = new TGMPA_Bulk_Installer(
2963
+ new TGMPA_Bulk_Installer_Skin(
2964
+ array(
2965
+ 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),
2966
+ 'nonce' => 'bulk-' . $this->_args['plural'],
2967
+ 'names' => $names,
2968
+ 'install_type' => $install_type,
2969
  )
2970
+ )
2971
+ );
2972
 
2973
+ // Wrap the install process with the appropriate HTML.
2974
+ echo '<div class="tgmpa">',
2975
+ '<h2 style="font-size: 23px; font-weight: 400; line-height: 29px; margin: 0; padding: 9px 15px 4px 0;">', esc_html( get_admin_page_title() ), '</h2>
2976
+ <div class="update-php" style="width: 100%; height: 98%; min-height: 850px; padding-top: 1px;">';
2977
 
2978
+ // Process the bulk installation submissions.
2979
+ add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2980
 
2981
+ if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2982
+ // Inject our info into the update transient.
2983
+ $this->tgmpa->inject_update_info( $to_inject );
2984
 
2985
+ $installer->bulk_upgrade( $file_paths );
2986
+ } else {
2987
+ $installer->bulk_install( $sources );
2988
+ }
2989
 
2990
+ remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1 );
2991
 
2992
+ echo '</div></div>';
2993
 
2994
+ return true;
2995
+ }
2996
 
2997
+ // Bulk activation process.
2998
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
2999
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
3000
 
3001
+ // Did user actually select any plugins to activate ?
3002
+ if ( empty( $_POST['plugin'] ) ) {
3003
+ echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'tgmpa' ), '</p></div>';
3004
 
3005
+ return false;
3006
+ }
3007
 
3008
+ // Grab plugin data from $_POST.
3009
+ $plugins = array();
3010
+ if ( isset( $_POST['plugin'] ) ) {
3011
+ $plugins = array_map( 'urldecode', (array) $_POST['plugin'] );
3012
+ $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins );
3013
+ }
3014
 
3015
+ $plugins_to_activate = array();
3016
+ $plugin_names = array();
3017
 
3018
+ // Grab the file paths for the selected & inactive plugins from the registration array.
3019
+ foreach ( $plugins as $slug ) {
3020
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
3021
+ $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path'];
3022
+ $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name'];
 
3023
  }
3024
+ }
3025
+ unset( $slug );
3026
 
3027
+ // Return early if there are no plugins to activate.
3028
+ if ( empty( $plugins_to_activate ) ) {
3029
+ echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'tgmpa' ), '</p></div>';
3030
 
3031
+ return false;
3032
+ }
3033
 
3034
+ // Now we are good to go - let's start activating plugins.
3035
+ $activate = activate_plugins( $plugins_to_activate );
3036
 
3037
+ if ( is_wp_error( $activate ) ) {
3038
+ echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>';
3039
+ } else {
3040
+ $count = count( $plugin_names ); // Count so we can use _n function.
3041
+ $plugin_names = array_map( array( 'TGMPA_Utils', 'wrap_in_strong' ), $plugin_names );
3042
+ $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability.
3043
+ $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
3044
+
3045
+ printf( // WPCS: xss ok.
3046
+ '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',
3047
+ esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'tgmpa' ) ),
3048
+ $imploded
3049
+ );
3050
 
3051
+ // Update recently activated plugins option.
3052
+ $recent = (array) get_option( 'recently_activated' );
3053
+ foreach ( $plugins_to_activate as $plugin => $time ) {
3054
+ if ( isset( $recent[ $plugin ] ) ) {
3055
+ unset( $recent[ $plugin ] );
 
3056
  }
 
3057
  }
3058
+ update_option( 'recently_activated', $recent );
 
 
 
3059
  }
3060
 
3061
+ unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3062
 
3063
+ return true;
 
3064
  }
3065
 
3066
+ return false;
3067
+ }
3068
 
3069
+ /**
3070
+ * Prepares all of our information to be outputted into a usable table.
3071
+ *
3072
+ * @since 2.2.0
3073
+ */
3074
+ public function prepare_items() {
3075
+ $columns = $this->get_columns(); // Get all necessary column information.
3076
+ $hidden = array(); // No columns to hide, but we must set as an array.
3077
+ $sortable = array(); // No reason to make sortable columns.
3078
+ $primary = $this->get_primary_column_name(); // Column which has the row actions.
3079
+ $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers.
 
 
3080
 
3081
+ // Process our bulk activations here.
3082
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
3083
+ $this->process_bulk_actions();
3084
  }
 
 
3085
 
3086
+ // Store all of our plugin data into $items array so WP_List_Table can use it.
3087
+ $this->items = apply_filters( 'wpms_tgmpa_table_data_items', $this->_gather_plugin_data() );
3088
+ }
3089
 
3090
+ /* *********** DEPRECATED METHODS *********** */
3091
 
3092
  /**
3093
+ * Retrieve plugin data, given the plugin name.
3094
  *
3095
+ * @since 2.2.0
3096
+ * @deprecated 2.5.0 use {@see TGM_Plugin_Activation::_get_plugin_data_from_name()} instead.
3097
+ * @see TGM_Plugin_Activation::_get_plugin_data_from_name()
3098
  *
3099
+ * @param string $name Name of the plugin, as it was registered.
3100
+ * @param string $data Optional. Array key of plugin data to return. Default is slug.
3101
+ * @return string|boolean Plugin slug if found, false otherwise.
3102
  */
3103
+ protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
3104
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'TGM_Plugin_Activation::_get_plugin_data_from_name()' );
3105
+
3106
+ return $this->tgmpa->_get_plugin_data_from_name( $name, $data );
3107
  }
3108
  }
3109
 
3110
+ /**
3111
+ * Hack: Prevent TGMPA v2.4.1- bulk installer class from being loaded if 2.4.1- is loaded after 2.5+.
3112
+ *
3113
+ * @since 2.5.2
3114
+ *
3115
+ * {@internal The TGMPA_Bulk_Installer class was originally called TGM_Bulk_Installer.
3116
+ * For more information, see that class.}}
3117
+ */
3118
+ class TGM_Bulk_Installer {
3119
+ }
3120
 
3121
+ /**
3122
+ * Hack: Prevent TGMPA v2.4.1- bulk installer skin class from being loaded if 2.4.1- is loaded after 2.5+.
3123
+ *
3124
+ * @since 2.5.2
3125
+ *
3126
+ * {@internal The TGMPA_Bulk_Installer_Skin class was originally called TGM_Bulk_Installer_Skin.
3127
+ * For more information, see that class.}}
3128
+ */
3129
+ class TGM_Bulk_Installer_Skin {
 
3130
  }
3131
 
3132
  /**
3139
  *
3140
  * @since 2.2.0
3141
  */
3142
+ add_action( 'admin_init', '\WPMailSMTP\tgmpa_load_bulk_installer' );
 
 
 
 
 
 
 
 
 
3143
 
3144
+ /**
3145
+ * Load bulk installer
3146
+ */
3147
+ function tgmpa_load_bulk_installer() {
3148
+ // Silently fail if 2.5+ is loaded *after* an older version.
3149
+ if ( ! isset( $GLOBALS['wp_mail_smtp_tgmpa'] ) ) {
3150
+ return;
3151
+ }
3152
 
3153
+ // Get TGMPA class instance.
3154
+ $tgmpa_instance = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
3155
+
3156
+ if ( isset( $_GET['page'] ) && $tgmpa_instance->menu === $_GET['page'] ) {
3157
+ if ( ! class_exists( '\Plugin_Upgrader', false ) ) {
3158
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3159
+ }
3160
 
3161
+ if ( ! class_exists( '\WPMailSMTP\TGMPA_Bulk_Installer', false ) ) {
3162
 
3163
+ /**
3164
+ * Installer class to handle bulk plugin installations.
3165
+ *
3166
+ * Extends WP_Upgrader and customizes to suit the installation of multiple
3167
+ * plugins.
3168
+ *
3169
+ * @since 2.2.0
3170
+ *
3171
+ * {@internal Since 2.5.0 the class is an extension of Plugin_Upgrader rather than WP_Upgrader.}}
3172
+ * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer to TGMPA_Bulk_Installer.
3173
+ * This was done to prevent backward compatibility issues with v2.3.6.}}
3174
+ *
3175
+ * @package TGM-Plugin-Activation
3176
+ * @author Thomas Griffin
3177
+ * @author Gary Jones
3178
+ */
3179
+ class TGMPA_Bulk_Installer extends \Plugin_Upgrader {
3180
  /**
3181
+ * Holds result of bulk plugin installation.
 
 
 
3182
  *
3183
  * @since 2.2.0
3184
  *
3185
+ * @var string
 
 
 
 
 
 
3186
  */
3187
+ public $result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3188
 
3189
+ /**
3190
+ * Flag to check if bulk installation is occurring or not.
3191
+ *
3192
+ * @since 2.2.0
3193
+ *
3194
+ * @var boolean
3195
+ */
3196
+ public $bulk = false;
3197
 
3198
+ /**
3199
+ * TGMPA instance
3200
+ *
3201
+ * @since 2.5.0
3202
+ *
3203
+ * @var object
3204
+ */
3205
+ protected $tgmpa;
 
 
3206
 
3207
+ /**
3208
+ * Whether or not the destination directory needs to be cleared ( = on update).
3209
+ *
3210
+ * @since 2.5.0
3211
+ *
3212
+ * @var bool
3213
+ */
3214
+ protected $clear_destination = false;
3215
 
3216
+ /**
3217
+ * References parent constructor and sets defaults for class.
3218
+ *
3219
+ * @since 2.2.0
3220
+ *
3221
+ * @param \Bulk_Upgrader_Skin|null $skin Installer skin.
3222
+ */
3223
+ public function __construct( $skin = null ) {
3224
+ // Get TGMPA class instance.
3225
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
3226
 
3227
+ parent::__construct( $skin );
 
 
3228
 
3229
+ if ( isset( $this->skin->options['install_type'] ) && 'update' === $this->skin->options['install_type'] ) {
3230
+ $this->clear_destination = true;
3231
  }
3232
 
3233
+ if ( $this->tgmpa->is_automatic ) {
3234
+ $this->activate_strings();
 
 
 
 
 
 
3235
  }
3236
 
3237
+ add_action( 'upgrader_process_complete', array( $this->tgmpa, 'populate_file_path' ) );
3238
+ }
 
 
 
 
 
 
 
 
 
 
3239
 
3240
+ /**
3241
+ * Sets the correct activation strings for the installer skin to use.
3242
+ *
3243
+ * @since 2.2.0
3244
+ */
3245
+ public function activate_strings() {
3246
+ $this->strings['activation_failed'] = __( 'Plugin activation failed.', 'tgmpa' );
3247
+ $this->strings['activation_success'] = __( 'Plugin activated successfully.', 'tgmpa' );
3248
+ }
3249
+
3250
+ /**
3251
+ * Performs the actual installation of each plugin.
3252
+ *
3253
+ * @since 2.2.0
3254
+ *
3255
+ * @see WP_Upgrader::run()
3256
+ *
3257
+ * @param array $options The installation config options.
3258
+ * @return null|array Return early if error, array of installation data on success.
3259
+ */
3260
+ public function run( $options ) {
3261
+ $result = parent::run( $options );
3262
 
3263
+ // Reset the strings in case we changed one during automatic activation.
3264
+ if ( $this->tgmpa->is_automatic ) {
3265
+ if ( 'update' === $this->skin->options['install_type'] ) {
3266
+ $this->upgrade_strings();
3267
+ } else {
3268
+ $this->install_strings();
3269
+ }
3270
  }
3271
 
3272
+ return $result;
3273
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3274
 
3275
+ /**
3276
+ * Processes the bulk installation of plugins.
3277
+ *
3278
+ * @since 2.2.0
3279
+ *
3280
+ * {@internal This is basically a near identical copy of the WP Core
3281
+ * Plugin_Upgrader::bulk_upgrade() method, with minor adjustments to deal with
3282
+ * new installs instead of upgrades.
3283
+ * For ease of future synchronizations, the adjustments are clearly commented, but no other
3284
+ * comments are added. Code style has been made to comply.}}
3285
+ *
3286
+ * @see Plugin_Upgrader::bulk_upgrade()
3287
+ * @see https://core.trac.wordpress.org/browser/tags/4.2.1/src/wp-admin/includes/class-wp-upgrader.php#L838
3288
+ * (@internal Last synced: Dec 31st 2015 against https://core.trac.wordpress.org/browser/trunk?rev=36134}}
3289
+ *
3290
+ * @param array $plugins The plugin sources needed for installation.
3291
+ * @param array $args Arbitrary passed extra arguments.
3292
+ * @return array|false Install confirmation messages on success, false on failure.
3293
+ */
3294
+ public function bulk_install( $plugins, $args = array() ) {
3295
+ // [TGMPA + ] Hook auto-activation in.
3296
+ add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3297
+
3298
+ $defaults = array(
3299
+ 'clear_update_cache' => true,
3300
+ );
3301
+ $parsed_args = wp_parse_args( $args, $defaults );
3302
 
3303
+ $this->init();
3304
+ $this->bulk = true;
3305
 
3306
+ $this->install_strings(); // [TGMPA + ] adjusted.
3307
 
3308
+ /* [TGMPA - ] $current = get_site_transient( 'update_plugins' ); */
3309
 
3310
+ /* [TGMPA - ] add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); */
3311
 
3312
+ $this->skin->header();
3313
 
3314
+ // Connect to the Filesystem first.
3315
+ $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
3316
+ if ( ! $res ) {
3317
+ $this->skin->footer();
3318
+ return false;
3319
+ }
3320
 
3321
+ $this->skin->bulk_header();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3322
 
3323
+ /*
3324
+ * Only start maintenance mode if:
3325
+ * - running Multisite and there are one or more plugins specified, OR
3326
+ * - a plugin with an update available is currently active.
3327
+ * @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.
3328
+ */
3329
+ $maintenance = ( is_multisite() && ! empty( $plugins ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3330
 
3331
+ /*
3332
+ [TGMPA - ]
3333
+ foreach ( $plugins as $plugin )
3334
+ $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) );
3335
+ */
3336
+ if ( $maintenance ) {
3337
+ $this->maintenance_mode( true );
3338
+ }
3339
 
3340
+ $results = array();
3341
+
3342
+ $this->update_count = count( $plugins );
3343
+ $this->update_current = 0;
3344
+ foreach ( $plugins as $plugin ) {
3345
+ $this->update_current++;
3346
+
3347
+ $result = $this->run(
3348
+ array(
3349
+ 'package' => $plugin, // [TGMPA + ] adjusted.
3350
+ 'destination' => \WP_PLUGIN_DIR,
3351
+ 'clear_destination' => false, // [TGMPA + ] adjusted.
3352
+ 'clear_working' => true,
3353
+ 'is_multi' => true,
3354
+ 'hook_extra' => array(
3355
+ 'plugin' => $plugin,
3356
+ ),
3357
+ )
3358
+ );
3359
 
3360
+ $results[ $plugin ] = $this->result;
 
3361
 
3362
+ // Prevent credentials auth screen from displaying multiple times.
3363
+ if ( false === $result ) {
3364
+ break;
3365
+ }
3366
+ } //end foreach $plugins
3367
 
3368
+ $this->maintenance_mode( false );
 
3369
 
3370
  /**
3371
+ * Fires when the bulk upgrader process is complete.
3372
  *
3373
+ * @since WP 3.6.0 / TGMPA 2.5.0
3374
  *
3375
+ * @param \Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might
3376
+ * be a Theme_Upgrader or Core_Upgrade instance.
3377
+ * @param array $data {
3378
+ * Array of bulk item update data.
3379
  *
3380
+ * @type string $action Type of action. Default 'update'.
3381
+ * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
3382
+ * @type bool $bulk Whether the update process is a bulk update. Default true.
3383
+ * @type array $packages Array of plugin, theme, or core packages to update.
3384
+ * }
3385
  */
3386
+ do_action( 'upgrader_process_complete', $this, array(
3387
+ 'action' => 'install', // [TGMPA + ] adjusted.
3388
+ 'type' => 'plugin',
3389
+ 'bulk' => true,
3390
+ 'plugins' => $plugins,
3391
+ ) );
3392
 
3393
+ $this->skin->bulk_footer();
3394
 
3395
+ $this->skin->footer();
3396
 
3397
+ // Cleanup our hooks, in case something else does a upgrade on this connection.
3398
+ /* [TGMPA - ] remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); */
3399
 
3400
+ // [TGMPA + ] Remove our auto-activation hook.
3401
+ remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3402
 
3403
+ // Force refresh of plugin update information.
3404
+ wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
3405
+
3406
+ return $results;
3407
+ }
3408
+
3409
+ /**
3410
+ * Handle a bulk upgrade request.
3411
+ *
3412
+ * @since 2.5.0
3413
+ *
3414
+ * @see Plugin_Upgrader::bulk_upgrade()
3415
+ *
3416
+ * @param array $plugins The local WP file_path's of the plugins which should be upgraded.
3417
+ * @param array $args Arbitrary passed extra arguments.
3418
+ * @return string|bool Install confirmation messages on success, false on failure.
3419
+ */
3420
+ public function bulk_upgrade( $plugins, $args = array() ) {
3421
+
3422
+ add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3423
 
3424
+ $result = parent::bulk_upgrade( $plugins, $args );
 
3425
 
3426
+ remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
 
 
3427
 
3428
+ return $result;
3429
+ }
3430
+
3431
+ /**
3432
+ * Abuse a filter to auto-activate plugins after installation.
3433
+ *
3434
+ * Hooked into the 'upgrader_post_install' filter hook.
3435
+ *
3436
+ * @since 2.5.0
3437
+ *
3438
+ * @param bool $bool The value we need to give back (true).
3439
+ * @return bool
3440
+ */
3441
+ public function auto_activate( $bool ) {
3442
+ // Only process the activation of installed plugins if the automatic flag is set to true.
3443
+ if ( $this->tgmpa->is_automatic ) {
3444
+ // Flush plugins cache so the headers of the newly installed plugins will be read correctly.
3445
+ wp_clean_plugins_cache();
3446
 
3447
+ // Get the installed plugin file.
3448
+ $plugin_info = $this->plugin_info();
3449
+
3450
+ // Don't try to activate on upgrade of active plugin as WP will do this already.
3451
+ if ( ! is_plugin_active( $plugin_info ) ) {
3452
+ $activate = activate_plugin( $plugin_info );
3453
+
3454
+ // Adjust the success string based on the activation result.
3455
+ $this->strings['process_success'] = $this->strings['process_success'] . "<br />\n";
3456
+
3457
+ if ( is_wp_error( $activate ) ) {
3458
+ $this->skin->error( $activate );
3459
+ $this->strings['process_success'] .= $this->strings['activation_failed'];
3460
+ } else {
3461
+ $this->strings['process_success'] .= $this->strings['activation_success'];
3462
  }
3463
  }
 
 
3464
  }
3465
+
3466
+ return $bool;
3467
  }
3468
  }
3469
+ }
3470
 
3471
+ if ( ! class_exists( '\WPMailSMTP\TGMPA_Bulk_Installer_Skin', false ) ) {
3472
 
3473
+ /**
3474
+ * Installer skin to set strings for the bulk plugin installations..
3475
+ *
3476
+ * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple
3477
+ * plugins.
3478
+ *
3479
+ * @since 2.2.0
3480
+ *
3481
+ * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer_Skin to
3482
+ * TGMPA_Bulk_Installer_Skin.
3483
+ * This was done to prevent backward compatibility issues with v2.3.6.}}
3484
+ *
3485
+ * @see https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
3486
+ *
3487
+ * @package TGM-Plugin-Activation
3488
+ * @author Thomas Griffin
3489
+ * @author Gary Jones
3490
+ */
3491
+ class TGMPA_Bulk_Installer_Skin extends \Bulk_Upgrader_Skin {
3492
  /**
3493
+ * Holds plugin info for each individual plugin installation.
 
 
 
3494
  *
3495
  * @since 2.2.0
3496
  *
3497
+ * @var array
3498
+ */
3499
+ public $plugin_info = array();
3500
+
3501
+ /**
3502
+ * Holds names of plugins that are undergoing bulk installations.
3503
  *
3504
+ * @since 2.2.0
3505
  *
3506
+ * @var array
 
 
3507
  */
3508
+ public $plugin_names = array();
 
 
 
 
 
 
 
 
3509
 
3510
+ /**
3511
+ * Integer to use for iteration through each plugin installation.
3512
+ *
3513
+ * @since 2.2.0
3514
+ *
3515
+ * @var integer
3516
+ */
3517
+ public $i = 0;
3518
 
3519
+ /**
3520
+ * TGMPA instance
3521
+ *
3522
+ * @since 2.5.0
3523
+ *
3524
+ * @var object
3525
+ */
3526
+ protected $tgmpa;
3527
 
3528
+ /**
3529
+ * Constructor. Parses default args with new ones and extracts them for use.
3530
+ *
3531
+ * @since 2.2.0
3532
+ *
3533
+ * @param array $args Arguments to pass for use within the class.
3534
+ */
3535
+ public function __construct( $args = array() ) {
3536
+ // Get TGMPA class instance.
3537
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['wp_mail_smtp_tgmpa'] ), 'get_instance' ) );
3538
+
3539
+ // Parse default and new args.
3540
+ $defaults = array(
3541
+ 'url' => '',
3542
+ 'nonce' => '',
3543
+ 'names' => array(),
3544
+ 'install_type' => 'install',
3545
+ );
3546
+ $args = wp_parse_args( $args, $defaults );
3547
 
3548
+ // Set plugin names to $this->plugin_names property.
3549
+ $this->plugin_names = $args['names'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3550
 
3551
+ // Extract the new args.
3552
+ parent::__construct( $args );
3553
+ }
3554
 
3555
+ /**
3556
+ * Sets install skin strings for each individual plugin.
3557
+ *
3558
+ * Checks to see if the automatic activation flag is set and uses the
3559
+ * the proper strings accordingly.
3560
+ *
3561
+ * @since 2.2.0
3562
+ */
3563
+ public function add_strings() {
3564
+ if ( 'update' === $this->options['install_type'] ) {
3565
+ parent::add_strings();
3566
+ /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3567
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3568
+ } else {
3569
+ /* translators: 1: plugin name, 2: error message. */
3570
+ $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while installing %1$s: <strong>%2$s</strong>.', 'tgmpa' );
3571
+ /* translators: 1: plugin name. */
3572
+ $this->upgrader->strings['skin_update_failed'] = __( 'The installation of %1$s failed.', 'tgmpa' );
3573
 
3574
+ if ( $this->tgmpa->is_automatic ) {
3575
+ // Automatic activation strings.
3576
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3577
+ /* translators: 1: plugin name. */
3578
+ $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed and activated successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3579
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations and activations have been completed.', 'tgmpa' );
 
 
 
 
 
3580
  /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3581
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing and Activating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3582
  } else {
3583
+ // Default installation strings.
3584
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3585
  /* translators: 1: plugin name. */
3586
+ $this->upgrader->strings['skin_update_successful'] = esc_html__( '%1$s installed successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3587
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations have been completed.', 'tgmpa' );
3588
+ /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3589
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3590
  }
3591
  }
3592
+ }
3593
 
3594
+ /**
3595
+ * Outputs the header strings and necessary JS before each plugin installation.
3596
+ *
3597
+ * @since 2.2.0
3598
+ *
3599
+ * @param string $title Unused in this implementation.
3600
+ */
3601
+ public function before( $title = '' ) {
3602
+ if ( empty( $title ) ) {
3603
+ $title = esc_html( $this->plugin_names[ $this->i ] );
 
 
3604
  }
3605
+ parent::before( $title );
3606
+ }
3607
 
3608
+ /**
3609
+ * Outputs the footer strings and necessary JS after each plugin installation.
3610
+ *
3611
+ * Checks for any errors and outputs them if they exist, else output
3612
+ * success strings.
3613
+ *
3614
+ * @since 2.2.0
3615
+ *
3616
+ * @param string $title Unused in this implementation.
3617
+ */
3618
+ public function after( $title = '' ) {
3619
+ if ( empty( $title ) ) {
3620
+ $title = esc_html( $this->plugin_names[ $this->i ] );
 
 
 
 
3621
  }
3622
+ parent::after( $title );
3623
 
3624
+ $this->i++;
3625
+ }
 
 
 
 
 
 
3626
 
3627
+ /**
3628
+ * Outputs links after bulk plugin installation is complete.
3629
+ *
3630
+ * @since 2.2.0
3631
+ */
3632
+ public function bulk_footer() {
3633
+ // Serve up the string to say installations (and possibly activations) are complete.
3634
+ parent::bulk_footer();
3635
 
3636
+ // Flush plugins cache so we can make sure that the installed plugins list is always up to date.
3637
+ wp_clean_plugins_cache();
3638
 
3639
+ $this->tgmpa->show_tgmpa_version();
 
3640
 
3641
+ // Display message based on if all plugins are now active or not.
3642
+ $update_actions = array();
 
 
 
 
 
 
 
 
3643
 
3644
+ if ( $this->tgmpa->is_tgmpa_complete() ) {
3645
+ // All plugins are active, so we display the complete string and hide the menu to protect users.
3646
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
3647
+ $update_actions['dashboard'] = sprintf(
3648
+ esc_html( $this->tgmpa->strings['complete'] ),
3649
+ '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>'
3650
+ );
3651
+ } else {
3652
+ $update_actions['tgmpa_page'] = '<a href="' . esc_url( $this->tgmpa->get_tgmpa_url() ) . '" target="_parent">' . esc_html( $this->tgmpa->strings['return'] ) . '</a>';
 
 
 
 
3653
  }
3654
 
 
 
3655
  /**
3656
+ * Filter the list of action links available following bulk plugin installs/updates.
3657
  *
3658
+ * @since 2.5.0
 
 
 
 
 
 
 
 
 
 
 
3659
  *
3660
+ * @param array $update_actions Array of plugin action links.
3661
+ * @param array $plugin_info Array of information for the last-handled plugin.
 
3662
  */
3663
+ $update_actions = apply_filters( 'wpms_tgmpa_update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );
3664
+
3665
+ if ( ! empty( $update_actions ) ) {
3666
+ $this->feedback( implode( ' | ', (array) $update_actions ) );
3667
  }
3668
  }
3669
+
3670
+ /* *********** DEPRECATED METHODS *********** */
3671
+
3672
+ /**
3673
+ * Flush header output buffer.
3674
+ *
3675
+ * @since 2.2.0
3676
+ * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3677
+ * @see Bulk_Upgrader_Skin::flush_output()
3678
+ */
3679
+ public function before_flush_output() {
3680
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3681
+ $this->flush_output();
3682
+ }
3683
+
3684
+ /**
3685
+ * Flush footer output buffer and iterate $this->i to make sure the
3686
+ * installation strings reference the correct plugin.
3687
+ *
3688
+ * @since 2.2.0
3689
+ * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3690
+ * @see Bulk_Upgrader_Skin::flush_output()
3691
+ */
3692
+ public function after_flush_output() {
3693
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3694
+ $this->flush_output();
3695
+ $this->i++;
3696
+ }
3697
  }
3698
  }
3699
  }
3700
  }
3701
 
 
3702
 
3703
+ /**
3704
+ * Generic utilities for TGMPA.
3705
+ *
3706
+ * All methods are static, poor-dev name-spacing class wrapper.
3707
+ *
3708
+ * Class was called TGM_Utils in 2.5.0 but renamed TGMPA_Utils in 2.5.1 as this was conflicting with Soliloquy.
3709
+ *
3710
+ * @since 2.5.0
3711
+ *
3712
+ * @package TGM-Plugin-Activation
3713
+ * @author Juliette Reinders Folmer
3714
+ */
3715
+ class TGMPA_Utils {
3716
  /**
3717
+ * Whether the PHP filter extension is enabled.
3718
  *
3719
+ * @see http://php.net/book.filter
3720
  *
3721
+ * @since 2.5.0
3722
+ *
3723
+ * @static
3724
+ *
3725
+ * @var bool $has_filters True is the extension is enabled.
3726
+ */
3727
+ public static $has_filters;
3728
+
3729
+ /**
3730
+ * Wrap an arbitrary string in <em> tags. Meant to be used in combination with array_map().
3731
  *
3732
  * @since 2.5.0
3733
  *
3734
+ * @static
3735
+ *
3736
+ * @param string $string Text to be wrapped.
3737
+ * @return string
3738
  */
3739
+ public static function wrap_in_em( $string ) {
3740
+ return '<em>' . wp_kses_post( $string ) . '</em>';
3741
+ }
 
 
 
 
 
 
 
 
 
 
3742
 
3743
+ /**
3744
+ * Wrap an arbitrary string in <strong> tags. Meant to be used in combination with array_map().
3745
+ *
3746
+ * @since 2.5.0
3747
+ *
3748
+ * @static
3749
+ *
3750
+ * @param string $string Text to be wrapped.
3751
+ * @return string
3752
+ */
3753
+ public static function wrap_in_strong( $string ) {
3754
+ return '<strong>' . wp_kses_post( $string ) . '</strong>';
3755
+ }
3756
 
3757
+ /**
3758
+ * Helper function: Validate a value as boolean
3759
+ *
3760
+ * @since 2.5.0
3761
+ *
3762
+ * @static
3763
+ *
3764
+ * @param mixed $value Arbitrary value.
3765
+ * @return bool
3766
+ */
3767
+ public static function validate_bool( $value ) {
3768
+ if ( ! isset( self::$has_filters ) ) {
3769
+ self::$has_filters = extension_loaded( 'filter' );
3770
  }
3771
 
3772
+ if ( self::$has_filters ) {
3773
+ return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
3774
+ } else {
3775
+ return self::emulate_filter_bool( $value );
3776
+ }
3777
+ }
 
 
 
 
 
 
 
 
3778
 
3779
+ /**
3780
+ * Helper function: Cast a value to bool
3781
+ *
3782
+ * @since 2.5.0
3783
+ *
3784
+ * @static
3785
+ *
3786
+ * @param mixed $value Value to cast.
3787
+ * @return bool
3788
+ */
3789
+ protected static function emulate_filter_bool( $value ) {
3790
+ // @codingStandardsIgnoreStart
3791
+ static $true = array(
3792
+ '1',
3793
+ 'true', 'True', 'TRUE',
3794
+ 'y', 'Y',
3795
+ 'yes', 'Yes', 'YES',
3796
+ 'on', 'On', 'ON',
3797
+ );
3798
+ static $false = array(
3799
+ '0',
3800
+ 'false', 'False', 'FALSE',
3801
+ 'n', 'N',
3802
+ 'no', 'No', 'NO',
3803
+ 'off', 'Off', 'OFF',
3804
+ );
3805
+ // @codingStandardsIgnoreEnd
3806
+
3807
+ if ( is_bool( $value ) ) {
3808
+ return $value;
3809
+ } elseif ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) {
3810
+ return (bool) $value;
3811
+ } elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) {
3812
+ return (bool) $value;
3813
+ } elseif ( is_string( $value ) ) {
3814
+ $value = trim( $value );
3815
+ if ( in_array( $value, $true, true ) ) {
3816
+ return true;
3817
+ } elseif ( in_array( $value, $false, true ) ) {
3818
+ return false;
3819
  } else {
3820
+ return false;
3821
  }
3822
  }
3823
 
3824
+ return false;
3825
+ }
3826
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
wp_mail_smtp.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /**
3
  * Plugin Name: WP Mail SMTP
4
- * Version: 1.3.1
5
  * Plugin URI: https://wpforms.com/
6
  * Description: Reconfigures the wp_mail() function to use SMTP instead of mail() and creates an options page to manage the settings.
7
  * Author: WPForms
@@ -17,7 +17,7 @@
17
  * http://www.gnu.org/licenses/gpl.txt
18
  */
19
 
20
- define( 'WPMS_PLUGIN_VER', '1.3.1' );
21
  define( 'WPMS_PHP_VER', '5.3' );
22
 
23
  /**
1
  <?php
2
  /**
3
  * Plugin Name: WP Mail SMTP
4
+ * Version: 1.3.2
5
  * Plugin URI: https://wpforms.com/
6
  * Description: Reconfigures the wp_mail() function to use SMTP instead of mail() and creates an options page to manage the settings.
7
  * Author: WPForms
17
  * http://www.gnu.org/licenses/gpl.txt
18
  */
19
 
20
+ define( 'WPMS_PLUGIN_VER', '1.3.2' );
21
  define( 'WPMS_PHP_VER', '5.3' );
22
 
23
  /**