You are here

office_hours.module in Office Hours 5

Creates a field and widget for inserting working or office hours per day

implementation of hook_menu.

File

office_hours.module
View source
<?php

/**
* @file
* Creates a field and widget for inserting working or office hours per day
*
* 
*

/**
* implementation of hook_menu.
*/
function office_hours_menu($may_cache) {
  if (!$may_cache) {
    drupal_add_css(drupal_get_path('module', 'office_hours') . '/office_hours.css');
  }
}

/**
 * Implementation of hook_field_info
 *
 * @return
 *   An array keyed by field type name. Each element of the array is an associative
 *   array with these keys and values:
 *   - "label": The human-readable label for the field type.
 */
function office_hours_field_info() {
  return array(
    'office_hours' => array(
      'label' => 'Office Hours',
    ),
  );
}

/**
 * implementation of hook_field 
 * Handle the parameters for a field.
 *
 * @param $op
 * @param $field
 *   The field on which the operation is to be performed.
 * @return
 *   This varies depending on the operation.
 *   - "form": an array of form elements to add to
 *     the settings page.
 *   - "validate": no return value. Use form_set_error().
 *   - "save": an array of names of form elements to
 *     be saved in the database.
 *   - "database columns": an array keyed by column name, with arrays of column
 *     information as values.
 *   - "callbacks": an array describing the field's behaviour regarding hook_field
 *     operations. The array is keyed by hook_field operations ('view', 'validate'...)
 *     and has the following possible values :
 *       CONTENT_CALLBACK_NONE     : do nothing for this operation
 *       CONTENT_CALLBACK_CUSTOM   : use the behaviour in hook_field(operation)
 *       CONTENT_CALLBACK_DEFAULT  : use content.module's default bahaviour
 *     Note : currently only the 'view' operation implements this feature.
 *   - "tables": an array of 'tables' definitions as expected by views.module
 *     (see Views Documentation).
 *   - "arguments": an array of 'arguments' definitions as expected by views.module
 *     (see Views Documentation).
 *   - "filters": an array of 'filters' definitions as expected by views.module
 *     (see Views Documentation).
 *     When providing several filters, it is recommended to use the 'name'
 *     attribute in order to let the user distinguish between them. If no 'name'
 *     is specified for a filter, the key of the filter will be used instead.
 */
