post_password ) { return false; } if ( 'publish' === $post->post_status ) { return true; } return 'private' === $post->post_status && $this->private_included(); } /** * Whether the site has asked for its private posts to be sent. * * Off until somebody says otherwise. Turning it on changes what leaves the * site, and a plugin update is not consent. */ public function private_included(): bool { return (bool) $this->connection->get( 'sync_private', false ); } /** * The customer role ids a post should be tagged with. * * Empty means "nobody" — which is not the same as "everybody", and callers * must treat it as a reason not to send rather than a reason to send * untagged. An untagged item on an RBAC workspace is invisible to every * visitor, so an empty list quietly sent is content nobody can find. * * @param \WP_Post $post The post. * * @return string[] */ public function roles_for( \WP_Post $post ): array { if ( ! $this->is_expressible( $post ) ) { return array(); } if ( 'publish' === $post->post_status ) { return array( Roles::ANONYMOUS ); } $type = get_post_type_object( $post->post_type ); $cap = (string) ( $type->cap->read_private_posts ?? 'read_private_posts' ); $roles = self::roles_with_cap( $cap ); /** * Filters the roles a post is tagged with. * * For a membership plugin whose access is not a WordPress capability: * return the customer role ids that may read this post. Returning an * empty array stops it being sent. * * @param string[] $roles Customer role ids. * @param \WP_Post $post The post. */ return array_values( array_unique( (array) apply_filters( 'sharelyai_post_roles', $roles, $post ) ) ); } /** * Every role holding a capability, as customer role ids. * * Roles in WordPress are flat — a role's capability list is the whole of * it — so this is a read of `wp_roles()` and not a permission check. * * @param string $cap The capability. * * @return string[] */ public static function roles_with_cap( string $cap ): array { $out = array(); foreach ( (array) wp_roles()->roles as $slug => $role ) { if ( 1 !== preg_match( self::SLUG, (string) $slug ) ) { // A slug the platform would refuse was never mirrored, so // tagging with it would fail every token that named it. continue; } if ( ! empty( $role['capabilities'][ $cap ] ) ) { $out[] = Roles::PREFIX . $slug; } } return $out; } /** * Whether the visitor making this request may read a post. * * The last check before a result is rendered. The platform has already * filtered by role, but it filtered against the roles on an item, which * were right at the last sync — this is WordPress answering for itself, * now. * * @param int $post_id The post. */ public static function visitor_can_read( int $post_id ): bool { $post = get_post( $post_id ); if ( ! $post instanceof \WP_Post ) { return false; } if ( '' !== (string) $post->post_password ) { return false; } if ( 'publish' === $post->post_status ) { return true; } return current_user_can( 'read_post', $post_id ); } }