settings = $settings; } /** * Registers hooks. * * @return void */ public function register(): void { add_action( 'admin_post_' . self::NONCE_RESPONSE, array( $this, 'handle_response' ) ); add_action( 'admin_notices', array( $this, 'render' ) ); } /** * Prints the notice, if this administrator is eligible to see it. * * @return void */ public function render(): void { if ( ! current_user_can( self::CAPABILITY ) || ! $this->is_eligible() ) { return; } $screen = get_current_screen(); if ( ! $screen instanceof \WP_Screen || ! $this->screen_allowed( $screen->id ) ) { return; } printf( '

%1$s %2$s

%3$s %4$s %5$s

', esc_html__( 'Is AI Blackout doing its job?', 'ai-blackout' ), esc_html__( 'If it is working for you, a review helps another site owner find it before their content gets scraped.', 'ai-blackout' ), sprintf( '%2$s', esc_url( $this->get_response_url( 'yes' ) ), esc_html__( 'Sure, happy to!', 'ai-blackout' ) ), sprintf( '%2$s', esc_url( $this->get_response_url( 'later' ) ), esc_html__( 'Maybe later', 'ai-blackout' ) ), sprintf( '%2$s', esc_url( $this->get_response_url( 'problem' ) ), esc_html__( "Something's not working", 'ai-blackout' ) ) ); } /** * Handles an answer to the notice. * * @return void */ public function handle_response(): void { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to change these settings.', 'ai-blackout' ), '', array( 'response' => 403 ) ); } check_admin_referer( self::NONCE_RESPONSE ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- check_admin_referer() ran above; the value is validated against a fixed list below. $response = isset( $_GET['response'] ) ? sanitize_key( wp_unslash( $_GET['response'] ) ) : ''; if ( ! in_array( $response, array( 'yes', 'later', 'problem' ), true ) ) { wp_safe_redirect( admin_url() ); exit; } $user_id = get_current_user_id(); if ( 'later' === $response ) { update_user_meta( $user_id, self::META_STATE, (string) ( time() + self::SNOOZE_FOR ) ); $referer = wp_get_referer(); wp_safe_redirect( $referer ? $referer : admin_url() ); exit; } // 'yes' and 'problem' both end the ask for good. update_user_meta( $user_id, self::META_STATE, 'never' ); wp_redirect( 'yes' === $response ? self::REVIEW_URL : self::SUPPORT_URL ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- destination is one of two hardcoded constants above, not request input. exit; } /** * Whether the current administrator is eligible to see the notice. * * @return bool True when it should be shown. */ private function is_eligible(): bool { if ( ! $this->settings->get( 'onboarding_complete', false ) ) { return false; } $state = get_user_meta( get_current_user_id(), self::META_STATE, true ); if ( 'never' === $state ) { return false; } if ( is_numeric( $state ) && (int) $state > time() ) { return false; } return ( time() - $this->get_first_eligible_at() ) >= self::ELIGIBLE_AFTER; } /** * Returns when the site first became eligible, seeding it on first use. * * @return int Unix timestamp. */ private function get_first_eligible_at(): int { $first_eligible = (int) $this->settings->get( self::SETTING_FIRST_ELIGIBLE, 0 ); if ( $first_eligible <= 0 ) { $first_eligible = time(); $this->settings->set( self::SETTING_FIRST_ELIGIBLE, $first_eligible ); } return $first_eligible; } /** * Whether a screen ID is allowed to carry the notice. * * @param string $screen_id Screen ID from get_current_screen(). * @return bool True when allowed. */ private function screen_allowed( string $screen_id ): bool { return in_array( $screen_id, self::GENERIC_SCREENS, true ) || MainScreen::is_own_screen(); } /** * Returns the nonced URL that answers the notice. * * @param string $response One of 'yes', 'later', 'problem'. * @return string Admin URL. */ private function get_response_url( string $response ): string { return wp_nonce_url( add_query_arg( array( 'action' => self::NONCE_RESPONSE, 'response' => $response, ), admin_url( 'admin-post.php' ) ), self::NONCE_RESPONSE ); } }