Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add Abilities API integration for Jetpack Forms.
4 changes: 4 additions & 0 deletions projects/packages/forms/src/class-jetpack-forms.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ public static function load_contact_form() {
$dashboard->init();
}

// Initialize Abilities API integration
require_once __DIR__ . '/contact-form/class-abilities.php';
ContactForm\Abilities::init();

if ( is_admin() && apply_filters_deprecated( 'tmp_grunion_allow_editor_view', array( true ), '0.30.5', '', 'This functionality will be removed in an upcoming version.' ) ) {
add_action( 'current_screen', '\Automattic\Jetpack\Forms\ContactForm\Editor_View::add_hooks' );
}
Expand Down
200 changes: 200 additions & 0 deletions projects/packages/forms/src/contact-form/class-abilities.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<?php
/**
* Jetpack Forms Abilities API Integration
*
* @package automattic/jetpack-forms
*/

namespace Automattic\Jetpack\Forms\ContactForm;

/**
* Handles registration of Jetpack Forms abilities with the Abilities API.
*/
class Abilities {

/**
* Initialize abilities registration.
*/
public static function init() {
if ( function_exists( 'wp_register_ability' ) ) {
add_action( 'wp_abilities_api_categories_init', array( __CLASS__, 'register_category' ) );
add_action( 'wp_abilities_api_init', array( __CLASS__, 'register_abilities' ), 1 );
}
}

/**
* Register the Jetpack Forms ability category.
*/
public static function register_category() {
if ( function_exists( 'wp_register_ability_category' ) ) {
wp_register_ability_category(

Check failure on line 30 in projects/packages/forms/src/contact-form/class-abilities.php

View workflow job for this annotation

GitHub Actions / Static analysis

UndefError PhanUndeclaredFunction Call to undeclared function \wp_register_ability_category() FAQ on Phan issues: pdWQjU-Jb-p2
'jetpack-forms',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend using a class constant here and elsewhere.

array(
'label' => __( 'Jetpack Forms', 'jetpack-forms' ),
'description' => __( 'Abilities related to Jetpack Forms functionality.', 'jetpack-forms' ),
)
);
}
}

/**
* Register all Jetpack Forms abilities.
*/
public static function register_abilities() {
wp_register_ability(

Check failure on line 44 in projects/packages/forms/src/contact-form/class-abilities.php

View workflow job for this annotation

GitHub Actions / Static analysis

UndefError PhanUndeclaredFunction Call to undeclared function \wp_register_ability() FAQ on Phan issues: pdWQjU-Jb-p2
'jetpack/list-form-responses',
array(
'label' => __( 'List Jetpack Form Responses', 'jetpack-forms' ),
'description' => __( 'Retrieve Jetpack form responses from a specific site.', 'jetpack-forms' ),
'category' => 'jetpack-forms',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'blog_id' => array(
'type' => 'string',
'description' => 'Site ID (numeric) or site URL to get form responses from. Defaults to current site if not specified.',
),
Comment on lines +53 to +56
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's been some discussion around this — whether abilities themselves should have inputs for things like blog_id — as opposed to letting the implementing code handle which blog is active.

Is there a specific purpose for this in this context?

'per_page' => array(
'type' => 'integer',
'default' => 20,
'minimum' => 1,
'maximum' => 100,
),
),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'responses' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'id' => array( 'type' => 'integer' ),
'form' => array( 'type' => 'string' ),
'author' => array( 'type' => 'string' ),
'date' => array( 'type' => 'string' ),
'content' => array( 'type' => 'string' ),
),
),
),
),
),
'execute_callback' => array( __CLASS__, 'execute_list_responses' ),
'permission_callback' => array( __CLASS__, 'check_permission' ),
)
);
}

