Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-travel-engine/admin/class-wp-travel-engine-admin.php
+++ b/wp-travel-engine/admin/class-wp-travel-engine-admin.php
@@ -148,8 +148,8 @@
* Exclude custom trips (from manual booking) from trip count on Trips list.
*
* @param stdClass $counts An object containing the post counts by status.
- * @param string $type Post type.
- * @param string $perm Permission level.
+ * @param string $type Post type.
+ * @param string $perm Permission level.
* @return stdClass Modified counts.
* @since 6.7.3
*/
@@ -253,6 +253,7 @@
'position' => 19,
'condition' => file_exists( WPTRAVELENGINE_UPDATES_DATA_PATH . '/data.json' ),
),
+ 'wptravelengine-logs' => new WPTravelEngineLoggerAdminLogsPage(),
);
$menus = apply_filters( 'wptravelengine-admin:boooking:submenus', $menus );
@@ -2520,6 +2521,11 @@
$wp_travel_engine_setting = get_post_meta( $enquiry_id, 'wp_travel_engine_setting', true );
$wp_travel_engine_enquiry_formdata = get_post_meta( $enquiry_id, 'wp_travel_engine_enquiry_formdata', true );
$wte_old_enquiry_details = isset( $wp_travel_engine_setting['enquiry'] ) ? $wp_travel_engine_setting['enquiry'] : array();
+
+ $enquiry_display = wptravelengine_get_enquiry_form_field_map( isset( $wp_travel_engine_enquiry_formdata['package_id'] ) ? absint( $wp_travel_engine_enquiry_formdata['package_id'] ) : 0 );
+ $enquiry_field_map = $enquiry_display['field_map'];
+ $validation_only_types = $enquiry_display['validation_only_types'];
+
ob_start();
?>
<div style="background-color:#ffffff" class="wpte-main-wrap wpte-edit-enquiry">
@@ -2530,12 +2536,12 @@
<?php
if ( ! empty( $wp_travel_engine_enquiry_formdata ) ) :
foreach ( $wp_travel_engine_enquiry_formdata as $key => $data ) :
- $data = is_array( $data ) ? implode( ', ', $data ) : $data;
- $data_label = wp_travel_engine_get_enquiry_field_label_by_name( $key );
-
- if ( 'package_name' === $key ) {
- $data_label = esc_html__( 'Package Name', 'wp-travel-engine' );
+ if ( wptravelengine_enquiry_should_hide_field( $key, $enquiry_field_map, $validation_only_types ) ) {
+ continue;
}
+
+ $data = is_array( $data ) ? implode( ', ', $data ) : $data;
+ $data_label = wptravelengine_enquiry_get_field_display_label( $key, $enquiry_field_map );
?>
<li>
<b><?php echo esc_html( $data_label ); ?></b>
--- a/wp-travel-engine/admin/meta-parts/enquiry.php
+++ b/wp-travel-engine/admin/meta-parts/enquiry.php
@@ -11,8 +11,11 @@
$wp_travel_engine_setting = get_post_meta( $post->ID, 'wp_travel_engine_setting', true );
$wp_travel_engine_enquiry_formdata = get_post_meta( $post->ID, 'wp_travel_engine_enquiry_formdata', true );
-$wte_old_enquiry_details = $wp_travel_engine_setting[ 'enquiry' ] ?? array();
+$wte_old_enquiry_details = $wp_travel_engine_setting['enquiry'] ?? array();
+$enquiry_display = wptravelengine_get_enquiry_form_field_map( isset( $wp_travel_engine_enquiry_formdata['package_id'] ) ? absint( $wp_travel_engine_enquiry_formdata['package_id'] ) : 0 );
+$enquiry_field_map = $enquiry_display['field_map'];
+$validation_only_types = $enquiry_display['validation_only_types'];
?>
<div class="wpte-main-wrap wpte-edit-enquiry">
<div class="wpte-block-wrap">
@@ -23,12 +26,12 @@
<?php
if ( ! empty( $wp_travel_engine_enquiry_formdata ) ) :
foreach ( $wp_travel_engine_enquiry_formdata as $key => $data ) :
- $data = is_array( $data ) ? implode( ', ', $data ) : $data;
- $data_label = wp_travel_engine_get_enquiry_field_label_by_name( $key );
-
- if ( 'package_name' === $key ) {
- $data_label = esc_html__( 'Package Name', 'wp-travel-engine' );
+ if ( wptravelengine_enquiry_should_hide_field( $key, $enquiry_field_map, $validation_only_types ) ) {
+ continue;
}
+
+ $data = is_array( $data ) ? implode( ', ', $data ) : $data;
+ $data_label = wptravelengine_enquiry_get_field_display_label( $key, $enquiry_field_map );
?>
<li>
<b><?php echo esc_html( $data_label ); ?></b>
@@ -36,91 +39,89 @@
<?php echo wp_kses_post( $data ); ?>
</span>
</li>
- <?php
+ <?php
endforeach;
- else :
- if ( ! empty( $wte_old_enquiry_details ) ) :
- if ( isset( $wte_old_enquiry_details[ 'pname' ] ) ) :
- ?>
+ elseif ( ! empty( $wte_old_enquiry_details ) ) :
+ if ( isset( $wte_old_enquiry_details['pname'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Package Name', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'pname' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['pname'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'name' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['name'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Name', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'name' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['name'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'email' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['email'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Email', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'email' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['email'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'country' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['country'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Country', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'country' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['country'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'contact' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['contact'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Contact', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'contact' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['contact'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'adults' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['adults'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Adults', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'adults' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['adults'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'children' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['children'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Children', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'children' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['children'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- if ( isset( $wte_old_enquiry_details[ 'message' ] ) ) :
- ?>
+ if ( isset( $wte_old_enquiry_details['message'] ) ) :
+ ?>
<li>
<b><?php esc_html_e( 'Message', 'wp-travel-engine' ); ?></b>
<span>
- <?php echo wp_kses_post( $wte_old_enquiry_details[ 'message' ] ); ?>
+ <?php echo wp_kses_post( $wte_old_enquiry_details['message'] ); ?>
</span>
</li>
- <?php
+ <?php
endif;
- endif;
endif;
?>
</ul>
--- a/wp-travel-engine/dist/blocks/trip-pages/search/block.php
+++ b/wp-travel-engine/dist/blocks/trip-pages/search/block.php
@@ -7,88 +7,4 @@
use WPTravelEngineModulesTripSearch;
TripSearch::enqueue_scripts();
-do_action( 'wp_travel_engine_trip_archive_wrap' );
-return;
-
-// TODO: Remove this once above is stable
-// use WPTravelEngineModulesTripSearch;
-// wp_enqueue_script( 'wp-travel-engine' );
-// wp_enqueue_style( 'wp-travel-engine' );
-
-?>
- <div class="wp-travel-engine-archive-outer-wrap">
- <?php
- /**
- * wp_travel_engine_archive_sidebar hook
- *
- * @hooked wte_advanced_search_archive_sidebar
- */
- do_action( 'wp_travel_engine_archive_sidebar' );
- ?>
- <div class="wp-travel-engine-archive-repeater-wrap">
- <?php
- Wp_Travel_Engine_Archive_Hooks::archive_filters_sub_options();
- ?>
- <div class="wte-category-outer-wrap">
- <?php
- $j = 1;
- $view_mode = wp_travel_engine_get_archive_view_mode();
- $show_sidebar = wptravelengine_toggled( get_option( 'wptravelengine_show_trip_search_sidebar', 'yes' ) );
- $classes = apply_filters( 'wte_advanced_search_trip_results_grid_classes', $show_sidebar ? 'col-2 category-grid' : 'col-3 category-grid' );
- $view_class = 'grid' === $view_mode ? $classes : 'category-list';
-
- echo '<div class="category-main-wrap ' . esc_attr( $view_class ) . '">';
- $query = new WP_Query( TripSearch::get_query_args() );
- $user_wishlists = wptravelengine_user_wishlists();
- $template_name = wptravelengine_get_template_by_view_mode( $view_mode );
-
- while ( $query->have_posts() ) {
- $query->the_post();
- $details = wte_get_trip_details( get_the_ID() );
- $details['j'] = $j;
- $details['user_wishlists'] = $user_wishlists;
-
- wptravelengine_get_template( $template_name, $details );
- $j++;
- }
- wp_reset_postdata();
- echo '</div>';
-
- $total_pages = $query->max_num_pages;
- $big = 999999999; // need an unlikely integer
-
- if ( $total_pages > 1 ) {
- $current_page = max( 1, get_query_var( 'paged' ) );
- echo '<div class="trip-pagination pagination">';
- echo '<div class="nav-links">';
- echo paginate_links(
- array(
- 'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
- 'format' => '?paged=%#%',
- 'current' => $current_page,
- 'total' => $total_pages,
- 'prev_text' => __( 'Previous', 'wp-travel-engine' ),
- 'next_text' => __( 'Next', 'wp-travel-engine' ),
- 'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'wp-travel-engine' ) . ' </span>',
- )
- ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- echo '</div>';
- echo '</div>';
- }
- ?>
- </div>
- <div id="loader" style="display: none">
- <div class="table">
- <div class="table-grid">
- <div class="table-cell">
- <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </div>
- </div>
- </div>
- </div>
- <?php
- $nonce = wp_create_nonce( 'wte_show_ajax_result' );
- ?>
- <input type="hidden" name="search-nonce" id="search-nonce" value="<?php echo esc_attr( $nonce ); ?>">
- </div>
- </div>
+do_action( 'wp_travel_engine_trip_archive_wrap' );
No newline at end of file
--- a/wp-travel-engine/dist/global/button-form-field.asset.php
+++ b/wp-travel-engine/dist/global/button-form-field.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array(), 'version' => 'dc0b742d290723376f62');
--- a/wp-travel-engine/dist/public/components/trip-booking-modal.asset.php
+++ b/wp-travel-engine/dist/public/components/trip-booking-modal.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'fa7a88a26825dab691fa');
+<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '41de58368d2c79596187');
--- a/wp-travel-engine/dist/public/exports.asset.php
+++ b/wp-travel-engine/dist/public/exports.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'b550c566e80e6c335841');
+<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '70a063f602b585c694d6');
--- a/wp-travel-engine/dist/public/my-account.asset.php
+++ b/wp-travel-engine/dist/public/my-account.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => 'dc62ac15975d7f77b3d7');
+<?php return array('dependencies' => array(), 'version' => '1fa5cfafdb4a925a7576');
--- a/wp-travel-engine/dist/public/single-trip.asset.php
+++ b/wp-travel-engine/dist/public/single-trip.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-dom-ready', 'wp-i18n'), 'version' => '75b1adb46de1a421d4c7');
+<?php return array('dependencies' => array('wp-dom-ready', 'wp-i18n'), 'version' => '1c02c424ff1c82e646a0');
--- a/wp-travel-engine/dist/public/trip-archive.asset.php
+++ b/wp-travel-engine/dist/public/trip-archive.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '7378cab2471d2c5495c1');
+<?php return array('dependencies' => array(), 'version' => 'fa5249d8991eb0d23fef');
--- a/wp-travel-engine/dist/public/trip-checkout.asset.php
+++ b/wp-travel-engine/dist/public/trip-checkout.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash'), 'version' => '37b7584ba9467303ed79');
+<?php return array('dependencies' => array('lodash'), 'version' => '93600446307e39110341');
--- a/wp-travel-engine/dist/public/trip-wishlist.asset.php
+++ b/wp-travel-engine/dist/public/trip-wishlist.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '682a1f10eedf90cb4d7e');
+<?php return array('dependencies' => array(), 'version' => 'fcd562bac5b1f54e3fc9');
--- a/wp-travel-engine/dist/public/wte-public.asset.php
+++ b/wp-travel-engine/dist/public/wte-public.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => 'bc79d4c9af5c44fff58a');
+<?php return array('dependencies' => array('wp-i18n'), 'version' => '509b101b72b0de07888e');
--- a/wp-travel-engine/includes/backend/templates/booking/legacy/partials/remaining-payment.php
+++ b/wp-travel-engine/includes/backend/templates/booking/legacy/partials/remaining-payment.php
@@ -22,7 +22,7 @@
}
}
$is_customized_reservation = $booking->get_meta( '_user_edited' );
-if ( $payment_amount >= $due_amount && $is_customized_reservation ) {
+if ( $is_customized_reservation ) {
return;
}
if ( ! $is_booking_edit_enabled || ! $booking->has_due_payment() || ! $booking->get_order_items() ) {
--- a/wp-travel-engine/includes/class-wp-travel-engine-custom-shortcodes.php
+++ b/wp-travel-engine/includes/class-wp-travel-engine-custom-shortcodes.php
@@ -369,14 +369,19 @@
'wte_trip'
);
- if ( ! in_array( $attr['layout'], array( 'grid', 'list' ) ) ) {
- return '<h1>' . sprintf( __( 'Layout not found: %s', 'wp-travel-engine' ), $attr['layout'] ) . '</h1>';
+ $allowed_layouts = array( 'grid', 'list' );
+ if ( ! in_array( $attr['layout'], $allowed_layouts, true ) ) {
+ return '<h1>' . esc_html__( 'Invalid layout parameter. Allowed values: grid, list', 'wp-travel-engine' ) . '</h1>';
+ }
+
+ $attr['postsnumber'] = absint( $attr['postsnumber'] );
+ if ( $attr['postsnumber'] < 1 ) {
+ $attr['postsnumber'] = get_option( 'posts_per_page', 10 );
}
if ( ! empty( $attr['ids'] ) ) {
- $ids = array();
- $ids = explode( ',', $attr['ids'] );
- $attr['ids'] = $ids;
+ $ids = array_map( 'absint', explode( ',', $attr['ids'] ) );
+ $attr['ids'] = array_filter( $ids );
}
ob_start();
@@ -405,26 +410,29 @@
'wte_trip_tax'
);
- if ( ! in_array( $attr['layout'], array( 'grid', 'list' ) ) ) {
- return '<h1>' . sprintf( __( 'Layout not found: %s', 'wp-travel-engine' ), $attr['layout'] ) . '</h1>';
+ $allowed_layouts = array( 'grid', 'list' );
+ if ( ! in_array( $attr['layout'], $allowed_layouts, true ) ) {
+ return '<h1>' . esc_html__( 'Invalid layout parameter. Allowed values: grid, list', 'wp-travel-engine' ) . '</h1>';
+ }
+
+ $attr['postsnumber'] = absint( $attr['postsnumber'] );
+ if ( $attr['postsnumber'] < 1 ) {
+ $attr['postsnumber'] = get_option( 'posts_per_page', 10 );
}
if ( ! empty( $attr['activities'] ) ) {
- $activities = array();
- $activities = explode( ',', $attr['activities'] );
- $attr['activities'] = $activities;
+ $activities = array_map( 'absint', explode( ',', $attr['activities'] ) );
+ $attr['activities'] = array_filter( $activities );
}
if ( ! empty( $attr['destination'] ) ) {
- $destination = array();
- $destination = explode( ',', $attr['destination'] );
- $attr['destination'] = $destination;
+ $destination = array_map( 'absint', explode( ',', $attr['destination'] ) );
+ $attr['destination'] = array_filter( $destination );
}
if ( ! empty( $attr['trip_types'] ) ) {
- $trip_types = array();
- $trip_types = explode( ',', $attr['trip_types'] );
- $attr['trip_types'] = $trip_types;
+ $trip_types = array_map( 'absint', explode( ',', $attr['trip_types'] ) );
+ $attr['trip_types'] = array_filter( $trip_types );
}
ob_start();
--- a/wp-travel-engine/includes/class-wp-travel-engine-enquiry-form-shortcodes.php
+++ b/wp-travel-engine/includes/class-wp-travel-engine-enquiry-form-shortcodes.php
@@ -199,7 +199,7 @@
<?php
do_action( 'wte_enquiry_contact_form_before_submit_button' );
?>
- <input type="submit" class="enquiry-submit" name="enquiry_submit_button" id="enquiry_submit_button" value="<?php echo esc_attr__( 'Send Email', 'wp-travel-engine' ); ?>">
+ <button type="submit" class="wpte-button enquiry-submit" name="enquiry_submit_button" id="enquiry_submit_button"><?php echo esc_attr__( 'Send Email', 'wp-travel-engine' ); ?></button>
<?php
do_action( 'wte_enquiry_contact_form_after_submit_button' );
?>
--- a/wp-travel-engine/includes/class-wte-ajax.php
+++ b/wp-travel-engine/includes/class-wte-ajax.php
@@ -67,6 +67,23 @@
$ajax_registry->register( AjaxUserWishlist::class );
$ajax_registry->register( AjaxLoadMoreDestination::class );
+ /**
+ * Button Field Actions
+ *
+ * @since 6.7.6
+ */
+ $ajax_registry->register( AjaxAdminButtonAction::class );
+ $ajax_registry->register( AjaxPublicButtonAction::class );
+
+ /**
+ * Logger AJAX Controllers
+ *
+ * @since 6.7.6
+ */
+ $ajax_registry->register( AjaxClearLogs::class );
+ $ajax_registry->register( AjaxDownloadLog::class );
+ $ajax_registry->register( AjaxSendLogSupport::class );
+
add_action(
'wp_ajax_nopriv_email_test',
function () {
--- a/wp-travel-engine/includes/classes/Booking/Email/Template_Tags.php
+++ b/wp-travel-engine/includes/classes/Booking/Email/Template_Tags.php
@@ -72,14 +72,20 @@
parent::__construct();
}
+ /**
+ * Get the trip URL.
+ *
+ * @return string
+ * @since 6.7.6 Updated: only get url from this function.
+ */
public function get_trip_url() {
$order_trip = $this->cart_info_parser->get_item();
if ( ! empty( $order_trip ) ) {
- return '<a href=' . esc_url( get_permalink( $order_trip->get_trip_id() ) ) . '>' . esc_html( $order_trip->get_trip_title() ) . '</a>';
+ return esc_url( get_permalink( $order_trip->get_trip_id() ) );
}
- return '<a href=' . esc_url( get_permalink( $this->trip->ID ) ) . '>' . esc_html( $this->trip->title ) . '</a>';
+ return esc_url( get_permalink( $this->trip->ID ) );
}
public function get_billing_first_name() {
@@ -492,7 +498,7 @@
$is_triggered_manually = 'wte_resend_purchase_receipt' === ( sanitize_text_field( $_REQUEST['action'] ?? '' ) );
ob_start();
-
+ echo '<table border="0" width="100%" cellspacing="0" cellpadding="0">';
foreach ( $this->order_trips as $trip ) :
$trip = (object) $trip;
$trip_modal = new Trip( $trip->ID );
@@ -597,6 +603,7 @@
}
}
endforeach;
+ echo '</table>';
return ob_get_clean();
}
@@ -865,9 +872,9 @@
?>
<tr style="font-size: 16px;">
<td colspan="2">
- <span style="display: block;padding: 8px 16px;background-color: rgba(15, 29, 35, 0.04);border-radius: 4px;margin: 0 -16px;">
+ <span style="display: flex;padding: 8px 16px;background-color: rgba(15, 29, 35, 0.04);border-radius: 4px;margin: 0 -16px;">
<strong style="width: 50%;display: inline-block;"><?php esc_html_e( 'Total', 'wp-travel-engine' ); ?></strong>
- <strong style="width: 49%;text-align: right;display: inline-block;">
+ <strong style="width: 50%;text-align: right;display: inline-block;">
<?php
echo wptravelengine_the_price( $amounts['total'], false );
if ( $tax_enable == 'yes' && isset( $global_settings['tax_type_option'] ) && 'inclusive' === $global_settings['tax_type_option'] ) {
@@ -949,9 +956,9 @@
<?php if ( 'booking_only' === $payment_gateway || 'check_payments' === $payment_gateway ) { ?>
<tr>
<td colspan="2">
- <span style="display: block;padding: 8px 16px;background-color: #147dfe1a;border-radius: 4px;margin: 0 -16px;">
+ <span style="display: flex;padding: 8px 16px;background-color: #147dfe1a;border-radius: 4px;margin: 0 -16px;">
<strong style="width: 50%;display: inline-block;"><?php esc_html_e( 'Payable Amount', 'wp-travel-engine' ); ?></strong>
- <strong style="width: 49%;text-align: right;display: inline-block;">
+ <strong style="width: 50%;text-align: right;display: inline-block;">
<?php
echo wptravelengine_the_price( $payment_model->get_payable_amount(), false );
?>
@@ -962,9 +969,9 @@
<?php } else { ?>
<tr>
<td colspan="2">
- <span style="display: block;padding: 8px 16px;background-color: #147dfe1a;border-radius: 4px;margin: 0 -16px;">
+ <span style="display: flex;padding: 8px 16px;background-color: #147dfe1a;border-radius: 4px;margin: 0 -16px;">
<strong style="width: 50%;display: inline-block;"><?php esc_html_e( 'Amount Paid', 'wp-travel-engine' ); ?></strong>
- <strong style="width: 49%;text-align: right;display: inline-block;">
+ <strong style="width: 50%;text-align: right;display: inline-block;">
<?php
echo wptravelengine_the_price( $amounts['deposit'], false );
?>
@@ -1033,82 +1040,84 @@
// Trip Booking Summary.
echo $this->get_trip_booking_summary();
?>
- <tr>
- <td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
- </tr>
- <?php
- // Set called from booking details to true.
- $this->called_from_booking_details = true;
- // Trip Booking Payment Details.
- echo $this->get_trip_booking_payment();
-
- // Bank Details.
- if ( $this->payment && 'direct_bank_transfer' === $this->payment->payment_gateway ) :
- $bank_details_labels = array(
- 'account_name' => __( 'Account Name', 'wp-travel-engine' ),
- 'account_number' => __( 'Account Number', 'wp-travel-engine' ),
- 'bank_name' => __( 'Bank Name', 'wp-travel-engine' ),
- 'sort_code' => __( 'Sort Code', 'wp-travel-engine' ),
- 'iban' => __( 'IBAN', 'wp-travel-engine' ),
- 'swift' => __( 'BIC/Swift', 'wp-travel-engine' ),
- );
-
- $settings = get_option( 'wp_travel_engine_settings', array() );
-
- $bank_accounts = wte_array_get( $settings, 'bank_transfer.accounts', array() );
- ?>
+ <table border="0" width="100%" cellspacing="0" cellpadding="0">
<tr>
<td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
</tr>
- <tr>
- <td colspan="2" style="font-size: 16px;line-height: 1.75;font-weight: bold;padding: 8px 0 4px;"><?php esc_html_e( 'Bank Details:', 'wp-travel-engine' ); ?></td>
- </tr>
<?php
- foreach ( $bank_accounts as $account ) :
- foreach ( $bank_details_labels as $key => $label ) :
+ // Set called from booking details to true.
+ $this->called_from_booking_details = true;
+ // Trip Booking Payment Details.
+ echo $this->get_trip_booking_payment();
+
+ // Bank Details.
+ if ( $this->payment && 'direct_bank_transfer' === $this->payment->payment_gateway ) :
+ $bank_details_labels = array(
+ 'account_name' => __( 'Account Name', 'wp-travel-engine' ),
+ 'account_number' => __( 'Account Number', 'wp-travel-engine' ),
+ 'bank_name' => __( 'Bank Name', 'wp-travel-engine' ),
+ 'sort_code' => __( 'Sort Code', 'wp-travel-engine' ),
+ 'iban' => __( 'IBAN', 'wp-travel-engine' ),
+ 'swift' => __( 'BIC/Swift', 'wp-travel-engine' ),
+ );
+
+ $settings = get_option( 'wp_travel_engine_settings', array() );
+
+ $bank_accounts = wte_array_get( $settings, 'bank_transfer.accounts', array() );
+ ?>
+ <tr>
+ <td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
+ </tr>
+ <tr>
+ <td colspan="2" style="font-size: 16px;line-height: 1.75;font-weight: bold;padding: 8px 0 4px;"><?php esc_html_e( 'Bank Details:', 'wp-travel-engine' ); ?></td>
+ </tr>
+ <?php
+ foreach ( $bank_accounts as $account ) :
+ foreach ( $bank_details_labels as $key => $label ) :
+ ?>
+ <tr>
+ <td style="color: #566267;"><?php esc_html_e( $label, 'wp-travel-engine' ); ?></td>
+ <td style="width: 50%;text-align: right;"><strong><?php echo isset( $account[ $key ] ) ? esc_html( $account[ $key ] ) : ''; ?></strong></td>
+ </tr>
+ <?php
+ endforeach;
?>
- <tr>
- <td style="color: #566267;"><?php esc_html_e( $label, 'wp-travel-engine' ); ?></td>
- <td style="width: 50%;text-align: right;"><strong><?php isset( $account[ $key ] ) ? esc_html_e( $account[ $key ], 'wp-travel-engine' ) : ''; ?></strong></td>
- </tr>
<?php
endforeach;
+ endif;
+
+ // Check Payment Details.
+ if ( $this->payment && 'check_payments' === $this->payment->payment_gateway ) :
?>
+ <tr>
+ <td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
+ </tr>
+ <tr>
+ <td colspan="2" style="font-size: 16px;line-height: 1.75;font-weight: bold;padding: 8px 0 4px;"><?php esc_html_e( 'Check Payment Instructions:', 'wp-travel-engine' ); ?></td>
+ </tr>
+ <tr>
+ <td colspan="2" style="color: #566267;"><?php echo esc_html( wptravelengine_settings()->get( 'check_payment.instruction', '' ) ); ?></td>
+ </tr>
<?php
- endforeach;
- endif;
-
- // Check Payment Details.
- if ( $this->payment && 'check_payments' === $this->payment->payment_gateway ) :
- ?>
- <tr>
- <td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
- </tr>
- <tr>
- <td colspan="2" style="font-size: 16px;line-height: 1.75;font-weight: bold;padding: 8px 0 4px;"><?php esc_html_e( 'Check Payment Instructions:', 'wp-travel-engine' ); ?></td>
- </tr>
- <tr>
- <td colspan="2" style="color: #566267;"><?php esc_html_e( wptravelengine_settings()->get( 'check_payment.instruction', '' ), 'wp-travel-engine' ); ?></td>
- </tr>
- <?php
- endif;
+ endif;
- // Additional Notes.
- if ( ! empty( $this->additional_notes ) ) :
- ?>
- <tr>
- <td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
- </tr>
- <tr>
- <td colspan="2" style="font-size: 16px;line-height: 1.75;font-weight: bold;padding: 8px 0 4px;"><?php esc_html_e( 'Additional Notes:', 'wp-travel-engine' ); ?></td>
- </tr>
- <tr>
- <td colspan="2" style="color: #566267;"><?php echo esc_html( $this->additional_notes ); ?></td>
- </tr>
- <?php
+ // Additional Notes.
+ if ( ! empty( $this->additional_notes ) ) :
+ ?>
+ <tr>
+ <td colspan="2"><hr style="border: none;border-top: 1px solid #DCDFEA;"></td>
+ </tr>
+ <tr>
+ <td colspan="2" style="font-size: 16px;line-height: 1.75;font-weight: bold;padding: 8px 0 4px;"><?php esc_html_e( 'Additional Notes:', 'wp-travel-engine' ); ?></td>
+ </tr>
+ <tr>
+ <td colspan="2" style="color: #566267;"><?php echo esc_html( $this->additional_notes ); ?></td>
+ </tr>
+ <?php
+ endif;
+ do_action( 'wptravelengine_email_template_after_additional_notes', $this );
endif;
- do_action( 'wptravelengine_email_template_after_additional_notes', $this );
- endif;
+ echo '</table>';
return ob_get_clean();
}
@@ -1179,20 +1188,20 @@
$trip = $this->trip;
- $traveler_data = get_post_meta( $this->booking->ID, 'wp_travel_engine_placeorder_setting', true );
- $personal_options = isset( $traveler_data['place_order'] ) ? $traveler_data['place_order'] : array();
+ $booking_id = $this->booking->ID;
+ $_booking = new Booking( $booking_id );
+ $edit_booking_link = admin_url() . 'post.php?post=' . $booking_id . '&action=edit';
+
+ $traveller_data = $_booking->get_travelers();
- $item = $this->trip;
- $pno = ( isset( $item->pax ) ) ? array_sum( $item->pax ) : 0;
- if ( isset( $personal_options['travelers'] ) ) :
- $traveller_email_template_content = $this->get_traveller_template( 'traveller-data', $pno, $personal_options );
+ $pno = ( isset( $trip->pax ) ) ? array_sum( $trip->pax ) : 0;
+ if ( ! empty( $traveller_data ) ) :
+ $traveller_email_template_content = $this->get_traveller_template( 'traveller-data', $pno, $traveller_data );
else :
$traveller_email_template_content = '';
endif;
- $booking_id = $this->booking->ID;
- $_booking = new Booking( $booking_id );
- $edit_booking_link = admin_url() . 'post.php?post=' . $booking_id . '&action=edit';
+ $order_trip = reset( $this->order_trips );
parent::set_tags(
apply_filters(
@@ -1205,13 +1214,13 @@
'{billing_address}' => $this->get_billing_address(),
'{city}' => $this->get_billing_city(),
'{country}' => $this->get_billing_country(),
- '{tdate}' => ( isset( $trip->has_time ) && $trip->has_time && isset( $trip->datetime ) ) ? wp_date( 'Y-m-d H:i', strtotime( $trip->datetime ) ) : wp_date( get_option( 'date-format', 'Y-m-d' ), strtotime( isset( $trip->datetime ) ? $trip->datetime : '' ) ),
+ '{tdate}' => wptravelengine_format_trip_datetime( $trip->datetime ),
'{traveler}' => isset( $trip->pax ) ? array_sum( $trip->pax ) : 0,
// '{child-traveler}' => $trip->pax['child'],
'{tprice}' => isset( $trip->cost ) ? wte_get_formated_price( $trip->cost, $this->currency ) : 0,
'{price}' => $_booking->get_total_paid_amount() == 0 ? 0 : wte_get_formated_price( $_booking->get_total_paid_amount(), $this->currency ),
'{total_cost}' => wte_get_formated_price( $this->cart_info->total ?? $this->cart_info['total'] ?? 0, $this->currency ),
- '{due}' => $this->get_due_amount(),
+ '{due}' => wte_get_formated_price( max( 0, $_booking->get_total_due_amount() ), $this->currency ),
'{booking_url}' => $edit_booking_link,
'{date}' => $this->get_current_date(),
'{booking_id}' => sprintf( __( 'Booking #%1$s', 'wp-travel-engine' ), $this->booking->ID ),
@@ -1240,13 +1249,14 @@
'{customer_email}' => $this->get_billing_email(),
'{trip_booked_date}' => $this->get_current_date(),
'{trip_start_date}' => wptravelengine_format_trip_datetime( $trip->datetime ),
- '{trip_end_date}' => wptravelengine_format_trip_end_datetime( $trip->datetime, new Trip( $trip->ID ) ),
- '{no_of_travellers}' => isset( $trip->pax ) ? array_sum( $trip->pax ) : 0,
+ '{trip_end_date}' => wptravelengine_format_trip_datetime( isset( $order_trip['end_datetime'] ) ? $order_trip['end_datetime'] : '' ),
+ '{no_of_travellers}' => $this->get_no_of_travellers(),
'{trip_total_price}' => $this->get_total_amount(),
- '{trip_paid_amount}' => $this->get_paid_amount(),
- '{trip_due_amount}' => $this->get_due_amount(),
+ '{trip_paid_amount}' => wte_get_formated_price( $_booking->get_total_paid_amount(), $this->currency ),
+ '{trip_due_amount}' => wte_get_formated_price( max( 0, $_booking->get_total_due_amount() ), $this->currency ),
'{payment_link}' => $_booking->get_due_payment_link(),
'{trip_code}' => $this->get_trip_code(),
+ '{trip_extra_fee}' => wte_get_formated_price( $this->get_trip_extra_fee( $_booking ), $this->currency ),
),
$this->payment->ID,
$this->booking->ID
@@ -1257,6 +1267,44 @@
}
/**
+ * Get the trip extra fee.
+ * This function consists the extra fee..
+ *
+ * @param Booking $booking
+ * @return mixed
+ * @since 6.7.6
+ */
+ public function get_trip_extra_fee( Booking $booking ) {
+ $payment_data = $booking->get_payments_data( false );
+ $total_paid_amount = $booking->get_total_paid_amount();
+ if ( $total_paid_amount === 0.0 ) {
+ return isset( $payment_data['totals']['extra_charges'] ) ? $payment_data['totals']['extra_charges'] : 0;
+ }
+ // Addition of extra fee for the trip addition of ( Tax, Booking Fee, Payment Gateway Fee ).
+ $payment_calculator = PaymentCalculator::for( $booking->get_currency() );
+ $payable = $payment_calculator->subtract( $payment_data['totals']['payable'] ?? 0, $total_paid_amount );
+ // Make sure payable is positive number because payment transaction fee can be added to paid amount directly which can be negative after sub.
+ $payable = (string) abs( floatval( $payable ) );
+ $extra_fee = $payment_calculator->add( $payable, $payment_data['totals']['extra_charges'] ?? 0 );
+ return $extra_fee;
+ }
+
+ /**
+ * Get the number of travellers.
+ *
+ * @return int
+ * @since 6.7.6
+ */
+ public function get_no_of_travellers(): int {
+ if ( ! empty( $this->trip->pax ) ) {
+ return array_sum( $this->trip->pax );
+ } elseif ( ! empty( $this->cart_info['items'][0]['travelers_count'] ) ) {
+ return (int) $this->cart_info['items'][0]['travelers_count'];
+ }
+ return 0;
+ }
+
+ /**
* Set default tags.
*
* @param array $tags The tags.
--- a/wp-travel-engine/includes/classes/Builders/FormFields/BillingEditFormFields.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/BillingEditFormFields.php
@@ -37,7 +37,7 @@
if ( $name ) {
$field['name'] = sprintf( 'billing[%s]', $name );
$field['id'] = sprintf( 'billing_%s', $name );
- $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : $field['field_label'];
+ $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : ( $field['field_label'] ?? '' );
$field['default'] = $this->defaults[ $name ] ?? $field['default'] ?? '';
$field['validations']['required'] = false;
@@ -89,7 +89,7 @@
}
}
- if ( static::$mode !== 'edit' ) {
+ if ( static::$mode !== 'edit' && ! ( $field['skip_disabled'] ?? false ) ) {
$field['option_attributes'] = array(
'disabled' => 'disabled',
);
--- a/wp-travel-engine/includes/classes/Builders/FormFields/DefaultFormFields.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/DefaultFormFields.php
@@ -286,6 +286,7 @@
'options' => apply_filters(
'wptravelengine_payments_form_fields_status_options',
array(
+ '__skip_none_option__' => true,
'completed' => __( 'Completed', 'wp-travel-engine' ),
'pending' => __( 'Pending', 'wp-travel-engine' ),
'failed' => __( 'Failed', 'wp-travel-engine' ),
--- a/wp-travel-engine/includes/classes/Builders/FormFields/EmergencyEditFormFields.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/EmergencyEditFormFields.php
@@ -50,7 +50,7 @@
if ( $name ) {
$field['name'] = sprintf( 'emergency_contacts[%s][]', $name );
$field['id'] = sprintf( 'emergency_contacts_%s', $name );
- $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : $field['field_label'];
+ $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : ( $field['field_label'] ?? '' );
$field['default'] = $this->defaults[ $name ] ?? $field['default'] ?? '';
$field['validations']['required'] = false;
// Convert country code to country name to show in the emergency form.
@@ -76,7 +76,7 @@
);
}
- if ( static::$mode !== 'edit' ) {
+ if ( static::$mode !== 'edit' && ! ( $field['skip_disabled'] ?? false ) ) {
$field['option_attributes'] = array(
'disabled' => 'disabled',
);
--- a/wp-travel-engine/includes/classes/Builders/FormFields/FormField.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/FormField.php
@@ -2,8 +2,7 @@
namespace WPTravelEngineBuildersFormFields;
-use WPTravelEngineUtilitiesFormBuilderFieldsCurrencyPicker;
-
+use WPTravelEngineHelpersFunctions;
if ( ! class_exists( 'WP_Travel_Engine_Form_Field' ) ) {
require_once WP_TRAVEL_ENGINE_ABSPATH . 'includes/lib/wte-form-framework/class-wte-form-field.php';
}
@@ -71,11 +70,11 @@
* @return array The modified options array.
*/
public function modify_form_field_options( $options ): array {
- if ( ! empty( $options ) && ! array_key_exists( '', $options ) ) {
- $options = array_merge( array( '' => __( 'None', 'wp-travel-engine' ) ), $options );
+ if ( ! is_array( $options ) ) {
+ return $options;
}
- return $options;
+ return Functions::add_none_option_to_select( $options, false );
}
/**
--- a/wp-travel-engine/includes/classes/Builders/FormFields/LeadTravellersFormFields.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/LeadTravellersFormFields.php
@@ -2,21 +2,31 @@
/**
* Lead Travellers Form Fields.
*
+ * @package WPTravelEngineBuildersFormFields
* @since 6.4.3
*/
namespace WPTravelEngineBuildersFormFields;
+use WPTravelEngineCoreModelsPostCustomer as CustomerModel;
+
/**
* Form field class to render lead travellers form fields.
*/
class LeadTravellersFormFields extends LeadTravellerFormFields {
/**
+ * Form fields configuration.
+ *
* @var array
*/
public $fields;
+ /**
+ * Constructor.
+ *
+ * @param array $args Optional arguments (unused, for compatibility).
+ */
public function __construct( $args = array() ) {
$this->fields = DefaultFormFields::lead_traveller_form_fields();
parent::__construct();
@@ -51,7 +61,7 @@
public function lead_traveller_form_fields() {
?>
<div id="wpte-lead-traveller" class="wpte-checkout__form-section">
- <h5 class="wpte-checkout__form-title"><?php printf( __( 'Lead Traveller %d', 'wp-travel-engine' ), 1 ); ?></h5>
+ <h5 class="wpte-checkout__form-title"><?php echo esc_html( sprintf( /* translators: %d: traveller number */ __( 'Lead Traveller %d', 'wp-travel-engine' ), 1 ) ); ?></h5>
<?php
$this->render_traveler_fields( $this->fields );
?>
@@ -61,30 +71,83 @@
/**
+ * Get lead traveller data from customer meta (logged-in users).
+ *
+ * Uses in-request static cache and WordPress object cache (when available) to avoid
+ * repeated user/customer/meta queries when the form is rendered multiple times or across requests.
+ *
+ * @since 6.7.6
+ * @return array Lead traveller data (index 0) from customer meta, or empty array.
+ */
+ protected function get_customer_lead_cached() {
+ static $cache = array();
+ $user_id = get_current_user_id();
+ if ( isset( $cache[ $user_id ] ) ) {
+ return $cache[ $user_id ];
+ }
+ $cache_group = 'wptravelengine_lead_traveller';
+ $cache_key = 'customer_lead_' . $user_id;
+ $from_object_cache = function_exists( 'wp_cache_get' ) ? wp_cache_get( $cache_key, $cache_group ) : false;
+ if ( is_array( $from_object_cache ) ) {
+ $cache[ $user_id ] = $from_object_cache;
+ return $from_object_cache;
+ }
+ $customer_lead = array();
+ if ( $user_id && is_user_logged_in() ) {
+ $user_data = get_user_by( 'id', $user_id );
+ $customer_id = $user_data ? CustomerModel::is_exists( $user_data->user_email ) : 0;
+ if ( $customer_id ) {
+ $customer_data = new CustomerModel( $customer_id );
+ $customer_meta = $customer_data->get_customer_meta();
+ if ( isset( $customer_meta['wptravelengine_traveller_details'][0] ) && is_array( $customer_meta['wptravelengine_traveller_details'][0] ) ) {
+ $customer_lead = $customer_meta['wptravelengine_traveller_details'][0];
+ }
+ }
+ }
+ $cache[ $user_id ] = $customer_lead;
+ if ( function_exists( 'wp_cache_set' ) ) {
+ wp_cache_set( $cache_key, $customer_lead, $cache_group, 300 );
+ }
+ return $customer_lead;
+ }
+
+ /**
* Map the fields.
*
* @param array $fields Form fields.
* @return array
*/
protected function map_fields( $fields ) {
- $form_data = WTE()->session->get( 'travellers_form_data' );
- if ( ! $form_data ) {
- $form_data = array();
+ // Session lead traveller (index 0); merged on top of customer data so current-session input is preserved (e.g. after validation errors).
+ $form_data = WTE()->session->get( 'travellers_form_data' );
+ $session_lead = array();
+ if ( is_array( $form_data ) && isset( $form_data[0] ) && is_array( $form_data[0] ) ) {
+ $session_lead = $form_data[0];
+ }
+
+ // Saved lead traveller from customer meta (logged-in users only); used as base so users do not re-enter every time.
+ // Cached per request to avoid repeated user/customer/meta queries when the form is rendered multiple times.
+ $customer_lead = $this->get_customer_lead_cached();
+
+ // Start with customer data; overlay only non-empty session values so empty session keys don't blank out customer data. Allow 0 and '0' as valid.
+ $merged_lead = $customer_lead;
+ foreach ( $session_lead as $key => $value ) {
+ if ( ! empty( $value ) || $value === '0' || $value === 0 ) {
+ $merged_lead[ $key ] = $value;
+ }
}
$fields = array_map(
- function ( $field ) use ( $form_data ) {
+ function ( $field ) use ( $merged_lead ) {
$name = preg_match( '#[([^[]+)]$#', $field['name'], $matches ) ? $matches[1] : $field['name'];
if ( $name ) {
- $field['class'] = 'wpte-checkout__input';
- $field['wrapper_class'] = 'wpte-checkout__form-col';
- $field['name'] = sprintf( 'travellers[%d][%s]', 0, $name );
-
- $field['id'] = sprintf( 'travellers_%d_%s', 0, $name );
-
+ $field['class'] = 'wpte-checkout__input';
+ $field['wrapper_class'] = 'wpte-checkout__form-col';
+ $field['name'] = sprintf( 'travellers[%d][%s]', 0, $name );
+ $field['id'] = sprintf( 'travellers_%d_%s', 0, $name );
}
- $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : $field['field_label'];
- $field['default'] = $form_data[0][ $name ] ?? $field['default'] ?? '';
+ $field['field_label'] = isset( $field['placeholder'] ) && '' !== $field['placeholder'] ? $field['placeholder'] : $field['field_label'];
+ $field['default'] = $merged_lead[ $name ] ?? $field['default'] ?? '';
return $field;
},
--- a/wp-travel-engine/includes/classes/Builders/FormFields/PaymentEditFormFields.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/PaymentEditFormFields.php
@@ -91,6 +91,25 @@
foreach ( $diff_fields as $slug => $label ) {
$fields[ $slug ] = $this->get_default_structure( $slug, $label );
}
+
+ foreach ( $defaults['button_details'] ?? array() as $key => $btn ) {
+ $id = $btn['id'] ?? "btn_{$key}";
+ $fields[ $id ] = wp_parse_args(
+ $btn,
+ array(
+ 'id' => $id,
+ 'name' => '',
+ 'type' => 'button',
+ 'button_type' => 'button',
+ 'button_text' => __( 'Capture', 'wp-travel-engine' ),
+ 'class' => 'wpte-button wpte-outlined wpte-button-full',
+ 'wrapper_class' => 'wpte-field',
+ 'order' => $btn['order'] ?? 100,
+ 'skip_disabled' => true,
+ 'allow_nopriv' => false,
+ )
+ );
+ }
} else {
$labels['tax'] = wptravelengine_get_tax_label();
$fields['tax'] = $this->get_default_structure( 'tax', $labels['tax'] );
@@ -264,7 +283,7 @@
$name = str_replace( 'payments_', '', $field['id'] );
- $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : $field['field_label'];
+ $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : ( $field['field_label'] ?? '' );
$field['default'] = $this->defaults[ $name ] ?? $field['default'] ?? '';
// Set dynamic prefix if currency is available and field explicitly requests it.
@@ -287,7 +306,7 @@
unset( $field['attributes']['show_prefix'] );
}
- if ( static::$mode !== 'edit' ) {
+ if ( static::$mode !== 'edit' && ! ( $field['skip_disabled'] ?? false ) ) {
$field['option_attributes'] = array(
'disabled' => 'disabled',
);
--- a/wp-travel-engine/includes/classes/Builders/FormFields/TravellerEditFormFields.php
+++ b/wp-travel-engine/includes/classes/Builders/FormFields/TravellerEditFormFields.php
@@ -43,7 +43,7 @@
if ( $name ) {
$field['name'] = sprintf( 'travellers[%s][%s]', $name, $this->count );
$field['id'] = sprintf( 'travellers[%s][%s]', $name, $this->count );
- $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : $field['field_label'];
+ $field['field_label'] = isset( $field['placeholder'] ) && $field['placeholder'] !== '' ? $field['placeholder'] : ( $field['field_label'] ?? '' );
$field['default'] = $this->defaults[ $name ] ?? $field['default'] ?? '';
$field['validations']['required'] = false;
@@ -70,7 +70,7 @@
);
}
- if ( static::$mode !== 'edit' ) {
+ if ( static::$mode !== 'edit' && ! ( $field['skip_disabled'] ?? false ) ) {
$field['option_attributes'] = array(
'disabled' => 'disabled',
);
--- a/wp-travel-engine/includes/classes/Builders/admin-settings/emails/emails-notification.php
+++ b/wp-travel-engine/includes/classes/Builders/admin-settings/emails/emails-notification.php
@@ -85,6 +85,7 @@
'{additional_note}' => __( 'The additional note filled in new checkout template.', 'wp-travel-engine' ),
'{bank_details}' => __( 'Banks Accounts Details. This tag will be replaced with the bank details and sent to the customer receipt email when Bank Transfer method has been chosen by the customer.', 'wp-travel-engine' ),
'{check_payment_instruction}' => __( 'Instructions to make check payment.', 'wp-travel-engine' ),
+ '{trip_extra_fee}' => __( 'The extra fee for the trip.', 'wp-travel-engine' ),
)
)
);
--- a/wp-travel-engine/includes/classes/Core/Booking/BookingProcess.php
+++ b/wp-travel-engine/includes/classes/Core/Booking/BookingProcess.php
@@ -167,7 +167,8 @@
$this->booking->set_meta( 'wptravelengine_billing_details', $sanitized_billing['billing'] ?? array() );
}
- // Add new travelers info to booking meta.
+ // Gather sanitized lead traveller from request (so process_customer can set it in one place).
+ $lead_traveller = array();
if ( $this->request->get_param( 'travellers' ) ) {
// Set travelers form data to session.
WTE()->session->set( 'travellers_form_data', $this->request->get_param( 'travellers' ) );
@@ -181,8 +182,13 @@
// Set travelers details to booking meta.
$this->booking->set_meta( 'wptravelengine_travelers_details', $sanitized_travellers['travelers'] ?? array() );
+
+ $lead_traveller = $sanitized_travellers['travelers'][0] ?? array();
}
+ // Create/fetch customer and set lead traveller details in one place. Lead traveller is already sanitized via TravelersValidator::sanitized().
+ $this->customer = $this->process_customer( $lead_traveller );
+
// Add new emergency contacts info to booking meta.
if ( $this->request->get_param( 'emergency' ) ) {
// Set emergency form data to session.
@@ -208,7 +214,6 @@
$this->update_coupon_usage();
- $this->customer = $this->process_customer();
$this->customer->update_customer_bookings( $this->booking->get_id() );
$this->payment = $this->create_payment();
@@ -449,11 +454,13 @@
/**
* Process customer insert and update customer meta.
*
+ * @param array $lead_traveller Optional. Sanitized lead traveller data (index 0) to store on customer for future checkouts.
* @return Customer
*/
- protected function process_customer(): Customer {
+ protected function process_customer( array $lead_traveller = array() ): Customer {
+ $email = sanitize_email( $this->billing_info['email'] ?? '' );
- if ( $customer_id = Customer::is_exists( $this->billing_info['email'] ?? '' ) ) {
+ if ( $customer_id = Customer::is_exists( $email ) ) {
try {
$customer_model = new Customer( $customer_id );
} catch ( Exception $e ) {
@@ -462,7 +469,7 @@
array(
'post_status' => 'publish',
'post_type' => 'customer',
- 'post_title' => $this->billing_info['email'],
+ 'post_title' => $email,
)
);
}
@@ -473,11 +480,15 @@
array(
'post_status' => 'publish',
'post_type' => 'customer',
- 'post_title' => $this->billing_info['email'],
+ 'post_title' => $email,
)
);
}
+ if ( ! empty( $lead_traveller ) ) {
+ $customer_model->set_my_meta( 'wptravelengine_traveller_details.0', $lead_traveller );
+ }
+
$customer_model->maybe_register_as_user();
return $customer_model;
--- a/wp-travel-engine/includes/classes/Core/Cart/Cart.php
+++ b/wp-travel-engine/includes/classes/Core/Cart/Cart.php
@@ -60,8 +60,8 @@
public function calculate_totals(): void {
if ( ! $this->booking_ref ) {
- $payment_gateway_obj = PaymentGateways::instance()->get_payment_gateway( $this->payment_gateway );
- $this->version = ( $payment_gateway_obj instanceof BaseGateway ) ? $payment_gateway_obj::$cart_version : $this->version;
+ $payment_gateway_obj = PaymentGateways::instance()->get_payment_gateway( $this->payment_gateway );
+ $this->version = ( $payment_gateway_obj instanceof BaseGateway ) ? $payment_gateway_obj::$cart_version : $this->version;
}
$this->reset_totals();
--- a/wp-travel-engine/includes/classes/Core/Controllers/Ajax/AdminButtonAction.php
+++ b/wp-travel-engine/includes/classes/Core/Controllers/Ajax/AdminButtonAction.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Admin Button Action AJAX Controller.
+ *
+ * @package WPTravelEngine/Core/Controllers/Ajax
+ * @since 6.7.6
+ */
+
+namespace WPTravelEngineCoreControllersAjax;
+
+use WPTravelEngineAbstractsAjaxController;
+
+/**
+ * AJAX controller for admin-only button field actions.
+ * Requires user to be logged in.
+ */
+class AdminButtonAction extends AjaxController {
+
+ /**
+ * Nonce key for AJAX request.
+ *
+ * @var string
+ */
+ const NONCE_KEY = '_nonce';
+
+ /**
+ * Nonce action for verification.
+ *
+ * @var string
+ */
+ const NONCE_ACTION = 'wte_admin_button_action_nonce';
+
+ /**
+ * AJAX action name.
+ *
+ * @var string
+ */
+ const ACTION = 'wte_admin_button_action';
+
+ /**
+ * Allow non-privileged users to access this action.
+ * Set to false for admin-only actions.
+ *
+ * @var bool
+ */
+ const ALLOW_NOPRIV = false;
+
+ /**
+ * Process the AJAX request.
+ * This method is called after nonce verification.
+ *
+ * Add-ons should hook into the action hooks to handle button logic.
+ *
+ * @return void
+ */
+ protected function process_request() {
+ do_action( 'wptravelengine_admin_button_action', $this->request );
+ }
+}
--- a/wp-travel-engine/includes/classes/Core/Controllers/Ajax/ClearLogs.php
+++ b/wp-travel-engine/includes/classes/Core/Controllers/Ajax/ClearLogs.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * Clear Logs AJAX Controller.
+ *
+ * @package WPTravelEngineCoreControllersAjax
+ * @since 6.7.6
+ */
+
+namespace WPTravelEngineCoreControllersAjax;
+
+use WPTravelEngineAbstractsAjaxController;
+use WPTravelEngineLoggerUtilitiesLogUtils;
+
+/**
+ * Handles clear logs AJAX request.
+ *
+ * @since 6.7.6
+ */
+class ClearLogs extends AjaxController {
+
+ const NONCE_KEY = 'nonce';
+ const NONCE_ACTION = 'wptravelengine_clear_logs';
+ const ACTION = 'wptravelengine_clear_logs';
+ const ALLOW_NOPRIV = false;
+
+ /**
+ * Process request.
+ *
+ * @return void
+ */
+ protected function process_request() {
+ // Check capability
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_send_json_error( array( 'message' => __( 'You do not have sufficient permissions.', 'wp-travel-engine' ) ) );
+ }
+
+ // Get log directory
+ $log_dir = LogUtils::get_log_directory();
+
+ if ( ! is_dir( $log_dir ) ) {
+ wp_send_json_error( array( 'message' => __( 'Log directory not found.', 'wp-travel-engine' ) ) );
+ }
+
+ // Check if deleting a single file or all files
+ $single_file = $this->request->get_param( 'file' );
+
+ if ( ! empty( $single_file ) ) {
+ $this->delete_single_file( $log_dir, $single_file );
+ } else {
+ $this->delete_all_files( $log_dir );
+ }
+ }
+
+ /**
+ * Delete a single log file.
+ *
+ * @param string $log_dir Log directory path.
+ * @param string $filename File name to delete.
+ * @return void
+ */
+ protected function delete_single_file( string $log_dir, string $filename ): void {
+ $filename = sanitize_file_name( $filename );
+ $file_path = $log_dir . '/' . $filename;
+
+ // Security: verify file exists first (realpath returns false for non-existent files)
+ if ( ! file_exists( $file_path ) ) {
+ wp_send_json_error( array( 'message' => __( 'File not found.', 'wp-travel-engine' ) ) );
+ }
+
+ // Security: ensure file is within log directory (path traversal protection)
+ $real_file_path = realpath( $file_path );
+ $real_log_dir = realpath( $log_dir );
+ if ( false === $real_file_path || false === $real_log_dir || strpos( $real_file_path, $real_log_dir ) !== 0 ) {
+ wp_send_json_error( array( 'message' => __( 'Invalid file path.', 'wp-travel-engine' ) ) );
+ }
+
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
+ if ( unlink( $file_path ) ) {
+ LogUtils::clear_cache();
+
+ wp_send_json_success(
+ array(
+ 'message' => sprintf(
+ // translators: %s file name
+ __( 'Log file "%s" deleted successfully.', 'wp-travel-engine' ),
+ $filename
+ ),
+ )
+ );
+ } else {
+ wp_send_json_error( array( 'message' => __( 'Failed to delete file.', 'wp-travel-engine' ) ) );
+ }
+ }
+
+ /**
+ * Delete all log files.
+ *
+ * @param string $log_dir Log directory path.
+ * @return void
+ */
+ protected function delete_all_files( string $log_dir ): void {
+ // Get all log files
+ $utils = new LogUtils();
+ $log_files = $utils->get_log_files( $log_dir );
+
+ if ( empty( $log_files ) ) {
+ wp_send_json_success( array( 'message' => __( 'No log files to delete.', 'wp-travel-engine' ) ) );
+ }
+
+ $deleted = 0;
+ $real_log_dir = realpath( $log_dir );
+
+ // Delete all log files with path traversal protection
+ foreach ( $log_files as $file ) {
+ // Security: verify file exists first
+ if ( ! file_exists( $file ) ) {
+ continue;
+ }
+
+ // Security: ensure file is within log directory (path traversal protection)
+ $real_file_path = realpath( $file );
+ if ( false === $real_file_path || false === $real_log_dir || strpos( $real_file_path, $real_log_dir ) !== 0 ) {
+ continue;
+ }
+
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
+ if ( unlink( $file ) ) {
+ ++$deleted;
+ }
+ }
+
+ // Clear cache
+ LogUtils::clear_cache();
+
+ wp_send_json_success(
+ array(
+ 'message' => sprintf(
+ // translators: %d number of files deleted
+ __( '%d log files deleted successfully.', 'wp-travel-engine' ),
+ $deleted
+ ),
+ )
+ );
+ }
+}
--- a/wp-travel-engine/includes/classes/Core/Controllers/Ajax/DownloadLog.php
+++ b/wp-travel-engine/includes/classes/Core/Controllers/Ajax/DownloadLog.php
@@ -0,0 +1,192 @@
+<?php
+/**
+ * Download Log AJAX Controller.
+ *
+ * @package WPTravelEngineCoreControllersAjax
+ * @since 6.7.6
+ */
+
+namespace WPTravelEngineCoreControllersAjax;
+
+use WPTravelEngineAbstractsAjaxController;
+use WPTravelEngineLoggerUtilitiesLogUtils;
+
+/**
+ * Handles log download AJAX request.
+ *
+ * Supports both single file download and filtered multi-file download.
+ *
+ * @since 6.7.6
+ */
+class DownloadLog extends AjaxController {
+
+ const NONCE_KEY = 'nonce';
+ const NONCE_ACTION = 'wptravelengine_download_log';
+ const ACTION = 'wptravelengine_download_log';
+ const ALLOW_NOPRIV = false;
+
+ /**
+ * Process request.
+ *
+ * Handles two modes:
+ * - Single file: If 'file' param provided, downloads that specific file
+ * - Filtered: If no 'file' param, downloads filtered logs from all files
+ *
+ * @return void
+ */
+ protected function process_request() {
+ // Check capability
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_die( __( 'You do not have sufficient permissions.', 'wp-travel-engine' ), 403 );
+ }
+
+ // Check if downloading a single file
+ $single_file = $this->request->get_param( 'file' );
+ if ( ! empty( $single_file ) ) {
+ $this->download_single_file( $single_file );
+ return;
+ }
+
+ // Otherwise, download filtered logs
+ $this->download_filtered_logs();
+ }
+
+ /**
+ * Download a single log file.
+ *
+ * @param string $filename Filename to download.
+ * @return void
+ */
+ protected function download_single_file( string $filename ): void {
+ $filename = sanitize_file_name( $filename );
+
+ // Security: Validate file extension - only .log files allowed
+ if ( ! preg_match( '/.log$/', $filename ) ) {
+ wp_die( __( 'Invalid file type. Only .log files are allowed.', 'wp-travel-engine' ), 403 );
+ }
+
+ $log_dir = LogUtils::get_log_directory();
+ $file_path = $log_dir . '/' . $filename;
+
+ // Security: Verify file exists first (realpath returns false for non-existent files)
+ if ( ! file_exists( $file_path ) ) {
+ wp_die( __( 'File not foun