PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | class.jetpack-modules-list-table.php +303 -132 12.7.316.3-a.1 View file →
@@ -6,8 +6,12 @@
6 6 */
7 7
8 8 use Automattic\Jetpack\Assets;
9 9
10 +if ( ! defined( 'ABSPATH' ) ) {
11 + exit( 0 );
12 +}
13 +
10 14 if ( ! class_exists( 'WP_List_Table' ) ) {
11 15 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
12 16 }
13 17
@@ -15,8 +19,125 @@
15 19 * Jetpack modules list table.
16 20 */
17 21 class Jetpack_Modules_List_Table extends WP_List_Table {
18 22
23 + /**
24 + * Order in which product groups are displayed on the page.
25 + *
26 + * @var string[]
27 + */
28 + const PRODUCT_GROUP_ORDER = array( 'growth', 'performance', 'security', 'other' );
29 +
30 + /**
31 + * Map of module slug to product group.
32 + *
33 + * Modeled on the Offline Mode dashboard grouping so the modules page reads
34 + * as a set of focused product areas. Any module not listed falls back to
35 + * the "other" group.
36 + *
37 + * @return array<string, string> Module slug => product group slug.
38 + */
39 + public static function get_module_product_groups_map() {
40 + return array(
41 + // Grow your audience.
42 + 'blaze' => 'growth',
43 + 'comment-likes' => 'growth',
44 + 'comments' => 'growth',
45 + 'contact-form' => 'growth',
46 + 'gravatar-hovercards' => 'growth',
47 + 'likes' => 'growth',
48 + 'publicize' => 'growth',
49 + 'related-posts' => 'growth',
50 + 'sharedaddy' => 'growth',
51 + 'simple-payments' => 'growth',
52 + 'stats' => 'growth',
53 + 'subscriptions' => 'growth',
54 + 'woocommerce-analytics' => 'growth',
55 + 'wordads' => 'growth',
56 + 'wpcom-reader' => 'growth',
57 +
58 + // Speed up your site.
59 + 'carousel' => 'performance',
60 + 'google-fonts' => 'performance',
61 + 'infinite-scroll' => 'performance',
62 + 'photon' => 'performance',
63 + 'photon-cdn' => 'performance',
64 + 'search' => 'performance',
65 + 'tiled-gallery' => 'performance',
66 + 'videopress' => 'performance',
67 +
68 + // Protect your site.
69 + 'account-protection' => 'security',
70 + 'monitor' => 'security',
71 + 'notes' => 'security',
72 + 'protect' => 'security',
73 + 'sso' => 'security',
74 + 'vaultpress' => 'security',
75 + 'waf' => 'security',
76 + );
77 + }
78 +
79 + /**
80 + * Get the product group slug for a given module.
81 + *
82 + * @param string $slug Module slug.
83 + * @return string Product group slug.
84 + */
85 + public static function get_module_product_group( $slug ) {
86 + $map = self::get_module_product_groups_map();
87 +
88 + return $map[ $slug ] ?? 'other';
89 + }
90 +
91 + /**
92 + * Get the translated display name for a product group.
93 + *
94 + * @param string $group Product group slug.
95 + * @return string Translated group name.
96 + */
97 + public static function get_product_group_name( $group ) {
98 + switch ( $group ) {
99 + case 'growth':
100 + return __( 'Grow your audience', 'jetpack' );
101 + case 'performance':
102 + return __( 'Speed up your site', 'jetpack' );
103 + case 'security':
104 + return __( 'Protect your site', 'jetpack' );
105 + default:
106 + return __( 'Other features', 'jetpack' );
107 + }
108 + }
109 +
110 + /**
111 + * Whether a module is flagged as recommended.
112 + *
113 + * @param array $module Module data (with raw, untranslated module tags).
114 + * @return bool
115 + */
116 + public static function is_module_recommended( $module ) {
117 + return ! empty( $module['module_tags'] ) && in_array( 'Recommended', (array) $module['module_tags'], true );
118 + }
119 +
120 + /**
121 + * Remove the legacy `vaultpress` module from the modules screen.
122 + *
123 + * The `vaultpress` module is a leftover stub for the original standalone
124 + * VaultPress plugin. It is hard-coded as never activatable (see
125 + * Jetpack_Admin::is_module_available()) and is unrelated to the modern
126 + * VaultPress Backup product, which is delivered through My Jetpack and the
127 + * standalone Jetpack VaultPress Backup plugin rather than a Jetpack module.
128 + * Showing a permanently-unavailable row that merely reuses the modern
129 + * product's name is misleading, so we drop it from this screen only. Other
130 + * surfaces (REST, the React dashboard, standalone plugins) are unaffected.
131 + *
132 + * @param array $modules Array of Jetpack modules keyed by slug.
133 + * @return array Modules without the legacy `vaultpress` entry.
134 + */
135 + public static function hide_legacy_vaultpress_module( $modules ) {
136 + unset( $modules['vaultpress'] );
137 + return $modules;
138 + }
139 +
19 140 /** Constructor. */
20 141 public function __construct() {
21 142 parent::__construct();
22 143
@@ -21,8 +142,11 @@
21 142 parent::__construct();
22 143
23 144 Jetpack::init();
24 145
146 + // Scope this to the modules screen so REST/React/standalone surfaces keep the legacy slug.
147 + add_filter( 'jetpack_modules_list_table_items', array( __CLASS__, 'hide_legacy_vaultpress_module' ) );
148 +
25 149 if ( $this->compat_fields && is_array( $this->compat_fields ) ) {
26 150 array_push( $this->compat_fields, 'all_items' );
27 151 }
28 152
@@ -50,9 +174,9 @@
50 174 Assets::get_file_url_for_environment(
51 175 '_inc/build/jetpack-modules.models.min.js',
52 176 '_inc/jetpack-modules.models.js'
53 177 ),
54 - array( 'jquery', 'backbone', 'underscore' ),
178 + array( 'jquery', 'backbone' ),
55 179 JETPACK__VERSION,
56 180 false // @todo Can this be put in the footer?
57 181 );
58 182 wp_register_script(
@@ -60,9 +184,9 @@
60 184 Assets::get_file_url_for_environment(
61 185 '_inc/build/jetpack-modules.views.min.js',
62 186 '_inc/jetpack-modules.views.js'
63 187 ),
64 - array( 'jquery', 'backbone', 'underscore', 'wp-util' ),
188 + array( 'jquery', 'backbone', 'wp-util' ),
65 189 JETPACK__VERSION,
66 190 false // @todo Can this be put in the footer?
67 191 );
68 192 wp_register_script(
@@ -79,13 +203,23 @@
79 203 JETPACK__VERSION,
80 204 true
81 205 );
82 206
207 + $modules_for_js = (array) Jetpack::get_translated_modules( $this->all_items );
208 + foreach ( array_keys( $modules_for_js ) as $slug ) {
209 + $group = self::get_module_product_group( $slug );
210 +
211 + $modules_for_js[ $slug ]['product_group'] = $group;
212 + $modules_for_js[ $slug ]['product_group_name'] = self::get_product_group_name( $group );
213 + // Detect recommendation from the raw (untranslated) module tags.
214 + $modules_for_js[ $slug ]['recommended'] = isset( $this->all_items[ $slug ] ) && self::is_module_recommended( $this->all_items[ $slug ] );
215 + }
216 +
83 217 wp_localize_script(
84 218 'jetpack-modules-list-table',
85 219 'jetpackModulesData',
86 220 array(
87 - 'modules' => Jetpack::get_translated_modules( $this->all_items ),
221 + 'modules' => $modules_for_js,
88 222 'i18n' => array(
89 223 'search_placeholder' => __( 'Search modules…', 'jetpack' ),
90 224 ),
91 225 'modalinfo' => $this->module_info_check( $modal_info, $this->all_items ),
@@ -112,31 +246,50 @@
112 246 */
113 247 public function js_templates() {
114 248 ?>
115 249 <script type="text/html" id="tmpl-Jetpack_Modules_List_Table_Template">
116 - <# var i = 0;
250 + <# var i = 0; var currentGroup = null;
117 251 if ( data.items.length ) {
118 252 _.each( data.items, function( item, key, list ) {
119 253 if ( item === undefined ) return;
120 - if ( 'lazy-images' == item.module && ! item.activated ) return; #>
121 - <tr class="jetpack-module <# if ( ++i % 2 ) { #> alternate<# } #><# if ( item.activated ) { #> active<# } #><# if ( ! item.available ) { #> unavailable<# } #>" id="{{{ item.module }}}">
254 + if ( item.product_group !== currentGroup ) {
255 + currentGroup = item.product_group; #>
256 + <tr class="jetpack-module-group-header" data-group="{{ item.product_group }}">
257 + <td colspan="2"><h2 class="jetpack-module-group-header__title">{{ item.product_group_name }}</h2></td>
258 + </tr>
259 + <# } #>
260 + <tr class="jetpack-module<# if ( ++i % 2 ) { #> alternate<# } #><# if ( item.activated ) { #> active<# } #><# if ( ! item.available ) { #> unavailable<# } #>" id="{{{ item.module }}}">
122 261 <th scope="row" class="check-column">
123 - <input type="checkbox" name="modules[]" value="{{{ item.module }}}" {{{ item.disabled }}} />
262 + <# if ( item.available ) { #><input type="checkbox" name="modules[]" value="{{{ item.module }}}" {{{ item.disabled }}} /><# } #>
124 263 </th>
125 - <td class='name column-name'>
126 - <p class='info'><a href="{{{item.learn_more_button}}}" target="blank" style="text-decoration: none;">{{{ item.name }}}</a></p>
127 - <div class="row-actions">
128 - <# if ( item.configurable ) { #>
129 - <span class='configure'>{{{ item.configurable }}}</span>
130 - <# } #>
131 - <# if ( item.activated && 'vaultpress' !== item.module && item.available ) { #>
132 - <span class='delete'><a class="dops-button is-compact" 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>
133 - <# } else if ( item.available ) { #>
134 - <span class='activate'><a class="dops-button is-compact" 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>
135 - <# } #>
136 - <# if ( ! item.available ) { #>
137 - <p class='unavailable_reason'>{{{ item.unavailable_reason }}}</p>
138 - <# } #>
264 + <td class="name column-name">
265 + <div class="jetpack-module__body">
266 + <div class="jetpack-module__info">
267 + <# if ( item.learn_more_button ) { #><a class="jetpack-module__name" href="{{{ item.learn_more_button }}}" target="_blank" rel="noopener noreferrer">{{ item.name }}</a><# } else { #><span class="jetpack-module__name">{{ item.name }}</span><# } #>
268 + <# if ( item.description ) { #><span class="jetpack-module__description">{{ item.description }}</span><# } #>
269 + </div>
270 + <div class="jetpack-module__meta">
271 + <span class="jetpack-module__badges">
272 + <# if ( item.recommended && item.available ) { #><span class="jetpack-module__badge is-recommended"><?php esc_html_e( 'Recommended', 'jetpack' ); ?></span><# } #>
273 + <# if ( item.available ) { #>
274 + <# if ( item.activated ) { #><span class="jetpack-module__badge is-active"><?php esc_html_e( 'Active', 'jetpack' ); ?></span><# } else { #><span class="jetpack-module__badge is-inactive"><?php esc_html_e( 'Inactive', 'jetpack' ); ?></span><# } #>
275 + <# } #>
276 + </span>
277 + <div class="row-actions">
278 + <# if ( item.configurable ) { #>
279 + <span class="configure">{{{ item.configurable }}}</span>
280 + <# } #>
281 + <# if ( item.available ) { #>
282 + <# if ( item.activated ) { #>
283 + <a class="jetpack-module__toggle is-active" role="switch" aria-checked="true" href="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>?page=jetpack&#038;action=deactivate&#038;module={{{ item.module }}}&#038;_wpnonce={{{ item.deactivate_nonce }}}"><span class="jetpack-module__toggle-track"><span class="jetpack-module__toggle-thumb"></span></span><span class="screen-reader-text"><?php esc_html_e( 'Deactivate', 'jetpack' ); ?></span></a>
284 + <# } else { #>
285 + <a class="jetpack-module__toggle" role="switch" aria-checked="false" href="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>?page=jetpack&#038;action=activate&#038;module={{{ item.module }}}&#038;_wpnonce={{{ item.activate_nonce }}}"><span class="jetpack-module__toggle-track"><span class="jetpack-module__toggle-thumb"></span></span><span class="screen-reader-text"><?php esc_html_e( 'Activate', 'jetpack' ); ?></span></a>
286 + <# } #>
287 + <# } else { #>
288 + <span class="jetpack-module__unavailable">{{ item.unavailable_reason }}</span>
289 + <# } #>
290 + </div>
291 + </div>
139 292 </div>
140 293 </td>
141 294 </tr>
142 295 <#
@@ -284,8 +437,50 @@
284 437 return $actions;
285 438 }
286 439
287 440 /**
441 + * Print the module rows grouped by product group.
442 + *
443 + * Overrides WP_List_Table::display_rows() so the server-rendered (no-JS)
444 + * output matches the grouped, card-style layout produced by the Backbone
445 + * template.
446 + */
447 + public function display_rows() {
448 + $buckets = array();
449 + foreach ( self::PRODUCT_GROUP_ORDER as $group ) {
450 + $buckets[ $group ] = array();
451 + }
452 +
453 + foreach ( $this->items as $item ) {
454 + $group = self::get_module_product_group( $item['module'] );
455 + $buckets[ $group ][] = $item;
456 + }
457 +
458 + foreach ( $buckets as $group => $items ) {
459 + if ( empty( $items ) ) {
460 + continue;
461 + }
462 + $this->group_header_row( $group );
463 + foreach ( $items as $item ) {
464 + $this->single_row( $item );
465 + }
466 + }
467 + }
468 +
469 + /**
470 + * Print a product group header row.
471 + *
472 + * @param string $group Product group slug.
473 + */
474 + protected function group_header_row( $group ) {
475 + printf(
476 + '<tr class="jetpack-module-group-header" data-group="%1$s"><td colspan="2"><h2 class="jetpack-module-group-header__title">%2$s</h2></td></tr>',
477 + esc_attr( $group ),
478 + esc_html( self::get_product_group_name( $group ) )
479 + );
480 + }
481 +
482 + /**
288 483 * Print a single row of the table.
289 484 *
290 485 * @param object|array $item Item.
291 486 */
@@ -292,159 +487,135 @@
292 487 public function single_row( $item ) {
293 488 static $i = 0;
294 489 $row_class = ( ( ++$i ) % 2 ) ? ' alternate' : '';
295 490
491 + $available = Jetpack_Admin::is_module_available( $item );
492 +
296 493 if ( ! empty( $item['activated'] ) ) {
297 494 $row_class .= ' active';
298 495 }
299 -
300 - if ( ! Jetpack_Admin::is_module_available( $item ) ) {
496 + if ( ! $available ) {
301 497 $row_class .= ' unavailable';
302 498 }
303 499
304 500 echo '<tr class="jetpack-module' . esc_attr( $row_class ) . '" id="' . esc_attr( $item['module'] ) . '">';
305 - $this->single_row_columns( $item );
306 - echo '</tr>';
307 - }
308 501
309 - /**
310 - * Table classes.
311 - *
312 - * @return string[] HTML.
313 - */
314 - public function get_table_classes() {
315 - return array( 'table', 'table-bordered', 'wp-list-table', 'widefat', 'fixed' );
316 - }
502 + echo '<th scope="row" class="check-column">';
503 + if ( $available ) {
504 + printf( '<input type="checkbox" name="modules[]" value="%s" />', esc_attr( $item['module'] ) );
505 + }
506 + echo '</th>';
317 507
318 - /**
319 - * Column checkbox.
320 - *
321 - * @param object|array $item Item.
322 - * @return string HTML.
323 - */
324 - public function column_cb( $item ) {
325 - if ( ! Jetpack_Admin::is_module_available( $item ) ) {
326 - return '';
508 + echo '<td class="name column-name">';
509 + echo '<div class="jetpack-module__body">';
510 +
511 + echo '<div class="jetpack-module__info">';
512 + if ( ! empty( $item['learn_more_button'] ) ) {
513 + printf(
514 + '<a class="jetpack-module__name" href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
515 + esc_url( $item['learn_more_button'] ),
516 + wp_kses_post( wptexturize( $item['name'] ) )
517 + );
518 + } else {
519 + echo '<span class="jetpack-module__name">' . wp_kses_post( wptexturize( $item['name'] ) ) . '</span>';
327 520 }
521 + if ( ! empty( $item['description'] ) ) {
522 + echo '<span class="jetpack-module__description">' . esc_html( wp_strip_all_tags( $item['description'] ) ) . '</span>';
523 + }
524 + echo '</div>';
328 525
329 - return sprintf( '<input type="checkbox" name="modules[]" value="%s" />', $item['module'] );
330 - }
526 + echo '<div class="jetpack-module__meta">';
527 + echo '<span class="jetpack-module__badges">';
528 + if ( $available && self::is_module_recommended( $item ) ) {
529 + echo '<span class="jetpack-module__badge is-recommended">' . esc_html__( 'Recommended', 'jetpack' ) . '</span>';
530 + }
531 + if ( $available ) {
532 + if ( ! empty( $item['activated'] ) ) {
533 + echo '<span class="jetpack-module__badge is-active">' . esc_html__( 'Active', 'jetpack' ) . '</span>';
534 + } else {
535 + echo '<span class="jetpack-module__badge is-inactive">' . esc_html__( 'Inactive', 'jetpack' ) . '</span>';
536 + }
537 + }
538 + echo '</span>';
331 539
332 - /**
333 - * Column icon.
334 - *
335 - * @return string HTML.
336 - */
337 - public function column_icon() {
338 - $badge_text = '';
339 - $free_text = '';
340 - ob_start();
341 - ?>
342 - <a href="#TB_inline?width=600&height=550&inlineId=more-info-module-settings-modal" class="thickbox">
343 - <div class="module-image">
344 - <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>
345 - </div>
346 - </a>
347 - <?php
348 - return ob_get_clean();
540 + echo '<div class="row-actions">';
541 + if ( ! empty( $item['configurable'] ) ) {
542 + echo '<span class="configure">' . wp_kses_post( $item['configurable'] ) . '</span>';
543 + }
544 + if ( $available ) {
545 + echo $this->get_module_toggle_html( $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in helper.
546 + } elseif ( ! empty( $item['unavailable_reason'] ) ) {
547 + echo '<span class="jetpack-module__unavailable">' . wp_kses_post( $item['unavailable_reason'] ) . '</span>';
548 + }
549 + // Close .row-actions, .jetpack-module__meta, and .jetpack-module__body.
550 + echo '</div></div></div>';
551 + echo '</td>';
552 + echo '</tr>';
349 553 }
350 554
351 555 /**
352 - * Column name.
556 + * Build the activate/deactivate toggle control markup for a module row.
353 557 *
354 - * @param object|array $item Item.
558 + * @param object|array $item Module data.
355 559 * @return string HTML.
356 560 */
357 - public function column_name( $item ) {
358 - $actions = array(
359 - 'info' => sprintf( '<a href="%s" target="blank">%s</a>', esc_url( $item['learn_more_button'] ), esc_html__( 'Feature Info', 'jetpack' ) ),
360 - );
561 + protected function get_module_toggle_html( $item ) {
562 + $is_active = ! empty( $item['activated'] );
361 563
362 - if ( ! empty( $item['configurable'] ) ) {
363 - $actions['configure'] = $item['configurable'];
364 - }
365 -
366 - if ( empty( $item['activated'] ) && Jetpack_Admin::is_module_available( $item ) ) {
367 - $url = wp_nonce_url(
564 + if ( $is_active ) {
565 + $url = wp_nonce_url(
368 566 Jetpack::admin_url(
369 567 array(
370 568 'page' => 'jetpack',
371 - 'action' => 'activate',
569 + 'action' => 'deactivate',
372 570 'module' => $item['module'],
373 571 )
374 572 ),
375 - 'jetpack_activate-' . $item['module']
573 + 'jetpack_deactivate-' . $item['module']
376 574 );
377 - $actions['activate'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html__( 'Activate', 'jetpack' ) );
378 - } elseif ( ! empty( $item['activated'] ) ) {
379 - $url = wp_nonce_url(
575 + $label = sprintf(
576 + /* translators: %s: Jetpack module name. */
577 + __( 'Deactivate %s', 'jetpack' ),
578 + $item['name']
579 + );
580 + $class = 'jetpack-module__toggle is-active';
581 + $aria = 'true';
582 + } else {
583 + $url = wp_nonce_url(
380 584 Jetpack::admin_url(
381 585 array(
382 586 'page' => 'jetpack',
383 - 'action' => 'deactivate',
587 + 'action' => 'activate',
384 588 'module' => $item['module'],
385 589 )
386 590 ),
387 - 'jetpack_deactivate-' . $item['module']
591 + 'jetpack_activate-' . $item['module']
388 592 );
389 - $actions['delete'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html__( 'Deactivate', 'jetpack' ) );
593 + $label = sprintf(
594 + /* translators: %s: Jetpack module name. */
595 + __( 'Activate %s', 'jetpack' ),
596 + $item['name']
597 + );
598 + $class = 'jetpack-module__toggle';
599 + $aria = 'false';
390 600 }
391 601
392 - return $this->row_actions( $actions ) . wptexturize( $item['name'] );
602 + return sprintf(
603 + '<a class="%1$s" role="switch" aria-checked="%2$s" href="%3$s"><span class="jetpack-module__toggle-track"><span class="jetpack-module__toggle-thumb"></span></span><span class="screen-reader-text">%4$s</span></a>',
604 + esc_attr( $class ),
605 + esc_attr( $aria ),
606 + esc_url( $url ),
607 + esc_html( $label )
608 + );
393 609 }
394 610
395 611 /**
396 - * Column description.
612 + * Table classes.
397 613 *
398 - * @param object|array $item Item.
399 - * @return string HTML.
614 + * @return string[] HTML.
400 615 */
401 - public function column_description( $item ) {
402 - ob_start();
403 - // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
404 - /** This action is documented in class.jetpack-admin.php */
405 - echo apply_filters( 'jetpack_short_module_description', $item['description'], $item['module'] );
406 - /** This action is documented in class.jetpack-admin.php */
407 - do_action( 'jetpack_learn_more_button_' . $item['module'] );
408 - echo '<div id="more-info-' . $item['module'] . '" class="more-info">';
409 - /** This action is documented in class.jetpack-admin.php */
410 - do_action( 'jetpack_module_more_info_' . $item['module'] );
411 - echo '</div>';
412 - // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
413 - return ob_get_clean();
414 - }
415 -
416 - /**
417 - * Return module tags HTML.
418 - *
419 - * @param object|array $item Item.
420 - * @return string HTML.
421 - */
422 - public function column_module_tags( $item ) {
423 - $module_tags = array();
424 - foreach ( array_map( 'jetpack_get_module_i18n_tag', $item['module_tags'] ) as $module_tag ) {
425 - $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 ) ) ) );
426 - }
427 - return implode( ', ', $module_tags );
428 - }
429 -
430 - /**
431 - * Column default value.
432 - *
433 - * @param object|array $item Item.
434 - * @param string $column_name Column name.
435 - * @return string
436 - */
437 - public function column_default( $item, $column_name ) {
438 - switch ( $column_name ) {
439 - case 'icon':
440 - case 'name':
441 - case 'description':
442 - return '';
443 - default:
444 - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
445 - return print_r( $item, true );
446 - }
616 + public function get_table_classes() {
617 + return array( 'table', 'table-bordered', 'wp-list-table', 'widefat', 'fixed' );
447 618 }
448 619
449 620 /**
450 621 * Check if the info parameter provided in the URL corresponds to an actual module.