509 lines
12 KiB
PHP
509 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* The Pattern library class file.
|
|
*
|
|
* @package GenerateBlocks\Pattern_Library
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit; // Exit if accessed directly.
|
|
}
|
|
|
|
/**
|
|
* Main class for the Pattern Library Rest functions.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
class GenerateBlocks_Pattern_Library_Rest extends GenerateBlocks_Singleton {
|
|
|
|
/**
|
|
* Initialize all hooks.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function init() {
|
|
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
|
|
}
|
|
|
|
/**
|
|
* Register the REST routes.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register_routes(): void {
|
|
$namespace = 'generateblocks/v1';
|
|
|
|
register_rest_route(
|
|
$namespace,
|
|
'/pattern-library/libraries',
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'list_libraries' ),
|
|
'permission_callback' => function() {
|
|
return apply_filters(
|
|
'generateblocks_can_view_pattern_library',
|
|
$this->edit_posts_permission()
|
|
);
|
|
},
|
|
)
|
|
);
|
|
|
|
register_rest_route(
|
|
$namespace,
|
|
'/pattern-library/libraries/save',
|
|
array(
|
|
'methods' => WP_REST_Server::CREATABLE,
|
|
'callback' => array( $this, 'save_libraries' ),
|
|
'permission_callback' => array( $this, 'manage_options_permission' ),
|
|
)
|
|
);
|
|
|
|
register_rest_route(
|
|
$namespace,
|
|
'/pattern-library/categories',
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'list_categories' ),
|
|
'permission_callback' => function() {
|
|
return apply_filters(
|
|
'generateblocks_can_view_pattern_library',
|
|
$this->edit_posts_permission()
|
|
);
|
|
},
|
|
)
|
|
);
|
|
|
|
register_rest_route(
|
|
$namespace,
|
|
'/pattern-library/patterns',
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'list_patterns' ),
|
|
'permission_callback' => function() {
|
|
return apply_filters(
|
|
'generateblocks_can_view_pattern_library',
|
|
$this->edit_posts_permission()
|
|
);
|
|
},
|
|
)
|
|
);
|
|
|
|
register_rest_route(
|
|
$namespace,
|
|
'/pattern-library/get-cache-data',
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_cache_data' ),
|
|
'permission_callback' => function() {
|
|
return apply_filters(
|
|
'generateblocks_can_view_pattern_library',
|
|
$this->edit_posts_permission()
|
|
);
|
|
},
|
|
)
|
|
);
|
|
|
|
register_rest_route(
|
|
$namespace,
|
|
'/pattern-library/clear-cache',
|
|
array(
|
|
'methods' => WP_REST_Server::EDITABLE,
|
|
'callback' => array( $this, 'clear_cache' ),
|
|
'permission_callback' => array( $this, 'edit_posts_permission' ),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Manage options permission callback.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function manage_options_permission(): bool {
|
|
return current_user_can( 'manage_options' );
|
|
}
|
|
|
|
/**
|
|
* Manage options permission callback.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function edit_posts_permission(): bool {
|
|
return current_user_can( 'edit_posts' );
|
|
}
|
|
|
|
/**
|
|
* Returns a list of registered libraries.
|
|
*
|
|
* @param WP_REST_Request $request The request.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function list_libraries( WP_REST_Request $request ): WP_REST_Response {
|
|
$is_enabled = $request->get_param( 'is_enabled' );
|
|
$libraries = GenerateBlocks_Libraries::get_instance();
|
|
$data = $libraries->get_all( rest_sanitize_boolean( $is_enabled ) );
|
|
$data = array_values( $data ); // Fix indexes.
|
|
|
|
return new WP_REST_Response(
|
|
array(
|
|
'error' => false,
|
|
'data' => $data,
|
|
),
|
|
200
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Saves the list of libraries.
|
|
*
|
|
* @param WP_REST_Request $request The request.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function save_libraries( WP_REST_Request $request ): WP_REST_Response {
|
|
$data = $request->get_param( 'data' );
|
|
|
|
if ( ! is_array( $data ) ) {
|
|
return $this->error( 400, __( 'Invalid library data.', 'generateblocks' ) );
|
|
}
|
|
|
|
$libraries = array_values(
|
|
array_filter(
|
|
array_map(
|
|
[ $this, 'sanitize_library_data' ],
|
|
$data
|
|
)
|
|
)
|
|
);
|
|
|
|
update_option( 'generateblocks_pattern_libraries', $libraries );
|
|
return $this->success( $libraries );
|
|
}
|
|
|
|
/**
|
|
* Returns a list of categories.
|
|
*
|
|
* @param WP_REST_Request $request The request.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function list_categories( WP_REST_Request $request ): WP_REST_Response {
|
|
$library_id = $this->sanitize_request_string( $request->get_param( 'libraryId' ) );
|
|
|
|
if ( ! $library_id ) {
|
|
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
|
}
|
|
|
|
$libraries = GenerateBlocks_Libraries::get_instance();
|
|
$library = $libraries->get_one( $library_id );
|
|
|
|
if ( ! is_null( $library ) ) {
|
|
return self::remote_fetch( $library, 'categories' );
|
|
}
|
|
|
|
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
|
}
|
|
|
|
/**
|
|
* Returns a list of patterns.
|
|
*
|
|
* @param WP_REST_Request $request The request.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function list_patterns( WP_REST_Request $request ): WP_REST_Response {
|
|
$library_id = $this->sanitize_request_string( $request->get_param( 'libraryId' ) );
|
|
$search = $this->sanitize_request_string( $request->get_param( 'search' ) );
|
|
$category_id = $this->sanitize_request_string( $request->get_param( 'categoryId' ) );
|
|
|
|
if ( ! $library_id ) {
|
|
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
|
}
|
|
|
|
$libraries = GenerateBlocks_Libraries::get_instance();
|
|
$library = $libraries->get_one( $library_id );
|
|
|
|
if ( ! is_null( $library ) ) {
|
|
return self::remote_fetch(
|
|
$library,
|
|
'patterns',
|
|
array(
|
|
'search' => $search,
|
|
'categoryId' => $category_id,
|
|
)
|
|
);
|
|
}
|
|
|
|
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
|
}
|
|
|
|
/**
|
|
* Fetch pattern library remotely.
|
|
*
|
|
* @param GenerateBlocks_Library_DTO $library The library to fetch data.
|
|
* @param string $collection The collection type. Either 'categories' or 'patterns'.
|
|
* @param array $query_args The extra query arguments.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
private function remote_fetch(
|
|
GenerateBlocks_Library_DTO $library,
|
|
string $collection,
|
|
array $query_args = array()
|
|
): WP_REST_Response {
|
|
if ( ! in_array( $collection, array( 'categories', 'patterns' ), true ) ) {
|
|
return $this->error( 400, __( 'Invalid library collection.', 'generateblocks' ) );
|
|
}
|
|
|
|
$domain = esc_url_raw( $library->domain );
|
|
|
|
if ( ! $domain ) {
|
|
return $this->error( 400, __( 'Invalid library domain.', 'generateblocks' ) );
|
|
}
|
|
|
|
$endpoint = trailingslashit( $domain ) . "wp-json/generateblocks-pro/v1/pattern-library/$collection";
|
|
$url = add_query_arg( $query_args, $endpoint );
|
|
$cache_key = $library->id . '-' . $collection;
|
|
$cache = GenerateBlocks_Libraries::get_cached_data( $cache_key, $query_args, $collection );
|
|
|
|
if ( false !== $cache ) {
|
|
return $this->success( $cache );
|
|
}
|
|
|
|
$request = wp_remote_get(
|
|
$url,
|
|
array(
|
|
'headers' => array(
|
|
'X-GB-Public-Key' => $library->public_key,
|
|
),
|
|
'timeout' => 15,
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $request ) ) {
|
|
return $this->error( 500, "Unable to request from $endpoint" );
|
|
}
|
|
|
|
$response_code = (int) wp_remote_retrieve_response_code( $request );
|
|
|
|
if ( 200 !== $response_code ) {
|
|
return $this->error( $response_code ? $response_code : 500, "Unable to request from $endpoint" );
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body( $request );
|
|
$body = json_decode( $body, true );
|
|
|
|
if ( ! is_array( $body ) ) {
|
|
return $this->error( 500, __( 'Invalid library response.', 'generateblocks' ) );
|
|
}
|
|
|
|
$data = $body['response']['data'] ?? [];
|
|
|
|
if ( ! is_array( $data ) ) {
|
|
return $this->error( 500, __( 'Invalid library response data.', 'generateblocks' ) );
|
|
}
|
|
|
|
// Cache our data.
|
|
GenerateBlocks_Libraries::set_cached_data( $data, $cache_key, $collection );
|
|
|
|
return $this->success( $data );
|
|
}
|
|
|
|
/**
|
|
* Get the expiry time of a cache.
|
|
*
|
|
* @param WP_REST_Request $request The request.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function get_cache_data( WP_REST_Request $request ): WP_REST_Response {
|
|
$id = $this->sanitize_request_string( $request->get_param( 'id' ) );
|
|
|
|
if ( ! $id ) {
|
|
return $this->error( 400, __( 'Invalid library id.', 'generateblocks' ) );
|
|
}
|
|
|
|
if ( is_null( GenerateBlocks_Libraries::get_instance()->get_one( $id ) ) ) {
|
|
return $this->error( 404, __( 'Library not found.', 'generateblocks' ) );
|
|
}
|
|
|
|
$expiration_time = get_option( '_transient_timeout_' . $id . '-patterns_0' );
|
|
|
|
if ( ! $expiration_time ) {
|
|
return $this->failed( 'no_cache' );
|
|
}
|
|
|
|
$current_time = time();
|
|
$cache_made_time = $expiration_time - GenerateBlocks_Libraries::get_cache_expiry();
|
|
$can_clear_cache = $current_time > ( $cache_made_time + 300 );
|
|
|
|
return $this->success(
|
|
[
|
|
'expiry_time_raw' => $expiration_time,
|
|
'expiry_time' => gmdate( 'Y-m-d H:i:s', $expiration_time ),
|
|
'can_clear' => $can_clear_cache,
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Clear caches for a specific collection.
|
|
*
|
|
* @param WP_REST_Request $request The request.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
public function clear_cache( WP_REST_Request $request ): WP_REST_Response {
|
|
$id = $this->sanitize_request_string( $request->get_param( 'id' ) );
|
|
|
|
if ( ! $id ) {
|
|
return $this->error( 400, __( 'Invalid library id.', 'generateblocks' ) );
|
|
}
|
|
|
|
if ( is_null( GenerateBlocks_Libraries::get_instance()->get_one( $id ) ) ) {
|
|
return $this->error( 404, __( 'Library not found.', 'generateblocks' ) );
|
|
}
|
|
|
|
GenerateBlocks_Libraries::delete_cached_data( $id . '-categories' );
|
|
GenerateBlocks_Libraries::delete_cached_data( $id . '-patterns', 'patterns' );
|
|
GenerateBlocks_Libraries::delete_cached_data( $id . '_global-style-data' );
|
|
|
|
return $this->success( [] );
|
|
}
|
|
|
|
/**
|
|
* Sanitize scalar REST request values.
|
|
*
|
|
* @param mixed $value The value to sanitize.
|
|
* @return string
|
|
*/
|
|
private function sanitize_request_string( $value ): string {
|
|
if ( ! is_scalar( $value ) ) {
|
|
return '';
|
|
}
|
|
|
|
return sanitize_text_field( (string) $value );
|
|
}
|
|
|
|
/**
|
|
* Sanitize scalar REST request URLs.
|
|
*
|
|
* @param mixed $value The value to sanitize.
|
|
* @return string
|
|
*/
|
|
private function sanitize_request_url( $value ): string {
|
|
if ( ! is_string( $value ) ) {
|
|
return '';
|
|
}
|
|
|
|
return esc_url_raw( $value, array( 'http', 'https' ) );
|
|
}
|
|
|
|
/**
|
|
* Sanitize a saved library row.
|
|
*
|
|
* @param mixed $library The library data.
|
|
* @return array
|
|
*/
|
|
private function sanitize_library_data( $library ): array {
|
|
if ( ! is_array( $library ) ) {
|
|
return [];
|
|
}
|
|
|
|
$id = $this->sanitize_request_string( $library['id'] ?? '' );
|
|
|
|
if ( ! $id ) {
|
|
return [];
|
|
}
|
|
|
|
$is_local = rest_sanitize_boolean( $library['isLocal'] ?? false );
|
|
$is_default = rest_sanitize_boolean( $library['isDefault'] ?? false );
|
|
$is_enabled = rest_sanitize_boolean( $library['isEnabled'] ?? false );
|
|
|
|
if ( ! $is_local && ! $is_default ) {
|
|
$domain = $this->sanitize_request_url( $library['domain'] ?? '' );
|
|
|
|
if ( ! $domain ) {
|
|
return [];
|
|
}
|
|
|
|
// Save all data if this is a remote library.
|
|
return [
|
|
'id' => $id,
|
|
'name' => $this->sanitize_request_string( $library['name'] ?? '' ),
|
|
'domain' => $domain,
|
|
'publicKey' => $this->sanitize_request_string( $library['publicKey'] ?? '' ),
|
|
'isEnabled' => $is_enabled,
|
|
'isDefault' => $is_default,
|
|
'isLocal' => $is_local,
|
|
];
|
|
}
|
|
|
|
// Only save the ID and status for local and default libraries.
|
|
// The rest of the data will be supplied via the PHP filter.
|
|
return [
|
|
'id' => $id,
|
|
'isEnabled' => $is_enabled,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Returns a success response.
|
|
*
|
|
* @param array $data The data.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
private function success( array $data ): WP_REST_Response {
|
|
return new WP_REST_Response(
|
|
array(
|
|
'success' => true,
|
|
'response' => array(
|
|
'data' => $data,
|
|
),
|
|
),
|
|
200
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns a success response.
|
|
*
|
|
* @param string $message The error message.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
private function failed( string $message ): WP_REST_Response {
|
|
return new WP_REST_Response(
|
|
array(
|
|
'success' => false,
|
|
'response' => $message,
|
|
),
|
|
200
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns a error response.
|
|
*
|
|
* @param int $code Error code.
|
|
* @param string $message Error message.
|
|
*
|
|
* @return WP_REST_Response
|
|
*/
|
|
private function error( int $code, string $message ): WP_REST_Response {
|
|
return new WP_REST_Response(
|
|
array(
|
|
'error' => true,
|
|
'success' => false,
|
|
'error_code' => $code,
|
|
'response' => $message,
|
|
),
|
|
$code
|
|
);
|
|
}
|
|
}
|
|
|
|
GenerateBlocks_Pattern_Library_Rest::get_instance()->init();
|