diff --git a/HealthCheck/BackCompat/class-wp-site-health.php b/HealthCheck/BackCompat/class-wp-site-health.php index db7738bd..d2a6308c 100644 --- a/HealthCheck/BackCompat/class-wp-site-health.php +++ b/HealthCheck/BackCompat/class-wp-site-health.php @@ -55,9 +55,11 @@ public function enqueues() { return; } - wp_enqueue_style( 'health-check', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check.css', array(), HEALTH_CHECK_PLUGIN_VERSION ); + $health_check = include HEALTH_CHECK_PLUGIN_DIRECTORY . 'build/health-check.asset.php'; - wp_enqueue_script( 'health-check', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check.js', array( 'jquery' ), HEALTH_CHECK_PLUGIN_VERSION ); + wp_enqueue_style( 'health-check', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check.css', array(), $health_check['version'] ); + + wp_enqueue_script( 'health-check', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check.js', array( 'jquery' ), $health_check['version'] ); wp_localize_script( 'health-check', diff --git a/HealthCheck/Tools/class-health-check-beta-features.php b/HealthCheck/Tools/class-health-check-beta-features.php new file mode 100644 index 00000000..6b3a64a1 --- /dev/null +++ b/HealthCheck/Tools/class-health-check-beta-features.php @@ -0,0 +1,70 @@ +label = __( 'Beta features', 'health-check' ); + $this->description = __( 'The plugin may contain beta features, which you as the site owner can enable or disable as you wish.', 'health-check' ); + + parent::__construct(); + + add_action( 'admin_init', array( $this, 'toggle_beta_features' ) ); + } + + public function toggle_beta_features() { + if ( ! isset( $_GET['health-check-beta-features'] ) ) { + return; + } + + if ( ! isset( $_GET['_wpnonce'] ) ) { + return; + } + + if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'health-check-beta-features' ) ) { + return; + } + + if ( 'enable' === $_GET['health-check-beta-features'] ) { + update_option( 'health-check-beta-features', true ); + } else { + update_option( 'health-check-beta-features', false ); + } + + wp_safe_redirect( admin_url( 'site-health.php?tab=tools' ) ); + } + + public function tab_content() { + $feature_status = get_option( 'health-check-beta-features', false ); + + if ( ! $feature_status ) { + printf( + '%s', + esc_url( wp_nonce_url( add_query_arg( 'health-check-beta-features', 'enable' ), 'health-check-beta-features' ) ), + esc_html__( 'Enable beta features', 'health-check' ) + ); + } else { + printf( + '%s', + esc_url( wp_nonce_url( add_query_arg( 'health-check-beta-features', 'disable' ), 'health-check-beta-features' ) ), + esc_html__( 'Disable beta features', 'health-check' ) + ); + } + } + +} + +new Health_Check_Beta_Features(); diff --git a/HealthCheck/class-health-check-screenshots.php b/HealthCheck/class-health-check-screenshots.php new file mode 100644 index 00000000..1b713f7a --- /dev/null +++ b/HealthCheck/class-health-check-screenshots.php @@ -0,0 +1,256 @@ +should_404 ) { + return; + } + + global $wp_query; + $wp_query->set_404(); + status_header( 404 ); + nocache_headers(); + } + + public function delete_screenshot() { + if ( ! is_admin() || ! isset( $_GET['health-check-delete-screenshot'] ) || ! $this->user_can_screenshot() ) { + return; + } + + // Validate nonces. + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'health-check-delete-screenshot' ) ) { + return; + } + + wp_delete_post( $_GET['health-check-delete-screenshot'], true ); + + wp_safe_redirect( admin_url( 'site-health.php?tab=screenshots' ) ); + } + + public function display_screenshot() { + if ( ! isset( $_GET['health-check-screenshot'] ) ) { + return; + } + + $screenshot_id = $_GET['health-check-screenshot']; + $screenshot = get_posts( + array( + 'post_type' => 'health-check-images', + 'posts_per_page' => 1, + 'meta_key' => 'hash_id', + 'meta_value' => $screenshot_id, + ) + ); + + if ( empty( $screenshot ) ) { + $this->should_404 = true; + return; + } + + if ( is_array( $screenshot ) ) { + $screenshot = $screenshot[0]; + } + + $image = $screenshot->screenshot; + $image = explode( ';', $image, 2 ); + + $image_type = str_replace( 'data:', '', $image[0] ); + + if ( ! in_array( $image_type, $this->allowed_image_mimes, true ) ) { + return; + } + + header( 'Content-Type: ' . $image_type ); + + if ( isset( $_GET['dl'] ) ) { + header( 'Content-Disposition: attachment; filename="' . sanitize_title( $screenshot->post_title ) . '.jpeg"' ); + } + + $data = str_replace( 'base64,', '', $image[1] ); + echo base64_decode( $data ); + + die(); + } + + public function add_site_health_tab_content( $tab ) { + if ( 'screenshots' !== $tab ) { + return; + } + + include_once( HEALTH_CHECK_PLUGIN_DIRECTORY . '/pages/screenshots.php' ); + } + + public function add_site_health_navigation_tabs( $tabs ) { + return array_merge( + $tabs, + array( + 'screenshots' => esc_html__( 'Screenshots', 'health-check' ), + ) + ); + } + + public function user_can_screenshot() { + return current_user_can( 'view_site_health_checks' ); + } + + public function register_post_type() { + register_post_type( + 'health-check-images', + array( + 'labels' => array( + 'name' => __( 'Screenshots', 'health-check' ), + 'singular_name' => __( 'Screenshot', 'health-check' ), + ), + 'public' => false, + 'show_ui' => false, + 'show_in_menu' => false, + 'show_in_nav_menus' => false, + 'show_in_admin_bar' => false, + 'exclude_from_search' => true, + 'has_archive' => false, + 'hierarchical' => false, + 'rewrite' => false, + 'query_var' => false, + 'supports' => array( 'title' ), + ) + ); + } + + public function register_rest_routes() { + register_rest_route( + 'health-check/v1', + '/screenshot', + array( + 'methods' => 'POST', + 'callback' => array( $this, 'store_screenshot' ), + 'permission_callback' => array( $this, 'user_can_screenshot' ), + 'args' => array( + 'nonce' => array( + 'required' => true, + 'validate_callback' => function( $param, $request, $key ) { + return wp_verify_nonce( $param, 'health-check-screenshot' ); + }, + ), + 'label' => array( + 'required' => true, + 'validate_callback' => function( $param, $request, $key ) { + return is_string( $param ) && ! empty( $param ); + }, + ), + 'screenshot' => array( + 'required' => true, + 'validate_callback' => function( $param, $request, $key ) { + return is_string( $param ) && 'data:image/jpeg;' === substr( $param, 0, 16 ); + }, + ), + ), + ) + ); + } + + public function store_screenshot( \WP_REST_Request $request ) { + // Create a new post in the `health-check-images` post type. + $post_id = wp_insert_post( + array( + 'post_type' => 'health-check-images', + 'post_title' => sanitize_text_field( $request->get_param( 'label' ) ), + 'post_status' => 'publish', + 'meta_input' => array( + 'screenshot' => $request->get_param( 'screenshot' ), + 'hash_id' => wp_hash( $request->get_param( 'screenshot' ) ), + ), + ) + ); + } + + public function enqueue_scripts() { + if ( ! $this->user_can_screenshot() ) { + return; + } + + $asset = include HEALTH_CHECK_PLUGIN_DIRECTORY . 'build/health-check-global.asset.php'; + + wp_enqueue_script( 'health-check-tools', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check-global.js', array( 'jquery', 'wp-a11y' ), $asset['version'] ); + + wp_localize_script( + 'health-check-tools', + 'HealthCheckTools', + array( + 'nonce' => array( + 'rest' => wp_create_nonce( 'wp_rest' ), + 'screenshot' => wp_create_nonce( 'health-check-screenshot' ), + ), + 'rest' => array( + 'screenshot' => rest_url( 'health-check/v1/screenshot' ), + ), + ) + ); + } + + public function admin_menubar_button( $wp_menu ) { + if ( ! $this->user_can_screenshot() ) { + return; + } + + if ( ! is_admin() ) { + require_once( trailingslashit( ABSPATH ) . 'wp-admin/includes/plugin.php' ); + } + + // Add top-level menu item. + $wp_menu->add_menu( + array( + 'id' => 'health-check-screenshot', + 'title' => esc_html__( 'Take screenshot', 'health-check' ), + 'href' => '#', + 'meta' => array( + 'class' => 'health-check-take-screenshot', + ), + ) + ); + } + +} + +new Health_Check_Screenshots(); diff --git a/HealthCheck/class-health-check.php b/HealthCheck/class-health-check.php index fe3d85be..60ac9a99 100644 --- a/HealthCheck/class-health-check.php +++ b/HealthCheck/class-health-check.php @@ -232,7 +232,9 @@ public function enqueues() { return; } - wp_enqueue_style( 'health-check', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check.css', array(), HEALTH_CHECK_PLUGIN_VERSION ); + $health_check = include HEALTH_CHECK_PLUGIN_DIRECTORY . 'build/health-check.asset.php'; + + wp_enqueue_style( 'health-check', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check.css', array(), $health_check['version'] ); // If the WordPress 5.2+ version of Site Health is used, do some extra checks to not mess with core scripts and styles. if ( 'site-health' === $screen->id ) { @@ -246,7 +248,9 @@ public function enqueues() { } } - wp_enqueue_script( 'health-check-tools', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check-tools.js', array( 'jquery' ), HEALTH_CHECK_PLUGIN_VERSION ); + $health_check_tools = include HEALTH_CHECK_PLUGIN_DIRECTORY . 'build/health-check-tools.asset.php'; + + wp_enqueue_script( 'health-check-tools', trailingslashit( HEALTH_CHECK_PLUGIN_URL ) . 'build/health-check-tools.js', array( 'jquery' ), $health_check_tools['version'] ); wp_localize_script( 'health-check-tools', diff --git a/health-check.php b/health-check.php index c2a2db06..a1120d28 100644 --- a/health-check.php +++ b/health-check.php @@ -61,6 +61,7 @@ // Include class-files used by our plugin. require_once( dirname( __FILE__ ) . '/HealthCheck/class-health-check.php' ); require_once( dirname( __FILE__ ) . '/HealthCheck/class-health-check-loopback.php' ); +require_once( dirname( __FILE__ ) . '/HealthCheck/class-health-check-screenshots.php' ); require_once( dirname( __FILE__ ) . '/HealthCheck/class-health-check-troubleshoot.php' ); // Tools section. @@ -72,6 +73,7 @@ require_once( dirname( __FILE__ ) . '/HealthCheck/Tools/class-health-check-phpinfo.php' ); require_once( dirname( __FILE__ ) . '/HealthCheck/Tools/class-health-check-htaccess.php' ); require_once( dirname( __FILE__ ) . '/HealthCheck/Tools/class-health-check-robotstxt.php' ); +require_once( dirname( __FILE__ ) . '/HealthCheck/Tools/class-health-check-beta-features.php' ); // Initialize our plugin. new Health_Check(); diff --git a/mu-plugin/health-check-troubleshooting-mode.php b/mu-plugin/health-check-troubleshooting-mode.php index d8c83660..a11793ea 100644 --- a/mu-plugin/health-check-troubleshooting-mode.php +++ b/mu-plugin/health-check-troubleshooting-mode.php @@ -145,13 +145,17 @@ public function enqueue_assets() { return; } - wp_enqueue_style( 'health-check', plugins_url( '/health-check/build/health-check.css' ), array(), HEALTH_CHECK_TROUBLESHOOTING_MODE_PLUGIN_VERSION ); + $health_check = include WP_PLUGIN_DIR . '/health-check/build/health-check.asset.php'; + + wp_enqueue_style( 'health-check', plugins_url( '/health-check/build/health-check.css' ), array(), $health_check['version'] ); if ( ! wp_script_is( 'site-health', 'registered' ) ) { - wp_enqueue_script( 'site-health', plugins_url( '/health-check/build/health-check.js' ), array( 'jquery', 'wp-a11y', 'wp-util' ), HEALTH_CHECK_TROUBLESHOOTING_MODE_PLUGIN_VERSION, true ); + wp_enqueue_script( 'site-health', plugins_url( '/health-check/build/health-check.js' ), array( 'jquery', 'wp-a11y', 'wp-util' ), $health_check['version'], true ); } - wp_enqueue_script( 'health-check', plugins_url( '/health-check/build/troubleshooting-mode.js' ), array( 'site-health' ), HEALTH_CHECK_TROUBLESHOOTING_MODE_PLUGIN_VERSION, true ); + $troubleshooter = include WP_PLUGIN_DIR . '/health-check/build/troubleshooting-mode.asset.php'; + + wp_enqueue_script( 'health-check', plugins_url( '/health-check/build/troubleshooting-mode.js' ), array( 'site-health' ), $troubleshooter['version'], true ); } /** diff --git a/package-lock.json b/package-lock.json index 69d01487..2c624614 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.1.0", "license": "GPL-2.0+", "dependencies": { - "clipboard": "~2.0.11" + "clipboard": "~2.0.11", + "html2canvas": "^1.4.1" }, "devDependencies": { "@wordpress/env": "~8.2.0", @@ -5069,6 +5070,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -6222,6 +6231,14 @@ "node": ">=12.22" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-loader": { "version": "6.8.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", @@ -9079,6 +9096,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -15450,6 +15479,14 @@ "node": ">=8" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -15934,6 +15971,14 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -20552,6 +20597,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -21413,6 +21463,14 @@ "integrity": "sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==", "dev": true }, + "css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "requires": { + "utrie": "^1.0.2" + } + }, "css-loader": { "version": "6.8.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", @@ -23547,6 +23605,15 @@ "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true }, + "html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "requires": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + } + }, "http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -28290,6 +28357,14 @@ "minimatch": "^3.0.4" } }, + "text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "requires": { + "utrie": "^1.0.2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -28650,6 +28725,14 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, + "utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "requires": { + "base64-arraybuffer": "^1.0.2" + } + }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/package.json b/package.json index c7fb6d98..9a8c193b 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,14 @@ }, "description": "WordPress site health tester, and troubleshooting tools for end users and anyone providing support", "devDependencies": { - "@wordpress/scripts": "~26.6.0", "@wordpress/env": "~8.2.0", + "@wordpress/scripts": "~26.6.0", "@wordpress/stylelint-config": "~21.19.0", "postcss-scss": "~4.0.6" }, "dependencies": { - "clipboard": "~2.0.11" + "clipboard": "~2.0.11", + "html2canvas": "^1.4.1" }, "engines": { "node": ">=18" diff --git a/pages/screenshots.php b/pages/screenshots.php new file mode 100644 index 00000000..e2527281 --- /dev/null +++ b/pages/screenshots.php @@ -0,0 +1,119 @@ + + +
+ +
++ %s', + esc_html_x( 'Take Screenshot', 'Description of the button label', 'health-check' ) + ) + ); + ?> +
+ +' . esc_html__( 'You have not taken any screenshots, return here when you have to view them.', 'health-check' ) . '
'; + } + + foreach ( $screenshots as $screenshot ) { + $controls = array( + 'view' => sprintf( + '%s', + esc_url( + add_query_arg( + array( + 'health-check-screenshot' => $screenshot->hash_id, + ), + site_url() + ) + ), + esc_html__( 'View screenshot', 'health-check' ) + ), + 'embed' => sprintf( + '', + esc_attr( + sprintf( + '', + add_query_arg( + array( + 'health-check-screenshot' => $screenshot->hash_id, + ), + site_url() + ), + esc_attr( $screenshot->post_title ) + ) + ), + esc_html__( 'Copy forum markup', 'health-check' ), + esc_html__( 'Copied!', 'health-check' ) + ), + 'delete' => sprintf( + '%s', + esc_url( + wp_nonce_url( + add_query_arg( + array( + 'tab' => 'screenshots', + 'health-check-delete-screenshot' => $screenshot->ID, + ), + admin_url( 'site-health.php' ) + ), + 'health-check-delete-screenshot' + ) + ), + esc_html__( 'Delete screenshot', 'health-check' ) + ), + ); + + printf( + '