You are here

bat.module in Booking and Availability Management Tools for Drupal 7

Same filename and directory in other branches
  1. 8 bat.module

Provides basic underlying functionality and configuration options used by all BAT modules.

File

bat.module
View source
<?php

/**
 * @file
 * Provides basic underlying functionality and configuration options used
 * by all BAT modules.
 */

/**
 * Implements hook_permission().
 */
function bat_permission() {
  $permissions = array(
    'configure bat settings' => array(
      'title' => t('Configure BAT'),
      'description' => t('Allows users to manage site-wide BAT configuration settings.'),
      'restrict access' => TRUE,
    ),
  );
  return $permissions;
}

/**
 * Implements hook_menu().
 */
function bat_menu() {
  $items = array();

  // Top level "BAT" container.
  $items['admin/bat'] = array(
    'title' => 'BAT',
    'description' => 'Administer BAT.',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array(
      'access administration pages',
    ),
    'file path' => drupal_get_path('module', 'system'),
    'file' => 'system.admin.inc',
    'weight' => -7,
  );
  $items['admin/bat/config'] = array(
    'title' => 'Configuration',
    'description' => 'Configure settings and business rules for BAT management.',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array(
      'configure bat settings',
    ),
    'type' => MENU_NORMAL_ITEM,
    'weight' => 10,
    'file path' => drupal_get_path('module', 'system'),
    'file' => 'system.admin.inc',
  );
  $items['admin/bat/config/date-format'] = array(
    'title' => 'Date format',
    'description' => 'Configure date format.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'bat_date_format_form',
    ),
    'access arguments' => array(
      'configure bat settings',
    ),
    'type' => MENU_NORMAL_ITEM,
    'weight' => 30,
  );
  return $items;
}

/**
 * Main booking settings form.
 */
function bat_date_format_form($form, &$form_state) {
  $form['bat_date_format'] = array(
    '#type' => 'item',
    '#title' => t('BAT PHP Date Format'),
    '#description' => t("A custom date format for events, search summary and calendar pop-ups. Define a php date format string like 'Y-m-d H:i' (see <a href=\"@link\">http://php.net/date</a> for more details).", array(
      '@link' => 'http://php.net/date',
    )),
  );
  $form['bat_date_format']['bat_date_format'] = array(
    '#type' => 'textfield',
    '#size' => 12,
    '#prefix' => '<div class="container-inline form-item">' . t('Date format') . ': &nbsp;',
    '#suffix' => '</div>',
    '#default_value' => variable_get('bat_date_format', 'Y-m-d H:i'),
  );
  $form['bat_date_format']['bat_daily_date_format'] = array(
    '#type' => 'textfield',
    '#size' => 12,
    '#prefix' => '<div class="container-inline form-item">' . t('Daily date format') . ': &nbsp;',
    '#suffix' => '</div>',
    '#default_value' => variable_get('bat_daily_date_format', 'Y-m-d'),
  );
  $form['bat_hourly_format'] = array(
    '#type' => 'item',
    '#title' => 'Hourly PHP Date Format',
    '#description' => t("Define a php date format string like 'H:i' (see <a href=\"@link\">http://php.net/date</a> for more details).", array(
      '@link' => 'http://php.net/date',
    )),
  );
  $form['bat_hourly_format']['bat_hourly_date_format'] = array(
    '#type' => 'textfield',
    '#size' => 12,
    '#prefix' => '<div class="container-inline form-item">' . t('Time format') . ': &nbsp;',
    '#suffix' => '</div>',
    '#default_value' => variable_get('bat_hourly_date_format', 'H:i'),
  );
  return system_settings_form($form);
}

/**
 * Implements hook_date_format_types().
 */
function bat_date_format_types() {
  return array(
    'bat_daily' => t('BAT Daily Date format'),
    'bat' => t('BAT Date format'),
  );
}

/**
 * Implements hook_date_formats().
 */