function office_hours_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $options = _create_hours_arr($field, FALSE);
      $optlim = _create_hours_arr($field);
      $form = array();
      $form['hoursformat'] = array(
        '#type' => 'select',
        '#title' => t('Hours format'),
        '#options' => array(
          t('24 hrs.'),
          t('12 hrs'),
        ),
        '#default_value' => $field['hoursformat'] ? $field['hoursformat'] : 0,
        '#required' => FALSE,
        '#description' => t('Format of the clock. IMPORTANT NOTE: if you do not select "Multiple values", you can enter only one day.'),
      );
      $form['granularity'] = array(
        '#type' => 'select',
        '#title' => t('granularity of time'),
        '#options' => array(
          0 => t('Hours'),
          30 => t('Half hours'),
          15 => t('Quarter hours'),
        ),
        '#default_value' => $field['granularity'] ? $field['granularity'] : 0,
        '#required' => FALSE,
        '#description' => t('Allow inserting quarter hours, half hours, or only hours of the day'),
      );
      $form['limitstart'] = array(
        '#type' => 'select',
        '#title' => t('Limit widget start hours'),
        '#options' => $options,
        '#default_value' => $field['limitstart'] ? $field['limitstart'] : FALSE,
        '#required' => FALSE,
        '#description' => t('Set the allowed start hours. Note: if you just changed the clock format, please submit before changing this.'),
      );
      $form['limitend'] = array(
        '#type' => 'select',
        '#title' => t('Limit widget end hours'),
        '#options' => $options,
        '#default_value' => $field['limitend'] ? $field['limitend'] : FALSE,
        '#required' => FALSE,
        '#description' => t('Set the allowed end hours. Note: if you just changed the clock format, please submit before changing this'),
      );
      $form['defaultstart'] = array(
        '#type' => 'select',
        '#title' => t('Start hours default'),
        '#options' => $optlim,
        '#default_value' => $field['defaultstart'] ? $field['defaultstart'] : FALSE,
        '#required' => FALSE,
        '#description' => t('Use this field if you want for specific hours to show by default.'),
      );
      $form['defaultend'] = array(
        '#type' => 'select',
        '#title' => t('End hours default'),
        '#options' => $optlim,
        '#default_value' => $field['defaultend'] ? $field['defaultend'] : FALSE,
        '#required' => FALSE,
        '#description' => t('Use this field if you want for specific hours to show by default.'),
      );
      $form['valhrs'] = array(
        '#type' => 'checkbox',
        '#title' => t('Validate hours'),
        //'#options' => array('validatehrs'=> t('Check that closing hours are not earlier than opening hours')),
        '#required' => FALSE,
        '#default_value' => isset($field['valhrs']) ? $field['valhrs'] : 0,
        '#description' => t('Check that closing hours are not earlier than opening hours. Please note that this will work as long as the opening hours are not through midnight.'),
      );
      return $form;
    case 'validate':
      if ($field['defaultend'] != 'none' && $field['defaultstart'] != 'none') {
        if (tf_to_mil($field['defaultend']) <= tf_to_mil($field['defaultstart'])) {
          form_set_error('defaultend', 'Default ending hours are earlier than start hours');
        }
      }
      if ($field['limitend'] != 'none' && $field['limitstart'] != 'none') {
        if (tf_to_mil($field['limitend']) <= tf_to_mil($field['limitstart'])) {
          form_set_error('limitend', 'Limit ending hours are earlier than start hours');
        }
      }
      break;
    case 'save':
      return array(
        'hoursformat',
        'granularity',
        'limitstart',
        'limitend',
        'defaultstart',
        'defaultend',
        'valhrs',
      );
    case 'database columns':
      $columns = array(
        'day' => array(
          'type' => 'int',
          'length' => 1,
          'unsigned' => TRUE,
        ),
        'starthours' => array(
          'type' => 'varchar',
          'length' => 10,
          'not null' => FALSE,
          'sortable' => TRUE,
        ),
        'endhours' => array(
          'type' => 'varchar',
          'length' => 10,
          'not null' => FALSE,
          'sortable' => TRUE,
        ),
      );
      return $columns;
    case 'callbacks':
      return array(
        'view' => CONTENT_CALLBACK_CUSTOM,
      );
    case 'tables':
      $tables = content_views_field_tables($field);
      return $tables;
    case 'arguments':
      $arguments = content_views_field_arguments($field);
      return $arguments;
    case 'filters':
      return array(
        'substring' => array(
          'operator' => 'views_handler_operator_like',
          'handler' => 'views_handler_filter_like',
        ),
        'alpha_order' => array(
          'name' => 'alphabetical order',
          'operator' => 'views_handler_operator_gtlt',
        ),
      );
  }
}

/**
 * implementation of hook_field
 * Define the behavior of a field type.
 *
 * @param $op
 *   What kind of action is being performed. Possible values.
 * @param &$node
 *   The node the action is being performed on. This argument is passed by
 *   reference for performance only; do not modify it.
 * @param $field
 *   The field the action is being performed on.
 * @param &$node_field
 *   The contents of the field in this node. Changes to this variable will
 *   be saved back to the node object.
 * @return
 *   This varies depending on the operation.
 *   - The "load" operation should return an object containing extra values
 *     to be merged into the node object.
 *   - The "view" operation should return a string containing an HTML
 *     representation of the field data.
 *   - The "insert", "update", "delete", "validate", and "submit" operations
 *     have no return value.
 *
 * In most cases, only "validate" operations is relevant ; the rest
 * have default implementations in content_field() that usually suffice.
 */
