TGM_Plugin_Activation

Automatic plugin installation and activation library.

Defined (1)

The class is defined in the following location(s).

/includes/class-tgm-plugin-activation.php  
  1. class TGM_Plugin_Activation { 
  2.  
  3. /** 
  4. * Holds a copy of itself, so it can be referenced by the class name. 
  5. * @since 1.0.0 
  6. * @var TGM_Plugin_Activation 
  7. */ 
  8. static $instance; 
  9.  
  10. /** 
  11. * Holds arrays of plugin details. 
  12. * @since 1.0.0 
  13. * @var array 
  14. */ 
  15. public $plugins = array(); 
  16.  
  17. /** 
  18. * Parent menu slug for plugins page. 
  19. * @since 2.2.0 
  20. * @var string Parent menu slug. Defaults to 'themes.php'. 
  21. */ 
  22. public $parent_menu_slug = 'themes.php'; 
  23.  
  24. /** 
  25. * Parent URL slug for URL references. 
  26. * This is useful if you want to place the custom plugins page as a 
  27. * submenu item under a custom parent menu. 
  28. * @since 2.2.0 
  29. * @var string Parent URL slug. Defaults to 'themes.php'. 
  30. */ 
  31. public $parent_url_slug = 'themes.php'; 
  32.  
  33. /** 
  34. * Name of the querystring argument for the admin page. 
  35. * @since 1.0.0 
  36. * @var string 
  37. */ 
  38. public $menu = 'install-required-plugins'; 
  39.  
  40. /** 
  41. * Text domain for localization support. 
  42. * @since 1.1.0 
  43. * @var string 
  44. */ 
  45. public $domain = 'tgmpa'; 
  46.  
  47. /** 
  48. * Default absolute path to folder containing pre-packaged plugin zip files. 
  49. * @since 2.0.0 
  50. * @var string Absolute path prefix to packaged zip file location. Default is empty string. 
  51. */ 
  52. public $default_path = ''; 
  53.  
  54. /** 
  55. * Flag to show admin notices or not. 
  56. * @since 2.1.0 
  57. * @var boolean 
  58. */ 
  59. public $has_notices = true; 
  60.  
  61. /** 
  62. * Flag to set automatic activation of plugins. Off by default. 
  63. * @since 2.2.0 
  64. * @var boolean 
  65. */ 
  66. public $is_automatic = false; 
  67.  
  68. /** 
  69. * Optional message to display before the plugins table. 
  70. * @since 2.2.0 
  71. * @var string Message filtered by wp_kses_post(). Default is empty string. 
  72. */ 
  73. public $message = ''; 
  74.  
  75. /** 
  76. * Holds configurable array of strings. 
  77. * Default values are added in the constructor. 
  78. * @since 2.0.0 
  79. * @var array 
  80. */ 
  81. public $strings = array(); 
  82.  
  83. /** 
  84. * Adds a reference of this object to $instance, populates default strings,  
  85. * does the tgmpa_init action hook, and hooks in the interactions to init. 
  86. * @since 1.0.0 
  87. * @see TGM_Plugin_Activation::init() 
  88. */ 
  89. public function __construct() { 
  90.  
  91. self::$instance =& $this; 
  92.  
  93. $this->strings = array( 
  94. 'page_title' => __( 'Install Required Plugins', $this->domain ),  
  95. 'menu_title' => __( 'Install Plugins', $this->domain ),  
  96. 'installing' => __( 'Installing Plugin: %s', $this->domain ),  
  97. 'oops' => __( 'Something went wrong.', $this->domain ),  
  98. 'notice_can_install_required' => _n_noop( 'This theme requires the following plugin: %1$s.', 'This theme requires the following plugins: %1$s.' ),  
  99. 'notice_can_install_recommended' => _n_noop( 'This theme recommends the following plugin: %1$s.', 'This theme recommends the following plugins: %1$s.' ),  
  100. 'notice_cannot_install' => _n_noop( 'Sorry, but you do not have the correct permissions to install the %s plugin. Contact the administrator of this site for help on getting the plugin installed.', 'Sorry, but you do not have the correct permissions to install the %s plugins. Contact the administrator of this site for help on getting the plugins installed.' ),  
  101. 'notice_can_activate_required' => _n_noop( 'The following required plugin is currently inactive: %1$s.', 'The following required plugins are currently inactive: %1$s.' ),  
  102. 'notice_can_activate_recommended' => _n_noop( 'The following recommended plugin is currently inactive: %1$s.', 'The following recommended plugins are currently inactive: %1$s.' ),  
  103. 'notice_cannot_activate' => _n_noop( 'Sorry, but you do not have the correct permissions to activate the %s plugin. Contact the administrator of this site for help on getting the plugin activated.', 'Sorry, but you do not have the correct permissions to activate the %s plugins. Contact the administrator of this site for help on getting the plugins activated.' ),  
  104. 'notice_ask_to_update' => _n_noop( 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.', 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.' ),  
  105. 'notice_cannot_update' => _n_noop( 'Sorry, but you do not have the correct permissions to update the %s plugin. Contact the administrator of this site for help on getting the plugin updated.', 'Sorry, but you do not have the correct permissions to update the %s plugins. Contact the administrator of this site for help on getting the plugins updated.' ),  
  106. 'install_link' => _n_noop( 'Begin installing plugin', 'Begin installing plugins' ),  
  107. 'activate_link' => _n_noop( 'Activate installed plugin', 'Activate installed plugins' ),  
  108. 'return' => __( 'Return to Required Plugins Installer', $this->domain ),  
  109. 'plugin_activated' => __( 'Plugin activated successfully.', $this->domain ),  
  110. 'complete' => __( 'All plugins installed and activated successfully. %1$s', $this->domain ),  
  111. ); 
  112.  
  113. /** Annouce that the class is ready, and pass the object (for advanced use) */ 
  114. do_action_ref_array( 'tgmpa_init', array( &$this ) ); 
  115.  
  116. /** When the rest of WP has loaded, kick-start the rest of the class */ 
  117. add_action( 'init', array( &$this, 'init' ) ); 
  118.  
  119.  
  120. /** 
  121. * Initialise the interactions between this class and WordPress. 
  122. * Hooks in three new methods for the class: admin_menu, notices and styles. 
  123. * @since 2.0.0 
  124. * @see TGM_Plugin_Activation::admin_menu() 
  125. * @see TGM_Plugin_Activation::notices() 
  126. * @see TGM_Plugin_Activation::styles() 
  127. */ 
  128. public function init() { 
  129.  
  130. do_action( 'tgmpa_register' ); 
  131. /** After this point, the plugins should be registered and the configuration set */ 
  132.  
  133. /** Proceed only if we have plugins to handle */ 
  134. if ( $this->plugins ) { 
  135. $sorted = array(); // Prepare variable for sorting 
  136.  
  137. foreach ( $this->plugins as $plugin ) 
  138. $sorted[] = $plugin['name']; 
  139.  
  140. array_multisort( $sorted, SORT_ASC, $this->plugins ); // Sort plugins alphabetically by name 
  141.  
  142. add_action( 'admin_menu', array( &$this, 'admin_menu' ) ); 
  143. add_action( 'admin_head', array( &$this, 'dismiss' ) ); 
  144. add_filter( 'install_plugin_complete_actions', array( &$this, 'actions' ) ); 
  145.  
  146. /** Load admin bar in the header to remove flash when installing plugins */ 
  147. if ( $this->is_tgmpa_page() ) { 
  148. remove_action( 'wp_footer', 'wp_admin_bar_render', 1000 ); 
  149. remove_action( 'admin_footer', 'wp_admin_bar_render', 1000 ); 
  150. add_action( 'wp_head', 'wp_admin_bar_render', 1000 ); 
  151. add_action( 'admin_head', 'wp_admin_bar_render', 1000 ); 
  152.  
  153. if ( $this->has_notices ) { 
  154. add_action( 'admin_notices', array( &$this, 'notices' ) ); 
  155. add_action( 'admin_init', array( &$this, 'admin_init' ), 1 ); 
  156. add_action( 'admin_enqueue_scripts', array( &$this, 'thickbox' ) ); 
  157. add_action( 'switch_theme', array( &$this, 'update_dismiss' ) ); 
  158.  
  159. /** Setup the force activation hook */ 
  160. foreach ( $this->plugins as $plugin ) { 
  161. if ( isset( $plugin['force_activation'] ) && true === $plugin['force_activation'] ) { 
  162. add_action( 'admin_init', array( &$this, 'force_activation' ) ); 
  163. break; 
  164.  
  165. /** Setup the force deactivation hook */ 
  166. foreach ( $this->plugins as $plugin ) { 
  167. if ( isset( $plugin['force_deactivation'] ) && true === $plugin['force_deactivation'] ) { 
  168. add_action( 'switch_theme', array( &$this, 'force_deactivation' ) ); 
  169. break; 
  170.  
  171.  
  172. /** 
  173. * Handles calls to show plugin information via links in the notices. 
  174. * We get the links in the admin notices to point to the TGMPA page, rather 
  175. * than the typical plugin-install.php file, so we can prepare everything 
  176. * beforehand. 
  177. * WP doesn't make it easy to show the plugin information in the thickbox - 
  178. * here we have to require a file that includes a function that does the 
  179. * main work of displaying it, enqueue some styles, set up some globals and 
  180. * finally call that function before exiting. 
  181. * Down right easy once you know how... 
  182. * @since 2.1.0 
  183. * @global string $tab Used as iframe div class names, helps with styling 
  184. * @global string $body_id Used as the iframe body ID, helps with styling 
  185. * @return null Returns early if not the TGMPA page. 
  186. */ 
  187. public function admin_init() { 
  188.  
  189. if ( ! $this->is_tgmpa_page() ) 
  190. return; 
  191.  
  192. if ( isset( $_REQUEST['tab'] ) && 'plugin-information' == $_REQUEST['tab'] ) { 
  193. require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for install_plugin_information() 
  194.  
  195. wp_enqueue_style( 'plugin-install' ); 
  196.  
  197. global $tab, $body_id; 
  198. $body_id = $tab = 'plugin-information'; 
  199.  
  200. install_plugin_information(); 
  201.  
  202. exit; 
  203.  
  204.  
  205. /** 
  206. * Enqueues thickbox scripts/styles for plugin info. 
  207. * Thickbox is not automatically included on all admin pages, so we must 
  208. * manually enqueue it for those pages. 
  209. * Thickbox is only loaded if the user has not dismissed the admin 
  210. * notice or if there are any plugins left to install and activate. 
  211. * @since 2.1.0 
  212. */ 
  213. public function thickbox() { 
  214.  
  215. if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', true ) ) 
  216. add_thickbox(); 
  217.  
  218.  
  219. /** 
  220. * Adds submenu page under 'Appearance' tab. 
  221. * This method adds the submenu page letting users know that a required 
  222. * plugin needs to be installed. 
  223. * This page disappears once the plugin has been installed and activated. 
  224. * @since 1.0.0 
  225. * @see TGM_Plugin_Activation::init() 
  226. * @see TGM_Plugin_Activation::install_plugins_page() 
  227. */ 
  228. public function admin_menu() { 
  229.  
  230. // Make sure privileges are correct to see the page 
  231. if ( ! current_user_can( 'install_plugins' ) ) 
  232. return; 
  233.  
  234. $this->populate_file_path(); 
  235.  
  236. foreach ( $this->plugins as $plugin ) { 
  237. if ( ! is_plugin_active( $plugin['file_path'] ) ) { 
  238.  
  239. /** Theme Check Fix 
  240. add_submenu_page( 
  241. $this->parent_menu_slug, // Parent menu slug 
  242. $this->strings['page_title'], // Page title 
  243. $this->strings['menu_title'], // Menu title 
  244. 'edit_theme_options', // Capability 
  245. $this->menu, // Menu slug 
  246. array( &$this, 'install_plugins_page' ) // Callback 
  247. ); 
  248. */ 
  249.  
  250. add_theme_page( 
  251. $this->strings['page_title'], // Page title 
  252. $this->strings['menu_title'], // Menu title 
  253. 'edit_theme_options', // Capability 
  254. $this->menu, // Menu slug 
  255. array( &$this, 'install_plugins_page' ) // Callback 
  256. ); 
  257.  
  258. break; 
  259.  
  260.  
  261. /** 
  262. * Echoes plugin installation form. 
  263. * This method is the callback for the admin_menu method function. 
  264. * This displays the admin page and form area where the user can select to install and activate the plugin. 
  265. * @since 1.0.0 
  266. * @return null Aborts early if we're processing a plugin installation action 
  267. */ 
  268. public function install_plugins_page() { 
  269.  
  270. /** Store new instance of plugin table in object */ 
  271. $plugin_table = new TGMPA_List_Table; 
  272.  
  273. /** Return early if processing a plugin installation action */ 
  274. if ( isset( $_POST[sanitize_key( 'action' )] ) && 'tgmpa-bulk-install' == $_POST[sanitize_key( 'action' )] && $plugin_table->process_bulk_actions() || $this->do_plugin_install() ) 
  275. return; 
  276.  
  277. ?> 
  278. <div class="tgmpa wrap"> 
  279.  
  280. <?php screen_icon( apply_filters( 'tgmpa_default_screen_icon', 'themes' ) ); ?> 
  281. <h2><?php echo esc_html( get_admin_page_title() ); ?></h2> 
  282. <?php $plugin_table->prepare_items(); ?> 
  283.  
  284. <?php if ( isset( $this->message ) ) _e( wp_kses_post( $this->message ), $this->domain ); ?> 
  285.  
  286. <form id="tgmpa-plugins" action="" method="post"> 
  287. <input type="hidden" name="tgmpa-page" value="<?php echo $this->menu; ?>" /> 
  288. <?php $plugin_table->display(); ?> 
  289. </form> 
  290.  
  291. </div> 
  292. <?php 
  293.  
  294.  
  295. /** 
  296. * Installs a plugin or activates a plugin depending on the hover 
  297. * link clicked by the user. 
  298. * Checks the $_GET variable to see which actions have been 
  299. * passed and responds with the appropriate method. 
  300. * Uses WP_Filesystem to process and handle the plugin installation 
  301. * method. 
  302. * @since 1.0.0 
  303. * @uses WP_Filesystem 
  304. * @uses WP_Error 
  305. * @uses WP_Upgrader 
  306. * @uses Plugin_Upgrader 
  307. * @uses Plugin_Installer_Skin 
  308. * @return boolean True on success, false on failure 
  309. */ 
  310. protected function do_plugin_install() { 
  311.  
  312. /** All plugin information will be stored in an array for processing */ 
  313. $plugin = array(); 
  314.  
  315. /** Checks for actions from hover links to process the installation */ 
  316. if ( isset( $_GET[sanitize_key( 'plugin' )] ) && ( isset( $_GET[sanitize_key( 'tgmpa-install' )] ) && 'install-plugin' == $_GET[sanitize_key( 'tgmpa-install' )] ) ) { 
  317. check_admin_referer( 'tgmpa-install' ); 
  318.  
  319. $plugin['name'] = $_GET[sanitize_key( 'plugin_name' )]; // Plugin name 
  320. $plugin['slug'] = $_GET[sanitize_key( 'plugin' )]; // Plugin slug 
  321. $plugin['source'] = $_GET[sanitize_key( 'plugin_source' )]; // Plugin source 
  322.  
  323. /** Pass all necessary information via URL if WP_Filesystem is needed */ 
  324. $url = wp_nonce_url( 
  325. add_query_arg( 
  326. array( 
  327. 'page' => $this->menu,  
  328. 'plugin' => $plugin['slug'],  
  329. 'plugin_name' => $plugin['name'],  
  330. 'plugin_source' => $plugin['source'],  
  331. 'tgmpa-install' => 'install-plugin',  
  332. ),  
  333. admin_url( $this->parent_url_slug ) 
  334. ),  
  335. 'tgmpa-install' 
  336. ); 
  337. $method = ''; // Leave blank so WP_Filesystem can populate it as necessary 
  338. $fields = array( sanitize_key( 'tgmpa-install' ) ); // Extra fields to pass to WP_Filesystem 
  339.  
  340. if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $fields ) ) ) 
  341. return true; 
  342.  
  343. if ( ! WP_Filesystem( $creds ) ) { 
  344. request_filesystem_credentials( $url, $method, true, false, $fields ); // Setup WP_Filesystem 
  345. return true; 
  346.  
  347. require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for plugins_api 
  348. require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Need for upgrade classes 
  349.  
  350. /** Set plugin source to WordPress API link if available */ 
  351. if ( isset( $plugin['source'] ) && 'repo' == $plugin['source'] ) { 
  352. $api = plugins_api( 'plugin_information', array( 'slug' => $plugin['slug'], 'fields' => array( 'sections' => false ) ) ); 
  353.  
  354. if ( is_wp_error( $api ) ) 
  355. wp_die( $this->strings['oops'] . var_dump( $api ) ); 
  356.  
  357. if ( isset( $api->download_link ) ) 
  358. $plugin['source'] = $api->download_link; 
  359.  
  360. /** Set type, based on whether the source starts with http:// or https:// */ 
  361. $type = preg_match( '|^http(s)?://|', $plugin['source'] ) ? 'web' : 'upload'; 
  362.  
  363. /** Prep variables for Plugin_Installer_Skin class */ 
  364. $title = sprintf( $this->strings['installing'], $plugin['name'] ); 
  365. $url = add_query_arg( array( 'action' => 'install-plugin', 'plugin' => $plugin['slug'] ), 'update.php' ); 
  366. if ( isset( $_GET['from'] ) ) 
  367. $url .= add_query_arg( 'from', urlencode( stripslashes( $_GET['from'] ) ), $url ); 
  368.  
  369. $nonce = 'install-plugin_' . $plugin['slug']; 
  370.  
  371. /** Prefix a default path to pre-packaged plugins */ 
  372. $source = ( 'upload' == $type ) ? $this->default_path . $plugin['source'] : $plugin['source']; 
  373.  
  374. /** Create a new instance of Plugin_Upgrader */ 
  375. $upgrader = new Plugin_Upgrader( $skin = new Plugin_Installer_Skin( compact( 'type', 'title', 'url', 'nonce', 'plugin', 'api' ) ) ); 
  376.  
  377. /** Perform the action and install the plugin from the $source urldecode() */ 
  378. $upgrader->install( $source ); 
  379.  
  380. /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */ 
  381. wp_cache_flush(); 
  382.  
  383. /** Only activate plugins if the config option is set to true */ 
  384. if ( $this->is_automatic ) { 
  385. $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method 
  386. $activate = activate_plugin( $plugin_activate ); // Activate the plugin 
  387. $this->populate_file_path(); // Re-populate the file path now that the plugin has been installed and activated 
  388.  
  389. if ( is_wp_error( $activate ) ) { 
  390. echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>'; 
  391. echo '<p><a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '" title="' . esc_attr( $this->strings['return'] ) . '" target="_parent">' . __( 'Return to Required Plugins Installer', $this->domain ) . '</a></p>'; 
  392. return true; // End it here if there is an error with automatic activation 
  393. else { 
  394. echo '<p>' . $this->strings['plugin_activated'] . '</p>'; 
  395.  
  396. /** Display message based on if all plugins are now active or not */ 
  397. $complete = array(); 
  398. foreach ( $this->plugins as $plugin ) { 
  399. if ( ! is_plugin_active( $plugin['file_path'] ) ) { 
  400. echo '<p><a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '" title="' . esc_attr( $this->strings['return'] ) . '" target="_parent">' . __( $this->strings['return'], $this->domain ) . '</a></p>'; 
  401. $complete[] = $plugin; 
  402. break; 
  403. /** Nothing to store */ 
  404. else { 
  405. $complete[] = ''; 
  406.  
  407. /** Filter out any empty entries */ 
  408. $complete = array_filter( $complete ); 
  409.  
  410. /** All plugins are active, so we display the complete string and hide the plugin menu */ 
  411. if ( empty( $complete ) ) { 
  412. echo '<p>' . sprintf( $this->strings['complete'], '<a href="' . admin_url() . '" title="' . __( 'Return to the Dashboard', $this->domain ) . '">' . __( 'Return to the Dashboard', $this->domain ) . '</a>' ) . '</p>'; 
  413. echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>'; 
  414.  
  415. return true; 
  416. /** Checks for actions from hover links to process the activation */ 
  417. elseif ( isset( $_GET[sanitize_key( 'plugin' )] ) && ( isset( $_GET[sanitize_key( 'tgmpa-activate' )] ) && 'activate-plugin' == $_GET[sanitize_key( 'tgmpa-activate' )] ) ) { 
  418. check_admin_referer( 'tgmpa-activate', 'tgmpa-activate-nonce' ); 
  419.  
  420. /** Populate $plugin array with necessary information */ 
  421. $plugin['name'] = $_GET[sanitize_key( 'plugin_name' )]; 
  422. $plugin['slug'] = $_GET[sanitize_key( 'plugin' )]; 
  423. $plugin['source'] = $_GET[sanitize_key( 'plugin_source' )]; 
  424.  
  425. $plugin_data = get_plugins( '/' . $plugin['slug'] ); // Retrieve all plugins 
  426. $plugin_file = array_keys( $plugin_data ); // Retrieve all plugin files from installed plugins 
  427. $plugin_to_activate = $plugin['slug'] . '/' . $plugin_file[0]; // Match plugin slug with appropriate plugin file 
  428. $activate = activate_plugin( $plugin_to_activate ); // Activate the plugin 
  429.  
  430. if ( is_wp_error( $activate ) ) { 
  431. echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>'; 
  432. echo '<p><a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '" title="' . esc_attr( $this->strings['return'] ) . '" target="_parent">' . __( $this->strings['return'], $this->domain ) . '</a></p>'; 
  433. return true; // End it here if there is an error with activation 
  434. else { 
  435. /** Make sure message doesn't display again if bulk activation is performed immediately after a single activation */ 
  436. if ( ! isset( $_POST[sanitize_key( 'action' )] ) ) { 
  437. $msg = sprintf( __( 'The following plugin was activated successfully: %s.', $this->domain ), '<strong>' . $plugin['name'] . '</strong>' ); 
  438. echo '<div id="message" class="updated"><p>' . $msg . '</p></div>'; 
  439.  
  440. return false; 
  441.  
  442.  
  443. /** 
  444. * Echoes required plugin notice. 
  445. * Outputs a message telling users that a specific plugin is required for 
  446. * their theme. If appropriate, it includes a link to the form page where 
  447. * users can install and activate the plugin. 
  448. * @since 1.0.0 
  449. * @global object $current_screen 
  450. * @return null Returns early if we're on the Install page 
  451. */ 
  452. public function notices() { 
  453.  
  454. global $current_screen; 
  455.  
  456. /** Remove nag on the install page */ 
  457. if ( $this->is_tgmpa_page() ) 
  458. return; 
  459.  
  460. $installed_plugins = get_plugins(); // Retrieve a list of all the plugins 
  461. $this->populate_file_path(); 
  462.  
  463. $message = array(); // Store the messages in an array to be outputted after plugins have looped through 
  464. $install_link = false; // Set to false, change to true in loop if conditions exist, used for action link 'install' 
  465. $install_link_count = 0; // Used to determine plurality of install action link text 
  466. $activate_link = false; // Set to false, change to true in loop if conditions exist, used for action link 'activate' 
  467. $activate_link_count = 0; // Used to determine plurality of activate action link text 
  468.  
  469. foreach ( $this->plugins as $plugin ) { 
  470. /** If the plugin is installed and active, check for minimum version argument before moving forward */ 
  471. if ( is_plugin_active( $plugin['file_path'] ) ) { 
  472. /** A minimum version has been specified */ 
  473. if ( isset( $plugin['version'] ) ) { 
  474. if ( isset( $installed_plugins[$plugin['file_path']]['Version'] ) ) { 
  475. /** If the current version is less than the minimum required version, we display a message */ 
  476. if ( version_compare( $installed_plugins[$plugin['file_path']]['Version'], $plugin['version'], '<' ) ) { 
  477. if ( current_user_can( 'install_plugins' ) ) 
  478. $message['notice_ask_to_update'][] = $plugin['name']; 
  479. else 
  480. $message['notice_cannot_update'][] = $plugin['name']; 
  481. /** Can't find the plugin, so iterate to the next condition */ 
  482. else { 
  483. continue; 
  484. /** No minimum version specified, so iterate over the plugin */ 
  485. else { 
  486. continue; 
  487.  
  488. /** Not installed */ 
  489. if ( ! isset( $installed_plugins[$plugin['file_path']] ) ) { 
  490. $install_link = true; // We need to display the 'install' action link 
  491. $install_link_count++; // Increment the install link count 
  492. if ( current_user_can( 'install_plugins' ) ) { 
  493. if ( $plugin['required'] ) 
  494. $message['notice_can_install_required'][] = $plugin['name']; 
  495. /** This plugin is only recommended */ 
  496. else 
  497. $message['notice_can_install_recommended'][] = $plugin['name']; 
  498. /** Need higher privileges to install the plugin */ 
  499. else { 
  500. $message['notice_cannot_install'][] = $plugin['name']; 
  501. /** Installed but not active */ 
  502. elseif ( is_plugin_inactive( $plugin['file_path'] ) ) { 
  503. $activate_link = true; // We need to display the 'activate' action link 
  504. $activate_link_count++; // Increment the activate link count 
  505. if ( current_user_can( 'activate_plugins' ) ) { 
  506. if ( ( isset( $plugin['required'] ) ) && ( $plugin['required'] ) ) 
  507. $message['notice_can_activate_required'][] = $plugin['name']; 
  508. /** This plugin is only recommended */ 
  509. else { 
  510. $message['notice_can_activate_recommended'][] = $plugin['name']; 
  511. /** Need higher privileges to activate the plugin */ 
  512. else { 
  513. $message['notice_cannot_activate'][] = $plugin['name']; 
  514.  
  515. /** Only process the nag messages if the user has not dismissed them already */ 
  516. if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', true ) ) { 
  517. /** If we have notices to display, we move forward */ 
  518. if ( ! empty( $message ) ) { 
  519. krsort( $message ); // Sort messages 
  520. $rendered = ''; // Display all nag messages as strings 
  521.  
  522. /** Grab all plugin names */ 
  523. foreach ( $message as $type => $plugin_groups ) { 
  524. $linked_plugin_groups = array(); 
  525.  
  526. /** Count number of plugins in each message group to calculate singular/plural message */ 
  527. $count = count( $plugin_groups ); 
  528.  
  529. /** Loop through the plugin names to make the ones pulled from the .org repo linked */ 
  530. foreach ( $plugin_groups as $plugin_group_single_name ) { 
  531. $external_url = $this->_get_plugin_data_from_name( $plugin_group_single_name, 'external_url' ); 
  532. $source = $this->_get_plugin_data_from_name( $plugin_group_single_name, 'source' ); 
  533.  
  534. if ( $external_url && preg_match( '|^http(s)?://|', $external_url ) ) { 
  535. $linked_plugin_groups[] = '<a href="' . esc_url( $external_url ) . '" title="' . $plugin_group_single_name . '" target="_blank">' . $plugin_group_single_name . '</a>'; 
  536. elseif ( ! $source || preg_match( '|^http://wordpress.org/extend/plugins/|', $source ) ) { 
  537. $url = add_query_arg( 
  538. array( 
  539. 'tab' => 'plugin-information',  
  540. 'plugin' => $this->_get_plugin_data_from_name( $plugin_group_single_name ),  
  541. 'TB_iframe' => 'true',  
  542. 'width' => '640',  
  543. 'height' => '500',  
  544. ),  
  545. admin_url( 'plugin-install.php' ) 
  546. ); 
  547.  
  548. $linked_plugin_groups[] = '<a href="' . esc_url( $url ) . '" class="thickbox" title="' . $plugin_group_single_name . '">' . $plugin_group_single_name . '</a>'; 
  549. else { 
  550. $linked_plugin_groups[] = $plugin_group_single_name; // No hyperlink 
  551.  
  552. if ( isset( $linked_plugin_groups ) && (array) $linked_plugin_groups ) 
  553. $plugin_groups = $linked_plugin_groups; 
  554.  
  555. $last_plugin = array_pop( $plugin_groups ); // Pop off last name to prep for readability 
  556. $imploded = empty( $plugin_groups ) ? '<em>' . $last_plugin . '</em>' : '<em>' . ( implode( ', ', $plugin_groups ) . '</em> and <em>' . $last_plugin . '</em>' ); 
  557.  
  558. $rendered .= '<p>' . sprintf( translate_nooped_plural( $this->strings[$type], $count, $this->domain ), $imploded, $count ) . '</p>'; // All messages now stored 
  559.  
  560. /** Setup variables to determine if action links are needed */ 
  561. $show_install_link = $install_link ? '<a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '">' . translate_nooped_plural( $this->strings['install_link'], $install_link_count, $this->domain ) . '</a>' : ''; 
  562. $show_activate_link = $activate_link ? '<a href="' . admin_url( 'plugins.php' ) . '">' . translate_nooped_plural( $this->strings['activate_link'], $activate_link_count, $this->domain ) . '</a>' : ''; 
  563.  
  564. /** Define all of the action links */ 
  565. $action_links = apply_filters( 
  566. 'tgmpa_notice_action_links',  
  567. array( 
  568. 'install' => ( current_user_can( 'install_plugins' ) ) ? $show_install_link : '',  
  569. 'activate' => ( current_user_can( 'activate_plugins' ) ) ? $show_activate_link : '',  
  570. 'dismiss' => '<a class="dismiss-notice" href="' . add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ) . '" target="_parent">' . __( 'Dismiss this notice', $this->domain ) . '</a>',  
  571. ); 
  572.  
  573. $action_links = array_filter( $action_links ); // Remove any empty array items 
  574. if ( $action_links ) 
  575. $rendered .= '<p>' . implode( ' | ', $action_links ) . '</p>'; 
  576.  
  577. /** Register the nag messages and prepare them to be processed */ 
  578. if ( isset( $this->strings['nag_type'] ) ) 
  579. add_settings_error( 'tgmpa', 'tgmpa', $rendered, sanitize_html_class( strtolower( $this->strings['nag_type'] ), 'updated' ) ); 
  580. else 
  581. add_settings_error( 'tgmpa', 'tgmpa', $rendered, 'updated' ); 
  582.  
  583. /** Admin options pages already output settings_errors, so this is to avoid duplication */ 
  584. if ( 'options-general' !== $current_screen->parent_base ) 
  585. settings_errors( 'tgmpa' ); 
  586.  
  587.  
  588. /** 
  589. * Add dismissable admin notices. 
  590. * Appends a link to the admin nag messages. If clicked, the admin notice disappears and no longer is visible to users. 
  591. * @since 2.1.0 
  592. */ 
  593. public function dismiss() { 
  594.  
  595. if ( isset( $_GET[sanitize_key( 'tgmpa-dismiss' )] ) ) 
  596. update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', 1 ); 
  597.  
  598.  
  599. /** 
  600. * Add individual plugin to our collection of plugins. 
  601. * If the required keys are not set, the plugin is not added. 
  602. * @since 2.0.0 
  603. * @param array $plugin Array of plugin arguments. 
  604. */ 
  605. public function register( $plugin ) { 
  606.  
  607. if ( ! isset( $plugin['slug'] ) || ! isset( $plugin['name'] ) ) 
  608. return; 
  609.  
  610. $this->plugins[] = $plugin; 
  611.  
  612.  
  613. /** 
  614. * Amend default configuration settings. 
  615. * @since 2.0.0 
  616. * @param array $config 
  617. */ 
  618. public function config( $config ) { 
  619.  
  620. $keys = array( 'default_path', 'parent_menu_slug', 'parent_url_slug', 'domain', 'has_notices', 'menu', 'is_automatic', 'message', 'strings' ); 
  621.  
  622. foreach ( $keys as $key ) { 
  623. if ( isset( $config[$key] ) ) { 
  624. if ( is_array( $config[$key] ) ) { 
  625. foreach ( $config[$key] as $subkey => $value ) 
  626. $this->{$key}[$subkey] = $value; 
  627. } else { 
  628. $this->$key = $config[$key]; 
  629.  
  630.  
  631. /** 
  632. * Amend action link after plugin installation. 
  633. * @since 2.0.0 
  634. * @param array $install_actions Existing array of actions 
  635. * @return array Amended array of actions 
  636. */ 
  637. public function actions( $install_actions ) { 
  638.  
  639. /** Remove action links on the TGMPA install page */ 
  640. if ( $this->is_tgmpa_page() ) 
  641. return false; 
  642.  
  643. return $install_actions; 
  644.  
  645.  
  646. /** 
  647. * Set file_path key for each installed plugin. 
  648. * @since 2.1.0 
  649. */ 
  650. public function populate_file_path() { 
  651.  
  652. /** Add file_path key for all plugins */ 
  653. foreach ( $this->plugins as $plugin => $values ) 
  654. $this->plugins[$plugin]['file_path'] = $this->_get_plugin_basename_from_slug( $values['slug'] ); 
  655.  
  656.  
  657. /** 
  658. * Helper function to extract the file path of the plugin file from the 
  659. * plugin slug, if the plugin is installed. 
  660. * @since 2.0.0 
  661. * @param string $slug Plugin slug (typically folder name) as provided by the developer 
  662. * @return string Either file path for plugin if installed, or just the plugin slug 
  663. */ 
  664. protected function _get_plugin_basename_from_slug( $slug ) { 
  665.  
  666. $keys = array_keys( get_plugins() ); 
  667.  
  668. foreach ( $keys as $key ) { 
  669. if ( preg_match( '|^' . $slug .'|', $key ) ) 
  670. return $key; 
  671.  
  672. return $slug; 
  673.  
  674.  
  675. /** 
  676. * Retrieve plugin data, given the plugin name. 
  677. * Loops through the registered plugins looking for $name. If it finds it,  
  678. * it returns the $data from that plugin. Otherwise, returns false. 
  679. * @since 2.1.0 
  680. * @param string $name Name of the plugin, as it was registered 
  681. * @param string $data Optional. Array key of plugin data to return. Default is slug 
  682. * @return string|boolean Plugin slug if found, false otherwise. 
  683. */ 
  684. protected function _get_plugin_data_from_name( $name, $data = 'slug' ) { 
  685.  
  686. foreach ( $this->plugins as $plugin => $values ) { 
  687. if ( $name == $values['name'] && isset( $values[$data] ) ) 
  688. return $values[$data]; 
  689.  
  690. return false; 
  691.  
  692.  
  693. /** 
  694. * Determine if we're on the TGMPA Install page. 
  695. * We use $current_screen when it is available, and a slightly less ideal 
  696. * conditional when it isn't (like when displaying the plugin information 
  697. * thickbox). 
  698. * @since 2.1.0 
  699. * @global object $current_screen 
  700. * @return boolean True when on the TGMPA page, false otherwise. 
  701. */ 
  702. protected function is_tgmpa_page() { 
  703.  
  704. global $current_screen; 
  705.  
  706. if ( ! is_null( $current_screen ) && $this->parent_menu_slug == $current_screen->parent_file && isset( $_GET['page'] ) && $this->menu === $_GET['page'] ) 
  707. return true; 
  708.  
  709. if ( isset( $_GET['page'] ) && $this->menu === $_GET['page'] ) 
  710. return true; 
  711.  
  712. return false; 
  713.  
  714.  
  715. /** 
  716. * Delete dismissable nag option when theme is switched. 
  717. * This ensures that the user is again reminded via nag of required 
  718. * and/or recommended plugins if they re-activate the theme. 
  719. * @since 2.1.1 
  720. */ 
  721. public function update_dismiss() { 
  722.  
  723. delete_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice' ); 
  724.  
  725.  
  726. /** 
  727. * Forces plugin activation if the parameter 'force_activation' is 
  728. * set to true. 
  729. * This allows theme authors to specify certain plugins that must be 
  730. * active at all times while using the current theme. 
  731. * Please take special care when using this parameter as it has the 
  732. * potential to be harmful if not used correctly. Setting this parameter 
  733. * to true will not allow the specified plugin to be deactivated unless 
  734. * the user switches themes. 
  735. * @since 2.2.0 
  736. */ 
  737. public function force_activation() { 
  738.  
  739. /** Set file_path parameter for any installed plugins */ 
  740. $this->populate_file_path(); 
  741.  
  742. $installed_plugins = get_plugins(); 
  743.  
  744. foreach ( $this->plugins as $plugin ) { 
  745. /** Oops, plugin isn't there so iterate to next condition */ 
  746. if ( isset( $plugin['force_activation'] ) && $plugin['force_activation'] && ! isset( $installed_plugins[$plugin['file_path']] ) ) 
  747. continue; 
  748. /** There we go, activate the plugin */ 
  749. elseif ( isset( $plugin['force_activation'] ) && $plugin['force_activation'] && is_plugin_inactive( $plugin['file_path'] ) ) 
  750. activate_plugin( $plugin['file_path'] ); 
  751.  
  752.  
  753. /** 
  754. * Forces plugin deactivation if the parameter 'force_deactivation' 
  755. * is set to true. 
  756. * This allows theme authors to specify certain plugins that must be 
  757. * deactived upon switching from the current theme to another. 
  758. * Please take special care when using this parameter as it has the 
  759. * potential to be harmful if not used correctly. 
  760. * @since 2.2.0 
  761. */ 
  762. public function force_deactivation() { 
  763.  
  764. /** Set file_path parameter for any installed plugins */ 
  765. $this->populate_file_path(); 
  766.  
  767. foreach ( $this->plugins as $plugin ) { 
  768. /** Only proceed forward if the paramter is set to true and plugin is active */ 
  769. if ( isset( $plugin['force_deactivation'] ) && $plugin['force_deactivation'] && is_plugin_active( $plugin['file_path'] ) ) 
  770. deactivate_plugins( $plugin['file_path'] ); 
  771.  
  772.