'en', 'currency' => '€', 'admin_email' => get_option( 'admin_email' ), 'auto_confirm' => 0, 'guest_emails' => 1, 'sync_interval' => 'ethebo_30min', 'accent' => '#2563eb', 'delete_data' => 0, 'mail_from_name' => '', 'mail_from_email' => '', // Payments (online methods and their keys are added by Ethereal Booking Pro). 'pay_methods' => array( 'arrival' ), 'bank_details' => '', 'arrival_note' => '', 'currency_code' => 'EUR', 'deposit' => 100, ) ); } /** * HTML the plugin prints (escaped late with wp_kses()): post HTML plus the forms of the search bar * and the booking form, and the plugin's inline SVG icons. */ function ethebo_allowed_html() { static $allowed = null; if ( null !== $allowed ) { return $allowed; } $common = array( 'class' => true, 'id' => true, 'style' => true, 'title' => true, 'role' => true, 'hidden' => true, 'tabindex' => true, 'data-*' => true, 'aria-*' => true, 'aria-hidden' => true, 'aria-label' => true, ); $field = $common + array( 'type' => true, 'name' => true, 'value' => true, 'checked' => true, 'selected' => true, 'disabled' => true, 'readonly' => true, 'required' => true, 'placeholder' => true, 'min' => true, 'max' => true, 'step' => true, 'rows' => true, 'autocomplete' => true, 'inputmode' => true, 'for' => true, 'form' => true, ); $svg = array( 'class' => true, 'viewbox' => true, 'width' => true, 'height' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'aria-hidden' => true, 'focusable' => true, 'role' => true, 'xmlns' => true, 'd' => true, 'x' => true, 'y' => true, 'rx' => true, 'ry' => true, 'cx' => true, 'cy' => true, 'r' => true, 'points' => true, 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true, ); $allowed = wp_kses_allowed_html( 'post' ); foreach ( array( 'div', 'span', 'p', 'a', 'ul', 'ol', 'li', 'section', 'article', 'header', 'nav', 'img', 'strong', 'em', 'b', 'i', 'small', 'h2', 'h3', 'h4', 'details', 'summary', 'dl', 'dt', 'dd' ) as $tag ) { $allowed[ $tag ] = ( $allowed[ $tag ] ?? array() ) + $common; } $allowed['img'] += array( 'src' => true, 'srcset' => true, 'sizes' => true, 'alt' => true, 'width' => true, 'height' => true, 'loading' => true, 'decoding' => true, 'fetchpriority' => true ); $allowed['a'] += array( 'href' => true, 'target' => true, 'rel' => true ); $allowed['form'] = $common + array( 'action' => true, 'method' => true, 'novalidate' => true, 'autocomplete' => true ); $allowed['fieldset'] = $common; $allowed['legend'] = $common; $allowed['label'] = $field; $allowed['input'] = $field; $allowed['select'] = $field; $allowed['option'] = $field; $allowed['textarea'] = $field; $allowed['button'] = $field; $allowed['output'] = $field; $allowed['noscript'] = array(); foreach ( array( 'svg', 'path', 'rect', 'circle', 'line', 'polyline', 'g' ) as $tag ) { $allowed[ $tag ] = $svg; } return $allowed; } function ethebo_opt( $key ) { $opts = wp_parse_args( (array) get_option( 'ethebo_settings', array() ), ethebo_defaults() ); return isset( $opts[ $key ] ) ? $opts[ $key ] : null; } function ethebo_cron_schedules( $schedules ) { $schedules['ethebo_15min'] = array( 'interval' => 15 * MINUTE_IN_SECONDS, 'display' => 'Every 15 minutes' ); $schedules['ethebo_30min'] = array( 'interval' => 30 * MINUTE_IN_SECONDS, 'display' => 'Every 30 minutes' ); return $schedules; } function ethebo_sync_intervals() { return array( 'ethebo_15min' => ethebo__( 'Every 15 minutes' ), 'ethebo_30min' => ethebo__( 'Every 30 minutes' ), 'hourly' => ethebo__( 'Every hour' ), 'twicedaily' => ethebo__( 'Twice a day' ), ); } function ethebo_statuses() { return array( 'pending' => ethebo__( 'Pending' ), 'confirmed' => ethebo__( 'Confirmed' ), 'blocked' => ethebo__( 'Blocked' ), 'cancelled' => ethebo__( 'Cancelled' ), ); } function ethebo_source_label( $source ) { $labels = array( 'website' => ethebo__( 'Website' ), 'manual' => ethebo__( 'Manual' ), 'booking' => 'Booking.com', 'airbnb' => 'Airbnb', ); if ( isset( $labels[ $source ] ) ) { return $labels[ $source ]; } return 0 === strpos( $source, 'ical' ) ? 'iCal' : $source; } /** Bookings made on the website or added manually – everything else comes from sync. */ function ethebo_is_local_source( $source ) { return in_array( $source, array( 'website', 'manual' ), true ); } function ethebo_price( $amount ) { $amount = (float) $amount; $decimals = floor( $amount ) == $amount ? 0 : 2; return number_format_i18n( $amount, $decimals ) . ' ' . ethebo_opt( 'currency' ); } function ethebo_valid_date( $date ) { $d = DateTime::createFromFormat( '!Y-m-d', (string) $date ); return $d && $d->format( 'Y-m-d' ) === $date; } function ethebo_nights( $check_in, $check_out ) { $a = new DateTimeImmutable( $check_in, new DateTimeZone( 'UTC' ) ); $b = new DateTimeImmutable( $check_out, new DateTimeZone( 'UTC' ) ); return $b > $a ? (int) $a->diff( $b )->days : 0; } function ethebo_format_date( $date ) { return wp_date( 'd.m.Y', strtotime( $date . ' 00:00:00 UTC' ), new DateTimeZone( 'UTC' ) ); } function ethebo_meta( $post_id, $key, $default = '' ) { $value = get_post_meta( $post_id, '_ethebo_' . $key, true ); return '' === $value ? $default : $value; } /* ------------------------------------------------------------------------- * Email * * Every email is sent as HTML (clean, in the site colour) with a plain-text * version inside the same message, from the sender set in Settings. Both help * the email land in the Inbox instead of Spam. * ---------------------------------------------------------------------- */ /** Booking details as label => value (empty values skipped). */ function ethebo_booking_rows( $b ) { $rows = array( ethebo__( 'Booking' ) => '#' . $b->id, ethebo__( 'Property' ) => get_the_title( $b->property_id ), ethebo__( 'Check-in' ) => ethebo_format_date( $b->check_in ), ethebo__( 'Check-out' ) => ethebo_format_date( $b->check_out ), ethebo__( 'Nights' ) => ethebo_nights( $b->check_in, $b->check_out ), ethebo__( 'Guests' ) => $b->guests, ethebo__( 'Total' ) => ethebo_price( $b->total ), ethebo__( 'Payment' ) => $b->payment_method ? ETHEBO_Payments::label( $b->payment_method ) . ' – ' . ETHEBO_Payments::status_label( $b ) : '', ethebo__( 'Name' ) => $b->guest_name, ethebo__( 'Email' ) => $b->guest_email, ethebo__( 'Phone' ) => $b->guest_phone, ethebo__( 'Notes' ) => $b->notes, ); return array_filter( $rows, function ( $v ) { return '' !== trim( (string) $v ); } ); } /** Plain-text summary (also used in the text part of the emails). */ function ethebo_booking_summary( $b ) { $lines = array(); foreach ( ethebo_booking_rows( $b ) as $label => $value ) { $lines[] = str_pad( $label . ':', 14 ) . $value; } return implode( "\n", $lines ); } /** * Sends a booking email. * * @param string $to Recipient. * @param string $subject Subject. * @param string $intro First paragraph(s). * @param object|null $b Booking (its details are listed) or null. * @param string $extra Extra block, e.g. bank details (shown in a box). * @param string $reply_to Reply-To address. * @param array $button [ url, label ] optional button. */ function ethebo_send_mail( $to, $subject, $intro, $b = null, $extra = '', $reply_to = '', $button = array() ) { $subject = trim( preg_replace( '/[\r\n\t]+/', ' ', (string) $subject ) ); // A subject can never add email headers. $site = get_bloginfo( 'name' ); $rows = $b ? ethebo_booking_rows( $b ) : array(); $accent = sanitize_hex_color( ethebo_opt( 'accent' ) ); $accent = $accent ? $accent : '#2563eb'; // Plain text. $text = $intro; if ( $rows ) { $text .= "\n\n" . ethebo_booking_summary( $b ); } if ( $extra ) { $text .= "\n\n" . $extra; } if ( $button ) { $text .= "\n\n" . $button[1] . ': ' . $button[0]; } $text .= "\n\n— \n" . $site . "\n" . home_url( '/' ); // HTML (inline styles: email programs ignore