repository = $repository; $this->security = $security; $this->cssService = $cssService; } /** * Initialize KiviCare hooks * * @return void * @since 1.0.0 */ public function initialize(): void { // Only initialize if KiviCare is active if (!$this->isKiviCareActive()) { return; } // Doctor filtering hooks $this->initializeDoctorHooks(); // Service filtering hooks $this->initializeServiceHooks(); // Appointment data filtering $this->initializeAppointmentHooks(); // Frontend rendering hooks $this->initializeFrontendHooks(); // Admin interface hooks $this->initializeAdminHooks(); // API endpoint filtering $this->initializeApiHooks(); do_action('care_book_ultimate_hooks_initialized'); } /** * Initialize doctor-related hooks * * @return void * @since 1.0.0 */ private function initializeDoctorHooks(): void { // Filter doctor lists in dropdowns add_filter('kivicare_doctor_list', [$this, 'filterDoctorList'], 10, 2); add_filter('kc_doctor_dropdown_data', [$this, 'filterDoctorDropdown'], 10, 2); // Filter doctor availability queries add_filter('kivicare_available_doctors', [$this, 'filterAvailableDoctors'], 10, 3); add_filter('kc_get_doctors_by_service', [$this, 'filterDoctorsByService'], 10, 2); // Filter doctor profile data add_filter('kivicare_doctor_profile_data', [$this, 'filterDoctorProfile'], 10, 2); // Filter doctor search results add_filter('kivicare_doctor_search_results', [$this, 'filterDoctorSearchResults'], 10, 2); // Hook into doctor time slot generation add_filter('kivicare_doctor_time_slots', [$this, 'filterDoctorTimeSlots'], 10, 3); // Filter doctor listings on frontend add_filter('kivicare_frontend_doctors', [$this, 'filterFrontendDoctors'], 10, 1); } /** * Initialize service-related hooks * * @return void * @since 1.0.0 */ private function initializeServiceHooks(): void { // Filter service lists in dropdowns add_filter('kivicare_service_list', [$this, 'filterServiceList'], 10, 2); add_filter('kc_service_dropdown_data', [$this, 'filterServiceDropdown'], 10, 2); // Filter available services add_filter('kivicare_available_services', [$this, 'filterAvailableServices'], 10, 3); add_filter('kc_get_services_by_doctor', [$this, 'filterServicesByDoctor'], 10, 2); // Filter service search results add_filter('kivicare_service_search_results', [$this, 'filterServiceSearchResults'], 10, 2); // Filter service listings on frontend add_filter('kivicare_frontend_services', [$this, 'filterFrontendServices'], 10, 1); // Filter service booking options add_filter('kivicare_service_booking_options', [$this, 'filterServiceBookingOptions'], 10, 2); } /** * Initialize appointment-related hooks * * @return void * @since 1.0.0 */ private function initializeAppointmentHooks(): void { // Filter appointment booking data add_filter('kivicare_appointment_booking_data', [$this, 'validateAppointmentBooking'], 10, 2); // Filter appointment calendar events add_filter('kivicare_calendar_events', [$this, 'filterCalendarEvents'], 10, 2); // Hook into appointment creation validation add_filter('kivicare_before_appointment_save', [$this, 'validateAppointmentSave'], 10, 2); // Filter appointment list queries add_filter('kivicare_appointment_query_args', [$this, 'filterAppointmentQuery'], 10, 2); } /** * Initialize frontend rendering hooks * * @return void * @since 1.0.0 */ private function initializeFrontendHooks(): void { // Hook into frontend widget rendering add_filter('kivicare_widget_render_data', [$this, 'filterWidgetData'], 10, 3); // Filter shortcode output add_filter('kivicare_shortcode_content', [$this, 'filterShortcodeContent'], 10, 3); // Hook into booking form rendering add_filter('kivicare_booking_form_fields', [$this, 'filterBookingFormFields'], 10, 2); // Filter frontend search results add_filter('kivicare_frontend_search', [$this, 'filterFrontendSearch'], 10, 2); } /** * Initialize admin interface hooks * * @return void * @since 1.0.0 */ private function initializeAdminHooks(): void { // Add admin notices for restricted entities add_action('admin_notices', [$this, 'showAdminNotices']); // Filter admin list table data add_filter('kivicare_admin_list_data', [$this, 'filterAdminListData'], 10, 3); // Hook into admin dashboard widgets add_filter('kivicare_dashboard_stats', [$this, 'filterDashboardStats'], 10, 1); } /** * Initialize API endpoint hooks * * @return void * @since 1.0.0 */ private function initializeApiHooks(): void { // Filter REST API responses add_filter('rest_prepare_kivicare_doctor', [$this, 'filterDoctorApiResponse'], 10, 3); add_filter('rest_prepare_kivicare_service', [$this, 'filterServiceApiResponse'], 10, 3); // Filter AJAX responses add_filter('kivicare_ajax_response', [$this, 'filterAjaxResponse'], 10, 3); // Hook into API data queries add_filter('kivicare_api_query_args', [$this, 'filterApiQueryArgs'], 10, 3); } /** * Filter doctor list * * @param array $doctors List of doctors * @param array $args Query arguments * @return array Filtered doctors * @since 1.0.0 */ public function filterDoctorList(array $doctors, array $args = []): array { $hiddenDoctors = $this->getHiddenEntities(RestrictionType::DOCTOR); if (empty($hiddenDoctors)) { return $doctors; } return array_filter($doctors, function($doctor) use ($hiddenDoctors) { $doctorId = $this->extractEntityId($doctor); return !in_array($doctorId, $hiddenDoctors, true); }); } /** * Filter doctor dropdown data * * @param array $dropdown_data Dropdown data * @param array $args Query arguments * @return array Filtered dropdown data * @since 1.0.0 */ public function filterDoctorDropdown(array $dropdown_data, array $args = []): array { $hiddenDoctors = $this->getHiddenEntities(RestrictionType::DOCTOR); if (empty($hiddenDoctors)) { return $dropdown_data; } return array_filter($dropdown_data, function($option) use ($hiddenDoctors) { $doctorId = $this->extractEntityId($option, 'value'); return !in_array($doctorId, $hiddenDoctors, true); }); } /** * Filter available doctors * * @param array $doctors Available doctors * @param int $service_id Service ID * @param array $args Additional arguments * @return array Filtered doctors * @since 1.0.0 */ public function filterAvailableDoctors(array $doctors, int $service_id = 0, array $args = []): array { $hiddenDoctors = $this->getHiddenEntities(RestrictionType::DOCTOR); if (empty($hiddenDoctors)) { return $doctors; } return array_filter($doctors, function($doctor) use ($hiddenDoctors) { $doctorId = $this->extractEntityId($doctor); return !in_array($doctorId, $hiddenDoctors, true); }); } /** * Filter service list * * @param array $services List of services * @param array $args Query arguments * @return array Filtered services * @since 1.0.0 */ public function filterServiceList(array $services, array $args = []): array { $hiddenServices = $this->getHiddenEntities(RestrictionType::SERVICE); if (empty($hiddenServices)) { return $services; } return array_filter($services, function($service) use ($hiddenServices) { $serviceId = $this->extractEntityId($service); return !in_array($serviceId, $hiddenServices, true); }); } /** * Filter service dropdown data * * @param array $dropdown_data Dropdown data * @param array $args Query arguments * @return array Filtered dropdown data * @since 1.0.0 */ public function filterServiceDropdown(array $dropdown_data, array $args = []): array { $hiddenServices = $this->getHiddenEntities(RestrictionType::SERVICE); if (empty($hiddenServices)) { return $dropdown_data; } return array_filter($dropdown_data, function($option) use ($hiddenServices) { $serviceId = $this->extractEntityId($option, 'value'); return !in_array($serviceId, $hiddenServices, true); }); } /** * Validate appointment booking data * * @param array $booking_data Booking data * @param array $args Additional arguments * @return array|false Validated booking data or false to prevent booking * @since 1.0.0 */ public function validateAppointmentBooking(array $booking_data, array $args = []): array|false { // Check if doctor is hidden if (!empty($booking_data['doctor_id'])) { $hiddenDoctors = $this->getHiddenEntities(RestrictionType::DOCTOR); if (in_array((int) $booking_data['doctor_id'], $hiddenDoctors, true)) { // Log attempt to book hidden doctor $this->security->logSecurityEvent( 'hidden_entity_booking_attempt', "Attempt to book hidden doctor ID: {$booking_data['doctor_id']}" ); return false; } } // Check if service is hidden if (!empty($booking_data['service_id'])) { $hiddenServices = $this->getHiddenEntities(RestrictionType::SERVICE); if (in_array((int) $booking_data['service_id'], $hiddenServices, true)) { // Log attempt to book hidden service $this->security->logSecurityEvent( 'hidden_entity_booking_attempt', "Attempt to book hidden service ID: {$booking_data['service_id']}" ); return false; } } return $booking_data; } /** * Filter calendar events * * @param array $events Calendar events * @param array $args Query arguments * @return array Filtered events * @since 1.0.0 */ public function filterCalendarEvents(array $events, array $args = []): array { $hiddenDoctors = $this->getHiddenEntities(RestrictionType::DOCTOR); $hiddenServices = $this->getHiddenEntities(RestrictionType::SERVICE); if (empty($hiddenDoctors) && empty($hiddenServices)) { return $events; } return array_filter($events, function($event) use ($hiddenDoctors, $hiddenServices) { // Filter by doctor if (!empty($event['doctor_id']) && in_array((int) $event['doctor_id'], $hiddenDoctors, true)) { return false; } // Filter by service if (!empty($event['service_id']) && in_array((int) $event['service_id'], $hiddenServices, true)) { return false; } return true; }); } /** * Filter widget data * * @param array $data Widget data * @param string $widget_type Widget type * @param array $args Widget arguments * @return array Filtered data * @since 1.0.0 */ public function filterWidgetData(array $data, string $widget_type, array $args = []): array { // Apply filtering based on widget type switch ($widget_type) { case 'doctor_list': case 'doctor_booking': return $this->filterDoctorList($data, $args); case 'service_list': case 'service_booking': return $this->filterServiceList($data, $args); default: return $data; } } /** * Filter API response data * * @param \WP_REST_Response $response REST response * @param \WP_Post $post Post object * @param \WP_REST_Request $request REST request * @return \WP_REST_Response Modified response * @since 1.0.0 */ public function filterDoctorApiResponse(\WP_REST_Response $response, \WP_Post $post, \WP_REST_Request $request): \WP_REST_Response { $data = $response->get_data(); $doctorId = $post->ID; $hiddenDoctors = $this->getHiddenEntities(RestrictionType::DOCTOR); if (in_array($doctorId, $hiddenDoctors, true)) { // Return empty response or error for hidden doctors return new \WP_REST_Response( ['code' => 'doctor_not_available', 'message' => 'Doctor not available'], 404 ); } return $response; } /** * Show admin notices for restricted entities * * @return void * @since 1.0.0 */ public function showAdminNotices(): void { // Only show on KiviCare admin pages if (!$this->isKiviCareAdminPage()) { return; } $stats = $this->cssService->getStatistics(); $total_hidden = array_sum(array_column($stats, 'hidden_count')); if ($total_hidden > 0) { printf( '

%s: %s

', esc_html__('Care Book Ultimate', 'care-book-ultimate'), esc_html(sprintf( _n( '%d entity is currently hidden from appointments', '%d entities are currently hidden from appointments', $total_hidden, 'care-book-ultimate' ), $total_hidden )) ); } } /** * Get hidden entities with caching * * @param RestrictionType $entityType Entity type * @return array Hidden entity IDs * @since 1.0.0 */ private function getHiddenEntities(RestrictionType $entityType): array { $cache_key = $entityType->value; if (!isset($this->cached_restrictions[$cache_key])) { $this->cached_restrictions[$cache_key] = $this->repository->getHiddenEntities($entityType); } return $this->cached_restrictions[$cache_key]; } /** * Extract entity ID from various data structures * * @param mixed $entity Entity data * @param string $id_field ID field name * @return int Entity ID * @since 1.0.0 */ private function extractEntityId(mixed $entity, string $id_field = 'id'): int { if (is_array($entity)) { return (int) ($entity[$id_field] ?? $entity['ID'] ?? 0); } if (is_object($entity)) { return (int) ($entity->$id_field ?? $entity->ID ?? 0); } return (int) $entity; } /** * Check if KiviCare is active * * @return bool * @since 1.0.0 */ private function isKiviCareActive(): bool { return function_exists('is_plugin_active') && is_plugin_active('kivicare/kivicare.php'); } /** * Check if current admin page is KiviCare related * * @return bool * @since 1.0.0 */ private function isKiviCareAdminPage(): bool { $screen = get_current_screen(); if (!$screen) { return false; } // Check for KiviCare admin pages $kivicare_pages = [ 'kivicare', 'kc_appointment', 'kc_doctor', 'kc_service', 'kc_patient' ]; return in_array($screen->id, $kivicare_pages, true) || strpos($screen->id, 'kivicare') !== false || strpos($screen->id, 'kc_') !== false; } /** * Clear restriction cache * Called when restrictions are modified * * @return void * @since 1.0.0 */ public function clearCache(): void { $this->cached_restrictions = []; } }