repository = $repository; $this->formatter = $formatter; } /** * Returns the capability required to view the report. * * @return string */ public static function capability(): string { /** * Filters the capability required to view the visibility report. * * @param string $capability Capability name. */ return (string) apply_filters( 'blocksieve_report_capability', 'manage_options' ); } /** * Registers the Tools submenu entry. * * @return void */ public function register_menu(): void { $hook = add_management_page( __( 'Block Visibility', 'blocksieve' ), 'BlockSieve', // Menu label: the plugin name, not translated. self::capability(), self::SLUG, array( $this, 'render' ) ); if ( $hook ) { add_action( 'load-' . $hook, array( $this, 'maybe_rescan' ) ); } } /** * Returns the admin URL of the report screen. * * @return string */ public static function url(): string { return add_query_arg( 'page', self::SLUG, admin_url( 'tools.php' ) ); } /** * Adds a "Settings" row action on the Plugins screen that opens the report. * * Hooked to `plugin_action_links_{basename}`. Omitted for users who cannot * view the report, so the link never leads to a permissions error. * * @param array $links Existing row action links. * @return array */ public function add_action_links( array $links ): array { if ( ! current_user_can( self::capability() ) ) { return $links; } $settings = sprintf( '%s', esc_url( self::url() ), esc_html__( 'Settings', 'blocksieve' ) ); return array_merge( array( 'settings' => $settings ), $links ); } /** * Renders the report screen. * * @return void */ public function render(): void { if ( ! current_user_can( self::capability() ) ) { wp_die( esc_html__( 'You are not allowed to view this report.', 'blocksieve' ) ); } $report = $this->repository->get(); $table = new ReportTable( $report, $this->formatter ); $table->prepare_items(); echo '
'; printf( '

%1$s

%3$s
', esc_html__( 'Block Visibility', 'blocksieve' ), esc_url( $this->rescan_url() ), esc_html__( 'Rescan now', 'blocksieve' ) ); $this->render_scan_status( $report ); printf( '

%s

', esc_html__( 'Every block on this site that is controlled by a visibility condition, and whether any of it is misconfigured.', 'blocksieve' ) ); $this->render_summary( $report ); $this->render_notices( $report ); echo '
'; printf( '', esc_attr( self::SLUG ) ); // Keep the active view when the search or filter form is submitted. if ( 'all' !== $table->current_view() ) { printf( '', esc_attr( $table->current_view() ) ); } $table->views(); $table->search_box( __( 'Search blocks', 'blocksieve' ), 'blocksieve-search' ); $table->display(); echo '
'; echo '
'; } /** * Handles a rescan request before any output, then redirects. * * Runs on the screen's `load-` hook. Redirecting drops the one-shot * `blocksieve-rescan` / `_wpnonce` arguments; otherwise every sort, pagination and * view link (all built from the current URL) would trigger a full rescan. * * @return void */ public function maybe_rescan(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The nonce is verified below, before acting. if ( ! isset( $_GET['blocksieve-rescan'] ) || ! current_user_can( self::capability() ) ) { return; } check_admin_referer( self::RESCAN_ACTION ); $this->repository->refresh(); wp_safe_redirect( remove_query_arg( array( 'blocksieve-rescan', '_wpnonce' ) ) ); exit; } /** * Renders the summary cards. * * @param UsageReport $report Report. * @return void */ private function render_summary( UsageReport $report ): void { $cards = array( __( 'Controlled blocks', 'blocksieve' ) => number_format_i18n( $report->total_blocks() ), __( 'Locations', 'blocksieve' ) => number_format_i18n( $report->total_locations() ), __( 'Active', 'blocksieve' ) => number_format_i18n( $report->total_active() ), __( 'Needs attention', 'blocksieve' ) => number_format_i18n( $report->total_warnings() ), ); echo '
'; foreach ( $cards as $label => $value ) { printf( '
%1$s%2$s
', esc_html( $value ), esc_html( $label ) ); } echo '
'; } /** * Renders warnings about the scan itself. * * @param UsageReport $report Report. * @return void */ private function render_notices( UsageReport $report ): void { if ( ! $report->truncated ) { return; } printf( '

%s

', esc_html__( 'This site has more content than a single scan inspects, so the report is incomplete. Raise the limit with the blocksieve_scan_limit filter.', 'blocksieve' ) ); } /** * Builds the nonce-protected rescan URL. * * @return string */ private function rescan_url(): string { return wp_nonce_url( add_query_arg( array( 'page' => self::SLUG, 'blocksieve-rescan' => '1', ), admin_url( 'tools.php' ) ), self::RESCAN_ACTION ); } /** * Renders when the report was last scanned, under the Rescan button. * * @param UsageReport $report Report. * @return void */ private function render_scan_status( UsageReport $report ): void { if ( $report->generated <= 0 ) { return; } echo '

'; printf( esc_html( /* translators: 1: human-readable time difference, 2: number of items scanned. */ _n( 'Last scanned %1$s ago across %2$s item.', 'Last scanned %1$s ago across %2$s items.', $report->scanned, 'blocksieve' ) ), esc_html( human_time_diff( $report->generated ) ), esc_html( number_format_i18n( $report->scanned ) ) ); echo '

'; } }