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(
'
',
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
);
}
}