Expand the search results sorting filter

The search results sorting filter contains four sorting criteria options: title (post_title), price (price_from), rating (rating), available date (av_date_from).
However, the plugin also supports sorting by several more parameters: post publication date (post_date), post modified date (post_modified), menu order (menu_order).
Below is an example of adding a new sorting parameter to the filter (requires BA Book Everything 1.8.1 or higher).

add_filter('babe_search_filter_sort_by_args', 'child_theme_babe_search_filter_sort_by_args', 10, 1);
function child_theme_babe_search_filter_sort_by_args( array $filters ): array
{
    return array_merge( [
        'post_date_desc' => __( 'Sort by...', 'your-child-theme-textdomain' ),
        'post_date_asc' => __( 'Sort by...', 'your-child-theme-textdomain' ),
    ], $filters );
}

add_filter('babe_search_filter_to_get_posts_args', 'child_theme_babe_search_filter_to_get_posts_args', 10, 1);
function child_theme_babe_search_filter_to_get_posts_args( array $args ): array
{
    if( $args['search_results_sort_by'] === 'post_date_desc' ){
        $args['sort'] = 'post_date';
        $args['sort_by'] = 'DESC';
    }
    if( $args['search_results_sort_by'] === 'post_date_asc' ){
        $args['sort'] = 'post_date';
        $args['sort_by'] = 'ASC';
    }
    return $args;
}

add_filter('babe_search_result_args', 'child_theme_babe_search_result_args', 10, 1);
function child_theme_babe_search_result_args( array $args ): array
{
    if( empty($_GET['search_results_sort_by']) ){
        $args['search_results_sort_by'] = 'post_date_desc';
    }
    return $args;
}

Add custom fields to the checkout

Add new text checkout field

Here is an example of how to add an additional text field ‘new_field’ into the checkout form:

add_filter('babe_checkout_args', 'customtheme_babe_checkout_args', 10, 2);
/**
 * Add checkout text field
 */
function customtheme_babe_checkout_args( $args_meta, $args ) {
    $args_meta['new_field'] = $args['meta']['new_field'] ?? '';
    return $args_meta;
}
////////
add_filter('babe_checkout_field_label', 'customtheme_babe_checkout_field_label', 10, 2);
/**
 * Add checkout field title
 */
function customtheme_babe_checkout_field_label( $field_title, $field_name ) {
    if ($field_name === 'new_field'){
       $field_title = __('Field Title', 'textdomain');
    }
    return $field_title;
}
////////
add_filter('babe_checkout_field_required', 'customtheme_babe_checkout_field_required', 10, 2);
/**
 * Required tag for checkout field. Use it only if you need to make field required
*/
function customtheme_babe_checkout_field_required($required_tag, $field_name){
   if ($field_name === 'new_field'){
      $required_tag = 'required="required"';
   }
   return $required_tag;
}
///////////////////
add_filter('babe_sanitize_checkout_vars', 'customtheme_sanitize_checkout_vars', 10, 2);
/**
 * Add fields to sanitize checkout vars method
 */
function customtheme_sanitize_checkout_vars( $output, $arr ) {

    $output['new_field'] = isset($arr['new_field']) ? sanitize_text_field($arr['new_field']) : '';
    return $output;
}

Add another field types: radio, checkbox and select

The next snippet can be used as an example for custom radio, checkbox and select fields:

function customtheme_get_hotels(){
    return [
        'hotel1' => __('Hotel name 1', 'textdomain'),
        'hotel2' => __('Hotel name 2', 'textdomain'),
        'hotel3' => __('Hotel name 3', 'textdomain'),
        'hotel4' => __('Hotel name 4', 'textdomain'),
        'hotel5' => __('Hotel name 5', 'textdomain'),
    ];
}

function customtheme_get_radios(){
    return [
        'radio1' => __('Radio value 1', 'textdomain'),
        'radio2' => __('Radio value 2', 'textdomain'),
    ];
}

add_filter('babe_checkout_field_label', 'customtheme_babe_checkout_field_label', 10, 2);
function customtheme_babe_checkout_field_label( $field_title, $field_name ) {

    if ( $field_name === 'checkbox' ){
        $field_title = __( 'Checkbox title', 'textdomain' );
    }
    if ( $field_name === 'radio' ){
        $field_title = __( 'Radio title', 'textdomain' );
    }
    if ( $field_name === 'hotel' ){
        $field_title = __( 'Hotel', 'textdomain' );
    }

    return $field_title;
}

