class.jetpack-modules-list-table.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. <?php
  2. if ( ! class_exists( 'WP_List_Table' ) )
  3. require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
  4. class Jetpack_Modules_List_Table extends WP_List_Table {
  5. function __construct() {
  6. parent::__construct();
  7. Jetpack::init();
  8. // In WP 4.2 WP_List_Table will be sanitizing which values are __set()
  9. global $wp_version;
  10. if ( version_compare( $wp_version, '4.2-z', '>=' ) && $this->compat_fields && is_array( $this->compat_fields ) ) {
  11. array_push( $this->compat_fields, 'all_items' );
  12. }
  13. $this->items = $this->all_items = Jetpack_Admin::init()->get_modules();
  14. $this->items = $this->filter_displayed_table_items( $this->items );
  15. /**
  16. * Filters the list of modules available to be displayed in the Jetpack Settings screen.
  17. *
  18. * @since 3.0.0
  19. *
  20. * @param array $this->items Array of Jetpack modules.
  21. */
  22. $this->items = apply_filters( 'jetpack_modules_list_table_items', $this->items );
  23. $this->_column_headers = array( $this->get_columns(), array(), array(), 'name' );
  24. $modal_info = isset( $_GET['info'] ) ? $_GET['info'] : false;
  25. wp_register_script(
  26. 'models.jetpack-modules',
  27. Jetpack::get_file_url_for_environment(
  28. '_inc/build/jetpack-modules.models.min.js',
  29. '_inc/jetpack-modules.models.js'
  30. ),
  31. array( 'backbone', 'underscore' ),
  32. JETPACK__VERSION
  33. );
  34. wp_register_script(
  35. 'views.jetpack-modules',
  36. Jetpack::get_file_url_for_environment(
  37. '_inc/build/jetpack-modules.views.min.js',
  38. '_inc/jetpack-modules.views.js'
  39. ),
  40. array( 'backbone', 'underscore', 'wp-util' ),
  41. JETPACK__VERSION
  42. );
  43. wp_register_script(
  44. 'jetpack-modules-list-table',
  45. Jetpack::get_file_url_for_environment(
  46. '_inc/build/jetpack-modules.min.js',
  47. '_inc/jetpack-modules.js'
  48. ),
  49. array(
  50. 'views.jetpack-modules',
  51. 'models.jetpack-modules',
  52. 'jquery',
  53. ),
  54. JETPACK__VERSION,
  55. true
  56. );
  57. wp_localize_script( 'jetpack-modules-list-table', 'jetpackModulesData', array(
  58. 'modules' => Jetpack::get_translated_modules( $this->all_items ),
  59. 'i18n' => array(
  60. 'search_placeholder' => __( 'Search Modules…', 'jetpack' ),
  61. ),
  62. 'modalinfo' => $this->module_info_check( $modal_info, $this->all_items ),
  63. 'nonces' => array(
  64. 'bulk' => wp_create_nonce( 'bulk-jetpack_page_jetpack_modules' ),
  65. ),
  66. ) );
  67. wp_enqueue_script( 'jetpack-modules-list-table' );
  68. /**
  69. * Filters the js_templates callback value.
  70. *
  71. * @since 3.6.0
  72. *
  73. * @param array array( $this, 'js_templates' ) js_templates callback.
  74. */
  75. add_action( 'admin_footer', apply_filters( 'jetpack_modules_list_table_js_template_callback', array( $this, 'js_templates' ) ), 9 );
  76. }
  77. function js_templates() {
  78. ?>
  79. <script type="text/html" id="tmpl-Jetpack_Modules_List_Table_Template">
  80. <# var i = 0;
  81. if ( data.items.length ) {
  82. _.each( data.items, function( item, key, list ) {
  83. if ( item === undefined ) return;
  84. if ( 'manage' == item.module && item.activated ) return; #>
  85. <tr class="jetpack-module <# if ( ++i % 2 ) { #> alternate<# } #><# if ( item.activated ) { #> active<# } #><# if ( ! item.available ) { #> unavailable<# } #>" id="{{{ item.module }}}">
  86. <th scope="row" class="check-column">
  87. <# if ( 'videopress' !== item.module ) { #>
  88. <input type="checkbox" name="modules[]" value="{{{ item.module }}}" />
  89. <# } #>
  90. </th>
  91. <td class='name column-name'>
  92. <span class='info'><a href="{{{item.learn_more_button}}}" target="blank">{{{ item.name }}}</a></span>
  93. <div class="row-actions">
  94. <# if ( item.configurable ) { #>
  95. <span class='configure'>{{{ item.configurable }}}</span>
  96. <# } #>
  97. <# if ( item.activated && 'vaultpress' !== item.module && item.available && 'videopress' !== item.module ) { #>
  98. <span class='delete'><a href="<?php echo admin_url( 'admin.php' ); ?>?page=jetpack&#038;action=deactivate&#038;module={{{ item.module }}}&#038;_wpnonce={{{ item.deactivate_nonce }}}"><?php _e( 'Deactivate', 'jetpack' ); ?></a></span>
  99. <# } else if ( item.available && 'videopress' !== item.module ) { #>
  100. <span class='activate'><a href="<?php echo admin_url( 'admin.php' ); ?>?page=jetpack&#038;action=activate&#038;module={{{ item.module }}}&#038;_wpnonce={{{ item.activate_nonce }}}"><?php _e( 'Activate', 'jetpack' ); ?></a></span>
  101. <# } #>
  102. </div>
  103. </td>
  104. </tr>
  105. <#
  106. });
  107. } else {
  108. #>
  109. <tr class="no-modules-found">
  110. <td colspan="2"><?php esc_html_e( 'No Modules Found' , 'jetpack' ); ?></td>
  111. </tr>
  112. <#
  113. }
  114. #>
  115. </script>
  116. <?php
  117. }
  118. function get_views() {
  119. $modules = Jetpack_Admin::init()->get_modules();
  120. $array_of_module_tags = wp_list_pluck( $modules, 'module_tags' );
  121. $module_tags = call_user_func_array( 'array_merge', $array_of_module_tags );
  122. $module_tags_unique = array_count_values( $module_tags );
  123. ksort( $module_tags_unique );
  124. $format = '<a href="%3$s"%4$s data-title="%1$s">%1$s <span class="count">(%2$s)</span></a>';
  125. $title = __( 'All', 'jetpack' );
  126. $count = count( $modules );
  127. $url = esc_url( remove_query_arg( 'module_tag' ) );
  128. $current = empty( $_GET['module_tag'] ) ? ' class="current all"' : ' class="all"';
  129. $views = array(
  130. 'all' => sprintf( $format, $title, $count, $url, $current ),
  131. );
  132. foreach ( $module_tags_unique as $title => $count ) {
  133. if ( 'Jumpstart' == $title ) {
  134. continue;
  135. }
  136. $key = sanitize_title( $title );
  137. if ( 'centralized-management' === $key && Jetpack::is_module_active( 'manage' ) ) {
  138. continue;
  139. }
  140. $display_title = esc_html( wptexturize( $title ) );
  141. $url = esc_url( add_query_arg( 'module_tag', urlencode( $title ) ) );
  142. $current = '';
  143. if ( ! empty( $_GET['module_tag'] ) && $title == $_GET['module_tag'] )
  144. $current = ' class="current"';
  145. $views[ $key ] = sprintf( $format, $display_title, $count, $url, $current );
  146. }
  147. return $views;
  148. }
  149. function views() {
  150. $views = $this->get_views();
  151. echo "<ul class='subsubsub'>\n";
  152. foreach ( $views as $class => $view ) {
  153. $views[ $class ] = "\t<li class='$class'>$view</li>";
  154. }
  155. echo implode( "\n", $views ) . "\n";
  156. echo "</ul>";
  157. }
  158. function filter_displayed_table_items( $modules ) {
  159. return array_filter( $modules, array( $this, 'is_module_displayed' ) );
  160. }
  161. static function is_module_displayed( $module ) {
  162. // Handle module tag based filtering.
  163. if ( ! empty( $_REQUEST['module_tag'] ) ) {
  164. $module_tag = sanitize_text_field( $_REQUEST['module_tag'] );
  165. if ( ! in_array( $module_tag, $module['module_tags'] ) )
  166. return false;
  167. }
  168. // If nothing rejected it, include it!
  169. return true;
  170. }
  171. static function sort_requires_connection_last( $module1, $module2 ) {
  172. if ( $module1['requires_connection'] == $module2['requires_connection'] )
  173. return 0;
  174. if ( $module1['requires_connection'] )
  175. return 1;
  176. if ( $module2['requires_connection'] )
  177. return -1;
  178. return 0;
  179. }
  180. function get_columns() {
  181. $columns = array(
  182. 'cb' => '<input type="checkbox" />',
  183. 'name' => __( 'Name', 'jetpack' ),
  184. );
  185. return $columns;
  186. }
  187. function get_bulk_actions() {
  188. $actions = array(
  189. 'bulk-activate' => __( 'Activate', 'jetpack' ),
  190. 'bulk-deactivate' => __( 'Deactivate', 'jetpack' ),
  191. );
  192. return $actions;
  193. }
  194. function single_row( $item ) {
  195. static $i = 0;
  196. $row_class = ( ++$i % 2 ) ? ' alternate' : '';
  197. if ( ! empty( $item['activated'] ) )
  198. $row_class .= ' active';
  199. if ( ! Jetpack_Admin::is_module_available( $item ) ) {
  200. $row_class .= ' unavailable';
  201. }
  202. echo '<tr class="jetpack-module' . esc_attr( $row_class ) . '" id="' . esc_attr( $item['module'] ) . '">';
  203. $this->single_row_columns( $item );
  204. echo '</tr>';
  205. }
  206. function get_table_classes() {
  207. return array( 'table', 'table-bordered', 'wp-list-table', 'widefat', 'fixed', 'jetpack-modules' );
  208. }
  209. function column_cb( $item ) {
  210. if ( ! Jetpack_Admin::is_module_available( $item ) )
  211. return '';
  212. return sprintf( '<input type="checkbox" name="modules[]" value="%s" />', $item['module'] );
  213. }
  214. function column_icon( $item ) {
  215. $badge_text = $free_text = '';
  216. ob_start();
  217. ?>
  218. <a href="#TB_inline?width=600&height=550&inlineId=more-info-module-settings-modal" class="thickbox">
  219. <div class="module-image">
  220. <p><span class="module-image-badge"><?php echo $badge_text; ?></span><span class="module-image-free" style="display: none"><?php echo $free_text; ?></span></p>
  221. </div>
  222. </a>
  223. <?php
  224. return ob_get_clean();
  225. }
  226. function column_name( $item ) {
  227. $actions = array(
  228. 'info' => sprintf( '<a href="%s" target="blank">%s</a>', esc_url( $item['learn_more_button'] ), esc_html__( 'Feature Info', 'jetpack' ) ),
  229. );
  230. if ( ! empty( $item['configurable'] ) ) {
  231. $actions['configure'] = $item['configurable'];
  232. }
  233. if ( empty( $item['activated'] ) && Jetpack_Admin::is_module_available( $item ) ) {
  234. $url = wp_nonce_url(
  235. Jetpack::admin_url( array(
  236. 'page' => 'jetpack',
  237. 'action' => 'activate',
  238. 'module' => $item['module'],
  239. ) ),
  240. 'jetpack_activate-' . $item['module']
  241. );
  242. $actions['activate'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html__( 'Activate', 'jetpack' ) );
  243. } elseif ( ! empty( $item['activated'] ) ) {
  244. $url = wp_nonce_url(
  245. Jetpack::admin_url( array(
  246. 'page' => 'jetpack',
  247. 'action' => 'deactivate',
  248. 'module' => $item['module'],
  249. ) ),
  250. 'jetpack_deactivate-' . $item['module']
  251. );
  252. $actions['delete'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html__( 'Deactivate', 'jetpack' ) );
  253. }
  254. return $this->row_actions( $actions ) . wptexturize( $item['name'] );
  255. }
  256. function column_description( $item ) {
  257. ob_start();
  258. /** This action is documented in class.jetpack-admin.php */
  259. echo apply_filters( 'jetpack_short_module_description', $item['description'], $item['module'] );
  260. /** This action is documented in class.jetpack-admin.php */
  261. do_action( 'jetpack_learn_more_button_' . $item['module'] );
  262. echo '<div id="more-info-' . $item['module'] . '" class="more-info">';
  263. /** This action is documented in class.jetpack-admin.php */
  264. do_action( 'jetpack_module_more_info_' . $item['module'] );
  265. echo '</div>';
  266. return ob_get_clean();
  267. }
  268. function column_module_tags( $item ) {
  269. $module_tags = array();
  270. foreach( $item['module_tags'] as $module_tag ) {
  271. $module_tags[] = sprintf( '<a href="%3$s" data-title="%2$s">%1$s</a>', esc_html( $module_tag ), esc_attr( $module_tag ), esc_url( add_query_arg( 'module_tag', urlencode( $module_tag ) ) ) );
  272. }
  273. return implode( ', ', $module_tags );
  274. }
  275. function column_default( $item, $column_name ) {
  276. switch ( $column_name ) {
  277. case 'icon':
  278. case 'name':
  279. case 'description':
  280. break;
  281. default:
  282. return print_r( $item, true );
  283. }
  284. }
  285. //Check if the info parameter provided in the URL corresponds to an actual module
  286. function module_info_check( $info = false, $modules ) {
  287. if ( false == $info ) {
  288. return false;
  289. } else if ( array_key_exists( $info, $modules ) ) {
  290. return $info;
  291. }
  292. }
  293. /**
  294. * Core switched their `display_tablenav()` method to protected, so we can't access it directly.
  295. * Instead, let's include an access function to make it doable without errors!
  296. *
  297. * @see https://github.com/WordPress/WordPress/commit/d28f6344de97616de8ece543ed290c4ba2383622
  298. *
  299. * @param string $which
  300. *
  301. * @return mixed
  302. */
  303. function unprotected_display_tablenav( $which = 'top' ) {
  304. return $this->display_tablenav( $which );
  305. }
  306. }