OwlCyberSecurity - MANAGER
Edit File: Datepicker.php
<?php namespace WPForms\Admin\Helpers; use DateTimeImmutable; /** * Timespan and popover date-picker helper methods. * * @since 1.8.2 */ class Datepicker { /** * Number of timespan days by default. * "Last 30 Days", by default. * * @since 1.8.2 */ const TIMESPAN_DAYS = '30'; /** * Timespan (date range) delimiter. * * @since 1.8.2 */ const TIMESPAN_DELIMITER = ' - '; /** * Default date format. * * @since 1.8.2 */ const DATE_FORMAT = 'Y-m-d'; /** * Default date-time format. * * @since 1.8.2 */ const DATETIME_FORMAT = 'Y-m-d H:i:s'; /** * Sets the timespan (or date range) selected. * * Includes: * 1. Start date object in WP timezone. * 2. End date object in WP timezone. * 3. Number of "Last X days", if applicable, otherwise returns "custom". * 4. Label associated with the selected date filter choice. @see "get_date_filter_choices". * * @since 1.8.2 * * @return array */ public static function process_timespan() { $dates = (string) filter_input( INPUT_GET, 'date', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); // Return default timespan if dates are empty. if ( empty( $dates ) ) { return self::get_timespan_dates( self::TIMESPAN_DAYS ); } $dates = self::maybe_validate_string_timespan( $dates ); list( $start_date, $end_date ) = explode( self::TIMESPAN_DELIMITER, $dates ); // Return default timespan if start date is more recent than end date. if ( strtotime( $start_date ) > strtotime( $end_date ) ) { return self::get_timespan_dates( self::TIMESPAN_DAYS ); } $timezone = wpforms_get_timezone(); // Retrieve the timezone string for the site. $start_date = date_create_immutable( $start_date, $timezone ); $end_date = date_create_immutable( $end_date, $timezone ); // Return default timespan if date creation fails. if ( ! $start_date || ! $end_date ) { return self::get_timespan_dates( self::TIMESPAN_DAYS ); } // Set time to 0:0:0 for start date and 23:59:59 for end date. $start_date = $start_date->setTime( 0, 0, 0 ); $end_date = $end_date->setTime( 23, 59, 59 ); $days_diff = ''; $current_date = date_create_immutable( 'now', $timezone )->setTime( 23, 59, 59 ); // Calculate days difference only if end date is equal to current date. if ( ! $current_date->diff( $end_date )->format( '%a' ) ) { $days_diff = $end_date->diff( $start_date )->format( '%a' ); } list( $days, $timespan_label ) = self::get_date_filter_choices( $days_diff ); return [ $start_date, // WP timezone. $end_date, // WP timezone. $days, // e.g., 22. $timespan_label, // e.g., Custom. ]; } /** * Sets the timespan (or date range) for performing mysql queries. * * Includes: * 1. Start date object in WP timezone. * 2. End date object in WP timezone. * * @param null|array $timespan Given timespan (dates) preferably in WP timezone. * * @since 1.8.2 * * @return array */ public static function process_timespan_mysql( $timespan = null ) { // Retrieve and validate timespan if none is given. if ( empty( $timespan ) || ! is_array( $timespan ) ) { $timespan = self::process_timespan(); } list( $start_date, $end_date ) = $timespan; // Ideally should be in WP timezone. // If the time period is not a date object, return empty values. if ( ! ( $start_date instanceof DateTimeImmutable ) || ! ( $end_date instanceof DateTimeImmutable ) ) { return [ '', '' ]; } // If given timespan is already in UTC timezone, return as it is. if ( date_timezone_get( $start_date )->getName() === 'UTC' && date_timezone_get( $end_date )->getName() === 'UTC' ) { return [ $start_date, // UTC timezone. $end_date, // UTC timezone. ]; } $mysql_timezone = timezone_open( 'UTC' ); return [ $start_date->setTimezone( $mysql_timezone ), // UTC timezone. $end_date->setTimezone( $mysql_timezone ), // UTC timezone. ]; } /** * Helper method to generate WP and UTC based date-time instances. * * Includes: * 1. Start date object in WP timezone. * 2. End date object in WP timezone. * 3. Start date object in UTC timezone. * 4. End date object in UTC timezone. * * @since 1.8.2 * * @param string $dates Given timespan (dates) in string. i.e. "2023-01-16 - 2023-02-15". * * @return bool|array */ public static function process_string_timespan( $dates ) { $dates = self::maybe_validate_string_timespan( $dates ); list( $start_date, $end_date ) = explode( self::TIMESPAN_DELIMITER, $dates ); // Return false if the start date is more recent than the end date. if ( strtotime( $start_date ) > strtotime( $end_date ) ) { return false; } $timezone = wpforms_get_timezone(); // Retrieve the timezone object for the site. $start_date = date_create_immutable( $start_date, $timezone ); $end_date = date_create_immutable( $end_date, $timezone ); // Return false if the date creation fails. if ( ! $start_date || ! $end_date ) { return false; } // Set the time to 0:0:0 for the start date and 23:59:59 for the end date. $start_date = $start_date->setTime( 0, 0, 0 ); $end_date = $end_date->setTime( 23, 59, 59 ); // Since we will need the initial datetime instances after the query, // we need to return new objects when modifications made. // Convert the dates to UTC timezone. $mysql_timezone = timezone_open( 'UTC' ); $utc_start_date = $start_date->setTimezone( $mysql_timezone ); $utc_end_date = $end_date->setTimezone( $mysql_timezone ); return [ $start_date, // WP timezone. $end_date, // WP timezone. $utc_start_date, // UTC timezone. $utc_end_date, // UTC timezone. ]; } /** * Sets the timespan (or date range) for performing mysql queries. * * Includes: * 1. A list of date filter options for the datepicker module. * 2. Currently selected filter or date range values. Last "X" days, or i.e. Feb 8, 2023 - Mar 9, 2023. * 3. Assigned timespan dates. * * @param null|array $timespan Given timespan (dates) preferably in WP timezone. * * @since 1.8.2 * * @return array */ public static function process_datepicker_choices( $timespan = null ) { // Retrieve and validate timespan if none is given. if ( empty( $timespan ) || ! is_array( $timespan ) ) { $timespan = self::process_timespan(); } list( $start_date, $end_date, $days ) = $timespan; $filters = self::get_date_filter_choices(); $selected = isset( $filters[ $days ] ) ? $days : 'custom'; $value = self::concat_dates( $start_date, $end_date ); $chosen_filter = $selected === 'custom' ? $value : $filters[ $selected ]; $choices = []; foreach ( $filters as $choice => $label ) { $timespan_dates = self::get_timespan_dates( $choice ); $checked = checked( $selected, $choice, false ); $choices[] = sprintf( '<label class="%s">%s<input type="radio" aria-hidden="true" name="timespan" value="%s" %s></label>', $checked ? 'is-selected' : '', esc_html( $label ), esc_attr( self::concat_dates( ...$timespan_dates ) ), esc_attr( $checked ) ); } return [ $choices, $chosen_filter, $value, ]; } /** * Based on the specified date-time range, calculates the comparable prior time period to estimate trends. * * Includes: * 1. Start date object in the given (original) timezone. * 2. End date object in the given (original) timezone. * * @since 1.8.2 * * @param DateTimeImmutable $start_date Start date for the timespan. * @param DateTimeImmutable $end_date End date for the timespan. * * @return bool|array */ public static function get_prev_timespan_dates( $start_date, $end_date ) { if ( ! ( $start_date instanceof DateTimeImmutable ) || ! ( $end_date instanceof DateTimeImmutable ) ) { return false; } $days_diff = $end_date->diff( $start_date )->format( '%a' ); $days_modifier = (int) $days_diff <= 0 ? '1' : (string) $days_diff; return [ $start_date->modify( "-{$days_modifier} day" ), $start_date->modify( '-1 second' ), ]; } /** * The number of days is converted to the start and end date range. * * @since 1.8.2 * * @param string $days Timespan days. * * @return array */ private static function get_timespan_dates( $days ) { list( $timespan_key, $timespan_label ) = self::get_date_filter_choices( $days ); // Bail early, if the given number of days is NOT a number nor a numeric string. if ( ! is_numeric( $days ) ) { return [ '', '', $timespan_key, $timespan_label ]; } $end_date = date_create_immutable( 'now', wpforms_get_timezone() ); $start_date = $end_date; if ( (int) $days > 0 ) { $start_date = $start_date->modify( "-{$days} day" ); } $start_date = $start_date->setTime( 0, 0, 0 ); $end_date = $end_date->setTime( 23, 59, 59 ); return [ $start_date, // WP timezone. $end_date, // WP timezone. $timespan_key, // i.e. 30. $timespan_label, // i.e. Last 30 days. ]; } /** * Check the delimiter to see if the end date is specified. * We can assume that the start and end dates are the same if the end date is missing. * * @since 1.8.2 * * @param string $dates Given timespan (dates) in string. i.e. "2023-01-16 - 2023-02-15" or "2023-01-16". * * @return string */ private static function maybe_validate_string_timespan( $dates ) { // "-" (en dash) is used as a delimiter for the datepicker module. if ( strpos( $dates, self::TIMESPAN_DELIMITER ) !== false ) { return $dates; } return $dates . self::TIMESPAN_DELIMITER . $dates; } /** * Returns a list of date filter options for the datepicker module. * * @since 1.8.2 * * @param string|null $key Optional. Key associated with available filters. * * @return array */ private static function get_date_filter_choices( $key = null ) { // Available date filters. $choices = [ '0' => esc_html__( 'Today', 'wpforms-lite' ), '1' => esc_html__( 'Yesterday', 'wpforms-lite' ), '7' => esc_html__( 'Last 7 days', 'wpforms-lite' ), '30' => esc_html__( 'Last 30 days', 'wpforms-lite' ), '90' => esc_html__( 'Last 90 days', 'wpforms-lite' ), '365' => esc_html__( 'Last 1 year', 'wpforms-lite' ), 'custom' => esc_html__( 'Custom', 'wpforms-lite' ), ]; // Bail early, and return the full list of options. if ( is_null( $key ) ) { return $choices; } // Return the "Custom" filter if the given key is not found. $key = isset( $choices[ $key ] ) ? $key : 'custom'; return [ $key, $choices[ $key ] ]; } /** * Concatenate given dates into a single string. i.e. "2023-01-16 - 2023-02-15". * * @since 1.8.2 * * @param DateTimeImmutable $start_date Start date. * @param DateTimeImmutable $end_date End date. * @param int|string $fallback Fallback value if dates are not valid. * * @return string */ private static function concat_dates( $start_date, $end_date, $fallback = '' ) { // Bail early, if the given dates are not valid. if ( ! ( $start_date instanceof DateTimeImmutable ) || ! ( $end_date instanceof DateTimeImmutable ) ) { return $fallback; } return implode( self::TIMESPAN_DELIMITER, [ $start_date->format( self::DATE_FORMAT ), $end_date->format( self::DATE_FORMAT ), ] ); } }