add_filter('babe_checkout_after_contact_fields', 'customtheme_add_fields', 10, 2);
function customtheme_add_fields( $output, $args ){

    $hotels = customtheme_get_hotels();
    $radios = customtheme_get_radios();

    $hotels_options = '<option value>'.__( 'Select...', 'textdomain' ).'</option>';
    $hotel = !empty($args['meta']['hotel']) ? $args['meta']['hotel'] : '';

    foreach( $hotels as $hotel_code => $hotel_name){
        $hotels_options .= '<option value="'.esc_attr($hotel_code).'" '.selected($hotel, $hotel_code,false).'>'.esc_html($hotel_name).'</option>';
    }

    $radio_value = !empty($args['meta']['radio']) ? $args['meta']['radio'] : '';

    $checkbox_value = !empty($args['meta']['checkbox']) ? 1 : 0;

    $output .= '<h3>'. __( 'Checkbox and Hotel', 'textdomain' ) .'</h3>';

    $output .= '<div class="address_fields_group input_group">
        <div class="checkout-form-block checkout_select_block">
          <div class="checkout_select_title">
             <div class="checkout_checkbox_wrapper checkout_form_input_field_content">
              <input type="checkbox" class="checkout_form_input_field_checkbox" name="checkbox" id="checkbox" value="1" '. checked($checkbox_value, 1, false) . ' > 
              <label for="checkbox">'. BABE_html::checkout_field_label('checkbox') .'</label>
             </div>
          </div>
       </div>
       
       <div class="checkout-form-block checkout_select_block">
          <div class="checkout_select_title">
             <div class="checkout_select_wrapper checkout_form_input_field_content">
              <label class="checkout_form_input_label">' . BABE_html::checkout_field_label('hotel') . '</label>
              <select class="checkout_select_field select2" name="hotel" id="hotel">'.$hotels_options.'</select>
             </div>
          </div>
       </div>
       
       <div class="checkout-form-block checkout_select_block">
          <div class="checkout_select_title">
             <div class="checkout_radio_wrapper checkout_form_input_field_content">
              <label class="checkout_form_input_label">' . BABE_html::checkout_field_label('radio') . '</label>';

    foreach( $radios as $radio_code => $radio_name){
        $radio_value = empty($radio_value) ? $radio_code : $radio_value;
        $output .= '<div class="checkout_form_input_field_radio_wrapper">
               <input type="radio" class="checkout_form_input_field_radio" id="'.sanitize_key($radio_code).'" name="radio" value="'.esc_attr($radio_code).'" '. checked($radio_value, $radio_code, false) . ' > 
              <label for="'.sanitize_key($radio_code).'">'. esc_html($radio_name) .'</label>
           </div>';
    }

    $output .= '
             </div>
          </div>
       </div>
       
     </div>';

    return $output;
}

add_filter('babe_sanitize_checkout_vars', 'customtheme_sanitize_checkout_vars', 10, 2);
function customtheme_sanitize_checkout_vars( $output, $arr ) {

    $hotels = customtheme_get_hotels();
    $radios = customtheme_get_radios();

    $output['checkbox'] = !empty($arr['checkbox']) ? 1 : 0;
    $output['radio'] = isset($arr['radio'], $radios[$arr['radio']]) ? $arr['radio'] : '';
    $output['hotel'] = isset($arr['hotel'], $hotels[$arr['hotel']]) ? $arr['hotel'] : '';
    return $output;
}

add_filter('babe_order_customer_details_fields', 'customtheme_customer_details_fields', 10, 2);
function customtheme_customer_details_fields($order_meta, $order_id){

    $order_meta['checkbox'] = !empty($order_meta['checkbox']) ? __( 'yes', 'textdomain' ) : __( 'no', 'textdomain' );

    $hotels = customtheme_get_hotels();
    if ( !empty($order_meta['hotel']) && isset($hotels[ $order_meta['hotel'] ]) ){
        $order_meta['hotel'] = $hotels[ $order_meta['hotel'] ];
    }

    $radios = customtheme_get_radios();
    if ( !empty($order_meta['radio']) && isset($radios[ $order_meta['radio'] ]) ){
        $order_meta['radio'] = $radios[ $order_meta['radio'] ];
    }

    return $order_meta;
}

