You are here

calendar_systems.module in Calendar Systems 6.2

Contains Calendar Systems module hooks, helpers and API functions.

@todo

  • Drush integration.
  • Implement alterers.
  • Add Drush make file.
  • Add Watchdog loggings.
  • Implement the boring hook_help().
  • Add date FAPI element integration.
  • Procedural and OOP API designation.
  • Implement input validation for non-gregorian dates.
  • All calnedar internal callbacks should be invoked via calendar_systems_call() API.
  • Allow submodules to define their callbacks in their desired path, and set the path in their implementation of hook_calendar_info();

File

calendar_systems.module
View source
<?php

/**
 * @file
 * Contains Calendar Systems module hooks, helpers and API functions.
 *
 * @todo
 *   - Drush integration.
 *   - Implement alterers.
 *   - Add Drush make file.
 *   - Add Watchdog loggings.
 *   - Implement the boring hook_help().
 *   - Add date FAPI element integration.
 *   - Procedural and OOP API designation.
 *   - Implement input validation for non-gregorian dates.
 *   - All calnedar internal callbacks should be invoked
 *     via calendar_systems_call() API.
 *   - Allow submodules to define their callbacks in their
 *     desired path, and set the path in their implementation of
 *     hook_calendar_info();
 */

/**
 * Implements hook_perm().
 */
function calendar_systems_perm() {
  return array(
    // Authenticated users:
    'select different calendar',
    // Administerator users:
    'administer calendar systems',
  );
}

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

  // Calendars listing form.
  $items['admin/settings/date-time/calendars'] = array(
    'title' => 'Calendars',
    'description' => 'Configuration options for all available calendars.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'calendar_systems_calendars_form',
    ),
    'access arguments' => array(
      'administer calendar systems',
    ),
    'file' => 'calendar_systems.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 3,
  );

  // A calendar configuration form.
  $items['admin/settings/date-time/calendars/%'] = array(
    'title callback' => 'calendar_systems_calendar_title',
    'title arguments' => array(
      4,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'calendar_systems_calendar_form',
      4,
    ),
    'access arguments' => array(
      'administer calendar systems',
    ),
    'file' => 'calendar_systems.admin.inc',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function calendar_systems_menu_alter($items) {

  // Set the date/time settings page as the default local task.
  $items['admin/settings/date-time/date-time'] = $items['admin/settings/date-time'];
  $items['admin/settings/date-time/date-time']['type'] = MENU_DEFAULT_LOCAL_TASK;
  $items['admin/settings/date-time']['type'] = MENU_NORMAL_ITEM;
}

/**
 * Calendar menu title callback.
 *
 * @param $identifier
 *   Calendar identifier.
 *
 * @return
 *   Calendar menu title.
 */
function calendar_systems_calendar_title($identifier) {
  return sprintf('%s Calendar', calendar_systems_calendars('name', $identifier));
}

/**
 * Implements hook_patch().
 *
 * @see http://drupal.org/project/patchdoq
 * @see http://doques.net/node/367
 */
function calendar_systems_patch() {

  // Include boring patch helpers.
  module_load_include('patch.inc', 'calendar_systems');
  $patchdoq = array();
  $patches = _calendar_systems_patches();

  // Build the array as per required by patchdoq.
  foreach ($patches as $identifier => $patch) {
    $patchdoq[$identifier] = array(
      'name' => $patch['title'],
      'description' => $patch['description'],
      'file' => $patch['path'] . $patch['name'],
      'patch arguments' => '-p0',
    );
  }
  return $patchdoq;
}

/**
 * Implements hook_format_date().
 *
 * This is a temporary hook implementation which employs a procedural concept of
 * Factory design pattern until we all get a better format_date() function at Drupal core.
 * Till those days we need to patch the includes/common.inc file to provide
 * the ability to implement hook_format_date().
 *
 * @param $timestamp
 *   Unix timestamp to be formatted.
 *   The proper timezone value has been added to this by format_date().
 * @param $timezone
 *   Timezone offset in seconds.
 * @param $format
 *   PHP date() function format string.
 * @param $langcode
 *   Optional language code to translate to a language other than the default.
 *
 * @return
 *   The formatted date or FALSE otherwise.
 *
 * @see format_date()
 * @see calendar_systems_patch()
 * @see patch/common.inc-format_date.d6.patch
 */
function calendar_systems_format_date($timestamp, $format, $timezone = NULL, $langcode = NULL) {

  // Cherrypick the best-fit calendar for current user/language
  // combination and call its formatter callback, if available.
  $calendar = calendar_systems_pick();
  if (isset($calendar['format callback']) && function_exists($calendar['format callback'])) {
    return $calendar['format callback']($timestamp, $format, $timezone, $langcode, $calendar);
  }

  // Unexpected failure! Falling back to format_date() itself.
  return FALSE;
}

/**
 * Implements hook_theme().
 */
function calendar_systems_theme() {
  return array(
    'calendar_systems_calendars_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'file' => 'calendar_systems.admin.inc',
    ),
    'calendar_systems_languages_overview_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'file' => 'calendar_systems.admin.inc',
    ),
  );
}

