ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-tgm-plugin-activation.php (155996B)


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