add_action( 'cmb2_admin_init', 'customtheme_order_customer_extra_fields', 20, 1);
function customtheme_order_customer_extra_fields() {

    $hotels = customtheme_get_hotels();
    $radios = customtheme_get_radios();

    $cmb = new_cmb2_box( array(
        'id'            => 'order_customer_extra_fields',
        'title'         => __( 'Extra fields', 'textdomain' ),
        'object_types'  => array( BABE_Post_types::$order_post_type, ), // Post type
        'context'       => 'normal',
        'priority'      => 'high',
        'show_names'    => true,
    ) );

    $cmb->add_field( array(
        'name' => BABE_html::checkout_field_label('checkbox'),
        'id'   => 'checkbox',
        'type'    => 'checkbox',
    ) );

    $cmb->add_field( array(
        'name' => BABE_html::checkout_field_label('radio'),
        'id'   => 'radio',
        'type'    => 'radio',
        'options' => $radios,
    ) );

    $cmb->add_field( array(
        'name' => BABE_html::checkout_field_label('hotel'),
        'id'   => 'hotel',
        'type'    => 'select',
        'show_option_none' => true,
        'options' => $hotels,
    ) );
}

How to translate BA Book Everything with WPML plugin

The BA Book Everything plugin is compatible with WPML plugin, one of the most popular WordPress multilingual tools. WPML has excellent documentation that allows you to quickly translate your site into many languages. In this article, we will describe the additional steps needed to translate our plugin.

To start adding languages you are going to translate the plugin to please go to WPML > Languages > Add Language admin menu.

After adding language you may need to apply some settings according to your needs:

  • Set a default language
  • Language URL format
  • Language switcher options (the order of languages and how to handle languages without translation)
  • Managing widget areas where you can add the language switcher, etc.

Translation of the BA Settings admin menu

First create translations for system pages that are selected in BA Settings > General in the fields: Select search result page, Services page, Checkout page, Confirmation page, Terms & Conditions page, Admin page for order confirmation, My Account page.

You can then configure the settings in the BA Settings admin menu and translate the email messages.

Translation of the custom taxonomy terms, Services, Fees, Places

Before translating the booking object posts, you need to translate all custom taxonomy terms and other posts like Services, Fees, Places.

Custom taxonomies are created from the BA Book Everything > Taxonomies admin menu by adding a new term. The term name defines a new taxonomy name.

After adding a new language the plugin copies custom taxonomies from current language to the new language. So you have just to edit all terms in new language.

For posts like Services, Fees etc. you can add translations by editing them.

Translation of the booking categories

After translating the custom taxonomies you can translate the booking categories. You can do it via WPML > Taxonomy translation admin menu. From the drop-down menu choose the needed type of taxonomy (booking categories) and then add the translation of the already existing content.

Booking category defines booking rule, available taxonomies and other options which will be the same for all booking objects related to this booking category. Custom taxonomies are an option to select in the booking category settings.

After creating a translation you have to edit the translated category in order to add custom taxonomies properly.

Translation of the booking object posts

Once all the taxonomy and service posts are translated, you are free to translate your booking object posts by adding a translation on the posts list admin page or by editing booking object posts.

Translation of the Search form builder settings

The search form builder settings should be specified for each language in the BA Book Everything > Search Form admin menu.
That’s it.

Notes

Not all elements of the plugin can be translated. The booking rules from the BA Book Everything > Booking rules admin menu are not translatable. They are used as core settings for booking categories. Coupons and Orders post types with their corresponding admin menus are also untranslatable.

Add custom fields to the booking object

BA Book Everything uses CMB2 plugin to create metaboxes with custom fields. So you can follow CMB2 documentation to easily create own metaboxes and custom fields.
You can also use one of the plugin hooks to add new custom field to an existing metabox, as shown below. Then you can use function get_post_meta($post_id, 'new_field_slug', 1) to show new field in your booking object post template.

add_action('cmb2_booking_obj_after_av_dates', 'wptheme_cmb2_booking_obj_after_av_dates', 10);
/**
 * Add custom field to booking object post metabox
 *
 * @param object CMB2 object
 */
function wptheme_cmb2_booking_obj_after_av_dates( $cmb ) {

    $cmb->add_field( array(
    'name'       => __( 'New field title' ),
    'desc'       => __( 'field description (optional)' ),
    'id'         => 'new_field_slug',
    'type'       => 'text',
    ) );
    
   return;
}

