upskill-event-manager/includes/class-hvac-announcements-cpt.php
Ben c20b461e7d feat: Implement secure Trainer Announcements system with comprehensive features
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>
2025-08-20 13:34:15 -03:00

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;
}
}