PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 5.0.3
MainWP Dashboard: Self-hosted WordPress Management for Agencies v5.0.3
6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 5.2.2 5.3 All 153 releases
mainwp / class / class-mainwp-plugins-install-list-table.php

class-mainwp-plugins-install-list-table.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 5.0.3, at class/class-mainwp-plugins-install-list-table.php

556 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Installer List Table class.
4 *
5 * @package MainWP/Dashboard
6 */
7
8 namespace MainWP\Dashboard;
9
10 // Include class-wp-list-table.php.
11 if ( ! class_exists( '\WP_List_Table' ) ) {
12 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
13 }
14
15 /**
16 * Class MainWP_Plugins_Install_List_Table
17 *
18 * @package MainWP\Dashboard
19 * @subpackage List_Table
20 * @since 3.1.0
21 * @access private
22 */
23 class MainWP_Plugins_Install_List_Table extends \WP_List_Table {
24
25 /**
26 * Default direction.
27 *
28 * @var string $order
29 */
30 public $order = 'ASC';
31
32 /**
33 * Default order.
34 *
35 * @var int $orderby
36 */
37 public $orderby = null;
38
39 /**
40 * Groups array.
41 *
42 * @var array $groups
43 */
44 public $groups = array();
45
46 /**
47 * Error messages.
48 *
49 * @var mixed $error
50 */
51 private $error;
52
53 /**
54 * Method ajax_user_can()
55 *
56 * Chck if the current user has the WP ability "install_plugins".
57 *
58 * @return boolean True|False.
59 */
60 public function ajax_user_can() {
61 return current_user_can( 'install_plugins' );
62 }
63
64 /**
65 * Method prepair_items()
66 *
67 * @global array $tabs
68 * @global string $tab
69 * @global int $paged
70 * @global string $type
71 * @global string $term
72 * @global string $wp_version
73 */
74 public function prepare_items() { // phpcs:ignore -- Current complexity is the only way to achieve desired results, pull request solutions appreciated.
75 include ABSPATH . 'wp-admin/includes/plugin-install.php';
76
77 global $tab; // required.
78
79 wp_reset_vars( array( 'tab' ) );
80
81 $paged = $this->get_pagenum();
82
83 $per_page = 40;
84
85 // These are the tabs which are shown on the page.
86 $tabs = array();
87
88 if ( 'search' === $tab ) {
89 $tabs['search'] = esc_html__( 'Search Results', 'mainwp' );
90 }
91 $tabs['featured'] = _x( 'Featured', 'Plugin Installer' );
92 $tabs['popular'] = _x( 'Popular', 'Plugin Installer' );
93 $tabs['recommended'] = _x( 'Recommended', 'Plugin Installer' );
94 if ( 'beta' === $tab || false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
95 $tabs['beta'] = _x( 'Beta Testing', 'Plugin Installer' );
96 }
97 if ( current_user_can( 'upload_plugins' ) ) {
98 // No longer a real tab. Here for filter compatibility.
99 // Gets skipped in get_views().
100 $tabs['upload'] = esc_html__( 'Upload Plugin', 'mainwp' );
101 }
102
103 $nonmenu_tabs = array( 'plugin-information' );
104 // Valid actions to perform which do not have a Menu item.
105 // If a non-valid menu tab has been selected, And it's not a non-menu action.
106 if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs ) ) ) {
107
108 $tab = key( $tabs ); // phpcs:ignore -- required for custom bulk install plugins.
109 }
110
111 $args = array(
112 'page' => $paged,
113 'per_page' => $per_page,
114 'fields' => array(
115 'last_updated' => true,
116 'icons' => true,
117 'active_installs' => true,
118 ),
119 'locale' => get_locale(),
120 'installed_plugins' => array(),
121 );
122
123 switch ( $tab ) {
124 case 'search':
125 $type = isset( $_REQUEST['type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['type'] ) ) : 'term'; // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
126 $term = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
127
128 switch ( $type ) {
129 case 'tag':
130 $args['tag'] = sanitize_title_with_dashes( $term );
131 break;
132 case 'term':
133 $args['search'] = $term;
134 break;
135 case 'author':
136 $args['author'] = $term;
137 break;
138 }
139
140 break;
141
142 case 'featured':
143 $args['fields']['group'] = true;
144 $this->orderby = 'group';
145 // no b r e a k!
146 case 'popular':
147 case 'new':
148 case 'beta':
149 case 'recommended':
150 $args['browse'] = $tab;
151 break;
152
153 case 'favorites':
154 $user = isset( $_GET['user'] ) ? sanitize_text_field( wp_unslash( $_GET['user'] ) ) : get_user_option( 'wporg_favorites' ); // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
155 update_user_meta( get_current_user_id(), 'wporg_favorites', $user );
156 if ( $user ) {
157 $args['user'] = $user;
158 } else {
159 $args = false;
160 }
161
162 break;
163
164 default:
165 $args = false;
166 break;
167 }
168
169 if ( ! $args ) {
170 return;
171 }
172
173 $api = plugins_api( 'query_plugins', $args );
174
175 if ( is_wp_error( $api ) ) {
176 $this->error = $api;
177 return;
178 }
179
180 $this->items = $api->plugins;
181
182 if ( $this->orderby ) {
183 uasort( $this->items, array( $this, 'order_callback' ) );
184 }
185
186 $this->set_pagination_args(
187 array(
188 'total_items' => $api->info['results'],
189 'per_page' => $args['per_page'],
190 )
191 );
192
193 if ( isset( $api->info['groups'] ) ) {
194 $this->groups = $api->info['groups'];
195 }
196 }
197
198 /**
199 * Method no_items()
200 *
201 * Check for errors.
202 */
203 public function no_items() {
204 if ( isset( $this->error ) ) {
205 $message = $this->error->get_error_message() . '<p class="hide-if-no-js"><a href="#" class="button" onclick="document.location.reload(); return false;">' . esc_html__( 'Try again', 'mainwp' ) . '</a></p>';
206 } else {
207 $message = esc_html__( 'No plugins match your request.', 'mainwp' );
208 }
209 echo '<div class="ui message yellow">' . $message . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput
210 }
211
212 /**
213 * Method dislpay()
214 *
215 * Override the parent display() so we can provide a different container.
216 */
217 public function display() {
218 ?>
219 <div id="mainwp-install-plugins-container" class="ui four cards">
220 <?php $this->display_rows_or_placeholder(); ?>
221 </div>
222 <div class="ui hidden divider"></div>
223 <div class="ui column grid">
224 <div class="column right aligned">
225 <div class="inline field">
226 <?php $this->display_tablenav( 'bottom' ); ?>
227 </div>
228 </div>
229 </div>
230 <?php
231 }
232
233 /**
234 * Method display_tablenav()
235 *
236 * Displays the table Navigation.
237 *
238 * @param mixed $which Position information.
239 *
240 * @return mixed wp_referer_field();
241 */
242 protected function display_tablenav( $which ) {
243
244 if ( 'featured' === $GLOBALS['tab'] ) {
245 return;
246 }
247
248 if ( 'top' === $which ) {
249 wp_referer_field();
250 $this->pagination( $which );
251 } else {
252 $this->pagination( $which );
253 }
254 }
255
256 /**
257 * Method pagination()
258 *
259 * Build the pagination menu.
260 *
261 * @param mixed $which Position information.
262 *
263 * @return mixed Pagination HTML
264 */
265 protected function pagination( $which ) {
266 if ( empty( $this->_pagination_args ) ) {
267 return;
268 }
269
270 $total_items = $this->_pagination_args['total_items'];
271 $total_pages = (int) $this->_pagination_args['total_pages'];
272
273 $perpage_paging = '<span>' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
274
275 $current = (int) $this->get_pagenum();
276 $removable_query_args = wp_removable_query_args();
277
278 $current_url = set_url_scheme( 'http://' . ( isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' ) ); // phpcs:ignore WordPress.Security.NonceVerification
279
280 $current_url = remove_query_arg( $removable_query_args, $current_url );
281
282 $page_links = array();
283
284 $disable_first = false;
285 $disable_last = false;
286 $disable_prev = false;
287 $disable_next = false;
288
289 if ( 1 === $current ) {
290 $disable_first = true;
291 $disable_prev = true;
292 }
293 if ( 2 === $current ) {
294 $disable_first = true;
295 }
296 if ( $current === $total_pages ) {
297 $disable_last = true;
298 $disable_next = true;
299 }
300 if ( $current === $total_pages - 1 ) {
301 $disable_last = true;
302 }
303
304 if ( $disable_first ) {
305 $page_links[] = '<a class="item disabled" aria-hidden="true"><i class="angle double left icon"></i></a>';
306 } else {
307 $page_links[] = sprintf( "<a class='item' href='%s' title='" . esc_html__( 'First page' ) . "' aria-hidden='true'>%s</a>", esc_url( remove_query_arg( 'paged', $current_url ) ), '<i class="angle double left icon"></i>' );
308 }
309
310 if ( $disable_prev ) {
311 $page_links[] = '<a class="item disabled" aria-hidden="true"><i class="angle left icon"></i></a>';
312 } else {
313 $page_links[] = sprintf( "<a class='item' href='%s' title='" . esc_html__( 'Previous page' ) . "' aria-hidden='true'>%s</a>", esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ), '<i class="angle left icon"></i>' );
314 }
315
316 if ( $current - 1 > 0 ) {
317 $page_links[] = sprintf( "<a class='item' href='%s'>%s</a>", esc_url( add_query_arg( 'paged', $current - 1, $current_url ) ), $current - 1 );
318 }
319
320 $page_links[] = sprintf( "<a class='item active' href='%s'>%s</a>", esc_url( add_query_arg( 'paged', $current, $current_url ) ), $current );
321
322 if ( $current + 1 <= $total_pages ) {
323 $page_links[] = sprintf( "<a class='item' href='%s'>%s</a>", esc_url( add_query_arg( 'paged', $current + 1, $current_url ) ), $current + 1 );
324 }
325
326 if ( $disable_next ) {
327 $page_links[] = '<span class="item disabled " aria-hidden="true"><i class="right angle icon"></i></span>';
328 } else {
329 $page_links[] = sprintf( "<a class='item' href='%s' title='" . esc_html__( 'Next page', 'mainwp' ) . "'>%s</a>", esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ), '<i class="angle right icon"></i>' );
330 }
331
332 if ( $disable_last ) {
333 $page_links[] = '<a class="item disabled" aria-hidden="true"><i class="right angle double icon"></i></a>';
334 } else {
335 $page_links[] = sprintf( "<a class='item' href='%s' title='" . esc_html__( 'Last page', 'mainwp' ) . "' aria-hidden='true'>%s</a>", esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), '<i class="right angle double icon"></i>' );
336 }
337
338 if ( $total_pages > 1 ) {
339 $perpage_paging = $perpage_paging . "&nbsp;&nbsp;<div class='ui pagination menu'>" . join( "\n", $page_links ) . '</div>';
340 } else {
341 $perpage_paging = $perpage_paging;
342 }
343
344 ob_start();
345
346 echo $perpage_paging; // phpcs:ignore WordPress.Security.EscapeOutput
347
348 $output = ob_get_clean();
349
350 $this->_pagination = $output;
351
352 echo $this->_pagination; // phpcs:ignore WordPress.Security.EscapeOutput
353 }
354
355 /**
356 * Method get_columns()
357 *
358 * Get the collumns to display.
359 *
360 * @return array List of collumns to display.
361 */
362 public function get_columns() {
363 return array();
364 }
365
366 /**
367 * Method order_callback()
368 *
369 * @param object $plugin_a Plugin A.
370 * @param object $plugin_b Plugin B.
371 *
372 * @return int 0|1 Default 0.
373 */
374 private function order_callback( $plugin_a, $plugin_b ) {
375 $orderby = $this->orderby;
376 if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) {
377 return 0;
378 }
379
380 $a = $plugin_a->$orderby;
381 $b = $plugin_b->$orderby;
382
383 if ( $a === $b ) {
384 return 0;
385 }
386
387 if ( 'DESC' === $this->order ) {
388 return ( $a < $b ) ? 1 : -1;
389 } else {
390 return ( $a < $b ) ? -1 : 1;
391 }
392 }
393
394 /**
395 * Method display_rows()
396 *
397 * Build Plugin Cards.
398 *
399 * @return mixed Plugin cards.
400 */
401 public function display_rows() {
402 $plugins_allowedtags = array(
403 'a' => array(
404 'href' => array(),
405 'title' => array(),
406 'target' => array(),
407 ),
408 'abbr' => array( 'title' => array() ),
409 'acronym' => array( 'title' => array() ),
410 'code' => array(),
411 'pre' => array(),
412 'em' => array(),
413 'strong' => array(),
414 'ul' => array(),
415 'ol' => array(),
416 'li' => array(),
417 'p' => array(),
418 'br' => array(),
419 );
420
421 $plugins_group_titles = array(
422 'Performance' => _x( 'Performance', 'Plugin installer group title' ),
423 'Social' => _x( 'Social', 'Plugin installer group title' ),
424 'Tools' => _x( 'Tools', 'Plugin installer group title' ),
425 );
426
427 $group = null;
428
429 foreach ( (array) $this->items as $plugin ) {
430 if ( is_object( $plugin ) ) {
431 $plugin = (array) $plugin;
432 }
433
434 // Display the group heading if there is one.
435 if ( isset( $plugin['group'] ) && $plugin['group'] != $group ) { //phpcs:ignore -- to valid.
436 if ( isset( $this->groups[ $plugin['group'] ] ) ) {
437 $group_name = $this->groups[ $plugin['group'] ];
438 if ( isset( $plugins_group_titles[ $group_name ] ) ) {
439 $group_name = $plugins_group_titles[ $group_name ];
440 }
441 } else {
442 $group_name = $plugin['group'];
443 }
444
445 // Starting a new group, close off the divs of the last one.
446 if ( ! empty( $group ) ) {
447 echo '</div></div>';
448 }
449
450 echo '<div class="plugin-group"><h3>' . esc_html( $group_name ) . '</h3>';
451 // Needs an extra wrapping div for nth-child selectors to work.
452 echo '<div class="plugin-items">';
453
454 $group = $plugin['group'];
455 }
456 $title = wp_kses( $plugin['name'], $plugins_allowedtags );
457
458 // Remove any HTML from the description.
459 $description = wp_strip_all_tags( $plugin['short_description'] );
460 $version = wp_kses( $plugin['version'], $plugins_allowedtags );
461
462 $name = wp_strip_all_tags( $title . ' ' . $version );
463
464 $author = wp_kses( $plugin['author'], $plugins_allowedtags );
465 if ( ! empty( $author ) ) {
466 $author = ' <cite>' . sprintf( esc_html__( 'By %s', 'mainwp' ), $author ) . '</cite>';
467 }
468
469 $details_link = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin['slug'] . '&url=' . ( isset( $plugin['PluginURI'] ) ? rawurlencode( $plugin['PluginURI'] ) : '' ) . '&name=' . rawurlencode( $plugin['name'] ) );
470
471 if ( ! empty( $plugin['icons']['svg'] ) ) {
472 $plugin_icon_url = $plugin['icons']['svg'];
473 } elseif ( ! empty( $plugin['icons']['2x'] ) ) {
474 $plugin_icon_url = $plugin['icons']['2x'];
475 } elseif ( ! empty( $plugin['icons']['1x'] ) ) {
476 $plugin_icon_url = $plugin['icons']['1x'];
477 } else {
478 $plugin_icon_url = $plugin['icons']['default'];
479 }
480
481 $last_updated_timestamp = strtotime( $plugin['last_updated'] );
482 ?>
483
484 <div class="card plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>" plugin-slug="<?php echo esc_attr( $plugin['slug'] ); ?>" plugin-name="<?php echo esc_attr( $plugin['name'] ); ?>">
485 <?php
486 /**
487 * Action: mainwp_install_plugin_card_top
488 *
489 * Fires at the plugin card at top on the Install Plugins page.
490 *
491 * @since 4.1
492 */
493 do_action( 'mainwp_install_plugin_card_top' );
494 ?>
495 <div class="content">
496 <a class="right floated mini ui image open-plugin-details-modal" href="<?php echo esc_url( $details_link ); ?>"><img src="<?php echo esc_attr( $plugin_icon_url ); ?>" /></a>
497 <div class="header">
498 <a class="open-plugin-details-modal" href="<?php echo esc_url( $details_link ); ?>"><?php echo $title; // phpcs:ignore WordPress.Security.EscapeOutput ?></a>
499 </div>
500 <div class="meta">
501 <?php echo $author; // phpcs:ignore WordPress.Security.EscapeOutput ?>
502 </div>
503 <div class="description">
504 <?php echo esc_html( wp_strip_all_tags( $description ) ); ?>
505 </div>
506 </div>
507 <div class="extra content">
508 <div class="ui stacking grid">
509 <div class="four wide left aligned column">
510 <?php
511 wp_star_rating(
512 array(
513 'rating' => $plugin['rating'],
514 'type' => 'percent',
515 'number' => $plugin['num_ratings'],
516 )
517 );
518 ?>
519 </div>
520 <div class="twelve wide right aligned column"><span class="ui small text"><?php esc_html_e( 'Updated: ', 'mainwp' ); ?><?php printf( esc_html__( '%s ago', 'mainwp' ), human_time_diff( $last_updated_timestamp ) ); // phpcs:ignore WordPress.Security.EscapeOutput ?></span></div>
521 </div>
522 </div>
523 <div class="extra content">
524 <a href="<?php echo esc_attr( $details_link ); ?>" class="ui mini button open-plugin-details-modal"><?php echo esc_html( 'Plugin Details' ); ?></a>
525 <div class="ui radio checkbox right floated">
526 <input name="install-plugin" type="radio" id="install-plugin-<?php echo sanitize_html_class( $plugin['slug'] ); ?>" plugin-name="<?php echo esc_attr( $title ); ?>" plugin-version="<?php echo esc_attr( $version ); ?>">
527 <label><?php esc_html_e( 'Install Plugin', 'mainwp' ); ?></label>
528 </div>
529 </div>
530 <?php
531 /**
532 * Action: mainwp_install_plugin_card_bottom
533 *
534 * Fires at the plugin card at bottom on the Install Plugins page.
535 *
536 * @since 4.1
537 */
538 do_action( 'mainwp_install_plugin_card_bottom', $plugin );
539 ?>
540 </div>
541 <?php
542 }
543 ?>
544 <script type="text/javascript">
545 jQuery( document ).ready( function() {
546 jQuery( '.card .ui.star.rating' ).rating();
547 } );
548 </script>
549 <?php
550 // Close off the group divs of the last one.
551 if ( ! empty( $group ) ) {
552 echo '</div></div>';
553 }
554 }
555 }
556