add_filter( 'babe_post_content_after_calendar', 'wptheme_babe_post_content_after_calendar', 10, 4);
/**
 * Show new field in booking object post template after calendar section
 *
 * @param string - additional content, empty by default
 * @param string - post content
 * @param int - post ID
 * @param array - BABE post array
 * @return string
 */
function wptheme_babe_post_content_after_calendar( $additional_content, $post_content, $post_id, $post ) {

   $additional_content = '<div><label>'.__( 'New field title:' ).'</label> '. wp_kses_post( get_post_meta($post_id, 'new_field_slug', 1) ).'</div>';
    
   return $additional_content;
}

Search Form Builder

Since BA Book Everything 1.3.6 you can use the search form builder to add and customize search form fields easily just from your WordPress dashboard via “BA Book Everything > Search Form” menu.

You can use default form view without custom tabs and just edit/reorder fields in “Managing search form fields” section. Or you can create tabs depending on your categories and show different field sets in search form like in example below:

Examples by rental types

In examples below we describe main things that are different for each type of rental.

Please, find details about new rate system in this documentation.

Hotel / Apartments / Property

In the hotel business, apartments or property rental we sell the whole room (apartment/property) and we have standard price for object which is changing not linearly depending on number of guests. Suppose, we have deluxe rooms for up to 2 guest each.

Let’s create booking rule with “1 night” booking period and “Object” booking mode, then create “Hotel” category with this rule and create booking object “Deluxe room” with “Hotel” category.

On the “Deluxe room” editing screen set option “Maximum number of Guests” to 2, and “Number of items” – to the total number of the same rooms.

In prices section click new rate, setup general price (say, $100), then in “Conditional prices” add “IF >= 2 guest Price $120”. Save the rate and add new, if needed.

To create seasonal rates just specify “Date from” and “Date to” values. For weekend rate – leave checked only Fri and Sat (or whatever else you need) in the “Rate applies to days” option.

Then publish “Deluxe room” post. That’s it.

Hostel / Dorm

To sell beds in hostel/dorm we need create booking rule with “1 night” booking period and “Places” booking mode, then create “Hostel” category with this rule and create booking object “10 beds room” with “Hostel” category.

On the “10 beds room” editing screen set option “Maximum number of Guests” to 10.

In prices section click new rate, setup general price for a night per person (say, $20), then in “Conditional prices” add “IF >= 3 nights Price $17” if you have a discount on long stays. To limit stays set “Minimum booking”/ “Maximum booking” values to required number of nights. Save the rate and add new, if needed.

To create seasonal rates just specify “Date from” and “Date to” values. For weekend rate – leave checked only Fri and Sat (or whatever else you need) in the “Rate applies to days” option.

Then publish “10 beds room” post. That’s it.

Car rental

Suppose, we rent “class A” car, we have general price per day ($40), we need to charge for second driver (+ $10) and to apply a less price if rental duration more than 3 days ($35) or more than 7 days ($32).

Let’s create booking rule with “1 day” booking period and “Object” booking mode, then create “Car” category with this rule and create booking object “Car class A” with “Car” category.

On the “Car class A” editing screen set option “Maximum number of Guests” to 2 (for two drivers), and “Number of items” – to the total number of the same cars.

In prices section click new rate, setup general price ($40), then in “Conditional prices” add “IF >= 3 days Price $35”, “IF >= 7 days Price $32”, “IF >= 2 guests Price $50”, “IF >= 2 guests AND >= 3 days Price $45”, “IF >= 2 guests AND >= 7 days Price $42”. Save the rate and add new, if needed.

To create seasonal rates just specify “Date from” and “Date to” values.

Then publish “Car class A” post. That’s it.

Tours

Usually we need to set different prices for tours depending on guest age, limit guest age for some tours, and may limit maximum number of tickets per booking.

We should create booking rule with “recurrent custom” booking period, “yes” in “Use Age categories for prices?” option and “tickets” in booking mode option. Then create ages in Ages taxonomy, create Category with this booking rule and create booking object “Tour 1” with “tours” category.

On the “Tour 1” editing screen set option “Maximum number of Guests” to total number of tickets. In the “Schedule section” we need setup tour start times for every week day (press “Save schedule” button to save them).

