TGMPA_List_Table

List table class for handling plugins.

Defined (1)

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

/includes/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', 'onetone' ); 
  152.  
  153. return __( 'Recommended', 'onetone' ); 
  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', 'onetone' ); 
  167. break; 
  168. case 'external': 
  169. $string = __( 'External Source', 'onetone' ); 
  170. break; 
  171. case 'bundled': 
  172. $string = __( 'Pre-Packaged', 'onetone' ); 
  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', 'onetone' ); 
  186.  
  187. if ( ! $this->tgmpa->is_plugin_active( $slug ) ) { 
  188. $install_status = __( 'Installed But Not Activated', 'onetone' ); 
  189. } else { 
  190. $install_status = __( 'Active', 'onetone' ); 
  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', 'onetone' ); 
  196.  
  197. } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) { 
  198. $update_status = __( 'Requires Update', 'onetone' ); 
  199.  
  200. } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) { 
  201. $update_status = __( 'Update recommended', 'onetone' ); 
  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', 'onetone' ),  
  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', 'onetone' ); 
  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, 'onetone' ); 
  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, 'onetone' ); 
  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, 'onetone' ); 
  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"', 'onetone' ); 
  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:', 'onetone' ) . '</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:', 'onetone' ) . '</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:', 'onetone' ) . '</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.', 'onetone' ) . ' <a href="' . esc_url( self_admin_url() ) . '"> ' . esc_html__( 'Return to the Dashboard', 'onetone' ) . '</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', 'onetone' ),  
  378. 'source' => __( 'Source', 'onetone' ),  
  379. 'type' => __( 'Type', 'onetone' ),  
  380. ); 
  381.  
  382. if ( 'all' === $this->view_context || 'update' === $this->view_context ) { 
  383. $columns['version'] = __( 'Version', 'onetone' ); 
  384. $columns['status'] = __( 'Status', 'onetone' ); 
  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', 'onetone' ); 
  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', 'onetone' ); 
  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', 'onetone' ); 
  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. echo '<tr class="' . esc_attr( 'tgmpa-type-' . strtolower( $item['type'] ) ) . '">'; 
  464. $this->single_row_columns( $item ); 
  465. echo '</tr>'; 
  466.  
  467. /** 
  468. * Fires after each specific row in the TGMPA Plugins list table. 
  469. * The dynamic portion of the hook name, `$item['slug']`, refers to the slug 
  470. * for the plugin. 
  471. * @since 2.5.0 
  472. */ 
  473. do_action( "tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context ); 
  474.  
  475. /** 
  476. * Show the upgrade notice below a plugin row if there is one. 
  477. * @since 2.5.0 
  478. * @see /wp-admin/includes/update.php 
  479. * @param string $slug Plugin slug. 
  480. * @param array $item The information available in this table row. 
  481. * @return null Return early if upgrade notice is empty. 
  482. */ 
  483. public function wp_plugin_update_row( $slug, $item ) { 
  484. if ( empty( $item['upgrade_notice'] ) ) { 
  485. return; 
  486.  
  487. echo ' 
  488. <tr class="plugin-update-tr"> 
  489. <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange"> 
  490. <div class="update-message">',  
  491. esc_html__( 'Upgrade message from the plugin author:', 'onetone' ),  
  492. ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong> 
  493. </div> 
  494. </td> 
  495. </tr>'; 
  496.  
  497. /** 
  498. * Extra controls to be displayed between bulk actions and pagination. 
  499. * @since 2.5.0 
  500. * @param string $which 'top' or 'bottom' table navigation. 
  501. */ 
  502. public function extra_tablenav( $which ) { 
  503. if ( 'bottom' === $which ) { 
  504. $this->tgmpa->show_tgmpa_version(); 
  505.  
  506. /** 
  507. * Defines the bulk actions for handling registered plugins. 
  508. * @since 2.2.0 
  509. * @return array $actions The bulk actions for the plugin install table. 
  510. */ 
  511. public function get_bulk_actions() { 
  512.  
  513. $actions = array(); 
  514.  
  515. if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) { 
  516. if ( current_user_can( 'install_plugins' ) ) { 
  517. $actions['tgmpa-bulk-install'] = __( 'Install', 'onetone' ); 
  518.  
  519. if ( 'install' !== $this->view_context ) { 
  520. if ( current_user_can( 'update_plugins' ) ) { 
  521. $actions['tgmpa-bulk-update'] = __( 'Update', 'onetone' ); 
  522. if ( current_user_can( 'activate_plugins' ) ) { 
  523. $actions['tgmpa-bulk-activate'] = __( 'Activate', 'onetone' ); 
  524.  
  525. return $actions; 
  526.  
  527. /** 
  528. * Processes bulk installation and activation actions. 
  529. * The bulk installation process looks for the $_POST information and passes that 
  530. * through if a user has to use WP_Filesystem to enter their credentials. 
  531. * @since 2.2.0 
  532. */ 
  533. public function process_bulk_actions() { 
  534. // Bulk installation process. 
  535. if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) { 
  536.  
  537. check_admin_referer( 'bulk-' . $this->_args['plural'] ); 
  538.  
  539. $install_type = 'install'; 
  540. if ( 'tgmpa-bulk-update' === $this->current_action() ) { 
  541. $install_type = 'update'; 
  542.  
  543. $plugins_to_install = array(); 
  544.  
  545. // Did user actually select any plugins to install/update ? 
  546. if ( empty( $_POST['plugin'] ) ) { 
  547. if ( 'install' === $install_type ) { 
  548. $message = __( 'No plugins were selected to be installed. No action taken.', 'onetone' ); 
  549. } else { 
  550. $message = __( 'No plugins were selected to be updated. No action taken.', 'onetone' ); 
  551.  
  552. echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>'; 
  553.  
  554. return false; 
  555.  
  556. if ( is_array( $_POST['plugin'] ) ) { 
  557. $plugins_to_install = (array) $_POST['plugin']; 
  558. } elseif ( is_string( $_POST['plugin'] ) ) { 
  559. // Received via Filesystem page - un-flatten array (WP bug #19643). 
  560. $plugins_to_install = explode( ', ', $_POST['plugin'] ); 
  561.  
  562. // Sanitize the received input. 
  563. $plugins_to_install = array_map( 'urldecode', $plugins_to_install ); 
  564. $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install ); 
  565.  
  566. // Validate the received input. 
  567. foreach ( $plugins_to_install as $key => $slug ) { 
  568. // Check if the plugin was registered with TGMPA and remove if not. 
  569. if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) { 
  570. unset( $plugins_to_install[ $key ] ); 
  571. continue; 
  572.  
  573. // For install: make sure this is a plugin we *can* install and not one already installed. 
  574. if ( 'install' === $install_type && true === $this->tgmpa->is_plugin_installed( $slug ) ) { 
  575. unset( $plugins_to_install[ $key ] ); 
  576.  
  577. // For updates: make sure this is a plugin we *can* update (update available and WP version ok). 
  578. if ( 'update' === $install_type && false === $this->tgmpa->is_plugin_updatetable( $slug ) ) { 
  579. unset( $plugins_to_install[ $key ] ); 
  580.  
  581. // No need to proceed further if we have no plugins to handle. 
  582. if ( empty( $plugins_to_install ) ) { 
  583. if ( 'install' === $install_type ) { 
  584. $message = __( 'No plugins are available to be installed at this time.', 'onetone' ); 
  585. } else { 
  586. $message = __( 'No plugins are available to be updated at this time.', 'onetone' ); 
  587.  
  588. echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>'; 
  589.  
  590. return false; 
  591.  
  592. // Pass all necessary information if WP_Filesystem is needed. 
  593. $url = wp_nonce_url( 
  594. $this->tgmpa->get_tgmpa_url(),  
  595. 'bulk-' . $this->_args['plural'] 
  596. ); 
  597.  
  598. // Give validated data back to $_POST which is the only place the filesystem looks for extra fields. 
  599. $_POST['plugin'] = implode( ', ', $plugins_to_install ); // Work around for WP bug #19643. 
  600.  
  601. $method = ''; // Leave blank so WP_Filesystem can populate it as necessary. 
  602. $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem. 
  603.  
  604. if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) { 
  605. return true; // Stop the normal page form from displaying, credential request form will be shown. 
  606.  
  607. // Now we have some credentials, setup WP_Filesystem. 
  608. if ( ! WP_Filesystem( $creds ) ) { 
  609. // Our credentials were no good, ask the user for them again. 
  610. request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields ); 
  611.  
  612. return true; 
  613.  
  614. /** If we arrive here, we have the filesystem */ 
  615.  
  616. // Store all information in arrays since we are processing a bulk installation. 
  617. $names = array(); 
  618. $sources = array(); // Needed for installs. 
  619. $file_paths = array(); // Needed for upgrades. 
  620. $to_inject = array(); // Information to inject into the update_plugins transient. 
  621.  
  622. // Prepare the data for validated plugins for the install/upgrade. 
  623. foreach ( $plugins_to_install as $slug ) { 
  624. $name = $this->tgmpa->plugins[ $slug ]['name']; 
  625. $source = $this->tgmpa->get_download_url( $slug ); 
  626.  
  627. if ( ! empty( $name ) && ! empty( $source ) ) { 
  628. $names[] = $name; 
  629.  
  630. switch ( $install_type ) { 
  631.  
  632. case 'install': 
  633. $sources[] = $source; 
  634. break; 
  635.  
  636. case 'update': 
  637. $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path']; 
  638. $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ]; 
  639. $to_inject[ $slug ]['source'] = $source; 
  640. break; 
  641. unset( $slug, $name, $source ); 
  642.  
  643. // Create a new instance of TGMPA_Bulk_Installer. 
  644. $installer = new TGMPA_Bulk_Installer( 
  645. new TGMPA_Bulk_Installer_Skin( 
  646. array( 
  647. 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),  
  648. 'nonce' => 'bulk-' . $this->_args['plural'],  
  649. 'names' => $names,  
  650. 'install_type' => $install_type,  
  651. ); 
  652.  
  653. // Wrap the install process with the appropriate HTML. 
  654. echo '<div class="tgmpa">',  
  655. '<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> 
  656. <div class="update-php" style="width: 100%; height: 98%; min-height: 850px; padding-top: 1px;">'; 
  657.  
  658. // Process the bulk installation submissions. 
  659. add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 ); 
  660.  
  661. if ( 'tgmpa-bulk-update' === $this->current_action() ) { 
  662. // Inject our info into the update transient. 
  663. $this->tgmpa->inject_update_info( $to_inject ); 
  664.  
  665. $installer->bulk_upgrade( $file_paths ); 
  666. } else { 
  667. $installer->bulk_install( $sources ); 
  668.  
  669. remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1 ); 
  670.  
  671. echo '</div></div>'; 
  672.  
  673. return true; 
  674.  
  675. // Bulk activation process. 
  676. if ( 'tgmpa-bulk-activate' === $this->current_action() ) { 
  677. check_admin_referer( 'bulk-' . $this->_args['plural'] ); 
  678.  
  679. // Did user actually select any plugins to activate ? 
  680. if ( empty( $_POST['plugin'] ) ) { 
  681. echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'onetone' ), '</p></div>'; 
  682.  
  683. return false; 
  684.  
  685. // Grab plugin data from $_POST. 
  686. $plugins = array(); 
  687. if ( isset( $_POST['plugin'] ) ) { 
  688. $plugins = array_map( 'urldecode', (array) $_POST['plugin'] ); 
  689. $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins ); 
  690.  
  691. $plugins_to_activate = array(); 
  692. $plugin_names = array(); 
  693.  
  694. // Grab the file paths for the selected & inactive plugins from the registration array. 
  695. foreach ( $plugins as $slug ) { 
  696. if ( $this->tgmpa->can_plugin_activate( $slug ) ) { 
  697. $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path']; 
  698. $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name']; 
  699. unset( $slug ); 
  700.  
  701. // Return early if there are no plugins to activate. 
  702. if ( empty( $plugins_to_activate ) ) { 
  703. echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'onetone' ), '</p></div>'; 
  704.  
  705. return false; 
  706.  
  707. // Now we are good to go - let's start activating plugins. 
  708. $activate = activate_plugins( $plugins_to_activate ); 
  709.  
  710. if ( is_wp_error( $activate ) ) { 
  711. echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>'; 
  712. } else { 
  713. $count = count( $plugin_names ); // Count so we can use _n function. 
  714. $plugin_names = array_map( array( 'TGMPA_Utils', 'wrap_in_strong' ), $plugin_names ); 
  715. $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability. 
  716. $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'onetone' ) . ' ' . $last_plugin ); 
  717.  
  718. printf( // WPCS: xss ok. 
  719. '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',  
  720. esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'onetone' ) ),  
  721. $imploded 
  722. ); 
  723.  
  724. // Update recently activated plugins option. 
  725. $recent = (array) get_option( 'recently_activated' ); 
  726. foreach ( $plugins_to_activate as $plugin => $time ) { 
  727. if ( isset( $recent[ $plugin ] ) ) { 
  728. unset( $recent[ $plugin ] ); 
  729. update_option( 'recently_activated', $recent ); 
  730.  
  731. unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another. 
  732.  
  733. return true; 
  734.  
  735. return false; 
  736.  
  737. /** 
  738. * Prepares all of our information to be outputted into a usable table. 
  739. * @since 2.2.0 
  740. */ 
  741. public function prepare_items() { 
  742. $columns = $this->get_columns(); // Get all necessary column information. 
  743. $hidden = array(); // No columns to hide, but we must set as an array. 
  744. $sortable = array(); // No reason to make sortable columns. 
  745. $primary = $this->get_primary_column_name(); // Column which has the row actions. 
  746. $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers. 
  747.  
  748. // Process our bulk activations here. 
  749. if ( 'tgmpa-bulk-activate' === $this->current_action() ) { 
  750. $this->process_bulk_actions(); 
  751.  
  752. // Store all of our plugin data into $items array so WP_List_Table can use it. 
  753. $this->items = apply_filters( 'tgmpa_table_data_items', $this->_gather_plugin_data() ); 
  754.  
  755. /** *********** DEPRECATED METHODS *********** */ 
  756.  
  757. /** 
  758. * Retrieve plugin data, given the plugin name. 
  759. * @since 2.2.0 
  760. * @deprecated 2.5.0 use {@see TGM_Plugin_Activation::_get_plugin_data_from_name()} instead. 
  761. * @see TGM_Plugin_Activation::_get_plugin_data_from_name() 
  762. * @param string $name Name of the plugin, as it was registered. 
  763. * @param string $data Optional. Array key of plugin data to return. Default is slug. 
  764. * @return string|boolean Plugin slug if found, false otherwise. 
  765. */ 
  766. protected function _get_plugin_data_from_name( $name, $data = 'slug' ) { 
  767. _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'TGM_Plugin_Activation::_get_plugin_data_from_name()' ); 
  768.  
  769. return $this->tgmpa->_get_plugin_data_from_name( $name, $data );