TGM_Plugin_Activation

Automatic plugin installation and activation library.

Defined (1)

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

/functions/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. add_theme_page( 
  239. $this->parent_menu_slug, // Parent menu slug 
  240. $this->strings['page_title'], // Page title 
  241. $this->strings['menu_title'], // Menu title 
  242. 'edit_theme_options', // Capability 
  243. $this->menu, // Menu slug 
  244. array( &$this, 'install_plugins_page' ) // Callback 
  245. ); 
  246. break; 
  247.  
  248.  
  249. /** 
  250. * Echoes plugin installation form. 
  251. * This method is the callback for the admin_menu method function. 
  252. * This displays the admin page and form area where the user can select to install and activate the plugin. 
  253. * @since 1.0.0 
  254. * @return null Aborts early if we're processing a plugin installation action 
  255. */ 
  256. public function install_plugins_page() { 
  257.  
  258. /** Store new instance of plugin table in object */ 
  259. $plugin_table = new TGMPA_List_Table; 
  260.  
  261. /** Return early if processing a plugin installation action */ 
  262. if ( isset( $_POST[sanitize_key( 'action' )] ) && 'tgmpa-bulk-install' == $_POST[sanitize_key( 'action' )] && $plugin_table->process_bulk_actions() || $this->do_plugin_install() ) 
  263. return; 
  264.  
  265. ?> 
  266. <div class="tgmpa wrap"> 
  267.  
  268. <?php screen_icon( apply_filters( 'tgmpa_default_screen_icon', 'themes' ) ); ?> 
  269. <h2><?php echo esc_html( get_admin_page_title() ); ?></h2> 
  270. <?php $plugin_table->prepare_items(); ?> 
  271.  
  272. <?php if ( isset( $this->message ) ) _e( wp_kses_post( $this->message ), $this->domain ); ?> 
  273.  
  274. <form id="tgmpa-plugins" action="" method="post"> 
  275. <input type="hidden" name="tgmpa-page" value="<?php echo $this->menu; ?>" /> 
  276. <?php $plugin_table->display(); ?> 
  277. </form> 
  278.  
  279. </div> 
  280. <?php 
  281.  
  282.  
  283. /** 
  284. * Installs a plugin or activates a plugin depending on the hover 
  285. * link clicked by the user. 
  286. * Checks the $_GET variable to see which actions have been 
  287. * passed and responds with the appropriate method. 
  288. * Uses WP_Filesystem to process and handle the plugin installation 
  289. * method. 
  290. * @since 1.0.0 
  291. * @uses WP_Filesystem 
  292. * @uses WP_Error 
  293. * @uses WP_Upgrader 
  294. * @uses Plugin_Upgrader 
  295. * @uses Plugin_Installer_Skin 
  296. * @return boolean True on success, false on failure 
  297. */ 
  298. protected function do_plugin_install() { 
  299.  
  300. /** All plugin information will be stored in an array for processing */ 
  301. $plugin = array(); 
  302.  
  303. /** Checks for actions from hover links to process the installation */ 
  304. if ( isset( $_GET[sanitize_key( 'plugin' )] ) && ( isset( $_GET[sanitize_key( 'tgmpa-install' )] ) && 'install-plugin' == $_GET[sanitize_key( 'tgmpa-install' )] ) ) { 
  305. check_admin_referer( 'tgmpa-install' ); 
  306.  
  307. $plugin['name'] = $_GET[sanitize_key( 'plugin_name' )]; // Plugin name 
  308. $plugin['slug'] = $_GET[sanitize_key( 'plugin' )]; // Plugin slug 
  309. $plugin['source'] = $_GET[sanitize_key( 'plugin_source' )]; // Plugin source 
  310.  
  311. /** Pass all necessary information via URL if WP_Filesystem is needed */ 
  312. $url = wp_nonce_url( 
  313. add_query_arg( 
  314. array( 
  315. 'page' => $this->menu,  
  316. 'plugin' => $plugin['slug'],  
  317. 'plugin_name' => $plugin['name'],  
  318. 'plugin_source' => $plugin['source'],  
  319. 'tgmpa-install' => 'install-plugin',  
  320. ),  
  321. admin_url( $this->parent_url_slug ) 
  322. ),  
  323. 'tgmpa-install' 
  324. ); 
  325. $method = ''; // Leave blank so WP_Filesystem can populate it as necessary 
  326. $fields = array( sanitize_key( 'tgmpa-install' ) ); // Extra fields to pass to WP_Filesystem 
  327.  
  328. if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $fields ) ) ) 
  329. return true; 
  330.  
  331. if ( ! WP_Filesystem( $creds ) ) { 
  332. request_filesystem_credentials( $url, $method, true, false, $fields ); // Setup WP_Filesystem 
  333. return true; 
  334.  
  335. require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for plugins_api 
  336. require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Need for upgrade classes 
  337.  
  338. /** Set plugin source to WordPress API link if available */ 
  339. if ( isset( $plugin['source'] ) && 'repo' == $plugin['source'] ) { 
  340. $api = plugins_api( 'plugin_information', array( 'slug' => $plugin['slug'], 'fields' => array( 'sections' => false ) ) ); 
  341.  
  342. if ( is_wp_error( $api ) ) 
  343. wp_die( $this->strings['oops'] . var_dump( $api ) ); 
  344.  
  345. if ( isset( $api->download_link ) ) 
  346. $plugin['source'] = $api->download_link; 
  347.  
  348. /** Set type, based on whether the source starts with http:// or https:// */ 
  349. $type = preg_match( '|^http(s)?://|', $plugin['source'] ) ? 'web' : 'upload'; 
  350.  
  351. /** Prep variables for Plugin_Installer_Skin class */ 
  352. $title = sprintf( $this->strings['installing'], $plugin['name'] ); 
  353. $url = add_query_arg( array( 'action' => 'install-plugin', 'plugin' => $plugin['slug'] ), 'update.php' ); 
  354. if ( isset( $_GET['from'] ) ) 
  355. $url .= add_query_arg( 'from', urlencode( stripslashes( $_GET['from'] ) ), $url ); 
  356.  
  357. $nonce = 'install-plugin_' . $plugin['slug']; 
  358.  
  359. /** Prefix a default path to pre-packaged plugins */ 
  360. $source = ( 'upload' == $type ) ? $this->default_path . $plugin['source'] : $plugin['source']; 
  361.  
  362. /** Create a new instance of Plugin_Upgrader */ 
  363. $upgrader = new Plugin_Upgrader( $skin = new Plugin_Installer_Skin( compact( 'type', 'title', 'url', 'nonce', 'plugin', 'api' ) ) ); 
  364.  
  365. /** Perform the action and install the plugin from the $source urldecode() */ 
  366. $upgrader->install( $source ); 
  367.  
  368. /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */ 
  369. wp_cache_flush(); 
  370.  
  371. /** Only activate plugins if the config option is set to true */ 
  372. if ( $this->is_automatic ) { 
  373. $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method 
  374. $activate = activate_plugin( $plugin_activate ); // Activate the plugin 
  375. $this->populate_file_path(); // Re-populate the file path now that the plugin has been installed and activated 
  376.  
  377. if ( is_wp_error( $activate ) ) { 
  378. echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>'; 
  379. 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>'; 
  380. return true; // End it here if there is an error with automatic activation 
  381. else { 
  382. echo '<p>' . $this->strings['plugin_activated'] . '</p>'; 
  383.  
  384. /** Display message based on if all plugins are now active or not */ 
  385. $complete = array(); 
  386. foreach ( $this->plugins as $plugin ) { 
  387. if ( ! is_plugin_active( $plugin['file_path'] ) ) { 
  388. 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>'; 
  389. $complete[] = $plugin; 
  390. break; 
  391. /** Nothing to store */ 
  392. else { 
  393. $complete[] = ''; 
  394.  
  395. /** Filter out any empty entries */ 
  396. $complete = array_filter( $complete ); 
  397.  
  398. /** All plugins are active, so we display the complete string and hide the plugin menu */ 
  399. if ( empty( $complete ) ) { 
  400. 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>'; 
  401. echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>'; 
  402.  
  403. return true; 
  404. /** Checks for actions from hover links to process the activation */ 
  405. elseif ( isset( $_GET[sanitize_key( 'plugin' )] ) && ( isset( $_GET[sanitize_key( 'tgmpa-activate' )] ) && 'activate-plugin' == $_GET[sanitize_key( 'tgmpa-activate' )] ) ) { 
  406. check_admin_referer( 'tgmpa-activate', 'tgmpa-activate-nonce' ); 
  407.  
  408. /** Populate $plugin array with necessary information */ 
  409. $plugin['name'] = $_GET[sanitize_key( 'plugin_name' )]; 
  410. $plugin['slug'] = $_GET[sanitize_key( 'plugin' )]; 
  411. $plugin['source'] = $_GET[sanitize_key( 'plugin_source' )]; 
  412.  
  413. $plugin_data = get_plugins( '/' . $plugin['slug'] ); // Retrieve all plugins 
  414. $plugin_file = array_keys( $plugin_data ); // Retrieve all plugin files from installed plugins 
  415. $plugin_to_activate = $plugin['slug'] . '/' . $plugin_file[0]; // Match plugin slug with appropriate plugin file 
  416. $activate = activate_plugin( $plugin_to_activate ); // Activate the plugin 
  417.  
  418. if ( is_wp_error( $activate ) ) { 
  419. echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>'; 
  420. 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>'; 
  421. return true; // End it here if there is an error with activation 
  422. else { 
  423. /** Make sure message doesn't display again if bulk activation is performed immediately after a single activation */ 
  424. if ( ! isset( $_POST[sanitize_key( 'action' )] ) ) { 
  425. $msg = sprintf( __( 'The following plugin was activated successfully: %s.', $this->domain ), '<strong>' . $plugin['name'] . '</strong>' ); 
  426. echo '<div id="message" class="updated"><p>' . $msg . '</p></div>'; 
  427.  
  428. return false; 
  429.  
  430.  
  431. /** 
  432. * Echoes required plugin notice. 
  433. * Outputs a message telling users that a specific plugin is required for 
  434. * their theme. If appropriate, it includes a link to the form page where 
  435. * users can install and activate the plugin. 
  436. * @since 1.0.0 
  437. * @global object $current_screen 
  438. * @return null Returns early if we're on the Install page 
  439. */ 
  440. public function notices() { 
  441.  
  442. global $current_screen; 
  443.  
  444. /** Remove nag on the install page */ 
  445. if ( $this->is_tgmpa_page() ) 
  446. return; 
  447.  
  448. $installed_plugins = get_plugins(); // Retrieve a list of all the plugins 
  449. $this->populate_file_path(); 
  450.  
  451. $message = array(); // Store the messages in an array to be outputted after plugins have looped through 
  452. $install_link = false; // Set to false, change to true in loop if conditions exist, used for action link 'install' 
  453. $install_link_count = 0; // Used to determine plurality of install action link text 
  454. $activate_link = false; // Set to false, change to true in loop if conditions exist, used for action link 'activate' 
  455. $activate_link_count = 0; // Used to determine plurality of activate action link text 
  456.  
  457. foreach ( $this->plugins as $plugin ) { 
  458. /** If the plugin is installed and active, check for minimum version argument before moving forward */ 
  459. if ( is_plugin_active( $plugin['file_path'] ) ) { 
  460. /** A minimum version has been specified */ 
  461. if ( isset( $plugin['version'] ) ) { 
  462. if ( isset( $installed_plugins[$plugin['file_path']]['Version'] ) ) { 
  463. /** If the current version is less than the minimum required version, we display a message */ 
  464. if ( version_compare( $installed_plugins[$plugin['file_path']]['Version'], $plugin['version'], '<' ) ) { 
  465. if ( current_user_can( 'install_plugins' ) ) 
  466. $message['notice_ask_to_update'][] = $plugin['name']; 
  467. else 
  468. $message['notice_cannot_update'][] = $plugin['name']; 
  469. /** Can't find the plugin, so iterate to the next condition */ 
  470. else { 
  471. continue; 
  472. /** No minimum version specified, so iterate over the plugin */ 
  473. else { 
  474. continue; 
  475.  
  476. /** Not installed */ 
  477. if ( ! isset( $installed_plugins[$plugin['file_path']] ) ) { 
  478. $install_link = true; // We need to display the 'install' action link 
  479. $install_link_count++; // Increment the install link count 
  480. if ( current_user_can( 'install_plugins' ) ) { 
  481. if ( $plugin['required'] ) 
  482. $message['notice_can_install_required'][] = $plugin['name']; 
  483. /** This plugin is only recommended */ 
  484. else 
  485. $message['notice_can_install_recommended'][] = $plugin['name']; 
  486. /** Need higher privileges to install the plugin */ 
  487. else { 
  488. $message['notice_cannot_install'][] = $plugin['name']; 
  489. /** Installed but not active */ 
  490. elseif ( is_plugin_inactive( $plugin['file_path'] ) ) { 
  491. $activate_link = true; // We need to display the 'activate' action link 
  492. $activate_link_count++; // Increment the activate link count 
  493. if ( current_user_can( 'activate_plugins' ) ) { 
  494. if ( ( isset( $plugin['required'] ) ) && ( $plugin['required'] ) ) 
  495. $message['notice_can_activate_required'][] = $plugin['name']; 
  496. /** This plugin is only recommended */ 
  497. else { 
  498. $message['notice_can_activate_recommended'][] = $plugin['name']; 
  499. /** Need higher privileges to activate the plugin */ 
  500. else { 
  501. $message['notice_cannot_activate'][] = $plugin['name']; 
  502.  
  503. /** Only process the nag messages if the user has not dismissed them already */ 
  504. if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', true ) ) { 
  505. /** If we have notices to display, we move forward */ 
  506. if ( ! empty( $message ) ) { 
  507. krsort( $message ); // Sort messages 
  508. $rendered = ''; // Display all nag messages as strings 
  509.  
  510. /** Grab all plugin names */ 
  511. foreach ( $message as $type => $plugin_groups ) { 
  512. $linked_plugin_groups = array(); 
  513.  
  514. /** Count number of plugins in each message group to calculate singular/plural message */ 
  515. $count = count( $plugin_groups ); 
  516.  
  517. /** Loop through the plugin names to make the ones pulled from the .org repo linked */ 
  518. foreach ( $plugin_groups as $plugin_group_single_name ) { 
  519. $external_url = $this->_get_plugin_data_from_name( $plugin_group_single_name, 'external_url' ); 
  520. $source = $this->_get_plugin_data_from_name( $plugin_group_single_name, 'source' ); 
  521.  
  522. if ( $external_url && preg_match( '|^http(s)?://|', $external_url ) ) { 
  523. $linked_plugin_groups[] = '<a href="' . esc_url( $external_url ) . '" title="' . $plugin_group_single_name . '" target="_blank">' . $plugin_group_single_name . '</a>'; 
  524. elseif ( ! $source || preg_match( '|^http://wordpress.org/extend/plugins/|', $source ) ) { 
  525. $url = add_query_arg( 
  526. array( 
  527. 'tab' => 'plugin-information',  
  528. 'plugin' => $this->_get_plugin_data_from_name( $plugin_group_single_name ),  
  529. 'TB_iframe' => 'true',  
  530. 'width' => '640',  
  531. 'height' => '500',  
  532. ),  
  533. admin_url( 'plugin-install.php' ) 
  534. ); 
  535.  
  536. $linked_plugin_groups[] = '<a href="' . esc_url( $url ) . '" class="thickbox" title="' . $plugin_group_single_name . '">' . $plugin_group_single_name . '</a>'; 
  537. else { 
  538. $linked_plugin_groups[] = $plugin_group_single_name; // No hyperlink 
  539.  
  540. if ( isset( $linked_plugin_groups ) && (array) $linked_plugin_groups ) 
  541. $plugin_groups = $linked_plugin_groups; 
  542.  
  543. $last_plugin = array_pop( $plugin_groups ); // Pop off last name to prep for readability 
  544. $imploded = empty( $plugin_groups ) ? '<em>' . $last_plugin . '</em>' : '<em>' . ( implode( ', ', $plugin_groups ) . '</em> and <em>' . $last_plugin . '</em>' ); 
  545.  
  546. $rendered .= '<p>' . sprintf( translate_nooped_plural( $this->strings[$type], $count, $this->domain ), $imploded, $count ) . '</p>'; // All messages now stored 
  547.  
  548. /** Setup variables to determine if action links are needed */ 
  549. $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>' : ''; 
  550. $show_activate_link = $activate_link ? '<a href="' . admin_url( 'plugins.php' ) . '">' . translate_nooped_plural( $this->strings['activate_link'], $activate_link_count, $this->domain ) . '</a>' : ''; 
  551.  
  552. /** Define all of the action links */ 
  553. $action_links = apply_filters( 
  554. 'tgmpa_notice_action_links',  
  555. array( 
  556. 'install' => ( current_user_can( 'install_plugins' ) ) ? $show_install_link : '',  
  557. 'activate' => ( current_user_can( 'activate_plugins' ) ) ? $show_activate_link : '',  
  558. 'dismiss' => '<a class="dismiss-notice" href="' . add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ) . '" target="_parent">' . __( 'Dismiss this notice', $this->domain ) . '</a>',  
  559. ); 
  560.  
  561. $action_links = array_filter( $action_links ); // Remove any empty array items 
  562. if ( $action_links ) 
  563. $rendered .= '<p>' . implode( ' | ', $action_links ) . '</p>'; 
  564.  
  565. /** Register the nag messages and prepare them to be processed */ 
  566. if ( isset( $this->strings['nag_type'] ) ) 
  567. add_settings_error( 'tgmpa', 'tgmpa', $rendered, sanitize_html_class( strtolower( $this->strings['nag_type'] ), 'updated' ) ); 
  568. else 
  569. add_settings_error( 'tgmpa', 'tgmpa', $rendered, 'updated' ); 
  570.  
  571. /** Admin options pages already output settings_errors, so this is to avoid duplication */ 
  572. if ( 'options-general' !== $current_screen->parent_base ) 
  573. settings_errors( 'tgmpa' ); 
  574.  
  575.  
  576. /** 
  577. * Add dismissable admin notices. 
  578. * Appends a link to the admin nag messages. If clicked, the admin notice disappears and no longer is visible to users. 
  579. * @since 2.1.0 
  580. */ 
  581. public function dismiss() { 
  582.  
  583. if ( isset( $_GET[sanitize_key( 'tgmpa-dismiss' )] ) ) 
  584. update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', 1 ); 
  585.  
  586.  
  587. /** 
  588. * Add individual plugin to our collection of plugins. 
  589. * If the required keys are not set, the plugin is not added. 
  590. * @since 2.0.0 
  591. * @param array $plugin Array of plugin arguments. 
  592. */ 
  593. public function register( $plugin ) { 
  594.  
  595. if ( ! isset( $plugin['slug'] ) || ! isset( $plugin['name'] ) ) 
  596. return; 
  597.  
  598. $this->plugins[] = $plugin; 
  599.  
  600.  
  601. /** 
  602. * Amend default configuration settings. 
  603. * @since 2.0.0 
  604. * @param array $config 
  605. */ 
  606. public function config( $config ) { 
  607.  
  608. $keys = array( 'default_path', 'parent_menu_slug', 'parent_url_slug', 'domain', 'has_notices', 'menu', 'is_automatic', 'message', 'strings' ); 
  609.  
  610. foreach ( $keys as $key ) { 
  611. if ( isset( $config[$key] ) ) { 
  612. if ( is_array( $config[$key] ) ) { 
  613. foreach ( $config[$key] as $subkey => $value ) 
  614. $this->{$key}[$subkey] = $value; 
  615. } else { 
  616. $this->$key = $config[$key]; 
  617.  
  618.  
  619. /** 
  620. * Amend action link after plugin installation. 
  621. * @since 2.0.0 
  622. * @param array $install_actions Existing array of actions 
  623. * @return array Amended array of actions 
  624. */ 
  625. public function actions( $install_actions ) { 
  626.  
  627. /** Remove action links on the TGMPA install page */ 
  628. if ( $this->is_tgmpa_page() ) 
  629. return false; 
  630.  
  631. return $install_actions; 
  632.  
  633.  
  634. /** 
  635. * Set file_path key for each installed plugin. 
  636. * @since 2.1.0 
  637. */ 
  638. public function populate_file_path() { 
  639.  
  640. /** Add file_path key for all plugins */ 
  641. foreach ( $this->plugins as $plugin => $values ) 
  642. $this->plugins[$plugin]['file_path'] = $this->_get_plugin_basename_from_slug( $values['slug'] ); 
  643.  
  644.  
  645. /** 
  646. * Helper function to extract the file path of the plugin file from the 
  647. * plugin slug, if the plugin is installed. 
  648. * @since 2.0.0 
  649. * @param string $slug Plugin slug (typically folder name) as provided by the developer 
  650. * @return string Either file path for plugin if installed, or just the plugin slug 
  651. */ 
  652. protected function _get_plugin_basename_from_slug( $slug ) { 
  653.  
  654. $keys = array_keys( get_plugins() ); 
  655.  
  656. foreach ( $keys as $key ) { 
  657. if ( preg_match( '|^' . $slug .'|', $key ) ) 
  658. return $key; 
  659.  
  660. return $slug; 
  661.  
  662.  
  663. /** 
  664. * Retrieve plugin data, given the plugin name. 
  665. * Loops through the registered plugins looking for $name. If it finds it,  
  666. * it returns the $data from that plugin. Otherwise, returns false. 
  667. * @since 2.1.0 
  668. * @param string $name Name of the plugin, as it was registered 
  669. * @param string $data Optional. Array key of plugin data to return. Default is slug 
  670. * @return string|boolean Plugin slug if found, false otherwise. 
  671. */ 
  672. protected function _get_plugin_data_from_name( $name, $data = 'slug' ) { 
  673.  
  674. foreach ( $this->plugins as $plugin => $values ) { 
  675. if ( $name == $values['name'] && isset( $values[$data] ) ) 
  676. return $values[$data]; 
  677.  
  678. return false; 
  679.  
  680.  
  681. /** 
  682. * Determine if we're on the TGMPA Install page. 
  683. * We use $current_screen when it is available, and a slightly less ideal 
  684. * conditional when it isn't (like when displaying the plugin information 
  685. * thickbox). 
  686. * @since 2.1.0 
  687. * @global object $current_screen 
  688. * @return boolean True when on the TGMPA page, false otherwise. 
  689. */ 
  690. protected function is_tgmpa_page() { 
  691.  
  692. global $current_screen; 
  693.  
  694. if ( ! is_null( $current_screen ) && $this->parent_menu_slug == $current_screen->parent_file && isset( $_GET['page'] ) && $this->menu === $_GET['page'] ) 
  695. return true; 
  696.  
  697. if ( isset( $_GET['page'] ) && $this->menu === $_GET['page'] ) 
  698. return true; 
  699.  
  700. return false; 
  701.  
  702.  
  703. /** 
  704. * Delete dismissable nag option when theme is switched. 
  705. * This ensures that the user is again reminded via nag of required 
  706. * and/or recommended plugins if they re-activate the theme. 
  707. * @since 2.1.1 
  708. */ 
  709. public function update_dismiss() { 
  710.  
  711. delete_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice' ); 
  712.  
  713.  
  714. /** 
  715. * Forces plugin activation if the parameter 'force_activation' is 
  716. * set to true. 
  717. * This allows theme authors to specify certain plugins that must be 
  718. * active at all times while using the current theme. 
  719. * Please take special care when using this parameter as it has the 
  720. * potential to be harmful if not used correctly. Setting this parameter 
  721. * to true will not allow the specified plugin to be deactivated unless 
  722. * the user switches themes. 
  723. * @since 2.2.0 
  724. */ 
  725. public function force_activation() { 
  726.  
  727. /** Set file_path parameter for any installed plugins */ 
  728. $this->populate_file_path(); 
  729.  
  730. $installed_plugins = get_plugins(); 
  731.  
  732. foreach ( $this->plugins as $plugin ) { 
  733. /** Oops, plugin isn't there so iterate to next condition */ 
  734. if ( isset( $plugin['force_activation'] ) && $plugin['force_activation'] && ! isset( $installed_plugins[$plugin['file_path']] ) ) 
  735. continue; 
  736. /** There we go, activate the plugin */ 
  737. elseif ( isset( $plugin['force_activation'] ) && $plugin['force_activation'] && is_plugin_inactive( $plugin['file_path'] ) ) 
  738. activate_plugin( $plugin['file_path'] ); 
  739.  
  740.  
  741. /** 
  742. * Forces plugin deactivation if the parameter 'force_deactivation' 
  743. * is set to true. 
  744. * This allows theme authors to specify certain plugins that must be 
  745. * deactived upon switching from the current theme to another. 
  746. * Please take special care when using this parameter as it has the 
  747. * potential to be harmful if not used correctly. 
  748. * @since 2.2.0 
  749. */ 
  750. public function force_deactivation() { 
  751.  
  752. /** Set file_path parameter for any installed plugins */ 
  753. $this->populate_file_path(); 
  754.  
  755. foreach ( $this->plugins as $plugin ) { 
  756. /** Only proceed forward if the paramter is set to true and plugin is active */ 
  757. if ( isset( $plugin['force_deactivation'] ) && $plugin['force_deactivation'] && is_plugin_active( $plugin['file_path'] ) ) 
  758. deactivate_plugins( $plugin['file_path'] ); 
  759.  
  760.