In prices section click new rate, setup general price for each age and leave empty fields for ages that should be excluded from this tour. If you set 0 value as price for some age, this age will be available to book tickets and these tickets will be free. To limit number of tickets per booking set “Minimum booking”/ “Maximum booking” values to required number of tickets.

Save the rate, then publish “Tour 1” post. That’s it.

Cruises

Say, we have 4-day cruise in which we sell cabins for up to 3 guests each, and we need to set variable prices depending on the number of guests (like for hotel rooms).

We should create booking rule with “recurrent custom” booking period like for tours, but with “Object” booking mode, then create Category with this booking rule and create booking object “Cabin 1” with “cruise” category.

On the “Cabin 1” editing screen set “Cyclic availability: start after every N days” value to 4 and “Cyclic availability: available N days after start” value to 1. “Available from Date” should be the date of the first available cruise. And in the “Schedule section” we need setup cruise start time for every week day (press “Save schedule” button to save them).

“Maximum number of Guests” will set to 3, and “Number of items” will set to the total number of the same cabins.

In prices section click new rate, setup general price (say, $100), then in “Conditional prices” add “IF >= 2 guest Price $120”, “IF >= 3 guests Price $140”. Save the rate, then publish “Cabin 1” post. That’s it.

Put search form into the header

If your theme doesn’t contain header widget area, you can add search form into the site header programmatically. Put the following code into your child theme header.php file in the place where you want to display the form on the website:

<?php echo BABE_html::get_search_form('Form title'); ?>

Search form extra fields

Since BA Book Everything 1.3.6 you can use the search form builder to add and customize search form fields easily just from your WordPress dashboard.

This tutorial shows how to add custom taxonomy selection field and guests selection field into the search form in BA Book Everything version up to 1.3.5. Next PHP code example could be placed in your child theme functions.php:

 add_filter( 'babe_search_form_after_date_fields', 'wpbabe_search_form_add_guests', 10 );
/**
 * Adds guests select field into the search form.
 * 
 * @param string $content
 * 
 * @return string
 */
