booking = $booking; $this->settings = $settings; $this->offering = $this->booking->getOffering(); $this->request = Request::get(); // Fields $this->fields = new Fields($this->booking, $this->settings, $this->request); // Plugin Profile self::$profile = Profile::getInstance(); } /** * @param mixed[] $options * @return string */ public function start(array $options = []): string { $step = $this->booking->getStepToPerform($this->request); return $this->step($step, $options); } /** * @param string $step * @param mixed[] $options * @return string */ public function step(string $step, array $options = []): string { Assets::enqueueFrontend(['booking']); return $this->{$step}($options); } /** * @param mixed[] $options * @return string */ public function session(array $options = []): string { $method = $this->booking->getBookingMethod(); $multiple = BookingMethods::allowsMultipleSessions($method); $option_session = $method === BookingMethods::OPTION_SESSION; // Validate $errors = []; if ($this->request && $this->request->step === 'session') { if ($option_session) { $time_label = strtolower(wp_strip_all_tags($this->offering->getTypeLabel('time'))); foreach ($this->booking->getOptions() as $option_id => $quantity) { if (absint($quantity) <= 0 || count($this->booking->sessionsForOption((int) $option_id))) continue; $option = $this->offering->getOptions()->get((int) $option_id); $option_title = $option ? $option->title() : $this->offering->getTypeLabel('option'); $errors[] = sprintf( // translators: Placeholder values. esc_html__('Please select at least one %1$s for %2$s.', 'daytidy'), esc_html($time_label), '' . esc_html($option_title) . '' ); } } else if (!count($this->booking->getAllSessions())) { $errors[] = esc_html__('Please select at least one session for booking.', 'daytidy'); } } $options_selected = $this->booking->getTotalOptions() > 0; if ($option_session && $options_selected && !$this->fields->selectedOptionsHaveAvailableSessions()) { $option_label = strtolower(wp_strip_all_tags($this->offering->getTypeLabel('option'))); $time_label = strtolower(wp_strip_all_tags($this->offering->getTypeLabel('time'))); $errors[] = sprintf( // translators: Placeholder values. esc_html__('No %1$s is available for one or more selected %2$s. Please go back and choose another %2$s.', 'daytidy'), esc_html($time_label), esc_html($option_label) ); } return self::view()->render('booking.module.steps.session', [ 'booking' => $this->booking, 'offering' => $this->offering, 'fields' => $this->fields, 'request' => $this->request, 'multiple' => $multiple, 'option_session' => $option_session, 'errors' => $errors, 'options' => $options, ]); } /** * @param mixed[] $options * @return string */ public function date(array $options = []): string { $errors = []; if ($this->request && $this->request->step === 'date') { if (!$this->booking->getDate()) { $errors[] = esc_html__('Please select a booking date.', 'daytidy'); } else if (!$this->booking->getTime()) { $errors[] = esc_html__('Please select a session time for the selected date.', 'daytidy'); } } return self::view()->render('booking.module.steps.date', [ 'booking' => $this->booking, 'offering' => $this->offering, 'fields' => $this->fields, 'request' => $this->request, 'errors' => $errors, 'options' => $options, ]); } /** * @param mixed[] $options * @return string * @throws Exception */ public function time(array $options = []): string { return self::view()->render('booking.module.steps.time', [ 'booking' => $this->booking, 'offering' => $this->offering, 'fields' => $this->fields, 'request' => $this->request, 'options' => $options, 'date' => new DateTime($this->booking->getDate(), new DateTimeZone('UTC')), ]); } /** * @param mixed[] $options * @return string */ public function option(array $options = []): string { if ($this->booking->getBookingMethod() === BookingMethods::OPTION_SESSION) { $this->booking ->setSessions([]) ->setOptionSessions([]) ->setResources([]); } // Validate $errors = $this->request && $this->request->step === 'option' ? $this->booking->getOptionErrors() : []; return self::view()->render('booking.module.steps.option', [ 'booking' => $this->booking, 'offering' => $this->offering, 'fields' => $this->fields, 'request' => $this->request, 'errors' => $errors, 'options' => $options, 'available' => $this->fields->hasAvailableOptions(), 'selection_summary' => $this->selectionSummary(false), ]); } /** * @param mixed[] $options * @return string */ public function form(array $options = []): string { // Validate $errors = $this->request && $this->request->step === 'form' ? $this->booking->getFormErrors() : []; $pricing = $this->booking->getPricing(); return self::view()->render('booking.module.steps.form', [ 'booking' => $this->booking, 'offering' => $this->offering, 'fields' => $this->fields, 'request' => $this->request, 'errors' => $errors, 'options' => $options, 'selection_summary' => $this->selectionSummary(), 'pricing' => $pricing, 'price' => (new Price())->byOffering($this->offering), ]); } /** * @param mixed[] $options * @return string */ public function payment(array $options = []): string { Assets::enqueueFrontend(['booking', 'payment']); // Pricing $pricing = $this->booking->getPricing(); // Price Renderer $price = (new Price())->byOffering($this->offering); // Coupon Module Status $coupon_enabled = Modules::enabled('booking/coupons'); $applied_coupon = ''; if ($coupon_enabled && $this->booking->getCoupon() !== '') { [$coupon_valid] = (new CouponManager())->validateDraft($this->booking); if ($coupon_valid) $applied_coupon = $this->booking->getCoupon(); } // Payment Gateways $gateways = Gateways::paid(); if (apply_filters('DayTidy/F__DayTidy/Booking/Form/Steps__cartGatewayActive/active', false)) { $gateways = array_filter($gateways, function ($gateway) { return $gateway->type() === PaymentGateway::CART_INTEGRATED; }); } return self::view()->render('booking.module.steps.payment', [ 'booking' => $this->booking, 'offering' => $this->offering, 'fields' => $this->fields, 'pricing' => $pricing, 'price' => $price, 'request' => $this->request, 'free' => (new FreeGateway())->setBooking($this->booking), 'options' => $options, 'coupon_enabled' => $coupon_enabled, 'applied_coupon' => $applied_coupon, 'gateways' => $gateways, 'steps' => $this, 'selection_summary' => $this->selectionSummary(), ]); } private function selectionSummary(bool $include_options = true): string { if ($include_options && $this->booking->getBookingMethod() === BookingMethods::OPTION_SESSION) { return $this->optionSessionSummary(); } $parts = []; if ($include_options) { foreach ($this->booking->getOptions() as $option_id => $quantity) { if (!$quantity) continue; $option = $this->offering->getOptions()->get($option_id); if ($option) $parts[] = ($quantity > 1 ? $quantity . ' ' : '') . $option->title(); } } $sessions = $this->booking->getAllSessions(); if (count($sessions) > 1) { $parts[] = sprintf( /* translators: %d: number of selected sessions. */ _n('%d selected session', '%d selected sessions', count($sessions), 'daytidy'), count($sessions) ); } else if (count($sessions) === 1) { try { $timezone = $this->offering->getTimeZone(); $session = new DateTimeImmutable($sessions[0], new DateTimeZone('UTC')); $parts[] = wp_date('F j · g:i a', $session->getTimestamp(), $timezone); } catch (Exception $e) { // A stale or malformed selection should not break the booking form. } } return implode(' · ', $parts); } private function optionSessionSummary(): string { $parts = []; foreach ($this->booking->getOptions() as $option_id => $quantity) { if (!$quantity) continue; $option = $this->offering->getOptions()->get((int) $option_id); if (!$option) continue; $title = ($quantity > 1 ? $quantity . ' ' : '') . $option->title(); $sessions = []; foreach ($this->booking->sessionsForOption((int) $option_id) as $value) { try { $sessions[] = SessionLabel::render($value, $this->offering, $option->durationMinutes()); } catch (Exception $e) { // A stale or malformed selection should not break the booking form. } } $parts[] = $title . (count($sessions) ? ': ' . implode(', ', $sessions) : ''); } return implode(' · ', $parts); } public function checkout(Gateway $gateway): string { $gateway->setBooking($this->booking); // Preserve the gateway assets contract at its actual render boundary, // including third-party gateways that override checkout(). Assets::captureFrontendEnqueues([$gateway, 'assets']); return $gateway->checkout(); } /** * @param string $message * @param mixed[] $options * @return string */ public function thankyou(string $message, array $options = []): string { return self::view()->render('booking.module.steps.thankyou', [ 'booking' => $this->booking, 'offering' => $this->offering, 'request' => $this->request, 'message' => $message, 'selection_summary' => $this->selectionSummary(), 'options' => $options, ]); } }