/**
* Execute the list-form-responses ability.
*
* @param array $input Input parameters including blog_id.
* @return array|\WP_Error List of form responses or error.
*/
public static function execute_list_responses( $input ) {
$per_page = isset( $input['per_page'] ) ? $input['per_page'] : 20;

Check failure on line 96 in projects/packages/forms/src/contact-form/class-abilities.php

View workflow job for this annotation

GitHub Actions / Static analysis

Plugin PhanPluginDuplicateConditionalNullCoalescing "isset(X) ? X : Y" can usually be simplified to "X ?? Y" in PHP 7. The duplicated expression X was $input['per_page'] FAQ on Phan issues: pdWQjU-Jb-p2

$blog_id = self::resolve_site_identifier( isset( $input['blog_id'] ) ? $input['blog_id'] : get_current_blog_id() );

Check failure on line 98 in projects/packages/forms/src/contact-form/class-abilities.php

View workflow job for this annotation

GitHub Actions / Static analysis

Plugin PhanPluginDuplicateConditionalNullCoalescing "isset(X) ? X : Y" can usually be simplified to "X ?? Y" in PHP 7. The duplicated expression X was $input['blog_id'] FAQ on Phan issues: pdWQjU-Jb-p2
if ( is_wp_error( $blog_id ) ) {
return $blog_id;
}

switch_to_blog( $blog_id );

$query = new \WP_Query(
array(
'post_type' => 'feedback',
'post_status' => 'publish',
'post_parent__not' => 0,
'posts_per_page' => $per_page,
'orderby' => 'date',
'order' => 'DESC',
)
);

$responses = array();

foreach ( $query->posts as $post ) {
$parent = get_post( $post->post_parent );

$responses[] = array(
'id' => $post->ID,
'form' => $parent ? $parent->post_title : '',
'author' => $post->post_author,
'date' => $post->post_date,
'content' => $post->post_content,
);
}

restore_current_blog();
return array(
'responses' => $responses,
);
}

/**
* Resolve site identifier to blog ID.
*
* @param string|int $site_identifier Site ID or URL.
* @return int|\WP_Error Blog ID or error.
*/
private static function resolve_site_identifier( $site_identifier ) {
if ( empty( $site_identifier ) ) {
return new \WP_Error(
'missing_site_identifier',
__( 'No site identifier provided. Please provide a site ID or URL in the blog_id parameter.', 'jetpack-forms' ),
array( 'status' => 400 )
);
}

/**
* Filter to resolve a site URL to a blog ID.
*
* Allows implementations to provide custom site URL resolution logic.
*
* @param int|WP_Error|null $blog_id The resolved blog ID, WP_Error, or null if not resolved.
* @param string $site_identifier The original site identifier.
* @return int|WP_Error The resolved blog ID or WP_Error if not resolved.
*/
$site_identifier = apply_filters( 'jetpack_forms_resolve_site_url', null, $site_identifier );

return (int) $site_identifier;
}

/**
* Check permission for listing form responses.
*
* Checks if the user has access to the target site specified in blog_id parameter.
*
* @param array $input Input parameters including blog_id.
* @return bool|\WP_Error True if user has permission, WP_Error if not.
*/
public static function check_permission( $input ) {
$current_user = wp_get_current_user();
if ( ! $current_user->ID ) {
return new \WP_Error(
'not_logged_in',
__( 'You must be logged in to access form responses.', 'jetpack-forms' )
);
}

$blog_id = self::resolve_site_identifier( isset( $input['blog_id'] ) ? $input['blog_id'] : get_current_blog_id() );

Check failure on line 182 in projects/packages/forms/src/contact-form/class-abilities.php

View workflow job for this annotation

GitHub Actions / Static analysis

Plugin PhanPluginDuplicateConditionalNullCoalescing "isset(X) ? X : Y" can usually be simplified to "X ?? Y" in PHP 7. The duplicated expression X was $input['blog_id'] FAQ on Phan issues: pdWQjU-Jb-p2
if ( is_wp_error( $blog_id ) ) {
return $blog_id;
}

if ( ! current_user_can_for_site( $blog_id, 'edit_pages' ) ) {
return new \WP_Error(
'insufficient_permissions',
sprintf(
/* translators: %s: blog ID */
__( 'You do not have permission to access form responses on site %s. You need the "read" capability (member access).', 'jetpack-forms' ),
$blog_id
)
);
}

return true;
}
}
Loading