upskill-event-manager/includes/class-hvac-import-export-manager.php
Ben a74c273b1d feat: complete master trainer area audit and implementation
Systematic audit and implementation of missing Master Trainer functionality
with comprehensive WordPress best practices and security implementation.

## Features Implemented
- Master Events Overview (/master-trainer/events/) - KPI dashboard with filtering
- Import/Export Data Management (/master-trainer/import-export/) - CSV operations
- Communication Templates (/trainer/communication-templates/) - Professional templates
- Enhanced Announcements (/master-trainer/announcements/) - Dynamic shortcode integration
- Pending Approvals System (/master-trainer/pending-approvals/) - Workflow management

## Navigation & UX Improvements
- Removed redundant Events link from top-level navigation menu
- Reorganized administrative functions under Tools dropdown
- Enhanced navigation clarity and professional appearance
- Full responsive design with accessibility compliance

## Architecture & Security
- 5 new singleton manager classes following WordPress patterns
- Comprehensive role-based access control (hvac_master_trainer)
- Complete security implementation (nonces, sanitization, escaping)
- Performance optimizations with transient caching and conditional loading
- Professional error handling and user feedback systems

## Files Added (16 new files)
- 4 manager classes: Import/Export, Events Overview, Pending Approvals, Communication Templates
- 4 CSS files with responsive design and accessibility features
- 4 JavaScript files with AJAX functionality and error handling
- 2 new templates: Import/Export, Pending Approvals
- 2 enhanced templates: Events Overview, Communication Templates

## Files Modified (14 files)
- Core system integration in Plugin, Page Manager, Scripts/Styles classes
- Navigation system cleanup in Master Menu System
- Enhanced access control and role management
- Template updates for dynamic content integration

## Testing & Deployment
- Comprehensive testing with Playwright automation
- Successful staging deployment and verification
- All 5 missing pages now fully functional
- Navigation improvements verified working

Resolves master trainer area audit requirements with production-ready implementation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:56:42 -03:00

866 lines
No EOL
27 KiB
PHP

