%s', esc_url( $wizard_url ), esc_html__( 'Setup wizard', 'aeomatic' ) ), sprintf( '%s', esc_url( $settings_url ), esc_html__( 'Settings', 'aeomatic' ) ) ); return $links; } public function enqueue_assets( string $hook ): void { $is_aeob = false !== strpos( $hook, 'aeomatic' ) || false !== strpos( $hook, 'aeo_booster' ); if ( ! $is_aeob ) { return; } wp_enqueue_style( 'aeob-admin', AEOMATIC_URL . 'assets/css/admin.css', array(), AEOMATIC_VERSION ); wp_enqueue_script( 'aeob-admin', AEOMATIC_URL . 'assets/js/admin.js', array( 'wp-api-fetch', 'wp-i18n' ), AEOMATIC_VERSION, true ); wp_localize_script( 'aeob-admin', 'aeomaticAdmin', array( 'restUrl' => esc_url_raw( rest_url( 'aeomatic/v1/' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), ) ); if ( false !== strpos( $hook, self::SLUG_ACCESS ) ) { wp_enqueue_script( 'aeob-ai-access', AEOMATIC_URL . 'assets/js/ai-access.js', array( 'wp-api-fetch', 'wp-i18n' ), AEOMATIC_VERSION, true ); wp_set_script_translations( 'aeob-ai-access', 'aeomatic' ); } if ( false !== strpos( $hook, self::SLUG_ACTIVITY ) || false !== strpos( $hook, str_replace( '-', '_', self::SLUG_ACTIVITY ) ) ) { wp_enqueue_script( 'aeob-verify', AEOMATIC_URL . 'assets/js/verify.js', array( 'wp-api-fetch', 'wp-i18n' ), AEOMATIC_VERSION, true ); } } public function maybe_save(): void { if ( empty( $_POST['aeomatic_save'] ) ) { return; } if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Insufficient permissions.', 'aeomatic' ) ); } check_admin_referer( self::NONCE_ACTION ); // Sanitisation of individual fields is handled recursively inside AEOMATIC_Settings::save(). // The raw array must be passed unslashed so nested sanitisation functions work correctly. $input = isset( $_POST['aeob'] ) ? wp_unslash( $_POST['aeob'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash if ( ! is_array( $input ) ) { $input = array(); } AEOMATIC_Settings::save( $input ); flush_rewrite_rules(); AEOMATIC_Activity::log( 'settings_saved', __( 'Settings updated.', 'aeomatic' ) ); /** * Fires after core settings are saved. * * @param array $input Unslashed, unsanitised form input. */ do_action( 'aeomatic_settings_saved', $input ); add_settings_error( 'aeomatic_messages', 'aeomatic_saved', __( 'Settings saved.', 'aeomatic' ), 'updated' ); } public function render_dashboard(): void { require AEOMATIC_DIR . 'admin/views/dashboard.php'; } public function render_access(): void { require AEOMATIC_DIR . 'admin/views/ai-access.php'; } public function render_activity(): void { require AEOMATIC_DIR . 'admin/views/activity.php'; } public function render_gaps(): void { require AEOMATIC_DIR . 'admin/views/gaps.php'; } public function render_readiness(): void { require AEOMATIC_DIR . 'admin/views/ai-readiness.php'; } public function render_entity_scanner(): void { require AEOMATIC_DIR . 'admin/views/entity-scanner.php'; } public function render_settings(): void { require AEOMATIC_DIR . 'admin/views/settings.php'; } /** * Dismiss the review notice permanently when the user clicks "Already did" or "×". */ public function maybe_dismiss_review(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! isset( $_GET['aeomatic_dismiss_review'] ) ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } update_option( 'aeomatic_review_dismissed', true, false ); // Redirect back cleanly without the query param. $redirect = remove_query_arg( 'aeomatic_dismiss_review' ); wp_safe_redirect( $redirect ); exit; } /** * Snooze the review notice for 14 days when the user clicks "Remind me later". */ public function maybe_snooze_review(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! isset( $_GET['aeomatic_snooze_review'] ) ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } update_option( 'aeomatic_review_snoozed_until', time() + ( 14 * DAY_IN_SECONDS ), false ); $redirect = remove_query_arg( 'aeomatic_snooze_review' ); wp_safe_redirect( $redirect ); exit; } /** * Show a review request notice once the user has had time and engagement with * the plugin: * - 7 days since install, AND * - at least 5 schema renders (proves the plugin is actually doing work). * * The notice can be snoozed for 14 days ("Remind me later") or dismissed * permanently ("Already did" / "Leave a review" / "×"). Snooze gives genuinely * interested users a second chance instead of forcing a hard yes/no. */ public function maybe_show_review_notice(): void { // Already dismissed permanently. if ( get_option( 'aeomatic_review_dismissed' ) ) { return; } // Snoozed — not yet time to re-show. $snoozed_until = (int) get_option( 'aeomatic_review_snoozed_until', 0 ); if ( $snoozed_until && $snoozed_until > time() ) { return; } // Only admins see this. if ( ! current_user_can( 'manage_options' ) ) { return; } // Time gate: 7 days after install. $installed_at = (int) AEOMATIC_Settings::get( '_meta', 'installed_at' ); if ( ! $installed_at || ( time() - $installed_at ) < 7 * DAY_IN_SECONDS ) { return; } // Engagement gate: must have rendered schema at least 5 times. Avoids // nagging users who installed but haven't actually benefited yet. if ( class_exists( 'AEOMATIC_Activity' ) ) { $stats = AEOMATIC_Activity::get_stats(); $renders = (int) ( $stats['schema_renders'] ?? 0 ); if ( $renders < 5 ) { return; } } $review_url = 'https://wordpress.org/support/plugin/aeomatic/reviews/#new-post'; $dismiss_url = add_query_arg( 'aeomatic_dismiss_review', '1' ); $snooze_url = add_query_arg( 'aeomatic_snooze_review', '1' ); // Background-dismiss when the user clicks "Leave a review" — the link // opens wp.org in a new tab via target="_blank"; this fetch silently // records the dismiss without navigating the current admin page away. $review_onclick = sprintf( "fetch(%s,{credentials:'same-origin'}).catch(function(){});return true;", wp_json_encode( esc_url_raw( $dismiss_url ) ) ); ?>