This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,508 @@
<?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();