class availability_calendars_handler_filter_availability in Availability Calendars 6.2
@class availability_calendars_handler_filter_availability Views handler to filter on availability.
This filter inherits from specific to:
- Accept dates only (not DateTime's).
- Not accepting relative dates.
- Accept only future dates.
- Allow only 2 operators:
- =: available at a given date
- between: available from a given departure date to a departure date
- Add date popups to the (exposed) form elements.
Hierarchy
- class \availability_calendars_handler_filter_availability extends \views_handler_filter_date
Expanded class hierarchy of availability_calendars_handler_filter_availability
See also
views_handler_filter_date. But we make it more
1 string reference to 'availability_calendars_handler_filter_availability'
File
View source
class availability_calendars_handler_filter_availability extends views_handler_filter_date {
public static $instance;
function __construct() {
self::$instance = $this;
}
function option_definition() {
$options = parent::option_definition();
$options['operator'] = array(
'default' => 'between',
);
return $options;
}
function operators() {
// We only allow "available at <date>" and
// "available between <from> and <to>" as operators.
$operators = array(
'=' => array(
'title' => t('At (date)'),
'method' => 'op_simple',
'short' => t('at'),
'values' => 1,
),
'between' => array(
'title' => t('Period'),
'method' => 'op_between',
'short' => t('between'),
'values' => 2,
),
);
return $operators;
}
/**
* Add validation and date popup(s) to the value form.
*/
function value_form(&$form, &$form_state) {
parent::value_form($form, $form_state);
// Change labels.
if (isset($form['value']['min'])) {
$form['value']['min']['#title'] = t('Arrival date');
$form['value']['max']['#title'] = t('Departure date');
}
else {
$form['value']['#title'] = '';
}
// Remove the option, that date filter (our parent class) added, to define
// dates relatively.
if (empty($form_state['exposed']) && isset($form['value']['type'])) {
$form['value']['type']['#type'] = 'hidden';
$form['value']['type']['#default_value'] = 'date';
$form['value']['type']['#access'] = FALSE;
// Don't send to the client.
}
// Add validation.
$form['value']['#element_validate'][] = 'availability_calendars_handler_filter_availability_validate_value';
// Use date popups if that module is available.
if (module_exists('date_popup')) {
if (isset($form['value']['min'])) {
$form['value']['min'] = $this
->change_element_into_date_popup($form['value']['min']);
$form['value']['max'] = $this
->change_element_into_date_popup($form['value']['max']);
}
else {
if (isset($form['value']['#type']) && $form['value']['#type'] == 'textfield') {
$form['value'] = $this
->change_element_into_date_popup($form['value']);
}
}
}
}
/**
* Changes a (text) form element into a dete popup element.
*
* @param array $element
* @return array
* The changed form element.
*/
protected function change_element_into_date_popup($element) {
$element['#type'] = 'date_popup';
$element['#date_label_position'] = '';
$element['#date_type'] = 'DATE_ISO';
$element['#date_format'] = 'Y-m-d';
$element['#date_year_range'] = '-0:+2';
// @todo: where to place this code? is it needed in D6?
availability_calendars_handler_filter_availability_js_alter($dummy = TRUE);
return $element;
}
/**
* Validate that the time values convert to something usable.
*/
function validate_valid_time(&$form, $operator, $value) {
$operators = $this
->operators();
$required = FALSE;
if ($operator === NULL) {
// We are in exposed form validate.
$required = (bool) $this->options['expose']['required'];
$operator = array_key_exists('min', $form) ? 'between' : '=';
}
$now = format_date(time(), 'custom', 'Y-m-d');
if ($operators[$operator]['values'] == 1) {
// Note that the value can be an array with a date and time component.
$value = is_array($value) && array_key_exists('date', $value) ? $value['date'] : $value;
$this
->validate_valid_time_1($form, $value, $required, $now, t('Only future availability can be searched.'));
}
elseif ($operators[$operator]['values'] == 2) {
// Note that the min and max values can be an array with a date and time
// component or a string with a date and time component
// (yyyy-mm-dd hh:mm:ss).
$min_value = is_array($value['min']) && array_key_exists('date', $value['min']) ? $value['min']['date'] : substr($value['min'], 0, 10);
$min_valid = $this
->validate_valid_time_1($form['min'], $min_value, $required, $now, t('Only future availability can be searched.'));
$max_value = is_array($value['max']) && array_key_exists('date', $value['max']) ? $value['max']['date'] : substr($value['max'], 0, 10);
$required = $required || !empty($max_value);
$this
->validate_valid_time_1($form['max'], $max_value, $required, $min_valid ? $min_value : NULL, t('The departure date should be after the arrival date.'));
}
}
protected function validate_valid_time_1(&$element, $value, $required, $minimum, $minimum_error_message) {
$valid = TRUE;
if (empty($value)) {
if ($required) {
form_error($element, t('Field %field is required.', array(
'%field' => $element['#title'],
)));
$valid = FALSE;
}
}
else {
if (strlen($value) !== 10 || !checkdate(substr($value, 5, 2), substr($value, 8, 2), substr($value, 0, 4))) {
form_error($element, t('Invalid date format.'));
$valid = FALSE;
}
else {
if (!empty($minimum) && $value <= $minimum) {
form_error($element, $minimum_error_message);
$valid = FALSE;
}
}
}
return $valid;
}
function op_between($field) {
$arrival = is_array($this->value['min']) && array_key_exists('date', $this->value['min']) ? $this->value['min']['date'] : $this->value['min'];
$departure = is_array($this->value['max']) && array_key_exists('date', $this->value['max']) ? $this->value['max']['date'] : $this->value['max'];
$this
->query_available($arrival, $departure);
}
function op_simple($field) {
$this->value['min'] = $this->value['value'];
$this->value['max'] = format_date(mktime(12, 0, 0, substr($this->value['min'], 5, 2), (int) substr($this->value['min'], 8, 2) + 1, substr($this->value['min'], 0, 4)), 'custom', 'Y-m-d');
return $this
->op_between($field);
}
/**
* Add where clauses to the query to filter on availability.
*
* @param string $arrival yyyy-mm-dd.
* @param string $departure yyyy-mm-dd.
*/
function query_available($arrival, $departure) {
if (empty($arrival) || empty($departure)) {
// Don't add a clause if there's nothing to filter on.
return;
}
module_load_include('inc', 'availability_calendars', 'availability_calendars');
$states = availability_calendars_get_states();
$settings = availability_calendars_get_settings();
$default_is_available = $states[$settings->defaultstatus]['is_available'];
$classes = array();
foreach ($states as $class => $state) {
if ($state['is_available'] != $default_is_available) {
$classes[] = $class;
}
}
$classes = empty($classes) ? '' : "'" . implode("', '", $classes) . "'";
// Format from and to in a safe way.
$start = format_date(mktime(12, 0, 0, substr($arrival, 5, 2), substr($arrival, 8, 2), substr($arrival, 0, 4)), 'custom', 'Y-m-d');
$end = format_date(mktime(12, 0, 0, substr($departure, 5, 2), (int) substr($departure, 8, 2) - 1, substr($departure, 0, 4)), 'custom', 'Y-m-d');
// Build common part of subquery.
$base_table_alias = reset($this->query->tables[$this->table]);
$base_table_alias = $base_table_alias['alias'];
$from = 'FROM {availability_calendars_day} acd';
$where = "WHERE acd.nid = {$base_table_alias}.{$this->query->base_field} ";
$where .= "AND acd.date BETWEEN '{$start}' AND '{$end}' ";
$where .= "AND acd.status in ({$classes})";
if ($default_is_available == 1) {
// Default status = available: so no single day may be marked as non
// available. Check by doing a check on the existence of a non available
// day in the given period.
$subquery = "SELECT 1 {$from} {$where}";
$this->query
->add_where(0, "NOT EXISTS ({$subquery})");
}
else {
// Default status = not available: so all days must be marked as
// available. Check by doing a count on the available days in the given
// period which should equal the total number of days.
$timestamp_arrival = strtotime($arrival);
$timestamp_departure = strtotime($departure);
$days = (int) round(($timestamp_departure - $timestamp_arrival) / (60 * 60 * 24));
$subquery = "SELECT count(*) {$from} {$where}";
$this->query
->add_where(0, "({$subquery}) = {$days}");
}
}
}