9bb2171b92
add 'no dates' logic
534 lines
17 KiB
PHP
534 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;
|
|
}
|
|
|
|
$sizes = array( 'full', 'large', 'medium', 'thumbnail' );
|
|
$sizes_data = array();
|
|
foreach ( $sizes as $size ) {
|
|
$src = wp_get_attachment_image_src( $thumbnail_id, $size );
|
|
$sizes_data[ $size ] = ( $src && isset( $src[0] ) ) ? $src[0] : $image_data[0];
|
|
}
|
|
|
|
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' => $sizes_data,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
} |