/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ db_handler = $db_handler; $this->restriction_model = new Care_Booking_Restriction_Model(); $this->init_hooks(); } /** * Initialize WordPress hooks */ private function init_hooks() { // Admin menu add_action('admin_menu', [$this, 'add_admin_menu']); // Admin scripts and styles add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']); // AJAX handlers add_action('wp_ajax_care_booking_get_restrictions', [$this, 'ajax_get_restrictions']); add_action('wp_ajax_care_booking_toggle_restriction', [$this, 'ajax_toggle_restriction']); add_action('wp_ajax_care_booking_bulk_update', [$this, 'ajax_bulk_update']); add_action('wp_ajax_care_booking_get_entities', [$this, 'ajax_get_entities']); } /** * Add admin menu */ public function add_admin_menu() { add_management_page( __('Care Booking Control', 'care-booking-block'), __('Care Booking Control', 'care-booking-block'), 'manage_options', self::ADMIN_PAGE_SLUG, [$this, 'render_admin_page'] ); } /** * Enqueue admin assets * * @param string $hook_suffix Current admin page */ public function enqueue_admin_assets($hook_suffix) { // Only load on our admin page if (strpos($hook_suffix, self::ADMIN_PAGE_SLUG) === false) { return; } // Enqueue CSS wp_enqueue_style( 'care-booking-admin', CARE_BOOKING_BLOCK_PLUGIN_URL . 'admin/css/admin-style.css', [], CARE_BOOKING_BLOCK_VERSION ); // Enqueue JavaScript wp_enqueue_script( 'care-booking-admin', CARE_BOOKING_BLOCK_PLUGIN_URL . 'admin/js/admin-script.js', ['jquery'], CARE_BOOKING_BLOCK_VERSION, true ); // Localize script wp_localize_script('care-booking-admin', 'careBookingAjax', [ 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('care_booking_nonce'), 'strings' => [ 'loading' => __('Loading...', 'care-booking-block'), 'error' => __('An error occurred. Please try again.', 'care-booking-block'), 'confirm_bulk' => __('Are you sure you want to update all selected restrictions?', 'care-booking-block'), 'success_update' => __('Restriction updated successfully.', 'care-booking-block'), 'success_bulk' => __('Bulk update completed.', 'care-booking-block') ] ]); } /** * Render admin page */ public function render_admin_page() { // Check KiviCare availability if (!$this->is_kivicare_active()) { $this->render_kivicare_warning(); return; } include CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/partials/admin-display.php'; } /** * AJAX handler: Get restrictions */ public function ajax_get_restrictions() { // SECURITY: Enhanced CSRF protection with additional request validation if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) { wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]); wp_die(); // Additional security measure } // SECURITY: Check if request is actually via AJAX if (!wp_doing_ajax()) { wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced capability check with logging if (!current_user_can('manage_options')) { error_log('Care Booking Block: Unauthorized access attempt from user ID: ' . get_current_user_id()); wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]); wp_die(); } // SECURITY: Rate limiting check if (!$this->check_rate_limit('get_restrictions')) { wp_send_json_error(['message' => __('Too many requests. Please wait.', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced input sanitization and validation $restriction_type = sanitize_text_field($_POST['restriction_type'] ?? 'all'); $doctor_id = isset($_POST['doctor_id']) ? absint($_POST['doctor_id']) : null; // SECURITY: Validate restriction_type against whitelist $allowed_types = ['all', 'doctor', 'service']; if (!in_array($restriction_type, $allowed_types, true)) { error_log('Care Booking Block: Invalid restriction_type attempted: ' . $restriction_type); wp_send_json_error(['message' => __('Invalid restriction type', 'care-booking-block')]); wp_die(); } // SECURITY: Validate doctor_id if provided if ($doctor_id !== null && $doctor_id <= 0) { wp_send_json_error(['message' => __('Invalid doctor ID', 'care-booking-block')]); wp_die(); } try { if ($restriction_type === 'all') { $restrictions = $this->restriction_model->get_all(); } elseif (in_array($restriction_type, ['doctor', 'service'])) { $restrictions = $this->restriction_model->get_by_type($restriction_type); // Filter by doctor if specified if ($restriction_type === 'service' && $doctor_id) { $restrictions = array_filter($restrictions, function($r) use ($doctor_id) { return $r->doctor_id == $doctor_id; }); } } else { wp_send_json_error(['message' => __('Invalid parameters', 'care-booking-block')]); } // SECURITY: Convert to array format with output escaping $formatted_restrictions = []; foreach ($restrictions as $restriction) { $formatted_restrictions[] = [ 'id' => (int) $restriction->id, 'restriction_type' => esc_html($restriction->restriction_type), 'target_id' => (int) $restriction->target_id, 'doctor_id' => $restriction->doctor_id ? (int) $restriction->doctor_id : null, 'is_blocked' => (bool) $restriction->is_blocked, 'created_at' => esc_html($restriction->created_at), 'updated_at' => esc_html($restriction->updated_at) ]; } wp_send_json_success([ 'restrictions' => $formatted_restrictions, 'total' => count($formatted_restrictions) ]); } catch (Exception $e) { wp_send_json_error(['message' => __('Database error occurred', 'care-booking-block')]); } } /** * AJAX handler: Toggle restriction */ public function ajax_toggle_restriction() { // SECURITY: Enhanced CSRF protection if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) { wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]); wp_die(); } // SECURITY: AJAX request validation if (!wp_doing_ajax()) { wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced capability check with logging if (!current_user_can('manage_options')) { error_log('Care Booking Block: Unauthorized toggle attempt from user ID: ' . get_current_user_id()); wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]); wp_die(); } // SECURITY: Rate limiting if (!$this->check_rate_limit('toggle_restriction')) { wp_send_json_error(['message' => __('Too many requests. Please wait.', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced parameter validation and sanitization $restriction_type = sanitize_text_field($_POST['restriction_type'] ?? ''); $target_id = absint($_POST['target_id'] ?? 0); $doctor_id = isset($_POST['doctor_id']) ? absint($_POST['doctor_id']) : null; $is_blocked = isset($_POST['is_blocked']) ? (bool) $_POST['is_blocked'] : true; // SECURITY: Validate required parameters if (!$restriction_type || !$target_id) { error_log('Care Booking Block: Missing parameters in toggle_restriction'); wp_send_json_error(['message' => __('Missing required parameters', 'care-booking-block')]); wp_die(); } // SECURITY: Whitelist validation for restriction_type $allowed_types = ['doctor', 'service']; if (!in_array($restriction_type, $allowed_types, true)) { error_log('Care Booking Block: Invalid restriction_type in toggle: ' . $restriction_type); wp_send_json_error(['message' => __('Invalid restriction type', 'care-booking-block')]); wp_die(); } // SECURITY: Validate target_id range if ($target_id <= 0 || $target_id > PHP_INT_MAX) { wp_send_json_error(['message' => __('Invalid target ID', 'care-booking-block')]); wp_die(); } // SECURITY: Service restriction validation if ($restriction_type === 'service' && (!$doctor_id || $doctor_id <= 0)) { wp_send_json_error(['message' => __('Valid doctor_id required for service restrictions', 'care-booking-block')]); wp_die(); } try { // Validate target exists in KiviCare if (!$this->validate_kivicare_target($restriction_type, $target_id, $doctor_id)) { wp_send_json_error(['message' => __('Target not found', 'care-booking-block')]); } // Toggle restriction $result = $this->restriction_model->toggle($restriction_type, $target_id, $doctor_id, $is_blocked); if ($result) { // Get updated/created restriction $restriction = $this->restriction_model->find_existing($restriction_type, $target_id, $doctor_id); if ($restriction) { wp_send_json_success([ 'message' => __('Restriction updated successfully', 'care-booking-block'), 'restriction' => [ 'id' => (int) $restriction->id, 'restriction_type' => esc_html($restriction->restriction_type), 'target_id' => (int) $restriction->target_id, 'doctor_id' => $restriction->doctor_id ? (int) $restriction->doctor_id : null, 'is_blocked' => (bool) $restriction->is_blocked, 'updated_at' => esc_html($restriction->updated_at) ] ]); } } wp_send_json_error(['message' => __('Failed to update restriction', 'care-booking-block')]); } catch (Exception $e) { wp_send_json_error(['message' => __('Database error', 'care-booking-block')]); } } /** * AJAX handler: Bulk update */ public function ajax_bulk_update() { // SECURITY: Enhanced CSRF protection if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) { wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]); wp_die(); } // SECURITY: AJAX request validation if (!wp_doing_ajax()) { wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced capability check with logging if (!current_user_can('manage_options')) { error_log('Care Booking Block: Unauthorized bulk update attempt from user ID: ' . get_current_user_id()); wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]); wp_die(); } // SECURITY: Strict rate limiting for bulk operations if (!$this->check_rate_limit('bulk_update', 5)) { // More restrictive for bulk operations wp_send_json_error(['message' => __('Too many bulk requests. Please wait.', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced parameter validation if (!isset($_POST['restrictions'])) { error_log('Care Booking Block: Missing restrictions parameter in bulk update'); wp_send_json_error(['message' => __('Missing restrictions parameter', 'care-booking-block')]); wp_die(); } $restrictions = $_POST['restrictions']; // SECURITY: Type validation if (!is_array($restrictions)) { error_log('Care Booking Block: Invalid restrictions format in bulk update'); wp_send_json_error(['message' => __('Invalid restrictions format', 'care-booking-block')]); wp_die(); } // SECURITY: Strict bulk size limits for security if (count($restrictions) > 50) { // Reduced from 100 for security error_log('Care Booking Block: Bulk size limit exceeded: ' . count($restrictions)); wp_send_json_error(['message' => __('Bulk size limit exceeded (max 50)', 'care-booking-block')]); wp_die(); } // SECURITY: Validate each restriction item foreach ($restrictions as $index => $restriction) { if (!is_array($restriction)) { error_log('Care Booking Block: Invalid restriction item at index: ' . $index); wp_send_json_error(['message' => __('Invalid restriction data format', 'care-booking-block')]); wp_die(); } // Sanitize each restriction $restrictions[$index] = [ 'restriction_type' => sanitize_text_field($restriction['restriction_type'] ?? ''), 'target_id' => absint($restriction['target_id'] ?? 0), 'doctor_id' => isset($restriction['doctor_id']) ? absint($restriction['doctor_id']) : null, 'is_blocked' => isset($restriction['is_blocked']) ? (bool) $restriction['is_blocked'] : true ]; } try { $result = $this->restriction_model->bulk_toggle($restrictions); if (empty($result['errors'])) { wp_send_json_success([ 'message' => __('Bulk update completed', 'care-booking-block'), 'updated' => $result['updated'], 'errors' => [] ]); } else { // Partial failure wp_send_json_error([ 'message' => __('Partial failure in bulk update', 'care-booking-block'), 'updated' => $result['updated'], 'errors' => $result['errors'] ]); } } catch (Exception $e) { wp_send_json_error(['message' => __('Bulk update failed', 'care-booking-block')]); } } /** * AJAX handler: Get KiviCare entities */ public function ajax_get_entities() { // SECURITY: Enhanced CSRF protection if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) { wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]); wp_die(); } // SECURITY: AJAX request validation if (!wp_doing_ajax()) { wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced capability check with logging if (!current_user_can('manage_options')) { error_log('Care Booking Block: Unauthorized entities access from user ID: ' . get_current_user_id()); wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]); wp_die(); } // SECURITY: Rate limiting if (!$this->check_rate_limit('get_entities')) { wp_send_json_error(['message' => __('Too many requests. Please wait.', 'care-booking-block')]); wp_die(); } // SECURITY: Enhanced input validation $entity_type = sanitize_text_field($_POST['entity_type'] ?? ''); $doctor_id = isset($_POST['doctor_id']) ? absint($_POST['doctor_id']) : null; if (!$entity_type) { error_log('Care Booking Block: Missing entity_type parameter'); wp_send_json_error(['message' => __('Missing entity_type parameter', 'care-booking-block')]); wp_die(); } // SECURITY: Whitelist validation for entity_type $allowed_entity_types = ['doctors', 'services']; if (!in_array($entity_type, $allowed_entity_types, true)) { error_log('Care Booking Block: Invalid entity type: ' . $entity_type); wp_send_json_error(['message' => __('Invalid entity type', 'care-booking-block')]); wp_die(); } // SECURITY: Validate doctor_id if provided if ($doctor_id !== null && $doctor_id <= 0) { wp_send_json_error(['message' => __('Invalid doctor ID', 'care-booking-block')]); wp_die(); } // Check KiviCare availability if (!$this->is_kivicare_active()) { wp_send_json_error(['message' => __('KiviCare plugin not available', 'care-booking-block')]); } try { if ($entity_type === 'doctors') { $entities = $this->get_kivicare_doctors(); } else { $entities = $this->get_kivicare_services($doctor_id); } wp_send_json_success([ 'entities' => $entities, 'total' => count($entities) ]); } catch (Exception $e) { wp_send_json_error(['message' => __('Database error occurred', 'care-booking-block')]); } } /** * Check if KiviCare plugin is active * * @return bool True if KiviCare is active, false otherwise */ private function is_kivicare_active() { // Check if KiviCare plugin is active if (!function_exists('is_plugin_active')) { include_once(ABSPATH . 'wp-admin/includes/plugin.php'); } return is_plugin_active('kivicare/kivicare.php') || is_plugin_active('kivicare-clinic-management-system/kivicare.php'); } /** * Render KiviCare warning */ private function render_kivicare_warning() { ?>