function bat_date_formats() {
  $formats = array();
  $formats[] = array(
    'type' => 'bat_daily',
    'format' => variable_get('bat_daily_date_format', 'Y-m-d'),
    'locales' => array(),
  );
  $formats[] = array(
    'type' => 'bat',
    'format' => variable_get('bat_date_format', 'Y-m-d H:i'),
    'locales' => array(),
  );
  foreach ($formats as $format) {
    variable_set('date_format_' . $format['type'], $format['format']);
  }
  return $formats;
}

/**
 * Generic access control for entities that BAT defines.
 *
 * @param string $op
 *   The operation being performed. One of 'view', 'update', 'create' or
 *   'delete'.
 * @param object $entity
 *   Optionally an entity to check access for. If no entity is given, it will be
 *   determined whether access is allowed for all entities of the given type.
 * @param object $account
 *   The user to check for. Leave it to NULL to check for the global user.
 * @param string $entity_type
 *   The entity type of the entity to check for.
 *
 * @return bool
 *   Boolean indicating whether the user is granted or not.
 *
 * @see entity_access()
 */
function bat_entity_access($op, $entity, $account, $entity_type) {
  $rights =& drupal_static(__FUNCTION__, array());
  global $user;
  $account = isset($account) ? $account : $user;
  $entity_info = entity_get_info($entity_type);

  // $entity may be either an object or a entity type. Since entity types cannot
  // be an integer, use either nid or type as the static cache id.
  $cid = is_object($entity) ? $entity->{$entity_info['entity keys']['id']} : $entity;

  // If we are creating a new entity make sure we set the type so permissions get
  // applied.
  if ($op == 'create' && $cid == '') {
    $cid = $entity->type;
  }

  // If we've already checked access for this node, user and op, return the
  // cached result.
  if (isset($rights[$account->uid][$cid][$op])) {
    return $rights[$account->uid][$cid][$op];
  }

  // Grant generic administrator level access.
  if (user_access('bypass ' . $entity_type . ' entities access', $account)) {
    $rights[$account->uid][$cid][$op] = TRUE;
    return TRUE;
  }
  if ($op == 'view') {
    if (isset($entity)) {

      // When trying to figure out access to an entity, query the base table
      // using our access control tag.
      if (!empty($entity_info['access arguments']['access tag']) && module_implements('query_' . $entity_info['access arguments']['access tag'] . '_alter')) {
        $query = db_select($entity_info['base table']);
        $query
          ->addExpression('1');
        $result = (bool) $query
          ->addTag($entity_info['access arguments']['access tag'])
          ->addMetaData('account', $account)
          ->condition($entity_info['entity keys']['id'], $entity->{$entity_info['entity keys']['id']})
          ->range(0, 1)
          ->execute()
          ->fetchField();
        $rights[$account->uid][$cid][$op] = $result;
        return $result;
      }
      else {
        $rights[$account->uid][$cid][$op] = TRUE;
        return TRUE;
      }
    }
    else {
      $access = user_access('view any ' . $entity_type . ' entity', $account);
      $rights[$account->uid][$cid][$op] = $access;
      return $access;
    }
  }
  else {

    // Non-view operations.
    // First grant access to the entity for the specified operation if no other
    // module denies it and at least one other module says to grant access.
    $access_results = module_invoke_all('bat_entity_access', $op, $entity, $account, $entity_type);
    if (in_array(FALSE, $access_results, TRUE)) {
      $rights[$account->uid][$cid][$op] = FALSE;
      return FALSE;
    }
    elseif (in_array(TRUE, $access_results, TRUE)) {
      $rights[$account->uid][$cid][$op] = TRUE;
      return TRUE;
    }

    // Grant access based on entity type and bundle specific permissions with
    // special handling for the create operation since the entity passed in will
    // be initialized without ownership.
    if ($op == 'create') {

      // Assuming an entity was passed in and we know its bundle key, perform
      // the entity type and bundle-level access checks.
      if (isset($entity) && !empty($entity_info['entity keys']['bundle'])) {
        $access = user_access('create ' . $entity_type . ' entities', $account) || user_access('create ' . $entity_type . ' entities of bundle ' . $entity->{$entity_info['entity keys']['bundle']}, $account);
        $rights[$account->uid][$cid][$op] = $access;
        return $access;
      }
      else {

        // Otherwise perform an entity type-level access check.
        $access = user_access('create ' . $entity_type . ' entities', $account);
        $rights[$account->uid][$cid][$op] = $access;
        return $access;
      }
    }
    else {

      // Finally perform checks for the rest of operations. Begin by
      // extracting the bundle name from the entity if available.
      $bundle_name = '';
      if (isset($entity) && !empty($entity_info['entity keys']['bundle'])) {
        $bundle_name = $entity->{$entity_info['entity keys']['bundle']};
      }

      // For the delete and delete operations, first perform the entity type and
      // bundle-level access check for any entity.
      if (user_access($op . ' any ' . $entity_type . ' entity', $account) || user_access($op . ' any ' . $entity_type . ' entity of bundle ' . $bundle_name, $account)) {
        $rights[$account->uid][$cid][$op] = TRUE;
        return TRUE;
      }

      // Check an authenticated user's access to delete own entities.
      if ($account->uid && !empty($entity_info['access arguments']['user key']) && isset($entity->{$entity_info['access arguments']['user key']}) && $entity->{$entity_info['access arguments']['user key']} == $account->uid) {
        if (user_access($op . ' own ' . $entity_type . ' entities', $account) || user_access($op . ' own ' . $entity_type . ' entities of bundle ' . $bundle_name, $account)) {
          $rights[$account->uid][$cid][$op] = TRUE;
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

/**
 * Return permission names for a given entity type.
 */
function bat_entity_access_permissions($entity_type) {
  $entity_info = entity_get_info($entity_type);
  $labels = $entity_info['permission labels'];
  $permissions = array();

  // General 'bypass' permission.
  $permissions['bypass ' . $entity_type . ' entities access'] = array(
    'title' => t('Bypass access to @entity_type', array(
      '@entity_type' => $labels['plural'],
    )),
    'description' => t('Allows users to perform any action on @entity_type.', array(
      '@entity_type' => $labels['plural'],
    )),
    'restrict access' => TRUE,
  );

  // Generic create and edit permissions.
  $permissions['create ' . $entity_type . ' entities'] = array(
    'title' => t('Create @entity_type of any type', array(
      '@entity_type' => $labels['plural'],
    )),
  );
  if (!empty($entity_info['access arguments']['user key'])) {
    $permissions['view own ' . $entity_type . ' entities'] = array(
      'title' => t('View own @entity_type of any type', array(
        '@entity_type' => $labels['plural'],
      )),
    );
  }
  $permissions['view any ' . $entity_type . ' entity'] = array(
    'title' => t('View any @entity_type of any type', array(
      '@entity_type' => $labels['singular'],
    )),
    'restrict access' => TRUE,
  );
  if (!empty($entity_info['access arguments']['user key'])) {
    $permissions['update own ' . $entity_type . ' entities'] = array(
      'title' => t('Edit own @entity_type of any type', array(
        '@entity_type' => $labels['plural'],
      )),
    );
  }
  $permissions['update any ' . $entity_type . ' entity'] = array(
    'title' => t('Edit any @entity_type of any type', array(
      '@entity_type' => $labels['singular'],
    )),
    'restrict access' => TRUE,
  );
  if (!empty($entity_info['access arguments']['user key'])) {
    $permissions['delete own ' . $entity_type . ' entities'] = array(
      'title' => t('Delete own @entity_type of any type', array(
        '@entity_type' => $labels['plural'],
      )),
    );
  }
  $permissions['delete any ' . $entity_type . ' entity'] = array(
    'title' => t('Delete any @entity_type of any type', array(
      '@entity_type' => $labels['singular'],
    )),
    'restrict access' => TRUE,
  );

  // Per-bundle create and edit permissions.
  if (!empty($entity_info['entity keys']['bundle'])) {
    foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
      $permissions['create ' . $entity_type . ' entities of bundle ' . $bundle_name] = array(
        'title' => t('Create %bundle @entity_type', array(
          '@entity_type' => $labels['plural'],
          '%bundle' => $bundle_info['label'],
        )),
      );
      if (!empty($entity_info['access arguments']['user key'])) {
        $permissions['view own ' . $entity_type . ' entities of bundle ' . $bundle_name] = array(
          'title' => t('View own %bundle @entity_type', array(
            '@entity_type' => $labels['plural'],
            '%bundle' => $bundle_info['label'],
          )),
        );
      }
      $permissions['view any ' . $entity_type . ' entity of bundle ' . $bundle_name] = array(
        'title' => t('View any %bundle @entity_type', array(
          '@entity_type' => $labels['singular'],
          '%bundle' => $bundle_info['label'],
        )),
        'restrict access' => TRUE,
      );
      if (!empty($entity_info['access arguments']['user key'])) {
        $permissions['update own ' . $entity_type . ' entities of bundle ' . $bundle_name] = array(
          'title' => t('Edit own %bundle @entity_type', array(
            '@entity_type' => $labels['plural'],
            '%bundle' => $bundle_info['label'],
          )),
        );
      }
      $permissions['update any ' . $entity_type . ' entity of bundle ' . $bundle_name] = array(
        'title' => t('Edit any %bundle @entity_type', array(
          '@entity_type' => $labels['singular'],
          '%bundle' => $bundle_info['label'],
        )),
        'restrict access' => TRUE,
      );
      if (!empty($entity_info['access arguments']['user key'])) {
        $permissions['delete own ' . $entity_type . ' entities of bundle ' . $bundle_name] = array(
          'title' => t('Delete own %bundle @entity_type', array(
            '@entity_type' => $labels['plural'],
            '%bundle' => $bundle_info['label'],
          )),
        );
      }
      $permissions['delete any ' . $entity_type . ' entity of bundle ' . $bundle_name] = array(
        'title' => t('Delete any %bundle @entity_type', array(
          '@entity_type' => $labels['singular'],
          '%bundle' => $bundle_info['label'],
        )),
        'restrict access' => TRUE,
      );
    }
  }
  return $permissions;
}

/**
 * Implements hook_query_alter().
 *
 * Enforces access control for bat units during database queries.
 */
function bat_entity_access_query_alter($query, $entity_type, $base_table = NULL, $account = NULL, $op = 'view') {
  global $user;

  // Get the Drupal user account from the query if available, or
  // default to the logged in user if not.
  if (!isset($account) && !($account = $query
    ->getMetaData('account'))) {
    $account = $user;
  }

  // Do not apply any conditions for users with administrative view permissions.
  if (user_access('bypass ' . $entity_type . ' access', $account) || user_access($op . ' any ' . $entity_type . ' entity', $account)) {
    return;
  }

  // Get the entity type info array for the current access check and prepare a
  // conditions object.
  $entity_info = entity_get_info($entity_type);

  // If a base table wasn't specified, attempt to read it from the query if
  // available, look for a table in the query's tables array that matches the
  // base table of the given entity type, or just default to the first table.
  if (!isset($base_table) && !($base_table = $query
    ->getMetaData('base_table'))) {

    // Initialize the base table to the first table in the array. If a table can
    // not be found that matches the entity type's base table, this will result
    // in an invalid query if the first table is not the table we expect,
    // forcing the caller to actually properly pass a base table in that case.
    $tables = $query
      ->getTables();
    reset($tables);
    $base_table = key($tables);
    foreach ($tables as $table_info) {
      if (!$table_info instanceof SelectQueryInterface) {

        // If this table matches the entity type's base table, use its table
        // alias as the base table for the purposes of bundle and ownership
        // access checks.
        if ($table_info['table'] == $entity_info['base table']) {
          $base_table = $table_info['alias'];
        }
      }
    }
  }

  // Prepare an OR container for conditions. Conditions will be added that seek
  // to grant access, meaning any particular type of permission check may grant
  // access even if none of the others apply. At the end of this function, if no
  // conditions have been added to the array, a condition will be added that
  // always returns FALSE (1 = 0).
  $conditions = db_or();

  // Loop over every possible bundle for the given entity type.
  foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {

    // If the user has access to operation entities of the current bundle...
    if (user_access($op . ' any ' . $entity_type . ' entity of bundle ' . $bundle_name, $account)) {

      // Add a condition granting access if the entity specified by the view
      // query is of the same bundle.
      $conditions
        ->condition($base_table . '.' . $entity_info['entity keys']['bundle'], $bundle_name);
    }
    elseif ($account->uid && !empty($entity_info['access arguments']['user key']) && user_access($op . ' own ' . $entity_type . ' entities of bundle ' . $bundle_name, $account)) {

      // Add an AND condition group that grants access if the entity specified
      // by the view query matches the same bundle and belongs to the user.
      $conditions
        ->condition(db_and()
        ->condition($base_table . '.' . $entity_info['entity keys']['bundle'], $bundle_name)
        ->condition($base_table . '.' . $entity_info['access arguments']['user key'], $account->uid));
    }
  }

  // If the given entity type has a user ownership key...
  if (!empty($entity_info['access arguments']['user key'])) {

    // Perform 'operation own' access control for the entity in the query if the
    // user is authenticated.
    if ($account->uid && user_access($op . ' own ' . $entity_type . ' entities', $account)) {
      $conditions
        ->condition($base_table . '.' . $entity_info['access arguments']['user key'], $account->uid);
    }
  }

  // Prepare an array of condition alter hooks to invoke and an array of context
  // data for the current query.
  $hooks = array(
    'bat_entity_access_' . $op . '_condition_' . $entity_type,
    'bat_entity_access_' . $op . '_condition',
  );
  $context = array(
    'account' => $account,
    'entity_type' => $entity_type,
    'base_table' => $base_table,
  );

  // Allow other modules to add conditions to the array as necessary.
  drupal_alter($hooks, $conditions, $context);

  // If we have more than one condition based on the entity access permissions
  // and any hook implementations...
  if (count($conditions)) {

    // Add the conditions to the query.
    $query
      ->condition($conditions);
  }
  else {

    // Otherwise, since we don't have any possible conditions to match against,
    // we falsify this query. View checks are access grants, not access denials.
    $query
      ->where('1 = 0');
  }
}

/**
 * Utility function to create two related datepickers.
 *
 * We have a few forms that need a start and end date field
 * and we need to apply the same javascript to these forms in order to enforce
 * specific consistent behaviours and group the form elements and javascript
 * injection in one place.
 *
 * @param int $year
 * @param int $month
 *
 * @return array
 *   The array holding the field definitions
 */
function bat_date_range_fields($year = NULL, $month = NULL, $granularity = 'bat_hourly') {
  $date_range_fields = array();

  // Create unique ids and selectors for each datepicker.
  $start_date_id = drupal_html_id('datepicker-start-date');
  $start_date_selector = '#' . $start_date_id . ' .form-text';
  $end_date_id = drupal_html_id('datepicker-end-date');
  $end_date_selector = '#' . $start_date_id . ' .form-text';

  // Specify the default datepicker parameters (see date_popup_element_info())
  $datepicker_options = array(
    'dateFormat' => date_popup_format_to_popup(variable_get('bat_daily_date_format', 'Y-m-d')),
    // Limit bookings to X days in advance according to the
    // chosen configuration in your BAT installation.
    // Defaults to the current day.
    'minDate' => '+' . variable_get('bat_event_start_date', 0) . 'd',
  );
  if ($year && $month) {

    // Calculate min and max dates of the specified year/month.
    $date = new DateTime();
    $date
      ->setDate($year, $month, 01);
    $min_date = $date
      ->format(variable_get('bat_daily_date_format', 'Y-m-d'));
    $date
      ->modify('last day of this month');
    $max_date = $date
      ->format(variable_get('bat_daily_date_format', 'Y-m-d'));
    $datepicker_options += array(
      'minDate' => $min_date,
      'maxDate' => $max_date,
      'defaultDate' => $min_date,
      'numberOfMonths' => 1,
    );
  }
  else {
    $datepicker_options += array(
      'minDate' => '+' . variable_get('bat_event_start_date', 0) . 'd',
    );
  }
  $date_range_fields['bat_start_date'] = array(
    '#prefix' => '<div class="form-wrapper bat-date-range"><div class="start-date" id="' . $start_date_id . '">',
    '#suffix' => '</div>',
    '#type' => 'date_popup',
    '#title' => t('Event Start'),
    '#date_type' => DATE_DATETIME,
    '#date_format' => variable_get('bat_daily_date_format', 'Y-m-d'),
    '#date_increment' => 1,
    '#date_year_range' => '-1:+3',
    // Default parameters defined above, with an additional parameter
    // linking to the jQuery selector for the end datepicker.
    '#datepicker_options' => array_merge($datepicker_options, array(
      'endDateSelector' => $end_date_selector,
    )),
    '#required' => TRUE,
  );
  $date_range_fields['bat_end_date'] = array(
    '#prefix' => '<div class="end-date" id="' . $end_date_id . '">',
    '#suffix' => '</div></div>',
    '#type' => 'date_popup',
    '#title' => t('Event End'),
    '#date_type' => DATE_DATETIME,
    '#date_format' => variable_get('bat_daily_date_format', 'Y-m-d'),
    '#date_increment' => 1,
    '#date_year_range' => '-1:+3',
    // Default parameters defined above, with an additional parameter
    // parameter linking to the jQuery selector for the start datepicker.
    '#datepicker_options' => array_merge($datepicker_options, array(
      'startDateSelector' => $start_date_selector,
    )),
    '#required' => TRUE,
    '#attached' => array(
      'css' => array(
        drupal_get_path('module', 'bat') . '/css/bat_date_range_fields.css',
      ),
      'js' => array(
        drupal_get_path('module', 'bat') . '/js/bat_date_popup.js',
        array(
          'data' => array(
            'bat' => array(
              'batStartYear' => $year,
              'batStartMonth' => $month,
              'batBookingStartDay' => variable_get('bat_event_start_date', 0),
              'batDateFormat' => 'dd/mm/yy',
              'batDateGranularity' => $granularity,
              // Here we create a listing of all datepickers registered on the
              // current page. This is available for use in your own custom
              // jQuery scripts as Drupal.settings.bat.datepickers.
              'datepickers' => array(
                $start_date_selector => array(
                  'endDateSelector' => $end_date_selector,
                ),
              ),
            ),
          ),
          'type' => 'setting',
        ),
      ),
    ),
  );
  return $date_range_fields;
}

/**
 * Given a form state array, locate the start/end dates in the input array
 * and instantiate and return DateTime objects.
 */
function bat_form_input_get_start_end_dates($form_state) {

  // If form_state['values']['bat_X_date'] is not set, it is an empty array.
  // In this case, we need to check and set values so that
  // _constructor below will not fail.
  if (is_array($form_state['values']['bat_start_date'])) {
    if ($form_state['values']['bat_start_date']['date'] == '') {
      $start = new DateTime();
    }
    else {
      $start = new DateTime($form_state['values']['bat_start_date']['date'] . ' ' . $form_state['values']['bat_start_date']['time']);
    }
  }
  else {
    $start = new DateTime($form_state['values']['bat_start_date']);
  }
  if (is_array($form_state['values']['bat_end_date'])) {
    if ($form_state['values']['bat_end_date']['date'] == '') {
      $end = new DateTime();
    }
    else {
      $end = new DateTime($form_state['values']['bat_end_date']['date'] . ' ' . $form_state['values']['bat_end_date']['time']);
    }
  }
  else {
    $end = new DateTime($form_state['values']['bat_end_date']);
  }
  return array(
    $start,
    $end,
  );
}

/**
 * Given a form state array, locate the start/end dates in the
 * values array and instantiate and return DateTime objects.
 */
function bat_form_values_get_start_end_dates($form_state) {

  // Date values have a format of year-month-day.
  // This is one of the default expected formats, so there
  // is no need to explicitly define the format.
  // (see http://www.php.net/manual/en/datetime.formats.date.php)
  $start_date = $form_state['values']['bat_start_date'];
  $end_date = $form_state['values']['bat_end_date'];

  // If the input format is numeric we assume that is a Unix timestamp.
  if (is_numeric($start_date) && is_numeric($end_date)) {

    // The @ lets the DateTime class know that the date is
    // formatted as a Unix timestamp.
    $start_date = '@' . $start_date;
    $end_date = '@' . $end_date;
  }
  $start = new DateTime($start_date);
  $end = new DateTime($end_date);
  return array(
    $start,
    $end,
  );
}

/**
 * Validation callback that can be reused in all forms that need to
 * validate dates.
 *
 * The end date must be greater than start date.
 */
function bat_form_start_end_dates_validate($form, &$form_state) {
  list($start_date, $end_date) = bat_form_input_get_start_end_dates($form_state);

  // Fail validation if no dates are provided.
  if (empty($start_date) || empty($end_date)) {
    form_set_error('date_range', t('Please choose dates.'));
    return;
  }

  // Check date validity.
  $errors = bat_check_dates_validity($start_date, $end_date);

  // For some forms we need to ensure that the selected date matches with current values.
  if (isset($form_state['values']['curr_month']) && isset($form_state['values']['curr_year'])) {
    $curr_month = $form_state['values']['curr_month'];
    $curr_year = $form_state['values']['curr_year'];
    if ($start_date
      ->format('n') != $curr_month || $end_date
      ->format('n') != $curr_month || $start_date
      ->format('Y') != $curr_year || $end_date
      ->format('Y') != $curr_year) {
      $errors[] = t('Both the start and end date must be within the current month.');
    }
  }

  // When there are multiple errors for the same form element, Drupal only
  // displays the first. Here we concatenate all errors so that they will all
  // be displayed.
  if ($errors) {
    $error_msg = implode(' ', $errors);
    form_set_error('date_range', $error_msg);
  }
}

/**
 * Checks the logical validity of date values.
 *
 * @param DateTime $start_date
 *   The start date.
 * @param DateTime $end_date
 *   The end date.
 *
 * @return array
 *   An array with error messages.
 */
function bat_check_dates_validity(DateTime $start_date, DateTime $end_date) {
  $errors = array();

  // The end date must be greater or equal than start date.
  if ($end_date < $start_date) {
    $errors[] = t('End date must be on or after the start date.');
  }
  return $errors;
}

/**
 * Loads a DateTime object from a string.
 *
 * @param string $date_string
 *   Expects to see a date in the form Y-m-d H:i:s.
 *
 * @return DateTime
 *   Object or null if invalid
 */
function bat_date_load($date_string) {
  $date_string = check_plain($date_string);
  try {
    $date = new DateTime($date_string);
  } catch (Exception $e) {
    $date = 0;
  }
  return $date;
}

/**
 * Get the default database prefix.
 */
function bat_get_db_prefix() {
  global $databases;
  $prefix = '';
  if (!empty($databases['default']['default']['prefix'])) {
    if (is_array($databases['default']['default']['prefix'])) {
      $prefix = $databases['default']['default']['prefix']['default'];
    }
    else {
      $prefix = $databases['default']['default']['prefix'];
    }
  }
  return $prefix;
}

Functions

Namesort descending Description
bat_check_dates_validity Checks the logical validity of date values.
bat_date_formats Implements hook_date_formats().
bat_date_format_form Main booking settings form.
bat_date_format_types Implements hook_date_format_types().
bat_date_load Loads a DateTime object from a string.
bat_date_range_fields Utility function to create two related datepickers.
bat_entity_access Generic access control for entities that BAT defines.
bat_entity_access_permissions Return permission names for a given entity type.
bat_entity_access_query_alter Implements hook_query_alter().
bat_form_input_get_start_end_dates Given a form state array, locate the start/end dates in the input array and instantiate and return DateTime objects.
bat_form_start_end_dates_validate Validation callback that can be reused in all forms that need to validate dates.
bat_form_values_get_start_end_dates Given a form state array, locate the start/end dates in the values array and instantiate and return DateTime objects.
bat_get_db_prefix Get the default database prefix.
bat_menu Implements hook_menu().
bat_permission Implements hook_permission().