upskill-event-manager/includes/class-hvac-qr-generator.php
bengizmo 705e6b563c feat: Implement Training Leads system and restructure navigation menu
- Add comprehensive Training Leads system for HVAC trainers
  * New /trainer/training-leads/ page with tabular contact submission display
  * HVAC_Training_Leads class with AJAX status updates and filtering
  * Empty state messaging and profile sharing CTA
  * Database integration with existing contact forms system

- Restructure trainer navigation menu for better UX
  * Rename "Customize" to "Profile" with logical groupings
  * Move "Logout" under "Profile" submenu
  * Change "Personal Profile" to "Trainer Profile"
  * Add "Training Leads" under Profile section
  * Update help menu to show only question mark icon positioned far right

- Enhance documentation system
  * Fix /trainer/documentation/ page styling and navigation integration
  * Update content to reflect current platform features
  * Add Training Leads documentation and navigation guide
  * Implement proper WordPress template structure

- Update user management
  * Change joe@upskillhvac.com display name to "Joe Medosch"
  * Assign Joe as author of measureQuick headquarters venue
  * Assign Joe as author of measureQuick and Upskill HVAC organizers

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 16:02:57 -03:00

337 lines
No EOL
12 KiB
PHP

<?php
/**
* HVAC QR Code Generator
*
* Generates QR codes for trainer profile sharing using Google Charts API
*
* @package HVAC_Community_Events
* @since 1.0.0
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC QR Code Generator Class
*/
class HVAC_QR_Generator {
/**
* Instance
*
* @var HVAC_QR_Generator
*/
private static $instance = null;
/**
* Get instance
*/
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init_hooks();
}
/**
* Initialize WordPress hooks
*/
private function init_hooks() {
// AJAX handlers for profile sharing
add_action('wp_ajax_hvac_get_profile_share_data', [$this, 'ajax_get_profile_share_data']);
add_action('wp_ajax_nopriv_hvac_get_profile_share_data', [$this, 'ajax_get_profile_share_data']);
// URL rewrite rules for direct profile access
add_action('init', [$this, 'add_profile_rewrite_rules']);
add_filter('query_vars', [$this, 'add_profile_query_vars']);
}
/**
* Generate QR code URL using QR Server API
*
* @param string $data Data to encode in QR code
* @param int $size QR code size in pixels (default: 200)
* @param string $error_correction Error correction level (L, M, Q, H)
* @return string QR code image URL
*/
public function generate_qr_url($data, $size = 200, $error_correction = 'M') {
// Encode the data for URL
$encoded_data = urlencode($data);
// QR Server API URL (free and reliable alternative to Google Charts)
$qr_url = sprintf(
'https://api.qrserver.com/v1/create-qr-code/?size=%dx%d&data=%s&ecc=%s',
$size,
$size,
$encoded_data,
strtoupper($error_correction)
);
return $qr_url;
}
/**
* Generate QR code for trainer profile
*
* @param int $profile_id Trainer profile ID
* @param int $size QR code size in pixels
* @return string|false QR code URL or false on error
*/
public function generate_trainer_profile_qr($profile_id, $size = 200) {
if (!$profile_id) {
return false;
}
// Generate the profile URL
$profile_url = $this->get_trainer_profile_share_url($profile_id);
if (!$profile_url) {
return false;
}
return $this->generate_qr_url($profile_url, $size);
}
/**
* Get shareable trainer profile URL
*
* @param int $profile_id Trainer profile ID
* @return string|false Profile URL or false on error
*/
public function get_trainer_profile_share_url($profile_id) {
if (!$profile_id) {
return false;
}
// Get the Find a Trainer page URL
$find_trainer_page = get_page_by_path('find-a-trainer');
if (!$find_trainer_page) {
return false;
}
$base_url = get_permalink($find_trainer_page->ID);
// Create the profile-specific URL (with trailing slash for WordPress rewrite rules)
$profile_url = trailingslashit($base_url) . 'profile/' . $profile_id . '/';
return $profile_url;
}
/**
* Get trainer profile data for sharing
*
* @param int $profile_id Trainer profile ID
* @return array|false Profile data or false on error
*/
public function get_trainer_share_data($profile_id) {
if (!$profile_id) {
return false;
}
// Get the profile post
$profile = get_post($profile_id);
if (!$profile || $profile->post_type !== 'trainer_profile') {
return false;
}
// Get profile metadata
$user_id = get_post_meta($profile_id, 'user_id', true);
if (!$user_id) {
return false;
}
// Get user data
$user = get_userdata($user_id);
if (!$user) {
return false;
}
// Compile share data
$share_data = [
'profile_id' => $profile_id,
'user_id' => $user_id,
'trainer_name' => get_post_meta($profile_id, 'trainer_display_name', true) ?: $user->display_name,
'business_name' => get_user_meta($user_id, 'business_name', true),
'trainer_city' => get_post_meta($profile_id, 'trainer_city', true),
'trainer_state' => get_post_meta($profile_id, 'trainer_state', true),
'certification_type' => get_post_meta($profile_id, 'certification_type', true),
'profile_image' => get_post_meta($profile_id, 'profile_image_url', true),
'share_url' => $this->get_trainer_profile_share_url($profile_id),
'qr_code_url' => $this->generate_trainer_profile_qr($profile_id, 200)
];
return $share_data;
}
/**
* Parse trainer profile ID from URL
*
* @param string $url URL to parse
* @return int|false Profile ID or false if not found
*/
public function parse_profile_id_from_url($url = null) {
// First check if we have a query variable (from rewrite rule)
$profile_id = get_query_var('trainer_profile_id');
if ($profile_id) {
return intval($profile_id);
}
// Fallback to URL parsing
if (!$url) {
$url = $_SERVER['REQUEST_URI'];
}
// Check if URL matches pattern: /find-a-trainer/profile/{profile_id}
if (preg_match('/\/find-a-trainer\/profile\/(\d+)\/?/', $url, $matches)) {
return intval($matches[1]);
}
return false;
}
/**
* Generate profile card HTML for sharing
*
* @param int $profile_id Trainer profile ID
* @param array $options Display options
* @return string|false HTML content or false on error
*/
public function generate_profile_card_html($profile_id, $options = []) {
$share_data = $this->get_trainer_share_data($profile_id);
if (!$share_data) {
return false;
}
// Default options
$options = wp_parse_args($options, [
'show_qr' => true,
'qr_size' => 150,
'card_width' => 600,
'card_height' => 300
]);
$qr_url = $options['show_qr'] ? $this->generate_trainer_profile_qr($profile_id, $options['qr_size']) : '';
ob_start();
?>
<div class="hvac-share-profile-card" style="width: <?php echo esc_attr($options['card_width']); ?>px; height: <?php echo esc_attr($options['card_height']); ?>px; border: 2px solid #e0e0e0; border-radius: 12px; padding: 20px; background: #fff; display: flex; align-items: center; gap: 20px; font-family: Arial, sans-serif;">
<!-- Profile Image -->
<div class="hvac-share-avatar" style="width: 120px; height: 120px; flex-shrink: 0; position: relative;">
<?php if (!empty($share_data['profile_image'])): ?>
<img src="<?php echo esc_url($share_data['profile_image']); ?>"
alt="<?php echo esc_attr($share_data['trainer_name']); ?>"
style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%; background: #ddd;">
<?php else: ?>
<div style="width: 100%; height: 100%; background: #6c757d; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 40px; font-weight: bold;">
<?php echo esc_html(substr($share_data['trainer_name'], 0, 1)); ?>
</div>
<?php endif; ?>
<!-- mQ Certified Badge -->
<?php if ($share_data['certification_type'] === 'Certified measureQuick Trainer'): ?>
<div style="position: absolute; top: -5px; right: -5px; width: 35px; height: 35px;">
<img src="/wp-content/uploads/2025/08/mQ-Certified-trainer.png"
alt="measureQuick Certified Trainer"
style="width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));">
</div>
<?php endif; ?>
</div>
<!-- Profile Details -->
<div class="hvac-share-details" style="flex: 1; min-width: 0;">
<h2 style="margin: 0 0 8px 0; font-size: 24px; font-weight: 600; color: #333;">
<?php echo esc_html($share_data['trainer_name']); ?>
</h2>
<?php if (!empty($share_data['business_name'])): ?>
<p style="margin: 0 0 8px 0; font-size: 16px; color: #666; font-weight: 500;">
<?php echo esc_html($share_data['business_name']); ?>
</p>
<?php endif; ?>
<p style="margin: 0 0 8px 0; font-size: 16px; color: #666;">
<?php echo esc_html($share_data['trainer_city'] . ', ' . $share_data['trainer_state']); ?>
</p>
<?php if (!empty($share_data['certification_type'])): ?>
<p style="margin: 0 0 8px 0; font-size: 16px; color: #0073aa; font-weight: 500;">
<?php echo esc_html($share_data['certification_type']); ?>
</p>
<?php endif; ?>
</div>
<!-- QR Code -->
<?php if ($options['show_qr'] && $qr_url): ?>
<div class="hvac-share-qr" style="width: <?php echo esc_attr($options['qr_size']); ?>px; height: <?php echo esc_attr($options['qr_size']); ?>px; flex-shrink: 0;">
<img src="<?php echo esc_url($qr_url); ?>"
alt="QR Code for <?php echo esc_attr($share_data['trainer_name']); ?>"
style="width: 100%; height: 100%; object-fit: contain;">
</div>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
/**
* AJAX handler for getting profile share data
*/
public function ajax_get_profile_share_data() {
// Verify nonce
if (!wp_verify_nonce($_POST['nonce'], 'hvac_profile_sharing')) {
wp_send_json_error(['message' => 'Security check failed']);
return;
}
$profile_id = intval($_POST['profile_id']);
if (!$profile_id) {
wp_send_json_error(['message' => 'Invalid profile ID']);
return;
}
// Get the share data
$share_data = $this->get_trainer_share_data($profile_id);
if (!$share_data) {
wp_send_json_error(['message' => 'Profile not found or not accessible']);
return;
}
wp_send_json_success($share_data);
}
/**
* Add rewrite rules for direct profile access
*/
public function add_profile_rewrite_rules() {
// Add rewrite rule for /find-a-trainer/profile/{profile_id}
add_rewrite_rule(
'^find-a-trainer/profile/([0-9]+)/?$',
'index.php?pagename=find-a-trainer&trainer_profile_id=$matches[1]',
'top'
);
}
/**
* Add custom query variables
*/
public function add_profile_query_vars($vars) {
$vars[] = 'trainer_profile_id';
return $vars;
}
}
// Initialize
HVAC_QR_Generator::instance();