function office_hours_field($op, &$node, $field, &$node_field, $teaser, $page) {
  switch ($op) {
    case 'view':
      $context = $teaser ? 'teaser' : 'full';
      $formatter = isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default';
      $items = array();
      foreach ($node_field as $delta => $item) {
        $items[$delta]['view'] = content_format($field, $item, $formatter, $node);
      }
      return theme('field', $node, $field, $items, $teaser, $page);
    case 'validate':
      if ($field['valhrs'] == 1) {
        if (is_array($node_field)) {
          foreach ($node_field as $delta => $item) {
            $error_field = $field['field_name'] . '][' . $delta . '][endhours';
            $starthours = tf_to_mil($item['starthours']);
            $endhours = tf_to_mil($item['endhours']);
            if ($starthours >= $endhours) {
              form_set_error($error_field, t('Closing hours are earlier than opening hours'));
            }
          }
        }
      }
      break;
  }
}

/**
 * Declare information about a formatter.
 *
 * @return
 *   An array keyed by formatter name. Each element of the array is an associative
 *   array with these keys and values:
 *   - "label": The human-readable label for the formatter.
 *   - "field types": An array of field type names that can be displayed using
 *     this formatter.
 */
function office_hours_field_formatter_info() {
  return array(
    'default' => array(
      'label' => 'default',
      'field types' => array(
        'office_hours',
      ),
    ),
  );
}

/**
 * implementation of hook_field_formatter
 * Prepare an individual item for viewing in a browser.
 *
 * @param $field
 *   The field the action is being performed on.
 * @param $item
 *   An array, keyed by column, of the data stored for this item in this field.
 * @param $formatter
 *   The name of the formatter being used to display the field.
 * @param $node
 *   The node object, for context. Will be NULL in some cases.
 *   Warning : when displaying field retrieved by Views, $node will not
 *   be a "full-fledged" node object, but an object containg the data returned
 *   by the Views query (at least nid, vid, changed)
 * @return
 *   An HTML string containing the formatted item.
 *
 * It is important that this function at the minimum perform security
 * transformations such as running check_plain() or check_markup().
 */
function office_hours_field_formatter($field, $item, $formatter, $node) {
  static $count = 0;
  static $lastday = 7;
  if ($count > 0) {

    //this is not the first iteration.
    if ($lastday == $item['day']) {
      $display = 'hide';
    }
    else {
      $display = 'show';
    }
  }
  $count++;
  $lastday = $item['day'];
  if (isset($item['day'])) {
    $daykey = check_plain($item['day']);
    $days = array(
      0 => t('Sunday'),
      1 => t('Monday'),
      2 => t('Tuesday'),
      3 => t('Wednesday'),
      4 => t('Thursday'),
      5 => t('Friday'),
      6 => t('Saturday'),
    );
    $day = $days[$daykey];
  }
  else {
    $day = '';
  }
  $endhrs = check_plain($item['endhours']);
  $strhrs = check_plain($item['starthours']);
  if ($field['hoursformat'] == 1) {
    $endhrs = convert_to_ampm($endhrs);
    $strhrs = convert_to_ampm($strhrs);
  }
  switch ($formatter) {
    default:
      return theme('oh_formatter_display', $day, $strhrs, $endhrs, $display);
  }
}

/**
 * Declare information about a widget.
 *
 * @return
 *   An array keyed by widget name. Each element of the array is an associative
 *   array with these keys and values:
 *   - "label": The human-readable label for the widget.
 *   - "field types": An array of field type names that can be edited using
 *     this widget.
 */
function office_hours_widget_info() {
  return array(
    'default' => array(
      'label' => t('Office Hours'),
      'field types' => array(
        'office_hours',
      ),
    ),
  );
}

/**
 *  * Implementation of hook_widget().
 *
 * @param $op
 *   What kind of action is being performed. Possible values:
 * @param &$node
 *   The node the action is being performed on. This argument is passed by
 *   reference for performance only; do not modify it.
 * @param $field
 *   The field the action is being performed on.
 * @param &$node_field
 *   The contents of the field in this node. Changes to this variable will
 *   be saved back to the node object.
 * @return
 *   This varies depending on the operation.
 *   - The "form" operation should return an array of form elements to display.
 *   - Other operations have no return value.
 */
