where('id', '!=', $exceptId)->first(); } else { $exist = Calendar::where('slug', $slug)->first(); } if ($exist) { return false; } } if (is_numeric($slug)) { return false; } // check if $slug has any special characters or any space. We will only allow alpha-numeric values return preg_match('/^[a-zA-Z0-9_-]+$/', $slug); } public static function isEventSlugAvailable($slug, $checkDb = true, $calendarId = null, $exceptId = null) { if (in_array($slug, self::$reserved)) { return false; } if (strlen($slug) < 4) { return false; } if ($checkDb) { $eventQuery = CalendarSlot::where('slug', $slug); if ($exceptId) { $eventQuery->where('id', '!=', $exceptId); } if ($calendarId) { $eventQuery->where('calendar_id', $calendarId); } $exist = $eventQuery->first(); if ($exist) { return false; } } if (is_numeric($slug)) { return false; } // check if $slug has any special characters or any space. We will only allow alpha-numeric values return preg_match('/^[a-zA-Z0-9_-]+$/', $slug); } public static function getAppBaseUrl($extension = '') { return apply_filters('fluent_booking/admin_base_url', admin_url('admin.php?page=fluent-booking#/' . $extension), $extension); } public static function getAdminBookingUrl($bookingId) { return self::getAppBaseUrl('scheduled-events?booking_id=' . $bookingId); } public static function getUpgradeUrl() { return 'https://fluentbooking.com/pricing/?utm_source=plugin&utm_medium=wp_install&utm_campaign=fcal_upgrade'; } public static function getNextBookingGroup() { $lastBooking = Booking::orderBy('group_id', 'desc')->first(['group_id']); if ($lastBooking) { return $lastBooking->group_id + 1; } return 1; } public static function getNextIndex() { static $index = 0; $index += 1; return $index; } public static function getGlobalPaymentSettings() { static $settings; if ($settings) { return $settings; } $settings = get_option('fluent_booking_global_payment_settings', []); if (!$settings) { $settings = [ 'currency' => 'USD', 'is_active' => 'no' ]; } return $settings; } public static function isPaymentEnabled($calendarEvent = null) { $settings = self::getGlobalPaymentSettings(); if (Arr::get($settings, 'is_active') == 'yes') { return true; } if ($calendarEvent) { if ($calendarEvent->type != 'paid') { return false; } $exist = Meta::where('object_type', 'calendar_slot') ->where('object_id', $calendarEvent->id) ->where('key', 'payment_settings') ->first(); return $exist && $exist->value && Arr::get($exist->value, 'enabled') == 'yes'; } return false; } public static function isPaymentConfigured($method = 'stripe') { $settings = get_option('fluent_booking_payment_settings_' . $method, []); return Arr::get($settings, 'is_active', 'no') == 'yes'; } /** * Sanitize form inputs recursively. * * @param $input * * @return mixed $input */ public static function fluentbookingSanitizer($input, $attribute = null, $fields = []) { if (is_string($input)) { $element = Arr::get($fields, $attribute . '.element'); if (in_array($element, ['post_content', 'rich_text_input'])) { return wp_kses_post($input); } elseif ('textarea' === $element) { $input = sanitize_textarea_field($input); } elseif ('input_email' === $element) { $input = strtolower(sanitize_text_field($input)); } elseif ('input_url' === $element) { $input = sanitize_url($input); } else { $input = sanitize_text_field($input); } } elseif (is_array($input)) { foreach ($input as $key => &$value) { $attribute = $attribute ? $attribute . '[' . $key . ']' : $key; $value = self::fluentbookingSanitizer($value, $attribute, $fields); $attribute = null; } } return $input; } public static function getMeta($group, $objectId, $key, $withModel = false) { $meta = Meta::where('object_type', $group) ->where('object_id', $objectId) ->where('key', $key) ->first(); if ($meta) { if ($withModel) { return $meta; } return $meta->value; } return null; } public static function updateMeta($group, $objectId, $key, $value) { $meta = self::getMeta($group, $objectId, $key, true); if ($meta) { $meta->value = $value; $meta->save(); return $meta; } return Meta::create([ 'object_type' => $group, 'key' => $key, 'object_id' => $objectId, 'value' => $value ]); } public static function deleteMeta($group, $objectId, $key) { return Meta::where('object_type', $group) ->where('object_id', $objectId) ->where('key', $key) ->delete(); } public static function getBookingMeta($eventId, $metaKey, $withModel = false) { $bookingMeta = BookingMeta::where('booking_id', $eventId) ->where('meta_key', $metaKey) ->first(); if ($bookingMeta) { return $withModel ? $bookingMeta : $bookingMeta->value; } return null; } public static function updateBookingMeta($eventId, $metaKey, $value) { $bookingMeta = self::getBookingMeta($eventId, $metaKey, true); if ($bookingMeta) { $bookingMeta->value = $value; $bookingMeta->save(); return $bookingMeta; } return BookingMeta::create([ 'booking_id' => $eventId, 'meta_key' => $metaKey, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'value' => $value ]); } public static function getUserDisplayName($userId = null) { if (!$userId) { $userId = get_current_user_id(); } if (!$userId) { return ''; } $user = get_user_by('ID', $userId); $name = trim($user->first_name . ' ' . $user->last_name); if ($name) { return $name; } return $user->display_name; } public static function getUserEmail($userId = null) { $userId = $userId ?: get_current_user_id(); if (!$userId) { return ''; } $user = get_user_by('ID', $userId); return $user->user_email; } public static function excerpt($text, $max_length = 160) { // Strip HTML tags and convert entities to their corresponding characters $text = html_entity_decode(wp_strip_all_tags($text)); // Remove any line breaks, tabs, or extra whitespace $text = preg_replace('/\s+/', ' ', trim($text)); if (mb_strlen($text) > $max_length) { $text = mb_substr($text, 0, $max_length); $text = preg_replace('/\s+\S+$/', '', $text) . '...'; } return $text; } public static function generateSlotSlug($default, $calendar) { $original = sanitize_title($default, $default, 'display'); $default = $original; $counter = 1; while (CalendarSlot::where('calendar_id', $calendar->id)->where('slug', $default)->first()) { $default = $original . '-' . $counter; $counter += 1; } return apply_filters('fluent_booking/slot_slug', $default, $original); } public static function getIp() { $server = $_SERVER; $clientIp = Arr::get($server, 'HTTP_CLIENT_IP'); $xForwarded = Arr::get($server, 'HTTP_X_FORWARDED_FOR'); if (!empty($clientIp)) { $ip = $clientIp; } elseif (!empty($xForwarded)) { $ip = $clientIp; } else { $ip = $clientIp; } return sanitize_text_field($ip); } public static function fcal_sanitize_html($html) { if (!$html) { return $html; } // Return $html if it's just a plain text if (!preg_match('/<[^>]*>/', $html)) { return $html; } $tags = wp_kses_allowed_html('post'); $tags['style'] = [ 'types' => [], ]; // iframe $tags['iframe'] = [ 'width' => [], 'height' => [], 'src' => [], 'srcdoc' => [], 'title' => [], 'frameborder' => [], 'allow' => [], 'class' => [], 'id' => [], 'allowfullscreen' => [], 'style' => [], ]; //button $tags['button']['onclick'] = []; //svg if (empty($tags['svg'])) { $svg_args = [ 'svg' => [ 'class' => true, 'aria-hidden' => true, 'aria-labelledby' => true, 'role' => true, 'xmlns' => true, 'width' => true, 'height' => true, 'viewbox' => true, ], 'g' => ['fill' => true], 'title' => ['title' => true], 'path' => [ 'd' => true, 'fill' => true, 'transform' => true, ], ]; $tags = array_merge($tags, $svg_args); } $tags = apply_filters('fluent_booking/allowed_html_tags', $tags); return wp_kses($html, $tags); } /** * Sanitize inputs recursively. * * @param array $input * @param array $sanitizeMap * * @return array $input */ public static function fcal_backend_sanitizer($inputs, $sanitizeMap = []) { $originalValues = $inputs; foreach ($inputs as $key => &$value) { if (is_array($value)) { $value = self::fcal_backend_sanitizer($value, $sanitizeMap); } else { $method = Arr::get($sanitizeMap, $key); if (!$method) { continue; } if (is_callable($method)) { $value = call_user_func($method, $value); } elseif (method_exists(self::class, $method)) { $value = call_user_func([self::class, $method], $value); } } } return apply_filters('fluent_booking/backend_sanitized_values', $inputs, $originalValues); } /** * Recursively implode a multi-dimentional array * * @param string $glue * @param array $array * * @return string */ public static function fcalImplodeRecursive($glue, array $array) { $fn = function ($glue, array $array) use (&$fn) { $result = ''; foreach ($array as $item) { if (is_array($item)) { $result .= $fn($glue, $item); } else { $result .= $glue . $item; } } return $result; }; return ltrim($fn($glue, $array), $glue); } public static function getEventColors() { return apply_filters('fluent_booking/event_colors', [ [ 'label' => __('Red-Orange', 'fluent-booking'), 'value' => '#ff4f00' ], [ 'label' => __('Deep Lilac', 'fluent-booking'), 'value' => '#e55cff' ], [ 'label' => __('Purple', 'fluent-booking'), 'value' => '#8247f5' ], [ 'label' => __('Vivid Blue', 'fluent-booking'), 'value' => '#0099ff' ], [ 'label' => __('Cyan', 'fluent-booking'), 'value' => '#0ae8f0' ], [ 'label' => __('Emerald Green', 'fluent-booking'), 'value' => '#17e885' ], [ 'label' => __('Lime Green', 'fluent-booking'), 'value' => '#ccf000' ], [ 'label' => __('Amber', 'fluent-booking'), 'value' => '#ffa600' ] ]); } public static function getMeetingDurations() { return apply_filters('fluent_booking/meeting_durations_schema', [ [ 'value' => '15', 'label' => __('15 Minutes', 'fluent-booking') ], [ 'value' => '30', 'label' => __('30 Minutes', 'fluent-booking') ], [ 'value' => '45', 'label' => __('45 Minutes', 'fluent-booking') ], [ 'value' => '60', 'label' => __('60 Minutes', 'fluent-booking') ], [ 'value' => 'custom', 'label' => __('Custom', 'fluent-booking') ] ]); } public static function getMeetingMultiDurations() { return apply_filters('fluent_booking/meeting_multi_durations_schema', [ [ 'value' => '5', 'label' => __('5 Minutes', 'fluent-booking') ], [ 'value' => '10', 'label' => __('10 Minutes', 'fluent-booking') ], [ 'value' => '15', 'label' => __('15 Minutes', 'fluent-booking') ], [ 'value' => '30', 'label' => __('30 Minutes', 'fluent-booking') ], [ 'value' => '45', 'label' => __('45 Minutes', 'fluent-booking') ], [ 'value' => '50', 'label' => __('50 Minutes', 'fluent-booking') ], [ 'value' => '60', 'label' => __('60 Minutes', 'fluent-booking') ], [ 'value' => '90', 'label' => __('90 Minutes', 'fluent-booking') ], [ 'value' => '120', 'label' => __('120 Minutes', 'fluent-booking') ], [ 'value' => '150', 'label' => __('150 Minutes', 'fluent-booking') ], [ 'value' => '180', 'label' => __('180 Minutes', 'fluent-booking') ], [ 'value' => '240', 'label' => __('240 Minutes', 'fluent-booking') ], [ 'value' => '480', 'label' => __('480 Minutes', 'fluent-booking') ] ]); } public static function getDurationLookup($multiDuration = false) { $durations = $multiDuration ? self::getMeetingMultiDurations() : self::getMeetingDurations(); $durationLookup = []; foreach ($durations as $duration) { $durationLookup[$duration['value']] = $duration['label']; } return $durationLookup; } public static function getBufferTimes() { return apply_filters('fluent_booking/buffer_times_schema', [ [ 'value' => '0', 'label' => __('No buffer time', 'fluent-booking') ], [ 'value' => '5', 'label' => __('5 Minutes', 'fluent-booking') ], [ 'value' => '10', 'label' => __('10 Minutes', 'fluent-booking') ], [ 'value' => '15', 'label' => __('15 Minutes', 'fluent-booking') ], [ 'value' => '20', 'label' => __('20 Minutes', 'fluent-booking') ], [ 'value' => '30', 'label' => __('30 Minutes', 'fluent-booking') ], [ 'value' => '45', 'label' => __('45 Minutes', 'fluent-booking') ], [ 'value' => '60', 'label' => __('60 Minutes', 'fluent-booking') ], [ 'value' => '90', 'label' => __('90 Minutes', 'fluent-booking') ], [ 'value' => '120', 'label' => __('120 Minutes', 'fluent-booking') ] ]); } public static function getSlotIntervals() { return apply_filters('fluent_booking/slot_intervals_schema', [ [ 'value' => '', 'label' => __('Use event length (default)', 'fluent-booking') ], [ 'value' => '5', 'label' => __('5 Minutes', 'fluent-booking') ], [ 'value' => '10', 'label' => __('10 Minutes', 'fluent-booking') ], [ 'value' => '15', 'label' => __('15 Minutes', 'fluent-booking') ], [ 'value' => '20', 'label' => __('20 Minutes', 'fluent-booking') ], [ 'value' => '30', 'label' => __('30 Minutes', 'fluent-booking') ], [ 'value' => '45', 'label' => __('45 Minutes', 'fluent-booking') ], [ 'value' => '60', 'label' => __('60 Minutes', 'fluent-booking') ], [ 'value' => '75', 'label' => __('75 Minutes', 'fluent-booking') ], [ 'value' => '90', 'label' => __('90 Minutes', 'fluent-booking') ], [ 'value' => '105', 'label' => __('105 Minutes', 'fluent-booking') ], [ 'value' => '120', 'label' => __('120 Minutes', 'fluent-booking') ] ]); } public static function getBookingStatusChangingTimes() { return apply_filters('fluent_booking/booking_status_changing_times_schema', [ [ 'value' => '5', 'label' => __('5 Minutes', 'fluent-booking') ], [ 'value' => '10', 'label' => __('10 Minutes', 'fluent-booking') ], [ 'value' => '20', 'label' => __('20 Minutes', 'fluent-booking') ], [ 'value' => '30', 'label' => __('30 Minutes', 'fluent-booking') ], [ 'value' => '40', 'label' => __('40 Minutes', 'fluent-booking') ], [ 'value' => '50', 'label' => __('50 Minutes', 'fluent-booking') ], [ 'value' => '60', 'label' => __('1 Hour', 'fluent-booking') ], [ 'value' => '120', 'label' => __('2 Hours', 'fluent-booking') ], [ 'value' => '180', 'label' => __('3 Hours', 'fluent-booking') ], [ 'value' => '360', 'label' => __('6 Hours', 'fluent-booking') ], [ 'value' => '720', 'label' => __('12 Hours', 'fluent-booking') ], [ 'value' => '1440', 'label' => __('1 Day', 'fluent-booking') ], [ 'value' => '2880', 'label' => __('2 Days', 'fluent-booking') ] ]); } public static function getBookingPeriodOptions() { return apply_filters('fluent_booking/booking_period_options', [ 'upcoming' => __('Upcoming', 'fluent-booking'), 'completed' => __('Completed', 'fluent-booking'), 'pending' => __('Pending', 'fluent-booking'), 'cancelled' => __('Cancelled', 'fluent-booking'), 'all' => __('All', 'fluent-booking'), ]); } public static function getWeekSelectTimes() { return apply_filters('fluent_booking/week_select_times_schema', [ 'start' => '00:00', 'step' => '00:15', 'end' => '23:45' ]); } public static function getOverrideSelectTimes() { return apply_filters('fluent_booking/override_select_times_schema', [ 'start' => '00:00', 'step' => '00:15', 'end' => '23:45' ]); } public static function getWeeklyScheduleSchema() { return apply_filters('fluent_booking/weekly_schedule_schema', [ 'sun' => [ 'enabled' => false, 'slots' => [] ], 'mon' => [ 'enabled' => true, 'slots' => [ ['start' => '09:00', 'end' => '17:00'] ], ], 'tue' => [ 'enabled' => true, 'slots' => [ ['start' => '09:00', 'end' => '17:00'] ], ], 'wed' => [ 'enabled' => true, 'slots' => [ ['start' => '09:00', 'end' => '17:00'] ], ], 'thu' => [ 'enabled' => true, 'slots' => [ ['start' => '09:00', 'end' => '17:00'] ], ], 'fri' => [ 'enabled' => true, 'slots' => [ ['start' => '09:00', 'end' => '17:00'] ], ], 'sat' => [ 'enabled' => false, 'slots' => [] ], ]); } public static function getCustomFieldTypes() { return apply_filters('fluent_booking/custom_fields_types', [ [ 'value' => 'email', 'label' => __('Email', 'fluent-booking') ], [ 'value' => 'text', 'label' => __('Text', 'fluent-booking') ], [ 'value' => 'textarea', 'label' => __('Textarea', 'fluent-booking') ], [ 'value' => 'number', 'label' => __('Number', 'fluent-booking') ], [ 'value' => 'phone', 'label' => __('Phone', 'fluent-booking') ], [ 'value' => 'radio', 'label' => __('Radio', 'fluent-booking') ], [ 'value' => 'dropdown', 'label' => __('Select', 'fluent-booking') ], [ 'value' => 'multi-select', 'label' => __('Multi Select', 'fluent-booking') ], [ 'value' => 'checkbox', 'label' => __('Checkbox', 'fluent-booking') ], [ 'value' => 'checkbox-group', 'label' => __('Checkbox Group', 'fluent-booking') ], [ 'value' => 'date', 'label' => __('Date', 'fluent-booking') ] ]); } public static function getDefaultEmailNotificationSettings() { $assetUrl = App::getInstance()['url.assets']; $checkImage = $assetUrl . 'images/check-mark.png'; $cancelImage = $assetUrl . 'images/cancel-mark.png'; $scheduleImage = $assetUrl . 'images/schedule-mark.png'; return apply_filters('fluent_booking/default_email_notification_settings', [ 'booking_conf_attendee' => [ 'enabled' => true, 'title' => __('Booking Confirmation Email to Attendee', 'fluent-booking'), 'email' => [ 'subject' => 'Booking Confirmation between {{host.name}} & {{guest.full_name}}', 'body' => '
Event Name
{{booking.event_name}} with {{host.name}}
When
{{booking.full_start_end_guest_timezone}}
Who
Where
{{booking.location_details_html}}
Additional notes
{{guest.note}}
' . __('Need to make a change?', 'fluent-booking') . ' ' . __('Reschedule', 'fluent-booking') . ' or ' . __('Cancel', 'fluent-booking') . '
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
{{booking.full_start_end_host_timezone}}
Who
Where
{{booking.location_details_html}}
Note
{{guest.note}}
Additional Data
{{guest.form_data_html}}
Event Name
{{booking.event_name}} with {{host.name}}
{{booking.full_start_end_guest_timezone}}
Where
{{booking.location_details_html}}
Additional notes
{{guest.note}}
' . __('Need to make a change?', 'fluent-booking') . ' ' . __('Reschedule', 'fluent-booking') . ' or ' . __('Cancel', 'fluent-booking') . '
', 'times' => [ [ 'unit' => 'minutes', 'value' => 15, ] ] ], ], 'reminder_to_host' => [ 'enabled' => false, 'is_host' => true, 'title' => __('Configure Meeting Reminder to Organizer (You)', 'fluent-booking'), 'email' => [ 'additional_recipients' => '', 'subject' => 'Meeting Reminder with {{host.name}} @ {{booking.start_date_time_for_host}}', 'body' => 'Event Name
{{booking.event_name}} with {{guest.full_name}}
When
{{booking.full_start_end_host_timezone}}
Who
Where
{{booking.location_details_html}}
Note
{{guest.note}}
Additional Data
{{guest.form_data_html}}
A scheduled meeting has been canceled. Here are the details:
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
{{booking.full_start_end_host_timezone}} (cancelled)
Cancellation Reason
{{booking.cancel_reason}}
Who
Where
{{booking.location_details_html}}
Note
{{guest.note}}
Additional Data
{{guest.form_data_html}}
Your scheduled meeting has been canceled. Here are the details:
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
{{booking.full_start_end_host_timezone}} (cancelled)
Cancellation Reason
{{booking.cancel_reason}}
' ], ], 'rescheduled_by_attendee' => [ 'enabled' => true, 'is_host' => true, 'title' => __('Booking Rescheduled by Attendee (email to Organizer)', 'fluent-booking'), 'email' => [ 'additional_recipients' => '', 'subject' => 'A booking was rescheduled with {{guest.full_name}}', 'body' => 'A scheduled meeting has been rescheduled. Here are the details:
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
New Time: {{booking.full_start_end_host_timezone}} (new)
Previous Time: {{booking.previous_meeting_time}}
Rescheduling Reason
{{booking.reschedule_reason}}
Who
Where
{{booking.location_details_html}}
Note
{{guest.note}}
Additional Data
{{guest.form_data_html}}
Your scheduled meeting has been rescheduled. Here are the details:
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
New Time: {{booking.full_start_end_host_timezone}} (new)
Previous Time: {{booking.previous_meeting_time}}
Rescheduling Reason
{{booking.reschedule_reason}}
' . __('Need to make a change?', 'fluent-booking') . ' ' . __('Reschedule', 'fluent-booking') . ' or ' . __('Cancel', 'fluent-booking') . '
Someone has requested to schedule an event on your calendar. Here are the details:
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
{{booking.full_start_end_host_timezone}}
Who
Where
{{booking.location_details_html}}
Note
{{guest.note}}
Additional Data
{{guest.form_data_html}}
Please wait for the host to confirm your booking.
Event Name
{{booking.event_name}} with {{host.name}}
When
{{booking.full_start_end_guest_timezone}}
Who
Where
{{booking.location_details_html}}
Additional notes
{{guest.note}}
' . __('Need to make a change?', 'fluent-booking') . ' ' . __('Reschedule', 'fluent-booking') . ' or ' . __('Cancel', 'fluent-booking') . '
' ], ], 'declined_by_host' => [ 'enabled' => true, 'title' => __('Booking Declined by Organizer (email to Attendee)', 'fluent-booking'), 'email' => [ 'subject' => 'Booking Declined: Your booking was declined with {{host.name}}', 'body' => 'Your booking request has been declined. Here are the details:
Event Name
{{booking.event_name}} with {{guest.full_name}}
When
{{booking.full_start_end_host_timezone}} (declined)
Reason
{{booking.reject_reason}}
' ], ], ]); } public static function getAddToCalendarHtml($assetUrl = '') { $assetUrl = $assetUrl ?: App::getInstance()['url.assets']; $html = '| ' . __('Add to calendar', 'fluent-booking') . ' |
|