function wpbabe_search_form_add_guests( $content ) {
			
    $ages_arr = BABE_Post_types::get_ages_arr();
            
    if (empty($ages_arr)){
        $ages_arr[0] = array(
           'name' => 'Guests',
           'description' => '',
        ;
    }
            
    $max_guests = BABE_Settings::$settings['max_guests_select'];
            
    foreach ($ages_arr as $age_id => $age_term){
                
        $selected_value = isset( $_GET['guests'][$age_id] ) ? absint( $_GET['guests'][$age_id] ) : 0;
                
        $field_title = $age_term['name'] ? '<div class="input_select_title_value">'. $age_term['name'].'</div>' : '';
                
        $content .= '
	<div class="search_guests_block input_select_field">
	  <div class="input_select_title">
            ' . $field_title . '
            <div class="input_select_wrapper">
               <input type="text" id="age_'.$age_id.'" class="input_select_input input_select_input_value select_guests" name="guests['.$age_id.']" value="'.$selected_value.'">
               <ul class="input_select_list">
                  '.BABE_html::get_range_input_select_options(0, $max_guests, 1, $selected_value).'
               </ul>
               <i class="fas fa-chevron-down"></i>
             </div>
           </div>
	</div>';
           
    return $content;
}

 add_filter( 'babe_search_form_after_date_fields', 'wpbabe_search_form_add_taxonomy', 20 );
/**
 * Adds taxonomy select field to the search form.
 * 
 * @param string $content
 * 
 * @return string
 */
 function wpbabe_search_form_add_taxonomy( $content ) {
			
	global $post;
	$add_input_field = 'ba-destination'; /// change it to your taxonomy
			
	$selected = isset( $_GET['add_ids_'.$add_input_field]) ? intval( $_GET['add_ids_'.$add_input_field] ) : 0;
				
	$taxonomy = get_taxonomy( $add_input_field );
				
	if ( ! empty( $taxonomy ) ) {
				    
            $selected_term_name = $taxonomy->labels->all_items;
            $selected_value = '';
                  
            if ($selected){    
               $selected_term = get_term_by('id', $selected, $add_input_field);
               if (!empty($selected_term) && !is_wp_error($selected_term)){
                   $selected_term_name = $selected_term->name;
                   $selected_value = $selected;
               }    
             }
                  
             $args = array(
                     'taxonomy' => $add_input_field,
                     'parent_term_id' => 0,
                     'view' => 'list',
                     'add_count' => '',
                     'option_all' => 1,
                     'option_all_value' => 0,
                     'option_all_title' => $taxonomy->labels->all_items,
                     'id' => 'add_ids_list_'.$add_input_field,
                     'class' => 'add_ids_list',
                     'class_item' => 'term_item',
                     'class_item_selected' => 'term_item_selected',
                     'name' => '',
                     'prefix_char' => ' ',
                     'term_id_name' => 'term_id',
              );
	      
              $content .= '<div class="add_input_field" data-tax="'.$add_input_field.'">
		<div class="add_ids_title"><div class="add_ids_title_value">' . $selected_term_name . '</div><i class="fas fa-chevron-down"></i></div>
                            ' . BABE_Post_types::get_terms_children_hierarchy( $args, array($selected) ) . '
                            <input type="hidden" name="add_ids_'.$add_input_field.'" value="'.$selected.'">	
						</div>
			     ';
       }
			
       return $content;
 }

add_filter( 'babe_search_result_args', 'wpbabe_search_result_args', 10 );
/**
 * Get extra arg from the search form
 * 
 * @param array $args
 * 
 * @return array
 */
function wpbabe_search_result_args( $args ) {
		  
    $add_input_field = 'ba-destination'; /// change it to your taxonomy
            
    $selected = isset( $_GET['add_ids_'.$add_input_field] ) && absint($_GET['add_ids_'.$add_input_field]) ? absint($_GET['add_ids_'.$add_input_field]) : '';
			
    $selected_arr = $selected ? (array)$selected : array();
    $selected_arr = array_map( 'absint', $selected_arr );
    $args['terms'] = $args['terms'] + $selected_arr;
            
    if (isset( $_GET['guests'] )){
        $guests = array_map('absint', $_GET['guests']);
        $args['guests'] = array_sum($guests);
     }
			
     return $args;
 }

To make new taxonomy field expandable, use next snippet in your child theme javascript file:

$('.add_input_field .add_ids_title').on('click', function(event){
        event.preventDefault();
        search_add_input_toggle(this);
    });
    
    $('.add_input_field .add_ids_list .term_item').on('click', function(event){
        event.preventDefault();
        var parent = $(this).parent();
        search_add_input_toggle(parent);
        var selected = $(this).data('id');
        $(parent).parent().find('input[name="add_ids_'+$(parent).parent().data('tax')+'"]').val(selected);
        $(parent).find('.term_item').removeClass('term_item_selected');
        $(this).addClass('term_item_selected');
        $(parent).parent().find('.add_ids_title_value').html($(this).html());
    });
    
    function search_add_input_toggle(item){
        $(item).parent().find('.add_ids_list').toggleClass('active');
        $(item).parent().find('.add_ids_title i').toggleClass('fa-chevron-down');
        $(item).parent().find('.add_ids_title i').toggleClass('fa-chevron-up');
    }
    
    function search_add_input_close(item){
        $(item).find('.add_ids_list').removeClass('active');
        $(item).find('.add_ids_title i').addClass('fa-chevron-down');
        $(item).find('.add_ids_title i').removeClass('fa-chevron-up');
    }
    
    $(document).on('click', function(event){
         var par = $(event.target).closest('.add_input_field');
         if (par.length){
            $(par).siblings().each(function(el){
                search_add_input_close(this);
            });
         } else {
            $(document).find('.add_input_field .add_ids_list.active').parents().eq(2).each(function(el){
                search_add_input_close(this);
            });
         }
    });

Demo content setup

Magic one-click “Demo Setup” feature helps you get started with the plugin in minutes! Just go to “BA Settings > Demo content” admin menu and click “Setup Demo Content” button. Please do not close this page during the installation of demo content.

Customization & code examples

Tutorials and examples:

BA Book Everything plugin is in active development stage and the public API documentation is not ready yet. But if you are ready to dive into the code – start learning hooks and functions from BABE_html class and use them as a template for your own implementation. The plugin is built on static classes, which makes it incredibly flexible.

You can also request customization via contact form.

Cookies

This site uses cookies to enhance your user experience and analyse traffic to improve this website. By clicking any link on this page you are giving your consent for us to set cookies. Please, see our Privacy Policy for additional information.
Scroll to top