Initial Commit

This commit is contained in:
David Stone
2024-11-30 18:24:12 -07:00
commit e8f7955c1c
5432 changed files with 1397750 additions and 0 deletions

697
inc/api/trait-rest-api.php Normal file
View File

@ -0,0 +1,697 @@
<?php
/**
* A trait to be included in entities to enable REST API endpoints.
*
* @package WP_Ultimo
* @subpackage Apis
* @since 2.0.0
*/
namespace WP_Ultimo\Apis;
/**
* REST API trait.
*/
trait Rest_Api {
/**
* The base used in the route right after the namespace: <namespace>/<rest_base>.
*
* @since 2.0.0
* @var string
*/
protected $rest_base = '';
/**
* REST endpoints enabled for this entity.
*
* @since 2.0.0
* @var array
*/
protected $enabled_rest_endpoints = array(
'get_item',
'get_items',
'create_item',
'update_item',
'delete_item',
);
/**
* Returns the base used right after the namespace.
* Uses the `rest_base` attribute if set, `slug` otherwise.
*
* @since 2.0.0
* @return string
*/
public function get_rest_base() {
return (!empty($this->rest_base)) ? $this->rest_base : $this->slug;
} // end get_rest_base;
/**
* Registers the routes. Should be called by the entity
* to actually enable the REST API.
*
* @since 2.0.0
*/
public function enable_rest_api() {
$is_enabled = \WP_Ultimo\API::get_instance()->is_api_enabled();
if ($is_enabled) {
add_action('rest_api_init', array($this, 'register_routes_general'));
add_action('rest_api_init', array($this, 'register_routes_with_id'));
} // end if;
} // end enable_rest_api;
/**
* Register the endpoints that don't need an ID,
* like creation and lists.
*
* @since 2.0.0
*/
public function register_routes_general() {
$routes = array();
if (in_array('get_items', $this->enabled_rest_endpoints, true)) {
$routes = array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_items_rest'),
'permission_callback' => array($this, 'get_items_permissions_check'),
),
);
} // end if;
if (in_array('create_item', $this->enabled_rest_endpoints, true)) {
$routes[] = array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item_rest'),
'permission_callback' => array($this, 'create_item_permissions_check'),
'args' => $this->get_arguments_schema()
);
} // end if;
if (!empty($routes)) {
register_rest_route(
\WP_Ultimo\API::get_instance()->get_namespace(),
'/' . $this->get_rest_base(),
$routes,
true
);
} // end if;
do_action('wu_rest_register_routes_general', $routes, $this->get_rest_base(), 'create', $this);
} // end register_routes_general;
/**
* Register the endpoints that need an ID,
* like get, update and delete of a single element.
*
* @since 2.0.0
*/
public function register_routes_with_id() {
$routes = array();
if (in_array('get_item', $this->enabled_rest_endpoints, true)) {
$routes[] = array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item_rest'),
'permission_callback' => array($this, 'get_item_permissions_check'),
);
} // end if;
if (in_array('update_item', $this->enabled_rest_endpoints, true)) {
$routes[] = array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item_rest'),
'permission_callback' => array($this, 'update_item_permissions_check'),
'args' => $this->get_arguments_schema(true)
);
} // end if;
if (in_array('delete_item', $this->enabled_rest_endpoints, true)) {
$routes[] = array(
'methods' => \WP_REST_Server::DELETABLE,
'callback' => array($this, 'delete_item_rest'),
'permission_callback' => array($this, 'delete_item_permissions_check'),
);
} // end if;
if (!empty($routes)) {
register_rest_route(
\WP_Ultimo\API::get_instance()->get_namespace(),
'/' . $this->get_rest_base() . '/(?P<id>[\d]+)',
$routes,
true
);
} // end if;
do_action('wu_rest_register_routes_with_id', $routes, $this->get_rest_base(), 'update', $this);
} // end register_routes_with_id;
/**
* Returns a specific item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return \WP_REST_Response|\WP_Error
*/
public function get_item_rest($request) {
$item = $this->model_class::get_by_id($request['id']);
if (empty($item)) {
return new \WP_Error("wu_rest_{$this->slug}_invalid_id", __('Item not found.', 'wp-ultimo'), array('status' => 404));
} // end if;
return rest_ensure_response($item);
} // end get_item_rest;
/**
* Returns a list of items.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return \WP_REST_Response|\WP_Error
*/
public function get_items_rest($request) {
$items = $this->model_class::query($request->get_params());
return rest_ensure_response($items);
} // end get_items_rest;
/**
* Creates an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return \WP_REST_Response|\WP_Error
*/
public function create_item_rest($request) {
$body = json_decode($request->get_body(), true);
$model_name = (new $this->model_class(array()))->model;
$saver_function = "wu_create_{$model_name}";
if (function_exists($saver_function)) {
$item = call_user_func($saver_function, $body);
$saved = is_wp_error($item) ? $item : true;
} else {
$item = new $this->model_class($body);
$saved = $item->save();
} // end if;
if (is_wp_error($saved)) {
return rest_ensure_response($saved);
} // end if;
if (!$saved) {
return new \WP_Error("wu_rest_{$this->slug}", __('Something went wrong (Code 1).', 'wp-ultimo'), array('status' => 400));
} // end if;
return rest_ensure_response($item);
} // end create_item_rest;
/**
* Updates an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return \WP_REST_Response|\WP_Error
*/
public function update_item_rest($request) {
$id = wu_get_isset($request->get_url_params(), 'id');
$item = $this->model_class::get_by_id($id);
if (empty($item)) {
return new \WP_Error("wu_rest_{$this->slug}_invalid_id", __('Item not found.', 'wp-ultimo'), array('status' => 404));
} // end if;
$params = array_filter(
json_decode($request->get_body(), true),
array($this, 'is_not_credential_key'),
ARRAY_FILTER_USE_KEY
);
foreach ($params as $param => $value) {
$set_method = "set_{$param}";
if ($param === 'meta') {
$item->update_meta_batch($value);
} elseif (method_exists($item, $set_method)) {
call_user_func(array($item, $set_method), $value);
} else {
$error_message = sprintf(
/* translators: 1. Object class name; 2. Set method name */
__('The %1$s object does not have a %2$s method', 'wp-ultimo'),
get_class($item),
$set_method
);
return new \WP_Error(
"wu_rest_{$this->slug}_invalid_set_method",
$error_message,
array('status' => 400)
);
} // end if;
} // end foreach;
$saved = $item->save();
if (is_wp_error($saved)) {
return rest_ensure_response($saved);
} // end if;
if (!$saved) {
return new \WP_Error("wu_rest_{$this->slug}", __('Something went wrong (Code 2).', 'wp-ultimo'));
} // end if;
return rest_ensure_response($item);
} // end update_item_rest;
/**
* Deletes an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return \WP_REST_Response|\WP_Error
*/
public function delete_item_rest($request) {
$item = $this->model_class::get_by_id($request['id']);
if (empty($item)) {
return new \WP_Error("wu_rest_{$this->slug}_invalid_id", __('Item not found.', 'wp-ultimo'), array('status' => 404));
} // end if;
$result = $item->delete();
return rest_ensure_response($result);
} // end delete_item_rest;
/**
* Check permissions to list items.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return bool
*/
public function get_items_permissions_check($request) {
if (!\WP_Ultimo\API::get_instance()->check_authorization($request)) {
return false;
} // end if;
/**
* Filters if it is allowed to proceed with the request or not.
*
* @since 2.0.0
*
* @param bool $allowed Initial return value.
* @param array $rest_base Entity slug.
* @param Base_Manager $this The object instance.
*/
return apply_filters('wu_rest_get_items', true, $this->get_rest_base(), $this);
} // end get_items_permissions_check;
/**
* Check permissions to create an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return bool
*/
public function create_item_permissions_check($request) {
if (!\WP_Ultimo\API::get_instance()->check_authorization($request)) {
return false;
} // end if;
/**
* Filters if it is allowed to proceed with the request or not.
*
* @since 2.0.0
*
* @param bool $allowed Initial return value.
* @param array $rest_base Entity slug.
* @param Base_Manager $this The object instance.
*/
return apply_filters('wu_rest_create_item', true, $this->get_rest_base(), $this);
} // end create_item_permissions_check;
/**
* Check permissions to get an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return bool
*/
public function get_item_permissions_check($request) {
if (!\WP_Ultimo\API::get_instance()->check_authorization($request)) {
return false;
} // end if;
/**
* Filters if it is allowed to proceed with the request or not.
*
* @since 2.0.0
*
* @param bool $allowed Initial return value.
* @param array $rest_base Entity slug.
* @param Base_Manager $this The object instance.
*/
return apply_filters('wu_rest_get_item', true, $this->get_rest_base(), $this);
} // end get_item_permissions_check;
/**
* Check permissions to update an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return bool
*/
public function update_item_permissions_check($request) {
if (!\WP_Ultimo\API::get_instance()->check_authorization($request)) {
return false;
} // end if;
/**
* Filters if it is allowed to proceed with the request or not.
*
* @since 2.0.0
*
* @param bool $allowed Initial return value.
* @param array $rest_base Entity slug.
* @param Base_Manager $this The object instance.
*/
return apply_filters('wu_rest_update_item', true, $this->get_rest_base(), $this);
} // end update_item_permissions_check;
/**
* Check permissions to delete an item.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The request sent.
* @return bool
*/
public function delete_item_permissions_check($request) {
if (!\WP_Ultimo\API::get_instance()->check_authorization($request)) {
return false;
} // end if;
/**
* Filters if it is allowed to proceed with the request or not.
*
* @since 2.0.0
*
* @param bool $allowed Initial return value.
* @param array $rest_base Entity slug.
* @param Base_Manager $this The object instance.
*/
return apply_filters('wu_rest_delete_item', true, $this->get_rest_base(), $this);
} // end delete_item_permissions_check;
/**
* Checks if a value is not a credential key.
*
* @since 2.0.0
*
* @param string $value The value that will be checked.
* @return bool
*/
private function is_not_credential_key($value) {
$credentials_keys = array(
'api_key',
'api_secret',
'api-key',
'api-secret',
);
return !in_array($value, $credentials_keys, true);
} // end is_not_credential_key;
/**
* Checks if a value is not equal to "id".
*
* @since 2.0.0
*
* @param string $value The value that will be checked.
* @return bool
*/
private function is_not_id_key($value) {
$arr = array(
'id',
);
if ($this->slug === 'site') {
$arr = array(
'id',
'blog_id',
);
} // end if;
return !in_array($value, $arr, true);
} // end is_not_id_key;
/**
* Get the arguments for an endpoint
*
* @since 2.0.0
*
* @param bool $edit Context. In edit, some fields, like ids, are not mandatory.
* @return array
*/
public function get_arguments_schema($edit = false) {
$schema = wu_rest_get_endpoint_schema($this->model_class, $edit ? 'update' : 'create', true);
$args = array_filter($schema, array($this, 'is_not_id_key'), ARRAY_FILTER_USE_KEY);
return $this->filter_schema_arguments($args);
} // end get_arguments_schema;
/**
* Remove some properties from the API schema.
*
* @since 2.0.0
*
* @param array $args Schema array.
* @return array
*/
public function filter_schema_arguments($args) {
/**
* Filter the original api arguments.
*
* @since 2.0.0
*
* @param array $args API Arguments for this manager.
* @param object $this This manager.
*/
apply_filters('wu_before_' . $this->slug . '_api_arguments', $args, $this);
if ($this->slug !== 'broadcast' && isset($args['author_id'])) {
unset($args['author_id']);
} // end if;
if (isset($args['list_order'])) {
unset($args['list_order']);
} // end if;
$remove_status = apply_filters("wu_api_{$this->slug}_remove_status", array(
'broadcast',
'membership',
'product',
'payment',
));
if (!in_array($this->slug, $remove_status, true) && isset($args['status'])) {
unset($args['status']);
} // end if;
$remove_slug = apply_filters("wu_api_{$this->slug}_remove_slug", array(
'broadcast',
'product',
'checkout_form',
'event',
));
if (!in_array($this->slug, $remove_slug, true) && isset($args['slug'])) {
unset($args['slug']);
} // end if;
if ($this->slug === 'product' && isset($args['price_variations'])) {
unset($args['price_variations']);
} // end if;
if ($this->slug === 'payment' && isset($args['line_items'])) {
unset($args['line_items']);
} // end if;
if ($this->slug === 'site') {
if (isset($args['duplication_arguments'])) {
unset($args['duplication_arguments']);
} // end if;
if (isset($args['transient'])) {
unset($args['transient']);
} // end if;
} // end if;
if ($this->slug === 'email') {
if (isset($args['status'])) {
unset($args['status']);
} // end if;
if (isset($args['email_schedule'])) {
unset($args['email_schedule']);
} // end if;
} // end if;
if ($this->slug === 'broadcast') {
if (isset($args['message_targets'])) {
unset($args['message_targets']);
} // end if;
} // end if;
if (isset($args['billing_address'])) {
unset($args['billing_address']);
} // end if;
/**
* Filter after being changed.
*
* @since 2.0.0
*
* @param array $args API Arguments for this manager.
* @param object $this This manager.
*/
apply_filters('wu_after_' . $this->slug . '_api_arguments', $args, $this);
return $args;
} // end filter_schema_arguments;
} // end trait Rest_Api;