PluginProbe
Plugin Check (PCP) / trunk
Plugin Check (PCP) vtrunk
2.1.0 trunk 0.1 0.2.0 0.2.1 0.2.2 0.2.3 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 ci-artifacts
plugin-check / includes / Admin / Admin_Page.php

Admin_Page.php in Plugin Check (PCP) trunk, at includes/Admin/Admin_Page.php

476 lines 12.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class WordPress\Plugin_Check\Admin\Admin_Page
4 *
5 * @package plugin-check
6 */
7
8 namespace WordPress\Plugin_Check\Admin;
9
10 use WordPress\Plugin_Check\Checker\Check;
11 use WordPress\Plugin_Check\Checker\Check_Categories;
12 use WordPress\Plugin_Check\Checker\Check_Repository;
13 use WordPress\Plugin_Check\Checker\Check_Types;
14 use WordPress\Plugin_Check\Checker\Default_Check_Repository;
15
16 /**
17 * Class is handling admin tools page functionality.
18 *
19 * @since 1.0.0
20 *
21 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
22 */
23 final class Admin_Page {
24
25 /**
26 * Admin AJAX class instance.
27 *
28 * @since 1.0.0
29 * @var Admin_AJAX
30 */
31 protected $admin_ajax;
32
33 /**
34 * Admin page hook suffix.
35 *
36 * @since 1.0.0
37 * @var string
38 */
39 protected $hook_suffix = '';
40
41 /**
42 * Constructor.
43 *
44 * @since 1.0.0
45 *
46 * @param Admin_AJAX $admin_ajax Instance of Admin_AJAX.
47 */
48 public function __construct( Admin_AJAX $admin_ajax ) {
49 $this->admin_ajax = $admin_ajax;
50 }
51
52 /**
53 * Registers WordPress hooks for the admin page.
54 *
55 * @since 1.0.0
56 */
57 public function add_hooks() {
58 add_action( 'admin_menu', array( $this, 'add_and_initialize_page' ) );
59 add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 4 );
60 add_action( 'admin_enqueue_scripts', array( $this, 'add_jump_to_line_code_editor' ) );
61
62 $this->admin_ajax->add_hooks();
63 }
64
65 /**
66 * Adds the admin page under the tools menu.
67 *
68 * @since 1.0.0
69 */
70 public function add_page() {
71 $this->hook_suffix = add_management_page(
72 __( 'Plugin Check', 'plugin-check' ),
73 __( 'Plugin Check', 'plugin-check' ),
74 'activate_plugins',
75 'plugin-check',
76 array( $this, 'render_page' )
77 );
78 }
79
80 /**
81 * Adds and initializes the admin page under the tools menu.
82 *
83 * @since 1.0.0
84 */
85 public function add_and_initialize_page() {
86 $this->add_page();
87 add_action( 'load-' . $this->get_hook_suffix(), array( $this, 'initialize_page' ) );
88 }
89
90 /**
91 * Initializes page hooks.
92 *
93 * @since 1.0.0
94 */
95 public function initialize_page() {
96 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
97 add_action( 'admin_footer', array( $this, 'admin_footer' ) );
98
99 $this->add_help_tab();
100 }
101
102 /**
103 * Adds the plugin help tab.
104 *
105 * @since 1.1.0
106 */
107 public function add_help_tab() {
108 $screen = get_current_screen();
109
110 if ( ! $screen ) {
111 return;
112 }
113
114 $screen->add_help_tab(
115 array(
116 'id' => 'plugin-check',
117 'title' => __( 'Checks', 'plugin-check' ),
118 'content' => '',
119 'callback' => array( $this, 'render_help_tab' ),
120 )
121 );
122 }
123
124 /**
125 * Renders the plugin help tab.
126 *
127 * @since 1.1.0
128 */
129 public function render_help_tab() {
130 $check_repo = new Default_Check_Repository();
131 $collection = $check_repo->get_checks( Check_Repository::TYPE_ALL );
132
133 if ( empty( $collection ) ) {
134 return;
135 }
136
137 $category_labels = Check_Categories::get_categories();
138
139 echo '<dl>';
140
141 /**
142 * All checks to list.
143 *
144 * @var Check $check
145 */
146 foreach ( $collection as $key => $check ) {
147 $categories = array_map(
148 static function ( $category ) use ( $category_labels ) {
149 return $category_labels[ $category ] ?? $category;
150 },
151 $check->get_categories()
152 );
153 $categories = join( ', ', $categories );
154 ?>
155 <dt>
156 <code><?php echo esc_html( $key ); ?></code>
157 (<?php echo esc_html( $categories ); ?>)
158 </dt>
159 <dd>
160 <?php echo wp_kses( $check->get_description(), array( 'code' => array() ) ); ?>
161 <br>
162 <a href="<?php echo esc_url( $check->get_documentation_url() ); ?>">
163 <?php esc_html_e( 'Learn more', 'plugin-check' ); ?>
164 </a>
165 </dd>
166 <?php
167 }
168
169 echo '</dl>';
170 }
171
172 /**
173 * Loads the check's script.
174 *
175 * @since 1.0.0
176 */
177 public function enqueue_scripts() {
178 wp_enqueue_script(
179 'plugin-check-admin',
180 WP_PLUGIN_CHECK_PLUGIN_DIR_URL . 'assets/js/plugin-check-admin.js',
181 array(
182 'wp-util',
183 ),
184 WP_PLUGIN_CHECK_VERSION,
185 true
186 );
187
188 wp_enqueue_style(
189 'plugin-check-admin',
190 WP_PLUGIN_CHECK_PLUGIN_DIR_URL . 'assets/css/plugin-check-admin.css',
191 array(),
192 WP_PLUGIN_CHECK_VERSION
193 );
194
195 wp_add_inline_script(
196 'plugin-check-admin',
197 'const PLUGIN_CHECK = ' . json_encode(
198 array(
199 'nonce' => $this->admin_ajax->get_nonce(),
200 'actionGetChecksToRun' => Admin_AJAX::ACTION_GET_CHECKS_TO_RUN,
201 'actionSetUpRuntimeEnvironment' => Admin_AJAX::ACTION_SET_UP_ENVIRONMENT,
202 'actionRunChecks' => Admin_AJAX::ACTION_RUN_CHECKS,
203 'actionCleanUpRuntimeEnvironment' => Admin_AJAX::ACTION_CLEAN_UP_ENVIRONMENT,
204 'actionExportResults' => Admin_AJAX::ACTION_EXPORT_RESULTS,
205 'successMessage' => __( 'No errors found.', 'plugin-check' ),
206 'errorMessage' => __( 'Errors were found.', 'plugin-check' ),
207 'settingsPageUrl' => admin_url( 'options-general.php?page=plugin-check-settings' ),
208 /* translators: %d: Number of errors found. */
209 'errorString' => __( '%d error', 'plugin-check' ),
210 /* translators: %d: Number of errors found. */
211 'errorsString' => __( '%d errors', 'plugin-check' ),
212 /* translators: %d: Number of warnings found. */
213 'warningString' => __( '%d warning', 'plugin-check' ),
214 /* translators: %d: Number of warnings found. */
215 'warningsString' => __( '%d warnings', 'plugin-check' ),
216 /* translators: 1: Formatted error count string (e.g. "3 errors"), 2: Formatted warning count string (e.g. "2 warnings"). */
217 'summaryBothTemplate' => __( '%1$s and %2$s found.', 'plugin-check' ),
218 /* translators: %s: Formatted issue count string (e.g. "3 errors" or "2 warnings"). */
219 'summarySingleTemplate' => __( '%s found.', 'plugin-check' ),
220 'strings' => array(
221 'exportCsv' => __( 'Export CSV', 'plugin-check' ),
222 'exportJson' => __( 'Export JSON', 'plugin-check' ),
223 'exportCtrf' => __( 'Export CTRF', 'plugin-check' ),
224 'exportMarkdown' => __( 'Export Markdown', 'plugin-check' ),
225 'exporting' => __( 'Preparing export…', 'plugin-check' ),
226 'exportError' => __( 'Export failed.', 'plugin-check' ),
227 'noResults' => __( 'There are no results to export yet.', 'plugin-check' ),
228 ),
229 )
230 ),
231 'before'
232 );
233 }
234
235 /**
236 * Enqueue a script in the WordPress admin on plugin-editor.php.
237 *
238 * @since 1.0.0
239 *
240 * @param string $hook_suffix The current admin page.
241 */
242 public function add_jump_to_line_code_editor( $hook_suffix ) {
243 if ( 'plugin-editor.php' !== $hook_suffix ) {
244 return;
245 }
246
247 $line = (int) ( $_GET['line'] ?? 0 );
248 if ( ! $line ) {
249 return;
250 }
251
252 wp_add_inline_script(
253 'wp-theme-plugin-editor',
254 sprintf(
255 '
256 (
257 ( originalInitCodeEditor ) => {
258 wp.themePluginEditor.initCodeEditor = function() {
259 originalInitCodeEditor.apply( this, arguments );
260 this.instance.codemirror.doc.setCursor( %d - 1 );
261 };
262 }
263 )( wp.themePluginEditor.initCodeEditor );
264 ',
265 wp_json_encode( $line )
266 )
267 );
268 }
269
270 /**
271 * Returns the list of plugins.
272 *
273 * @since 1.0.0
274 *
275 * @return array List of available plugins.
276 */
277 private function get_available_plugins() {
278 $available_plugins = get_plugins();
279
280 if ( empty( $available_plugins ) ) {
281 return array();
282 }
283
284 $plugin_check_base_name = plugin_basename( WP_PLUGIN_CHECK_MAIN_FILE );
285
286 if ( isset( $available_plugins[ $plugin_check_base_name ] ) ) {
287 unset( $available_plugins[ $plugin_check_base_name ] );
288 }
289
290 return $available_plugins;
291 }
292
293 /**
294 * Renders the "Plugin Check" page.
295 *
296 * @since 1.0.0
297 *
298 * @SuppressWarnings(PHPMD.UnusedLocalVariable)
299 */
300 public function render_page() {
301 $available_plugins = $this->get_available_plugins();
302
303 $selected_plugin_basename = filter_input( INPUT_GET, 'plugin', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
304
305 $categories = Check_Categories::get_categories();
306 $types = Check_Types::get_types();
307
308 // Get user settings for category preferences.
309 $user_enabled_categories = get_user_setting( 'plugin_check_category_preferences', implode( '__', $this->get_default_check_categories_to_be_selected() ) );
310 $user_enabled_categories = explode( '__', $user_enabled_categories );
311
312 $check_repo = new Default_Check_Repository();
313
314 $collection = $check_repo->get_checks( Check_Repository::TYPE_ALL | Check_Repository::INCLUDE_EXPERIMENTAL )->filter(
315 static function ( Check $check ) {
316 return $check->get_stability() === Check::STABILITY_EXPERIMENTAL;
317 }
318 );
319
320 $has_experimental_checks = count( $collection ) > 0;
321
322 require WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'templates/admin-page.php';
323 }
324
325 /**
326 * Adds "check this plugin" link in the plugins list table.
327 *
328 * @since 1.0.0
329 *
330 * @param array $actions List of actions.
331 * @param string $plugin_file Plugin main file.
332 * @param array $plugin_data An array of plugin data.
333 * @param string $context The plugin context. By default this can include 'all',
334 * 'active', 'inactive', 'recently_activated', 'upgrade',
335 * 'mustuse', 'dropins', and 'search'.
336 * @return array The modified list of actions.
337 */
338 public function filter_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
339
340 if ( in_array( $context, array( 'mustuse', 'dropins' ), true ) ) {
341 return $actions;
342 }
343
344 $plugin_check_base_name = plugin_basename( WP_PLUGIN_CHECK_MAIN_FILE );
345 if ( $plugin_check_base_name === $plugin_file ) {
346 $actions[] = sprintf(
347 '<a href="%1$s">%2$s</a>',
348 esc_url( admin_url( 'tools.php?page=plugin-check' ) ),
349 esc_html__( 'Check a plugin', 'plugin-check' )
350 );
351 return $actions;
352 }
353
354 if ( current_user_can( 'activate_plugins' ) ) {
355 $actions[] = sprintf(
356 '<a href="%1$s">%2$s</a>',
357 esc_url( admin_url( "tools.php?page=plugin-check&plugin={$plugin_file}" ) ),
358 esc_html__( 'Check this plugin', 'plugin-check' )
359 );
360 }
361
362 return $actions;
363 }
364
365 /**
366 * Render the results table templates in the footer.
367 *
368 * @since 1.0.0
369 */
370 public function admin_footer() {
371 ob_start();
372 require WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'templates/results-table.php';
373 $results_table_template = ob_get_clean();
374 wp_print_inline_script_tag(
375 $results_table_template,
376 array(
377 'id' => 'tmpl-plugin-check-results-table',
378 'type' => 'text/template',
379 )
380 );
381
382 ob_start();
383 require WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'templates/results-row.php';
384 $results_row_template = ob_get_clean();
385 wp_print_inline_script_tag(
386 $results_row_template,
387 array(
388 'id' => 'tmpl-plugin-check-results-row',
389 'type' => 'text/template',
390 )
391 );
392
393 ob_start();
394 require WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'templates/results-false-positives.php';
395 $results_false_positives_template = ob_get_clean();
396 wp_print_inline_script_tag(
397 $results_false_positives_template,
398 array(
399 'id' => 'tmpl-plugin-check-results-false-positives',
400 'type' => 'text/template',
401 )
402 );
403
404 ob_start();
405 require WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'templates/results-complete.php';
406 $results_row_template = ob_get_clean();
407 wp_print_inline_script_tag(
408 $results_row_template,
409 array(
410 'id' => 'tmpl-plugin-check-results-complete',
411 'type' => 'text/template',
412 )
413 );
414 ?>
415 <style>
416 #plugin-check__results .notice,
417 #plugin-check__results .notice + h4 {
418 margin-top: 20px;
419 }
420 #plugin-check__results h4:first-child {
421 margin-top: 80.5px;
422 }
423 @media ( max-width: 782px ) {
424 #plugin-check__results h4:first-child {
425 margin-top: 88.5px;
426 }
427 }
428 .plugin-check__export-controls {
429 margin-top: 24px;
430 display: flex;
431 gap: 8px;
432 flex-wrap: wrap;
433 }
434 .plugin-check__export-controls.is-hidden {
435 display: none;
436 }
437 </style>
438 <?php
439 }
440
441 /**
442 * Gets the hook suffix under which the admin page is added.
443 *
444 * @since 1.0.0
445 *
446 * @return string Hook suffix, or empty string if admin page was not added.
447 */
448 public function get_hook_suffix() {
449 return $this->hook_suffix;
450 }
451
452 /**
453 * Gets default check categories to be selected.
454 *
455 * @since 1.0.2
456 *
457 * @return string[] An array of category slugs.
458 */
459 private function get_default_check_categories_to_be_selected() {
460 $default_check_categories = array(
461 'plugin_repo',
462 );
463
464 /**
465 * Filters the default check categories to be selected.
466 *
467 * @since 1.0.2
468 *
469 * @param string[] $default_check_categories An array of category slugs.
470 */
471 $default_categories = (array) apply_filters( 'wp_plugin_check_default_categories', $default_check_categories );
472
473 return $default_categories;
474 }
475 }
476