You are here

opening_hours.module in Opening hours 6

Same filename and directory in other branches
  1. 7 opening_hours.module

Opening hours module.

File

opening_hours.module
View source
<?php

/**
 * @file
 * Opening hours module.
 */

/**
 * Implements hook_menu().
 */
function opening_hours_menu() {
  $include_path = drupal_get_path('module', 'opening_hours') . '/includes';
  $items = array();
  $items['node/%node/opening_hours'] = array(
    'title' => 'Opening hours',
    'page callback' => 'opening_hours_node_edit_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'opening_hours_node_edit_access',
    'access arguments' => array(
      1,
    ),
    'weight' => 3,
    'file' => 'opening_hours.pages.inc',
    'file path' => $include_path,
    'type' => MENU_LOCAL_TASK,
  );
  $items['opening_hours/instances'] = array(
    'page callback' => 'opening_hours_crud_api_page',
    'access callback' => TRUE,
    'file' => 'opening_hours.pages.inc',
    'file path' => $include_path,
    'type' => MENU_CALLBACK,
  );
  $items['opening_hours/instances/%opening_hours_instance'] = array(
    'page callback' => 'opening_hours_instance_id_api_page',
    'page arguments' => array(
      2,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'edit opening hours for content',
    ),
    'file' => 'opening_hours.pages.inc',
    'file path' => $include_path,
    'type' => MENU_CALLBACK,
  );
  $items['admin/content/opening_hours'] = array(
    'title' => 'Opening hours',
    'page callback' => 'opening_hours_admin_settings_page',
    'access arguments' => array(
      'administer opening hours configuration',
    ),
    'file' => 'opening_hours.admin.inc',
    'file path' => $include_path,
  );
  $items['admin/content/opening_hours/blocked_day/add'] = array(
    'title' => 'Add blocked day',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'opening_hours_admin_blocked_day_add_form',
    ),
    'access arguments' => array(
      'administer opening hours configuration',
    ),
    'file' => 'opening_hours.admin.inc',
    'file path' => $include_path,
  );
  $items['admin/content/opening_hours/blocked_day/%/delete'] = array(
    'title' => 'Delete blocked day',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'opening_hours_admin_blocked_day_delete_form',
      4,
    ),
    'access arguments' => array(
      'administer opening hours configuration',
    ),
    'file' => 'opening_hours.admin.inc',
    'file path' => $include_path,
  );
  return $items;
}

/**
 * Implements hook_block().
 */
function opening_hours_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      return array(
        'week' => array(
          'info' => t('Opening hours for node by week'),
          'cache' => BLOCK_NO_CACHE,
        ),
      );
      break;
    case 'view':
      $block = new stdClass();
      if ($node = menu_get_object()) {
        $block->title = t('Opening hours');
        $block->content = theme('opening_hours_' . $delta, $node);
      }
      return $block;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * We add our own fieldset to the node settings form, so the user can
 * enable opening hours for a node type there.
 */
