This commit introduces a complete announcement management system for HVAC trainers with enterprise-grade security, performance optimization, and email notifications. ## Core Features - Custom post type for trainer announcements with categories and tags - Role-based permissions (master trainers can create/edit, all trainers can read) - AJAX-powered admin interface with real-time updates - Modal popup viewing for announcements on frontend - Automated email notifications when announcements are published - Google Drive integration for training resources ## Security Enhancements - Fixed critical capability mapping bug preventing proper permission checks - Added content disclosure protection for draft/private announcements - Fixed XSS vulnerabilities with proper output escaping and sanitization - Implemented permission checks on all AJAX endpoints - Added rate limiting to prevent abuse (30 requests/minute) - Email validation before sending notifications ## Performance Optimizations - Implemented intelligent caching for user queries (5-minute TTL) - Added cache versioning for announcement lists (2-minute TTL) - Automatic cache invalidation on content changes - Batch email processing to prevent timeouts (50 emails per batch) - Retry mechanism for failed email sends (max 3 attempts) ## Technical Implementation - Singleton pattern for all manager classes - WordPress coding standards compliance - Proper nonce verification on all AJAX requests - Comprehensive error handling and logging - Mobile-responsive UI with smooth animations - WCAG accessibility compliance ## Components Added - 6 PHP classes for modular architecture - 2 page templates (master announcements, trainer resources) - Admin and frontend JavaScript with jQuery integration - Comprehensive CSS for both admin and frontend - Email notification system with HTML templates - Complete documentation and implementation plans This system provides a secure, scalable foundation for trainer communications while following WordPress best practices and maintaining high code quality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			229 lines
		
	
	
		
			No EOL
		
	
	
		
			9.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			No EOL
		
	
	
		
			9.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * HVAC Announcements Custom Post Type
 | |
|  *
 | |
|  * @package HVAC_Community_Events
 | |
|  * @since 1.0.0
 | |
|  */
 | |
| 
 | |
