From 21fb7541d4fe486139da159f5b7665712d158789 Mon Sep 17 00:00:00 2001 From: ashfame Date: Thu, 12 Sep 2024 18:50:28 +0400 Subject: [PATCH 01/13] register meta fields for rest api --- src/plugin/class-liberation-engine.php | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index 71020630..201ea9ca 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -8,6 +8,7 @@ class LiberationEngine { public function __construct() { add_action( 'init', array( $this, 'register_post_types' ) ); + add_action( 'init', array( $this, 'register_meta_fields' ) ); } public function register_post_types(): void { @@ -57,6 +58,41 @@ public function register_post_types(): void { ); } + public function register_meta_fields() : void { + // @TODO: change to custom post types + register_post_meta( + 'post', 'guid', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + + register_post_meta( + 'post', 'raw_title', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + + register_post_meta( + 'post', 'raw_date', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + + register_post_meta( + 'post', 'raw_content', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + } + public function is_debug_mode(): bool { return defined( 'WP_DEBUG' ) && WP_DEBUG; } From 747d32e5b89cd4991c37995bb71096a5b58c0efc Mon Sep 17 00:00:00 2001 From: ashfame Date: Thu, 12 Sep 2024 19:15:53 +0400 Subject: [PATCH 02/13] intercept rest api call --- src/plugin/class-liberation-engine.php | 49 ++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index 201ea9ca..d0114bd7 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -9,9 +9,14 @@ class LiberationEngine { public function __construct() { add_action( 'init', array( $this, 'register_post_types' ) ); add_action( 'init', array( $this, 'register_meta_fields' ) ); + + // Intercept the REST API call for moving over 'guid' and 'raw_content' to posts table + add_filter( 'rest_pre_insert_post', array( $this, 'move_meta_fields' ), 10, 2 ); + add_filter( 'rest_pre_insert_post_meta', array( $this, 'prevent_saving_moved_meta_fields' ), 10, 3 ); + add_filter( 'rest_prepare_post', array( $this, 'remove_moved_meta_fields' ), 10, 3 ); } - public function register_post_types(): void { + public function register_post_types() : void { register_post_type( self::POST_TYPE_POST, array( @@ -58,6 +63,10 @@ public function register_post_types(): void { ); } + public function is_debug_mode() : bool { + return defined( 'WP_DEBUG' ) && WP_DEBUG; + } + public function register_meta_fields() : void { // @TODO: change to custom post types register_post_meta( @@ -93,7 +102,41 @@ public function register_meta_fields() : void { ); } - public function is_debug_mode(): bool { - return defined( 'WP_DEBUG' ) && WP_DEBUG; + public function move_meta_fields( $prepared_post, $request ) { + $meta = $request->get_param( 'meta' ); + + if ( isset( $meta[ 'guid' ] ) ) { + $prepared_post->guid = $meta[ 'guid' ]; + unset( $meta[ 'guid' ] ); + } + + if ( isset( $meta[ 'raw_content' ] ) ) { + $prepared_post->post_content_filtered = $meta[ 'raw_content' ]; + unset( $meta[ 'raw_content' ] ); + } + + $request->set_param( 'meta', $meta ); + + return $prepared_post; } + + + public function prevent_saving_moved_meta_fields( $meta, $post, $request ) { + unset( $meta[ 'guid' ] ); + unset( $meta[ 'raw_content' ] ); + + return $meta; + } + + public function remove_moved_meta_fields( $response, $post, $request ) { + $data = $response->get_data(); + + $data[ 'meta' ][ 'guid' ] = $post->guid; + $data[ 'meta' ][ 'raw_content' ] = $post->post_content_filtered; + + $response->set_data( $data ); + + return $response; + } + } From 5702c4c5d42a187dcaba6e51ab39360ffa01ef29 Mon Sep 17 00:00:00 2001 From: ashfame Date: Fri, 13 Sep 2024 11:41:47 +0400 Subject: [PATCH 03/13] adapt rest api intercept for custom post types --- src/plugin/class-liberation-engine.php | 94 +++++++++++++------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index d0114bd7..ccbec69f 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -6,14 +6,40 @@ class LiberationEngine { const string POST_TYPE_PRODUCT = 'liberated_product'; const string POST_TYPE_NAVIGATION = 'liberated_navigation'; + private array $custom_post_types; // allows us to loop through constants defined above + private array $post_meta_fields = [ 'guid', 'raw_title', 'raw_date', 'raw_content' ]; + public function __construct() { - add_action( 'init', array( $this, 'register_post_types' ) ); - add_action( 'init', array( $this, 'register_meta_fields' ) ); + $this->custom_post_types = $this->get_post_type_constants(); + + add_action( 'init', array( $this, 'register_post_type_and_meta_fields' ) ); + + // Intercept the REST API call for moving over 'guid' and 'raw_content' to posts table for our custom post types + foreach ( $this->custom_post_types as $post_type ) { + add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'move_meta_fields' ), 10, 2 ); + add_filter( 'rest_prepare_' . $post_type, array( $this, 'prepare_meta_fields' ), 10, 3 ); + } + } + + /** + * This function collects values of all constants defined as POST_TYPE_ in an array. + * Useful to declare multiple meta-fields on each one of them. + * Utilising this means a new constant when added to class is enough for inheriting meta-fields definition + * + * @return array + */ + private function get_post_type_constants() : array { + $reflection = new ReflectionClass( $this ); + $constants = $reflection->getConstants(); + + return array_filter( $constants, function ( $key ) { + return str_starts_with( $key, 'POST_TYPE_' ); + }, ARRAY_FILTER_USE_KEY ); + } - // Intercept the REST API call for moving over 'guid' and 'raw_content' to posts table - add_filter( 'rest_pre_insert_post', array( $this, 'move_meta_fields' ), 10, 2 ); - add_filter( 'rest_pre_insert_post_meta', array( $this, 'prevent_saving_moved_meta_fields' ), 10, 3 ); - add_filter( 'rest_prepare_post', array( $this, 'remove_moved_meta_fields' ), 10, 3 ); + public function register_post_type_and_meta_fields() : void { + $this->register_post_types(); + $this->register_meta_fields(); } public function register_post_types() : void { @@ -26,6 +52,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_posts', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), + 'supports' => array( 'custom-fields' ), ) ); register_post_type( @@ -37,6 +64,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_pages', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), + 'supports' => array( 'custom-fields' ), ) ); register_post_type( @@ -48,6 +76,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_products', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), + 'supports' => array( 'custom-fields' ), ) ); register_post_type( @@ -59,6 +88,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_navigations', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), + 'supports' => array( 'custom-fields' ), ) ); } @@ -68,38 +98,15 @@ public function is_debug_mode() : bool { } public function register_meta_fields() : void { - // @TODO: change to custom post types - register_post_meta( - 'post', 'guid', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) - ); - - register_post_meta( - 'post', 'raw_title', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) - ); - - register_post_meta( - 'post', 'raw_date', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) - ); - - register_post_meta( - 'post', 'raw_content', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) - ); + foreach ( $this->custom_post_types as $post_type ) { + foreach ( $this->post_meta_fields as $field ) { + register_post_meta( $post_type, $field, array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) ); + } + } } public function move_meta_fields( $prepared_post, $request ) { @@ -120,15 +127,7 @@ public function move_meta_fields( $prepared_post, $request ) { return $prepared_post; } - - public function prevent_saving_moved_meta_fields( $meta, $post, $request ) { - unset( $meta[ 'guid' ] ); - unset( $meta[ 'raw_content' ] ); - - return $meta; - } - - public function remove_moved_meta_fields( $response, $post, $request ) { + public function prepare_meta_fields( $response, $post, $request ) { $data = $response->get_data(); $data[ 'meta' ][ 'guid' ] = $post->guid; @@ -138,5 +137,4 @@ public function remove_moved_meta_fields( $response, $post, $request ) { return $response; } - } From 83b5b14beb3998d800d18a6b6c2423633fbbbaff Mon Sep 17 00:00:00 2001 From: ashfame Date: Fri, 13 Sep 2024 12:19:34 +0400 Subject: [PATCH 04/13] specify what custom post type supports --- src/plugin/class-liberation-engine.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index ccbec69f..6f9c9264 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -7,6 +7,7 @@ class LiberationEngine { const string POST_TYPE_NAVIGATION = 'liberated_navigation'; private array $custom_post_types; // allows us to loop through constants defined above + private array $custom_post_types_supports = array( 'title', 'editor', 'custom-fields' ); private array $post_meta_fields = [ 'guid', 'raw_title', 'raw_date', 'raw_content' ]; public function __construct() { @@ -52,7 +53,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_posts', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), - 'supports' => array( 'custom-fields' ), + 'supports' => $this->custom_post_types_supports, ) ); register_post_type( @@ -64,7 +65,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_pages', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), - 'supports' => array( 'custom-fields' ), + 'supports' => $this->custom_post_types_supports, ) ); register_post_type( @@ -76,7 +77,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_products', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), - 'supports' => array( 'custom-fields' ), + 'supports' => $this->custom_post_types_supports, ) ); register_post_type( @@ -88,7 +89,7 @@ public function register_post_types() : void { 'rest_base' => 'liberated_navigations', 'show_ui' => self::is_debug_mode(), 'show_in_menu' => self::is_debug_mode(), - 'supports' => array( 'custom-fields' ), + 'supports' => $this->custom_post_types_supports, ) ); } From 36e998c1cedaa29c7a656fc6052a2350af197dcf Mon Sep 17 00:00:00 2001 From: ashfame Date: Fri, 13 Sep 2024 14:03:33 +0400 Subject: [PATCH 05/13] pin wp version in wp-env --- .wp-env.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.wp-env.json b/.wp-env.json index 034def2b..7f17d96f 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,5 +1,5 @@ { - "core": "WordPress/WordPress", + "core": "WordPress/WordPress#6.6.2", "phpVersion": "8.3", "env": { "tests": { From 6c85aabc3f17d6dc336111e0979b0cec81c2ea8b Mon Sep 17 00:00:00 2001 From: ashfame Date: Fri, 13 Sep 2024 14:22:45 +0400 Subject: [PATCH 06/13] disable rest api auth as a mu-plugin in wp-env --- .wp-env.json | 3 ++- .../disable-rest-api-auth.php | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/mu-plugin-disable-rest-api-auth/disable-rest-api-auth.php diff --git a/.wp-env.json b/.wp-env.json index 7f17d96f..c46e4119 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -9,6 +9,7 @@ "plugins": [ "./src/plugin" ], "mappings": { "wp-content/phpunit.xml.dist": "./phpunit.xml.dist", - "wp-content/tests": "./tests" + "wp-content/tests": "./tests", + "wp-content/mu-plugins/mu-plugin-disable-rest-api.php": "./src/mu-plugin-disable-rest-api-auth/disable-rest-api-auth.php" } } diff --git a/src/mu-plugin-disable-rest-api-auth/disable-rest-api-auth.php b/src/mu-plugin-disable-rest-api-auth/disable-rest-api-auth.php new file mode 100644 index 00000000..48ec07cc --- /dev/null +++ b/src/mu-plugin-disable-rest-api-auth/disable-rest-api-auth.php @@ -0,0 +1,7 @@ + Date: Fri, 13 Sep 2024 19:52:44 +0400 Subject: [PATCH 07/13] change args of register_post_type + do it dynamically now --- src/plugin/class-liberation-engine.php | 69 +++++++------------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index 6f9c9264..37e9c3a8 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -43,59 +43,26 @@ public function register_post_type_and_meta_fields() : void { $this->register_meta_fields(); } - public function register_post_types() : void { - register_post_type( - self::POST_TYPE_POST, - array( - 'public' => false, - 'label' => 'Liberated Post', - 'show_in_rest' => true, - 'rest_base' => 'liberated_posts', - 'show_ui' => self::is_debug_mode(), - 'show_in_menu' => self::is_debug_mode(), - 'supports' => $this->custom_post_types_supports, - ) + public function register_post_types(): void { + $args = array( + 'public' => false, + 'exclude_from_search' => true, + 'publicly_queryable' => true, // we need preview links of draft posts to function + 'show_in_rest' => true, + 'show_ui' => false, + 'show_in_menu' => false, + 'supports' => $this->custom_post_types_supports, ); - register_post_type( - self::POST_TYPE_PAGE, - array( - 'public' => false, - 'label' => 'Liberated Page', - 'show_in_rest' => true, - 'rest_base' => 'liberated_pages', - 'show_ui' => self::is_debug_mode(), - 'show_in_menu' => self::is_debug_mode(), - 'supports' => $this->custom_post_types_supports, - ) - ); - register_post_type( - self::POST_TYPE_PRODUCT, - array( - 'public' => false, - 'label' => 'Liberated Product', - 'show_in_rest' => true, - 'rest_base' => 'liberated_products', - 'show_ui' => self::is_debug_mode(), - 'show_in_menu' => self::is_debug_mode(), - 'supports' => $this->custom_post_types_supports, - ) - ); - register_post_type( - self::POST_TYPE_NAVIGATION, - array( - 'public' => false, - 'label' => 'Liberated Navigation', - 'show_in_rest' => true, - 'rest_base' => 'liberated_navigations', - 'show_ui' => self::is_debug_mode(), - 'show_in_menu' => self::is_debug_mode(), - 'supports' => $this->custom_post_types_supports, - ) - ); - } - public function is_debug_mode() : bool { - return defined( 'WP_DEBUG' ) && WP_DEBUG; + foreach ( $this->custom_post_types as $post_type ) { + $label = $post_type; + $rest_base = $post_type . 's'; // plural + + $args['label'] = $label; + $args['rest_base'] = $rest_base; + + register_post_type( $post_type, $args ); + } } public function register_meta_fields() : void { From dc0469bb76b4ffdf51b38b755017ba992184761c Mon Sep 17 00:00:00 2001 From: ashfame Date: Fri, 13 Sep 2024 19:43:44 +0400 Subject: [PATCH 08/13] update phpcs to include missing file from scanning --- phpcs.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/phpcs.xml b/phpcs.xml index 91b8f8df..5c733e47 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -5,13 +5,18 @@ - ./src/plugin/plugin.php + ./src/plugin/ ./tests/plugin/ + */vendor/* + + + + From fd0550bbe2a70a2749b140e56b47256da1c4582d Mon Sep 17 00:00:00 2001 From: ashfame Date: Fri, 13 Sep 2024 20:13:27 +0400 Subject: [PATCH 09/13] make linter happy --- src/plugin/class-liberation-engine.php | 58 ++++++++++++++++---------- src/plugin/plugin.php | 2 +- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index 37e9c3a8..3fe8cf0e 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -1,14 +1,20 @@ custom_post_types = $this->get_post_type_constants(); @@ -29,16 +35,20 @@ public function __construct() { * * @return array */ - private function get_post_type_constants() : array { + private function get_post_type_constants(): array { $reflection = new ReflectionClass( $this ); $constants = $reflection->getConstants(); - return array_filter( $constants, function ( $key ) { - return str_starts_with( $key, 'POST_TYPE_' ); - }, ARRAY_FILTER_USE_KEY ); + return array_filter( + $constants, + function ( $key ) { + return str_starts_with( $key, 'POST_TYPE_' ); + }, + ARRAY_FILTER_USE_KEY + ); } - public function register_post_type_and_meta_fields() : void { + public function register_post_type_and_meta_fields(): void { $this->register_post_types(); $this->register_meta_fields(); } @@ -65,14 +75,18 @@ public function register_post_types(): void { } } - public function register_meta_fields() : void { + public function register_meta_fields(): void { foreach ( $this->custom_post_types as $post_type ) { foreach ( $this->post_meta_fields as $field ) { - register_post_meta( $post_type, $field, array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) ); + register_post_meta( + $post_type, + $field, + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); } } } @@ -80,14 +94,14 @@ public function register_meta_fields() : void { public function move_meta_fields( $prepared_post, $request ) { $meta = $request->get_param( 'meta' ); - if ( isset( $meta[ 'guid' ] ) ) { - $prepared_post->guid = $meta[ 'guid' ]; - unset( $meta[ 'guid' ] ); + if ( isset( $meta['guid'] ) ) { + $prepared_post->guid = $meta['guid']; + unset( $meta['guid'] ); } - if ( isset( $meta[ 'raw_content' ] ) ) { - $prepared_post->post_content_filtered = $meta[ 'raw_content' ]; - unset( $meta[ 'raw_content' ] ); + if ( isset( $meta['raw_content'] ) ) { + $prepared_post->post_content_filtered = $meta['raw_content']; + unset( $meta['raw_content'] ); } $request->set_param( 'meta', $meta ); @@ -98,8 +112,8 @@ public function move_meta_fields( $prepared_post, $request ) { public function prepare_meta_fields( $response, $post, $request ) { $data = $response->get_data(); - $data[ 'meta' ][ 'guid' ] = $post->guid; - $data[ 'meta' ][ 'raw_content' ] = $post->post_content_filtered; + $data['meta']['guid'] = $post->guid; + $data['meta']['raw_content'] = $post->post_content_filtered; $response->set_data( $data ); diff --git a/src/plugin/plugin.php b/src/plugin/plugin.php index 39c4fe65..c5db539c 100644 --- a/src/plugin/plugin.php +++ b/src/plugin/plugin.php @@ -12,4 +12,4 @@ require 'class-liberation-engine.php'; -new LiberationEngine(); +new Liberation_Engine(); From 95fbec8fb4d8b0cce1a7b575db18bae88d76b880 Mon Sep 17 00:00:00 2001 From: ashfame Date: Sat, 14 Sep 2024 01:35:04 +0400 Subject: [PATCH 10/13] add promote endpoint on liberated_* post types --- src/plugin/class-liberation-engine.php | 76 ++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index 3fe8cf0e..902725bd 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -26,6 +26,9 @@ public function __construct() { add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'move_meta_fields' ), 10, 2 ); add_filter( 'rest_prepare_' . $post_type, array( $this, 'prepare_meta_fields' ), 10, 3 ); } + + // Extend REST API to add a 'promote' endpoint + add_action( 'rest_api_init', array( $this, 'register_rest_api_route' ) ); } /** @@ -91,6 +94,47 @@ public function register_meta_fields(): void { } } + public function register_rest_api_route(): void { + foreach ( $this->custom_post_types as $post_type ) { + register_rest_route( + 'wp/v2', + '/' . $post_type . '/(?P\d+)/promote', + array( + 'methods' => 'POST', + 'callback' => array( $this, 'promote_post' ), + 'permission_callback' => '__return_true', + 'args' => array( + 'id' => array( + 'validate_callback' => function ( $param, $request, $key ) { + return is_numeric( $param ); + }, + ), + ), + ) + ); + } + } + + public function promote_post( $request ): WP_Error|WP_REST_Response { + $post_id = $request['id']; + $post = get_post( $post_id ); + + if ( ! $post ) { + return new WP_Error( 'no_post', 'Invalid post ID', array( 'status' => 404 ) ); + } + + $this->promote_liberated_post_types( $post ); + + return new WP_REST_Response( + array( + 'success' => true, + 'message' => 'Post promoted successfully', + 'post_id' => $post_id, + ), + 200 + ); + } + public function move_meta_fields( $prepared_post, $request ) { $meta = $request->get_param( 'meta' ); @@ -119,4 +163,36 @@ public function prepare_meta_fields( $response, $post, $request ) { return $response; } + + public function promote_liberated_post_types( $post ): bool { + $inserted_post_id = wp_insert_post( + array( + 'post_author' => $post->post_author, + 'post_date' => $post->post_date, + 'post_date_gmt' => $post->post_date_gmt, + 'post_modified' => $post->post_modified, + 'post_modified_gmt' => $post->post_modified_gmt, + 'post_content' => $post->post_content, + 'post_title' => $post->post_title, + 'post_excerpt' => $post->post_excerpt, + 'post_status' => 'publish', + 'comment_status' => $post->comment_status, + 'ping_status' => $post->ping_status, + 'post_password' => $post->post_password, + 'post_name' => $post->post_name, + 'post_type' => str_replace( 'liberated_', '', $post->post_type ), + ) + ); + + // @TODO: handle attachments, terms etc in future + // Note: Do not need anything from postmeta. + // We should potentially use another plugin here for this purpose and call its API to do it for us. + + if ( is_wp_error( $inserted_post_id ) ) { + return false; + } + + add_post_meta( $post->ID, '_liberated_post', $inserted_post_id ); + return true; + } } From 23877d22000d93b109043f23ae49f357540c5e6e Mon Sep 17 00:00:00 2001 From: ashfame Date: Sat, 14 Sep 2024 01:40:16 +0400 Subject: [PATCH 11/13] show final post id in response for promote endpoint --- src/plugin/class-liberation-engine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php index 902725bd..33cd125a 100644 --- a/src/plugin/class-liberation-engine.php +++ b/src/plugin/class-liberation-engine.php @@ -129,7 +129,7 @@ public function promote_post( $request ): WP_Error|WP_REST_Response { array( 'success' => true, 'message' => 'Post promoted successfully', - 'post_id' => $post_id, + 'post_id' => get_post_meta( $post_id, '_liberated_post', true ), ), 200 ); From f7061d66dfdc035f0c0aaf885c1015238548e7df Mon Sep 17 00:00:00 2001 From: ashfame Date: Sat, 14 Sep 2024 02:33:46 +0400 Subject: [PATCH 12/13] refactored into multiple classes following 'Single-Responsibility Principle' --- src/plugin/class-liberation-engine.php | 198 ----------------------- src/plugin/class-meta-fields-manager.php | 70 ++++++++ src/plugin/class-post-type-manager.php | 64 ++++++++ src/plugin/class-rest-api-extender.php | 86 ++++++++++ src/plugin/plugin.php | 12 +- 5 files changed, 230 insertions(+), 200 deletions(-) delete mode 100644 src/plugin/class-liberation-engine.php create mode 100644 src/plugin/class-meta-fields-manager.php create mode 100644 src/plugin/class-post-type-manager.php create mode 100644 src/plugin/class-rest-api-extender.php diff --git a/src/plugin/class-liberation-engine.php b/src/plugin/class-liberation-engine.php deleted file mode 100644 index 33cd125a..00000000 --- a/src/plugin/class-liberation-engine.php +++ /dev/null @@ -1,198 +0,0 @@ -custom_post_types = $this->get_post_type_constants(); - - add_action( 'init', array( $this, 'register_post_type_and_meta_fields' ) ); - - // Intercept the REST API call for moving over 'guid' and 'raw_content' to posts table for our custom post types - foreach ( $this->custom_post_types as $post_type ) { - add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'move_meta_fields' ), 10, 2 ); - add_filter( 'rest_prepare_' . $post_type, array( $this, 'prepare_meta_fields' ), 10, 3 ); - } - - // Extend REST API to add a 'promote' endpoint - add_action( 'rest_api_init', array( $this, 'register_rest_api_route' ) ); - } - - /** - * This function collects values of all constants defined as POST_TYPE_ in an array. - * Useful to declare multiple meta-fields on each one of them. - * Utilising this means a new constant when added to class is enough for inheriting meta-fields definition - * - * @return array - */ - private function get_post_type_constants(): array { - $reflection = new ReflectionClass( $this ); - $constants = $reflection->getConstants(); - - return array_filter( - $constants, - function ( $key ) { - return str_starts_with( $key, 'POST_TYPE_' ); - }, - ARRAY_FILTER_USE_KEY - ); - } - - public function register_post_type_and_meta_fields(): void { - $this->register_post_types(); - $this->register_meta_fields(); - } - - public function register_post_types(): void { - $args = array( - 'public' => false, - 'exclude_from_search' => true, - 'publicly_queryable' => true, // we need preview links of draft posts to function - 'show_in_rest' => true, - 'show_ui' => false, - 'show_in_menu' => false, - 'supports' => $this->custom_post_types_supports, - ); - - foreach ( $this->custom_post_types as $post_type ) { - $label = $post_type; - $rest_base = $post_type . 's'; // plural - - $args['label'] = $label; - $args['rest_base'] = $rest_base; - - register_post_type( $post_type, $args ); - } - } - - public function register_meta_fields(): void { - foreach ( $this->custom_post_types as $post_type ) { - foreach ( $this->post_meta_fields as $field ) { - register_post_meta( - $post_type, - $field, - array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) - ); - } - } - } - - public function register_rest_api_route(): void { - foreach ( $this->custom_post_types as $post_type ) { - register_rest_route( - 'wp/v2', - '/' . $post_type . '/(?P\d+)/promote', - array( - 'methods' => 'POST', - 'callback' => array( $this, 'promote_post' ), - 'permission_callback' => '__return_true', - 'args' => array( - 'id' => array( - 'validate_callback' => function ( $param, $request, $key ) { - return is_numeric( $param ); - }, - ), - ), - ) - ); - } - } - - public function promote_post( $request ): WP_Error|WP_REST_Response { - $post_id = $request['id']; - $post = get_post( $post_id ); - - if ( ! $post ) { - return new WP_Error( 'no_post', 'Invalid post ID', array( 'status' => 404 ) ); - } - - $this->promote_liberated_post_types( $post ); - - return new WP_REST_Response( - array( - 'success' => true, - 'message' => 'Post promoted successfully', - 'post_id' => get_post_meta( $post_id, '_liberated_post', true ), - ), - 200 - ); - } - - public function move_meta_fields( $prepared_post, $request ) { - $meta = $request->get_param( 'meta' ); - - if ( isset( $meta['guid'] ) ) { - $prepared_post->guid = $meta['guid']; - unset( $meta['guid'] ); - } - - if ( isset( $meta['raw_content'] ) ) { - $prepared_post->post_content_filtered = $meta['raw_content']; - unset( $meta['raw_content'] ); - } - - $request->set_param( 'meta', $meta ); - - return $prepared_post; - } - - public function prepare_meta_fields( $response, $post, $request ) { - $data = $response->get_data(); - - $data['meta']['guid'] = $post->guid; - $data['meta']['raw_content'] = $post->post_content_filtered; - - $response->set_data( $data ); - - return $response; - } - - public function promote_liberated_post_types( $post ): bool { - $inserted_post_id = wp_insert_post( - array( - 'post_author' => $post->post_author, - 'post_date' => $post->post_date, - 'post_date_gmt' => $post->post_date_gmt, - 'post_modified' => $post->post_modified, - 'post_modified_gmt' => $post->post_modified_gmt, - 'post_content' => $post->post_content, - 'post_title' => $post->post_title, - 'post_excerpt' => $post->post_excerpt, - 'post_status' => 'publish', - 'comment_status' => $post->comment_status, - 'ping_status' => $post->ping_status, - 'post_password' => $post->post_password, - 'post_name' => $post->post_name, - 'post_type' => str_replace( 'liberated_', '', $post->post_type ), - ) - ); - - // @TODO: handle attachments, terms etc in future - // Note: Do not need anything from postmeta. - // We should potentially use another plugin here for this purpose and call its API to do it for us. - - if ( is_wp_error( $inserted_post_id ) ) { - return false; - } - - add_post_meta( $post->ID, '_liberated_post', $inserted_post_id ); - return true; - } -} diff --git a/src/plugin/class-meta-fields-manager.php b/src/plugin/class-meta-fields-manager.php new file mode 100644 index 00000000..3125cdb0 --- /dev/null +++ b/src/plugin/class-meta-fields-manager.php @@ -0,0 +1,70 @@ +custom_post_types = $custom_post_types; + + add_action( 'init', array( $this, 'register_meta_fields' ) ); + + // Intercept the REST API call for moving over 'guid' and 'raw_content' to posts table for our custom post types + foreach ( $this->custom_post_types as $post_type ) { + add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'move_meta_fields' ), 10, 2 ); + add_filter( 'rest_prepare_' . $post_type, array( $this, 'prepare_meta_fields' ), 10, 3 ); + } + } + + public function register_meta_fields(): void { + foreach ( $this->custom_post_types as $post_type ) { + foreach ( $this->post_meta_fields as $field ) { + register_post_meta( + $post_type, + $field, + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + } + } + } + + /** + * This function moves some of the meta fields to the post object before saving + * + * @param object $prepared_post Post object that is going to be saved. + * @param WP_REST_Request $request REST API request object. + * @return object + */ + public function move_meta_fields( $prepared_post, WP_REST_Request $request ) { + $meta = $request->get_param( 'meta' ); + + if ( isset( $meta['guid'] ) ) { + $prepared_post->guid = $meta['guid']; + unset( $meta['guid'] ); + } + + if ( isset( $meta['raw_content'] ) ) { + $prepared_post->post_content_filtered = $meta['raw_content']; + unset( $meta['raw_content'] ); + } + + $request->set_param( 'meta', $meta ); + + return $prepared_post; + } + + public function prepare_meta_fields( $response, $post, $request ) { + $data = $response->get_data(); + + $data['meta']['guid'] = $post->guid; + $data['meta']['raw_content'] = $post->post_content_filtered; + + $response->set_data( $data ); + + return $response; + } +} diff --git a/src/plugin/class-post-type-manager.php b/src/plugin/class-post-type-manager.php new file mode 100644 index 00000000..137a5acb --- /dev/null +++ b/src/plugin/class-post-type-manager.php @@ -0,0 +1,64 @@ +custom_post_types = $this->get_post_type_constants(); + + add_action( 'init', array( $this, 'register_post_types' ) ); + } + + /** + * This function collects values of all constants defined as POST_TYPE_ in an array. + * Useful to declare multiple meta-fields on each one of them. + * Utilising this means a new constant when added to class is enough for inheriting meta-fields definition + * + * @return array + */ + private function get_post_type_constants(): array { + $reflection = new ReflectionClass( $this ); + $constants = $reflection->getConstants(); + + return array_filter( + $constants, + function ( $key ) { + return str_starts_with( $key, 'POST_TYPE_' ); + }, + ARRAY_FILTER_USE_KEY + ); + } + + public function register_post_types(): void { + $args = array( + 'public' => false, + 'exclude_from_search' => true, + 'publicly_queryable' => true, + 'show_in_rest' => true, + 'show_ui' => false, + 'show_in_menu' => false, + 'supports' => $this->custom_post_types_supports, + ); + + foreach ( $this->custom_post_types as $post_type ) { + $args['label'] = $post_type; + $args['rest_base'] = $post_type . 's'; + register_post_type( $post_type, $args ); + } + } + + public function get_custom_post_types(): array { + return $this->custom_post_types; + } +} diff --git a/src/plugin/class-rest-api-extender.php b/src/plugin/class-rest-api-extender.php new file mode 100644 index 00000000..a17c30c8 --- /dev/null +++ b/src/plugin/class-rest-api-extender.php @@ -0,0 +1,86 @@ +custom_post_types = $custom_post_types; + + add_action( 'rest_api_init', array( $this, 'register_route' ) ); + } + + public function register_route(): void { + foreach ( $this->custom_post_types as $post_type ) { + register_rest_route( + 'wp/v2', + '/' . $post_type . 's/(?P\d+)/promote', + array( + 'methods' => 'POST', + 'callback' => array( $this, 'promote_post' ), + 'permission_callback' => '__return_true', + 'args' => array( + 'id' => array( + 'validate_callback' => function ( $param, $request, $key ) { + return is_numeric( $param ); + }, + ), + ), + ) + ); + } + } + + public function promote_post( $request ): WP_Error|WP_REST_Response { + $post_id = $request['id']; + $post = get_post( $post_id ); + + if ( ! $post ) { + return new WP_Error( 'no_post', 'Invalid post ID', array( 'status' => 404 ) ); + } + + if ( $this->promote_liberated_post_types( $post ) ) { + return new WP_REST_Response( + array( + 'success' => true, + 'message' => 'Post promoted successfully', + 'post_id' => get_post_meta( $post_id, '_liberated_post', true ), + ), + 200 + ); + } else { + return new WP_Error( 'error', 'Could not promote post', array( 'status' => 500 ) ); + } + } + + public function promote_liberated_post_types( $post ): bool { + $inserted_post_id = wp_insert_post( + array( + 'post_author' => $post->post_author, + 'post_date' => $post->post_date, + 'post_date_gmt' => $post->post_date_gmt, + 'post_modified' => $post->post_modified, + 'post_modified_gmt' => $post->post_modified_gmt, + 'post_content' => $post->post_content, + 'post_title' => $post->post_title, + 'post_excerpt' => $post->post_excerpt, + 'post_status' => 'publish', + 'comment_status' => $post->comment_status, + 'ping_status' => $post->ping_status, + 'post_password' => $post->post_password, + 'post_name' => $post->post_name, + 'post_type' => str_replace( 'liberated_', '', $post->post_type ), + ) + ); + + // @TODO: handle attachments, terms etc in future + // Note: Do not need anything from postmeta. + // We should potentially use another plugin here for this purpose and call its API to do it for us. + + if ( is_wp_error( $inserted_post_id ) ) { + return false; + } + + add_post_meta( $post->ID, '_liberated_post', $inserted_post_id ); + return true; + } +} diff --git a/src/plugin/plugin.php b/src/plugin/plugin.php index c5db539c..86f5abbf 100644 --- a/src/plugin/plugin.php +++ b/src/plugin/plugin.php @@ -10,6 +10,14 @@ exit; } -require 'class-liberation-engine.php'; +require 'class-post-type-manager.php'; +require 'class-meta-fields-manager.php'; +require 'class-rest-api-extender.php'; -new Liberation_Engine(); +( function () { + $post_type_manager = new Post_Type_Manager(); + $custom_post_types = $post_type_manager->get_custom_post_types(); + + new Meta_Fields_Manager( $custom_post_types ); + new Rest_API_Extender( $custom_post_types ); +} )(); From a3dc7989ae831ab0715d44e417f0dd53669603ba Mon Sep 17 00:00:00 2001 From: ashfame Date: Tue, 17 Sep 2024 13:27:26 +0400 Subject: [PATCH 13/13] improve type hinting --- src/plugin/class-meta-fields-manager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/class-meta-fields-manager.php b/src/plugin/class-meta-fields-manager.php index 3125cdb0..499396ff 100644 --- a/src/plugin/class-meta-fields-manager.php +++ b/src/plugin/class-meta-fields-manager.php @@ -39,7 +39,7 @@ public function register_meta_fields(): void { * @param WP_REST_Request $request REST API request object. * @return object */ - public function move_meta_fields( $prepared_post, WP_REST_Request $request ) { + public function move_meta_fields( object $prepared_post, WP_REST_Request $request ): object { $meta = $request->get_param( 'meta' ); if ( isset( $meta['guid'] ) ) { @@ -57,7 +57,7 @@ public function move_meta_fields( $prepared_post, WP_REST_Request $request ) { return $prepared_post; } - public function prepare_meta_fields( $response, $post, $request ) { + public function prepare_meta_fields( WP_REST_Response $response, object $post, WP_REST_Request $request ): WP_REST_Response { $data = $response->get_data(); $data['meta']['guid'] = $post->guid;