function opening_hours_form_node_type_form_alter(&$form, &$form_state) {
  $form['opening_hours'] = array(
    '#type' => 'fieldset',
    '#title' => t('Opening hours'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['opening_hours']['opening_hours_enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable opening hours for this content type'),
    '#default_value' => variable_get('opening_hours_enabled_' . $form['#node_type']->type, FALSE),
  );
}

/**
 * Implements hook_perm().
 */
function opening_hours_perm() {
  return array(
    'edit opening hours for content',
    'administer opening hours configuration',
  );
}

/**
 * Implements hook_cron().
 */
function opening_hours_cron() {
  $last_monthly = variable_get('opening_hours_last_monthly_cron', 0);

  // If more than 30 days has passed since last cron, run it again.
  if ($last_monthly < $_SERVER['REQUEST_TIME'] - 30 * 86400) {

    // Delete repeating instance copies more than a week in the past.
    db_query("\n      DELETE FROM {opening_hours}\n      WHERE original_instance_id IS NOT NULL\n      AND customised = 0\n      AND date < '%s'\n    ", array(
      ':date' => date('Y-m-d', $_SERVER['REQUEST_TIME'] - 7 * 86400),
    ));

    // Propagate instances that are still repeating.
    $propagate_query = db_query("\n      SELECT * FROM {opening_hours}\n      WHERE repeat_end_date > '%s'\n      AND repeat_rule IS NOT NULL AND repeat_rule != ''\n    ", array(
      ':date' => date('Y-m-d', $_SERVER['REQUEST_TIME']),
    ));
    while ($instance = db_fetch_object($propagate_query)) {
      opening_hours_repeat_instance_propagate($instance);
    }
  }
  variable_set('opening_hours_last_monthly_cron', $_SERVER['REQUEST_TIME']);
}

/**
 * Implements hook_theme().
 */
function opening_hours_theme($existing, $type, $theme, $path) {
  return array(
    'opening_hours_admin' => array(
      'arguments' => array(),
      'path' => $path . '/templates',
      'template' => 'opening_hours_admin',
    ),
    'opening_hours_presentation' => array(
      'arguments' => array(),
      'path' => $path . '/templates',
      'template' => 'opening_hours_presentation',
    ),
    'opening_hours_week' => array(
      'arguments' => array(
        'node' => NULL,
      ),
      'path' => $path . '/templates',
      'template' => 'opening_hours_week',
    ),
  );
}

/**
 * Implements of hook_admin_theme().
 */
function opening_hours_admin_theme_options($op = 'info', $option = NULL) {
  switch ($op) {
    case 'info':
      return array(
        'opening_hours' => array(
          'title' => t('Opening hours'),
          'description' => t('Use the administration theme when administering the opening hours calender.'),
        ),
      );
    case 'check':
      return $option == 'opening_hours' && arg(0) == 'node' && arg(2) == 'opening_hours';
  }
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function opening_hours_ctools_plugin_directory($module, $plugin) {
  if ($module == 'ctools' && !empty($plugin)) {
    return "plugins/{$plugin}";
  }
}

/**
 * Access callback for the opening hours node edit page.
 */
function opening_hours_node_edit_access($node) {
  return user_access('edit opening hours for content') && variable_get('opening_hours_enabled_' . $node->type);
}

/**
 * Check if any opening hours has been input on a node.
 *
 * Used for hiding the opening_hours block on pages where it will never
 * display any data.
 *
 * @param integer $nid
 *   Node ID to check.
 *
 * @return boolean
 *   TRUE if opening hours are present, FALSE if not.
 */
function opening_hours_present_on_node($nid, $reset = FALSE) {
  static $presence = array();
  $verdict = FALSE;

  // Load Drupal’s cache if $presence array is empty.
  if (!$reset && empty($presence) && ($cache = cache_get('opening_hours_present_on_node'))) {
    if (is_array($cache->data)) {
      $presence = $cache->data;
    }
  }

  // Check the static cache.
  if (!isset($presence[$nid]) || $reset) {
    $presence[$nid] = (bool) db_result(db_query("\n      SELECT instance_id FROM {opening_hours} WHERE nid = %d LIMIT 1\n    ", array(
      ':nid' => $nid,
    )));
    cache_set('opening_hours_present_on_node', $presence);
  }
  return $presence[$nid];
}

/**
 * Load opening hours instance by id.
 */
function opening_hours_instance_load($instance_id) {
  $query = db_query("SELECT * FROM {opening_hours} WHERE instance_id = %d LIMIT 1", array(
    ':id' => $instance_id,
  ));
  if ($row = db_fetch_object($query)) {
    return opening_hours_instance_prepare($row);
  }
  return FALSE;
}

/**
 * Load opening hours instances by nid and date.
 */
function opening_hours_instance_load_multiple($nids, $from_date, $to_date) {

  // Make sure nids is an array.
  if (!is_array($nids)) {
    $nids = array(
      $nids,
    );
  }

  // Filter nids, so we don't pass nasty things to the database.
  array_filter($nids, 'is_numeric');
  array_filter($nids);
  $query = db_query("\n    SELECT * FROM {opening_hours} WHERE nid IN (" . implode(',', $nids) . ")\n    AND date BETWEEN '%s' AND '%s'\n    ORDER BY start_time\n  ", array(
    ':from_date' => $from_date,
    ':to_date' => $to_date,
  ));
  $results = array();
  while ($row = db_fetch_object($query)) {
    $results[] = opening_hours_instance_prepare($row);
  }
  return $results;
}

/**
 * Prepare an instance object loaded from the database for use with Backbone.
 */
function opening_hours_instance_prepare($instance) {

  // Cast integers to the correct type.
  $instance->instance_id = (int) $instance->instance_id;
  $instance->nid = (int) $instance->nid;
  $instance->original_instance_id = !empty($instance->original_instance_id) ? (int) $instance->original_instance_id : NULL;

  // Backbone expects the primary key to be named `id`. Let's not disappoint.
  $instance->id = $instance->instance_id;
  $instance->start_time = opening_hours_format_time($instance->start_time);
  $instance->end_time = opening_hours_format_time($instance->end_time);
  return $instance;
}

/**
 * Format a time value from the database, stripping the seconds.
 */
function opening_hours_format_time($time) {
  $matches = array();
  preg_match('/^([0-2]?\\d):([0-5]?\\d)/', $time, $matches);
  if (!empty($matches[1]) && !empty($matches[2])) {
    return $matches[1] . ':' . $matches[2];
  }
  return NULL;
}

/**
 * Propagates a repeating instance.
 *
 * Makes copies of the event each time it repeats until either the
 * repeat rule ends or two years have passed.
 */
function opening_hours_repeat_instance_propagate(&$instance) {

  // Maximum limit is about two years in the future.
  $limit = $_SERVER['REQUEST_TIME'] + 365 * 86400;

  // Set up the increment for the repeat rule.
  if ($instance->repeat_rule == 'weekly') {
    $increment = 7 * 86400;
  }
  if (!empty($instance->repeat_end_date)) {

    // Use noon on the date when converting to timestamp to dodge
    // daylight savings issues.
    $end_date = strtotime($instance->repeat_end_date . 'T12:00:00');

    // If the end date is before the limit, it becomes the new limit.
    if ($end_date && $end_date < $limit) {
      $limit = $end_date;
    }
  }

  // Bail if we don't have an increment.
  if (empty($increment) || $increment < 2) {
    return;
  }
  $current_date = strtotime($instance->date . 'T12:00:00');

  // Figure out how far the instance has already been propagated, and
  // start there.
  $start_point_date = db_result(db_query('
    SELECT MAX(date) FROM {opening_hours} WHERE original_instance_id = %d
  ', array(
    ':id' => $instance->instance_id,
  )));
  if ($start_point_date) {
    $start_point_date = strtotime($start_point_date . 'T12:00:00');

    // If our start point is later than the current date, use that when
    // iterating, so we don't generate duplicate entries.
    if ($start_point_date > $current_date) {
      $current_date = $start_point_date;
    }
  }
  while ($current_date < $limit) {
    $current_date += $increment;

    // Generate the new propagated instance.
    $propagated = (object) array(
      'nid' => $instance->nid,
      'date' => date('Y-m-d', $current_date),
      'start_time' => $instance->start_time,
      'end_time' => $instance->end_time,
      'original_instance_id' => $instance->instance_id,
      'customised' => 0,
    );

    // Propagate the notice, if set.
    if (!empty($instance->notice)) {
      $propagated->notice = $instance->notice;
    }
    drupal_write_record('opening_hours', $propagated);
  }
}

/**
 * Prevent additional propagation of instance.
 *
 * Sets the repeat end date of an instance to the date of the latest
 * existing instance to prevent additional propagation.
 */
function opening_hours_repeat_stop_propagation($instance_id) {

  // Get the date of the last non-deleted instance.
  $max_date = db_result(db_query('
    SELECT MAX(date) FROM {opening_hours} WHERE original_instance_id = %d
  ', array(
    ':id' => $instance_id,
  )));
  db_query("\n    UPDATE {opening_hours}\n    SET repeat_end_date = '%s'\n    WHERE instance_id = %d\n  ", array(
    ':date' => $max_date,
    ':id' => $instance_id,
  ));
}

