*/ private const PAGE_TOKEN_MAP = array( self::PAGE_TOKEN_DASHBOARD => self::CAP_VIEW_PRODUCTS, self::PAGE_TOKEN_PRODUCTS_LIST => self::CAP_VIEW_PRODUCTS, self::PAGE_TOKEN_PRODUCTS_NEW => self::CAP_VIEW_PRODUCTS, self::PAGE_TOKEN_SETTINGS => self::CAP_FALLBACK_MANAGE, self::PAGE_TOKEN_DIAGNOSTICS => self::CAP_VIEW_DIAGNOSTICS, self::PAGE_TOKEN_LOGS => self::CAP_VIEW_LOGS, self::PAGE_TOKEN_SYNC_JOBS => self::CAP_RUN_SYNC, self::PAGE_TOKEN_MIGRATION => self::CAP_VIEW_DIAGNOSTICS, self::PAGE_TOKEN_CLICKS => self::CAP_VIEW_DIAGNOSTICS, self::PAGE_TOKEN_AUDIT => self::CAP_VIEW_DIAGNOSTICS, ); /** * §3.3 OR-list extension: per-page-token map of secondary * (fallback) capabilities accepted alongside the primary * capability returned by {@see self::requiredCapability()}. * * Mirrors the REST {@see \Affilun\Presentation\Rest\Permission\CapabilityPolicy} * `[primary, fallback]` shape so a site that has not yet * granted the dedicated `affilun_*` capability still authorizes * `manage_options` administrators by default. Pages whose * primary capability already IS the fallback (`manage_options` * Settings, `edit_posts` Products) need no entry here. * * @var array> */ private const FALLBACK_MAP = array( self::PAGE_TOKEN_DIAGNOSTICS => array( self::CAP_FALLBACK_MANAGE ), self::PAGE_TOKEN_LOGS => array( self::CAP_FALLBACK_MANAGE ), self::PAGE_TOKEN_SYNC_JOBS => array( self::CAP_FALLBACK_MANAGE ), self::PAGE_TOKEN_MIGRATION => array( self::CAP_FALLBACK_MANAGE ), self::PAGE_TOKEN_CLICKS => array( self::CAP_FALLBACK_MANAGE ), self::PAGE_TOKEN_AUDIT => array( self::CAP_FALLBACK_MANAGE ), ); /** * Return the required capability literal for the given page * token. * * Unknown page tokens deliberately deny by default to * {@see self::CAP_FALLBACK_MANAGE} rather than throw — admin * pages registered by future slices may legitimately * introduce new tokens before this class is updated, and a * deny-by-default response keeps the surface failing closed * in the meantime. * * @param string $pageToken Stable page token (case-sensitive). */ public static function requiredCapability( string $pageToken ): string { return self::PAGE_TOKEN_MAP[ $pageToken ] ?? self::CAP_FALLBACK_MANAGE; } /** * Return the OR-list of capability literals that authorize * access to the supplied page token (Phase 2 KI-008). * * Order: primary capability first (the one returned by * {@see self::requiredCapability()}), followed by zero or more * fallback capabilities the policy accepts. Mirrors the REST * {@see \Affilun\Presentation\Rest\Permission\CapabilityPolicy} * OR-list contract so a site that has not yet granted the * dedicated `affilun_*` capability still authorizes * `manage_options` administrators by default. * * Unknown page tokens deny by default to a single-element list * containing {@see self::CAP_FALLBACK_MANAGE}, matching * {@see self::requiredCapability()}'s deny-by-default policy. * * @param string $pageToken Stable page token (case-sensitive). * * @return list */ public static function requiredCapabilities( string $pageToken ): array { $primary = self::requiredCapability( $pageToken ); $fallback = self::FALLBACK_MAP[ $pageToken ] ?? array(); $ordered = array_merge( array( $primary ), $fallback ); // De-duplicate while preserving order so a primary that is // itself the fallback (Settings, Products) does not appear // twice in the returned list. $seen = array(); $result = array(); foreach ( $ordered as $cap ) { if ( array_key_exists( $cap, $seen ) ) { continue; } $seen[ $cap ] = true; $result[] = $cap; } return $result; } /** * Return the full closed set of page tokens this policy * currently recognizes, in declaration order. * * Used by tests to assert that every documented page token is * mapped, and by diagnostics surfaces that want to enumerate * the known admin pages. * * @return list */ public static function validPageTokens(): array { return array_values( array_keys( self::PAGE_TOKEN_MAP ) ); } /** * Report whether the supplied capability probe authorizes * access to the given page token. * * The probe closure carries the per-request capability check * (typically a thin wrapper around `current_user_can()`); its * signature is `function (string $cap): bool`. Returns true * when the probe returns true for the page token's required * capability literal. * * @param callable $capProbe Per-request capability probe. * @param string $pageToken Stable page token (case-sensitive). * * @phpstan-param callable(string): bool $capProbe */ public static function isAuthorized( callable $capProbe, string $pageToken ): bool { foreach ( self::requiredCapabilities( $pageToken ) as $cap ) { if ( (bool) $capProbe( $cap ) ) { return true; } } return false; } }