/**
 * Implements hook_user().
 *
 * Adds user specific calendar configuration.
 */
function calendar_systems_user($op, &$edit, &$account, $category = NULL) {
  if ($op == 'form' && $category == 'account' && user_access('select different calendar')) {
    $form = array();

    // Get all avilable calendars names.
    $calendars = calendar_systems_calendars('names');

    // Add a dropdown widget under timezone
    // fieldset, so that she can choose her fave.
    // The value of user specific calendar will be
    // available in $account->calendar property.
    $form['timezone']['calendar'] = array(
      '#type' => 'select',
      '#title' => t('Default calendar'),
      '#description' => t('Selecting a different calendar will change all dates across the site.'),
      '#access' => user_access('select different calendar'),
      '#options' => $calendars += array(
        'default' => t('Site default calendar'),
      ),
      '#default_value' => $account->calendar ? $account->calendar : 'default',
    );

    // Render.
    return $form;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters locale_languages_overview_form to inject calendar options.
 */
function calendar_systems_form_locale_languages_overview_form_alter(&$form, $form_state) {

  // Get a list of available calendar names.
  $calendars = calendar_systems_calendars('names');

  // For each language add a dropdown list,
  // to let the admin choose her fave calendar.
  $form['calendar_systems_languages'] = array(
    '#tree' => TRUE,
  );
  foreach (array_keys($form['enabled']['#options']) as $lang) {
    $form['calendar_systems_languages'][$lang] = array(
      '#type' => 'select',
      '#access' => user_access('administer calendar systems'),
      '#options' => $calendars += array(
        'default' => t('Default Calendar'),
      ),
      '#default_value' => variable_get('calendar_systems_' . $lang . '_calendar', 'default'),
    );
  }

  // Overwrite the default themer callback.
  $form['#theme'] = 'calendar_systems_languages_overview_form';

  // And append a submission one.
  $form['#submit'][] = 'calendar_systems_languages_overview_form_submit';
}

/**
 * Submission callback for Locale language overview form.
 */
function calendar_systems_languages_overview_form_submit($form, &$form_state) {

  // Save the choosen calendar for a specific locale in a
  // "calendar_systems_{LANGUAGE}_calendar" formatted db variable.
  foreach ($form_state['values']['calendar_systems_languages'] as $lang => $calendar) {
    variable_set('calendar_systems_' . $lang . '_calendar', $calendar);
  }
}

/**
 * API function to provide a list of available calendars.
 *
 * @param $op
 *   The format of return list. Allowed values are:
 *   - calendar: Returns information about calendar identified by $identifier.
 *   - calendars: Returns a list of all available calendar informations.
 *   - name: Return the name of a calendar identified by $identifier.
 *   - names: Returns a list of all available calendar names.
 *   - identifiers: Returns a list of available calendar identifiers.
 * @param $identifier
 *   A calendar identifier to filter the results.
 *
 * @return
 *   An array of calendars or a single calendar.
 *
 * @see _calendar_systems_build()
 */
function calendar_systems_calendars($op = 'calendars', $identifier = NULL) {
  list($calendars, $names) = _calendar_systems_build();
  switch ($op) {

    // Return a array of identifier keyed calendars information.
    case 'calendars':
      return $calendars;

    // Return the information about the calendar specified by $identifier.
    case 'calendar':
      return is_array($calendars[$identifier]) ? array_merge($calendars[$identifier], array(
        'identifier' => $identifier,
      )) : NULL;

    // Return an array of all available calendar names.
    case 'names':
      return $names;

    // Return the name of the calendar specified by $identifier.
    case 'name':
      return $names[$identifier];

    // Return an array of all available calendar identifiers.
    case 'identifiers':
      return array_keys($calendars);
  }
}

/**
 * API function to get current default calendar.
 *
 * @return
 *   An array of default calendar information.
 */
function calendar_systems_default() {

  // Return the system default calendar, use gregorian calendar as fallback.
  return calendar_systems_calendars('calendar', variable_get('calendar_systems_default_calendar', 'gregorian'));
}

/**
 * API function which selects the best-fit calendar for the environment.
 *
 * At the first level it checks current user calendar settings,
 * and then the locale specific calendar settings. If there were no
 * user/locale specific settings, the default calendar will be chosen.
 *
 * @return
 *   Calendar identifier.
 */
function calendar_systems_pick() {
  global $user;
  global $language;

  // Calendar per user.
  if (isset($user->calendar) && $user->calendar != 'default' && user_access('select different calendar')) {
    return calendar_systems_calendars('calendar', $user->calendar);
  }

  // Calendar per language.
  if ($calendar = variable_get('calendar_systems_' . $language->language . '_calendar', FALSE)) {
    return $calendar == 'default' ? calendar_systems_default() : calendar_systems_calendars('calendar', $calendar);
  }

  // Default calendar.
  return calendar_systems_default();
}

/**
 * API function to call a calendar's internal routine.
 *
 * @param $function
 *   Function "alias" to call.
 * @param $pick
 *   Whether to cherrypick the calendar or just return the
 *   default one in case that the $calendar is not passed.
 * @param $args
 *   An array of arguments to be passed to $function.
 * @param $calendar
 *   Calendar identifier to call its $function callback.
 *
 * @retrun
 *   The returned value of called calendar function or NULL on fail.
 *
 * @see calendar_systems_pick()
 * @see calendar_systems_default()
 */
function calendar_systems_call($function, $pick = TRUE, $args = array(), $calendar = '') {

  // Cherrypick the best-fit calendar if requsted so.
  $calendar = empty($calendar) ? $pick ? calendar_systems_pick() : calendar_systems_default() : calendar_systems_calendars('calendar', $calendar);

  // Call the calendar callback, if available.
  if (isset($calendar[$function]) && function_exists($calendar[$function])) {
    return call_user_func_array($calendar[$function], $args);
  }

  // Cry otherwise!
  return NULL;
}

/**
 * Helper function to build an array of defined calendars.
 *
 * @return
 *   A 2D array of available calendars info and names.
 */
function _calendar_systems_build() {
  $names = $calendars = array();
  $calendars_defined = module_invoke_all('calendar_info');
  foreach ($calendars_defined as $identifier => $info) {

    // Skip lazy modules without a format callback.
    if (!isset($info['format callback']) || !function_exists($info['format callback'])) {
      continue;
    }

    // Loadup the calendar configs.
    $info['config'] = variable_get('calendar_systems_settings_' . $identifier, '');
    $calendars[$identifier] = $info;
    $names[$identifier] = $info['name'];
  }
  return array(
    $calendars,
    $names,
  );
}

/**
 * API function acting as a fallback for 3rdparty Calendar Systems modules.
 *
 * Modules formatter callbacks that does not define a particular
 * format should pass the format to this API function, so the timestamp
 * will get formatted by the default formatter.
 *
 * @param $timestamp
 *   Unix timestamp to be formatted.
 * @param $format
 *   Date format as required by PHP date().
 * @param $timezone
 *   Time zone offset in seconds.
 *
 * @return
 *   Formatted date based on the passed $format.
 *
 * @todo
 *   - Pass it through the format_date() once it got improved.
 */
function calendar_systems_formatter($timestamp, $format, $timezone = 0) {
  $date = '';
  $max = strlen($format);
  for ($i = 0; $i < $max; $i++) {
    $c = $format[$i];
    if (strpos('AaDlM', $c) !== FALSE) {
      $date .= t(gmdate($c, $timestamp), array(), $langcode);
    }
    elseif ($c == 'F') {
      $date .= trim(t('!long-month-name ' . gmdate($c, $timestamp), array(
        '!long-month-name' => '',
      ), $langcode));
    }
    elseif (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
      $date .= gmdate($c, $timestamp);
    }
    elseif ($c == 'r') {
      $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode);
    }
    elseif ($c == 'O') {
      $date .= sprintf('%s%02d%02d', $timezone < 0 ? '-' : '+', abs($timezone / 3600), abs($timezone % 3600) / 60);
    }
    elseif ($c == 'Z') {
      $date .= $timezone;
    }
    elseif ($c == '\\') {
      $date .= $format[++$i];
    }
    else {
      $date .= date($c, $timestamp);
    }
  }
  return $date;
}

/**
 * Implements hook_calendar_info().
 *
 * This is the calendar_gregorian.module built into the
 * calendar_systems.module, So we got a default fake Gregorian calendar.
 *
 * @return
 *   An array of Gregorian calendar information.
 */
function calendar_systems_calendar_info() {
  $calendar = array();
  $calendar['gregorian'] = array(
    'name' => t('Gregorian'),
    'format callback' => 'calendar_gregorian_formatter',
    'month days callback' => 'calendar_gregorian_month_days',
  );
  return $calendar;
}

/**
 * Gregorian calendar (fake) date formatter callback.
 *
 * @param $timestamp
 *   Unix timestamp to be formatted.
 *   The proper timezone value has been added by the native format_date().
 * @param $type
 *   The format to use. Can be "small", "medium" or "large" for the preconfigured
 *   date formats. If "custom" is specified, then $format is required as well.
 * @param $format
 *   PHP date() function format string.
 * @param $langcode
 *   Optional language code to translate to a language other than the default.
 * @param $calendar
 *   A copy of calendar information.
 *
 * @return
 *   The formatted date or FALSE otherwise.
 *
 * @see format_date()
 */
function calendar_gregorian_formatter($timestamp, $type, $format, $langcode, $calendar) {
  return FALSE;
}

/**
 * Calendar month days helper. Also acts as a fallback
 * for a missing calendar_module_month_days() callback.
 *
 * @return
 *   An array of ordered numbers corresponding
 *   to a Gregorian year monthes length of days.
 */
function calendar_gregorian_month_days() {
  return array(
    31,
    28,
    31,
    30,
    31,
    30,
    31,
    31,
    30,
    31,
    30,
    31,
  );
}

Functions

Namesort descending Description
calendar_gregorian_formatter Gregorian calendar (fake) date formatter callback.
calendar_gregorian_month_days Calendar month days helper. Also acts as a fallback for a missing calendar_module_month_days() callback.
calendar_systems_calendars API function to provide a list of available calendars.
calendar_systems_calendar_info Implements hook_calendar_info().
calendar_systems_calendar_title Calendar menu title callback.
calendar_systems_call API function to call a calendar's internal routine.
calendar_systems_default API function to get current default calendar.
calendar_systems_formatter API function acting as a fallback for 3rdparty Calendar Systems modules.
calendar_systems_format_date Implements hook_format_date().
calendar_systems_form_locale_languages_overview_form_alter Implements hook_form_FORM_ID_alter().
calendar_systems_languages_overview_form_submit Submission callback for Locale language overview form.
calendar_systems_menu Implements hook_menu().
calendar_systems_menu_alter Implements hook_menu_alter().
calendar_systems_patch Implements hook_patch().
calendar_systems_perm Implements hook_perm().
calendar_systems_pick API function which selects the best-fit calendar for the environment.
calendar_systems_theme Implements hook_theme().
calendar_systems_user Implements hook_user().
_calendar_systems_build Helper function to build an array of defined calendars.