/**
 * Helper function to load our JavaScript dependencies.
 */
function opening_hours_add_js($type = 'presentation', $nid = FALSE) {
  $files = array(
    'opening_hours.prototype.js',
    'opening_hours.core.js',
  );

  // If jQuery Update is configured to use development versions of
  // jQuery, it's probably safe to assume that we want development
  // versions of Backbone and Underscore.
  $uncompressed = variable_get('jquery_update_compression_type', '') == 'none';
  $files[] = $uncompressed ? 'underscore.js' : 'underscore-min.js';
  $settings = array(
    'blockedDays' => variable_get('opening_hours_blocked_days', array()),
    'firstDayOfWeek' => (int) variable_get('date_first_day', 1),
    // Options for the jQuery UI datepicker date formatter.
    'formatDate' => array(
      'monthNames' => array(
        t('January'),
        t('February'),
        t('March'),
        t('April'),
        t('May'),
        t('June'),
        t('July'),
        t('August'),
        t('September'),
        t('October'),
        t('November'),
        t('December'),
      ),
      'dayNames' => array(
        t('Sunday'),
        t('Monday'),
        t('Tuesday'),
        t('Wednesday'),
        t('Thursday'),
        t('Friday'),
        t('Saturday'),
      ),
    ),
  );

  // We need the datepicker plugin for formatting and selecting dates.
  date_popup_load();
  if ($type == 'admin') {

    // We use jQuery UI dialogs for editing opening hours.
    jquery_ui_add('ui.dialog');
    $files[] = $uncompressed ? 'backbone.js' : 'backbone-min.js';
    $files[] = 'opening_hours.models.js';
    $files[] = 'opening_hours.collections.js';
    $files[] = 'opening_hours.views.js';
    $files[] = 'opening_hours.routers.js';
    $files[] = 'opening_hours.admin.js';

    // For the admin page, we need the node ID, passed from the page callback.
    $settings['nid'] = $nid;
    $settings['path'] = base_path() . drupal_get_path('module', 'opening_hours');
  }
  elseif ($type == 'presentation') {
    $files[] = 'opening_hours.presentation.js';
  }
  $path = drupal_get_path('module', 'opening_hours');
  foreach ($files as $filename) {
    drupal_add_js($path . '/js/' . $filename);
  }
  drupal_add_js(array(
    'OpeningHours' => $settings,
  ), 'setting');
}