<?php
/**
* HVAC Import Export Manager
*
* Manages data import/export functionality for master trainers
*
* @package HVAC_Community_Events
* @since 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC_Import_Export_Manager class
*/
class HVAC_Import_Export_Manager {
/**
* Instance
*
* @var HVAC_Import_Export_Manager
*/
private static $instance = null;
/**
* Script version for cache busting
*
* @var string
*/
private $version;
/**
* Get instance
*
* @return HVAC_Import_Export_Manager
*/
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->version = defined('HVAC_PLUGIN_VERSION') ? HVAC_PLUGIN_VERSION : '1.0.0';
$this->init_hooks();
}
/**
* Initialize hooks
*
* @return void
*/
private function init_hooks() {
// AJAX handlers for import/export operations
add_action('wp_ajax_hvac_export_trainers', array($this, 'export_trainers'));
add_action('wp_ajax_hvac_export_events', array($this, 'export_events'));
add_action('wp_ajax_hvac_export_user_profiles', array($this, 'export_user_profiles'));
add_action('wp_ajax_hvac_import_trainer_profiles', array($this, 'import_trainer_profiles'));
add_action('wp_ajax_hvac_import_events', array($this, 'import_events'));
add_action('wp_ajax_hvac_bulk_update_users', array($this, 'bulk_update_users'));
// Asset loading for import-export page
add_action('wp_enqueue_scripts', array($this, 'enqueue_assets'));
}
/**
* Check if current page is import-export page
*
* @return bool
*/
private function is_import_export_page() {
$current_url = $_SERVER['REQUEST_URI'];
return (strpos($current_url, '/master-trainer/import-export/') !== false);
}
/**
* Enqueue assets for import-export page
*
* @return void
*/
public function enqueue_assets() {
if (!$this->is_import_export_page()) {
return;
}
// Enqueue CSS
wp_enqueue_style(
'hvac-import-export',
HVAC_PLUGIN_URL . 'assets/css/hvac-import-export.css',
array(),
$this->version
);
// Enqueue JavaScript
wp_enqueue_script(
'hvac-import-export',
HVAC_PLUGIN_URL . 'assets/js/hvac-import-export.js',
array('jquery'),
$this->version,
true
);
// Localize script
wp_localize_script('hvac-import-export', 'hvac_import_export', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('hvac_import_export_nonce'),
'strings' => array(
'processing' => __('Processing...', 'hvac-community-events'),
'export_complete' => __('Export completed successfully!', 'hvac-community-events'),
'import_complete' => __('Import completed successfully!', 'hvac-community-events'),
'error' => __('An error occurred. Please try again.', 'hvac-community-events'),
'confirm_import' => __('Are you sure you want to import this data? This action cannot be undone.', 'hvac-community-events'),
'select_file' => __('Please select a file to import.', 'hvac-community-events'),
'invalid_file' => __('Invalid file type. Please select a CSV file.', 'hvac-community-events'),
),
));
}
/**
* Export trainers to CSV
*
* @return void
*/
public function export_trainers() {
// Security checks
if (!check_ajax_referer('hvac_import_export_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Security check failed'));
}
if (!$this->check_master_trainer_permission()) {
wp_send_json_error(array('message' => 'Insufficient permissions'));
}
try {
$trainers = $this->get_all_trainers();
// Create CSV data
$csv_data = $this->create_trainers_csv($trainers);
// Generate filename with timestamp
$filename = 'hvac_trainers_export_' . date('Y-m-d_H-i-s') . '.csv';
wp_send_json_success(array(
'data' => $csv_data,
'filename' => $filename,
'count' => count($trainers),
'message' => sprintf(__('%d trainers exported successfully', 'hvac-community-events'), count($trainers))
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Export failed: ' . $e->getMessage()));
}
}
/**
* Export events to CSV
*
* @return void
*/
public function export_events() {
// Security checks
if (!check_ajax_referer('hvac_import_export_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Security check failed'));
}
if (!$this->check_master_trainer_permission()) {
wp_send_json_error(array('message' => 'Insufficient permissions'));
}
try {
$events = $this->get_all_events();
// Create CSV data
$csv_data = $this->create_events_csv($events);
// Generate filename with timestamp
$filename = 'hvac_events_export_' . date('Y-m-d_H-i-s') . '.csv';
wp_send_json_success(array(
'data' => $csv_data,
'filename' => $filename,
'count' => count($events),
'message' => sprintf(__('%d events exported successfully', 'hvac-community-events'), count($events))
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Export failed: ' . $e->getMessage()));
}
}
/**
* Export user profiles with metadata to CSV
*
* @return void
*/
public function export_user_profiles() {
// Security checks
if (!check_ajax_referer('hvac_import_export_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Security check failed'));
}
if (!$this->check_master_trainer_permission()) {
wp_send_json_error(array('message' => 'Insufficient permissions'));
}
try {
$users = $this->get_all_users_with_metadata();
// Create CSV data
$csv_data = $this->create_user_profiles_csv($users);
// Generate filename with timestamp
$filename = 'hvac_user_profiles_export_' . date('Y-m-d_H-i-s') . '.csv';
wp_send_json_success(array(
'data' => $csv_data,
'filename' => $filename,
'count' => count($users),
'message' => sprintf(__('%d user profiles exported successfully', 'hvac-community-events'), count($users))
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Export failed: ' . $e->getMessage()));
}
}
/**
* Import trainer profiles from CSV
*
* @return void
*/
public function import_trainer_profiles() {
// Security checks
if (!check_ajax_referer('hvac_import_export_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Security check failed'));
}
if (!$this->check_master_trainer_permission()) {
wp_send_json_error(array('message' => 'Insufficient permissions'));
}
// Check if file was uploaded
if (!isset($_FILES['import_file']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
wp_send_json_error(array('message' => 'No file uploaded or upload error'));
}
// Validate file type
if (!$this->validate_csv_file($_FILES['import_file'])) {
wp_send_json_error(array('message' => 'Invalid file type. Please upload a CSV file.'));
}
try {
$results = $this->process_trainer_profiles_import($_FILES['import_file']['tmp_name']);
wp_send_json_success(array(
'results' => $results,
'message' => sprintf(
__('Import completed: %d created, %d updated, %d errors', 'hvac-community-events'),
$results['created'],
$results['updated'],
$results['errors']
)
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Import failed: ' . $e->getMessage()));
}
}
/**
* Import events from CSV
*
* @return void
*/
public function import_events() {
// Security checks
if (!check_ajax_referer('hvac_import_export_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Security check failed'));
}
if (!$this->check_master_trainer_permission()) {
wp_send_json_error(array('message' => 'Insufficient permissions'));
}
// Check if file was uploaded
if (!isset($_FILES['import_file']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
wp_send_json_error(array('message' => 'No file uploaded or upload error'));
}
// Validate file type
if (!$this->validate_csv_file($_FILES['import_file'])) {
wp_send_json_error(array('message' => 'Invalid file type. Please upload a CSV file.'));
}
try {
$results = $this->process_events_import($_FILES['import_file']['tmp_name']);
wp_send_json_success(array(
'results' => $results,
'message' => sprintf(
__('Import completed: %d created, %d updated, %d errors', 'hvac-community-events'),
$results['created'],
$results['updated'],
$results['errors']
)
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Import failed: ' . $e->getMessage()));
}
}
/**
* Bulk update users
*
* @return void
*/
public function bulk_update_users() {
// Security checks
if (!check_ajax_referer('hvac_import_export_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Security check failed'));
}
if (!$this->check_master_trainer_permission()) {
wp_send_json_error(array('message' => 'Insufficient permissions'));
}
// Check if file was uploaded
if (!isset($_FILES['import_file']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
wp_send_json_error(array('message' => 'No file uploaded or upload error'));
}
// Validate file type
if (!$this->validate_csv_file($_FILES['import_file'])) {
wp_send_json_error(array('message' => 'Invalid file type. Please upload a CSV file.'));
}
try {
$results = $this->process_bulk_user_updates($_FILES['import_file']['tmp_name']);
wp_send_json_success(array(
'results' => $results,
'message' => sprintf(
__('Bulk update completed: %d updated, %d errors', 'hvac-community-events'),
$results['updated'],
$results['errors']
)
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Bulk update failed: ' . $e->getMessage()));
}
}
/**
* Check if user has master trainer permission
*
* @return bool
*/
private function check_master_trainer_permission() {
$user = wp_get_current_user();
return (in_array('hvac_master_trainer', $user->roles) || current_user_can('manage_options'));
}
/**
* Get all trainers
*
* @return array
*/
private function get_all_trainers() {
$users = get_users(array(
'role__in' => array('hvac_trainer', 'hvac_master_trainer'),
'meta_query' => array(
array(
'key' => 'hvac_trainer_status',
'value' => array('approved', 'active'),
'compare' => 'IN'
)
)
));
$trainers = array();
foreach ($users as $user) {
$trainers[] = array(
'id' => $user->ID,
'username' => $user->user_login,
'email' => $user->user_email,
'first_name' => get_user_meta($user->ID, 'first_name', true),
'last_name' => get_user_meta($user->ID, 'last_name', true),
'business_name' => get_user_meta($user->ID, 'business_name', true),
'business_email' => get_user_meta($user->ID, 'business_email', true),
'phone' => get_user_meta($user->ID, 'phone', true),
'website' => get_user_meta($user->ID, 'website', true),
'status' => get_user_meta($user->ID, 'hvac_trainer_status', true),
'certification_type' => get_user_meta($user->ID, 'certification_type', true),
'certification_status' => get_user_meta($user->ID, 'certification_status', true),
'date_certified' => get_user_meta($user->ID, 'date_certified', true),
'business_type' => get_user_meta($user->ID, 'business_type', true),
'training_audience' => get_user_meta($user->ID, 'training_audience', true),
'registered_date' => $user->user_registered,
'roles' => implode(',', $user->roles)
);
}
return $trainers;
}
/**
* Get all events
*
* @return array
*/
private function get_all_events() {
$events_query = new WP_Query(array(
'post_type' => 'tribe_events',
'post_status' => 'any',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_hvac_trainer_event',
'value' => '1',
'compare' => '='
)
)
));
$events = array();
if ($events_query->have_posts()) {
while ($events_query->have_posts()) {
$events_query->the_post();
$post_id = get_the_ID();
$events[] = array(
'id' => $post_id,
'title' => get_the_title(),
'description' => get_the_content(),
'start_date' => get_post_meta($post_id, '_EventStartDate', true),
'end_date' => get_post_meta($post_id, '_EventEndDate', true),
'venue' => get_post_meta($post_id, '_EventVenueID', true),
'organizer' => get_post_meta($post_id, '_EventOrganizerID', true),
'trainer_id' => get_post_meta($post_id, '_hvac_trainer_id', true),
'cost' => get_post_meta($post_id, '_EventCost', true),
'url' => get_post_meta($post_id, '_EventURL', true),
'status' => get_post_status($post_id),
'created_date' => get_the_date('Y-m-d H:i:s'),
'modified_date' => get_the_modified_date('Y-m-d H:i:s')
);
}
}
wp_reset_postdata();
return $events;
}
/**
* Get all users with metadata
*
* @return array
*/
private function get_all_users_with_metadata() {
$users = get_users(array(
'role__in' => array('hvac_trainer', 'hvac_master_trainer', 'subscriber'),
'number' => -1
));
$user_profiles = array();
foreach ($users as $user) {
$all_meta = get_user_meta($user->ID);
$user_profile = array(
'id' => $user->ID,
'username' => $user->user_login,
'email' => $user->user_email,
'display_name' => $user->display_name,
'registered_date' => $user->user_registered,
'roles' => implode(',', $user->roles)
);
// Add all user meta
foreach ($all_meta as $key => $values) {
if (is_array($values) && count($values) === 1) {
$user_profile[$key] = $values[0];
} else {
$user_profile[$key] = maybe_serialize($values);
}
}
$user_profiles[] = $user_profile;
}
return $user_profiles;
}
/**
* Create trainers CSV data
*
* @param array $trainers
* @return string
*/
private function create_trainers_csv($trainers) {
if (empty($trainers)) {
return '';
}
// Create CSV content
$output = fopen('php://temp', 'r+');
// Headers
$headers = array_keys($trainers[0]);
fputcsv($output, $headers);
// Data rows
foreach ($trainers as $trainer) {
fputcsv($output, $trainer);
}
// Get CSV content
rewind($output);
$csv_data = stream_get_contents($output);
fclose($output);
return $csv_data;
}
/**
* Create events CSV data
*
* @param array $events
* @return string
*/
private function create_events_csv($events) {
if (empty($events)) {
return '';
}
// Create CSV content
$output = fopen('php://temp', 'r+');
// Headers
$headers = array_keys($events[0]);
fputcsv($output, $headers);
// Data rows
foreach ($events as $event) {
fputcsv($output, $event);
}
// Get CSV content
rewind($output);
$csv_data = stream_get_contents($output);
fclose($output);
return $csv_data;
}
/**
* Create user profiles CSV data
*
* @param array $users
* @return string
*/
private function create_user_profiles_csv($users) {
if (empty($users)) {
return '';
}
// Create CSV content
$output = fopen('php://temp', 'r+');
// Headers
$headers = array_keys($users[0]);
fputcsv($output, $headers);
// Data rows
foreach ($users as $user) {
fputcsv($output, $user);
}
// Get CSV content
rewind($output);
$csv_data = stream_get_contents($output);
fclose($output);
return $csv_data;
}
/**
* Validate CSV file
*
* @param array $file
* @return bool
*/
private function validate_csv_file($file) {
// Check file extension
$file_extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if ($file_extension !== 'csv') {
return false;
}
// Check MIME type
$allowed_mime_types = array('text/csv', 'text/plain', 'application/csv');
if (!in_array($file['type'], $allowed_mime_types)) {
return false;
}
// Check file size (max 10MB)
if ($file['size'] > 10 * 1024 * 1024) {
return false;
}
return true;
}
/**
* Process trainer profiles import
*
* @param string $file_path
* @return array
*/
private function process_trainer_profiles_import($file_path) {
$results = array(
'created' => 0,
'updated' => 0,
'errors' => 0,
'details' => array()
);
$handle = fopen($file_path, 'r');
if (!$handle) {
throw new Exception('Cannot open CSV file');
}
// Get headers
$headers = fgetcsv($handle);
if (!$headers) {
fclose($handle);
throw new Exception('Cannot read CSV headers');
}
$row_number = 1;
while (($row = fgetcsv($handle)) !== FALSE) {
$row_number++;
try {
$data = array_combine($headers, $row);
$this->import_single_trainer_profile($data);
$results['updated']++;
} catch (Exception $e) {
$results['errors']++;
$results['details'][] = "Row $row_number: " . $e->getMessage();
}
}
fclose($handle);
return $results;
}
/**
* Process events import
*
* @param string $file_path
* @return array
*/
private function process_events_import($file_path) {
$results = array(
'created' => 0,
'updated' => 0,
'errors' => 0,
'details' => array()
);
$handle = fopen($file_path, 'r');
if (!$handle) {
throw new Exception('Cannot open CSV file');
}
// Get headers
$headers = fgetcsv($handle);
if (!$headers) {
fclose($handle);
throw new Exception('Cannot read CSV headers');
}
$row_number = 1;
while (($row = fgetcsv($handle)) !== FALSE) {
$row_number++;
try {
$data = array_combine($headers, $row);
$result = $this->import_single_event($data);
if ($result === 'created') {
$results['created']++;
} else {
$results['updated']++;
}
} catch (Exception $e) {
$results['errors']++;
$results['details'][] = "Row $row_number: " . $e->getMessage();
}
}
fclose($handle);
return $results;
}
/**
* Process bulk user updates
*
* @param string $file_path
* @return array
*/
private function process_bulk_user_updates($file_path) {
$results = array(
'updated' => 0,
'errors' => 0,
'details' => array()
);
$handle = fopen($file_path, 'r');
if (!$handle) {
throw new Exception('Cannot open CSV file');
}
// Get headers
$headers = fgetcsv($handle);
if (!$headers) {
fclose($handle);
throw new Exception('Cannot read CSV headers');
}
$row_number = 1;
while (($row = fgetcsv($handle)) !== FALSE) {
$row_number++;
try {
$data = array_combine($headers, $row);
$this->update_single_user($data);
$results['updated']++;
} catch (Exception $e) {
$results['errors']++;
$results['details'][] = "Row $row_number: " . $e->getMessage();
}
}
fclose($handle);
return $results;
}
/**
* Import single trainer profile
*
* @param array $data
* @return void
*/
private function import_single_trainer_profile($data) {
// Find user by email or username
$user = null;
if (!empty($data['email'])) {
$user = get_user_by('email', sanitize_email($data['email']));
}
if (!$user && !empty($data['username'])) {
$user = get_user_by('login', sanitize_user($data['username']));
}
if (!$user) {
throw new Exception('User not found');
}
// Update user meta
$meta_fields = array(
'first_name', 'last_name', 'business_name', 'business_email',
'phone', 'website', 'certification_type', 'certification_status',
'date_certified', 'business_type', 'training_audience'
);
foreach ($meta_fields as $field) {
if (isset($data[$field]) && $data[$field] !== '') {
update_user_meta($user->ID, $field, sanitize_text_field($data[$field]));
}
}
}
/**
* Import single event
*
* @param array $data
* @return string
*/
private function import_single_event($data) {
// Check if event exists by title
$existing_event = get_page_by_title($data['title'], OBJECT, 'tribe_events');
$event_data = array(
'post_title' => sanitize_text_field($data['title']),
'post_content' => wp_kses_post($data['description']),
'post_type' => 'tribe_events',
'post_status' => 'publish'
);
if ($existing_event) {
$event_data['ID'] = $existing_event->ID;
wp_update_post($event_data);
return 'updated';
} else {
$event_id = wp_insert_post($event_data);
if ($event_id && !is_wp_error($event_id)) {
// Add event metadata
if (!empty($data['start_date'])) {
update_post_meta($event_id, '_EventStartDate', sanitize_text_field($data['start_date']));
}
if (!empty($data['end_date'])) {
update_post_meta($event_id, '_EventEndDate', sanitize_text_field($data['end_date']));
}
update_post_meta($event_id, '_hvac_trainer_event', '1');
return 'created';
}
}
throw new Exception('Failed to import event');
}
/**
* Update single user
*
* @param array $data
* @return void
*/
private function update_single_user($data) {
// Find user
$user = null;
if (!empty($data['id'])) {
$user = get_user_by('id', intval($data['id']));
} elseif (!empty($data['email'])) {
$user = get_user_by('email', sanitize_email($data['email']));
}
if (!$user) {
throw new Exception('User not found');
}
// Update user data
$user_data = array('ID' => $user->ID);
if (!empty($data['display_name'])) {
$user_data['display_name'] = sanitize_text_field($data['display_name']);
}
if (count($user_data) > 1) {
wp_update_user($user_data);
}
// Update meta fields
foreach ($data as $key => $value) {
if (!in_array($key, array('id', 'username', 'email', 'display_name', 'registered_date', 'roles')) && $value !== '') {
update_user_meta($user->ID, $key, sanitize_text_field($value));
}
}
}
}
// Initialize the class
HVAC_Import_Export_Manager::instance();