| if (!defined('ABSPATH')) {
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Class HVAC_Announcements_CPT
 | |
|  *
 | |
|  * Registers and manages the Trainer Announcements custom post type
 | |
|  */
 | |
| class HVAC_Announcements_CPT {
 | |
|     
 | |
|     /**
 | |
|      * Post type key
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     const POST_TYPE = 'hvac_announcement';
 | |
|     
 | |
|     /**
 | |
|      * Category taxonomy key
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     const TAXONOMY_CATEGORY = 'hvac_announcement_cat';
 | |
|     
 | |
|     /**
 | |
|      * Tag taxonomy key
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     const TAXONOMY_TAG = 'hvac_announcement_tag';
 | |
|     
 | |
|     /**
 | |
|      * Instance of this class
 | |
|      *
 | |
|      * @var HVAC_Announcements_CPT
 | |
|      */
 | |
|     private static $instance = null;
 | |
|     
 | |
|     /**
 | |
|      * Get instance of this class
 | |
|      *
 | |
|      * @return HVAC_Announcements_CPT
 | |
|      */
 | |
|     public static function get_instance() {
 | |
|         if (null === self::$instance) {
 | |
|             self::$instance = new self();
 | |
|         }
 | |
|         return self::$instance;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Constructor
 | |
|      */
 | |
|     private function __construct() {
 | |
|         $this->init_hooks();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Initialize hooks
 | |
|      */
 | |
|     private function init_hooks() {
 | |
|         add_action('init', array($this, 'register_post_type'));
 | |
|         add_action('init', array($this, 'register_taxonomies'));
 | |
|         // Removed broken map_meta_cap filter - WordPress handles this automatically with proper capability_type
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Register the custom post type
 | |
|      */
 | |
|     public function register_post_type() {
 | |
|         $labels = array(
 | |
|             'name'                  => _x('Trainer Announcements', 'Post type general name', 'hvac'),
 | |
|             'singular_name'         => _x('Trainer Announcement', 'Post type singular name', 'hvac'),
 | |
|             'menu_name'             => _x('Announcements', 'Admin Menu text', 'hvac'),
 | |
|             'name_admin_bar'        => _x('Announcement', 'Add New on Toolbar', 'hvac'),
 | |
|             'add_new'               => __('Add New', 'hvac'),
 | |
|             'add_new_item'          => __('Add New Announcement', 'hvac'),
 | |
|             'new_item'              => __('New Announcement', 'hvac'),
 | |
|             'edit_item'             => __('Edit Announcement', 'hvac'),
 | |
|             'view_item'             => __('View Announcement', 'hvac'),
 | |
|             'all_items'             => __('All Announcements', 'hvac'),
 | |
|             'search_items'          => __('Search Announcements', 'hvac'),
 | |
|             'parent_item_colon'     => __('Parent Announcements:', 'hvac'),
 | |
|             'not_found'             => __('No announcements found.', 'hvac'),
 | |
|             'not_found_in_trash'    => __('No announcements found in Trash.', 'hvac'),
 | |
|             'featured_image'        => _x('Announcement Featured Image', 'Overrides the "Featured Image" phrase', 'hvac'),
 | |
|             'set_featured_image'    => _x('Set featured image', 'Overrides the "Set featured image" phrase', 'hvac'),
 | |
|             'remove_featured_image' => _x('Remove featured image', 'Overrides the "Remove featured image" phrase', 'hvac'),
 | |
|             'use_featured_image'    => _x('Use as featured image', 'Overrides the "Use as featured image" phrase', 'hvac'),
 | |
|             'archives'              => _x('Announcement Archives', 'The post type archive label', 'hvac'),
 | |
|             'insert_into_item'      => _x('Insert into announcement', 'Overrides the "Insert into post" phrase', 'hvac'),
 | |
|             'uploaded_to_this_item' => _x('Uploaded to this announcement', 'Overrides the "Uploaded to this post" phrase', 'hvac'),
 | |
|             'filter_items_list'     => _x('Filter announcements list', 'Screen reader text', 'hvac'),
 | |
|             'items_list_navigation' => _x('Announcements list navigation', 'Screen reader text', 'hvac'),
 | |
|             'items_list'            => _x('Announcements list', 'Screen reader text', 'hvac'),
 | |
|         );
 | |
|         
 | |
|         $args = array(
 | |
|             'labels'                => $labels,
 | |
|             'public'                => false,
 | |
|             'publicly_queryable'    => false,
 | |
|             'show_ui'               => false, // We'll use custom UI
 | |
|             'show_in_menu'          => false,
 | |
|             'query_var'             => false,
 | |
|             'rewrite'               => false,
 | |
|             'capability_type'       => array('hvac_announcement', 'hvac_announcements'),
 | |
|             'map_meta_cap'          => true,
 | |
|             'has_archive'           => false,
 | |
|             'hierarchical'          => false,
 | |
|             'menu_position'         => null,
 | |
|             'menu_icon'             => 'dashicons-megaphone',
 | |
|             'supports'              => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields'),
 | |
|             'show_in_rest'          => true, // Enable Gutenberg
 | |
|             'rest_base'             => 'hvac-announcements',
 | |
|             'rest_controller_class' => 'WP_REST_Posts_Controller',
 | |
|         );
 | |
|         
 | |
|         register_post_type(self::POST_TYPE, $args);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Register custom taxonomies
 | |
|      */
 | |
|     public function register_taxonomies() {
 | |
|         // Register Category taxonomy
 | |
|         $category_labels = array(
 | |
|             'name'                       => _x('Announcement Categories', 'taxonomy general name', 'hvac'),
 | |
|             'singular_name'              => _x('Announcement Category', 'taxonomy singular name', 'hvac'),
 | |
|             'search_items'               => __('Search Categories', 'hvac'),
 | |
|             'popular_items'              => __('Popular Categories', 'hvac'),
 | |
|             'all_items'                  => __('All Categories', 'hvac'),
 | |
|             'parent_item'                => null,
 | |
|             'parent_item_colon'          => null,
 | |
|             'edit_item'                  => __('Edit Category', 'hvac'),
 | |
|             'update_item'                => __('Update Category', 'hvac'),
 | |
|             'add_new_item'               => __('Add New Category', 'hvac'),
 | |
|             'new_item_name'              => __('New Category Name', 'hvac'),
 | |
|             'separate_items_with_commas' => __('Separate categories with commas', 'hvac'),
 | |
|             'add_or_remove_items'        => __('Add or remove categories', 'hvac'),
 | |
|             'choose_from_most_used'      => __('Choose from the most used categories', 'hvac'),
 | |
|             'not_found'                  => __('No categories found.', 'hvac'),
 | |
|             'menu_name'                  => __('Categories', 'hvac'),
 | |
|         );
 | |
|         
 | |
|         $category_args = array(
 | |
|             'hierarchical'          => true,
 | |
|             'labels'                => $category_labels,
 | |
|             'show_ui'               => false, // We'll manage through custom UI
 | |
|             'show_admin_column'     => false,
 | |
|             'update_count_callback' => '_update_post_term_count',
 | |
|             'query_var'             => false,
 | |
|             'rewrite'               => false,
 | |
|             'show_in_rest'          => true,
 | |
|             'rest_base'             => 'hvac-announcement-categories',
 | |
|         );
 | |
|         
 | |
|         register_taxonomy(self::TAXONOMY_CATEGORY, self::POST_TYPE, $category_args);
 | |
|         
 | |
|         // Register Tag taxonomy
 | |
|         $tag_labels = array(
 | |
|             'name'                       => _x('Announcement Tags', 'taxonomy general name', 'hvac'),
 | |
|             'singular_name'              => _x('Announcement Tag', 'taxonomy singular name', 'hvac'),
 | |
|             'search_items'               => __('Search Tags', 'hvac'),
 | |
|             'popular_items'              => __('Popular Tags', 'hvac'),
 | |
|             'all_items'                  => __('All Tags', 'hvac'),
 | |
|             'parent_item'                => null,
 | |
|             'parent_item_colon'          => null,
 | |
|             'edit_item'                  => __('Edit Tag', 'hvac'),
 | |
|             'update_item'                => __('Update Tag', 'hvac'),
 | |
|             'add_new_item'               => __('Add New Tag', 'hvac'),
 | |
|             'new_item_name'              => __('New Tag Name', 'hvac'),
 | |
|             'separate_items_with_commas' => __('Separate tags with commas', 'hvac'),
 | |
|             'add_or_remove_items'        => __('Add or remove tags', 'hvac'),
 | |
|             'choose_from_most_used'      => __('Choose from the most used tags', 'hvac'),
 | |
|             'not_found'                  => __('No tags found.', 'hvac'),
 | |
|             'menu_name'                  => __('Tags', 'hvac'),
 | |
|         );
 | |
|         
 | |
|         $tag_args = array(
 | |
|             'hierarchical'          => false,
 | |
|             'labels'                => $tag_labels,
 | |
|             'show_ui'               => false, // We'll manage through custom UI
 | |
|             'show_admin_column'     => false,
 | |
|             'update_count_callback' => '_update_post_term_count',
 | |
|             'query_var'             => false,
 | |
|             'rewrite'               => false,
 | |
|             'show_in_rest'          => true,
 | |
|             'rest_base'             => 'hvac-announcement-tags',
 | |
|         );
 | |
|         
 | |
|         register_taxonomy(self::TAXONOMY_TAG, self::POST_TYPE, $tag_args);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get post type name
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public static function get_post_type() {
 | |
|         return self::POST_TYPE;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get category taxonomy name
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public static function get_category_taxonomy() {
 | |
|         return self::TAXONOMY_CATEGORY;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get tag taxonomy name
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public static function get_tag_taxonomy() {
 | |
|         return self::TAXONOMY_TAG;
 | |
|     }
 | |
| } |