Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for building the UX to liberate a simple post #36

Merged
merged 13 commits into from
Sep 17, 2024
Merged
5 changes: 3 additions & 2 deletions .wp-env.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"core": "WordPress/WordPress",
"core": "WordPress/WordPress#6.6.2",
"phpVersion": "8.3",
"env": {
"tests": {
Expand All @@ -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"
}
}
7 changes: 6 additions & 1 deletion phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
<arg name="colors"/>
<arg name="extensions" value="php,css"/>

<file>./src/plugin/plugin.php</file>
<file>./src/plugin/</file>
<file>./tests/plugin/</file>

<exclude-pattern>*/vendor/*</exclude-pattern>

<rule ref="WordPress">
<exclude name="Squiz.Commenting.ClassComment.Missing"/>
<exclude name="Squiz.Commenting.FileComment.Missing"/>
<exclude name="Squiz.Commenting.FileComment.MissingPackageTag"/>
<exclude name="Squiz.Commenting.FunctionComment.Missing"/>
<exclude name="Squiz.Commenting.VariableComment.Missing"/>
<exclude name="Squiz.Commenting.InlineComment.InvalidEndChar"/>
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed"/>
</rule>
</ruleset>
7 changes: 7 additions & 0 deletions src/mu-plugin-disable-rest-api-auth/disable-rest-api-auth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
/**
* This is loaded as a mu-plugin in wp-env for development
*/

add_filter( 'rest_authentication_errors', '__return_true' );
add_filter( 'determine_current_user', function () { return 1; }, 99999 );
63 changes: 0 additions & 63 deletions src/plugin/class-liberation-engine.php

This file was deleted.

70 changes: 70 additions & 0 deletions src/plugin/class-meta-fields-manager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

class Meta_Fields_Manager {
private array $custom_post_types;
private array $post_meta_fields = array( 'guid', 'raw_title', 'raw_date', 'raw_content' );

public function __construct( $custom_post_types ) {
$this->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( object $prepared_post, WP_REST_Request $request ): object {
$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( WP_REST_Response $response, object $post, WP_REST_Request $request ): WP_REST_Response {
$data = $response->get_data();

$data['meta']['guid'] = $post->guid;
$data['meta']['raw_content'] = $post->post_content_filtered;

$response->set_data( $data );

return $response;
}
}
64 changes: 64 additions & 0 deletions src/plugin/class-post-type-manager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

class Post_Type_Manager {
const string POST_TYPE_POST = 'liberated_post';
const string POST_TYPE_PAGE = 'liberated_page';
const string POST_TYPE_PRODUCT = 'liberated_product';
const string POST_TYPE_NAVIGATION = 'liberated_navigation';

/**
* Allows us to loop through constants defined above
*
* @var array $custom_post_types
*/
private array $custom_post_types;
private array $custom_post_types_supports = array( 'title', 'editor', 'custom-fields' );

public function __construct() {
$this->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;
}
}
86 changes: 86 additions & 0 deletions src/plugin/class-rest-api-extender.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

class Rest_API_Extender {
private array $custom_post_types;

public function __construct( $custom_post_types ) {
$this->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<id>\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;
}
}
12 changes: 10 additions & 2 deletions src/plugin/plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 LiberationEngine();
( 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 );
} )();
Loading