PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 10.7.2
Jetpack – WP Security, Backup, Speed, & Growth v10.7.2
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / class.jetpack-modules-list-table.php
jetpack Last commit date
3rd-party 4 years ago _inc 4 years ago css 4 years ago extensions 4 years ago images 4 years ago jetpack_vendor 4 years ago json-endpoints 3 years ago modules 1 year ago sal 4 years ago src 4 years ago vendor 4 years ago views 4 years ago CHANGELOG.md 4 years ago LICENSE.txt 5 years ago SECURITY.md 5 years ago class-jetpack-connection-status.php 5 years ago class-jetpack-pre-connection-jitms.php 4 years ago class-jetpack-recommendations-banner.php 4 years ago class-jetpack-stats-dashboard-widget.php 4 years ago class-jetpack-wizard-banner.php 5 years ago class-jetpack-xmlrpc-methods.php 5 years ago class.frame-nonce-preview.php 4 years ago class.jetpack-admin.php 4 years ago class.jetpack-affiliate.php 4 years ago class.jetpack-autoupdate.php 4 years ago class.jetpack-bbpress-json-api.compat.php 5 years ago class.jetpack-cli.php 4 years ago class.jetpack-client-server.php 4 years ago class.jetpack-connection-banner.php 4 years ago class.jetpack-data.php 5 years ago class.jetpack-gutenberg.php 4 years ago class.jetpack-heartbeat.php 4 years ago class.jetpack-idc.php 4 years ago class.jetpack-modules-list-table.php 4 years ago class.jetpack-network-sites-list-table.php 4 years ago class.jetpack-network.php 4 years ago class.jetpack-plan.php 4 years ago class.jetpack-post-images.php 4 years ago class.jetpack-twitter-cards.php 5 years ago class.jetpack-user-agent.php 4 years ago class.jetpack.php 4 years ago class.json-api-endpoints.php 3 years ago class.json-api.php 4 years ago class.photon.php 4 years ago composer.json 4 years ago functions.compat.php 5 years ago functions.cookies.php 5 years ago functions.gallery.php 6 years ago functions.global.php 4 years ago functions.opengraph.php 4 years ago functions.photon.php 4 years ago jest.config.js 4 years ago jetpack.php 1 year ago json-api-config.php 5 years ago json-endpoints.php 7 years ago load-jetpack.php 4 years ago locales.php 7 years ago readme.txt 1 year ago require-lib.php 5 years ago uninstall.php 5 years ago wpml-config.xml 10 years ago
class.jetpack-modules-list-table.php
470 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Jetpack modules list table.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Assets;
9
10 if ( ! class_exists( 'WP_List_Table' ) ) {
11 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
12 }
13
14 /**
15 * Jetpack modules list table.
16 */
17 class Jetpack_Modules_List_Table extends WP_List_Table {
18
19 /** Constructor. */
20 public function __construct() {
21 parent::__construct();
22
23 Jetpack::init();
24
25 if ( $this->compat_fields && is_array( $this->compat_fields ) ) {
26 array_push( $this->compat_fields, 'all_items' );
27 }
28
29 /**
30 * Filters the list of modules available to be displayed in the Jetpack Settings screen.
31 *
32 * @since 3.0.0
33 *
34 * @param array $modules Array of Jetpack modules.
35 */
36 $this->all_items = apply_filters( 'jetpack_modules_list_table_items', Jetpack_Admin::init()->get_modules() );
37 $this->items = $this->all_items;
38 $this->items = $this->filter_displayed_table_items( $this->items );
39 $this->_column_headers = array( $this->get_columns(), array(), array(), 'name' );
40 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a view, not a model or controller.
41 $modal_info = isset( $_GET['info'] ) ? $_GET['info'] : false;
42
43 wp_register_script(
44 'models.jetpack-modules',
45 Assets::get_file_url_for_environment(
46 '_inc/build/jetpack-modules.models.min.js',
47 '_inc/jetpack-modules.models.js'
48 ),
49 array( 'backbone', 'underscore' ),
50 JETPACK__VERSION,
51 false // @todo Can this be put in the footer?
52 );
53 wp_register_script(
54 'views.jetpack-modules',
55 Assets::get_file_url_for_environment(
56 '_inc/build/jetpack-modules.views.min.js',
57 '_inc/jetpack-modules.views.js'
58 ),
59 array( 'backbone', 'underscore', 'wp-util' ),
60 JETPACK__VERSION,
61 false // @todo Can this be put in the footer?
62 );
63 wp_register_script(
64 'jetpack-modules-list-table',
65 Assets::get_file_url_for_environment(
66 '_inc/build/jetpack-modules.min.js',
67 '_inc/jetpack-modules.js'
68 ),
69 array(
70 'views.jetpack-modules',
71 'models.jetpack-modules',
72 'jquery',
73 ),
74 JETPACK__VERSION,
75 true
76 );
77
78 wp_localize_script(
79 'jetpack-modules-list-table',
80 'jetpackModulesData',
81 array(
82 'modules' => Jetpack::get_translated_modules( $this->all_items ),
83 'i18n' => array(
84 'search_placeholder' => __( 'Search Modules…', 'jetpack' ),
85 ),
86 'modalinfo' => $this->module_info_check( $modal_info, $this->all_items ),
87 'nonces' => array(
88 'bulk' => wp_create_nonce( 'bulk-jetpack_page_jetpack_modules' ),
89 ),
90 )
91 );
92
93 wp_enqueue_script( 'jetpack-modules-list-table' );
94
95 /**
96 * Filters the js_templates callback value.
97 *
98 * @since 3.6.0
99 *
100 * @param array array( $this, 'js_templates' ) js_templates callback.
101 */
102 add_action( 'admin_footer', apply_filters( 'jetpack_modules_list_table_js_template_callback', array( $this, 'js_templates' ) ), 9 );
103 }
104
105 /**
106 * Output row template.
107 */
108 public function js_templates() {
109 ?>
110 <script type="text/html" id="tmpl-Jetpack_Modules_List_Table_Template">
111 <# var i = 0;
112 if ( data.items.length ) {
113 _.each( data.items, function( item, key, list ) {
114 if ( item === undefined ) return; #>
115 <tr class="jetpack-module <# if ( ++i % 2 ) { #> alternate<# } #><# if ( item.activated ) { #> active<# } #><# if ( ! item.available ) { #> unavailable<# } #>" id="{{{ item.module }}}">
116 <th scope="row" class="check-column">
117 <input type="checkbox" name="modules[]" value="{{{ item.module }}}" />
118 </th>
119 <td class='name column-name'>
120 <span class='info'><a href="{{{item.learn_more_button}}}" target="blank">{{{ item.name }}}</a></span>
121 <div class="row-actions">
122 <# if ( item.configurable ) { #>
123 <span class='configure'>{{{ item.configurable }}}</span>
124 <# } #>
125 <# if ( item.activated && 'vaultpress' !== item.module && item.available ) { #>
126 <span class='delete'><a href="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>?page=jetpack&#038;action=deactivate&#038;module={{{ item.module }}}&#038;_wpnonce={{{ item.deactivate_nonce }}}"><?php esc_html_e( 'Deactivate', 'jetpack' ); ?></a></span>
127 <# } else if ( item.available ) { #>
128 <span class='activate'><a href="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>?page=jetpack&#038;action=activate&#038;module={{{ item.module }}}&#038;_wpnonce={{{ item.activate_nonce }}}"><?php esc_html_e( 'Activate', 'jetpack' ); ?></a></span>
129 <# } #>
130 <# if ( ! item.available ) { #>
131 <span class='unavailable_reason'>{{{ item.unavailable_reason }}}</span>
132 <# } #>
133 </div>
134 </td>
135 </tr>
136 <#
137 });
138 } else {
139 #>
140 <tr class="no-modules-found">
141 <td colspan="2"><?php esc_html_e( 'No Modules Found', 'jetpack' ); ?></td>
142 </tr>
143 <#
144 }
145 #>
146 </script>
147 <?php
148 }
149
150 /**
151 * Get views data.
152 *
153 * @return array Maps identifier to display HTML.
154 */
155 public function get_views() {
156 /** This filter is already documented in class.jetpack-modules-list-table.php */
157 $modules = apply_filters( 'jetpack_modules_list_table_items', Jetpack_Admin::init()->get_modules() );
158 $array_of_module_tags = wp_list_pluck( $modules, 'module_tags' );
159 $module_tags = array_merge( ...array_values( $array_of_module_tags ) );
160 $module_tags_unique = array_count_values( $module_tags );
161 ksort( $module_tags_unique );
162
163 $format = '<a href="%3$s"%4$s data-title="%1$s">%1$s <span class="count">(%2$s)</span></a>';
164 $title = __( 'All', 'jetpack' );
165 $count = count( $modules );
166 $url = esc_url( remove_query_arg( 'module_tag' ) );
167 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a view, not a model or controller.
168 $current = empty( $_GET['module_tag'] ) ? ' class="current all"' : ' class="all"';
169 $views = array(
170 'all' => sprintf( $format, $title, $count, $url, $current ),
171 );
172 foreach ( $module_tags_unique as $title => $count ) {
173 $key = sanitize_title( $title );
174 $display_title = esc_html( wptexturize( $title ) );
175 $url = esc_url( add_query_arg( 'module_tag', rawurlencode( $title ) ) );
176 $current = '';
177 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a view, not a model or controller.
178 if ( ! empty( $_GET['module_tag'] ) && $title === $_GET['module_tag'] ) {
179 $current = ' class="current"';
180 }
181 $views[ $key ] = sprintf( $format, $display_title, $count, $url, $current );
182 }
183 return $views;
184 }
185
186 /**
187 * Output views HTML.
188 */
189 public function views() {
190 $views = $this->get_views();
191
192 echo "<ul class='subsubsub'>\n";
193 foreach ( $views as $class => $view ) {
194 $views[ $class ] = "\t<li class='$class'>$view</li>";
195 }
196 echo implode( "\n", $views ) . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Is HTML. Escaping happens in get_views().
197 echo '</ul>';
198 }
199
200 /**
201 * Filter a modules array for displayed items.
202 *
203 * @param array $modules Modules.
204 * @return array Displayed modules.
205 */
206 public function filter_displayed_table_items( $modules ) {
207 return array_filter( $modules, array( $this, 'is_module_displayed' ) );
208 }
209
210 /**
211 * Determine if a module is displayed.
212 *
213 * @param array $module Module data.
214 * @return bool
215 */
216 public static function is_module_displayed( $module ) {
217 // Handle module tag based filtering.
218 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a view, not a model or controller.
219 if ( ! empty( $_REQUEST['module_tag'] ) ) {
220 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a view, not a model or controller.
221 $module_tag = sanitize_text_field( $_REQUEST['module_tag'] );
222 if ( ! in_array( $module_tag, $module['module_tags'], true ) ) {
223 return false;
224 }
225 }
226
227 // If nothing rejected it, include it!
228 return true;
229 }
230
231 /**
232 * Sort callback to put modules with `requires_connection` last.
233 *
234 * @param array $module1 Module data.
235 * @param array $module2 Module data.
236 * @return int Indicating the relative ordering of module1 and module2.
237 */
238 public static function sort_requires_connection_last( $module1, $module2 ) {
239 if ( (bool) $module1['requires_connection'] === (bool) $module2['requires_connection'] ) {
240 return 0;
241 }
242 if ( $module1['requires_connection'] ) {
243 return 1;
244 }
245 if ( $module2['requires_connection'] ) {
246 return -1;
247 }
248
249 return 0;
250 }
251
252 /**
253 * Get table columns.
254 *
255 * @return string[] Column name to header HTML.
256 */
257 public function get_columns() {
258 $columns = array(
259 'cb' => '<input type="checkbox" />',
260 'name' => __( 'Name', 'jetpack' ),
261 );
262 return $columns;
263 }
264
265 /**
266 * Get bulk actions for the table.
267 *
268 * @return string[] Actions, code => text.
269 */
270 public function get_bulk_actions() {
271 $actions = array(
272 'bulk-activate' => __( 'Activate', 'jetpack' ),
273 'bulk-deactivate' => __( 'Deactivate', 'jetpack' ),
274 );
275 return $actions;
276 }
277
278 /**
279 * Print a single row of the table.
280 *
281 * @param object|array $item Item.
282 */
283 public function single_row( $item ) {
284 static $i = 0;
285 $row_class = ( ( ++$i ) % 2 ) ? ' alternate' : '';
286
287 if ( ! empty( $item['activated'] ) ) {
288 $row_class .= ' active';
289 }
290
291 if ( ! Jetpack_Admin::is_module_available( $item ) ) {
292 $row_class .= ' unavailable';
293 }
294
295 echo '<tr class="jetpack-module' . esc_attr( $row_class ) . '" id="' . esc_attr( $item['module'] ) . '">';
296 $this->single_row_columns( $item );
297 echo '</tr>';
298 }
299
300 /**
301 * Table classes.
302 *
303 * @return string[] HTML.
304 */
305 public function get_table_classes() {
306 return array( 'table', 'table-bordered', 'wp-list-table', 'widefat', 'fixed', 'jetpack-modules' );
307 }
308
309 /**
310 * Column checkbox.
311 *
312 * @param object|array $item Item.
313 * @return string HTML.
314 */
315 public function column_cb( $item ) {
316 if ( ! Jetpack_Admin::is_module_available( $item ) ) {
317 return '';
318 }
319
320 return sprintf( '<input type="checkbox" name="modules[]" value="%s" />', $item['module'] );
321 }
322
323 /**
324 * Column icon.
325 *
326 * @return string HTML.
327 */
328 public function column_icon() {
329 $badge_text = '';
330 $free_text = '';
331 ob_start();
332 ?>
333 <a href="#TB_inline?width=600&height=550&inlineId=more-info-module-settings-modal" class="thickbox">
334 <div class="module-image">
335 <p><span class="module-image-badge"><?php echo esc_html( $badge_text ); ?></span><span class="module-image-free" style="display: none"><?php echo esc_html( $free_text ); ?></span></p>
336 </div>
337 </a>
338 <?php
339 return ob_get_clean();
340
341 }
342
343 /**
344 * Column name.
345 *
346 * @param object|array $item Item.
347 * @return string HTML.
348 */
349 public function column_name( $item ) {
350 $actions = array(
351 'info' => sprintf( '<a href="%s" target="blank">%s</a>', esc_url( $item['learn_more_button'] ), esc_html__( 'Feature Info', 'jetpack' ) ),
352 );
353
354 if ( ! empty( $item['configurable'] ) ) {
355 $actions['configure'] = $item['configurable'];
356 }
357
358 if ( empty( $item['activated'] ) && Jetpack_Admin::is_module_available( $item ) ) {
359 $url = wp_nonce_url(
360 Jetpack::admin_url(
361 array(
362 'page' => 'jetpack',
363 'action' => 'activate',
364 'module' => $item['module'],
365 )
366 ),
367 'jetpack_activate-' . $item['module']
368 );
369 $actions['activate'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html__( 'Activate', 'jetpack' ) );
370 } elseif ( ! empty( $item['activated'] ) ) {
371 $url = wp_nonce_url(
372 Jetpack::admin_url(
373 array(
374 'page' => 'jetpack',
375 'action' => 'deactivate',
376 'module' => $item['module'],
377 )
378 ),
379 'jetpack_deactivate-' . $item['module']
380 );
381 $actions['delete'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html__( 'Deactivate', 'jetpack' ) );
382 }
383
384 return $this->row_actions( $actions ) . wptexturize( $item['name'] );
385 }
386
387 /**
388 * Column description.
389 *
390 * @param object|array $item Item.
391 * @return string HTML.
392 */
393 public function column_description( $item ) {
394 ob_start();
395 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
396 /** This action is documented in class.jetpack-admin.php */
397 echo apply_filters( 'jetpack_short_module_description', $item['description'], $item['module'] );
398 /** This action is documented in class.jetpack-admin.php */
399 do_action( 'jetpack_learn_more_button_' . $item['module'] );
400 echo '<div id="more-info-' . $item['module'] . '" class="more-info">';
401 /** This action is documented in class.jetpack-admin.php */
402 do_action( 'jetpack_module_more_info_' . $item['module'] );
403 echo '</div>';
404 // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
405 return ob_get_clean();
406 }
407
408 /**
409 * Return module tags HTML.
410 *
411 * @param object|array $item Item.
412 * @return string HTML.
413 */
414 public function column_module_tags( $item ) {
415 $module_tags = array();
416 foreach ( $item['module_tags'] as $module_tag ) {
417 $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', rawurlencode( $module_tag ) ) ) );
418 }
419 return implode( ', ', $module_tags );
420 }
421
422 /**
423 * Column default value.
424 *
425 * @param object|array $item Item.
426 * @param string $column_name Column name.
427 * @return string
428 */
429 public function column_default( $item, $column_name ) {
430 switch ( $column_name ) {
431 case 'icon':
432 case 'name':
433 case 'description':
434 return '';
435 default:
436 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
437 return print_r( $item, true );
438 }
439 }
440
441 /**
442 * Check if the info parameter provided in the URL corresponds to an actual module.
443 *
444 * @param string|false $info Info parameter.
445 * @param array $modules Modules array.
446 * @return string|false
447 */
448 public function module_info_check( $info, $modules ) {
449 if ( ! $info ) {
450 return false;
451 } elseif ( array_key_exists( $info, $modules ) ) {
452 return $info;
453 }
454 }
455
456 /**
457 * Core switched their `display_tablenav()` method to protected, so we can't access it directly.
458 * Instead, let's include an access function to make it doable without errors!
459 *
460 * @see https://github.com/WordPress/WordPress/commit/d28f6344de97616de8ece543ed290c4ba2383622
461 *
462 * @param string $which Which nav table to display.
463 * @return mixed
464 */
465 public function unprotected_display_tablenav( $which = 'top' ) {
466 return $this->display_tablenav( $which );
467 }
468
469 }
470