/**
 * Preprocess variables for the week template.
 */
function template_preprocess_opening_hours_week(&$vars) {
  static $once;

  // Only add JavaScript and templates the first time this is run on a page.
  if (!$once) {
    drupal_add_css(drupal_get_path('module', 'opening_hours') . '/css/opening_hours.theme.css');
    opening_hours_add_js();

    // Add our client-side templates to the page.
    $vars['preface'] = theme('opening_hours_presentation');
    $once = TRUE;
  }
}

Functions

Namesort descending Description
opening_hours_add_js Helper function to load our JavaScript dependencies.
opening_hours_admin_theme_options Implements of hook_admin_theme().
opening_hours_block Implements hook_block().
opening_hours_cron Implements hook_cron().
opening_hours_ctools_plugin_directory Implements hook_ctools_plugin_directory().
opening_hours_format_time Format a time value from the database, stripping the seconds.
opening_hours_form_node_type_form_alter Implements hook_form_FORM_ID_alter().
opening_hours_instance_load Load opening hours instance by id.
opening_hours_instance_load_multiple Load opening hours instances by nid and date.
opening_hours_instance_prepare Prepare an instance object loaded from the database for use with Backbone.
opening_hours_menu Implements hook_menu().
opening_hours_node_edit_access Access callback for the opening hours node edit page.
opening_hours_perm Implements hook_perm().
opening_hours_present_on_node Check if any opening hours has been input on a node.
opening_hours_repeat_instance_propagate Propagates a repeating instance.
opening_hours_repeat_stop_propagation Prevent additional propagation of instance.
opening_hours_theme Implements hook_theme().
template_preprocess_opening_hours_week Preprocess variables for the week template.