prefix . 'yoast_indexable'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared return (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ); } /** * Assert the caller confirmed a destructive operation. * * @since 0.0.34 * @param array $input Ability input. * @param string $message Optional operation-specific message. * @return true|WP_Error */ public static function assert_confirmed( array $input, string $message = '' ) { if ( empty( $input['confirm'] ) ) { return new WP_Error( 'confirmation_required', '' !== $message ? $message : __( 'This operation cannot be undone. Pass confirm: true to proceed.', 'acrossai-abilities-manager' ) ); } return true; } /** * Build the permission_callback for one ability. * * A SINGLE `current_user_can( $floor )`, never OR-ed with a default, and the filter runs only * after the floor is cleared — so it can tighten access and never widen it * (PATTERN-FILTERABLE-CAPABILITY-RAISE-ONLY). * * Yoast defines `wpseo_manage_options`, but it maps to `manage_options` by default and is * `add_cap`-editable, so the floor stands alone rather than composing with something a site owner * may have widened. * * @since 0.0.34 * @param string $floor WordPress capability floor. * @return callable */ public static function can( string $floor = 'manage_options' ): callable { return static function () use ( $floor ): bool { $allowed = current_user_can( $floor ); if ( ! $allowed ) { return false; } /** * Filters whether the current user may use a Yoast SEO ability. * * Consulted only for a user who already clears the floor, so it can deny but never * grant. Returning the filter's value directly would let any plugin on the site hand an * SEO write to a subscriber, which is the opposite of what the filter is for. * * @since 0.0.34 * @param bool $allowed Whether access is granted. Always true at this point. * @param string $floor WordPress capability floor. */ return (bool) apply_filters( self::PERMISSION_FILTER, $allowed, $floor ); }; } /** * Build a success envelope. * * @since 0.0.34 * @param array $payload Ability-specific keys. * @param string $message Human-readable summary. * @return array */ public static function ok( array $payload, string $message ): array { unset( $payload['success'], $payload['message'], $payload['error_code'] ); return array_merge( array( 'success' => true ), $payload, array( 'message' => $message ) ); } /** * Build a failure envelope from a WP_Error. * * @since 0.0.34 * @param WP_Error $error Error to unwrap. * @return array */ public static function fail( WP_Error $error ): array { return self::error( (string) $error->get_error_code(), (string) $error->get_error_message() ); } /** * Build a failure envelope from an explicit code and message. * * @since 0.0.34 * @param string $code Machine-readable error code. * @param string $message Human-readable message. * @return array */ public static function error( string $code, string $message ): array { return array( 'success' => false, 'error_code' => $code, 'message' => $message, ); } }