email working - remove clients - preview polish

This commit is contained in:
stack_admn committed 2026-11-01 21:41:37 -08:00
1 parent 2b1424d127
commit da83beaec3
19 files changed
+1342 -1060

No files matched your search

+186 -250
View File
@@ -38,13 +38,12 @@ class HSSM_Frontend {
// Register page template
add_filter( 'theme_page_templates', array( $this, 'register_page_template' ) );
add_filter( 'page_template', array( $this, 'load_page_template' ) );
add_filter( 'request', array( $this, 'keep_preview_page_query' ) );
// Handle page routing
add_action( 'template_redirect', array( $this, 'handle_page_routing' ) );
// AJAX handlers
add_action( 'wp_ajax_hssm_save_slide', array( $this, 'ajax_save_slide' ) );
add_action( 'wp_ajax_hssm_upload_image', array( $this, 'ajax_upload_image' ) );
add_action( 'wp_ajax_hssm_get_slides', array( $this, 'ajax_get_slides' ) );
add_action( 'wp_ajax_hssm_get_slide_for_edit', array( $this, 'ajax_get_slide_for_edit' ) );
add_action( 'wp_ajax_hssm_delete_slide', array( $this, 'ajax_delete_slide' ) );
@@ -54,7 +53,6 @@ class HSSM_Frontend {
add_action( 'wp_ajax_hssm_duplicate_slide', array( $this, 'ajax_duplicate_slide' ) );
add_action( 'wp_ajax_hssm_toggle_slide_status', array( $this, 'ajax_toggle_slide_status' ) );
add_action( 'wp_ajax_hssm_approve_slide', array( $this, 'ajax_approve_slide' ) );
add_action( 'wp_ajax_hssm_preview_slides', array( $this, 'ajax_preview_slides' ) );
// Notification Settings AJAX
add_action( 'wp_ajax_hssm_get_notification_settings', array( $this, 'ajax_get_notification_settings' ) );
@@ -63,9 +61,12 @@ class HSSM_Frontend {
// Frontend assets
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
add_filter( 'body_class', array( $this, 'add_tool_page_body_class' ) );
// Filter slides query on preview page
add_action( 'pre_get_posts', array( $this, 'filter_preview_slides_query' ) );
add_action( 'admin_init', array( $this, 'maybe_nest_plugin_pages' ) );
}
/**
@@ -114,6 +115,30 @@ class HSSM_Frontend {
return $template;
}
/**
* Prevent ?hssm_slide= from hijacking the preview page.
*
* The slide CPT used query_var "hssm_slide", so that GET param turned
* the page request into a missing slide query and WordPress 404'd.
*
* @since 1.1.3
* @param array $query_vars Parsed request vars.
* @return array
*/
public function keep_preview_page_query( $query_vars ) {
if ( empty( $query_vars['pagename'] ) ) {
return $query_vars;
}
unset( $query_vars['hssm_slide'], $query_vars['hssm_slide_item'] );
if ( isset( $query_vars['name'] ) && isset( $query_vars['post_type'] ) && 'hssm_slide' === $query_vars['post_type'] ) {
unset( $query_vars['name'], $query_vars['post_type'] );
}
return $query_vars;
}
/**
* Handle frontend page routing
@@ -122,15 +147,39 @@ class HSSM_Frontend {
*/
public function handle_page_routing() {
global $post;
// Check if this is our slide dashboard page
if ( is_page() && get_page_template_slug( $post->ID ) === 'page-slide-dashboard.php' ) {
// Check authentication with Members plugin
if ( ! $this->check_dashboard_access() ) {
$this->redirect_to_login();
return;
}
if ( ! is_page() || ! $post ) {
return;
}
$template = get_page_template_slug( $post->ID );
if ( ! in_array( $template, array( 'page-slide-dashboard.php', 'page-slide-preview.php' ), true ) ) {
return;
}
if ( ! $this->check_dashboard_access() ) {
$this->redirect_to_login();
}
}
/**
* Mark dashboard and preview as plugin tool pages for CSS.
*
* @since 1.1.5
* @param array $classes Body classes.
* @return array
*/
public function add_tool_page_body_class( $classes ) {
if ( ! is_page() ) {
return $classes;
}
$template = get_page_template_slug();
if ( in_array( $template, array( 'page-slide-dashboard.php', 'page-slide-preview.php' ), true ) ) {
$classes[] = 'hssm-tool-page';
}
return $classes;
}
/**
@@ -182,8 +231,8 @@ class HSSM_Frontend {
* @since 1.0.0
*/
private function redirect_to_login() {
$login_url = wp_login_url( get_permalink() );
wp_redirect( $login_url );
$redirect = home_url( add_query_arg( array() ) );
wp_safe_redirect( wp_login_url( $redirect ) );
exit;
}
@@ -207,12 +256,13 @@ class HSSM_Frontend {
if ( ! $is_dashboard && ! $is_preview ) {
return;
}
// Check access (preview page may be public, but dashboard requires auth)
if ( $is_dashboard && ! $this->check_dashboard_access() ) {
if ( ! $this->check_dashboard_access() ) {
return;
}
wp_enqueue_style( 'dashicons' );
// Enqueue styles (both pages need dashboard CSS)
wp_enqueue_style(
'hssm-dashboard',
@@ -239,6 +289,7 @@ class HSSM_Frontend {
wp_localize_script( 'hssm-dashboard', 'hssmDashboard', array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'hssm_dashboard_nonce' ),
'previewUrl' => hssm_get_page_url_by_template( 'page-slide-preview.php', '/slide-preview/' ),
'strings' => array(
'save_success' => __( 'Slide saved successfully!', 'hi-school-slide-manager' ),
'save_error' => __( 'Error saving slide. Please try again.', 'hi-school-slide-manager' ),
@@ -246,6 +297,9 @@ class HSSM_Frontend {
'delete_success' => __( 'Slide deleted successfully!', 'hi-school-slide-manager' ),
'required_fields' => __( 'Please fill in all required fields.', 'hi-school-slide-manager' ),
'max_forms' => __( 'Maximum 5 slide forms allowed at once.', 'hi-school-slide-manager' ),
'preview_save_first' => __( 'Save the slide first, then preview it as a banner.', 'hi-school-slide-manager' ),
'approve_confirm' => __( 'Approve this slide?', 'hi-school-slide-manager' ),
'approve_success' => __( 'Slide approved.', 'hi-school-slide-manager' ),
),
) );
@@ -286,188 +340,6 @@ class HSSM_Frontend {
}
}
/**
* AJAX handler for image uploads
*
* @since 1.0.0
*/
public function ajax_upload_image() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return;
}
// This is handled by media_handle_upload in standard WP AJAX flow usually,
// but if we need custom handling it goes here.
// For now, assuming standard media uploader is used on frontend which might not need this
// if using wp.media JS library directly.
// However, if we need to process the upload specifically for this plugin:
if ( ! empty( $_FILES['file'] ) ) {
$file = $_FILES['file'];
$attachment_id = media_handle_upload( 'file', 0 );
if ( is_wp_error( $attachment_id ) ) {
wp_send_json_error( array( 'message' => 'Upload failed: ' . $attachment_id->get_error_message() ) );
} else {
wp_send_json_success( array(
'attachment_id' => $attachment_id,
'url' => wp_get_attachment_url( $attachment_id )
) );
}
} else {
wp_send_json_error( array( 'message' => 'No file provided' ) );
}
}
/**
* AJAX handler for previewing slides
*
* @since 1.0.0
*/
public function ajax_preview_slides() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! current_user_can( 'read' ) ) {
wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return;
}
$site_slug = isset( $_POST['site'] ) ? sanitize_text_field( $_POST['site'] ) : '';
$date = isset( $_POST['date'] ) ? sanitize_text_field( $_POST['date'] ) : current_time( 'Y-m-d' );
// This logic mirrors hssm_get_preview_slides_for_date in the template
// We need to fetch slides that would be active on this date for this site
$meta_query = array(
'relation' => 'AND',
array(
'relation' => 'OR',
array(
'key' => '_hssm_start_date',
'value' => '',
'compare' => '=',
),
array(
'key' => '_hssm_start_date',
'value' => $date,
'compare' => '<=',
),
array(
'key' => '_hssm_start_date',
'compare' => 'NOT EXISTS',
),
),
array(
'relation' => 'OR',
array(
'key' => '_hssm_end_date',
'value' => $date,
'compare' => '>=',
),
array(
'key' => '_hssm_end_date',
'compare' => 'NOT EXISTS',
),
array(
'key' => '_hssm_end_date',
'value' => '',
'compare' => '=',
),
),
);
$tax_query = array();
if ( ! empty( $site_slug ) ) {
$tax_query[] = array(
'taxonomy' => 'hssm_site',
'field' => 'slug',
'terms' => $site_slug,
);
}
$args = array(
'post_type' => 'hssm_slide',
'post_status' => array( 'publish', 'future' ),
'posts_per_page' => -1,
'orderby' => 'meta_value_num',
'meta_key' => '_hssm_order',
'order' => 'ASC',
'meta_query' => $meta_query,
'tax_query' => $tax_query
);
$slides = get_posts( $args );
$formatted_slides = array();
foreach ( $slides as $slide ) {
$formatted_slides[] = $this->format_slide_for_frontend( $slide->ID );
}
wp_send_json_success( array( 'slides' => $formatted_slides ) );
}
/**
* AJAX handler for saving slides
*
* @since 1.0.0
*/
public function ajax_save_slide() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'hssm_dashboard_nonce' ) || ! $this->check_dashboard_access() ) {
wp_send_json_error( array( 'message' => 'Unauthorized' ) );
return;
}
// Sanitize and validate input
$slide_data = $this->sanitize_slide_data( $_POST );
if ( ! $this->validate_slide_data( $slide_data ) ) {
wp_send_json_error( array( 'message' => 'Invalid slide data' ) );
}
// Create the slide post
$post_args = array(
'post_type' => 'hssm_slide',
'post_title' => $slide_data['title'],
'post_content' => $slide_data['content'],
'post_excerpt' => $slide_data['content'],
'post_status' => $slide_data['post_status'],
'post_author' => get_current_user_id(),
);
// If scheduling for future, set the post date
if ( $slide_data['post_status'] === 'future' && ! empty( $slide_data['start_date'] ) ) {
$post_args['post_date'] = $slide_data['start_date'] . ' 00:00:00';
$post_args['post_date_gmt'] = get_gmt_from_date( $post_args['post_date'] );
}
$slide_id = wp_insert_post( $post_args );
if ( is_wp_error( $slide_id ) ) {
wp_send_json_error( array( 'message' => 'Failed to create slide' ) );
}
// Save meta data
$this->save_slide_meta( $slide_id, $slide_data );
// Save taxonomies
$this->save_slide_taxonomies( $slide_id, $slide_data );
// Set featured image if provided
if ( ! empty( $slide_data['featured_image_id'] ) ) {
set_post_thumbnail( $slide_id, $slide_data['featured_image_id'] );
}
wp_send_json_success( array(
'message' => 'Slide saved successfully!',
'slide_id' => $slide_id,
'slide_data' => $this->format_slide_for_frontend( $slide_id )
) );
}
/**
* AJAX handler for image uploads
*
* @since 1.0.0
*/
/**
* Sanitize slide data from form
*
@@ -504,10 +376,9 @@ class HSSM_Frontend {
'content' => wp_kses_post( $data['content'] ?? '' ),
'buttons' => $this->sanitize_buttons( $buttons ),
'target_sites' => array_map( 'sanitize_text_field', $target_sites ),
'client' => sanitize_text_field( $data['client'] ?? '' ),
'start_date' => sanitize_text_field( $data['start_date'] ?? '' ),
'end_date' => sanitize_text_field( $data['end_date'] ?? '' ),
'post_status' => in_array( $data['post_status'] ?? 'draft', array( 'draft', 'publish', 'future' ) ) ? $data['post_status'] : 'draft',
'post_status' => in_array( $data['post_status'] ?? 'draft', array( 'draft', 'publish', 'future' ), true ) ? ( $data['post_status'] ?? 'draft' ) : 'draft',
'auto_publish' => ! empty( $data['auto_publish'] ),
'auto_unpublish' => ! empty( $data['auto_unpublish'] ),
'order' => intval( $data['order'] ?? 0 ),
@@ -646,11 +517,6 @@ class HSSM_Frontend {
} else {
hssm_debug_log( 'No target sites to save for slide ' . $slide_id );
}
// Set client
if ( ! empty( $data['client'] ) ) {
wp_set_post_terms( $slide_id, array( $data['client'] ), 'hssm_client' );
}
}
/**
@@ -671,7 +537,6 @@ class HSSM_Frontend {
'buttons' => get_post_meta( $slide_id, '_hssm_buttons', true ),
'featured_image' => get_the_post_thumbnail_url( $slide_id, 'medium' ),
'target_sites' => wp_get_post_terms( $slide_id, 'hssm_site', array( 'fields' => 'slugs' ) ),
'client' => wp_get_post_terms( $slide_id, 'hssm_client', array( 'fields' => 'slugs' ) ),
'status' => $slide->post_status,
'created' => $slide->post_date,
);
@@ -699,8 +564,10 @@ class HSSM_Frontend {
// Get filter parameters
$status_filter = isset( $_POST['status_filter'] ) ? sanitize_text_field( $_POST['status_filter'] ) : '';
if ( 'scheduled' === $status_filter ) {
$status_filter = 'future';
}
$site_filter = isset( $_POST['site_filter'] ) ? sanitize_text_field( $_POST['site_filter'] ) : '';
$client_filter = isset( $_POST['client_filter'] ) ? sanitize_text_field( $_POST['client_filter'] ) : '';
$month_filter = isset( $_POST['month_filter'] ) ? sanitize_text_field( $_POST['month_filter'] ) : ''; // Format: YYYY-MM
$year_filter = isset( $_POST['year_filter'] ) ? intval( $_POST['year_filter'] ) : 0;
@@ -734,21 +601,6 @@ class HSSM_Frontend {
}
}
// Add client filter using taxonomy query
if ( ! empty( $client_filter ) ) {
// Get client term by slug or ID
$client_term = is_numeric( $client_filter )
? get_term( intval( $client_filter ), 'hssm_client' )
: get_term_by( 'slug', $client_filter, 'hssm_client' );
if ( $client_term && ! is_wp_error( $client_term ) ) {
$args['tax_query'][] = array(
'taxonomy' => 'hssm_client',
'field' => 'term_id',
'terms' => $client_term->term_id,
);
}
}
// Add month/year filter using date query
if ( ! empty( $month_filter ) ) {
// Format: YYYY-MM
@@ -876,9 +728,11 @@ class HSSM_Frontend {
$site_terms = wp_get_post_terms( $slide->ID, 'hssm_site', array( 'fields' => 'all' ) );
$site_names = array();
$site_slugs = array();
if ( ! empty( $site_terms ) && ! is_wp_error( $site_terms ) ) {
foreach ( $site_terms as $site_term ) {
$site_names[] = $site_term->name;
$site_slugs[] = $site_term->slug;
}
}
@@ -900,8 +754,8 @@ class HSSM_Frontend {
'status' => $slide->post_status,
'status_display' => $status_display,
'sites' => $site_names,
'site_slugs' => $site_slugs,
'target_sites' => $site_names,
'client' => $this->get_slide_client_name( $slide->ID ),
'start_date' => $this->format_dashboard_date( $start_date ),
'end_date' => $this->format_dashboard_date( $end_date ),
'auto_publish' => isset( $slide_meta['_hssm_auto_publish'][0] ) ? $slide_meta['_hssm_auto_publish'][0] : '',
@@ -965,8 +819,7 @@ class HSSM_Frontend {
return;
}
// Check permissions
if ( ! current_user_can( 'edit_posts' ) ) {
if ( ! $this->check_dashboard_access() ) {
wp_send_json_error( array( 'message' => 'Permission denied' ) );
return;
}
@@ -994,9 +847,9 @@ class HSSM_Frontend {
if ( $result ) {
hssm_debug_log( "Slide $slide_id approved with action: $approve_action" );
// Trigger 2: Notification to Creator (Author) when approved
$notifications = new HSSM_Notifications();
$notifications->notify_creator_slide_approved( $slide_id );
$notifications->notify_itc_slide_approved( $slide_id );
wp_send_json_success( array(
'message' => 'Slide approved successfully',
@@ -1301,21 +1154,6 @@ class HSSM_Frontend {
return implode( ', ', $site_names );
}
/**
* Get client name for a slide from taxonomy
*
* @since 1.0.0
* @param int $slide_id Slide ID
* @return string Client name or empty string
*/
private function get_slide_client_name( $slide_id ) {
$client_terms = wp_get_post_terms( $slide_id, 'hssm_client', array( 'fields' => 'all' ) );
if ( ! empty( $client_terms ) && ! is_wp_error( $client_terms ) ) {
return $client_terms[0]->name;
}
return '';
}
/**
* Get available sites for targeting
@@ -1355,6 +1193,112 @@ class HSSM_Frontend {
)
);
}
/**
* Nest preview and confirmation pages under the slide dashboard.
*
* @since 1.1.3
*/
public function maybe_nest_plugin_pages() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$dashboard_query = new WP_Query(
array(
'post_type' => 'page',
'post_status' => array( 'publish', 'private' ),
'posts_per_page' => 1,
'no_found_rows' => true,
'meta_key' => '_wp_page_template',
'meta_value' => 'page-slide-dashboard.php',
)
);
if ( ! $dashboard_query->have_posts() ) {
wp_reset_postdata();
return;
}
$parent_id = (int) $dashboard_query->posts[0]->ID;
wp_reset_postdata();
$this->maybe_create_confirmation_page( $parent_id );
$child_templates = array( 'page-slide-preview.php', 'page-slide-confirmation.php' );
foreach ( $child_templates as $template ) {
$pages_query = new WP_Query(
array(
'post_type' => 'page',
'post_status' => array( 'publish', 'private' ),
'posts_per_page' => 20,
'no_found_rows' => true,
'meta_key' => '_wp_page_template',
'meta_value' => $template,
)
);
if ( ! $pages_query->have_posts() ) {
wp_reset_postdata();
continue;
}
foreach ( $pages_query->posts as $page ) {
if ( (int) $page->ID === $parent_id ) {
continue;
}
if ( (int) $page->post_parent !== $parent_id ) {
wp_update_post(
array(
'ID' => $page->ID,
'post_parent' => $parent_id,
)
);
}
}
wp_reset_postdata();
}
}
/**
* Create the ITC confirmation page when it is missing.
*
* @since 1.1.3
* @param int $parent_id Dashboard page ID.
*/
private function maybe_create_confirmation_page( $parent_id ) {
$existing = new WP_Query(
array(
'post_type' => 'page',
'post_status' => array( 'publish', 'private' ),
'posts_per_page' => 1,
'no_found_rows' => true,
'meta_key' => '_wp_page_template',
'meta_value' => 'page-slide-confirmation.php',
)
);
if ( $existing->have_posts() ) {
wp_reset_postdata();
return;
}
wp_reset_postdata();
wp_insert_post(
array(
'post_title' => 'Slide Confirmation',
'post_name' => 'slide-confirmation',
'post_status' => 'publish',
'post_type' => 'page',
'post_parent' => absint( $parent_id ),
'meta_input' => array(
'_wp_page_template' => 'page-slide-confirmation.php',
),
)
);
}
/**
* AJAX handler for duplicating slides
@@ -1439,14 +1383,6 @@ class HSSM_Frontend {
if ( is_wp_error( $updated ) ) {
wp_send_json_error( array( 'message' => 'Failed to update slide status' ) );
}
// Trigger notification if published via toggle?
// Logic check: If toggled to publish, it means it's Live.
if ( $new_status === 'publish' ) {
// Trigger 3: Notification to ITCGuys when slide goes Live (via manual toggle)
$notifications = new HSSM_Notifications();
$notifications->notify_itc_slide_live( $slide_id );
}
wp_send_json_success( array(
'message' => 'Slide status updated successfully',