` to slot straight into an ability's `execute()` * return type. * * @since 1.3.0 * * @param array $data The ability's result payload. * @param array $args The ability's input arguments (carries the * optional `reveal_personal_data` opt-in). * @param array $opts Anonymiser options forwarded to * {@see Anonymizer::anonymize()} — e.g. * `mask_context_names` for pure person records. * * @return array The redacted (or payment-stripped) payload. */ public static function redact( array $data, array $args = [], array $opts = [] ): array { $mode = PrivacyMode::resolve(); // No anonymisation, but payment/card data is never allowed through. if ( $mode === PrivacyMode::Off ) { $result = Anonymizer::strip_payment_data( $data ); } elseif ( self::allows_reveal( $args, $mode, $opts ) ) { // Deliberate, authorised reveal — still strip payment/card data. $result = Anonymizer::strip_payment_data( $data ); } else { $result = Anonymizer::anonymize( $data, $opts ); } /** * The anonymiser preserves the string-keyed top level it was given, so * the payload can be narrowed back to the ability return shape. * * @var array $result */ return $result; } /** * Whether raw personal data may be revealed for this call. * * @since 1.3.0 * * @param array $args The tool-call arguments. * @param PrivacyMode $mode The resolved privacy mode. * @param array $opts Redaction options; `reveal_capability` sets the * gating capability for this call (typically the * ability's own `check_permission()` capability). * * @return bool */ public static function allows_reveal( array $args, PrivacyMode $mode, array $opts = [] ): bool { if ( $mode !== PrivacyMode::Balanced ) { return false; } // Schema defaults are not injected into $args, so a missing key is false. if ( empty( $args['reveal_personal_data'] ) ) { return false; } return current_user_can( self::resolve_reveal_capability( $opts ) ); } /** * Resolve the capability that gates revealing personal data. * * Precedence: an explicit `$opts['reveal_capability']` (an ability passes its * own `check_permission()` capability, so "if you may call it, you may reveal * it"), otherwise the `albert/privacy/reveal_capability` filter, defaulting to * `manage_options` — a capability that exists on every site, unlike * `manage_woocommerce`. * * @since 1.3.0 * * @param array $opts Redaction options. * * @return string The gating capability. */ private static function resolve_reveal_capability( array $opts ): string { if ( isset( $opts['reveal_capability'] ) && is_string( $opts['reveal_capability'] ) && $opts['reveal_capability'] !== '' ) { return $opts['reveal_capability']; } /** * Filters the capability required to reveal personal data. * * Consulted only when a caller has not passed an explicit * `reveal_capability` option. * * @since 1.3.0 * * @param string $capability The gating capability (default `manage_options`). */ $capability = apply_filters( 'albert/privacy/reveal_capability', 'manage_options' ); return ( is_string( $capability ) && $capability !== '' ) ? $capability : 'manage_options'; } }