function office_hours_widget($op, &$node, $field, &$node_field) {
  switch ($op) {
    case 'prepare form values':
      $items = array();
      foreach ($node_field as $key => $item) {
        $day = $item['day'];
        $delta = $day * 2;

        //0,2,4,6,8,10,12
        $deltaplus = $delta + 1;

        //1,3,5,7,9,11,13
        if (!isset($items[$delta])) {
          $items[$delta] = $item;
        }
        else {
          $items[$deltaplus] = $item;
        }
      }
      $node_field = $items;
      break;
    case 'form':
      $ophours = _create_hours_arr($field, TRUE);
      $opdays = array(
        t('Sunday'),
        t('Monday'),
        t('Tuesday'),
        t('Wednesday'),
        t('Thursday'),
        t('Friday'),
        t('Saturday'),
      );
      $form = array();
      $form[$field['field_name']] = array(
        '#tree' => TRUE,
      );
      $form[$field['field_name']]['#type'] = 'fieldset';
      $form[$field['field_name']]['#title'] = t($field['widget']['label']);
      $form[$field['field_name']]['#description'] = t('Mark the days you want to show and set the hours accordingly');
      $form[$field['field_name']]['#attributes'] = array(
        'class' => 'office-container-inline',
      );
      foreach (range(0, 6) as $day) {

        //day is 0,1,2,3,4,5,6
        if ($field['multiple']) {
          $delta = $day * 2;

          //0,2,4,6,8,10,12
          $deltaplus = $delta + 1;

          //1,3,5,7,9,11,13
        }
        else {
          $delta = $day;
        }
        $form[$field['field_name']][$delta]['day'] = array(
          '#type' => 'value',
          '#value' => $day,
        );
        $form[$field['field_name']][$delta]['daycheck'] = array(
          '#type' => 'checkbox',
          '#title' => $opdays[$day],
          '#description' => $field['widget']['description'],
          '#default_value' => isset($node_field[$delta]['day']) ? 1 : 0,
          '#prefix' => '<div class="office-hours-block">',
        );
        $form[$field['field_name']][$delta]['starthours'] = array(
          '#type' => 'select',
          '#title' => '',
          '#options' => $ophours,
          '#required' => $field['required'],
          '#description' => $field['widget']['description'],
          '#prefix' => '<div class ="oh-toggle">',
          '#default_value' => isset($node_field[$delta]['starthours']) ? $node_field[$delta]['starthours'] : $field['defaultstart'],
        );
        $form[$field['field_name']][$delta]['endhours'] = array(
          '#type' => 'select',
          '#title' => '',
          '#options' => $ophours,
          '#required' => $field['required'],
          '#description' => $field['widget']['description'],
          '#default_value' => isset($node_field[$delta]['endhours']) ? $node_field[$delta]['endhours'] : $node_field['defaultend'],
        );
        if ($field['multiple']) {
          $form[$field['field_name']][$delta]['morelink'] = array(
            '#value' => t('Additional hours block:'),
            '#prefix' => '<span class ="morelink">',
            '#suffix' => '</span>',
          );
          $form[$field['field_name']][$deltaplus]['day'] = array(
            '#type' => 'value',
            '#value' => $day,
          );
          $form[$field['field_name']][$deltaplus]['starthours'] = array(
            // from more link
            '#type' => 'select',
            '#title' => '',
            '#options' => $ophours,
            '#description' => $field['widget']['description'],
            '#prefix' => '<div class ="oh-toggle-more">',
            '#default_value' => isset($node_field[$deltaplus]['starthours']) ? $node_field[$deltaplus]['starthours'] : '',
          );
          $form[$field['field_name']][$deltaplus]['endhours'] = array(
            //from more link
            '#type' => 'select',
            '#title' => '',
            '#options' => $ophours,
            '#description' => $field['widget']['description'],
            '#default_value' => isset($node_field[$deltaplus]['endhours']) ? $node_field[$deltaplus]['endhours'] : '',
            '#suffix' => '</div></div></div>',
          );
        }
      }
      return $form;
    case 'process form values':
      foreach ($node_field as $delta => $item) {
        if ($item['day'] == NULL && ($delta == 0 || $delta == 1)) {

          //adjust for sunday.
          $node_field[$delta]['day'] = '0';
        }
        if ($item['daycheck'] == 0 && $delta % 2 == 0) {
          unset($node_field[$delta]);
        }
        if ($item['starthours'] == 'none' || $item['endhours'] == 'none') {
          unset($node_field[$delta]);
        }
      }
      break;
  }
}

