XylotrechusZ
<?php
/**
* The Forminator_Date class.
*
* @package Forminator
*/
if ( ! defined( 'ABSPATH' ) ) {
die();
}
/**
* Class Forminator_Date
*
* @since 1.0
*/
class Forminator_Date extends Forminator_Field {
/**
* Name
*
* @var string
*/
public $name = '';
/**
* Slug
*
* @var string
*/
public $slug = 'date';
/**
* Position
*
* @var int
*/
public $position = 12;
/**
* Type
*
* @var string
*/
public $type = 'date';
/**
* Options
*
* @var string
*/
public $options = array();
/**
* Icon
*
* @var string
*/
public $icon = 'sui-icon-calendar';
/**
* Forminator_Date constructor.
*
* @since 1.0
*/
public function __construct() {
parent::__construct();
$this->name = esc_html__( 'Datepicker', 'forminator' );
}
/**
* Field defaults
*
* @since 1.0
* @return array
*/
public function defaults() {
return array(
'field_type' => 'picker',
'date_format' => 'mm/dd/yy',
'default_date' => 'none',
'field_label' => esc_html__( 'Date', 'forminator' ),
'placeholder' => esc_html__( 'Choose Date', 'forminator' ),
'icon' => 'true',
'day_label' => esc_html__( 'Day', 'forminator' ),
'day_placeholder' => esc_html__( 'E.g. 01', 'forminator' ),
'month_label' => esc_html__( 'Month', 'forminator' ),
'month_placeholder' => esc_html__( 'E.g. 01', 'forminator' ),
'year_label' => esc_html__( 'Year', 'forminator' ),
'year_placeholder' => esc_html__( 'E.g. 2000', 'forminator' ),
'restrict_message' => esc_html__( 'Please select one of the available dates.', 'forminator' ),
);
}
/**
* Autofill Setting
*
* @since 1.0.5
*
* @param array $settings Settings.
*
* @return array
*/
public function autofill_settings( $settings = array() ) {
$providers = apply_filters( 'forminator_field_' . $this->slug . '_autofill', array(), $this->slug );
// TODO: support for multiple field date.
$autofill_settings = array(
'date' => array(
'values' => forminator_build_autofill_providers( $providers ),
),
);
return $autofill_settings;
}
/**
* Field front-end markup
*
* @since 1.0
*
* @param array $field Field.
* @param Forminator_Render_Form $views_obj Forminator_Render_Form object.
* @param array $draft_value Draft value.
*
* @return mixed
*/
public function markup( $field, $views_obj, $draft_value = null ) {
$settings = $views_obj->model->settings;
$this->field = $field;
$html = '';
$design = $this->get_form_style( $settings );
$name = self::get_property( 'element_id', $field );
$required = self::get_property( 'required', $field, false );
$placeholder = $this->sanitize_value( self::get_property( 'placeholder', $field ) );
$label = $this->sanitize_value( self::get_property( 'field_label', $field ) );
$description = self::get_property( 'description', $field );
$type = trim( self::get_property( 'field_type', $field ) );
$has_icon = self::get_property( 'icon', $field, false, 'bool' );
$has_icon = filter_var( $has_icon, FILTER_VALIDATE_BOOLEAN );
$date_format = esc_html( self::get_property( 'date_format', $field, 'm/d/Y' ) );
$start_date_type = self::get_property( 'start-date', $field, '' );
$end_date_type = self::get_property( 'end-date', $field, '' );
$start_of_week = self::get_property( 'start_of_week', $field, get_option( 'start_of_week' ) );
$disabled_dates = self::get_property( 'disabled-dates', $field, array() );
$disabled_range = self::get_property( 'disable-date-range', $field, array() );
$id = self::get_field_id( $name );
$describedby = esc_attr( $id . '-description' );
$is_basic = 'basic' === $design;
$descr_position = self::get_description_position( $field, $settings );
if ( false !== strpos( $date_format, '-' ) ) {
$sep = '-';
} elseif ( false !== strpos( $date_format, '.' ) ) {
$sep = '.';
} else {
$sep = '/';
}
$formats = explode( $sep, $date_format );
$min_year = esc_html( self::get_property( 'min_year', $field, gmdate( 'Y' ) - 100 ) );
$max_year = esc_html( self::get_property( 'max_year', $field, gmdate( 'Y' ) + 100 ) );
$prefill = false;
$is_prefil_valid = false;
// Check if Pre-fill parameter used.
if ( $this->has_prefill( $field ) ) {
// We have pre-fill parameter, use its value or $value.
$prefill = $this->get_prefill( $field, $prefill );
}
$parsed_date = self::parse_date( $prefill, $date_format );
if ( $parsed_date && $this->check_date( $parsed_date['month'], $parsed_date['day'], $parsed_date['year'] ) ) {
$is_prefil_valid = true;
}
if ( 'picker' === $type ) {
$html .= '<div class="forminator-field">';
}
if ( 'picker' === $type ) {
$restrict = array();
$restrict_type = self::get_property( 'howto-restrict', $field );
if ( 'week' === $restrict_type ) {
$days = forminator_week_days();
$i = 0;
foreach ( $days as $k => $day ) {
if ( ! self::get_property( $k, $field ) ) {
$restrict[] = $i;
}
++$i;
}
} elseif ( 'custom' === $restrict_type ) {
$dates = self::get_property( 'date_multiple', $field );
$default_value = '';
$default_date = esc_html( self::get_property( 'default_date', $field, false ) );
if ( ! empty( $dates ) ) {
foreach ( $dates as $k => $date ) {
$restrict[] = $date['value'];
}
}
if ( 'custom' === $default_date ) {
$default_date_value = esc_html( self::get_property( 'date', $field, '' ) );
$default_value = $default_date_value;
}
}
$default_value = '';
$default_date = self::get_property( 'default_date', $field, false );
if ( 'today' === $default_date ) {
$datepicker_format = $this->normalize_date_format( $date_format );
$default_value = current_time( $datepicker_format );
}
if ( 'custom' === $default_date ) {
$default_date_value = self::get_property( 'date', $field, '' );
$default_value = $default_date_value;
}
if ( $is_prefil_valid ) {
$default_value = $prefill;
}
$past_dates = self::get_property( 'past_dates', $field );
$past_dates = '' !== $default_value && 'disable' === $past_dates ? 'disable' : 'enable';
$start_date = '';
$end_date = '';
$start_date_field = '';
$end_date_field = '';
$start_offset = '';
$end_offset = '';
if ( ! empty( $start_date_type ) ) {
if ( 'specific' === $start_date_type ) {
$start_date = self::get_property( 'start-specific-date', $field, '' );
} else {
$start_offset_operator = self::get_property( 'start-offset-operator', $field, '+' );
$start_offset_value = self::get_property( 'start-offset-value', $field, '0' );
$start_offset_duration = self::get_property( 'start-offset-duration', $field, 'days' );
if ( 'today' === $start_date_type ) {
$start_date = date_i18n( 'Y-m-d', strtotime( $start_offset_operator . $start_offset_value . ' ' . $start_offset_duration, current_time( 'U' ) ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- We are using the current timestamp based on the site's timezone.
} else {
$start_date_field = $start_date_type;
$start_offset = $start_offset_operator . '_' . $start_offset_value . '_' . $start_offset_duration;
}
}
}
if ( ! empty( $end_date_type ) ) {
if ( 'specific' === $end_date_type ) {
$end_date = self::get_property( 'end-specific-date', $field, '' );
} else {
$end_offset_operator = self::get_property( 'end-offset-operator', $field, '+' );
$end_offset_value = self::get_property( 'end-offset-value', $field, '0' );
$end_offset_duration = self::get_property( 'end-offset-duration', $field, 'days' );
if ( 'today' === $end_date_type ) {
$end_date = date_i18n( 'Y-m-d', strtotime( $end_offset_operator . $end_offset_value . ' ' . $end_offset_duration, current_time( 'U' ) ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- We are using the current timestamp based on the site's timezone.
} else {
$end_date_field = $end_date_type;
$end_offset = $end_offset_operator . '_' . $end_offset_value . '_' . $end_offset_duration;
}
}
}
$icon_markup = array(
'<div class="forminator-input-with-icon">',
'</div>',
'calendar',
);
if ( ! empty( $start_date ) ) {
$min_year = gmdate( 'Y', strtotime( $start_date ) );
}
if ( ! empty( $end_date ) ) {
$max_year = gmdate( 'Y', strtotime( $end_date ) );
}
if ( isset( $draft_value['value'] ) ) {
$default_value = $draft_value['value'];
}
$html .= self::create_input(
array(
'autocomplete' => 'off',
'type' => 'text',
'size' => 1,
'name' => $name,
'value' => $default_value,
'placeholder' => $placeholder,
'id' => self::get_field_id( $name . '-picker' ),
'class' => 'forminator-input forminator-datepicker',
'data-required' => $required,
'data-format' => $date_format,
'data-restrict-type' => $restrict_type,
'data-restrict' => implode( ',', $restrict ),
'data-start-year' => $min_year,
'data-end-year' => $max_year,
'data-past-dates' => $past_dates,
'data-start-of-week' => $start_of_week,
'data-start-date' => $start_date,
'data-end-date' => $end_date,
'data-start-field' => $start_date_field,
'data-end-field' => $end_date_field,
'data-start-offset' => $start_offset,
'data-end-offset' => $end_offset,
'data-disable-date' => implode( ',', $disabled_dates ),
'data-disable-range' => implode( ',', $disabled_range ),
),
$label,
$description,
$required,
$descr_position,
$has_icon ? $icon_markup : ''
);
} elseif ( 'select' === $type ) {
if ( ! empty( $label ) ) {
$html .= self::get_field_label( $label, 'forminator-field-' . $name, $required );
// Mark day, month and year required markup as false.
$required = false;
}
if ( 'above' === $descr_position ) {
$html .= self::get_description( $description, $id, $descr_position );
}
$default_date = esc_html( self::get_property( 'default_date', $field, false ) );
$default_date_value = esc_html( self::get_property( 'date', $field, '' ) );
if ( $is_prefil_valid ) {
$default_value = $prefill;
}
if ( isset( $draft_value['value'] ) ) {
$parsed_date = $draft_value['value'];
$day = $parsed_date['day'];
$month = $parsed_date['month'];
$year = $parsed_date['year'];
} elseif ( $is_prefil_valid ) {
$day = $parsed_date['day'];
$month = $parsed_date['month'];
$year = $parsed_date['year'];
} elseif ( 'today' === $default_date ) {
list( $day, $month, $year ) = explode( ' ', current_time( 'j n Y' ) );
} elseif ( 'custom' === $default_date && ! empty( $default_date_value ) ) {
if ( empty( strtotime( $default_date_value ) ) ) {
if ( false !== strpos( $date_format, '-' ) || false !== strpos( $date_format, '.' ) ) {
$default_date_value = str_replace( array( '.', '-' ), '/', $default_date_value );
} elseif ( false !== strpos( $date_format, '/' ) ) {
$default_date_value = str_replace( '/', '-', $default_date_value );
}
}
$day = gmdate( 'j', strtotime( $default_date_value ) );
$month = gmdate( 'n', strtotime( $default_date_value ) );
$year = gmdate( 'Y', strtotime( $default_date_value ) );
} else {
$day = '';
$month = '';
$max_selected_year = esc_html( self::get_property( 'max_year', $field ) );
if ( $max_selected_year ) {
$year = '';
} else {
$year = gmdate( 'Y' );
}
}
// START: Row.
$html .= '<div class="forminator-date-select">';
$html .= '<div class="forminator-row" data-multiple="true">';
foreach ( $formats as $format ) {
switch ( $format ) {
case 'dd':
$day_id = self::get_subfield_id( $name, '-day' );
$html .= '<div id="' . $day_id . '" class="forminator-col">';
$html .= '<div class="forminator-field">';
$day_data = array(
'name' => $day_id,
'id' => self::get_field_id( $settings['form_id'] . '__field--' . $day_id ),
'class' => $is_basic ? '' : 'forminator-select2',
'data-format' => $date_format,
'data-parent' => $name,
);
if ( ! empty( $description ) ) {
$day_data['aria-describedby'] = $describedby;
}
if ( $required ) {
$label = self::get_property( 'day_label', $field );
if ( ! empty( $label ) ) {
$html .= sprintf(
'<label for="%s" class="forminator-label">%s %s</label>',
$day_data['id'],
$this->sanitize_value( $label ),
'<span class="forminator-required">*</span>'
);
}
$html .= self::create_select(
$day_data,
false,
$this->get_day(),
$day
);
} else {
$html .= self::create_select(
$day_data,
$this->sanitize_value( self::get_property( 'day_label', $field ) ),
$this->get_day(),
$day
);
}
$html .= '</div>';
$html .= '</div>';
break;
case 'mm':
$month_id = self::get_subfield_id( $name, '-month' );
$html .= '<div id="' . $month_id . '" class="forminator-col">';
$html .= '<div class="forminator-field">';
$month_data = array(
'name' => $month_id,
'id' => self::get_field_id( $settings['form_id'] . '__field--' . $month_id ),
'class' => $is_basic ? '' : 'forminator-select2',
'data-format' => $date_format,
'data-parent' => $name,
);
if ( ! empty( $description ) ) {
$month_data['aria-describedby'] = $describedby;
}
if ( $required ) {
$label = self::get_property( 'month_label', $field );
if ( ! empty( $label ) ) {
$html .= sprintf(
'<label for="%s" class="forminator-label">%s %s</label>',
$month_data['id'],
$this->sanitize_value( $label ),
'<span class="forminator-required">*</span>'
);
}
$html .= self::create_select(
$month_data,
false,
$this->get_months(),
$month
);
} else {
$html .= self::create_select(
$month_data,
$this->sanitize_value( self::get_property( 'month_label', $field ) ),
$this->get_months(),
$month
);
}
$html .= '</div>';
$html .= '</div>';
break;
case 'yy':
$year_id = self::get_subfield_id( $name, '-year' );
$html .= '<div id="' . $year_id . '" class="forminator-col">';
$html .= '<div class="forminator-field">';
$year_data = array(
'name' => $year_id,
'id' => self::get_field_id( $settings['form_id'] . '__field--' . $year_id ),
'class' => $is_basic ? '' : 'forminator-select2',
'data-format' => $date_format,
'data-parent' => $name,
);
if ( ! empty( $description ) ) {
$year_data['aria-describedby'] = $describedby;
}
if ( $required ) {
$label = self::get_property( 'year_label', $field );
if ( ! empty( $label ) ) {
$html .= sprintf(
'<label for="%s" class="forminator-label">%s %s</label>',
$year_data['id'],
$this->sanitize_value( $label ),
'<span class="forminator-required">*</span>'
);
}
$html .= self::create_select(
$year_data,
false,
$this->get_years( $min_year, $max_year ),
$year
);
} else {
$html .= self::create_select(
$year_data,
$this->sanitize_value( self::get_property( 'year_label', $field ) ),
$this->get_years( $min_year, $max_year ),
$year
);
}
$html .= '</div>';
$html .= '</div>';
break;
default:
break;
}
}
$html .= '</div>';
// END: Row.
$html .= '</div>';
if ( 'above' !== $descr_position ) {
$html .= self::get_description( $description, $id, $descr_position );
}
} elseif ( 'input' === $type ) {
$day_value = '';
$month_value = '';
$year_value = '';
if ( isset( $draft_value['value'] ) ) {
$parsed_date = $draft_value['value'];
$day_value = $parsed_date['day'];
$month_value = $parsed_date['month'];
$year_value = $parsed_date['year'];
} elseif ( $is_prefil_valid ) {
$day_value = $parsed_date['day'];
$month_value = $parsed_date['month'];
$year_value = $parsed_date['year'];
}
$html .= self::get_field_label( $label, 'forminator-field-' . $name, $required );
if ( 'above' === $descr_position ) {
$html .= self::get_description( $description, $id, $descr_position );
}
// START: Row.
$html .= '<div class="forminator-date-input">';
$html .= '<div class="forminator-row" data-multiple="true">';
foreach ( $formats as $format ) {
switch ( $format ) {
case 'dd':
$day = self::get_subfield_id( $name, '-day' );
$html .= '<div id="' . $day . '" class="forminator-col">';
$html .= '<div class="forminator-field">';
$day_data = array(
'type' => 'number',
'min' => 1,
'max' => 31,
'name' => $day,
'value' => esc_attr( $day_value ),
'placeholder' => $this->sanitize_value( self::get_property( 'day_placeholder', $field ) ),
'id' => self::get_field_id( $day ),
'class' => 'forminator-input',
'data-field' => 'day',
'data-format' => $date_format,
'data-parent' => $name,
);
if ( ! empty( $description ) ) {
$day_data['aria-describedby'] = $describedby;
}
if ( $required ) {
$label = self::get_property( 'day_label', $field );
if ( ! empty( $label ) ) {
$html .= sprintf(
'<label for="%s" class="forminator-label">%s %s</label>',
$day_data['id'],
$this->sanitize_value( $label ),
'<span class="forminator-required">*</span>'
);
}
$html .= self::create_input(
$day_data,
false,
'',
$required,
);
} else {
$html .= self::create_input(
$day_data,
$this->sanitize_value( self::get_property( 'day_label', $field ) ),
'',
$required,
);
}
$html .= '</div>';
$html .= '</div>';
break;
case 'mm':
$month = self::get_subfield_id( $name, '-month' );
$html .= '<div id="' . $month . '" class="forminator-col">';
$html .= '<div class="forminator-field">';
$month_data = array(
'type' => 'number',
'min' => 1,
'max' => 12,
'name' => $month,
'value' => esc_attr( $month_value ),
'placeholder' => $this->sanitize_value( self::get_property( 'month_placeholder', $field ) ),
'id' => self::get_field_id( $month ),
'class' => 'forminator-input',
'data-field' => 'month',
'data-format' => $date_format,
'data-parent' => $name,
);
if ( ! empty( $description ) ) {
$month_data['aria-describedby'] = $describedby;
}
if ( $required ) {
$label = self::get_property( 'month_label', $field );
if ( ! empty( $label ) ) {
$html .= sprintf(
'<label for="%s" class="forminator-label">%s %s</label>',
$month_data['id'],
$this->sanitize_value( $label ),
'<span class="forminator-required">*</span>'
);
}
$html .= self::create_input(
$month_data,
false,
'',
$required,
);
} else {
$html .= self::create_input(
$month_data,
$this->sanitize_value( self::get_property( 'month_label', $field ) ),
'',
$required,
);
}
$html .= '</div>';
$html .= '</div>';
break;
case 'yy':
$year = self::get_subfield_id( $name, '-year' );
$html .= '<div id="' . $year . '" class="forminator-col">';
$html .= '<div class="forminator-field">';
$year_data = array(
'type' => 'number',
'min' => 1,
'name' => $year,
'placeholder' => $this->sanitize_value( self::get_property( 'year_placeholder', $field ) ),
'id' => self::get_field_id( $year ),
'class' => 'forminator-input',
'data-field' => 'year',
'value' => esc_attr( $year_value ),
'data-format' => $date_format,
'data-parent' => $name,
);
if ( ! empty( $description ) ) {
$year_data['aria-describedby'] = $describedby;
}
if ( $required ) {
$label = self::get_property( 'year_label', $field );
if ( ! empty( $label ) ) {
$html .= sprintf(
'<label for="%s" class="forminator-label">%s %s</label>',
$year_data['id'],
$this->sanitize_value( $label ),
'<span class="forminator-required">*</span>'
);
}
$html .= self::create_input(
$year_data,
false,
'',
$required,
);
} else {
$html .= self::create_input(
$year_data,
$this->sanitize_value( self::get_property( 'year_label', $field ) ),
'',
$required,
);
}
$html .= '</div>';
$html .= '</div>';
break;
default:
break;
}
}
$html .= '</div>';
// END: Row.
$html .= '</div>';
if ( 'above' !== $descr_position ) {
$html .= self::get_description( $description, $id, $descr_position );
}
}
if ( 'picker' === $type ) {
$html .= '</div>';
}
return apply_filters( 'forminator_field_date_markup', $html, $field, $this );
}
/**
* Return modified date format
*
* @since 1.7.0.1
*
* @param string $date_format Date format.
*
* @return string
*/
public function normalize_date_format( $date_format ) {
$date_format = str_replace( 'dd', 'd', $date_format );
$date_format = str_replace( 'mm', 'm', $date_format );
$date_format = str_replace( 'yy', 'Y', $date_format );
return $date_format;
}
/**
* Return all years between two dates
*
* @since 1.0
*
* @param string $min_year Minimum year.
* @param string $max_year Maximum year.
*
* @return array
*/
public function get_years( $min_year = '', $max_year = '' ) {
$array = array();
$year = intval( gmdate( 'Y' ) );
$end = empty( $min_year ) ? $year - 100 : intval( $min_year ) - 1;
$start = empty( $max_year ) ? $year + 100 : intval( $max_year );
for ( $i = $start; $i > $end; $i-- ) {
$array[] = array(
'label' => $i,
'value' => $i,
);
}
array_unshift(
$array,
array(
'label' => esc_html__( 'Select Year', 'forminator' ),
'value' => '',
)
);
return apply_filters( 'forminator_field_date_get_years', $array, $min_year, $max_year, $year, $start, $end, $this );
}
/**
* Return monts
*
* @since 1.0
* @return array
*/
public function get_months() {
$array = array();
for ( $i = 1; $i < 13; $i++ ) {
$array[] = array(
'label' => $i,
'value' => $i,
);
}
array_unshift(
$array,
array(
'label' => esc_html__( 'Select month', 'forminator' ),
'value' => '',
)
);
return apply_filters( 'forminator_field_date_get_months', $array, $this );
}
/**
* Return days
*
* @since 1.0
* @return array
*/
public function get_day() {
$array = array();
for ( $i = 1; $i < 32; $i++ ) {
$array[] = array(
'label' => $i,
'value' => $i,
);
}
array_unshift(
$array,
array(
'label' => esc_html__( 'Select day', 'forminator' ),
'value' => '',
)
);
return apply_filters( 'forminator_field_date_get_day', $array, $this );
}
/**
* Parse date
*
* @since 1.0
*
* @param string|array $date - the date to be parsed.
* @param string $format - the data format.
*
* @return array
*/
public static function parse_date( $date, $format = 'yy-mm-dd' ) {
$date_info = array(
'year' => 0,
'month' => 0,
'day' => 0,
);
$position = substr( $format, 0, 8 );
if ( is_array( $date ) ) {
switch ( $position ) {
case 'mm/dd/yy':
case 'mm-dd-yy':
case 'mm.dd.yy':
$date_info['month'] = isset( $date['month'] ) ? $date['month'] : 0;
$date_info['day'] = isset( $date['day'] ) ? $date['day'] : 0;
$date_info['year'] = isset( $date['year'] ) ? $date['year'] : 0;
break;
case 'dd/mm/yy':
case 'dd-mm-yy':
case 'dd.mm.yy':
$date_info['day'] = isset( $date['day'] ) ? $date['day'] : 0;
$date_info['month'] = isset( $date['month'] ) ? $date['month'] : 0;
$date_info['year'] = isset( $date['year'] ) ? $date['year'] : 0;
break;
case 'yy-mm-dd':
case 'yy/mm/dd':
case 'yy.mm.dd':
$date_info['year'] = isset( $date['year'] ) ? $date['year'] : 0;
$date_info['month'] = isset( $date['month'] ) ? $date['month'] : 0;
$date_info['day'] = isset( $date['day'] ) ? $date['day'] : 0;
break;
default:
break;
}
return apply_filters( 'forminator_field_date_parse_dates', $date_info, $date, $format );
}
$date = preg_replace( '|[/\.]|', '-', $date );
if ( 'mm/dd/yy' === $position || 'mm-dd-yy' === $position || 'mm.dd.yy' === $position ) {
if ( preg_match( '/^(\d{1,2})-(\d{1,2})-(\d{1,4})$/', $date, $matches ) ) {
$date_info['month'] = $matches[1];
$date_info['day'] = $matches[2];
$date_info['year'] = $matches[3];
}
} elseif ( 'dd/mm/yy' === $position || 'dd-mm-yy' === $position || 'dd.mm.yy' === $position ) {
if ( preg_match( '/^(\d{1,2})-(\d{1,2})-(\d{1,4})$/', $date, $matches ) ) {
$date_info['day'] = $matches[1];
$date_info['month'] = $matches[2];
$date_info['year'] = $matches[3];
}
} elseif ( 'yy-mm-dd' === $position || 'yy/mm/dd' === $position || 'yy.mm.dd' === $position ) {
if ( preg_match( '/^(\d{1,4})-(\d{1,2})-(\d{1,2})$/', $date, $matches ) ) {
$date_info['year'] = $matches[1];
$date_info['month'] = $matches[2];
$date_info['day'] = $matches[3];
}
}
return apply_filters( 'forminator_field_date_parse_dates', $date_info, $date, $format );
}
/**
* Check data
*
* @since 1.0
*
* @param int $month - the month.
* @param int $day - the day.
* @param int $year - the year.
*
* @return bool
*/
public function check_date( $month, $day, $year ) {
if ( empty( $month ) || ! is_numeric( $month ) || empty( $day ) || ! is_numeric( $day )
|| empty( $year )
|| ! is_numeric( $year )
|| 4 !== strlen( $year ) ) {
return false;
}
return checkdate( $month, $day, $year );
}
/**
* Return field inline validation rules
*
* @since 1.0
* @return string
*/
public function get_validation_rules() {
$field = $this->field;
$id = self::get_property( 'element_id', $field );
$type = trim( self::get_property( 'field_type', $field ) );
$date_format = self::get_property( 'date_format', $field );
$rules = '';
if ( 'picker' === $type ) {
$rules .= '"' . $this->get_id( $field ) . '": {' . "\n";
if ( $this->is_required( $field ) ) {
$rules .= '"required": true,';
}
$rules .= '"dateformat": "' . $date_format . '",';
$rules .= '},' . "\n";
} elseif ( $this->is_required( $field ) ) {
$rules .= '"' . $this->get_id( $field ) . '-day": "required",';
$rules .= '"' . $this->get_id( $field ) . '-month": "required",';
$rules .= '"' . $this->get_id( $field ) . '-year": "required",';
}
return apply_filters( 'forminator_field_date_validation_rules', $rules, $id, $field );
}
/**
* Return field inline validation errors
*
* @since 1.0
* @return string
*/
public function get_validation_messages() {
$field = $this->field;
$type = trim( self::get_property( 'field_type', $field ) );
$date_format = self::get_property( 'date_format', $field );
$required_validation_message = self::get_property( 'required_message', $field, '' );
$month_label = self::get_property( 'month_label', $field, '' );
$day_label = self::get_property( 'day_label', $field, '' );
$year_label = self::get_property( 'year_label', $field, '' );
if ( empty( $required_validation_message ) ) {
if ( 'picker' === $type ) {
$required_validation_message = esc_html__( 'This field is required.', 'forminator' );
} else {
$required_validation_message = ' ' . esc_html__( 'field is required.', 'forminator' );
}
}
$messages = '';
if ( 'picker' === $type ) {
$messages = '"' . $this->get_id( $field ) . '": {' . "\n";
if ( $this->is_required( $field ) ) {
$required_validation_message = apply_filters(
'forminator_field_date_required_validation_message',
$required_validation_message,
$field,
$type,
$date_format,
$this
);
$messages .= '"required": "' . forminator_addcslashes( $required_validation_message ) . '",' . "\n";
}
$format_validation_message = apply_filters(
'forminator_field_date_format_validation_message',
esc_html__( 'Not valid date', 'forminator' ),
$field,
$type,
$date_format,
$this
);
$messages .= '"dateformat": "' . forminator_addcslashes( $format_validation_message ) . '",' . "\n";
$messages .= '},' . "\n";
} elseif ( $this->is_required( $field ) ) {
$day_validation_message = apply_filters(
'forminator_field_date_day_validation_message',
$required_validation_message,
$field,
$type,
$date_format,
$this
);
$messages = '"' . $this->get_id( $field ) . '-day": "<strong>' . $day_label . '</strong> ' . forminator_addcslashes( $day_validation_message ) . '",' . "\n";
$month_validation_message = apply_filters(
'forminator_field_date_month_validation_message',
$required_validation_message,
$field,
$type,
$date_format,
$this
);
$messages .= '"' . $this->get_id( $field ) . '-month": "<strong>' . $month_label . '</strong> ' . forminator_addcslashes( $month_validation_message ) . '",' . "\n";
$year_validation_message = apply_filters(
'forminator_field_date_year_validation_message',
$required_validation_message,
$field,
$type,
$date_format,
$this
);
$messages .= '"' . $this->get_id( $field ) . '-year": "<strong>' . $year_label . '</strong> ' . forminator_addcslashes( $year_validation_message ) . '",' . "\n";
}
return apply_filters( 'forminator_field_date_validation_message', $messages, $field, $type, $date_format, $this );
}
/**
* Field back-end validation
*
* @since 1.0
*
* @param array $field Field.
* @param array|string $data Data.
*/
public function validate( $field, $data ) {
$id = self::get_property( 'element_id', $field );
$start_date_type = self::get_property( 'start-date', $field, '' );
$end_date_type = self::get_property( 'end-date', $field, '' );
$date_type = self::get_property( 'field_type', $field, 'picker' );
$disabled_dates = self::get_property( 'disabled-dates', $field, array() );
$disabled_range = self::get_property( 'disable-date-range', $field, array() );
$restrict_type = self::get_property( 'howto-restrict', $field );
if ( $this->is_required( $field ) ) {
$required_validation_message = self::get_property( 'required_message', $field, esc_html__( 'This field is required. Please enter a valid date.', 'forminator' ) );
if ( empty( $data ) ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
$required_validation_message,
$id,
$data,
$this
);
return;
} elseif ( 'picker' !== $date_type ) {
$month = $data['month'];
$day = $data['day'];
$year = $data['year'];
if ( empty( $month ) ) {
$month_id = $id . '-month';
$this->validation_message[ $month_id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
esc_html__( 'Month field is required.', 'forminator' ),
$month_id,
$data,
$this
);
}
if ( empty( $day ) ) {
$day_id = $id . '-day';
$this->validation_message[ $day_id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
esc_html__( 'Day field is required.', 'forminator' ),
$day_id,
$data,
$this
);
}
if ( empty( $year ) ) {
$year_id = $id . '-year';
$this->validation_message[ $year_id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
esc_html__( 'Year field is required.', 'forminator' ),
$year_id,
$data,
$this
);
}
}
}
if ( empty( $data ) ) {
return;
}
if ( isset( $data['year'] ) && isset( $data['day'] ) && isset( $data['month'] )
&& empty( $data['year'] ) && empty( $data['day'] ) && empty( $data['month'] ) ) {
return;
}
// Always! (we dont have validate flag on builder) validate date_format.
$date_format = self::get_property( 'date_format', $field );
$date = self::parse_date( $data, $date_format );
$month = $date['month'];
$day = $date['day'];
$year = $date['year'];
// strtotime does not recognize all of our date formats so we need to convert all dates to 1 accepted format before processing.
if ( 'Y-m-d' !== datepicker_default_format( $date_format ) && ! is_array( $data ) ) {
$format_date = date_create_from_format( datepicker_default_format( $date_format ), $data );
if ( $format_date ) {
$data = date_format( $format_date, 'Y-m-d' );
} else {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_date_validation_message',
esc_html__( 'Please enter a valid date.', 'forminator' ),
$id,
$data,
$date_format,
$this
);
return;
}
}
if (
'select' !== $date_type &&
! $this->check_date( $date['month'], $date['day'], $date['year'] )
) {
$year_id = $id . '-year';
if ( strlen( $date['year'] ) !== 4 && 'picker' !== $date_type ) {
$this->validation_message[ $year_id ] = apply_filters(
'forminator_field_date_valid_year_validation_message',
esc_html__( 'Year field is invalid.', 'forminator' ),
$year_id,
$data,
$date_format,
$this
);
} else {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_date_validation_message',
esc_html__( 'Please enter a valid date.', 'forminator' ),
$id,
$data,
$date_format,
$this
);
}
} else {
if ( 'select' === $date_type ) {
// For year limits.
$min_year = self::get_property( 'min_year', $field, gmdate( 'Y' ) - 100 );
$max_year = self::get_property( 'max_year', $field, gmdate( 'Y' ) + 100 );
$year = intval( $date['year'] );
if ( ! empty( $min_year ) && ! empty( $max_year ) ) {
if ( $year < $min_year || $year > $max_year ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_maxmin_year_validation_message',
esc_html__( 'Please select a valid year.', 'forminator' )
);
}
} else {
if ( ! empty( $min_year ) ) {
if ( $year < $min_year ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_maxmin_year_validation_message',
esc_html__( 'Please select a valid year.', 'forminator' )
);
}
}
if ( ! empty( $max_year ) ) {
if ( $year > $max_year ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_maxmin_year_validation_message',
esc_html__( 'Please select a valid year.', 'forminator' )
);
}
}
}
/*
* For empty day or month.
* We are allowing year to be filled because it defaults to current year.
* In front-action.php (get_fields_info), we will remove the date submission data if only year has value coz its the default value.
*/
if (
( ! empty( $date['month'] ) || ! empty( $date['day'] ) ) &&
! $this->check_date( $date['month'], $date['day'], $date['year'] )
) {
if ( empty( $month ) ) {
$month_id = $id . '-month';
$this->validation_message[ $month_id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
esc_html__( 'Month field is invalid.', 'forminator' ),
$month_id,
$data,
$this
);
}
if ( empty( $day ) ) {
$day_id = $id . '-day';
$this->validation_message[ $day_id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
esc_html__( 'Day field is invalid.', 'forminator' ),
$day_id,
$data,
$this
);
}
if ( empty( $year ) ) {
$year_id = $id . '-year';
$this->validation_message[ $year_id ] = apply_filters(
'forminator_field_date_required_field_validation_message',
esc_html__( 'Year field is invalid.', 'forminator' ),
$year_id,
$data,
$this
);
}
}
}
if ( 'picker' === $date_type ) {
$custom_form = forminator_get_model_from_id( Forminator_Front_Action::$module_id );
$selected_date = preg_replace( '/(\d+)\D+(\d+)\D+(\d+)/', '$1/$2/$3', $data );
if ( 'week' === $restrict_type ) {
$restrict = array();
$days = forminator_week_days();
foreach ( $days as $k => $day ) {
if ( ! self::get_property( $k, $field ) ) {
$restrict[] = $k;
}
}
if ( ! empty( $restrict ) ) {
$current_day = gmdate( 'l', strtotime( $selected_date ) );
if ( in_array( strtolower( $current_day ), $restrict, true ) ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_between_date_validation_message',
self::get_property( 'restrict_message', $field, esc_html__( 'Please select one of the available dates.', 'forminator' ) )
);
}
}
}
if ( ! empty( $start_date_type ) ) {
if ( 'specific' === $start_date_type ) {
$start_date = self::get_property( 'start-specific-date', $field, '' );
} else {
$start_offset_operator = self::get_property( 'start-offset-operator', $field, '+' );
$start_offset_value = self::get_property( 'start-offset-value', $field, '0' );
$start_offset_duration = self::get_property( 'start-offset-duration', $field, 'days' );
if ( 'today' === $start_date_type ) {
$start_date = date_i18n( 'Y-m-d', strtotime( $start_offset_operator . $start_offset_value . ' ' . $start_offset_duration, current_time( 'U' ) ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- We are using the current timestamp based on the site's timezone.
} else {
$start_date_value = isset( Forminator_CForm_Front_Action::$prepared_data[ $start_date_type ] )
? Forminator_CForm_Front_Action::$prepared_data[ $start_date_type ] : '';
$start_date = '';
if ( ! empty( $start_date_value ) ) {
$start_date_field = $custom_form->get_field( $start_date_type, true );
$start_date_format = self::get_property( 'date_format', $start_date_field );
$start_date_format = date_create_from_format( datepicker_default_format( $start_date_format ), $start_date_value );
$start_date_value = date_format( $start_date_format, 'Y-m-d' );
$start_date = date_i18n( 'Y-m-d', strtotime( $start_date_value . ' ' . $start_offset_operator . $start_offset_value . ' ' . $start_offset_duration ) );
}
}
}
if ( ! empty( $start_date ) && strtotime( $selected_date ) < strtotime( $start_date ) ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_between_date_validation_message',
self::get_property( 'restrict_message', $field, esc_html__( 'Please select one of the available dates.', 'forminator' ) )
);
}
}
if ( ! empty( $end_date_type ) ) {
if ( 'specific' === $end_date_type ) {
$end_date = self::get_property( 'end-specific-date', $field, '' );
} else {
$end_offset_operator = self::get_property( 'end-offset-operator', $field, '+' );
$end_offset_value = self::get_property( 'end-offset-value', $field, '0' );
$end_offset_duration = self::get_property( 'end-offset-duration', $field, 'days' );
if ( 'today' === $end_date_type ) {
$end_date = date_i18n( 'Y-m-d', strtotime( $end_offset_operator . $end_offset_value . ' ' . $end_offset_duration, current_time( 'U' ) ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- We are using the current timestamp based on the site's timezone.
} else {
$end_date_value = isset( Forminator_CForm_Front_Action::$prepared_data[ $end_date_type ] )
? Forminator_CForm_Front_Action::$prepared_data[ $end_date_type ] : '';
$end_date = '';
if ( ! empty( $end_date_value ) ) {
$end_date_field = $custom_form->get_field( $end_date_type, true );
$end_date_format = self::get_property( 'date_format', $end_date_field );
$end_date_format = date_create_from_format( datepicker_default_format( $end_date_format ), $end_date_value );
$end_date_value = date_format( $end_date_format, 'Y-m-d' );
$end_date = date_i18n( 'Y-m-d', strtotime( $end_date_value . ' ' . $end_offset_operator . $end_offset_value . ' ' . $end_offset_duration ) );
}
}
}
if ( ! empty( $end_date ) && strtotime( $selected_date ) > strtotime( $end_date ) ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_between_date_validation_message',
self::get_property( 'restrict_message', $field, esc_html__( 'Please select one of the available dates.', 'forminator' ) )
);
}
}
// Change selected date format to the disabled-date format which is m/d/Y.
$selected_date_format = date_create_from_format( 'Y/m/d', $selected_date );
$formatted_selected_date = date_format( $selected_date_format, 'm/d/Y' );
if ( ! empty( $disabled_dates ) && in_array( $formatted_selected_date, $disabled_dates, true ) ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_disabled_validation_message',
self::get_property( 'restrict_message', $field, esc_html__( 'Please select one of the available dates.', 'forminator' ) )
);
}
if ( ! empty( $disabled_range ) ) {
$has_range = true;
foreach ( $disabled_range as $range ) {
$range_arr = array_map( 'trim', explode( '-', $range ) );
if ( ! empty( $range_arr ) ) {
$start_date = isset( $range_arr[0] ) ? $range_arr[0] : '';
$end_date = isset( $range_arr[1] ) ? $range_arr[1] : '';
if ( strtotime( $selected_date ) >= strtotime( $start_date ) && strtotime( $selected_date ) <= strtotime( $end_date ) ) {
$has_range = false;
continue;
}
}
}
if ( ! $has_range ) {
$this->validation_message[ $id ] = apply_filters(
'forminator_field_date_valid_disabled_validation_message',
self::get_property( 'restrict_message', $field, esc_html__( 'Please select one of the available dates.', 'forminator' ) )
);
}
}
}
}
}
/**
* Sanitize data
*
* @since 1.0.2
*
* @param array $field Field.
* @param array|string $data - the data to be sanitized.
*
* @return array|string $data - the data after sanitization
*/
public function sanitize( $field, $data ) {
$original_data = $data;
// Sanitize.
if ( is_array( $data ) ) {
$data = forminator_sanitize_array_field( $data );
} else {
$data = forminator_sanitize_field( $data );
}
return apply_filters( 'forminator_field_date_sanitize', $data, $field, $original_data );
}
/**
* Convert condition date to timestamp
*
* @param string $conditions_date Date from conditions.
* @return string|false Timestamp
*/
public static function prepare_condition_date( $conditions_date ) {
if ( is_numeric( $conditions_date ) ) {
// Timestamp is in milliseconds.
$compare_to = $conditions_date / 1000;
} else {
// Backward compatibility.
$compare_to = strtotime( $conditions_date );
}
return $compare_to;
}
}