TGMPA_List_Table

List table class for handling plugins.

Defined (1)

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

/class-tgm-plugin-activation.php  
  1. class TGMPA_List_Table extends WP_List_Table { 
  2. /** 
  3. * TGMPA instance. 
  4. * @since 2.5.0 
  5. * @var object 
  6. */ 
  7. protected $tgmpa; 
  8.  
  9. /** 
  10. * The currently chosen view. 
  11. * @since 2.5.0 
  12. * @var string One of: 'all', 'install', 'update', 'activate' 
  13. */ 
  14. public $view_context = 'all'; 
  15.  
  16. /** 
  17. * The plugin counts for the various views. 
  18. * @since 2.5.0 
  19. * @var array 
  20. */ 
  21. protected $view_totals = array( 
  22. 'all' => 0,  
  23. 'install' => 0,  
  24. 'update' => 0,  
  25. 'activate' => 0,  
  26. ); 
  27.  
  28. /** 
  29. * References parent constructor and sets defaults for class. 
  30. * @since 2.2.0 
  31. */ 
  32. public function __construct() { 
  33. $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) ); 
  34.  
  35. parent::__construct( 
  36. array( 
  37. 'singular' => 'plugin',  
  38. 'plural' => 'plugins',  
  39. 'ajax' => false,  
  40. ); 
  41.  
  42. if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'install', 'update', 'activate' ), true ) ) { 
  43. $this->view_context = sanitize_key( $_REQUEST['plugin_status'] ); 
  44.  
  45. add_filter( 'tgmpa_table_data_items', array( $this, 'sort_table_items' ) ); 
  46.  
  47. /** 
  48. * Get a list of CSS classes for the <table> tag. 
  49. * Overruled to prevent the 'plural' argument from being added. 
  50. * @since 2.5.0 
  51. * @return array CSS classnames. 
  52. */ 
  53. public function get_table_classes() { 
  54. return array( 'widefat', 'fixed' ); 
  55.  
  56. /** 
  57. * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table. 
  58. * @since 2.2.0 
  59. * @return array $table_data Information for use in table. 
  60. */ 
  61. protected function _gather_plugin_data() { 
  62. // Load thickbox for plugin links. 
  63. $this->tgmpa->admin_init(); 
  64. $this->tgmpa->thickbox(); 
  65.  
  66. // Categorize the plugins which have open actions. 
  67. $plugins = $this->categorize_plugins_to_views(); 
  68.  
  69. // Set the counts for the view links. 
  70. $this->set_view_totals( $plugins ); 
  71.  
  72. // Prep variables for use and grab list of all installed plugins. 
  73. $table_data = array(); 
  74. $i = 0; 
  75.  
  76. // Redirect to the 'all' view if no plugins were found for the selected view context. 
  77. if ( empty( $plugins[ $this->view_context ] ) ) { 
  78. $this->view_context = 'all'; 
  79.  
  80. foreach ( $plugins[ $this->view_context ] as $slug => $plugin ) { 
  81. $table_data[ $i ]['sanitized_plugin'] = $plugin['name']; 
  82. $table_data[ $i ]['slug'] = $slug; 
  83. $table_data[ $i ]['plugin'] = '<strong>' . $this->tgmpa->get_info_link( $slug ) . '</strong>'; 
  84. $table_data[ $i ]['source'] = $this->get_plugin_source_type_text( $plugin['source_type'] ); 
  85. $table_data[ $i ]['type'] = $this->get_plugin_advise_type_text( $plugin['required'] ); 
  86. $table_data[ $i ]['status'] = $this->get_plugin_status_text( $slug ); 
  87. $table_data[ $i ]['installed_version'] = $this->tgmpa->get_installed_version( $slug ); 
  88. $table_data[ $i ]['minimum_version'] = $plugin['version']; 
  89. $table_data[ $i ]['available_version'] = $this->tgmpa->does_plugin_have_update( $slug ); 
  90.  
  91. // Prep the upgrade notice info. 
  92. $upgrade_notice = $this->tgmpa->get_upgrade_notice( $slug ); 
  93. if ( ! empty( $upgrade_notice ) ) { 
  94. $table_data[ $i ]['upgrade_notice'] = $upgrade_notice; 
  95.  
  96. add_action( "tgmpa_after_plugin_row_{$slug}", array( $this, 'wp_plugin_update_row' ), 10, 2 ); 
  97.  
  98. $table_data[ $i ] = apply_filters( 'tgmpa_table_data_item', $table_data[ $i ], $plugin ); 
  99.  
  100. $i++; 
  101.  
  102. return $table_data; 
  103.  
  104. /** 
  105. * Categorize the plugins which have open actions into views for the TGMPA page. 
  106. * @since 2.5.0 
  107. */ 
  108. protected function categorize_plugins_to_views() { 
  109. $plugins = array( 
  110. 'all' => array(), // Meaning: all plugins which still have open actions. 
  111. 'install' => array(),  
  112. 'update' => array(),  
  113. 'activate' => array(),  
  114. ); 
  115.  
  116. foreach ( $this->tgmpa->plugins as $slug => $plugin ) { 
  117. if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) { 
  118. // No need to display plugins if they are installed, up-to-date and active. 
  119. continue; 
  120. } else { 
  121. $plugins['all'][ $slug ] = $plugin; 
  122.  
  123. if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) { 
  124. $plugins['install'][ $slug ] = $plugin; 
  125. } else { 
  126. if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) { 
  127. $plugins['update'][ $slug ] = $plugin; 
  128.  
  129. if ( $this->tgmpa->can_plugin_activate( $slug ) ) { 
  130. $plugins['activate'][ $slug ] = $plugin; 
  131.  
  132. return $plugins; 
  133.  
  134. /** 
  135. * Set the counts for the view links. 
  136. * @since 2.5.0 
  137. * @param array $plugins Plugins order by view. 
  138. */ 
  139. protected function set_view_totals( $plugins ) { 
  140. foreach ( $plugins as $type => $list ) { 
  141. $this->view_totals[ $type ] = count( $list ); 
  142.  
  143. /** 
  144. * Get the plugin required/recommended text string. 
  145. * @since 2.5.0 
  146. * @param string $required Plugin required setting. 
  147. * @return string 
  148. */ 
  149. protected function get_plugin_advise_type_text( $required ) { 
  150. if ( true === $required ) { 
  151. return __( 'Required', 'enigma' ); 
  152.  
  153. return __( 'Recommended', 'enigma' ); 
  154.  
  155. /** 
  156. * Get the plugin source type text string. 
  157. * @since 2.5.0 
  158. * @param string $type Plugin type. 
  159. * @return string 
  160. */ 
  161. protected function get_plugin_source_type_text( $type ) { 
  162. $string = ''; 
  163.  
  164. switch ( $type ) { 
  165. case 'repo': 
  166. $string = __( 'WordPress Repository', 'enigma' ); 
  167. break; 
  168. case 'external': 
  169. $string = __( 'External Source', 'enigma' ); 
  170. break; 
  171. case 'bundled': 
  172. $string = __( 'Pre-Packaged', 'enigma' ); 
  173. break; 
  174.  
  175. return $string; 
  176.  
  177. /** 
  178. * Determine the plugin status message. 
  179. * @since 2.5.0 
  180. * @param string $slug Plugin slug. 
  181. * @return string 
  182. */ 
  183. protected function get_plugin_status_text( $slug ) { 
  184. if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) { 
  185. return __( 'Not Installed', 'enigma' ); 
  186.  
  187. if ( ! $this->tgmpa->is_plugin_active( $slug ) ) { 
  188. $install_status = __( 'Installed But Not Activated', 'enigma' ); 
  189. } else { 
  190. $install_status = __( 'Active', 'enigma' ); 
  191.  
  192. $update_status = ''; 
  193.  
  194. if ( $this->tgmpa->does_plugin_require_update( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) { 
  195. $update_status = __( 'Required Update not Available', 'enigma' ); 
  196.  
  197. } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) { 
  198. $update_status = __( 'Requires Update', 'enigma' ); 
  199.  
  200. } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) { 
  201. $update_status = __( 'Update recommended', 'enigma' ); 
  202.  
  203. if ( '' === $update_status ) { 
  204. return $install_status; 
  205.  
  206. return sprintf( 
  207. /** translators: 1: install status, 2: update status */ 
  208. _x( '%1$s, %2$s', 'Install/Update Status', 'enigma' ),  
  209. $install_status,  
  210. $update_status 
  211. ); 
  212.  
  213. /** 
  214. * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type. 
  215. * @since 2.5.0 
  216. * @param array $items Prepared table items. 
  217. * @return array Sorted table items. 
  218. */ 
  219. public function sort_table_items( $items ) { 
  220. $type = array(); 
  221. $name = array(); 
  222.  
  223. foreach ( $items as $i => $plugin ) { 
  224. $type[ $i ] = $plugin['type']; // Required / recommended. 
  225. $name[ $i ] = $plugin['sanitized_plugin']; 
  226.  
  227. array_multisort( $type, SORT_DESC, $name, SORT_ASC, $items ); 
  228.  
  229. return $items; 
  230.  
  231. /** 
  232. * Get an associative array ( id => link ) of the views available on this table. 
  233. * @since 2.5.0 
  234. * @return array 
  235. */ 
  236. public function get_views() { 
  237. $status_links = array(); 
  238.  
  239. foreach ( $this->view_totals as $type => $count ) { 
  240. if ( $count < 1 ) { 
  241. continue; 
  242.  
  243. switch ( $type ) { 
  244. case 'all': 
  245. /** translators: 1: number of plugins. */ 
  246. $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins', 'enigma' ); 
  247. break; 
  248. case 'install': 
  249. /** translators: 1: number of plugins. */ 
  250. $text = _n( 'To Install <span class="count">(%s)</span>', 'To Install <span class="count">(%s)</span>', $count, 'enigma' ); 
  251. break; 
  252. case 'update': 
  253. /** translators: 1: number of plugins. */ 
  254. $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count, 'enigma' ); 
  255. break; 
  256. case 'activate': 
  257. /** translators: 1: number of plugins. */ 
  258. $text = _n( 'To Activate <span class="count">(%s)</span>', 'To Activate <span class="count">(%s)</span>', $count, 'enigma' ); 
  259. break; 
  260. default: 
  261. $text = ''; 
  262. break; 
  263.  
  264. if ( ! empty( $text ) ) { 
  265.  
  266. $status_links[ $type ] = sprintf( 
  267. '<a href="%s"%s>%s</a>',  
  268. esc_url( $this->tgmpa->get_tgmpa_status_url( $type ) ),  
  269. ( $type === $this->view_context ) ? ' class="current"' : '',  
  270. sprintf( $text, number_format_i18n( $count ) ) 
  271. ); 
  272.  
  273. return $status_links; 
  274.  
  275. /** 
  276. * Create default columns to display important plugin information 
  277. * like type, action and status. 
  278. * @since 2.2.0 
  279. * @param array $item Array of item data. 
  280. * @param string $column_name The name of the column. 
  281. * @return string 
  282. */ 
  283. public function column_default( $item, $column_name ) { 
  284. return $item[ $column_name ]; 
  285.  
  286. /** 
  287. * Required for bulk installing. 
  288. * Adds a checkbox for each plugin. 
  289. * @since 2.2.0 
  290. * @param array $item Array of item data. 
  291. * @return string The input checkbox with all necessary info. 
  292. */ 
  293. public function column_cb( $item ) { 
  294. return sprintf( 
  295. '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />',  
  296. esc_attr( $this->_args['singular'] ),  
  297. esc_attr( $item['slug'] ),  
  298. esc_attr( $item['sanitized_plugin'] ) 
  299. ); 
  300.  
  301. /** 
  302. * Create default title column along with the action links. 
  303. * @since 2.2.0 
  304. * @param array $item Array of item data. 
  305. * @return string The plugin name and action links. 
  306. */ 
  307. public function column_plugin( $item ) { 
  308. return sprintf( 
  309. '%1$s %2$s',  
  310. $item['plugin'],  
  311. $this->row_actions( $this->get_row_actions( $item ), true ) 
  312. ); 
  313.  
  314. /** 
  315. * Create version information column. 
  316. * @since 2.5.0 
  317. * @param array $item Array of item data. 
  318. * @return string HTML-formatted version information. 
  319. */ 
  320. public function column_version( $item ) { 
  321. $output = array(); 
  322.  
  323. if ( $this->tgmpa->is_plugin_installed( $item['slug'] ) ) { 
  324. $installed = ! empty( $item['installed_version'] ) ? $item['installed_version'] : _x( 'unknown', 'as in: "version nr unknown"', 'enigma' ); 
  325.  
  326. $color = ''; 
  327. if ( ! empty( $item['minimum_version'] ) && $this->tgmpa->does_plugin_require_update( $item['slug'] ) ) { 
  328. $color = ' color: #ff0000; font-weight: bold;'; 
  329.  
  330. $output[] = sprintf( 
  331. '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Installed version:', 'enigma' ) . '</p>',  
  332. $color,  
  333. $installed 
  334. ); 
  335.  
  336. if ( ! empty( $item['minimum_version'] ) ) { 
  337. $output[] = sprintf( 
  338. '<p><span style="min-width: 32px; text-align: right; float: right;">%1$s</span>' . __( 'Minimum required version:', 'enigma' ) . '</p>',  
  339. $item['minimum_version'] 
  340. ); 
  341.  
  342. if ( ! empty( $item['available_version'] ) ) { 
  343. $color = ''; 
  344. if ( ! empty( $item['minimum_version'] ) && version_compare( $item['available_version'], $item['minimum_version'], '>=' ) ) { 
  345. $color = ' color: #71C671; font-weight: bold;'; 
  346.  
  347. $output[] = sprintf( 
  348. '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Available version:', 'enigma' ) . '</p>',  
  349. $color,  
  350. $item['available_version'] 
  351. ); 
  352.  
  353. if ( empty( $output ) ) { 
  354. return ' '; // Let's not break the table layout. 
  355. } else { 
  356. return implode( "\n", $output ); 
  357.  
  358. /** 
  359. * Sets default message within the plugins table if no plugins 
  360. * are left for interaction. 
  361. * Hides the menu item to prevent the user from clicking and 
  362. * getting a permissions error. 
  363. * @since 2.2.0 
  364. */ 
  365. public function no_items() { 
  366. echo esc_html__( 'No plugins to install, update or activate.', 'enigma' ) . ' <a href="' . esc_url( self_admin_url() ) . '"> ' . esc_html__( 'Return to the Dashboard', 'enigma' ) . '</a>'; 
  367. echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>'; 
  368.  
  369. /** 
  370. * Output all the column information within the table. 
  371. * @since 2.2.0 
  372. * @return array $columns The column names. 
  373. */ 
  374. public function get_columns() { 
  375. $columns = array( 
  376. 'cb' => '<input type="checkbox" />',  
  377. 'plugin' => __( 'Plugin', 'enigma' ),  
  378. 'source' => __( 'Source', 'enigma' ),  
  379. 'type' => __( 'Type', 'enigma' ),  
  380. ); 
  381.  
  382. if ( 'all' === $this->view_context || 'update' === $this->view_context ) { 
  383. $columns['version'] = __( 'Version', 'enigma' ); 
  384. $columns['status'] = __( 'Status', 'enigma' ); 
  385.  
  386. return apply_filters( 'tgmpa_table_columns', $columns ); 
  387.  
  388. /** 
  389. * Get name of default primary column 
  390. * @since 2.5.0 / WP 4.3+ compatibility 
  391. * @access protected 
  392. * @return string 
  393. */ 
  394. protected function get_default_primary_column_name() { 
  395. return 'plugin'; 
  396.  
  397. /** 
  398. * Get the name of the primary column. 
  399. * @since 2.5.0 / WP 4.3+ compatibility 
  400. * @access protected 
  401. * @return string The name of the primary column. 
  402. */ 
  403. protected function get_primary_column_name() { 
  404. if ( method_exists( 'WP_List_Table', 'get_primary_column_name' ) ) { 
  405. return parent::get_primary_column_name(); 
  406. } else { 
  407. return $this->get_default_primary_column_name(); 
  408.  
  409. /** 
  410. * Get the actions which are relevant for a specific plugin row. 
  411. * @since 2.5.0 
  412. * @param array $item Array of item data. 
  413. * @return array Array with relevant action links. 
  414. */ 
  415. protected function get_row_actions( $item ) { 
  416. $actions = array(); 
  417. $action_links = array(); 
  418.  
  419. // Display the 'Install' action link if the plugin is not yet available. 
  420. if ( ! $this->tgmpa->is_plugin_installed( $item['slug'] ) ) { 
  421. /** translators: %2$s: plugin name in screen reader markup */ 
  422. $actions['install'] = __( 'Install %2$s', 'enigma' ); 
  423. } else { 
  424. // Display the 'Update' action link if an update is available and WP complies with plugin minimum. 
  425. if ( false !== $this->tgmpa->does_plugin_have_update( $item['slug'] ) && $this->tgmpa->can_plugin_update( $item['slug'] ) ) { 
  426. /** translators: %2$s: plugin name in screen reader markup */ 
  427. $actions['update'] = __( 'Update %2$s', 'enigma' ); 
  428.  
  429. // Display the 'Activate' action link, but only if the plugin meets the minimum version. 
  430. if ( $this->tgmpa->can_plugin_activate( $item['slug'] ) ) { 
  431. /** translators: %2$s: plugin name in screen reader markup */ 
  432. $actions['activate'] = __( 'Activate %2$s', 'enigma' ); 
  433.  
  434. // Create the actual links. 
  435. foreach ( $actions as $action => $text ) { 
  436. $nonce_url = wp_nonce_url( 
  437. add_query_arg( 
  438. array( 
  439. 'plugin' => urlencode( $item['slug'] ),  
  440. 'tgmpa-' . $action => $action . '-plugin',  
  441. ),  
  442. $this->tgmpa->get_tgmpa_url() 
  443. ),  
  444. 'tgmpa-' . $action,  
  445. 'tgmpa-nonce' 
  446. ); 
  447.  
  448. $action_links[ $action ] = sprintf( 
  449. '<a href="%1$s">' . esc_html( $text ) . '</a>', // $text contains the second placeholder. 
  450. esc_url( $nonce_url ),  
  451. '<span class="screen-reader-text">' . esc_html( $item['sanitized_plugin'] ) . '</span>' 
  452. ); 
  453.  
  454. $prefix = ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) ? 'network_admin_' : ''; 
  455. return apply_filters( "tgmpa_{$prefix}plugin_action_links", array_filter( $action_links ), $item['slug'], $item, $this->view_context ); 
  456.  
  457. /** 
  458. * Generates content for a single row of the table. 
  459. * @since 2.5.0 
  460. * @param object $item The current item. 
  461. */ 
  462. public function single_row( $item ) { 
  463. parent::single_row( $item ); 
  464.  
  465. /** 
  466. * Fires after each specific row in the TGMPA Plugins list table. 
  467. * The dynamic portion of the hook name, `$item['slug']`, refers to the slug 
  468. * for the plugin. 
  469. * @since 2.5.0 
  470. */ 
  471. do_action( "tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context ); 
  472.  
  473. /** 
  474. * Show the upgrade notice below a plugin row if there is one. 
  475. * @since 2.5.0 
  476. * @see /wp-admin/includes/update.php 
  477. * @param string $slug Plugin slug. 
  478. * @param array $item The information available in this table row. 
  479. * @return null Return early if upgrade notice is empty. 
  480. */ 
  481. public function wp_plugin_update_row( $slug, $item ) { 
  482. if ( empty( $item['upgrade_notice'] ) ) { 
  483. return; 
  484.  
  485. echo ' 
  486. <tr class="plugin-update-tr"> 
  487. <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange"> 
  488. <div class="update-message">',  
  489. esc_html__( 'Upgrade message from the plugin author:', 'enigma' ),  
  490. ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong> 
  491. </div> 
  492. </td> 
  493. </tr>'; 
  494.  
  495. /** 
  496. * Extra controls to be displayed between bulk actions and pagination. 
  497. * @since 2.5.0 
  498. * @param string $which 'top' or 'bottom' table navigation. 
  499. */ 
  500. public function extra_tablenav( $which ) { 
  501. if ( 'bottom' === $which ) { 
  502. $this->tgmpa->show_tgmpa_version(); 
  503.  
  504. /** 
  505. * Defines the bulk actions for handling registered plugins. 
  506. * @since 2.2.0 
  507. * @return array $actions The bulk actions for the plugin install table. 
  508. */ 
  509. public function get_bulk_actions() { 
  510.  
  511. $actions = array(); 
  512.  
  513. if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) { 
  514. if ( current_user_can( 'install_plugins' ) ) { 
  515. $actions['tgmpa-bulk-install'] = __( 'Install', 'enigma' ); 
  516.  
  517. if ( 'install' !== $this->view_context ) { 
  518. if ( current_user_can( 'update_plugins' ) ) { 
  519. $actions['tgmpa-bulk-update'] = __( 'Update', 'enigma' ); 
  520. if ( current_user_can( 'activate_plugins' ) ) { 
  521. $actions['tgmpa-bulk-activate'] = __( 'Activate', 'enigma' ); 
  522.  
  523. return $actions; 
  524.  
  525. /** 
  526. * Processes bulk installation and activation actions. 
  527. * The bulk installation process looks for the $_POST information and passes that 
  528. * through if a user has to use WP_Filesystem to enter their credentials. 
  529. * @since 2.2.0 
  530. */ 
  531. public function process_bulk_actions() { 
  532. // Bulk installation process. 
  533. if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) { 
  534.  
  535. check_admin_referer( 'bulk-' . $this->_args['plural'] ); 
  536.  
  537. $install_type = 'install'; 
  538. if ( 'tgmpa-bulk-update' === $this->current_action() ) { 
  539. $install_type = 'update'; 
  540.  
  541. $plugins_to_install = array(); 
  542.  
  543. // Did user actually select any plugins to install/update ? 
  544. if ( empty( $_POST['plugin'] ) ) { 
  545. if ( 'install' === $install_type ) { 
  546. $message = __( 'No plugins were selected to be installed. No action taken.', 'enigma' ); 
  547. } else { 
  548. $message = __( 'No plugins were selected to be updated. No action taken.', 'enigma' ); 
  549.  
  550. echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>'; 
  551.  
  552. return false; 
  553.  
  554. if ( is_array( $_POST['plugin'] ) ) { 
  555. $plugins_to_install = (array) $_POST['plugin']; 
  556. } elseif ( is_string( $_POST['plugin'] ) ) { 
  557. // Received via Filesystem page - un-flatten array (WP bug #19643). 
  558. $plugins_to_install = explode( ', ', $_POST['plugin'] ); 
  559.  
  560. // Sanitize the received input. 
  561. $plugins_to_install = array_map( 'urldecode', $plugins_to_install ); 
  562. $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install ); 
  563.  
  564. // Validate the received input. 
  565. foreach ( $plugins_to_install as $key => $slug ) { 
  566. // Check if the plugin was registered with TGMPA and remove if not. 
  567. if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) { 
  568. unset( $plugins_to_install[ $key ] ); 
  569. continue; 
  570.  
  571. // For install: make sure this is a plugin we *can* install and not one already installed. 
  572. if ( 'install' === $install_type && true === $this->tgmpa->is_plugin_installed( $slug ) ) { 
  573. unset( $plugins_to_install[ $key ] ); 
  574.  
  575. // For updates: make sure this is a plugin we *can* update (update available and WP version ok). 
  576. if ( 'update' === $install_type && false === $this->tgmpa->is_plugin_updatetable( $slug ) ) { 
  577. unset( $plugins_to_install[ $key ] ); 
  578.  
  579. // No need to proceed further if we have no plugins to handle. 
  580. if ( empty( $plugins_to_install ) ) { 
  581. if ( 'install' === $install_type ) { 
  582. $message = __( 'No plugins are available to be installed at this time.', 'enigma' ); 
  583. } else { 
  584. $message = __( 'No plugins are available to be updated at this time.', 'enigma' ); 
  585.  
  586. echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>'; 
  587.  
  588. return false; 
  589.  
  590. // Pass all necessary information if WP_Filesystem is needed. 
  591. $url = wp_nonce_url( 
  592. $this->tgmpa->get_tgmpa_url(),  
  593. 'bulk-' . $this->_args['plural'] 
  594. ); 
  595.  
  596. // Give validated data back to $_POST which is the only place the filesystem looks for extra fields. 
  597. $_POST['plugin'] = implode( ', ', $plugins_to_install ); // Work around for WP bug #19643. 
  598.  
  599. $method = ''; // Leave blank so WP_Filesystem can populate it as necessary. 
  600. $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem. 
  601.  
  602. if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) { 
  603. return true; // Stop the normal page form from displaying, credential request form will be shown. 
  604.  
  605. // Now we have some credentials, setup WP_Filesystem. 
  606. if ( ! WP_Filesystem( $creds ) ) { 
  607. // Our credentials were no good, ask the user for them again. 
  608. request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields ); 
  609.  
  610. return true; 
  611.  
  612. /** If we arrive here, we have the filesystem */ 
  613.  
  614. // Store all information in arrays since we are processing a bulk installation. 
  615. $names = array(); 
  616. $sources = array(); // Needed for installs. 
  617. $file_paths = array(); // Needed for upgrades. 
  618. $to_inject = array(); // Information to inject into the update_plugins transient. 
  619.  
  620. // Prepare the data for validated plugins for the install/upgrade. 
  621. foreach ( $plugins_to_install as $slug ) { 
  622. $name = $this->tgmpa->plugins[ $slug ]['name']; 
  623. $source = $this->tgmpa->get_download_url( $slug ); 
  624.  
  625. if ( ! empty( $name ) && ! empty( $source ) ) { 
  626. $names[] = $name; 
  627.  
  628. switch ( $install_type ) { 
  629.  
  630. case 'install': 
  631. $sources[] = $source; 
  632. break; 
  633.  
  634. case 'update': 
  635. $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path']; 
  636. $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ]; 
  637. $to_inject[ $slug ]['source'] = $source; 
  638. break; 
  639. unset( $slug, $name, $source ); 
  640.  
  641. // Create a new instance of TGMPA_Bulk_Installer. 
  642. $installer = new TGMPA_Bulk_Installer( 
  643. new TGMPA_Bulk_Installer_Skin( 
  644. array( 
  645. 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),  
  646. 'nonce' => 'bulk-' . $this->_args['plural'],  
  647. 'names' => $names,  
  648. 'install_type' => $install_type,  
  649. ); 
  650.  
  651. // Wrap the install process with the appropriate HTML. 
  652. echo '<div class="tgmpa">',  
  653. '<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> 
  654. <div class="update-php" style="width: 100%; height: 98%; min-height: 850px; padding-top: 1px;">'; 
  655.  
  656. // Process the bulk installation submissions. 
  657. add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 ); 
  658.  
  659. if ( 'tgmpa-bulk-update' === $this->current_action() ) { 
  660. // Inject our info into the update transient. 
  661. $this->tgmpa->inject_update_info( $to_inject ); 
  662.  
  663. $installer->bulk_upgrade( $file_paths ); 
  664. } else { 
  665. $installer->bulk_install( $sources ); 
  666.  
  667. remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1 ); 
  668.  
  669. echo '</div></div>'; 
  670.  
  671. return true; 
  672.  
  673. // Bulk activation process. 
  674. if ( 'tgmpa-bulk-activate' === $this->current_action() ) { 
  675. check_admin_referer( 'bulk-' . $this->_args['plural'] ); 
  676.  
  677. // Did user actually select any plugins to activate ? 
  678. if ( empty( $_POST['plugin'] ) ) { 
  679. echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'enigma' ), '</p></div>'; 
  680.  
  681. return false; 
  682.  
  683. // Grab plugin data from $_POST. 
  684. $plugins = array(); 
  685. if ( isset( $_POST['plugin'] ) ) { 
  686. $plugins = array_map( 'urldecode', (array) $_POST['plugin'] ); 
  687. $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins ); 
  688.  
  689. $plugins_to_activate = array(); 
  690. $plugin_names = array(); 
  691.  
  692. // Grab the file paths for the selected & inactive plugins from the registration array. 
  693. foreach ( $plugins as $slug ) { 
  694. if ( $this->tgmpa->can_plugin_activate( $slug ) ) { 
  695. $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path']; 
  696. $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name']; 
  697. unset( $slug ); 
  698.  
  699. // Return early if there are no plugins to activate. 
  700. if ( empty( $plugins_to_activate ) ) { 
  701. echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'enigma' ), '</p></div>'; 
  702.  
  703. return false; 
  704.  
  705. // Now we are good to go - let's start activating plugins. 
  706. $activate = activate_plugins( $plugins_to_activate ); 
  707.  
  708. if ( is_wp_error( $activate ) ) { 
  709. echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>'; 
  710. } else { 
  711. $count = count( $plugin_names ); // Count so we can use _n function. 
  712. $plugin_names = array_map( array( 'TGMPA_Utils', 'wrap_in_strong' ), $plugin_names ); 
  713. $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability. 
  714. $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'enigma' ) . ' ' . $last_plugin ); 
  715.  
  716. printf( // WPCS: xss ok. 
  717. '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',  
  718. esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'enigma' ) ),  
  719. $imploded 
  720. ); 
  721.  
  722. // Update recently activated plugins option. 
  723. $recent = (array) get_option( 'recently_activated' ); 
  724. foreach ( $plugins_to_activate as $plugin => $time ) { 
  725. if ( isset( $recent[ $plugin ] ) ) { 
  726. unset( $recent[ $plugin ] ); 
  727. update_option( 'recently_activated', $recent ); 
  728.  
  729. unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another. 
  730.  
  731. return true; 
  732.  
  733. return false; 
  734.  
  735. /** 
  736. * Prepares all of our information to be outputted into a usable table. 
  737. * @since 2.2.0 
  738. */ 
  739. public function prepare_items() { 
  740. $columns = $this->get_columns(); // Get all necessary column information. 
  741. $hidden = array(); // No columns to hide, but we must set as an array. 
  742. $sortable = array(); // No reason to make sortable columns. 
  743. $primary = $this->get_primary_column_name(); // Column which has the row actions. 
  744. $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers. 
  745.  
  746. // Process our bulk activations here. 
  747. if ( 'tgmpa-bulk-activate' === $this->current_action() ) { 
  748. $this->process_bulk_actions(); 
  749.  
  750. // Store all of our plugin data into $items array so WP_List_Table can use it. 
  751. $this->items = apply_filters( 'tgmpa_table_data_items', $this->_gather_plugin_data() ); 
  752.  
  753. /** *********** DEPRECATED METHODS *********** */ 
  754.  
  755. /** 
  756. * Retrieve plugin data, given the plugin name. 
  757. * @since 2.2.0 
  758. * @deprecated 2.5.0 use {@see TGM_Plugin_Activation::_get_plugin_data_from_name()} instead. 
  759. * @see TGM_Plugin_Activation::_get_plugin_data_from_name() 
  760. * @param string $name Name of the plugin, as it was registered. 
  761. * @param string $data Optional. Array key of plugin data to return. Default is slug. 
  762. * @return string|boolean Plugin slug if found, false otherwise. 
  763. */ 
  764. protected function _get_plugin_data_from_name( $name, $data = 'slug' ) { 
  765. _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'TGM_Plugin_Activation::_get_plugin_data_from_name()' ); 
  766.  
  767. return $this->tgmpa->_get_plugin_data_from_name( $name, $data );