/**
 * helper function to create hours array.
 * items are saved in 24 hours string format (i.e '18:00').
 */
function _create_hours_arr($field, $limit = TRUE) {
  $fl1 = $field['limitstart'];
  $fl2 = $field['limitend'];
  $gran = $field['granularity'] == 0 ? 60 : $field['granularity'];
  $limst = 0;
  $limend = 23;
  $mins = '00';
  $mine = 60;
  if ($limit === TRUE) {
    if ($fl1 != 'none') {
      list($limst, $mins) = explode(":", $fl1);
    }
    if ($fl2 != 'none') {
      list($limend, $mine) = explode(":", $fl2);
    }
  }
  $ophours['none'] = t('None');
  if ($field['hoursformat'] == 1) {

    //12-hour clock
    foreach (range($limst, $limend) as $i) {
      $mincounter = $i == $limst ? $mins : 0;

      //in the first iteration, start the count according to limst. if not, begin from 0
      for (; $mincounter <= 45; $mincounter = $mincounter + $gran) {
        if (!($i == $limend && $mincounter > $mine)) {

          //last iteration
          $mincounter = $mincounter == 0 ? '00' : $mincounter;

          //preparing the mincounter for use in a string
          $hr = $i . ':' . $mincounter;
          $ophours[$hr] = convert_to_ampm($hr);
        }
      }
    }
  }
  else {

    //24-hour clock
    foreach (range($limst, $limend) as $i) {
      $mincounter = $i == $limst ? $mins : 0;
      for (; $mincounter <= 45; $mincounter = $mincounter + $gran) {
        if (!($i == $limend && $mincounter > $mine)) {
          $mincounter = $mincounter == 0 ? '00' : $mincounter;
          $hr = $i . ':' . $mincounter;
          $ophours[$hr] = $hr;
        }
      }
    }
  }
  return $ophours;
}

/**
 * Helper function for conversion of clock format.
 */
function convert_to_ampm($hour) {
  list($hr, $min) = explode(":", $hour);
  if ($hr == '0') {

    // midnight
    $hr = 12;
    $ampm = ' AM';
  }
  elseif ($hr == 12) {

    // noon
    $hr = 12;
    $ampm = ' PM';
  }
  elseif ($hr > 12 && $hr < 24) {

    // a pm time
    $hr = $hr - 12;
    $ampm = ' PM';
  }
  else {
    $ampm = ' AM';
  }
  return $hr . ':' . $min . $ampm;
}
function tf_to_mil($hour) {
  list($hr, $min) = explode(":", $hour);
  $hr = $hr * 60 + $min;
  return $hr;
}

/**
 * Theme function for date node display
 */
function theme_oh_formatter_display($day, $strhrs, $endhrs, $display) {
  $display = !isset($display) ? 'show' : $display;
  return '<div class="oh-display"><span class="oh-display-day-' . $display . '">' . $day . '</span>
 <span class="oh-display-start">' . $strhrs . '</span>
 <span class="oh-display-separator"> - </span>
 <span class="oh-display-end">' . $endhrs . '</span></div>';
}

Functions

Namesort descending Description
convert_to_ampm Helper function for conversion of clock format.
office_hours_field implementation of hook_field Define the behavior of a field type.
office_hours_field_formatter implementation of hook_field_formatter Prepare an individual item for viewing in a browser.
office_hours_field_formatter_info Declare information about a formatter.
office_hours_field_info Implementation of hook_field_info
office_hours_field_settings implementation of hook_field Handle the parameters for a field.
office_hours_menu @file Creates a field and widget for inserting working or office hours per day
office_hours_widget Implementation of hook_widget().
office_hours_widget_info Declare information about a widget.
tf_to_mil
theme_oh_formatter_display Theme function for date node display
_create_hours_arr helper function to create hours array. items are saved in 24 hours string format (i.e '18:00').