Skip to content

Commit

Permalink
feat: template cloud integration [ref Codeinwp/templates-cloud#98]
Browse files Browse the repository at this point in the history
  • Loading branch information
abaicus committed Nov 22, 2024
1 parent 9781919 commit 99042ae
Show file tree
Hide file tree
Showing 12 changed files with 1,303 additions and 8 deletions.
10 changes: 6 additions & 4 deletions inc/class-main.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function init() {
}

add_filter( 'otter_blocks_about_us_metadata', array( $this, 'about_page' ) );

add_action( 'parse_query', array( $this, 'pagination_support' ) );
}

Expand Down Expand Up @@ -83,6 +83,8 @@ public function autoload_classes() {
'\ThemeIsle\GutenbergBlocks\Integration\Form_Email',
'\ThemeIsle\GutenbergBlocks\Server\Form_Server',
'\ThemeIsle\GutenbergBlocks\Server\Prompt_Server',
'\ThemeIsle\GutenbergBlocks\Template_Cloud',
'\ThemeIsle\GutenbergBlocks\Server\Template_Cloud_Server',
);

$classnames = apply_filters( 'otter_blocks_autoloader', $classnames );
Expand Down Expand Up @@ -532,13 +534,13 @@ public function generate_svg_attachment_metadata( $metadata, $attachment_id ) {

/**
* Disable canonical redirect to make Posts pagination feature work.
*
*
* @param \WP_Query $request The query object.
*/
public function pagination_support( $request ) {
if (
true === $request->is_singular &&
-1 === $request->current_post &&
true === $request->is_singular &&
-1 === $request->current_post &&
true === $request->is_paged &&
(
! empty( $request->query_vars['page'] ) ||
Expand Down
342 changes: 342 additions & 0 deletions inc/class-template-cloud.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
<?php
/**
* Template Cloud.
*
* @package ThemeIsle
*/

namespace ThemeIsle\GutenbergBlocks;

/**
* Class Template Cloud
*/
class Template_Cloud {
const SOURCES_SETTING_KEY = 'themeisle_template_cloud_sources';

const API_ENDPOINT_SUFFIX = 'ti-template-cloud/v1/patterns';

/**
* Initialize the module.
*
* @return void
*/
public function instance() {
add_action( 'init', array( $this, 'register_cloud_resources' ) );
}

/**
* Register categories and patterns.
*
* @return void
*/
public function register_cloud_resources() {
$this->register_pattern_categories();
$this->register_patterns();
}

/**
* Register the pattern categories.
*
* @return void
*/
private function register_pattern_categories() {
$sources = $this->get_pattern_sources();

if ( empty( $sources ) ) {
return;
}

foreach ( $sources as $source ) {
$slug = $this->slug_from_name( $source['name'] );

if ( ! \WP_Block_Pattern_Categories_Registry::get_instance()->is_registered( $slug ) ) {
register_block_pattern_category( $slug, [ 'label' => $source['name'] ] );
}
}
}

/**
* Register the patterns.
*
* @return void
*/
private function register_patterns() {
$cloud_data = $this->get_cloud_data();

if ( empty( $cloud_data ) ) {
return;
}

$all_patterns = [];

foreach ( $cloud_data as $source_data ) {
$patterns_for_source = [];

if ( ! is_array( $source_data ) || ! isset( $source_data['patterns'], $source_data['category'] ) ) {
continue;
}

$patterns = $source_data['patterns'];
$category = $source_data['category'];

// Make sure we don't have duplicates.
foreach ( $patterns as $pattern ) {
if ( isset( $patterns_for_source[ $pattern['id'] ] ) ) {
continue;
}

$pattern['categories'] = [ 'otter-blocks', 'otter-blocks-tc', $category ];

$patterns_for_source[ $pattern['id'] ] = $pattern;
}

$all_patterns = array_merge( $all_patterns, $patterns_for_source );
}

foreach ( $all_patterns as $pattern ) {
if ( ! isset( $pattern['slug'] ) ) {
continue;
}


register_block_pattern(
'otter-blocks/' . $pattern['slug'],
$pattern
);
}
}

/**
* Get all the cloud data for each source.
*
* @return array|array[]
*/
private function get_cloud_data() {
$sources = self::get_pattern_sources();

if ( empty( $sources ) ) {
return [];
}

return array_map(
function ( $source ) {
return [
'category' => $this->slug_from_name( $source['name'] ),
'patterns' => $this->get_patterns_for_key( $source['key'] ),
];
},
$sources
);
}

/**
* Get patterns for a certain access key.
*
* @param string $access_key The access key.
*
* @return array
*/
private function get_patterns_for_key( $access_key ) {
$patterns = get_transient( self::get_cache_key( $access_key ) );

if ( ! $patterns ) {
self::sync_sources();
}

$patterns = get_transient( self::get_cache_key( $access_key ) );

if ( ! $patterns ) {
return [];
}

$patterns = json_decode( $patterns, true );

return is_array( $patterns ) ? $patterns : array();
}

/**
* Get the slug from a name.
*
* @param string $name The name to slugify.
*
* @return string
*/
private function slug_from_name( $name ) {
return 'ti-tc-' . sanitize_key( str_replace( ' ', '-', $name ) );
}

/**
* Get the pattern sources.
*
* @return array
*/
public static function get_pattern_sources() {
return get_option( self::SOURCES_SETTING_KEY, [] );
}

/**
* Save the pattern sources.
*
* @param array $new_sources The new sources.
*
* @return bool
*/
public static function save_pattern_sources( $new_sources ) {
return update_option( self::SOURCES_SETTING_KEY, array_values( $new_sources ) );
}

/**
* Get the cache key for the patterns.
*
* @param string $key The key to use for connection.
*
* @return string
*/
public static function get_cache_key( $key ) {
return 'ti_tc_patterns_' . $key;
}

/**
* Save patterns for a certain access key.
*
* @param string $access_key The access key.
* @param array $patterns The patterns to save.
*
* @return bool
*/
public static function save_patterns_for_key( $access_key, $patterns ) {
return set_transient( self::get_cache_key( $access_key ), wp_json_encode( $patterns ), DAY_IN_SECONDS );
}

/**
* Delete patterns for a certain access key.
*
* @param string $access_key The access key.
*
* @return bool
*/
public static function delete_patterns_by_key( $access_key ) {
return delete_transient( self::get_cache_key( $access_key ) );
}

/**
* Sync sources.
*/
public static function sync_sources() {
$sources = self::get_pattern_sources();

if ( empty( $sources ) ) {
return [ 'success' => true ];
}

$errors = array();



foreach ( $sources as $source ) {
$url = trailingslashit( $source['url'] ) . self::API_ENDPOINT_SUFFIX;
$args = array(
'sslverify' => false,
'headers' => array(
'X-API-KEY' => $source['key'],
),
);

if ( function_exists( 'vip_safe_wp_remote_get' ) ) {
$response = vip_safe_wp_remote_get( $url, '', 3, 1, 20, $args );
} else {
$response = wp_remote_get( $url, $args ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
}

if ( is_wp_error( $response ) ) {
$errors[] = sprintf(
/* translators: 1: source name, 2: error message */
__( 'Error with %1$s: %2$s', 'otter-blocks' ),
$source['name'],
$response->get_error_message()
);

continue;
}

$code = wp_remote_retrieve_response_code( $response );

if ( 200 !== $code ) {
$errors[] = sprintf(
/* translators: 1: source name, 2: response code */
__( 'Error with %1$s: Invalid response code %2$s', 'otter-blocks' ),
$source['name'],
$code
);

continue;
}

$body = wp_remote_retrieve_body( $response );

if ( empty( $body ) ) {
$errors[] = sprintf(
/* translators: %s: source name */
__( 'Error with %s: Empty response', 'otter-blocks' ),
$source['name']
);

continue;
}

$decoded_body = json_decode( $body, true );

if ( ! is_array( $decoded_body ) ) {
$errors[] = sprintf(
/* translators: %s: source name */
__( 'Error with %s: Invalid response', 'otter-blocks' ),
$source['name']
);

continue;
}

if ( ! isset( $decoded_body['success'], $decoded_body['data'], $decoded_body['key_name'] ) || ! $decoded_body['success'] ) {
$errors[] = sprintf(
/* translators: %s: source name */
__( 'Error with %s: No patterns found', 'otter-blocks' ),
$source['name']
);

continue;
}

// Update key name if that has changed.
if ( $decoded_body['key_name'] !== $source['name'] ) {
self::update_source_name( $source['key'], $decoded_body['key_name'] );
}

self::save_patterns_for_key( $source['key'], $decoded_body['data'] );
}

return [
'success' => true,
'errors' => $errors,
];
}

/**
* Update Source Name on sync.
*
* @param string $key The key to use for connection.
* @param string $new_name The new name to use.
*
* @return void
*/
public static function update_source_name( $key, $new_name ) {
$sources = self::get_pattern_sources();

foreach ( $sources as $idx => $source ) {
if ( $source['key'] === $key ) {
$sources[ $idx ]['name'] = $new_name;
}
}

self::save_pattern_sources( $sources );
}
}
Loading

0 comments on commit 99042ae

Please sign in to comment.