wcm-abilities-api
WooCommerce Memberships 1.29+ WordPress Abilities API
Install
npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/woocommerce/wcm-abilities-api
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
git clone https://github.com/Lonsdale201/wp-agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
WooCommerce Memberships: Abilities API
Use this when building or reviewing privileged automation around Memberships plans, user memberships, and per-post content restriction rules through the WordPress Abilities API.
Misconception this skill corrects
"Memberships abilities are customer-facing REST endpoints for headless member dashboards."
They are privileged Abilities API operations. Plan and user-membership abilities check manage_woocommerce. The 1.29.0 post-restriction abilities check manage_woocommerce_membership_plans; the GET ability also reaches the trait's numeric edit_post check because its input is the post ID. The UPDATE ability input is an object/array, so add your own edit_post guard when wrapping it. Use these abilities for admin/editor/agent automation, not untrusted frontend flows.
When to use this skill
Trigger when ANY of the following is true:
- The task mentions WordPress Abilities API,
wp_register_ability(),wp_get_ability(),wp_abilities_api_init, or agent automation for Memberships. - Code contains ability names beginning with
woocommerce-memberships/. - Code needs to create/list/get/delete Memberships plans or user memberships through an ability layer instead of direct PHP APIs.
- Code reads or writes
/wc-memberships/v1/post-restriction-rules/{id}or uses the block editor Memberships sidebar restriction entity. - You are deciding whether to use Memberships REST API, PHP APIs, or Abilities API.
Registration facts
Memberships 1.28.0+ implements the SkyVerge framework HasAbilitiesContract in WC_Memberships. The framework initializes ability registration only when WordPress exposes both:
function_exists( 'wp_register_ability' )
function_exists( 'wp_register_ability_category' )
On supported WordPress versions, the framework hooks:
| Hook | Purpose |
|---|---|
wp_abilities_api_categories_init |
Registers Memberships ability categories. |
wp_abilities_api_init |
Registers the abilities. |
rest_api_init |
Registers framework REST routes only for abilities with explicit RestConfig. In 1.29.0 this applies to the post-restriction rule GET/PUT abilities. |
Do not assume these abilities exist on older WordPress installs. In WP 7.0+ contexts, they should be available if Memberships is loaded and no site-level code disables the Abilities API.
Categories
| Category slug | Meaning |
|---|---|
woocommerce-membership-plans |
Abilities related to WC_Memberships_Membership_Plan. |
woocommerce-user-memberships |
Abilities related to WC_Memberships_User_Membership. |
woocommerce-memberships-posts |
Abilities related to per-post membership restriction configuration. |
Ability map
| Ability | Class | Permission | Annotation | Input |
|---|---|---|---|---|
woocommerce-memberships/plans-create |
CreatePlan |
manage_woocommerce |
write, non-destructive, non-idempotent | Plan object data. |
woocommerce-memberships/plans-delete |
DeletePlan |
manage_woocommerce |
destructive | Integer plan ID. |
woocommerce-memberships/plans-get |
GetPlan |
manage_woocommerce |
readonly, idempotent | Integer plan ID. |
woocommerce-memberships/plans-list |
ListPlans |
manage_woocommerce |
readonly, idempotent | WP_Query-like args for plans. |
woocommerce-memberships/user-memberships-create |
CreateUserMembership |
manage_woocommerce |
write, non-destructive, non-idempotent | plan_id, user_id, optional product_id, order_id. |
woocommerce-memberships/user-memberships-delete |
DeleteUserMembership |
manage_woocommerce |
destructive | Integer user membership ID. |
woocommerce-memberships/user-memberships-get |
GetUserMembership |
manage_woocommerce |
readonly, idempotent | Integer user membership ID. |
woocommerce-memberships/user-memberships-list |
ListUserMemberships |
manage_woocommerce |
readonly, idempotent | user_id, optional status. |
woocommerce-memberships/post-restriction-rules-get |
GetPostRestrictionRules |
manage_woocommerce_membership_plans plus edit_post for direct integer input |
readonly, idempotent | Integer post ID. |
woocommerce-memberships/post-restriction-rules-update |
UpdatePostRestrictionRules |
manage_woocommerce_membership_plans in source; add edit_post in wrappers |
write, non-destructive, idempotent | Object with id and replacement rules. |
Output schemas use the plugin object JSON schemas:
WC_Memberships_Membership_Plan::getJsonSchema()WC_Memberships_User_Membership::getJsonSchema()PostRestrictionRulesSerializer::getJsonSchema()
Plan creation input
plans-create delegates to wc_memberships()->get_plans_instance()->createPlan( $data ).
Important input groups:
| Input | Notes |
|---|---|
name |
Required by schema. |
slug |
Optional plan slug. |
status |
draft or publish. |
description |
Optional description. |
access.method |
manual-only, signup, or purchase. |
access.product_ids |
Required by business rules when method is purchase. |
membership_length.type |
unlimited, specific, or fixed. |
membership_length.amount / period |
Required by business rules for specific. |
membership_length.start_date / end_date |
Required by business rules for fixed. |
rules.content_restriction |
Plan content restriction rules. |
rules.product_restriction |
Product view/purchase restriction rules. |
rules.purchasing_discount |
Member discount rules. |
Do not write the wc_memberships_rules option directly when an ability or plan API can create the plan and rules together.
User membership creation input
user-memberships-create delegates to Memberships's user membership manager:
wc_memberships()->get_user_memberships_instance()->create_user_membership( $data );
Schema fields:
| Input | Notes |
|---|---|
plan_id |
Required membership plan ID. |
user_id |
Required WP user ID. |
product_id |
Optional product that granted access. |
order_id |
Optional order that granted access. |
For purchase-based access, prefer passing meaningful product_id and order_id when the membership is truly tied to a purchase. Do not fake order/product relations just to satisfy reporting.
Post restriction rule abilities
Memberships 1.29.0 added Abilities API operations for the block-editor Memberships sidebar. They are configuration APIs for restrictable posts, not runtime access checks.
| Need | Ability | REST route |
|---|---|---|
| Read rules applying to a post | woocommerce-memberships/post-restriction-rules-get |
GET /wc-memberships/v1/post-restriction-rules/{id} |
| Replace post-specific rules | woocommerce-memberships/post-restriction-rules-update |
PUT /wc-memberships/v1/post-restriction-rules/{id} |
post-restriction-rules-get returns:
array(
'id' => 123,
'rules' => array(
array(
'id' => 'rule-id',
'membership_plan_id' => 456,
'access_schedule' => array( 'type' => 'immediate' ),
'editable' => true,
),
),
);
The response includes both post-specific rules and inherited rules from post-type/taxonomy level configuration. The editable flag is the safety boundary:
editable === true: rule targets this post directly and can be sent to the update ability.editable === false: inherited rule; render read-only and edit it on the membership plan/source rule, not from the post payload.
post-restriction-rules-update treats rules as the full desired state for post-specific content restriction rules:
- Existing direct rules omitted from the payload are deleted.
- Rows with a known direct rule
idare updated. - Rows without
idare added. rules: array()clears all direct post-specific rules.- Inherited rules are not affected, and sending an inherited rule ID causes a
422 invalid_inputbecause the ID does not belong to this post.
Safe PHP execution shape:
$get = wp_get_ability( 'woocommerce-memberships/post-restriction-rules-get' );
$current = $get ? $get->execute( $post_id ) : null;
if ( is_wp_error( $current ) || ! is_array( $current ) ) {
return $current;
}
$editable_rules = array_values( array_filter(
$current['rules'],
static fn( array $rule ): bool => ! empty( $rule['editable'] )
) );
$editable_rules[] = array(
'membership_plan_id' => $plan_id,
'access_schedule' => array( 'type' => 'delayed', 'amount' => 7, 'period' => 'days' ),
);
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return new WP_Error( 'forbidden', 'Cannot edit this post.', array( 'status' => 403 ) );
}
$update = wp_get_ability( 'woocommerce-memberships/post-restriction-rules-update' );
$result = $update ? $update->execute( array(
'id' => $post_id,
'rules' => $editable_rules,
) ) : new WP_Error( 'missing_ability' );
The update rule schema accepts membership_plan_id and optional access_schedule. Delayed schedules use type = delayed, positive amount, and period in days, weeks, months, or years; immediate schedules use array( 'type' => 'immediate' ).
The block editor sidebar also registers REST-exposed post meta for _wc_memberships_force_public and the per-post custom restriction message keys. Those meta writes are separate from rule replacement; use the abilities above for rule rows and normal post meta/REST for the sidebar's force-public/message settings.
Safe execution pattern
$ability = function_exists( 'wp_get_ability' )
? wp_get_ability( 'woocommerce-memberships/user-memberships-get' )
: null;
if ( ! $ability || ! current_user_can( 'manage_woocommerce' ) ) {
return new WP_Error( 'forbidden', 'Membership ability is unavailable.', array( 'status' => 403 ) );
}
$result = $ability->execute( 123 );
if ( is_wp_error( $result ) ) {
return $result;
}
Let the ability permission callback run; the explicit current_user_can() guard is useful when your code is about to choose between an admin path and a frontend-safe path.
Choosing the right surface
| Need | Prefer |
|---|---|
| Admin/agent automation on WP 7.0+ | Abilities API. |
| External integration over HTTP with Woo auth | Memberships REST API. |
| In-process plugin business logic | Public PHP APIs and objects. |
| Customer frontend/headless "my memberships" | Custom endpoint that checks ownership and uses Memberships access APIs. |
| Public member directory | /wc/v4/memberships/members/directory, with page/block validation and privacy-limited fields. |
| Block-editor per-post restriction UI | post-restriction-rules-get/update abilities or their /wc-memberships/v1/post-restriction-rules/{id} routes. |
Do not use manage_woocommerce abilities for a customer-facing dashboard. A customer should not be able to list arbitrary users' memberships or delete plans.
Security guardrails
- Never proxy ability execution from a public REST route without a capability check.
- Do not pass arbitrary frontend-controlled WP_Query args into
plans-list; even though the ability is admin-gated, sanitize UI inputs before execution. - Treat delete abilities as destructive and require an explicit admin confirmation in UI.
- Do not down-scope permission by filtering current user capabilities. Build a narrower custom endpoint/service when customers need self-service membership data.
- Do not assume every ability has a REST route. Plan and user-membership abilities pass
showInRest = truefor Abilities API metadata but do not provide the SkyVerge frameworkRestConfig; the post restriction rule abilities do. - For post rule updates, never round-trip inherited rows from GET into PUT. Filter to
editable === trueand intentionally rebuild the direct post-specific rule set. - The framework's route permission callback is invoked without request input before execution.
WP_Ability::execute()passes validated input to the permission callback, but the update input is an array, not a numeric ID, so the shared trait'sedit_postbranch does not fire there in 1.29.0. Add an explicitcurrent_user_can( 'edit_post', $post_id )check before custom update wrappers.
Common mistakes
// WRONG: exposing a privileged ability to any logged-in user.
register_rest_route( 'my/v1', '/membership', array(
'methods' => 'POST',
'permission_callback' => 'is_user_logged_in',
'callback' => function ( WP_REST_Request $request ) {
return wp_get_ability( 'woocommerce-memberships/user-memberships-delete' )->execute( (int) $request['id'] );
},
) );
// RIGHT: use capability checks for privileged automation.
register_rest_route( 'my/v1', '/admin/membership', array(
'methods' => 'POST',
'permission_callback' => static fn() => current_user_can( 'manage_woocommerce' ),
'callback' => function ( WP_REST_Request $request ) {
$ability = wp_get_ability( 'woocommerce-memberships/user-memberships-get' );
return $ability ? $ability->execute( (int) $request['id'] ) : new WP_Error( 'missing_ability' );
},
) );
Cross-references
- Use
wcm-membership-hooksfor lifecycle hooks, REST/webhooks, profile fields, member directory, CSV, and Subscriptions-linked memberships. - Use
wcm-data-model-subscriptions-linkfor CPT names, meta keys, rule storage, and Subscriptions relation storage. - Use
wcm-access-discountsfor access checks, restriction/drip behavior, and member discount APIs.
References
- Verified source paths:
wp-content/plugins/woocommerce-memberships/class-wc-memberships.phpwp-content/plugins/woocommerce-memberships/src/Abilities/Provider.phpwp-content/plugins/woocommerce-memberships/src/Plans/Abilities/wp-content/plugins/woocommerce-memberships/src/UserMemberships/Abilities/wp-content/plugins/woocommerce-memberships/src/Posts/Abilities/wp-content/plugins/woocommerce-memberships/src/Posts/Actions/SetPostRules.phpwp-content/plugins/woocommerce-memberships/src/Posts/Adapters/JsonSerializers/PostRestrictionRulesSerializer.phpwp-content/plugins/woocommerce-memberships/src/Posts/Traits/CanCheckRestrictablePostPermissionTrait.phpwp-content/plugins/woocommerce-memberships/src/Blocks/BlockEditorSidebar.phpwp-content/plugins/woocommerce-memberships/vendor/skyverge/wc-plugin-framework/woocommerce/Abilities/
Files (wp-agent-skills)
-
SKILL.md 15.2 KB
--- name: wcm-abilities-api description: WooCommerce Memberships 1.29+ WordPress Abilities API reference for membership plan, user membership, and per-post content restriction rule abilities, category slugs, registration requirements, permissions, schemas, annotations, REST route exposure, and safe automation guardrails. Use when code or a task mentions wp_register_ability, wp_get_ability, WP Abilities API, woocommerce-memberships/plans-create, plans-delete, plans-get, plans-list, user-memberships-create, user-memberships-delete, user-memberships-get, user-memberships-list, post-restriction-rules-get, post-restriction-rules-update, /wc-memberships/v1/post-restriction-rules, or privileged agent/headless/admin automation for WooCommerce Memberships. metadata: wp-skills-author: "Soczó Kristóf" wp-skills-contact: "mailto:lonsdale201@hotmail.com" wp-skills-plugin: "woocommerce-memberships" wp-skills-plugin-version-tested: "1.29.0" wp-skills-php-min: "7.4" wp-skills-last-updated: "2026-07-06" --- # WooCommerce Memberships: Abilities API Use this when building or reviewing privileged automation around Memberships plans, user memberships, and per-post content restriction rules through the WordPress Abilities API. ## Misconception this skill corrects > "Memberships abilities are customer-facing REST endpoints for headless member dashboards." They are privileged Abilities API operations. Plan and user-membership abilities check `manage_woocommerce`. The 1.29.0 post-restriction abilities check `manage_woocommerce_membership_plans`; the GET ability also reaches the trait's numeric `edit_post` check because its input is the post ID. The UPDATE ability input is an object/array, so add your own `edit_post` guard when wrapping it. Use these abilities for admin/editor/agent automation, not untrusted frontend flows. ## When to use this skill Trigger when ANY of the following is true: - The task mentions WordPress Abilities API, `wp_register_ability()`, `wp_get_ability()`, `wp_abilities_api_init`, or agent automation for Memberships. - Code contains ability names beginning with `woocommerce-memberships/`. - Code needs to create/list/get/delete Memberships plans or user memberships through an ability layer instead of direct PHP APIs. - Code reads or writes `/wc-memberships/v1/post-restriction-rules/{id}` or uses the block editor Memberships sidebar restriction entity. - You are deciding whether to use Memberships REST API, PHP APIs, or Abilities API. ## Registration facts Memberships 1.28.0+ implements the SkyVerge framework `HasAbilitiesContract` in `WC_Memberships`. The framework initializes ability registration only when WordPress exposes both: ```php function_exists( 'wp_register_ability' ) function_exists( 'wp_register_ability_category' ) ``` On supported WordPress versions, the framework hooks: | Hook | Purpose | |---|---| | `wp_abilities_api_categories_init` | Registers Memberships ability categories. | | `wp_abilities_api_init` | Registers the abilities. | | `rest_api_init` | Registers framework REST routes only for abilities with explicit `RestConfig`. In 1.29.0 this applies to the post-restriction rule GET/PUT abilities. | Do not assume these abilities exist on older WordPress installs. In WP 7.0+ contexts, they should be available if Memberships is loaded and no site-level code disables the Abilities API. ## Categories | Category slug | Meaning | |---|---| | `woocommerce-membership-plans` | Abilities related to `WC_Memberships_Membership_Plan`. | | `woocommerce-user-memberships` | Abilities related to `WC_Memberships_User_Membership`. | | `woocommerce-memberships-posts` | Abilities related to per-post membership restriction configuration. | ## Ability map | Ability | Class | Permission | Annotation | Input | |---|---|---|---|---| | `woocommerce-memberships/plans-create` | `CreatePlan` | `manage_woocommerce` | write, non-destructive, non-idempotent | Plan object data. | | `woocommerce-memberships/plans-delete` | `DeletePlan` | `manage_woocommerce` | destructive | Integer plan ID. | | `woocommerce-memberships/plans-get` | `GetPlan` | `manage_woocommerce` | readonly, idempotent | Integer plan ID. | | `woocommerce-memberships/plans-list` | `ListPlans` | `manage_woocommerce` | readonly, idempotent | WP_Query-like args for plans. | | `woocommerce-memberships/user-memberships-create` | `CreateUserMembership` | `manage_woocommerce` | write, non-destructive, non-idempotent | `plan_id`, `user_id`, optional `product_id`, `order_id`. | | `woocommerce-memberships/user-memberships-delete` | `DeleteUserMembership` | `manage_woocommerce` | destructive | Integer user membership ID. | | `woocommerce-memberships/user-memberships-get` | `GetUserMembership` | `manage_woocommerce` | readonly, idempotent | Integer user membership ID. | | `woocommerce-memberships/user-memberships-list` | `ListUserMemberships` | `manage_woocommerce` | readonly, idempotent | `user_id`, optional `status`. | | `woocommerce-memberships/post-restriction-rules-get` | `GetPostRestrictionRules` | `manage_woocommerce_membership_plans` plus `edit_post` for direct integer input | readonly, idempotent | Integer post ID. | | `woocommerce-memberships/post-restriction-rules-update` | `UpdatePostRestrictionRules` | `manage_woocommerce_membership_plans` in source; add `edit_post` in wrappers | write, non-destructive, idempotent | Object with `id` and replacement `rules`. | Output schemas use the plugin object JSON schemas: - `WC_Memberships_Membership_Plan::getJsonSchema()` - `WC_Memberships_User_Membership::getJsonSchema()` - `PostRestrictionRulesSerializer::getJsonSchema()` ## Plan creation input `plans-create` delegates to `wc_memberships()->get_plans_instance()->createPlan( $data )`. Important input groups: | Input | Notes | |---|---| | `name` | Required by schema. | | `slug` | Optional plan slug. | | `status` | `draft` or `publish`. | | `description` | Optional description. | | `access.method` | `manual-only`, `signup`, or `purchase`. | | `access.product_ids` | Required by business rules when method is `purchase`. | | `membership_length.type` | `unlimited`, `specific`, or `fixed`. | | `membership_length.amount` / `period` | Required by business rules for `specific`. | | `membership_length.start_date` / `end_date` | Required by business rules for `fixed`. | | `rules.content_restriction` | Plan content restriction rules. | | `rules.product_restriction` | Product view/purchase restriction rules. | | `rules.purchasing_discount` | Member discount rules. | Do not write the `wc_memberships_rules` option directly when an ability or plan API can create the plan and rules together. ## User membership creation input `user-memberships-create` delegates to Memberships's user membership manager: ```php wc_memberships()->get_user_memberships_instance()->create_user_membership( $data ); ``` Schema fields: | Input | Notes | |---|---| | `plan_id` | Required membership plan ID. | | `user_id` | Required WP user ID. | | `product_id` | Optional product that granted access. | | `order_id` | Optional order that granted access. | For purchase-based access, prefer passing meaningful `product_id` and `order_id` when the membership is truly tied to a purchase. Do not fake order/product relations just to satisfy reporting. ## Post restriction rule abilities Memberships 1.29.0 added Abilities API operations for the block-editor Memberships sidebar. They are configuration APIs for restrictable posts, not runtime access checks. | Need | Ability | REST route | |---|---|---| | Read rules applying to a post | `woocommerce-memberships/post-restriction-rules-get` | `GET /wc-memberships/v1/post-restriction-rules/{id}` | | Replace post-specific rules | `woocommerce-memberships/post-restriction-rules-update` | `PUT /wc-memberships/v1/post-restriction-rules/{id}` | `post-restriction-rules-get` returns: ```php array( 'id' => 123, 'rules' => array( array( 'id' => 'rule-id', 'membership_plan_id' => 456, 'access_schedule' => array( 'type' => 'immediate' ), 'editable' => true, ), ), ); ``` The response includes both post-specific rules and inherited rules from post-type/taxonomy level configuration. The `editable` flag is the safety boundary: - `editable === true`: rule targets this post directly and can be sent to the update ability. - `editable === false`: inherited rule; render read-only and edit it on the membership plan/source rule, not from the post payload. `post-restriction-rules-update` treats `rules` as the full desired state for post-specific content restriction rules: - Existing direct rules omitted from the payload are deleted. - Rows with a known direct rule `id` are updated. - Rows without `id` are added. - `rules: array()` clears all direct post-specific rules. - Inherited rules are not affected, and sending an inherited rule ID causes a `422 invalid_input` because the ID does not belong to this post. Safe PHP execution shape: ```php $get = wp_get_ability( 'woocommerce-memberships/post-restriction-rules-get' ); $current = $get ? $get->execute( $post_id ) : null; if ( is_wp_error( $current ) || ! is_array( $current ) ) { return $current; } $editable_rules = array_values( array_filter( $current['rules'], static fn( array $rule ): bool => ! empty( $rule['editable'] ) ) ); $editable_rules[] = array( 'membership_plan_id' => $plan_id, 'access_schedule' => array( 'type' => 'delayed', 'amount' => 7, 'period' => 'days' ), ); if ( ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'forbidden', 'Cannot edit this post.', array( 'status' => 403 ) ); } $update = wp_get_ability( 'woocommerce-memberships/post-restriction-rules-update' ); $result = $update ? $update->execute( array( 'id' => $post_id, 'rules' => $editable_rules, ) ) : new WP_Error( 'missing_ability' ); ``` The update rule schema accepts `membership_plan_id` and optional `access_schedule`. Delayed schedules use `type = delayed`, positive `amount`, and `period` in `days`, `weeks`, `months`, or `years`; immediate schedules use `array( 'type' => 'immediate' )`. The block editor sidebar also registers REST-exposed post meta for `_wc_memberships_force_public` and the per-post custom restriction message keys. Those meta writes are separate from rule replacement; use the abilities above for rule rows and normal post meta/REST for the sidebar's force-public/message settings. ## Safe execution pattern ```php $ability = function_exists( 'wp_get_ability' ) ? wp_get_ability( 'woocommerce-memberships/user-memberships-get' ) : null; if ( ! $ability || ! current_user_can( 'manage_woocommerce' ) ) { return new WP_Error( 'forbidden', 'Membership ability is unavailable.', array( 'status' => 403 ) ); } $result = $ability->execute( 123 ); if ( is_wp_error( $result ) ) { return $result; } ``` Let the ability permission callback run; the explicit `current_user_can()` guard is useful when your code is about to choose between an admin path and a frontend-safe path. ## Choosing the right surface | Need | Prefer | |---|---| | Admin/agent automation on WP 7.0+ | Abilities API. | | External integration over HTTP with Woo auth | Memberships REST API. | | In-process plugin business logic | Public PHP APIs and objects. | | Customer frontend/headless "my memberships" | Custom endpoint that checks ownership and uses Memberships access APIs. | | Public member directory | `/wc/v4/memberships/members/directory`, with page/block validation and privacy-limited fields. | | Block-editor per-post restriction UI | `post-restriction-rules-get/update` abilities or their `/wc-memberships/v1/post-restriction-rules/{id}` routes. | Do not use `manage_woocommerce` abilities for a customer-facing dashboard. A customer should not be able to list arbitrary users' memberships or delete plans. ## Security guardrails - Never proxy ability execution from a public REST route without a capability check. - Do not pass arbitrary frontend-controlled WP_Query args into `plans-list`; even though the ability is admin-gated, sanitize UI inputs before execution. - Treat delete abilities as destructive and require an explicit admin confirmation in UI. - Do not down-scope permission by filtering current user capabilities. Build a narrower custom endpoint/service when customers need self-service membership data. - Do not assume every ability has a REST route. Plan and user-membership abilities pass `showInRest = true` for Abilities API metadata but do not provide the SkyVerge framework `RestConfig`; the post restriction rule abilities do. - For post rule updates, never round-trip inherited rows from GET into PUT. Filter to `editable === true` and intentionally rebuild the direct post-specific rule set. - The framework's route permission callback is invoked without request input before execution. `WP_Ability::execute()` passes validated input to the permission callback, but the update input is an array, not a numeric ID, so the shared trait's `edit_post` branch does not fire there in 1.29.0. Add an explicit `current_user_can( 'edit_post', $post_id )` check before custom update wrappers. ## Common mistakes ```php // WRONG: exposing a privileged ability to any logged-in user. register_rest_route( 'my/v1', '/membership', array( 'methods' => 'POST', 'permission_callback' => 'is_user_logged_in', 'callback' => function ( WP_REST_Request $request ) { return wp_get_ability( 'woocommerce-memberships/user-memberships-delete' )->execute( (int) $request['id'] ); }, ) ); // RIGHT: use capability checks for privileged automation. register_rest_route( 'my/v1', '/admin/membership', array( 'methods' => 'POST', 'permission_callback' => static fn() => current_user_can( 'manage_woocommerce' ), 'callback' => function ( WP_REST_Request $request ) { $ability = wp_get_ability( 'woocommerce-memberships/user-memberships-get' ); return $ability ? $ability->execute( (int) $request['id'] ) : new WP_Error( 'missing_ability' ); }, ) ); ``` ## Cross-references - Use `wcm-membership-hooks` for lifecycle hooks, REST/webhooks, profile fields, member directory, CSV, and Subscriptions-linked memberships. - Use `wcm-data-model-subscriptions-link` for CPT names, meta keys, rule storage, and Subscriptions relation storage. - Use `wcm-access-discounts` for access checks, restriction/drip behavior, and member discount APIs. ## References - Verified source paths: - `wp-content/plugins/woocommerce-memberships/class-wc-memberships.php` - `wp-content/plugins/woocommerce-memberships/src/Abilities/Provider.php` - `wp-content/plugins/woocommerce-memberships/src/Plans/Abilities/` - `wp-content/plugins/woocommerce-memberships/src/UserMemberships/Abilities/` - `wp-content/plugins/woocommerce-memberships/src/Posts/Abilities/` - `wp-content/plugins/woocommerce-memberships/src/Posts/Actions/SetPostRules.php` - `wp-content/plugins/woocommerce-memberships/src/Posts/Adapters/JsonSerializers/PostRestrictionRulesSerializer.php` - `wp-content/plugins/woocommerce-memberships/src/Posts/Traits/CanCheckRestrictablePostPermissionTrait.php` - `wp-content/plugins/woocommerce-memberships/src/Blocks/BlockEditorSidebar.php` - `wp-content/plugins/woocommerce-memberships/vendor/skyverge/wc-plugin-framework/woocommerce/Abilities/`
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.