Initial commit of slide manager plugin
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Admin Class
|
||||
*
|
||||
* Handles admin functionality including settings pages and API key management
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* HSSM Admin Class
|
||||
*
|
||||
* Admin interface and settings management
|
||||
*/
|
||||
class HSSM_Admin {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
// Hook is registered in main class
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize admin functionality
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
// This is called during the admin_init hook from HSSM_Main
|
||||
$this->register_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menus
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function add_menus() {
|
||||
// Add settings page under the Slides menu
|
||||
add_submenu_page(
|
||||
'edit.php?post_type=hssm_slide',
|
||||
__( 'Slide Manager Settings', 'hi-school-slide-manager' ),
|
||||
__( 'Settings', 'hi-school-slide-manager' ),
|
||||
'manage_options',
|
||||
'hssm-settings',
|
||||
array( $this, 'settings_page' )
|
||||
);
|
||||
|
||||
// Add API documentation page
|
||||
add_submenu_page(
|
||||
'edit.php?post_type=hssm_slide',
|
||||
__( 'API Documentation', 'hi-school-slide-manager' ),
|
||||
__( 'API Docs', 'hi-school-slide-manager' ),
|
||||
'manage_options',
|
||||
'hssm-api-docs',
|
||||
array( $this, 'api_docs_page' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register plugin settings
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register_settings() {
|
||||
// Register settings
|
||||
register_setting( 'hssm_settings', 'hssm_plugin_mode' );
|
||||
register_setting( 'hssm_settings', 'hssm_api_url' );
|
||||
register_setting( 'hssm_settings', 'hssm_client_site_slug' );
|
||||
register_setting( 'hssm_settings', 'hssm_notification_emails' );
|
||||
register_setting( 'hssm_settings', 'hssm_cache_duration' );
|
||||
register_setting( 'hssm_settings', 'hssm_api_keys' );
|
||||
|
||||
// Add settings sections
|
||||
add_settings_section(
|
||||
'hssm_general',
|
||||
__( 'General Settings', 'hi-school-slide-manager' ),
|
||||
array( $this, 'general_section_callback' ),
|
||||
'hssm-settings'
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'hssm_notifications',
|
||||
__( 'Email Notifications', 'hi-school-slide-manager' ),
|
||||
array( $this, 'notifications_section_callback' ),
|
||||
'hssm-settings'
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'hssm_api',
|
||||
__( 'API Settings', 'hi-school-slide-manager' ),
|
||||
array( $this, 'api_section_callback' ),
|
||||
'hssm-settings'
|
||||
);
|
||||
|
||||
// Add settings fields
|
||||
add_settings_field(
|
||||
'hssm_plugin_mode',
|
||||
__( 'Plugin Mode', 'hi-school-slide-manager' ),
|
||||
array( $this, 'plugin_mode_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_general'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_api_url',
|
||||
__( 'Manager API URL', 'hi-school-slide-manager' ),
|
||||
array( $this, 'api_url_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_general'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_client_site_slug',
|
||||
__( 'Client Site Slug', 'hi-school-slide-manager' ),
|
||||
array( $this, 'client_site_slug_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_general'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_cache_duration',
|
||||
__( 'Cache Duration', 'hi-school-slide-manager' ),
|
||||
array( $this, 'cache_duration_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_general'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_notification_emails',
|
||||
__( 'Notification Recipients', 'hi-school-slide-manager' ),
|
||||
array( $this, 'notification_emails_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_notifications'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'hssm_api_keys',
|
||||
__( 'API Keys', 'hi-school-slide-manager' ),
|
||||
array( $this, 'api_keys_field' ),
|
||||
'hssm-settings',
|
||||
'hssm_api'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings page callback
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function settings_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
if ( isset( $_POST['submit'] ) || isset( $_POST['generate_api_key'] ) || isset( $_POST['delete_api_key'] ) ) {
|
||||
check_admin_referer( 'hssm_settings' );
|
||||
$this->handle_settings_save();
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
|
||||
<?php settings_errors(); ?>
|
||||
|
||||
<form method="post" action="">
|
||||
<?php
|
||||
wp_nonce_field( 'hssm_settings' );
|
||||
// Use a consistent page slug for do_settings_sections
|
||||
do_settings_sections( 'hssm-settings' );
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
|
||||
<div class="hssm-api-endpoint-info" style="margin-top: 30px; padding: 20px; background: #f9f9f9; border: 1px solid #ddd;">
|
||||
<h3><?php _e( 'API Endpoints', 'hi-school-slide-manager' ); ?></h3>
|
||||
<p><?php _e( 'Use these endpoints for companion sites:', 'hi-school-slide-manager' ); ?></p>
|
||||
<ul>
|
||||
<li><code><?php echo esc_html( rest_url( 'hssm/v1/slides/{site}' ) ); ?></code> - Get slides for a site</li>
|
||||
<li><code><?php echo esc_html( rest_url( 'hssm/v1/sites' ) ); ?></code> - Get available sites</li>
|
||||
<li><code><?php echo esc_html( rest_url( 'hssm/v1/slide/{id}' ) ); ?></code> - Get single slide</li>
|
||||
<li><code><?php echo esc_html( rest_url( 'hssm/v1/health' ) ); ?></code> - Health check (no auth required)</li>
|
||||
</ul>
|
||||
<p><strong><?php _e( 'Authentication:', 'hi-school-slide-manager' ); ?></strong> <?php _e( 'Include API key in X-HSSM-API-Key header or api_key parameter', 'hi-school-slide-manager' ); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* API documentation page
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function api_docs_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php _e( 'Hi-School Slide Manager API Documentation', 'hi-school-slide-manager' ); ?></h1>
|
||||
|
||||
<div class="hssm-api-docs">
|
||||
<h2><?php _e( 'Authentication', 'hi-school-slide-manager' ); ?></h2>
|
||||
<p><?php _e( 'All API endpoints (except health check) require authentication using an API key.', 'hi-school-slide-manager' ); ?></p>
|
||||
<p><?php _e( 'Include the API key in one of these ways:', 'hi-school-slide-manager' ); ?></p>
|
||||
<ul>
|
||||
<li><?php _e( 'Header:', 'hi-school-slide-manager' ); ?> <code>X-HSSM-API-Key: your-api-key</code></li>
|
||||
<li><?php _e( 'Query parameter:', 'hi-school-slide-manager' ); ?> <code>?api_key=your-api-key</code></li>
|
||||
</ul>
|
||||
|
||||
<h2><?php _e( 'Endpoints', 'hi-school-slide-manager' ); ?></h2>
|
||||
|
||||
<h3><code>GET /wp-json/hssm/v1/slides/{site}</code></h3>
|
||||
<p><?php _e( 'Get slides for a specific site.', 'hi-school-slide-manager' ); ?></p>
|
||||
<h4><?php _e( 'Parameters:', 'hi-school-slide-manager' ); ?></h4>
|
||||
<ul>
|
||||
<li><code>site</code> (required) - Site slug: hi-school, one-stop, or ace</li>
|
||||
<li><code>date</code> (optional) - Date to filter by (YYYY-MM-DD)</li>
|
||||
<li><code>limit</code> (optional) - Max slides to return (1-50, default: 10)</li>
|
||||
<li><code>status</code> (optional) - Filter by status: active, scheduled, all (default: active)</li>
|
||||
</ul>
|
||||
<h4><?php _e( 'Example:', 'hi-school-slide-manager' ); ?></h4>
|
||||
<pre><code>GET <?php echo esc_html( rest_url( 'hssm/v1/slides/hi-school?limit=5&date=2025-12-01' ) ); ?>
|
||||
Headers: X-HSSM-API-Key: your-api-key</code></pre>
|
||||
|
||||
<h3><code>GET /wp-json/hssm/v1/sites</code></h3>
|
||||
<p><?php _e( 'Get all available sites.', 'hi-school-slide-manager' ); ?></p>
|
||||
<h4><?php _e( 'Example:', 'hi-school-slide-manager' ); ?></h4>
|
||||
<pre><code>GET <?php echo esc_html( rest_url( 'hssm/v1/sites' ) ); ?>
|
||||
Headers: X-HSSM-API-Key: your-api-key</code></pre>
|
||||
|
||||
<h3><code>GET /wp-json/hssm/v1/slide/{id}</code></h3>
|
||||
<p><?php _e( 'Get a specific slide by ID.', 'hi-school-slide-manager' ); ?></p>
|
||||
<h4><?php _e( 'Parameters:', 'hi-school-slide-manager' ); ?></h4>
|
||||
<ul>
|
||||
<li><code>id</code> (required) - Slide ID</li>
|
||||
</ul>
|
||||
|
||||
<h3><code>GET /wp-json/hssm/v1/health</code></h3>
|
||||
<p><?php _e( 'Health check endpoint (no authentication required).', 'hi-school-slide-manager' ); ?></p>
|
||||
|
||||
<h2><?php _e( 'Response Format', 'hi-school-slide-manager' ); ?></h2>
|
||||
<p><?php _e( 'All responses are in JSON format. Slide objects include:', 'hi-school-slide-manager' ); ?></p>
|
||||
<pre><code>{
|
||||
"id": 123,
|
||||
"title": "Slide Title",
|
||||
"subtitle": "Slide Subtitle",
|
||||
"content": "Slide content...",
|
||||
"buttons": [
|
||||
{
|
||||
"text": "Learn More",
|
||||
"url": "https://example.com"
|
||||
}
|
||||
],
|
||||
"order": 0,
|
||||
"background_image": {
|
||||
"id": 456,
|
||||
"url": "https://example.com/image.jpg",
|
||||
"width": 1920,
|
||||
"height": 800,
|
||||
"alt": "Image description",
|
||||
"sizes": {
|
||||
"full": "https://example.com/image.jpg",
|
||||
"large": "https://example.com/image-1024x428.jpg"
|
||||
}
|
||||
},
|
||||
"schedule": {
|
||||
"start_date": "2025-01-01 00:00:00",
|
||||
"end_date": "2025-12-31 23:59:59",
|
||||
"auto_publish": true,
|
||||
"auto_unpublish": true
|
||||
},
|
||||
"target_sites": [
|
||||
{
|
||||
"slug": "hi-school",
|
||||
"name": "Hi-School Pharmacy"
|
||||
}
|
||||
],
|
||||
"client": {
|
||||
"slug": "client-name",
|
||||
"name": "Client Name"
|
||||
},
|
||||
"status": "publish",
|
||||
"created": "2025-01-01 12:00:00",
|
||||
"modified": "2025-01-01 12:00:00"
|
||||
}
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hssm-api-docs {
|
||||
max-width: 800px;
|
||||
}
|
||||
.hssm-api-docs h3 {
|
||||
background: #f0f0f0;
|
||||
padding: 10px;
|
||||
border-left: 4px solid #0073aa;
|
||||
}
|
||||
.hssm-api-docs pre {
|
||||
background: #f9f9f9;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.hssm-api-docs code {
|
||||
background: #f9f9f9;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.hssm-api-docs pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin assets
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $hook Current admin page hook
|
||||
*/
|
||||
public function enqueue_assets( $hook ) {
|
||||
// Enqueue general admin styles for all HSSM admin pages
|
||||
if ( strpos( $hook, 'hssm' ) !== false || ( isset($_GET['post_type']) && $_GET['post_type'] === 'hssm_slide' ) ) {
|
||||
wp_enqueue_style(
|
||||
'hssm-admin',
|
||||
HSSM_PLUGIN_URL . 'assets/css/admin.css',
|
||||
array(),
|
||||
HSSM_VERSION
|
||||
);
|
||||
wp_enqueue_script(
|
||||
'hssm-admin',
|
||||
HSSM_PLUGIN_URL . 'assets/js/admin.js',
|
||||
array( 'jquery' ),
|
||||
HSSM_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// Conditionally enqueue dashboard-specific slide styles and dequeue public slider styles
|
||||
global $pagenow;
|
||||
if ( ( $pagenow === 'edit.php' && isset($_GET['post_type']) && $_GET['post_type'] === 'hssm_slide' ) || is_page_template('page-slide-dashboard.php') || is_page_template('page-slide-preview.php') ) {
|
||||
wp_enqueue_style(
|
||||
'hssm-admin-slides-dashboard',
|
||||
HSSM_PLUGIN_URL . 'assets/css/admin-slides-dashboard.css',
|
||||
array(),
|
||||
HSSM_VERSION
|
||||
);
|
||||
// Dequeue the public slider styles on these admin-facing pages to prevent conflicts
|
||||
// This is largely redundant after changes in HSSM_Main::enqueue_frontend_assets,
|
||||
// but harmless to keep as a failsafe.
|
||||
wp_dequeue_style('hssm-slider');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle settings save
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function handle_settings_save() {
|
||||
// Handle general settings save
|
||||
if ( isset( $_POST['submit'] ) ) {
|
||||
if ( isset( $_POST['hssm_plugin_mode'] ) ) {
|
||||
update_option( 'hssm_plugin_mode', sanitize_text_field( $_POST['hssm_plugin_mode'] ) );
|
||||
}
|
||||
if ( isset( $_POST['hssm_api_url'] ) ) {
|
||||
update_option( 'hssm_api_url', esc_url_raw( $_POST['hssm_api_url'] ) );
|
||||
}
|
||||
if ( isset( $_POST['hssm_client_site_slug'] ) ) {
|
||||
update_option( 'hssm_client_site_slug', sanitize_text_field( $_POST['hssm_client_site_slug'] ) );
|
||||
}
|
||||
if ( isset( $_POST['hssm_cache_duration'] ) ) {
|
||||
update_option( 'hssm_cache_duration', absint( $_POST['hssm_cache_duration'] ) );
|
||||
}
|
||||
if ( isset( $_POST['hssm_notification_emails'] ) ) {
|
||||
$emails = explode( "\n", str_replace( "\r", "", $_POST['hssm_notification_emails'] ) );
|
||||
$emails = array_map( 'sanitize_email', $emails );
|
||||
$emails = array_filter( $emails );
|
||||
update_option( 'hssm_notification_emails', $emails );
|
||||
}
|
||||
add_settings_error( 'hssm_settings', 'settings_saved', __( 'Settings saved successfully.', 'hi-school-slide-manager' ), 'success' );
|
||||
}
|
||||
|
||||
// Handle API key generation
|
||||
if ( isset( $_POST['generate_api_key'] ) ) {
|
||||
$api_keys = get_option( 'hssm_api_keys', array() );
|
||||
$api_keys[] = wp_generate_password( 32, false );
|
||||
update_option( 'hssm_api_keys', $api_keys );
|
||||
add_settings_error( 'hssm_settings', 'api_key_generated', __( 'New API key generated successfully.', 'hi-school-slide-manager' ), 'success' );
|
||||
}
|
||||
|
||||
// Handle API key deletion
|
||||
if ( isset( $_POST['delete_api_key'] ) && isset( $_POST['api_key_index'] ) ) {
|
||||
$api_keys = get_option( 'hssm_api_keys', array() );
|
||||
$index = (int) $_POST['api_key_index'];
|
||||
if ( isset( $api_keys[ $index ] ) ) {
|
||||
unset( $api_keys[ $index ] );
|
||||
$api_keys = array_values( $api_keys ); // Reindex array
|
||||
update_option( 'hssm_api_keys', $api_keys );
|
||||
add_settings_error( 'hssm_settings', 'api_key_deleted', __( 'API key deleted successfully.', 'hi-school-slide-manager' ), 'success' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General section callback
|
||||
*/
|
||||
public function general_section_callback() {
|
||||
echo '<p>' . __( 'General plugin settings.', 'hi-school-slide-manager' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifications section callback
|
||||
*/
|
||||
public function notifications_section_callback() {
|
||||
echo '<p>' . __( 'Configure email notifications for new slides.', 'hi-school-slide-manager' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* API section callback
|
||||
*/
|
||||
public function api_section_callback() {
|
||||
echo '<p>' . __( 'Manage API keys for companion sites.', 'hi-school-slide-manager' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin mode field
|
||||
*/
|
||||
public function plugin_mode_field() {
|
||||
$value = get_option( 'hssm_plugin_mode', 'manager' );
|
||||
?>
|
||||
<select name="hssm_plugin_mode" id="hssm_plugin_mode">
|
||||
<option value="manager" <?php selected( $value, 'manager' ); ?>><?php _e( 'Manager (Source Site)', 'hi-school-slide-manager' ); ?></option>
|
||||
<option value="client" <?php selected( $value, 'client' ); ?>><?php _e( 'Client (Companion Site)', 'hi-school-slide-manager' ); ?></option>
|
||||
</select>
|
||||
<p class="description"><?php _e( 'Choose "Manager" for the main site where slides are created, or "Client" for sites that just display slides from the manager.', 'hi-school-slide-manager' ); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* API URL field
|
||||
*/
|
||||
public function api_url_field() {
|
||||
$value = get_option( 'hssm_api_url', '' );
|
||||
echo '<input type="url" name="hssm_api_url" value="' . esc_attr( $value ) . '" class="regular-text" placeholder="https://main-site.com/wp-json/" /> ';
|
||||
echo '<p class="description">' . __( 'The REST API base URL of the Manager site.', 'hi-school-slide-manager' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Client site slug field
|
||||
*/
|
||||
public function client_site_slug_field() {
|
||||
$value = get_option( 'hssm_client_site_slug', '' );
|
||||
?>
|
||||
<select name="hssm_client_site_slug" id="hssm_client_site_slug">
|
||||
<option value=""><?php _e( 'Select site...', 'hi-school-slide-manager' ); ?></option>
|
||||
<?php
|
||||
$sites = HSSM_Main::get_companion_sites();
|
||||
foreach ( $sites as $site ) {
|
||||
echo '<option value="' . esc_attr( $site['slug'] ) . '" ' . selected( $value, $site['slug'], false ) . '>' . esc_html( $site['name'] ) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<p class="description"><?php _e( 'Identify this site to the Manager API.', 'hi-school-slide-manager' ); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache duration field
|
||||
*/
|
||||
public function cache_duration_field() {
|
||||
$value = get_option( 'hssm_cache_duration', 3600 );
|
||||
echo '<input type="number" name="hssm_cache_duration" value="' . esc_attr( $value ) . '" min="300" max="86400" /> ';
|
||||
echo '<span class="description">' . __( 'Recommended cache duration for API responses (seconds)', 'hi-school-slide-manager' ) . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification emails field
|
||||
*/
|
||||
public function notification_emails_field() {
|
||||
$emails = get_option( 'hssm_notification_emails', array() );
|
||||
echo '<textarea name="hssm_notification_emails" rows="5" class="regular-text">' . esc_textarea( implode( "\n", $emails ) ) . '</textarea><br>';
|
||||
echo '<span class="description">' . __( 'Enter email addresses (one per line) to receive notifications when new slides are published.', 'hi-school-slide-manager' ) . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* API keys field
|
||||
*/
|
||||
public function api_keys_field() {
|
||||
$api_keys = get_option( 'hssm_api_keys', array() );
|
||||
|
||||
echo '<div class="hssm-api-keys">';
|
||||
|
||||
if ( empty( $api_keys ) ) {
|
||||
echo '<p>' . __( 'No API keys generated yet.', 'hi-school-slide-manager' ) . '</p>';
|
||||
} else {
|
||||
echo '<table class="widefat">';
|
||||
echo '<thead><tr><th>' . __( 'API Key', 'hi-school-slide-manager' ) . '</th><th>' . __( 'Actions', 'hi-school-slide-manager' ) . '</th></tr></thead>';
|
||||
echo '<tbody>';
|
||||
|
||||
foreach ( $api_keys as $index => $key ) {
|
||||
echo '<tr>';
|
||||
echo '<td><code>' . esc_html( $key ) . '</code></td>';
|
||||
echo '<td>';
|
||||
echo '<button type="button" class="button copy-api-key" data-key="' . esc_attr( $key ) . '">' . __( 'Copy', 'hi-school-slide-manager' ) . '</button> ';
|
||||
echo '<button type="submit" name="delete_api_key" value="1" class="button button-link-delete" onclick="return confirm(\'' . esc_js( __( 'Are you sure you want to delete this API key?', 'hi-school-slide-manager' ) ) . '\')">' . __( 'Delete', 'hi-school-slide-manager' ) . '</button>';
|
||||
echo '<input type="hidden" name="api_key_index" value="' . esc_attr( $index ) . '" />';
|
||||
echo '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
echo '</tbody></table>';
|
||||
}
|
||||
|
||||
echo '<p><button type="submit" name="generate_api_key" value="1" class="button button-secondary">' . __( 'Generate New API Key', 'hi-school-slide-manager' ) . '</button></p>';
|
||||
echo '<p class="description">' . __( 'API keys are used by companion sites to authenticate and fetch slides.', 'hi-school-slide-manager' ) . '</p>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
// Add JavaScript for copy functionality
|
||||
?>
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
$(".copy-api-key").on("click", function() {
|
||||
var key = $(this).data("key");
|
||||
navigator.clipboard.writeText(key).then(function() {
|
||||
alert("API key copied to clipboard!");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM API Client Class
|
||||
*
|
||||
* Handles fetching slides from the remote Manager site API
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
class HSSM_API_Client {
|
||||
|
||||
/**
|
||||
* Get slides for this site
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @return array|WP_Error Array of slides or WP_Error on failure
|
||||
*/
|
||||
public function get_slides() {
|
||||
$api_url = get_option( 'hssm_api_url' );
|
||||
$site_slug = get_option( 'hssm_client_site_slug' );
|
||||
$api_key = 'HI_SCHOOL_FIXED_API_KEY'; // Hardcoded fixed API key
|
||||
$cache_duration = get_option( 'hssm_cache_duration', 3600 );
|
||||
|
||||
if ( empty( $api_url ) || empty( $site_slug ) || empty( $api_key ) ) { // Added $api_key check here
|
||||
return new WP_Error( 'missing_config', __( 'API URL, Site Slug, or API Key not configured.', 'hi-school-slide-manager' ) ); // Updated error message
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
$cache_key = 'hssm_remote_slides_' . $site_slug;
|
||||
$cached_slides = get_transient( $cache_key );
|
||||
if ( false !== $cached_slides ) {
|
||||
return $cached_slides;
|
||||
}
|
||||
|
||||
// Build the request URL
|
||||
// Ensure trailing slash on API URL and remove leading slash from endpoint
|
||||
$endpoint = sprintf( 'hssm/v1/slides/%s', $site_slug );
|
||||
$full_url = trailingslashit( $api_url ) . $endpoint;
|
||||
|
||||
$args = array(
|
||||
'timeout' => 15,
|
||||
'headers' => array(
|
||||
'X-HSSM-API-Key' => $api_key,
|
||||
'Accept' => 'application/json',
|
||||
),
|
||||
);
|
||||
|
||||
$response = wp_remote_get( $full_url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$code = wp_remote_retrieve_response_code( $response );
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( 200 !== $code ) {
|
||||
return new WP_Error( 'api_error', sprintf( __( 'API returned code %d: %s', 'hi-school-slide-manager' ), $code, $body ) );
|
||||
}
|
||||
|
||||
$data = json_decode( $body, true );
|
||||
if ( ! is_array( $data ) || ! isset( $data['slides'] ) ) {
|
||||
return new WP_Error( 'invalid_response', __( 'Invalid API response format.', 'hi-school-slide-manager' ) );
|
||||
}
|
||||
|
||||
$slides = $data['slides'];
|
||||
|
||||
// Cache the results
|
||||
set_transient( $cache_key, $slides, $cache_duration );
|
||||
|
||||
return $slides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the slides cache
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function clear_cache() {
|
||||
$site_slug = get_option( 'hssm_client_site_slug' );
|
||||
if ( ! empty( $site_slug ) ) {
|
||||
delete_transient( 'hssm_remote_slides_' . $site_slug );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Autoloader Class
|
||||
*
|
||||
* Autoloader for the Hi-School Slide Manager plugin classes
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* HSSM Autoloader Class
|
||||
*
|
||||
* Handles automatic loading of plugin classes
|
||||
*/
|
||||
class HSSM_Autoloader {
|
||||
|
||||
/**
|
||||
* Initialize the autoloader
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function init() {
|
||||
spl_autoload_register( array( __CLASS__, 'autoload' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoload classes
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $class_name The class name to load
|
||||
*/
|
||||
public static function autoload( $class_name ) {
|
||||
// Only autoload classes that start with HSSM_
|
||||
if ( strpos( $class_name, 'HSSM_' ) !== 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert class name to file path
|
||||
$file_name = str_replace( '_', '-', strtolower( $class_name ) ) . '.php';
|
||||
$file_path = HSSM_PLUGIN_DIR . 'includes/class-' . $file_name;
|
||||
|
||||
// Include the file if it exists
|
||||
if ( file_exists( $file_path ) ) {
|
||||
require_once $file_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,520 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Main Class
|
||||
*
|
||||
* Main plugin class that orchestrates the Hi-School Slide Manager functionality
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* HSSM Main Class
|
||||
*
|
||||
* Core plugin functionality and initialization
|
||||
*/
|
||||
class HSSM_Main {
|
||||
|
||||
/**
|
||||
* Plugin instance
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var HSSM_Main
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Slides CPT manager
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var HSSM_Slides_CPT
|
||||
*/
|
||||
public $slides_cpt;
|
||||
|
||||
/**
|
||||
* REST API manager
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var HSSM_REST_API
|
||||
*/
|
||||
public $rest_api;
|
||||
|
||||
/**
|
||||
* Frontend manager
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var HSSM_Frontend
|
||||
*/
|
||||
public $frontend;
|
||||
|
||||
/**
|
||||
* Shortcode manager
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @var HSSM_Shortcode
|
||||
*/
|
||||
public $shortcode;
|
||||
|
||||
/**
|
||||
* Admin manager
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var HSSM_Admin
|
||||
*/
|
||||
public $admin;
|
||||
|
||||
/**
|
||||
* Email notifications manager
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var HSSM_Email_Notifications
|
||||
*/
|
||||
public $email_notifications;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function __construct() {
|
||||
// Private constructor to enforce singleton
|
||||
}
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return HSSM_Main
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the plugin
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
// Initialize core components
|
||||
$this->init_components();
|
||||
|
||||
// Hook into WordPress
|
||||
$this->init_hooks();
|
||||
|
||||
// Load text domain for translations
|
||||
$this->load_textdomain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize plugin components
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function init_components() {
|
||||
// Manually require class files (since they don't follow exact autoloader pattern)
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-slides-cpt.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-rest-api.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-admin.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-frontend.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-notifications.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-api-client.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-slider.php';
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-shortcode.php';
|
||||
|
||||
// Initialize slides custom post type
|
||||
$this->slides_cpt = new HSSM_Slides_CPT();
|
||||
|
||||
// Initialize REST API endpoints
|
||||
$this->rest_api = new HSSM_REST_API();
|
||||
|
||||
// Initialize admin functionality
|
||||
$this->admin = new HSSM_Admin();
|
||||
|
||||
// Initialize frontend functionality
|
||||
$this->frontend = new HSSM_Frontend();
|
||||
|
||||
// Initialize shortcode
|
||||
$this->shortcode = new HSSM_Shortcode();
|
||||
|
||||
// Initialize email notifications
|
||||
$this->email_notifications = new HSSM_Notifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize WordPress hooks
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function init_hooks() {
|
||||
$plugin_mode = get_option( 'hssm_plugin_mode', 'manager' );
|
||||
|
||||
// Core WordPress hooks
|
||||
if ( 'manager' === $plugin_mode ) {
|
||||
add_action( 'init', array( $this, 'register_post_types' ) );
|
||||
add_action( 'init', array( $this, 'register_taxonomies' ) );
|
||||
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
|
||||
}
|
||||
|
||||
// Frontend hooks
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ), 20 );
|
||||
add_action( 'template_redirect', array( $this, 'handle_frontend_pages' ) );
|
||||
|
||||
// Admin hooks
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'admin_menu', array( $this, 'add_admin_menus' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
|
||||
|
||||
// AJAX hooks for authenticated users
|
||||
// Note: Main AJAX handlers are in HSSM_Frontend class
|
||||
add_action( 'wp_ajax_hssm_upload_image', array( $this, 'ajax_upload_image' ) );
|
||||
add_action( 'wp_ajax_hssm_preview_slides', array( $this, 'ajax_preview_slides' ) );
|
||||
add_action( 'wp_ajax_hssm_send_preview_email', array( $this, 'ajax_send_preview_email' ) );
|
||||
|
||||
// Cron hooks for scheduled slides
|
||||
add_action( 'hssm_check_scheduled_slides', array( $this, 'check_scheduled_slides' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin text domain
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function load_textdomain() {
|
||||
load_plugin_textdomain(
|
||||
'hi-school-slide-manager',
|
||||
false,
|
||||
dirname( HSSM_PLUGIN_BASENAME ) . '/languages'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom post types
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register_post_types() {
|
||||
if ( $this->slides_cpt ) {
|
||||
$this->slides_cpt->register();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom taxonomies
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register_taxonomies() {
|
||||
if ( $this->slides_cpt ) {
|
||||
$this->slides_cpt->register_taxonomies();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
if ( $this->rest_api ) {
|
||||
$this->rest_api->register_routes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend assets
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function enqueue_frontend_assets() {
|
||||
// Prevent slider assets from loading on admin-facing "frontend" pages
|
||||
if ( is_page_template('page-slide-dashboard.php') || is_page_template('page-slide-preview.php') ) {
|
||||
// These pages will load their own specific styles (admin-slides-dashboard.css) or rely on explicit shortcode enqueueing.
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue slider assets globally so they are available for the shortcode on regular frontend pages
|
||||
wp_enqueue_style(
|
||||
'hssm-slider',
|
||||
HSSM_PLUGIN_URL . 'assets/css/slider.css',
|
||||
array(),
|
||||
HSSM_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'hssm-slider',
|
||||
HSSM_PLUGIN_URL . 'assets/js/slider.js',
|
||||
array( 'jquery' ),
|
||||
HSSM_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
if ( $this->frontend ) {
|
||||
$this->frontend->enqueue_assets();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle frontend page routing
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function handle_frontend_pages() {
|
||||
if ( $this->frontend ) {
|
||||
$this->frontend->handle_page_routing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize admin functionality
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function admin_init() {
|
||||
if ( $this->admin ) {
|
||||
$this->admin->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menus
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function add_admin_menus() {
|
||||
if ( $this->admin ) {
|
||||
$this->admin->add_menus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin assets
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $hook Current admin page hook
|
||||
*/
|
||||
public function enqueue_admin_assets( $hook ) {
|
||||
if ( $this->admin ) {
|
||||
$this->admin->enqueue_assets( $hook );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get companion sites configuration
|
||||
*
|
||||
* Centralized source of truth for companion sites
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return array List of companion sites
|
||||
*/
|
||||
public static function get_companion_sites() {
|
||||
return array(
|
||||
array(
|
||||
'slug' => 'hi-school',
|
||||
'name' => 'Hi-School Pharmacy',
|
||||
'description' => 'Hi-School Pharmacy companion site'
|
||||
),
|
||||
array(
|
||||
'slug' => 'one-stop',
|
||||
'name' => 'One Stop Hardware',
|
||||
'description' => 'One Stop Hardware companion site'
|
||||
),
|
||||
array(
|
||||
'slug' => 'ace',
|
||||
'name' => 'Ace Hardware',
|
||||
'description' => 'Ace Hardware companion site'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for scheduled slides that need to be activated/deactivated
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function check_scheduled_slides() {
|
||||
if ( $this->slides_cpt ) {
|
||||
$this->slides_cpt->check_scheduled_slides();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin activation
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function activate() {
|
||||
// Set up cron jobs
|
||||
self::setup_cron_jobs();
|
||||
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Set default options
|
||||
self::set_default_options();
|
||||
|
||||
// Create default taxonomy terms
|
||||
self::create_default_terms();
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin deactivation
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function deactivate() {
|
||||
// Clear cron jobs
|
||||
wp_clear_scheduled_hook( 'hssm_check_scheduled_slides' );
|
||||
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin uninstall
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function uninstall() {
|
||||
// Remove plugin options
|
||||
self::remove_plugin_options();
|
||||
|
||||
// Remove user meta
|
||||
self::remove_user_meta();
|
||||
|
||||
// Remove taxonomy terms and slides
|
||||
self::remove_plugin_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default taxonomy terms for sites
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function create_default_terms() {
|
||||
// Load the CPT class manually during activation
|
||||
require_once HSSM_PLUGIN_DIR . 'includes/class-hssm-slides-cpt.php';
|
||||
|
||||
// Create a temporary instance to set up default terms
|
||||
$slides_cpt = new HSSM_Slides_CPT();
|
||||
|
||||
// Register the taxonomies first
|
||||
$slides_cpt->register_taxonomies();
|
||||
|
||||
// Default terms are created automatically in the register_taxonomies method
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup cron jobs
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function setup_cron_jobs() {
|
||||
if ( ! wp_next_scheduled( 'hssm_check_scheduled_slides' ) ) {
|
||||
wp_schedule_event( time(), 'hourly', 'hssm_check_scheduled_slides' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default plugin options
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function set_default_options() {
|
||||
$default_options = array(
|
||||
'hssm_plugin_mode' => 'manager',
|
||||
'hssm_api_url' => '',
|
||||
'hssm_client_site_slug' => '',
|
||||
'hssm_notification_emails' => array( get_option( 'admin_email' ) ),
|
||||
'hssm_default_slide_duration' => 30, // days
|
||||
'hssm_cache_duration' => 3600, // 1 hour
|
||||
'hssm_api_version' => 'v1',
|
||||
'hssm_preview_page_slug' => 'slide-preview',
|
||||
'hssm_manager_page_slug' => 'slide-manager',
|
||||
'hssm_api_keys' => array( wp_generate_password( 32, false ) ), // Generate initial API key
|
||||
);
|
||||
|
||||
foreach ( $default_options as $option_name => $option_value ) {
|
||||
if ( ! get_option( $option_name ) ) {
|
||||
add_option( $option_name, $option_value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove plugin options
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function remove_plugin_options() {
|
||||
$options = array(
|
||||
'hssm_notification_emails',
|
||||
'hssm_default_slide_duration',
|
||||
'hssm_cache_duration',
|
||||
'hssm_api_version',
|
||||
'hssm_preview_page_slug',
|
||||
'hssm_manager_page_slug',
|
||||
'hssm_api_keys',
|
||||
);
|
||||
|
||||
foreach ( $options as $option ) {
|
||||
delete_option( $option );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user meta
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function remove_user_meta() {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->delete(
|
||||
$wpdb->usermeta,
|
||||
array( 'meta_key' => 'hssm_user_preferences' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove plugin data including slides and taxonomy terms
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function remove_plugin_data() {
|
||||
// Get all slide posts
|
||||
$slides = get_posts( array(
|
||||
'post_type' => 'hssm_slide',
|
||||
'numberposts' => -1,
|
||||
'post_status' => 'any'
|
||||
) );
|
||||
|
||||
// Delete all slides
|
||||
foreach ( $slides as $slide ) {
|
||||
wp_delete_post( $slide->ID, true );
|
||||
}
|
||||
|
||||
// Remove taxonomy terms
|
||||
$taxonomies = array( 'hssm_site', 'hssm_client' );
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$terms = get_terms( array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'hide_empty' => false,
|
||||
) );
|
||||
|
||||
if ( ! is_wp_error( $terms ) ) {
|
||||
foreach ( $terms as $term ) {
|
||||
wp_delete_term( $term->term_id, $taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Notifications Class
|
||||
*
|
||||
* Handles all email notifications for the slide manager
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* HSSM Notifications Class
|
||||
*/
|
||||
class HSSM_Notifications {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
// We'll hook into actions that trigger notifications
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification settings
|
||||
*
|
||||
* @return array Array of email addresses for different roles
|
||||
*/
|
||||
public static function get_settings() {
|
||||
$defaults = array(
|
||||
'editor_emails' => '',
|
||||
'itc_emails' => ''
|
||||
);
|
||||
|
||||
$settings = get_option( 'hssm_notification_settings', $defaults );
|
||||
|
||||
// Ensure we have an array and it contains our expected keys
|
||||
if ( ! is_array( $settings ) ) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
return wp_parse_args( $settings, $defaults );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save notification settings
|
||||
*
|
||||
* @param array $settings Array of settings
|
||||
* @return bool True if saved successfully
|
||||
*/
|
||||
public static function save_settings( $settings ) {
|
||||
$clean_settings = array(
|
||||
'editor_emails' => sanitize_textarea_field( $settings['editor_emails'] ),
|
||||
'itc_emails' => sanitize_textarea_field( $settings['itc_emails'] )
|
||||
);
|
||||
|
||||
return update_option( 'hssm_notification_settings', $clean_settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get emails as array from string
|
||||
*
|
||||
* @param string $emails_string Comma or newline separated emails
|
||||
* @return array Array of valid email addresses
|
||||
*/
|
||||
private function get_emails_array( $emails_string ) {
|
||||
if ( empty( $emails_string ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Replace newlines with commas
|
||||
$emails_string = str_replace( array( "\r", "\n" ), ',', $emails_string );
|
||||
$emails = explode( ',', $emails_string );
|
||||
|
||||
$clean_emails = array();
|
||||
foreach ( $emails as $email ) {
|
||||
$email = sanitize_email( trim( $email ) );
|
||||
if ( is_email( $email ) ) {
|
||||
$clean_emails[] = $email;
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique( $clean_emails );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to Editor when slide is Scheduled or Published Immediately
|
||||
*
|
||||
* @param int $slide_id Slide ID
|
||||
* @param string $status 'scheduled' or 'published'
|
||||
*/
|
||||
public function notify_editor_slide_created( $slide_id, $status ) {
|
||||
$settings = self::get_settings();
|
||||
$emails = $this->get_emails_array( $settings['editor_emails'] );
|
||||
|
||||
if ( empty( $emails ) ) {
|
||||
error_log( 'HSSM: No Editor emails configured for notification.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
|
||||
$target_sites = wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) );
|
||||
$site_slugs = ! empty( $target_sites ) ? implode( ',', $target_sites ) : '';
|
||||
|
||||
// Determine publish date
|
||||
$publish_date = ( $status === 'scheduled' )
|
||||
? get_post_meta( $slide_id, '_hssm_start_date', true )
|
||||
: current_time( 'Y-m-d' );
|
||||
|
||||
// Construct preview link
|
||||
// page-slide-preview.php logic usually takes 'date' and 'site' params
|
||||
// We'll link to the preview page. Assuming there is a page with the template, or we use a custom URL structure if defined.
|
||||
// The instructions mentioned: "generated link to the slide-preview page with params containing the slide-title, the publish-date... and the site slugs"
|
||||
|
||||
$preview_page_url = home_url( '/slide-preview/' ); // Basic assumption, adjust if needed based on setup
|
||||
// Better: find the page using the template
|
||||
$pages = get_pages(array(
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => 'page-slide-preview.php'
|
||||
));
|
||||
|
||||
if ( ! empty( $pages ) ) {
|
||||
$preview_page_url = get_permalink( $pages[0]->ID );
|
||||
}
|
||||
|
||||
$preview_link = add_query_arg( array(
|
||||
'preview_title' => urlencode( $slide->post_title ),
|
||||
'date' => $publish_date,
|
||||
'sites' => $site_slugs,
|
||||
'preview_mode' => 'editor_check'
|
||||
), $preview_page_url );
|
||||
|
||||
$subject = sprintf( 'New Slide %s: "%s"', ucfirst( $status ), $slide->post_title );
|
||||
|
||||
$message = "A new slide has been " . ( $status === 'scheduled' ? 'Scheduled' : 'Published Immediately' ) . ".\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "Target Date: " . $publish_date . "\n";
|
||||
$message .= "Target Sites: " . implode( ', ', $target_sites ) . "\n\n";
|
||||
$message .= "Preview Link: " . $preview_link . "\n\n";
|
||||
$message .= "Please review the slide.";
|
||||
|
||||
foreach ( $emails as $email ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to Creator (Author) when slide is Approved
|
||||
*
|
||||
* @param int $slide_id Slide ID
|
||||
*/
|
||||
public function notify_creator_slide_approved( $slide_id ) {
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
|
||||
$author_email = get_the_author_meta( 'user_email', $slide->post_author );
|
||||
|
||||
if ( ! is_email( $author_email ) ) return;
|
||||
|
||||
$subject = 'Your Slide Has Been Approved: "' . $slide->post_title . '"';
|
||||
|
||||
$message = "Good news! Your slide has been approved.\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "It is now scheduled/published according to your settings.";
|
||||
|
||||
wp_mail( $author_email, $subject, $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to ITCGuys when slide goes LIVE
|
||||
*
|
||||
* @param int $slide_id Slide ID
|
||||
*/
|
||||
public function notify_itc_slide_live( $slide_id ) {
|
||||
$settings = self::get_settings();
|
||||
$emails = $this->get_emails_array( $settings['itc_emails'] );
|
||||
|
||||
if ( empty( $emails ) ) return;
|
||||
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
|
||||
// Generate Confirmation Link
|
||||
// This link needs to trigger the "Slide is Live" confirmation page
|
||||
// We probably need a specialized page or endpoint for this.
|
||||
// Let's assume we use the Dashboard page with a specific action param or a separate page.
|
||||
// User requested: "We need a page 'Slide is Live' ... with a green confirmation button"
|
||||
// Let's use the preview page or dashboard page with a specific query arg to show this UI.
|
||||
// Or better, a dedicated endpoint. For now, let's point to the dashboard with a "confirm_live" param.
|
||||
|
||||
$confirm_url = home_url( '/slide-live-confirmation/' ); // Ideally we create this page or rewrite rule
|
||||
// For now, let's use the dashboard url with arguments, and we'll handle the UI there or create a rewrite.
|
||||
// Actually, creating a specific page 'Slide is Live' was requested.
|
||||
// I'll create a new template `page-slide-confirmation.php` later.
|
||||
|
||||
// Find confirmation page if exists, or construct URL
|
||||
$pages = get_pages(array(
|
||||
'meta_key' => '_wp_page_template',
|
||||
'meta_value' => 'page-slide-confirmation.php'
|
||||
));
|
||||
$confirmation_page_url = !empty($pages) ? get_permalink($pages[0]->ID) : home_url('/?hssm_action=confirm_live');
|
||||
|
||||
$confirm_link = add_query_arg( array(
|
||||
'slide_id' => $slide_id,
|
||||
'token' => wp_create_nonce( 'hssm_confirm_live_' . $slide_id ) // Note: Nonces depend on user session, might be tricky for email links if user isn't logged in.
|
||||
// Better to use a hash that doesn't expire or relies on a shared secret if ITC guys are not logged in.
|
||||
// Assuming ITC guys are logged in users for now or we just use a hash.
|
||||
), $confirmation_page_url );
|
||||
|
||||
// Since email links are clicked outside sessions often, maybe just a hash of slide ID + secret salt.
|
||||
$token = hash( 'sha256', $slide_id . 'hssm_secret_salt' );
|
||||
$confirm_link = add_query_arg( array(
|
||||
'slide_id' => $slide_id,
|
||||
'hssm_token' => $token
|
||||
), $confirmation_page_url );
|
||||
|
||||
|
||||
$target_sites = wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) );
|
||||
|
||||
$subject = 'ACTION REQUIRED: Slide is LIVE - "' . $slide->post_title . '"';
|
||||
|
||||
$message = "A slide has just gone LIVE.\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "Sites: " . implode( ', ', $target_sites ) . "\n\n";
|
||||
$message .= "Please check the sites to ensure it is displaying correctly.\n";
|
||||
$message .= "Once verified, click the link below to confirm to the Editor and Author:\n\n";
|
||||
$message .= $confirm_link;
|
||||
|
||||
foreach ( $emails as $email ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send FINAL notification to Editor and Author (triggered by ITC confirmation)
|
||||
*
|
||||
* @param int $slide_id Slide ID
|
||||
*/
|
||||
public function notify_all_confirmed_live( $slide_id ) {
|
||||
$slide = get_post( $slide_id );
|
||||
if ( ! $slide ) return;
|
||||
|
||||
// Editor Emails
|
||||
$settings = self::get_settings();
|
||||
$editor_emails = $this->get_emails_array( $settings['editor_emails'] );
|
||||
|
||||
// Author Email
|
||||
$author_email = get_the_author_meta( 'user_email', $slide->post_author );
|
||||
|
||||
$recipients = array_merge( $editor_emails, array( $author_email ) );
|
||||
$recipients = array_unique( array_filter( $recipients ) );
|
||||
|
||||
if ( empty( $recipients ) ) return;
|
||||
|
||||
$subject = 'CONFIRMED: Slide is Live and Verified - "' . $slide->post_title . '"';
|
||||
|
||||
$message = "The slide has been verified as LIVE by the ITC team.\n\n";
|
||||
$message .= "Title: " . $slide->post_title . "\n";
|
||||
$message .= "Status: Live & Verified\n\n";
|
||||
$message .= "You can view the slide on the live sites.";
|
||||
|
||||
foreach ( $recipients as $email ) {
|
||||
wp_mail( $email, $subject, $message );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Shortcode Class
|
||||
*
|
||||
* Handles the [hssm_slider] shortcode
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
class HSSM_Shortcode {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_shortcode( 'hssm_slider', array( $this, 'render_shortcode' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the [hssm_slider] shortcode
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string Shortcode output
|
||||
*/
|
||||
public function render_shortcode( $atts ) {
|
||||
$plugin_mode = get_option( 'hssm_plugin_mode', 'manager' );
|
||||
$slides = array();
|
||||
|
||||
if ( 'client' === $plugin_mode ) {
|
||||
// Fetch from remote API
|
||||
$api_client = new HSSM_API_Client();
|
||||
$result = $api_client->get_slides();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return '<div class="hssm-error">' . sprintf( __( 'HSSM API Error: %s', 'hi-school-slide-manager' ), $result->get_error_message() ) . '</div>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
$slides = $result;
|
||||
} else {
|
||||
// Manager mode - Fetch locally
|
||||
$site_slug = isset( $atts['site'] ) ? sanitize_text_field( $atts['site'] ) : get_option( 'hssm_client_site_slug', 'hi-school' );
|
||||
$date = isset( $atts['date'] ) ? sanitize_text_field( $atts['date'] ) : current_time( 'Y-m-d' );
|
||||
|
||||
// Build query args similar to what's in the REST API or Frontend class
|
||||
$args = array(
|
||||
'post_type' => 'hssm_slide',
|
||||
'posts_per_page' => isset( $atts['limit'] ) ? intval( $atts['limit'] ) : 10,
|
||||
'post_status' => array( 'publish', 'future', 'pending' ),
|
||||
'meta_key' => '_hssm_order',
|
||||
'orderby' => 'meta_value_num',
|
||||
'order' => 'ASC',
|
||||
);
|
||||
|
||||
if ( ! empty( $site_slug ) ) {
|
||||
$args['tax_query'] = array(
|
||||
array(
|
||||
'taxonomy' => 'hssm_site',
|
||||
'field' => 'slug',
|
||||
'terms' => $site_slug,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Add date filtering
|
||||
$target_date = $date . ' 23:59:59';
|
||||
$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' )
|
||||
)
|
||||
);
|
||||
|
||||
$query = new WP_Query( $args );
|
||||
if ( $query->have_posts() ) {
|
||||
while ( $query->have_posts() ) {
|
||||
$query->the_post();
|
||||
$slide_id = get_the_ID();
|
||||
|
||||
// Format data to match what the API returns
|
||||
$slides[] = array(
|
||||
'id' => $slide_id,
|
||||
'title' => get_the_title(),
|
||||
'subtitle' => get_post_meta( $slide_id, '_hssm_subtitle', true ),
|
||||
'content' => get_the_content(),
|
||||
'buttons' => get_post_meta( $slide_id, '_hssm_buttons', true ),
|
||||
'featured_image' => get_the_post_thumbnail_url( $slide_id, 'full' ),
|
||||
'hide_all_text' => get_post_meta( $slide_id, '_hssm_hide_all_text', true ) === '1',
|
||||
);
|
||||
}
|
||||
wp_reset_postdata();
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $slides ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$slider = new HSSM_Slider();
|
||||
return $slider->render( $slides, $atts );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Slider Rendering Class
|
||||
*
|
||||
* Handles rendering of the slider HTML/CSS/JS based on provided slide data
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
class HSSM_Slider {
|
||||
|
||||
/**
|
||||
* Render the slider
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @param array $slides Array of slide data
|
||||
* @param array $atts Shortcode attributes or configuration
|
||||
* @return string Slider HTML
|
||||
*/
|
||||
public function render( $slides, $atts = array() ) {
|
||||
if ( empty( $slides ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Merge attributes
|
||||
$atts = shortcode_atts( array(
|
||||
'id' => 'hssm-slider-' . uniqid(),
|
||||
'class' => '',
|
||||
'autoplay' => 'true',
|
||||
'interval' => '5000',
|
||||
), $atts );
|
||||
|
||||
ob_start();
|
||||
$this->enqueue_assets();
|
||||
?>
|
||||
<div id="<?php echo esc_attr( $atts['id'] ); ?>" class="hssm-slider-container <?php echo esc_attr( $atts['class'] ); ?>" data-autoplay="<?php echo esc_attr( $atts['autoplay'] ); ?>" data-interval="<?php echo esc_attr( $atts['interval'] ); ?>">
|
||||
<div class="hssm-slider">
|
||||
<div class="hssm-viewport">
|
||||
<div class="hssm-track">
|
||||
<?php foreach ( $slides as $index => $slide ) : ?>
|
||||
<?php
|
||||
$bg_image = isset( $slide['background_image']['url'] ) ? $slide['background_image']['url'] : '';
|
||||
if ( empty( $bg_image ) && isset( $slide['featured_image'] ) ) {
|
||||
$bg_image = $slide['featured_image'];
|
||||
}
|
||||
|
||||
$hide_text = isset( $slide['hide_all_text'] ) && $slide['hide_all_text'];
|
||||
?>
|
||||
<div class="hssm-slide" data-index="<?php echo esc_attr( $index ); ?>">
|
||||
<div class="hssm-slide-inner <?php echo $hide_text ? 'hssm-hide-text' : ''; ?>" style="background-image: url('<?php echo esc_url( $bg_image ); ?>');">
|
||||
<div class="hssm-slide-content-wrapper">
|
||||
<div class="hssm-slide-content">
|
||||
<?php if ( ! empty( $slide['title'] ) ) : ?>
|
||||
<h1 class="hssm-slide-title"><span><?php echo esc_html( $slide['title'] ); ?></span></h1>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! empty( $slide['subtitle'] ) ) : ?>
|
||||
<h4 class="hssm-slide-subtitle"><span><?php echo esc_html( $slide['subtitle'] ); ?></span></h4>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! empty( $slide['content'] ) ) : ?>
|
||||
<div class="hssm-slide-description">
|
||||
<span><?php echo wp_kses_post( $slide['content'] ); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! empty( $slide['buttons'] ) && is_array( $slide['buttons'] ) ) : ?>
|
||||
<?php
|
||||
// Filter out empty buttons
|
||||
$valid_buttons = array_filter($slide['buttons'], function($b) {
|
||||
return !empty($b['text']) && !empty($b['url']);
|
||||
});
|
||||
?>
|
||||
<?php if ( ! empty( $valid_buttons ) ) : ?>
|
||||
<div class="hssm-button-container">
|
||||
<?php foreach ( $valid_buttons as $button ) : ?>
|
||||
<a href="<?php echo esc_url( $button['url'] ); ?>" class="hssm-button">
|
||||
<?php echo esc_html( $button['text'] ); ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ( count( $slides ) > 1 ) : ?>
|
||||
<button class="hssm-arrow hssm-arrow-prev" aria-label="<?php _e( 'Previous Slide', 'hi-school-slide-manager' ); ?>">
|
||||
<span class="hssm-arrow-icon"></span>
|
||||
</button>
|
||||
<button class="hssm-arrow hssm-arrow-next" aria-label="<?php _e( 'Next Slide', 'hi-school-slide-manager' ); ?>">
|
||||
<span class="hssm-arrow-icon"></span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue slider assets
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
private function enqueue_assets() {
|
||||
wp_enqueue_style(
|
||||
'hssm-slider',
|
||||
HSSM_PLUGIN_URL . 'assets/css/slider.css',
|
||||
array(),
|
||||
HSSM_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'hssm-slider',
|
||||
HSSM_PLUGIN_URL . 'assets/js/slider.js',
|
||||
array( 'jquery' ),
|
||||
HSSM_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,946 @@
|
||||
<?php
|
||||
/**
|
||||
* HSSM Slides Custom Post Type
|
||||
*
|
||||
* Manages the custom post type for slides with detailed fields for multi-site management
|
||||
*
|
||||
* @package Hi_School_Slide_Manager
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 'Direct access forbidden.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* HSSM Slides CPT Class
|
||||
*
|
||||
* Handles the slides custom post type and its meta fields
|
||||
*/
|
||||
class HSSM_Slides_CPT {
|
||||
|
||||
/**
|
||||
* Post type slug
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var string
|
||||
*/
|
||||
const POST_TYPE = 'hssm_slide';
|
||||
|
||||
/**
|
||||
* Site taxonomy slug
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var string
|
||||
*/
|
||||
const SITE_TAXONOMY = 'hssm_site';
|
||||
|
||||
/**
|
||||
* Client taxonomy slug
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var string
|
||||
*/
|
||||
const CLIENT_TAXONOMY = 'hssm_client';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
|
||||
add_action( 'save_post', array( $this, 'save_meta_fields' ) );
|
||||
add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'add_custom_columns' ) );
|
||||
add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( $this, 'custom_column_content' ), 10, 2 );
|
||||
|
||||
// Add drag-and-drop ordering functionality
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
add_action( 'wp_ajax_hssm_update_slide_order', array( $this, 'ajax_update_slide_order' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the slides custom post type
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register() {
|
||||
$labels = array(
|
||||
'name' => _x( 'Slides', 'Post type general name', 'hi-school-slide-manager' ),
|
||||
'singular_name' => _x( 'Slide', 'Post type singular name', 'hi-school-slide-manager' ),
|
||||
'menu_name' => _x( 'Slides', 'Admin Menu text', 'hi-school-slide-manager' ),
|
||||
'name_admin_bar' => _x( 'Slide', 'Add New on Toolbar', 'hi-school-slide-manager' ),
|
||||
'add_new' => __( 'Add New', 'hi-school-slide-manager' ),
|
||||
'add_new_item' => __( 'Add New Slide', 'hi-school-slide-manager' ),
|
||||
'new_item' => __( 'New Slide', 'hi-school-slide-manager' ),
|
||||
'edit_item' => __( 'Edit Slide', 'hi-school-slide-manager' ),
|
||||
'view_item' => __( 'View Slide', 'hi-school-slide-manager' ),
|
||||
'all_items' => __( 'All Slides', 'hi-school-slide-manager' ),
|
||||
'search_items' => __( 'Search Slides', 'hi-school-slide-manager' ),
|
||||
'parent_item_colon' => __( 'Parent Slides:', 'hi-school-slide-manager' ),
|
||||
'not_found' => __( 'No slides found.', 'hi-school-slide-manager' ),
|
||||
'not_found_in_trash' => __( 'No slides found in Trash.', 'hi-school-slide-manager' ),
|
||||
'featured_image' => _x( 'Background Image', 'Overrides the "Featured Image" phrase', 'hi-school-slide-manager' ),
|
||||
'set_featured_image' => _x( 'Set background image', 'Overrides the "Set featured image" phrase', 'hi-school-slide-manager' ),
|
||||
'remove_featured_image' => _x( 'Remove background image', 'Overrides the "Remove featured image" phrase', 'hi-school-slide-manager' ),
|
||||
'use_featured_image' => _x( 'Use as background image', 'Overrides the "Use as featured image" phrase', 'hi-school-slide-manager' ),
|
||||
'archives' => _x( 'Slide archives', 'The post type archive label used in nav menus', 'hi-school-slide-manager' ),
|
||||
'insert_into_item' => _x( 'Insert into slide', 'Overrides the "Insert into post" phrase', 'hi-school-slide-manager' ),
|
||||
'uploaded_to_this_item' => _x( 'Uploaded to this slide', 'Overrides the "Uploaded to this post" phrase', 'hi-school-slide-manager' ),
|
||||
'filter_items_list' => _x( 'Filter slides list', 'Screen reader text for the filter links', 'hi-school-slide-manager' ),
|
||||
'items_list_navigation' => _x( 'Slides list navigation', 'Screen reader text for the pagination', 'hi-school-slide-manager' ),
|
||||
'items_list' => _x( 'Slides list', 'Screen reader text for the items list', 'hi-school-slide-manager' ),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'labels' => $labels,
|
||||
'public' => true,
|
||||
'publicly_queryable' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array( 'slug' => 'slide' ),
|
||||
'capability_type' => 'post',
|
||||
'has_archive' => true,
|
||||
'hierarchical' => false,
|
||||
'menu_position' => 20,
|
||||
'menu_icon' => 'dashicons-slides',
|
||||
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
|
||||
'show_in_rest' => true,
|
||||
'rest_base' => 'slides',
|
||||
'rest_controller_class' => 'HSSM_REST_Slides_Controller',
|
||||
);
|
||||
|
||||
register_post_type( self::POST_TYPE, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom taxonomies
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register_taxonomies() {
|
||||
// Site taxonomy for organizing slides by target site
|
||||
$site_labels = array(
|
||||
'name' => _x( 'Target Sites', 'taxonomy general name', 'hi-school-slide-manager' ),
|
||||
'singular_name' => _x( 'Target Site', 'taxonomy singular name', 'hi-school-slide-manager' ),
|
||||
'search_items' => __( 'Search Sites', 'hi-school-slide-manager' ),
|
||||
'all_items' => __( 'All Sites', 'hi-school-slide-manager' ),
|
||||
'parent_item' => __( 'Parent Site', 'hi-school-slide-manager' ),
|
||||
'parent_item_colon' => __( 'Parent Site:', 'hi-school-slide-manager' ),
|
||||
'edit_item' => __( 'Edit Site', 'hi-school-slide-manager' ),
|
||||
'update_item' => __( 'Update Site', 'hi-school-slide-manager' ),
|
||||
'add_new_item' => __( 'Add New Site', 'hi-school-slide-manager' ),
|
||||
'new_item_name' => __( 'New Site Name', 'hi-school-slide-manager' ),
|
||||
'menu_name' => __( 'Target Sites', 'hi-school-slide-manager' ),
|
||||
);
|
||||
|
||||
$site_args = array(
|
||||
'hierarchical' => false,
|
||||
'labels' => $site_labels,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array( 'slug' => 'slide-site' ),
|
||||
'show_in_rest' => true,
|
||||
);
|
||||
|
||||
register_taxonomy( self::SITE_TAXONOMY, array( self::POST_TYPE ), $site_args );
|
||||
|
||||
// Client taxonomy for organizing slides by client (optional)
|
||||
$client_labels = array(
|
||||
'name' => _x( 'Clients', 'taxonomy general name', 'hi-school-slide-manager' ),
|
||||
'singular_name' => _x( 'Client', 'taxonomy singular name', 'hi-school-slide-manager' ),
|
||||
'search_items' => __( 'Search Clients', 'hi-school-slide-manager' ),
|
||||
'all_items' => __( 'All Clients', 'hi-school-slide-manager' ),
|
||||
'parent_item' => __( 'Parent Client', 'hi-school-slide-manager' ),
|
||||
'parent_item_colon' => __( 'Parent Client:', 'hi-school-slide-manager' ),
|
||||
'edit_item' => __( 'Edit Client', 'hi-school-slide-manager' ),
|
||||
'update_item' => __( 'Update Client', 'hi-school-slide-manager' ),
|
||||
'add_new_item' => __( 'Add New Client', 'hi-school-slide-manager' ),
|
||||
'new_item_name' => __( 'New Client Name', 'hi-school-slide-manager' ),
|
||||
'menu_name' => __( 'Clients', 'hi-school-slide-manager' ),
|
||||
);
|
||||
|
||||
$client_args = array(
|
||||
'hierarchical' => false,
|
||||
'labels' => $client_labels,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array( 'slug' => 'slide-client' ),
|
||||
'show_in_rest' => true,
|
||||
);
|
||||
|
||||
register_taxonomy( self::CLIENT_TAXONOMY, array( self::POST_TYPE ), $client_args );
|
||||
|
||||
// Add default terms for the three companion sites
|
||||
$this->create_default_site_terms();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default site terms for the three companion sites
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function create_default_site_terms() {
|
||||
$default_sites = array();
|
||||
|
||||
// Use centralized method from HSSM_Main if available
|
||||
if ( class_exists( 'HSSM_Main' ) && method_exists( 'HSSM_Main', 'get_companion_sites' ) ) {
|
||||
$default_sites = HSSM_Main::get_companion_sites();
|
||||
} else {
|
||||
// Fallback
|
||||
$default_sites = array(
|
||||
array(
|
||||
'slug' => 'hi-school',
|
||||
'name' => 'Hi-School Pharmacy',
|
||||
'description' => 'Hi-School Pharmacy companion site'
|
||||
),
|
||||
array(
|
||||
'slug' => 'one-stop',
|
||||
'name' => 'One Stop Hardware',
|
||||
'description' => 'One Stop Hardware companion site'
|
||||
),
|
||||
array(
|
||||
'slug' => 'ace',
|
||||
'name' => 'Ace Hardware',
|
||||
'description' => 'Ace Hardware companion site'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $default_sites as $site ) {
|
||||
if ( ! term_exists( $site['slug'], self::SITE_TAXONOMY ) ) {
|
||||
wp_insert_term(
|
||||
$site['name'],
|
||||
self::SITE_TAXONOMY,
|
||||
array(
|
||||
'slug' => $site['slug'],
|
||||
'description' => $site['description']
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta boxes for slide fields
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function add_meta_boxes() {
|
||||
add_meta_box(
|
||||
'hssm_slide_details',
|
||||
__( 'Slide Details', 'hi-school-slide-manager' ),
|
||||
array( $this, 'slide_details_meta_box' ),
|
||||
self::POST_TYPE,
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
|
||||
add_meta_box(
|
||||
'hssm_background_image_help',
|
||||
__( 'Background Image Guidelines', 'hi-school-slide-manager' ),
|
||||
array( $this, 'background_image_help_meta_box' ),
|
||||
self::POST_TYPE,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
|
||||
add_meta_box(
|
||||
'hssm_slide_scheduling',
|
||||
__( 'Slide Scheduling', 'hi-school-slide-manager' ),
|
||||
array( $this, 'slide_scheduling_meta_box' ),
|
||||
self::POST_TYPE,
|
||||
'side',
|
||||
'default'
|
||||
);
|
||||
|
||||
add_meta_box(
|
||||
'hssm_slide_targeting',
|
||||
__( 'Site Targeting', 'hi-school-slide-manager' ),
|
||||
array( $this, 'slide_targeting_meta_box' ),
|
||||
self::POST_TYPE,
|
||||
'side',
|
||||
'default'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render slide details meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Post $post Current post object
|
||||
*/
|
||||
public function slide_details_meta_box( $post ) {
|
||||
wp_nonce_field( 'hssm_save_slide_meta', 'hssm_slide_meta_nonce' );
|
||||
|
||||
$subtitle = get_post_meta( $post->ID, '_hssm_subtitle', true );
|
||||
$buttons = get_post_meta( $post->ID, '_hssm_buttons', true );
|
||||
$order = get_post_meta( $post->ID, '_hssm_order', true );
|
||||
$hide_all_text = get_post_meta( $post->ID, '_hssm_hide_all_text', true );
|
||||
|
||||
// Initialize buttons array if empty
|
||||
if ( ! is_array( $buttons ) || empty( $buttons ) ) {
|
||||
$buttons = array(
|
||||
array( 'text' => 'Learn More', 'url' => '' )
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="hssm_subtitle"><?php _e( 'Subtitle', 'hi-school-slide-manager' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="hssm_subtitle" name="hssm_subtitle" value="<?php echo esc_attr( $subtitle ); ?>" class="regular-text" />
|
||||
<p class="description"><?php _e( 'Optional subtitle for the slide', 'hi-school-slide-manager' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label><?php _e( 'Call-to-Action Buttons', 'hi-school-slide-manager' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<div id="hssm-buttons-container">
|
||||
<?php foreach ( $buttons as $index => $button ) : ?>
|
||||
<div class="hssm-button-row" data-index="<?php echo esc_attr( $index ); ?>">
|
||||
<div class="hssm-button-fields">
|
||||
<label><?php _e( 'Button Text:', 'hi-school-slide-manager' ); ?></label>
|
||||
<input type="text" name="hssm_buttons[<?php echo esc_attr( $index ); ?>][text]" value="<?php echo esc_attr( $button['text'] ?: 'Learn More' ); ?>" class="regular-text" placeholder="Learn More" />
|
||||
|
||||
<label><?php _e( 'Button URL:', 'hi-school-slide-manager' ); ?></label>
|
||||
<input type="url" name="hssm_buttons[<?php echo esc_attr( $index ); ?>][url]" value="<?php echo esc_url( $button['url'] ?: '' ); ?>" class="regular-text" placeholder="https://example.com" />
|
||||
|
||||
<button type="button" class="button hssm-remove-button" <?php echo count( $buttons ) <= 1 ? 'style="display:none;"' : ''; ?>><?php _e( 'Remove', 'hi-school-slide-manager' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<button type="button" id="hssm-add-button" class="button button-secondary"><?php _e( 'Add Another Button', 'hi-school-slide-manager' ); ?></button>
|
||||
<span class="description"><?php _e( '(Maximum 5 buttons)', 'hi-school-slide-manager' ); ?></span>
|
||||
</p>
|
||||
|
||||
<p class="description"><?php _e( 'Add call-to-action buttons for the slide. The first button is typically the primary action.', 'hi-school-slide-manager' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="hssm_order"><?php _e( 'Display Order', 'hi-school-slide-manager' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="number" id="hssm_order" name="hssm_order" value="<?php echo esc_attr( $order ?: 0 ); ?>" min="0" />
|
||||
<p class="description"><?php _e( 'Display order for the slide (lower numbers show first). You can also drag and drop to reorder slides in the list view.', 'hi-school-slide-manager' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="hssm_hide_all_text"><?php _e( 'Text Visibility', 'hi-school-slide-manager' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" id="hssm_hide_all_text" name="hssm_hide_all_text" value="1" <?php checked( $hide_all_text, '1' ); ?> />
|
||||
<?php _e( 'Hide All Text (Image Only)', 'hi-school-slide-manager' ); ?>
|
||||
</label>
|
||||
<p class="description"><?php _e( 'If checked, the title, subtitle, and buttons will not be displayed on the slide.', 'hi-school-slide-manager' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<style>
|
||||
.hssm-button-row {
|
||||
margin-bottom: 15px;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.hssm-button-fields label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
font-weight: 600;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.hssm-button-fields input {
|
||||
margin-bottom: 8px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.hssm-remove-button {
|
||||
margin-top: 5px;
|
||||
color: #a00;
|
||||
border-color: #a00;
|
||||
}
|
||||
|
||||
.hssm-remove-button:hover {
|
||||
background: #a00;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function($) {
|
||||
let buttonIndex = <?php echo count( $buttons ); ?>;
|
||||
const maxButtons = 5;
|
||||
|
||||
// Add button functionality
|
||||
$('#hssm-add-button').on('click', function() {
|
||||
if ($('.hssm-button-row').length >= maxButtons) {
|
||||
alert('<?php _e( 'Maximum 5 buttons allowed', 'hi-school-slide-manager' ); ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
const newRow = `
|
||||
<div class="hssm-button-row" data-index="${buttonIndex}">
|
||||
<div class="hssm-button-fields">
|
||||
<label><?php _e( 'Button Text:', 'hi-school-slide-manager' ); ?></label>
|
||||
<input type="text" name="hssm_buttons[${buttonIndex}][text]" value="Learn More" class="regular-text" placeholder="Learn More" />
|
||||
|
||||
<label><?php _e( 'Button URL:', 'hi-school-slide-manager' ); ?></label>
|
||||
<input type="url" name="hssm_buttons[${buttonIndex}][url]" value="" class="regular-text" placeholder="https://example.com" />
|
||||
|
||||
<button type="button" class="button hssm-remove-button"><?php _e( 'Remove', 'hi-school-slide-manager' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('#hssm-buttons-container').append(newRow);
|
||||
buttonIndex++;
|
||||
|
||||
// Show remove buttons if we have more than 1
|
||||
if ($('.hssm-button-row').length > 1) {
|
||||
$('.hssm-remove-button').show();
|
||||
}
|
||||
|
||||
// Hide add button if we've reached the maximum
|
||||
if ($('.hssm-button-row').length >= maxButtons) {
|
||||
$('#hssm-add-button').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Remove button functionality
|
||||
$(document).on('click', '.hssm-remove-button', function() {
|
||||
$(this).closest('.hssm-button-row').remove();
|
||||
|
||||
// Hide remove buttons if we only have 1 left
|
||||
if ($('.hssm-button-row').length <= 1) {
|
||||
$('.hssm-remove-button').hide();
|
||||
}
|
||||
|
||||
// Show add button if we're under the maximum
|
||||
if ($('.hssm-button-row').length < maxButtons) {
|
||||
$('#hssm-add-button').show();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render background image help meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Post $post Current post object
|
||||
*/
|
||||
public function background_image_help_meta_box( $post ) {
|
||||
?>
|
||||
<div class="hssm-background-help">
|
||||
<h4><?php _e( 'Background Image Requirements:', 'hi-school-slide-manager' ); ?></h4>
|
||||
<ul>
|
||||
<li><?php _e( 'Recommended size: 1920x800px or larger', 'hi-school-slide-manager' ); ?></li>
|
||||
<li><?php _e( 'Aspect ratio: 16:9 or similar wide format', 'hi-school-slide-manager' ); ?></li>
|
||||
<li><?php _e( 'Format: JPG, PNG, or WebP', 'hi-school-slide-manager' ); ?></li>
|
||||
<li><?php _e( 'File size: Under 1MB for best performance', 'hi-school-slide-manager' ); ?></li>
|
||||
</ul>
|
||||
|
||||
<h4><?php _e( 'Design Tips:', 'hi-school-slide-manager' ); ?></h4>
|
||||
<ul>
|
||||
<li><?php _e( 'Keep important content away from edges', 'hi-school-slide-manager' ); ?></li>
|
||||
<li><?php _e( 'Ensure good contrast for text overlay', 'hi-school-slide-manager' ); ?></li>
|
||||
<li><?php _e( 'Consider mobile viewing (image will be cropped)', 'hi-school-slide-manager' ); ?></li>
|
||||
</ul>
|
||||
|
||||
<p><strong><?php _e( 'Note:', 'hi-school-slide-manager' ); ?></strong> <?php _e( 'This image will be used as a full-width background for the header slide on the selected companion sites.', 'hi-school-slide-manager' ); ?></p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hssm-background-help ul {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.hssm-background-help li {
|
||||
margin-bottom: 5px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.hssm-background-help h4 {
|
||||
margin-bottom: 8px;
|
||||
margin-top: 15px;
|
||||
color: #23282d;
|
||||
}
|
||||
.hssm-background-help h4:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render slide scheduling meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Post $post Current post object
|
||||
*/
|
||||
public function slide_scheduling_meta_box( $post ) {
|
||||
$start_date = get_post_meta( $post->ID, '_hssm_start_date', true );
|
||||
$end_date = get_post_meta( $post->ID, '_hssm_end_date', true );
|
||||
$auto_publish = get_post_meta( $post->ID, '_hssm_auto_publish', true );
|
||||
$auto_unpublish = get_post_meta( $post->ID, '_hssm_auto_unpublish', true );
|
||||
|
||||
?>
|
||||
<p>
|
||||
<label for="hssm_start_date"><strong><?php _e( 'Start Date', 'hi-school-slide-manager' ); ?></strong></label><br>
|
||||
<input type="datetime-local" id="hssm_start_date" name="hssm_start_date" value="<?php echo esc_attr( $start_date ); ?>" style="width: 100%;" />
|
||||
</p>
|
||||
<p>
|
||||
<label for="hssm_end_date"><strong><?php _e( 'End Date', 'hi-school-slide-manager' ); ?></strong></label><br>
|
||||
<input type="datetime-local" id="hssm_end_date" name="hssm_end_date" value="<?php echo esc_attr( $end_date ); ?>" style="width: 100%;" />
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="hssm_auto_publish" value="1" <?php checked( $auto_publish, '1' ); ?> />
|
||||
<?php _e( 'Auto-publish on start date', 'hi-school-slide-manager' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="hssm_auto_unpublish" value="1" <?php checked( $auto_unpublish, '1' ); ?> />
|
||||
<?php _e( 'Auto-unpublish on end date', 'hi-school-slide-manager' ); ?>
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render slide targeting meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Post $post Current post object
|
||||
*/
|
||||
public function slide_targeting_meta_box( $post ) {
|
||||
// Get current terms
|
||||
$current_sites = wp_get_post_terms( $post->ID, self::SITE_TAXONOMY, array( 'fields' => 'ids' ) );
|
||||
$current_clients = wp_get_post_terms( $post->ID, self::CLIENT_TAXONOMY, array( 'fields' => 'ids' ) );
|
||||
|
||||
// Get all available site terms
|
||||
$site_terms = get_terms( array(
|
||||
'taxonomy' => self::SITE_TAXONOMY,
|
||||
'hide_empty' => false,
|
||||
) );
|
||||
|
||||
// Get all available client terms
|
||||
$client_terms = get_terms( array(
|
||||
'taxonomy' => self::CLIENT_TAXONOMY,
|
||||
'hide_empty' => false,
|
||||
) );
|
||||
|
||||
?>
|
||||
<div class="hssm-targeting-section">
|
||||
<p><strong><?php _e( 'Target Sites', 'hi-school-slide-manager' ); ?></strong></p>
|
||||
<?php if ( ! empty( $site_terms ) && ! is_wp_error( $site_terms ) ) : ?>
|
||||
<?php foreach ( $site_terms as $term ) : ?>
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="hssm_target_sites[]" value="<?php echo esc_attr( $term->term_id ); ?>" <?php checked( in_array( $term->term_id, $current_sites ) ); ?> />
|
||||
<?php echo esc_html( $term->name ); ?>
|
||||
</label>
|
||||
<?php if ( $term->description ) : ?>
|
||||
<br><small class="description"><?php echo esc_html( $term->description ); ?></small>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<?php endforeach; ?>
|
||||
<?php else : ?>
|
||||
<p class="description"><?php _e( 'No sites available. Sites will be created automatically.', 'hi-school-slide-manager' ); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="hssm-targeting-section" style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
||||
<p><strong><?php _e( 'Client (Optional)', 'hi-school-slide-manager' ); ?></strong></p>
|
||||
<?php if ( ! empty( $client_terms ) && ! is_wp_error( $client_terms ) ) : ?>
|
||||
<select name="hssm_client" style="width: 100%;">
|
||||
<option value=""><?php _e( 'Select a client (optional)', 'hi-school-slide-manager' ); ?></option>
|
||||
<?php foreach ( $client_terms as $term ) : ?>
|
||||
<option value="<?php echo esc_attr( $term->term_id ); ?>" <?php selected( in_array( $term->term_id, $current_clients ) ); ?>>
|
||||
<?php echo esc_html( $term->name ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php else : ?>
|
||||
<p class="description">
|
||||
<?php _e( 'No clients available.', 'hi-school-slide-manager' ); ?>
|
||||
<a href="<?php echo admin_url( 'edit-tags.php?taxonomy=' . self::CLIENT_TAXONOMY . '&post_type=' . self::POST_TYPE ); ?>">
|
||||
<?php _e( 'Add clients here', 'hi-school-slide-manager' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hssm-targeting-section label {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
.hssm-targeting-section input[type="checkbox"] {
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Save meta field data
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $post_id Post ID
|
||||
*/
|
||||
public function save_meta_fields( $post_id ) {
|
||||
// Verify nonce
|
||||
if ( ! isset( $_POST['hssm_slide_meta_nonce'] ) || ! wp_verify_nonce( $_POST['hssm_slide_meta_nonce'], 'hssm_save_slide_meta' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is an autosave
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check post type
|
||||
if ( get_post_type( $post_id ) !== self::POST_TYPE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save meta fields
|
||||
$meta_fields = array(
|
||||
'hssm_subtitle' => 'sanitize_text_field',
|
||||
'hssm_order' => 'intval',
|
||||
'hssm_start_date' => 'sanitize_text_field',
|
||||
'hssm_end_date' => 'sanitize_text_field',
|
||||
);
|
||||
|
||||
foreach ( $meta_fields as $field => $sanitize_function ) {
|
||||
if ( isset( $_POST[ $field ] ) ) {
|
||||
$value = call_user_func( $sanitize_function, $_POST[ $field ] );
|
||||
update_post_meta( $post_id, '_' . $field, $value );
|
||||
}
|
||||
}
|
||||
|
||||
// Save buttons array
|
||||
if ( isset( $_POST['hssm_buttons'] ) && is_array( $_POST['hssm_buttons'] ) ) {
|
||||
$buttons = array();
|
||||
foreach ( $_POST['hssm_buttons'] as $button_data ) {
|
||||
if ( ! empty( $button_data['text'] ) || ! empty( $button_data['url'] ) ) {
|
||||
$buttons[] = array(
|
||||
'text' => sanitize_text_field( $button_data['text'] ),
|
||||
'url' => esc_url_raw( $button_data['url'] )
|
||||
);
|
||||
}
|
||||
}
|
||||
// Limit to 5 buttons maximum
|
||||
$buttons = array_slice( $buttons, 0, 5 );
|
||||
update_post_meta( $post_id, '_hssm_buttons', $buttons );
|
||||
} else {
|
||||
// If no buttons, set default
|
||||
update_post_meta( $post_id, '_hssm_buttons', array(
|
||||
array( 'text' => 'Learn More', 'url' => '' )
|
||||
) );
|
||||
}
|
||||
|
||||
// Save checkbox fields
|
||||
$checkbox_fields = array( 'hssm_auto_publish', 'hssm_auto_unpublish', 'hssm_hide_all_text' );
|
||||
foreach ( $checkbox_fields as $field ) {
|
||||
$value = isset( $_POST[ $field ] ) ? '1' : '0';
|
||||
update_post_meta( $post_id, '_' . $field, $value );
|
||||
}
|
||||
|
||||
// Save target sites taxonomy
|
||||
if ( isset( $_POST['hssm_target_sites'] ) && is_array( $_POST['hssm_target_sites'] ) ) {
|
||||
$target_sites = array_map( 'intval', $_POST['hssm_target_sites'] );
|
||||
wp_set_post_terms( $post_id, $target_sites, self::SITE_TAXONOMY );
|
||||
} else {
|
||||
wp_set_post_terms( $post_id, array(), self::SITE_TAXONOMY );
|
||||
}
|
||||
|
||||
// Save client taxonomy
|
||||
if ( isset( $_POST['hssm_client'] ) && ! empty( $_POST['hssm_client'] ) ) {
|
||||
$client_id = intval( $_POST['hssm_client'] );
|
||||
wp_set_post_terms( $post_id, array( $client_id ), self::CLIENT_TAXONOMY );
|
||||
} else {
|
||||
wp_set_post_terms( $post_id, array(), self::CLIENT_TAXONOMY );
|
||||
}
|
||||
|
||||
// Send email notification for new slides
|
||||
if ( get_post_status( $post_id ) === 'publish' && ! get_post_meta( $post_id, '_hssm_notification_sent', true ) ) {
|
||||
$this->send_new_slide_notification( $post_id );
|
||||
update_post_meta( $post_id, '_hssm_notification_sent', '1' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom columns to the slides list table
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $columns Current columns
|
||||
* @return array Modified columns
|
||||
*/
|
||||
public function add_custom_columns( $columns ) {
|
||||
$new_columns = array();
|
||||
$new_columns['cb'] = $columns['cb'];
|
||||
$new_columns['title'] = $columns['title'];
|
||||
$new_columns['target_sites'] = __( 'Target Sites', 'hi-school-slide-manager' );
|
||||
$new_columns['client'] = __( 'Client', 'hi-school-slide-manager' );
|
||||
$new_columns['schedule'] = __( 'Schedule', 'hi-school-slide-manager' );
|
||||
$new_columns['order'] = __( 'Order', 'hi-school-slide-manager' );
|
||||
$new_columns['date'] = $columns['date'];
|
||||
|
||||
return $new_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display custom column content
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $column Column name
|
||||
* @param int $post_id Post ID
|
||||
*/
|
||||
public function custom_column_content( $column, $post_id ) {
|
||||
switch ( $column ) {
|
||||
case 'title':
|
||||
// Add drag handle to title column
|
||||
echo '<span class="hssm-drag-handle dashicons dashicons-menu" title="' . esc_attr__( 'Drag to reorder', 'hi-school-slide-manager' ) . '"></span>';
|
||||
break;
|
||||
|
||||
case 'target_sites':
|
||||
$site_terms = wp_get_post_terms( $post_id, self::SITE_TAXONOMY );
|
||||
if ( ! empty( $site_terms ) && ! is_wp_error( $site_terms ) ) {
|
||||
$site_names = wp_list_pluck( $site_terms, 'name' );
|
||||
echo esc_html( implode( ', ', $site_names ) );
|
||||
} else {
|
||||
echo '<em>' . __( 'No sites selected', 'hi-school-slide-manager' ) . '</em>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'client':
|
||||
$client_terms = wp_get_post_terms( $post_id, self::CLIENT_TAXONOMY );
|
||||
if ( ! empty( $client_terms ) && ! is_wp_error( $client_terms ) ) {
|
||||
echo esc_html( $client_terms[0]->name );
|
||||
} else {
|
||||
echo '<em>' . __( 'No client', 'hi-school-slide-manager' ) . '</em>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'schedule':
|
||||
$start_date = get_post_meta( $post_id, '_hssm_start_date', true );
|
||||
$end_date = get_post_meta( $post_id, '_hssm_end_date', true );
|
||||
|
||||
if ( $start_date || $end_date ) {
|
||||
echo '<div>';
|
||||
if ( $start_date ) {
|
||||
echo '<strong>' . __( 'Start:', 'hi-school-slide-manager' ) . '</strong> ' . esc_html( date( 'M j, Y g:i A', strtotime( $start_date ) ) ) . '<br>';
|
||||
}
|
||||
if ( $end_date ) {
|
||||
echo '<strong>' . __( 'End:', 'hi-school-slide-manager' ) . '</strong> ' . esc_html( date( 'M j, Y g:i A', strtotime( $end_date ) ) );
|
||||
}
|
||||
echo '</div>';
|
||||
} else {
|
||||
echo '<em>' . __( 'No schedule set', 'hi-school-slide-manager' ) . '</em>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'order':
|
||||
$order = get_post_meta( $post_id, '_hssm_order', true );
|
||||
echo esc_html( $order ?: '0' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email notification for new slide
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $post_id Post ID
|
||||
*/
|
||||
private function send_new_slide_notification( $post_id ) {
|
||||
$notification_emails = get_option( 'hssm_notification_emails', array() );
|
||||
|
||||
if ( empty( $notification_emails ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$subject = sprintf( __( 'New Slide Created: %s', 'hi-school-slide-manager' ), $post->post_title );
|
||||
|
||||
$message = sprintf(
|
||||
__( 'A new slide has been created and published on %s.' . "\n\n" .
|
||||
'Title: %s' . "\n" .
|
||||
'Content: %s' . "\n" .
|
||||
'Edit URL: %s', 'hi-school-slide-manager' ),
|
||||
get_bloginfo( 'name' ),
|
||||
$post->post_title,
|
||||
wp_strip_all_tags( $post->post_content ),
|
||||
admin_url( 'post.php?post=' . $post_id . '&action=edit' )
|
||||
);
|
||||
|
||||
$headers = array( 'Content-Type: text/plain; charset=UTF-8' );
|
||||
|
||||
foreach ( $notification_emails as $email ) {
|
||||
wp_mail( $email, $subject, $message, $headers );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for scheduled slides that need status updates
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function check_scheduled_slides() {
|
||||
$current_time = current_time( 'mysql' );
|
||||
|
||||
// Auto-publish slides that should start now
|
||||
$slides_to_publish = get_posts( array(
|
||||
'post_type' => self::POST_TYPE,
|
||||
'post_status' => 'draft',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => '_hssm_auto_publish',
|
||||
'value' => '1',
|
||||
'compare' => '='
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_start_date',
|
||||
'value' => $current_time,
|
||||
'compare' => '<='
|
||||
)
|
||||
),
|
||||
'numberposts' => -1
|
||||
) );
|
||||
|
||||
foreach ( $slides_to_publish as $slide ) {
|
||||
wp_update_post( array(
|
||||
'ID' => $slide->ID,
|
||||
'post_status' => 'publish'
|
||||
) );
|
||||
}
|
||||
|
||||
// Auto-unpublish slides that should end now
|
||||
$slides_to_unpublish = get_posts( array(
|
||||
'post_type' => self::POST_TYPE,
|
||||
'post_status' => 'publish',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => '_hssm_auto_unpublish',
|
||||
'value' => '1',
|
||||
'compare' => '='
|
||||
),
|
||||
array(
|
||||
'key' => '_hssm_end_date',
|
||||
'value' => $current_time,
|
||||
'compare' => '<='
|
||||
)
|
||||
),
|
||||
'numberposts' => -1
|
||||
) );
|
||||
|
||||
foreach ( $slides_to_unpublish as $slide ) {
|
||||
wp_update_post( array(
|
||||
'ID' => $slide->ID,
|
||||
'post_status' => 'draft'
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts for drag-and-drop functionality
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $hook Current admin page hook
|
||||
*/
|
||||
public function enqueue_admin_scripts( $hook ) {
|
||||
global $post_type;
|
||||
|
||||
if ( $hook === 'edit.php' && $post_type === self::POST_TYPE ) {
|
||||
wp_enqueue_script( 'jquery-ui-sortable' );
|
||||
wp_enqueue_script(
|
||||
'hssm-admin-sortable',
|
||||
HSSM_PLUGIN_URL . 'assets/js/admin-sortable.js',
|
||||
array( 'jquery', 'jquery-ui-sortable' ),
|
||||
HSSM_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script( 'hssm-admin-sortable', 'hssmAjax', array(
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'hssm_update_slide_order' ),
|
||||
'postType' => self::POST_TYPE
|
||||
) );
|
||||
|
||||
// Add drag handle styles
|
||||
wp_add_inline_style( 'wp-admin', '
|
||||
.wp-list-table .hssm-drag-handle {
|
||||
cursor: move;
|
||||
color: #666;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.wp-list-table .hssm-drag-handle:hover {
|
||||
color: #000;
|
||||
}
|
||||
.wp-list-table tbody tr.ui-sortable-helper {
|
||||
background: #f0f0f0;
|
||||
border: 2px dashed #0073aa;
|
||||
}
|
||||
.wp-list-table tbody tr.ui-sortable-placeholder {
|
||||
background: #e0e0e0;
|
||||
height: 40px;
|
||||
}
|
||||
' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for updating slide order
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function ajax_update_slide_order() {
|
||||
// Verify nonce and permissions
|
||||
if ( ! wp_verify_nonce( $_POST['nonce'], 'hssm_update_slide_order' ) || ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_die( 'Unauthorized' );
|
||||
}
|
||||
|
||||
if ( isset( $_POST['slide_ids'] ) && is_array( $_POST['slide_ids'] ) ) {
|
||||
$slide_ids = array_map( 'intval', $_POST['slide_ids'] );
|
||||
|
||||
foreach ( $slide_ids as $index => $slide_id ) {
|
||||
update_post_meta( $slide_id, '_hssm_order', $index );
|
||||
}
|
||||
|
||||
wp_send_json_success( array( 'message' => __( 'Slide order updated successfully', 'hi-school-slide-manager' ) ) );
|
||||
}
|
||||
|
||||
wp_send_json_error( array( 'message' => __( 'Failed to update slide order', 'hi-school-slide-manager' ) ) );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user