Files
Hi-School-Slide-Manager/includes/class-hssm-rest-api.php
T

532 lines
17 KiB
PHP

<?php
/**
* HSSM REST API Class
*
* Handles REST API endpoints for companion sites to fetch slides
*
* @package Hi_School_Slide_Manager
* @since 1.0.0
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit( 'Direct access forbidden.' );
}
/**
* HSSM REST API Class
*
* Custom REST API endpoints for slide distribution to companion sites
*/
class HSSM_REST_API {
/**
* API namespace
*
* @since 1.0.0
* @var string
*/
const NAMESPACE = 'hssm/v1';
/**
* Constructor
*
* @since 1.0.0
*/
public function __construct() {
// Hook is registered in main class
}
/**
* Register REST API routes
*
* @since 1.0.0
*/
public function register_routes() {
// Get slides for a specific site
register_rest_route( self::NAMESPACE, '/slides/(?P<site>[a-zA-Z0-9-]+)', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_slides_for_site' ),
'permission_callback' => array( $this, 'check_api_permissions' ),
'args' => array(
'site' => array(
'required' => true,
'validate_callback' => array( $this, 'validate_site_slug' ),
'sanitize_callback' => 'sanitize_text_field',
'description' => 'Site slug (hi-school, one-stop, ace)',
),
'date' => array(
'required' => false,
'validate_callback' => array( $this, 'validate_date' ),
'sanitize_callback' => 'sanitize_text_field',
'description' => 'Date to get slides for (YYYY-MM-DD format)',
),
'limit' => array(
'required' => false,
'default' => 10,
'validate_callback' => array( $this, 'validate_limit' ),
'sanitize_callback' => 'absint',
'description' => 'Maximum number of slides to return (1-50)',
),
'status' => array(
'required' => false,
'default' => 'active',
'validate_callback' => array( $this, 'validate_status' ),
'sanitize_callback' => 'sanitize_text_field',
'description' => 'Slide status: active, scheduled, all',
),
),
) );
// Get all available sites
register_rest_route( self::NAMESPACE, '/sites', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_available_sites' ),
'permission_callback' => array( $this, 'check_api_permissions' ),
) );
// Get slide by ID
register_rest_route( self::NAMESPACE, '/slide/(?P<id>\d+)', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_slide_by_id' ),
'permission_callback' => array( $this, 'check_api_permissions' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => array( $this, 'validate_slide_id' ),
'sanitize_callback' => 'absint',
'description' => 'Slide ID',
),
),
) );
// Health check endpoint
register_rest_route( self::NAMESPACE, '/health', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'health_check' ),
'permission_callback' => '__return_true', // Public endpoint
) );
}
/**
* Get slides for a specific site
*
* @since 1.0.0
* @param WP_REST_Request $request Request object
* @return WP_REST_Response|WP_Error Response object
*/
public function get_slides_for_site( $request ) {
$site_slug = $request->get_param( 'site' );
$date = $request->get_param( 'date' );
$limit = $request->get_param( 'limit' );
$status = $request->get_param( 'status' );
// Get the site term
$site_term = get_term_by( 'slug', $site_slug, 'hssm_site' );
if ( ! $site_term ) {
return new WP_Error( 'invalid_site', 'Invalid site slug', array( 'status' => 404 ) );
}
// Build query args
$query_args = array(
'post_type' => 'hssm_slide',
'posts_per_page' => $limit,
'post_status' => 'publish',
'meta_key' => '_hssm_order',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'tax_query' => array(
array(
'taxonomy' => 'hssm_site',
'field' => 'term_id',
'terms' => $site_term->term_id,
),
),
);
// Add date filtering if specified
if ( $date ) {
$target_date = $date . ' 23:59:59';
$query_args['meta_query'] = array(
'relation' => 'AND',
array(
'relation' => 'OR',
array(
'key' => '_hssm_start_date',
'value' => '',
'compare' => '='
),
array(
'key' => '_hssm_start_date',
'value' => $target_date,
'compare' => '<='
),
array(
'key' => '_hssm_start_date',
'compare' => 'NOT EXISTS'
)
),
array(
'relation' => 'OR',
array(
'key' => '_hssm_end_date',
'value' => '',
'compare' => '='
),
array(
'key' => '_hssm_end_date',
'value' => $target_date,
'compare' => '>='
),
array(
'key' => '_hssm_end_date',
'compare' => 'NOT EXISTS'
)
)
);
}
// Handle status filtering
if ( $status === 'scheduled' ) {
$current_time = current_time( 'mysql' );
$query_args['meta_query'] = array(
array(
'key' => '_hssm_start_date',
'value' => $current_time,
'compare' => '>'
)
);
} elseif ( $status === 'all' ) {
$query_args['post_status'] = array( 'publish', 'future', 'draft' );
}
$slides_query = new WP_Query( $query_args );
$slides_data = array();
if ( $slides_query->have_posts() ) {
while ( $slides_query->have_posts() ) {
$slides_query->the_post();
$slides_data[] = $this->format_slide_data( get_post() );
}
wp_reset_postdata();
}
$response_data = array(
'slides' => $slides_data,
'site' => array(
'slug' => $site_slug,
'name' => $site_term->name,
'description' => $site_term->description,
),
'meta' => array(
'total_slides' => count( $slides_data ),
'query_date' => $date ?: current_time( 'Y-m-d' ),
'status_filter' => $status,
'timestamp' => current_time( 'c' ),
'cache_duration' => get_option( 'hssm_cache_duration', 3600 ),
),
);
return rest_ensure_response( $response_data );
}
/**
* Get all available sites
*
* @since 1.0.0
* @param WP_REST_Request $request Request object
* @return WP_REST_Response Response object
*/
public function get_available_sites( $request ) {
$sites = get_terms( array(
'taxonomy' => 'hssm_site',
'hide_empty' => false,
) );
$sites_data = array();
if ( ! is_wp_error( $sites ) ) {
foreach ( $sites as $site ) {
$sites_data[] = array(
'slug' => $site->slug,
'name' => $site->name,
'description' => $site->description,
'slide_count' => $this->get_site_slide_count( $site->term_id ),
);
}
}
return rest_ensure_response( array(
'sites' => $sites_data,
'total' => count( $sites_data ),
) );
}
/**
* Get slide by ID
*
* @since 1.0.0
* @param WP_REST_Request $request Request object
* @return WP_REST_Response|WP_Error Response object
*/
public function get_slide_by_id( $request ) {
$slide_id = $request->get_param( 'id' );
$slide = get_post( $slide_id );
if ( ! $slide || $slide->post_type !== 'hssm_slide' ) {
return new WP_Error( 'slide_not_found', 'Slide not found', array( 'status' => 404 ) );
}
return rest_ensure_response( $this->format_slide_data( $slide ) );
}
/**
* Health check endpoint
*
* @since 1.0.0
* @param WP_REST_Request $request Request object
* @return WP_REST_Response Response object
*/
public function health_check( $request ) {
return rest_ensure_response( array(
'status' => 'healthy',
'plugin' => 'Hi-School Slide Manager',
'version' => HSSM_VERSION,
'timestamp' => current_time( 'c' ),
'endpoints' => array(
'slides' => rest_url( self::NAMESPACE . '/slides/{site}' ),
'sites' => rest_url( self::NAMESPACE . '/sites' ),
'slide' => rest_url( self::NAMESPACE . '/slide/{id}' ),
),
) );
}
/**
* Check API permissions
*
* @since 1.0.0
* @param WP_REST_Request $request Request object
* @return bool|WP_Error Permission check result
*/
public function check_api_permissions( $request ) {
// Get the API key from header or query parameter
$api_key = $request->get_header( 'X-HSSM-API-Key' );
if ( ! $api_key ) {
$api_key = $request->get_param( 'api_key' );
}
if ( ! $api_key ) {
return new WP_Error( 'missing_api_key', 'API key required', array( 'status' => 401 ) );
}
// Validate API key
$valid_keys = get_option( 'hssm_api_keys', array() );
if ( ! in_array( $api_key, $valid_keys ) ) {
return new WP_Error( 'invalid_api_key', 'Invalid API key', array( 'status' => 403 ) );
}
return true;
}
/**
* Format slide data for API response
*
* @since 1.0.0
* @param WP_Post $slide Slide post object
* @return array Formatted slide data
*/
private function format_slide_data( $slide ) {
$slide_data = array(
'id' => $slide->ID,
'title' => $slide->post_title,
'subtitle' => get_post_meta( $slide->ID, '_hssm_subtitle', true ),
'content' => $slide->post_content,
'excerpt' => $slide->post_excerpt,
'buttons' => get_post_meta( $slide->ID, '_hssm_buttons', true ) ?: array(),
'order' => (int) get_post_meta( $slide->ID, '_hssm_order', true ),
'background_image' => $this->get_slide_background_image( $slide->ID ),
'schedule' => array(
'start_date' => get_post_meta( $slide->ID, '_hssm_start_date', true ),
'end_date' => get_post_meta( $slide->ID, '_hssm_end_date', true ),
'auto_publish' => get_post_meta( $slide->ID, '_hssm_auto_publish', true ) === '1',
'auto_unpublish' => get_post_meta( $slide->ID, '_hssm_auto_unpublish', true ) === '1',
),
'target_sites' => $this->get_slide_target_sites( $slide->ID ),
'client' => $this->get_slide_client( $slide->ID ),
'status' => $slide->post_status,
'created' => $slide->post_date,
'modified' => $slide->post_modified,
);
return $slide_data;
}
/**
* Get slide background image data
*
* @since 1.0.0
* @param int $slide_id Slide ID
* @return array|null Background image data
*/
private function get_slide_background_image( $slide_id ) {
$thumbnail_id = get_post_thumbnail_id( $slide_id );
if ( ! $thumbnail_id ) {
return null;
}
$image_data = wp_get_attachment_image_src( $thumbnail_id, 'full' );
if ( ! $image_data ) {
return null;
}
return array(
'id' => $thumbnail_id,
'url' => $image_data[0],
'width' => $image_data[1],
'height' => $image_data[2],
'alt' => get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true ),
'sizes' => array(
'full' => wp_get_attachment_image_src( $thumbnail_id, 'full' )[0],
'large' => wp_get_attachment_image_src( $thumbnail_id, 'large' )[0],
'medium' => wp_get_attachment_image_src( $thumbnail_id, 'medium' )[0],
'thumbnail' => wp_get_attachment_image_src( $thumbnail_id, 'thumbnail' )[0],
),
);
}
/**
* Get slide target sites
*
* @since 1.0.0
* @param int $slide_id Slide ID
* @return array Target sites data
*/
private function get_slide_target_sites( $slide_id ) {
$sites = wp_get_post_terms( $slide_id, 'hssm_site' );
$sites_data = array();
if ( ! is_wp_error( $sites ) ) {
foreach ( $sites as $site ) {
$sites_data[] = array(
'slug' => $site->slug,
'name' => $site->name,
);
}
}
return $sites_data;
}
/**
* Get slide client
*
* @since 1.0.0
* @param int $slide_id Slide ID
* @return array|null Client data
*/
private function get_slide_client( $slide_id ) {
$clients = wp_get_post_terms( $slide_id, 'hssm_client' );
if ( ! is_wp_error( $clients ) && ! empty( $clients ) ) {
$client = $clients[0];
return array(
'slug' => $client->slug,
'name' => $client->name,
);
}
return null;
}
/**
* Get slide count for a site
*
* @since 1.0.0
* @param int $site_term_id Site term ID
* @return int Slide count
*/
private function get_site_slide_count( $site_term_id ) {
$query = new WP_Query( array(
'post_type' => 'hssm_slide',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
'tax_query' => array(
array(
'taxonomy' => 'hssm_site',
'field' => 'term_id',
'terms' => $site_term_id,
),
),
) );
return $query->found_posts;
}
/**
* Validate site slug parameter
*
* @since 1.0.0
* @param string $value Site slug
* @param WP_REST_Request $request Request object
* @param string $param Parameter name
* @return bool Validation result
*/
public function validate_site_slug( $value, $request, $param ) {
$companion_sites = HSSM_Main::get_companion_sites();
$valid_sites = wp_list_pluck( $companion_sites, 'slug' );
return in_array( $value, $valid_sites );
}
/**
* Validate date parameter
*
* @since 1.0.0
* @param string $value Date string
* @param WP_REST_Request $request Request object
* @param string $param Parameter name
* @return bool Validation result
*/
public function validate_date( $value, $request, $param ) {
return (bool) strtotime( $value );
}
/**
* Validate limit parameter
*
* @since 1.0.0
* @param int $value Limit value
* @param WP_REST_Request $request Request object
* @param string $param Parameter name
* @return bool Validation result
*/
public function validate_limit( $value, $request, $param ) {
return is_numeric( $value ) && $value >= 1 && $value <= 50;
}
/**
* Validate status parameter
*
* @since 1.0.0
* @param string $value Status value
* @param WP_REST_Request $request Request object
* @param string $param Parameter name
* @return bool Validation result
*/
public function validate_status( $value, $request, $param ) {
$valid_statuses = array( 'active', 'scheduled', 'all' );
return in_array( $value, $valid_statuses );
}
/**
* Validate slide ID parameter
*
* @since 1.0.0
* @param int $value Slide ID
* @param WP_REST_Request $request Request object
* @param string $param Parameter name
* @return bool Validation result
*/
public function validate_slide_id( $value, $request, $param ) {
return is_numeric( $value ) && $value > 0;
}
}