Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add form responses v2 endpoint #29043

Merged
merged 19 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from 12 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 v2/v4 endpoint for form responses inbox
2 changes: 1 addition & 1 deletion projects/packages/forms/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"link-template": "https://github.com/automattic/jetpack-forms/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "0.5.x-dev"
"dev-trunk": "0.6.x-dev"
},
"textdomain": "jetpack-forms",
"version-constants": {
Expand Down
2 changes: 1 addition & 1 deletion projects/packages/forms/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@automattic/jetpack-forms",
"version": "0.5.1",
"version": "0.6.0-alpha",
"description": "Jetpack Forms",
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/forms/#readme",
"bugs": {
Expand Down
4 changes: 3 additions & 1 deletion projects/packages/forms/src/class-jetpack-forms.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/
class Jetpack_Forms {

const PACKAGE_VERSION = '0.5.1';
const PACKAGE_VERSION = '0.6.0-alpha';

/**
* Load the contact form module.
Expand Down Expand Up @@ -43,6 +43,8 @@ public static function load_contact_form() {
}

add_action( 'init', '\Automattic\Jetpack\Forms\ContactForm\Util::register_pattern' );

add_action( 'rest_api_init', array( new WPCOM_REST_API_V2_Endpoint_Forms(), 'register_rest_routes' ) );
}

/**
Expand Down
255 changes: 255 additions & 0 deletions projects/packages/forms/src/class-wpcom-rest-api-v2-endpoint-forms.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
<?php
/**
* The Forms Rest Controller class.
* Registers the REST routes for Jetpack Forms (taken from stats-admin).
*
* @package automattic/jetpack-forms
*/

namespace Automattic\Jetpack\Forms;

use Jetpack_Options;
use WP_Error;
use WP_REST_Controller;
use WP_REST_Server;

/**
* Handles the REST routes for Form Responses, aka Feedback.
* Routes are defined on Jetpack Plugin as WPCOM_REST_API_V2_Endpoint_Forms.
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
*/
class WPCOM_REST_API_V2_Endpoint_Forms extends WP_REST_Controller {
/**
* Is WPCOM or not
*
* @var bool
*/
protected $is_wpcom;
CGastrell marked this conversation as resolved.
Show resolved Hide resolved

/**
* Constructor.
*/
public function __construct() {
$this->is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;

$this->namespace = 'wpcom/v2';
$this->rest_base = 'forms';

add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
}

/**
* Registers the REST routes.
*
* Odyssey Stats is built from `wp-calypso`, which leverages the `public-api.wordpress.com` API.
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
* The current Site ID is added as part of the route, so that the front end doesn't have to handle the differences.
*
* @access public
*/
public function register_rest_routes() {
// Stats for single resource type.

register_rest_route(
$this->namespace,
$this->rest_base . '/responses',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_responses' ),
'permissions_check' => array( $this, 'get_responses_permission_check' ),
'args' => array(
'limit' => array(
'default' => 20,
'type' => 'integer',
'required' => false,
'validate_callback' => __CLASS__ . '::validate_posint',
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
),
'offset' => array(
'default' => 0,
'type' => 'integer',
'required' => false,
'validate_callback' => __CLASS__ . '::validate_non_neg_int',
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
),
'form_id' => array(
'type' => 'integer',
'required' => false,
'validate_callback' => __CLASS__ . '::validate_posint',
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
),
'search' => array(
'type' => 'text',
'required' => false,
'validate_callback' => __CLASS__ . '::validate_string',
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
),
),
)
);
}

/**
* Validates that the parameter is a positive integer.
*
* @since 4.3.0
*
* @param int $value Value to check.
* @param WP_REST_Request $request The request sent to the WP REST API.
* @param string $param Name of the parameter passed to endpoint holding $value.
*
* @return bool|WP_Error
*/
public static function validate_posint( $value, $request, $param ) {
if ( ! is_numeric( $value ) || $value <= 0 ) {
return new WP_Error(
'invalid_param',
sprintf(
/* Translators: Placeholder is a parameter name. */
esc_html__( '%s must be a positive integer.', 'jetpack-forms' ),
$param
)
);
}
return true;
}

/**
* Validates that the parameter is a non-negative integer (includes 0).
*
* @since 10.4.0
*
* @param int $value Value to check.
* @param WP_REST_Request $request The request sent to the WP REST API.
* @param string $param Name of the parameter passed to endpoint holding $value.
*
* @return bool|WP_Error
*/
public static function validate_non_neg_int( $value, $request, $param ) {
if ( ! is_numeric( $value ) || $value < 0 ) {
return new WP_Error(
'invalid_param',
/* translators: %s: The literal parameter name. Should not be translated. */
sprintf( esc_html__( '%s must be a non-negative integer.', 'jetpack-forms' ), $param )
);
}
return true;
}

/**
* Validates that the parameter is a string.
*
* @since 4.3.0
*
* @param string $value Value to check.
* @param WP_REST_Request $request The request sent to the WP REST API.
* @param string $param Name of the parameter passed to endpoint holding $value.
*
* @return bool|WP_Error
*/
public static function validate_string( $value, $request, $param ) {
if ( ! is_string( $value ) ) {
return new WP_Error(
'invalid_param',
sprintf(
/* Translators: Placeholder is a parameter name. */
esc_html__( '%s must be a string.', 'jetpack-forms' ),
$param
)
);
}
return true;
}

/**
* Returns Jetpack Forms responses.
*
* @param WP_REST_Request $request The request sent to the WP REST API.
*
* @return WP_REST_Response A response object containing Jetpack Forms responses.
*/
public function get_responses( $request ) {
$args = array(
'post_type' => 'feedback',
'post_status' => array( 'publish', 'draft' ),
);

if ( isset( $request['form_id'] ) ) {
$args['post_parent'] = $request['form_id'];
}

if ( isset( $request['limit'] ) ) {
$args['posts_per_page'] = $request['limit'];
}

if ( isset( $request['offset'] ) ) {
$args['offset'] = $request['offset'];
}

if ( isset( $request['search'] ) ) {
$args['s'] = $request['search'];
}

$query = new \WP_Query( $args );

$responses = array_map(
function ( $response ) {
$data = \Automattic\Jetpack\Forms\ContactForm\Contact_Form_Plugin::parse_fields_from_content( $response->ID );

return array(
'id' => $response->ID,
'uid' => $data['all_fields']['feedback_id'],
'date' => get_the_date( 'c', $data ),
'author_name' => $data['_feedback_author'],
'author_email' => $data['_feedback_author_email'],
'author_url' => $data['_feedback_author_url'],
'author_avatar' => empty( $data['_feedback_author_email'] ) ? '' : get_avatar_url( $data['_feedback_author_email'] ),
'email_marketing_consent' => $data['all_fields']['email_marketing_consent'],
'ip' => $data['_feedback_ip'],
'entry_title' => $data['all_fields']['entry_title'],
'entry_permalink' => $data['all_fields']['entry_permalink'],
'subject' => $data['_feedback_subject'],
'fields' => array_diff_key(
$data['all_fields'],
array(
'email_marketing_consent' => '',
'entry_title' => '',
'entry_permalink' => '',
'feedback_id' => '',
)
),
);
},
$query->posts
);

return rest_ensure_response(
array(
'responses' => $responses,
'total' => $query->found_posts,
)
);
}

/**
* Verifies that the current user has the requird capability for viewing form responses.
*
* @return true|WP_Error Returns true if the user has the required capability, else a WP_Error object.
*/
public function get_responses_permission_check() {
if ( ! is_user_member_of_blog( get_current_user_id(), $this->get_blog_id() ) ) {
CGastrell marked this conversation as resolved.
Show resolved Hide resolved
return new WP_Error(
'invalid_user_permission_jetpack_form_responses',
'unauthorized',
array( 'status' => rest_authorization_required_code() )
);
}

return true;
}

/**
* Get blog ID selectively depending on IS_WPCOM or not
*/
protected function get_blog_id() {
return $this->is_wpcom ? get_current_blog_id() : Jetpack_Options::get_option( 'id' );
}
}

if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
wpcom_rest_api_v2_load_plugin( 'Automattic\Jetpack\Forms\WPCOM_REST_API_V2_Endpoint_Forms' );
}
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,7 @@ public static function parse_fields_from_content( $post_id ) {
}

$fields['_feedback_all_fields'] = $all_values;
$fields['all_fields'] = $all_values;

$post_fields[ $post_id ] = $fields;

Expand Down
17 changes: 14 additions & 3 deletions projects/packages/forms/src/dashboard/class-dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,22 @@ public function load_admin_scripts( $hook ) {
'../../dist/dashboard/jetpack-forms-dashboard.js',
__FILE__,
array(
'in_footer' => true,
'textdomain' => 'jetpack-forms',
'enqueue' => true,
'in_footer' => true,
'textdomain' => 'jetpack-forms',
'enqueue' => true,
'dependencies' => array( 'wp-api-fetch' ), // this here just for testing, remove when done (apiFetch will be on build)
)
);

$api_root = defined( 'IS_WPCOM' ) && IS_WPCOM
? sprintf( '/wpcom/v2/sites/%s/', esc_url_raw( rest_url() ) ) // should we include full URL here (public-api.wordpress.com)?
: '/wp-json/wpcom/v2/';

wp_add_inline_script(
'jp-forms-dashboard',
'window.jetpackFormsData = ' . wp_json_encode( array( 'apiRoot' => $api_root ) ) . ';',
'before'
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: other

Add wpcom/v2/form-responses endpoint, mapped from .com
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: other
Comment: Updated composer.lock.


4 changes: 2 additions & 2 deletions projects/plugins/jetpack/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.