ShouldCheck. */ public static function menu() { add_management_page( 'ShouldCheck', 'ShouldCheck', 'manage_options', self::SLUG, array( __CLASS__, 'render' ) ); } /** * Load CSS and scripts only where they are needed. * * @param string $hook Admin page hook. */ public static function assets( $hook ) { if ( 'tools_page_' . self::SLUG === $hook ) { wp_enqueue_style( 'shouldcheck-admin', SHOULDCHECK_URL . 'assets/css/admin.css', array(), SHOULDCHECK_VERSION ); wp_enqueue_script( 'shouldcheck-review', SHOULDCHECK_URL . 'assets/js/review.js', array(), SHOULDCHECK_VERSION, true ); } if ( in_array( $hook, array( 'plugin-install.php', 'plugins.php' ), true ) && current_user_can( 'install_plugins' ) && ShouldCheck_Settings::active( 'beside' ) ) { wp_enqueue_style( 'shouldcheck-screens', SHOULDCHECK_URL . 'assets/css/screens.css', array( 'dashicons' ), SHOULDCHECK_VERSION ); } if ( 'plugin-install.php' === $hook && current_user_can( 'install_plugins' ) && ! is_multisite() && ShouldCheck_Settings::active( 'beside' ) ) { wp_enqueue_script( 'shouldcheck-install-screen', SHOULDCHECK_URL . 'assets/js/install-screen.js', array(), SHOULDCHECK_VERSION, true ); wp_localize_script( 'shouldcheck-install-screen', 'shouldcheckInstall', array( 'action' => admin_url( 'admin-post.php' ), 'nonce' => wp_create_nonce( 'shouldcheck_check_zip' ), ) ); } } /** * "ShouldCheck" link on the Plugins screen row for this plugin. * * @param array $links Action links. * @return array */ public static function action_links( $links ) { array_unshift( $links, 'Open ShouldCheck' ); return $links; } /** * Ask for the first-run choices on the Plugins screens until they are made. After a reinstall that found * kept data, point to the choice about it instead. Activation does not redirect, so this is the way in. */ public static function setup_notice() { global $pagenow; if ( ! in_array( $pagenow, array( 'plugins.php', 'plugin-install.php' ), true ) || ! current_user_can( 'manage_options' ) ) { return; } if ( get_user_meta( get_current_user_id(), 'shouldcheck_setup_dismissed', true ) ) { return; } $dismiss = wp_nonce_url( admin_url( 'admin-post.php?action=shouldcheck_dismiss_setup' ), 'shouldcheck_dismiss_setup' ); if ( is_array( get_option( 'shouldcheck_kept_data', false ) ) ) { echo '

ShouldCheck found your earlier reviews and history. Keep them or delete them. Open ShouldCheck · Not now

'; return; } if ( ShouldCheck_Settings::get( 'onboarded' ) ) { return; } echo '

ShouldCheck needs two choices before it appears on these screens. Choose now · Not now

'; } /** * Hide the first-run notice for this person. They can still choose on the ShouldCheck screen. */ public static function dismiss_setup() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'shouldcheck_dismiss_setup' ); update_user_meta( get_current_user_id(), 'shouldcheck_setup_dismissed', 1 ); wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'plugins.php' ) ); exit; } /** * Record the site's current state now. */ public static function check_now() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'shouldcheck_check_now' ); ShouldCheck_Site::reconcile(); wp_safe_redirect( ShouldCheck_Views::url( array( 'shouldcheck_message' => 'checked' ) ) ); exit; } /** * Save the two settings. */ public static function save_settings() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'shouldcheck_save_settings' ); $lookup = sanitize_key( wp_unslash( $_POST['package_lookup'] ?? 'no' ) ); $mode = sanitize_key( wp_unslash( $_POST['mode'] ?? 'beside' ) ); ShouldCheck_Settings::update( array( 'package_lookup' => 'yes' === $lookup ? 'yes' : 'no', 'beside' => 'manual' !== $mode, 'onboarded' => true, 'keep_data' => '1' === sanitize_key( wp_unslash( $_POST['keep_data'] ?? '0' ) ), ) ); wp_safe_redirect( ShouldCheck_Views::url( array( 'shouldcheck_message' => 'saved' ) ) ); exit; } /** * Show the first-run setup again. Reviews, history and settings stay as they are. */ public static function reset_setup() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'shouldcheck_reset_setup' ); ShouldCheck_Settings::update( array( 'onboarded' => false ) ); wp_safe_redirect( ShouldCheck_Views::url() . '#shouldcheck-settings' ); exit; } /** * After a reinstall that found kept data: keep it, or delete it now. */ public static function kept_decide() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'shouldcheck_kept_decide' ); $choice = sanitize_key( wp_unslash( $_POST['choice'] ?? '' ) ); if ( 'delete' === $choice ) { foreach ( array( ShouldCheck_Site::OPT_SNAPSHOTS, ShouldCheck_Site::OPT_EVENTS, ShouldCheck_Site::OPT_LAST_CHECK, ShouldCheck_Reports::OPTION, ShouldCheck_Settings::OPTION ) as $option ) { delete_option( $option ); } delete_option( 'shouldcheck_kept_data' ); ShouldCheck_Site::reconcile(); wp_safe_redirect( ShouldCheck_Views::url( array( 'shouldcheck_message' => 'cleared' ) ) . '#shouldcheck-settings' ); exit; } delete_option( 'shouldcheck_kept_data' ); wp_safe_redirect( ShouldCheck_Views::url( array( 'shouldcheck_message' => 'kept' ) ) ); exit; } /** * Delete one saved review. */ public static function delete_report() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'shouldcheck_report_delete' ); $id = sanitize_text_field( wp_unslash( $_POST['report_id'] ?? '' ) ); if ( ShouldCheck_Reports::is_id( $id ) ) { ShouldCheck_Reports::delete( $id ); } wp_safe_redirect( ShouldCheck_Views::url( array( 'shouldcheck_tab' => 'reviews', 'shouldcheck_message' => 'deleted', ) ) ); exit; } /** * Read a query argument as plain text. * * @param string $key Argument name. * @return string */ private static function arg( $key ) { return isset( $_GET[ $key ] ) ? sanitize_text_field( wp_unslash( $_GET[ $key ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended } /** * Render the screen. */ public static function render() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden', 'shouldcheck' ), '', array( 'response' => 403 ) ); } echo '
'; echo self::flash(); // phpcs:ignore WordPress.Security.EscapeOutput $upload = self::arg( 'shouldcheck_upload' ); $dir = self::arg( 'shouldcheck_directory' ); $update = self::arg( 'shouldcheck_update' ); $file = self::arg( 'shouldcheck_file' ); $inst = self::arg( 'shouldcheck_installed' ); if ( '' !== $upload ) { echo ShouldCheck_Installer::render_zip_review( $upload ); // phpcs:ignore WordPress.Security.EscapeOutput } elseif ( '' !== $dir && ShouldCheck_Requirements::valid_slug( $dir ) ) { echo ShouldCheck_Installer::render_directory_review( $dir ); // phpcs:ignore WordPress.Security.EscapeOutput } elseif ( '' !== $inst ) { echo ShouldCheck_Installer::render_installed_review( $inst ); // phpcs:ignore WordPress.Security.EscapeOutput } elseif ( '' !== $update && '' !== $file ) { echo ShouldCheck_Installer::render_update_review( $update, $file ); // phpcs:ignore WordPress.Security.EscapeOutput } else { if ( '' !== $dir ) { echo '

That is not a valid plugin identifier.

'; } $tab = self::arg( 'shouldcheck_tab' ); $tab = in_array( $tab, array( 'reviews', 'changes' ), true ) ? $tab : 'overview'; if ( '' !== self::arg( 'shouldcheck_view' ) ) { $tab = 'reviews'; } echo ShouldCheck_Views::heading( 'ShouldCheck', 'Tools' ); // phpcs:ignore WordPress.Security.EscapeOutput echo ShouldCheck_Views::tabs( $tab ); // phpcs:ignore WordPress.Security.EscapeOutput if ( 'reviews' === $tab ) { echo self::reviews_tab(); // phpcs:ignore WordPress.Security.EscapeOutput } elseif ( 'changes' === $tab ) { echo self::changes_tab(); // phpcs:ignore WordPress.Security.EscapeOutput } else { echo self::overview_tab(); // phpcs:ignore WordPress.Security.EscapeOutput } } echo '
'; } /** * One-time notices after an action. * * @return string */ private static function flash() { $out = ''; $msg = self::arg( 'shouldcheck_message' ); if ( 'checked' === $msg ) { $out .= '

Site checked. Any changes were recorded.

'; } elseif ( 'saved' === $msg ) { $out .= '

Settings saved.

'; } elseif ( 'kept' === $msg ) { $out .= '

Your earlier reviews and history are back.

'; } elseif ( 'cleared' === $msg ) { $out .= '

Earlier data deleted. Start fresh below.

'; } elseif ( 'deleted' === $msg ) { $out .= '

Review deleted.

'; } return $out; } /** * Overview. * * @return string */ private static function overview_tab() { $site = ShouldCheck_Site::snapshot(); $out = ''; $kept = get_option( 'shouldcheck_kept_data', false ); if ( is_array( $kept ) ) { $out .= ShouldCheck_Views::welcome_back_card( $kept ); } $out .= '

Check a plugin before you install it

Choose a plugin ZIP. ShouldCheck reads it and tells you, in plain words, what it found and what it could not check.

'; if ( current_user_can( 'install_plugins' ) && ! is_multisite() ) { $out .= '
' . wp_nonce_field( 'shouldcheck_check_zip', '_wpnonce', true, false ) . '
No file chosen
'; $out .= ''; } else { $out .= '

Checking uploads is not available for this user or on a multisite network.

'; } if ( ! ShouldCheck_Settings::get( 'onboarded' ) ) { $out .= '

First time here? Finish the quick setup below.

'; } $out .= '
'; $out .= '

Recent reviews

' . ShouldCheck_Views::reports_table( ShouldCheck_Reports::all(), 8 ); if ( count( ShouldCheck_Reports::all() ) > 8 ) { $out .= '

All reviews

'; } $out .= '
'; $out .= '
'; $last = get_option( ShouldCheck_Site::OPT_LAST_CHECK, '' ); $out .= '

This site

' . ShouldCheck_Views::site_card( $site ) . '

Last compared: ' . esc_html( $last ? $last . ' UTC' : 'not yet' ) . '. Compared hourly by WP-Cron, which depends on site traffic.

' . wp_nonce_field( 'shouldcheck_check_now', '_wpnonce', true, false ) . '
'; $out .= '
'; $out .= ShouldCheck_Views::checkout_card(); $out .= '

Recent site changes

' . ShouldCheck_Views::timeline_table( ShouldCheck_Site::timeline( 6 ) ) . '

All site changes

'; $out .= ShouldCheck_Views::settings_card( ShouldCheck_Settings::all() ); return $out; } /** * Reviews tab: list and one report. * * @return string */ private static function reviews_tab() { $out = ''; $selected = self::arg( 'shouldcheck_view' ); $reports = ShouldCheck_Reports::all(); if ( '' !== $selected ) { $report = ShouldCheck_Reports::find( $selected ); if ( $report ) { $out .= ShouldCheck_Views::report_page( $report ); } else { $out .= '

That review was not found. It may have been deleted.

'; } } $out .= '

All reviews ' . (int) count( $reports ) . ' kept, newest first

' . ShouldCheck_Views::reports_table( $reports, 0 ) . '
'; return $out; } /** * Site changes tab. * * @return string */ private static function changes_tab() { $site = ShouldCheck_Site::snapshot(); $out = '

Site changes

' . ShouldCheck_Views::timeline_table( ShouldCheck_Site::timeline( 60 ) ) . '
'; $out .= '

Current state

' . ShouldCheck_Views::site_card( $site ) . '
'; return $out; } }